Commit Graph

20 Commits

Author SHA1 Message Date
soroush.asadi 428eae9643 Fix (review): seat picker must surface the skill the run resolves
Adversarial review found a display-vs-run mismatch: the seat picker collapses the library to the
first row per key, but ListSkills ordered by version only — so for a key the org authored alongside
a higher-versioned builtin, the picker showed/flagged the builtin while the run injected the org's
own skill. ListSkills now orders the same way the run-time catalog resolves (Published-first,
org-owned-over-builtin, then latest version with the same Ordinal comparison), computed in-memory so
the version tiebreak can't diverge from SkillCatalog. The run itself was already correct; this aligns
what the operator sees with what executes. No client change needed.

SkillRunScopingTests now also asserts the library's first row for a key the org authored is the
org-owned Published row, not the builtin.

Verified: skills test subset 4/4 (full suite green pre-merge).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 18:30:12 +03:30
soroush.asadi 2ebe2808be Wire skills into agent runs: org-scoped, published-only, org-preferred resolution
ISkillCatalog.GetByKeysAsync now takes the org id and resolves each key within that org's namespace
only — the org's own published skill, else a shared builtin (null org), never another org's. Org-owned
is preferred over the builtin; only Published (golden-tested) skills are injected; the resolved
skill@version is recorded in the prompt heading and run trace. AgentRunExecutor threads
context.OrganizationId. SeatsPage now loads the org library (builtins + authored + installed), dedupes
to one entry per key, and flags drafts (won't run until published).

Verified: ArchitectureTests 8/8, IntegrationTests 48/48 (new SkillRunScopingTests: a run assembles the
org's own skill over the builtin of the same key, and another org's same-key skill never leaks in),
client build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 13:35:53 +03:30
soroush.asadi 62883ed01f Skill marketplace: publish, install, org-aware listing (+ adversarial-review fixes)
Orgs can now share skills across the tenant boundary — the next step after the per-org library.

Endpoints (all ManageSkills-gated + audited):
- POST /{key}/publish — list one of your published versions on the marketplace (Visibility→Public;
  only a Published/golden-tested skill may be listed). POST /{key}/unpublish reverses it.
- POST /install — copy a publicly-listed skill (by row id) into your org as a private Installed
  copy; rejects installing your own skill and duplicate (org+key+version) installs.
- GET /marketplace?organizationId= — other orgs' Authored+Public+Published skills (yours excluded),
  each flagged whether that exact (key, version) is already in your library.
- SkillSummary now carries Id (install targets a specific source row). Authored skills default to
  private — listing is an explicit publish step, never a side effect of authoring.

UI (Skills page): a Marketplace tab with Install / "In your library"; Publish / Unlist on your own
published skills; a "Listed" badge.

Fixes from the adversarial review (4 confirmed findings, all addressed):
- HIGH — Public⟹Published is now a domain invariant (Skill.Index forces PrivateToOrg whenever the
  re-derived status isn't Published), so re-authoring a listed version without golden tests can no
  longer leave it Public+Draft or decouple the marketplace gate from the eval gate.
- MEDIUM — install now uses an insert-only indexer path so the (org,key,version) unique index is the
  source of truth: a race with a concurrent install/author becomes a clean 409, never an in-place
  clobber of an existing row's content/ownership.
- MEDIUM/LOW — AlreadyInLibrary is computed per (key, version) to match the install conflict rule, so
  a newer, not-yet-owned version of a key you already hold still shows as installable.

Verified: ArchitectureTests 8/8, IntegrationTests 47/47 (SkillMarketplaceTests: publish gate, own-org
exclusion, cross-org list→install→private copy, duplicate 409, per-version flag, Public⟹Published
invariant, Member 403), client build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 12:27:22 +03:30
soroush.asadi fad476f115 Dynamic per-org skill library: in-app authoring, versioning, fork (+ marketplace seam)
Skills move from a global Git-only registry to a per-company library that orgs author and
version in-app — Git stays as the shared *starter* library.

Domain & persistence:
- Skill gains OrganizationId (null = shared builtin, visible to every org), Origin
  (Builtin | Authored | Installed), AuthoredByMemberId. Identity is now
  (OrganizationId, SkillKey, Version); the unique index uses NULLS NOT DISTINCT so builtins
  stay unique by key+version while each org gets its own namespace (and can fork a builtin).
  AddSkillOwnership migration backfills existing rows as Builtin.
- Owned GoldenExample rows are cloned in Skill.Index so a fork can't re-parent the source's
  tracked entities.

Authoring (tenant, dynamic):
- POST /api/skills/authored — structured fields → same indexer pipeline (embedding +
  publish gate apply identically), tagged org + author. POST /api/skills/{key}/fork copies a
  builtin/global skill into your org as an editable Authored draft. List/Get are org-scoped
  (your org + shared builtins). New Capability.ManageSkills (Owner + TeamOwner), audited.
- GET /api/skills/marketplace: read-only seam listing public skills across orgs (install is
  the next step).

Security (from adversarial review — two confirmed criticals):
- Managing shared builtins is an operator action, not a tenant one. /index (posts arbitrary
  content as a global builtin) and /sync (re-indexes the shared library) now require a
  platform admin key (X-Skills-Admin-Key, fixed-time compare, fail-closed when unset) via
  SkillAdminOptions — previously any authenticated user of any org could inject/poison global
  skills. New test asserts an authenticated Owner without the key gets 403 on both.

UI: new /skills library page — browse shared + org skills grouped by key with their versions,
create / new-version / fork, golden-test editor + body, Draft/Published badge and the
publish-gate hint (needs roles + ≥1 golden test).

Verified: ArchitectureTests 8/8, IntegrationTests 46/46 (new SkillLibraryTests: org
isolation, version coexistence, fork, publish gate, Member 403, admin-gate 403), client build
green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 11:09:02 +03:30
soroush.asadi 1e65654114 Org structure: divisions → products/services → teams + custom model base URL
The object spine becomes definable (data model was designed-for from day one):
- Division and Product entities (Product carries kind: Product|Service, optional DivisionId);
  Team gains nullable ProductId — pre-structure teams keep working. AddDivisionsAndProducts
  migration; org-scoped validation; owner-only writes (audited); list endpoints.
- /structure page: define divisions, products/services (with division), teams (under a
  product). Org chart now renders the full spine — org → divisions → products → teams →
  seats — with parentless layers linking up to the org.
- BYOK custom URL: the SeatsPage model-connection form gains a Base URL field (provider
  list: stub/openai/ollama/vllm/custom). Backend already supported it end to end —
  ApiConfig.Endpoint flows into the OpenAI-compatible adapter ({base}/v1/chat/completions),
  so any OpenAI-compatible gateway or self-hosted model works; the config list shows it.

Verified: ArchitectureTests 8/8, IntegrationTests 45/45 (new OrgStructureTests: spine
creation, kind tags, org-scoped validation 400s, Member 403), client build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 18:13:52 +03:30
soroush.asadi 4416d99360 Any seat can be AI-staffed: engineer/designer/analyst atoms + role-aware seat suggestions
The core product thesis made tangible beyond PO/QA:
- Four new golden-tested skill atoms in skills/: code-implementation + bug-diagnosis
  (engineer — output is a reviewable patch/diagnosis artifact; Git write-back stays Phase 2),
  ui-design-spec (designer), requirements-analysis (analyst, also tagged product-owner).
  The catalogue now spans five roles with eight atoms.
- Seat configurator: SuggestedSkills — maps the seat's free-text role name to skill role
  tags and offers the matching set one click ("Use set"). Any role name → staffed with AI.
- AnyRoleSeatTests: an "Backend Engineer" seat (Edison, gated) runs the same pipeline —
  skills assemble, implement-code/Draft parsed, proposal held in the review inbox like any
  governed action. SkillSyncTests updated for the larger catalogue.

Verified: IntegrationTests 44/44, client build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:57:10 +03:30
soroush.asadi d853609213 UI completion pass + accountability & benchmarking
UI (daily-drivable now):
- Board: dnd-kit drag-and-drop between columns; click a card → task detail drawer (Sheet)
  with status, member assignee picker, send-to-AI-seat dispatch, description/artifact,
  parent/children navigation; seat-triad assignee chips (AI indigo monogram / human slate).
- Cartable page (the personal pending slice), Members & invitations page (invite + copy
  join token; V1 sends no email), Review inbox now shows a word-level diff of your edits
  vs the proposal (lib/diff.ts, LCS), Org chart page (React Flow: org → teams → seats in
  the human/open/AI triad). Nav reordered; nothing left "soon".

Accountability & benchmarking:
- Identity: GET /members (directory + org role) and GET /invitations (with join token,
  inviter-only) — the directory also resolves names client-side everywhere.
- OrgBoard: work_item_transitions recorded on every status change (AddWorkItemTransitions
  migration); GET /performance — per assignee (human and AI on the same scale): pending by
  column, done, worked hours (time in InProgress), avg cycle time (start of work → done),
  plus the unassigned-pending count. Owner-level capability.
- Performance page: benchmark table merging board metrics with AI trust metrics (approval
  rate + edit distance from analytics); flags work with no one accountable.

Verified: build green; ArchitectureTests 8/8; IntegrationTests 43/43 (new: directory,
invitations list + Member 403s, transition-derived worked-hours/cycle-time, unassigned
count); client npm build green (TS strict).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 12:54:13 +03:30
soroush.asadi fe7a5c481e M6: working memory + the PO→QA trigger + analytics — V1 complete
Working memory (Memory module's first real code):
- MemoryEntry (schema "memory", vector(384), InitialMemory migration); TeamMemory implements
  the SharedKernel ITeamMemory seam (embed-and-store on write, cosine recall on read);
  GET /api/memory/search. HashingTextEmbedder promoted to SharedKernel (pure, deterministic;
  swapped for ONNX/BYOK embedders later behind ITextEmbedder).
- Written on approval: Governance's approve stores an Approval/Correction entry per decision.
- Read at assembly: the executor recalls the team's top-3 relevant entries; the prompt gains
  a "# Team memory" section (treated as data, not instructions).

The single V1 event trigger:
- IAgentDispatcher (SharedKernel) implemented by Assembler's AgentRunDispatcher (shared by
  the API and triggers). OrgBoard's QaHandoffTrigger: a task hitting done creates a QA task
  (provenance parent, assigned to the QA agent) and dispatches a run for the team's QA AI
  seat. Guardrails: Test/Review tasks never re-trigger (no self-cascade) and a task hands
  off at most once. Audited as handoff.triggered.

Analytics — the V1 verdict view:
- IBoardStats (SharedKernel) implemented by OrgBoard; GET /api/governance/analytics returns
  approval rate, avg edit distance, per-agent metrics + edit-distance trend, tasks done.
- UI: /analytics — stat cards, per-agent table, recharts edit-distance trend per agent.

Verified: build green; ArchitectureTests 8/8; IntegrationTests 42/42 incl. the M6 acceptance
end to end — a dev marks a story done → Quill wakes via the handoff (QA task with provenance,
assigned to the agent) → drafts a test plan that waits in review → approve records the second
agent's edit distance → analytics show approval rate 100%, avg edit distance > 0, and trends
for BOTH Aria and Quill; memory written on Aria's corrected approval is recalled into her next
prompt; the guardrails hold. Client build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 12:07:35 +03:30
soroush.asadi d83ad87151 M5: action gate + review inbox — edit distance captured for real
SharedKernel:
- ActionRisk (risk lives on the action) + GatePolicy (the pure autonomy x risk matrix:
  Read never holds, Draft/Publish hold unless Autonomous, Destructive ALWAYS holds).
- IActionGate (AgentActionProposal -> execute|hold) and IBoardWriter.AttachArtifactAsync.

Governance:
- ReviewItem (held action: artifact, child titles, trace, decision, edit distance) in a new
  review_items table (AddReviewItems migration).
- ActionGate: hold -> ReviewItem + "action.held" audit; autonomous -> execute + audit.
- HeldActionExecutor: writes the artifact onto the task and creates the child tasks via
  IBoardWriter (implemented by OrgBoard — no cross-module table access).
- Review inbox API: GET /api/governance/reviews (scope-filtered to where the caller may
  approve), POST /reviews/{id}/approve (optional edited content/children -> normalized
  edit distance recorded — the north-star metric), POST /reviews/{id}/sendback. Deciding
  twice is 409; Members are 403.

Assembler:
- OutputParser (numbered-list child titles, conservative) and the executor now hands every
  completed run's proposal to the gate.

OrgBoard: WorkItem.AttachArtifact + BoardWriter.AttachArtifactAsync.

Verified: build green; ArchitectureTests 8/8; IntegrationTests 41/41 incl. the full M5
acceptance — Aria (gated) proposes a spec, it waits in the inbox with its trace, a Member is
403'd, the owner edits-and-approves, the spec + four child stories land on the board, edit
distance > 0 is recorded and audited; Quill (autonomous) executes straight to the board;
destructive holds even for an autonomous seat and can be sent back. Plus the GatePolicy matrix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 07:45:35 +03:30
soroush.asadi d9f9349117 M4: the assembler — assemble → model → parse (Increment 2)
SharedKernel contracts (so Assembler stays decoupled): IAgentRunContextProvider (agent +
task) and ISkillCatalog (skill prompts by key). Implemented by OrgBoard (AgentRunContextProvider)
and Skills (SkillCatalog).

Assembler:
- PromptAssembler builds house-style + identity + the agent's skill bodies + the task, and
  derives the primary action + risk from the agent's first skill. RAG/working-memory join at M6.
- AgentRunExecutor (real): resolve context + skills → assemble → resolve BYOK config (with
  fallback) → call IModelClient → parse into action + risk → capture all on the AgentRun.

Verified: build green; ArchitectureTests 8/8; IntegrationTests 29/29 — incl. the M4 acceptance:
assigning a Spec task to Aria (PO, gated, stub BYOK) yields a Completed run with the assembled
prompt (skill body + task title), action "write-spec", risk "Draft", and model output. Nothing
executes — the gate is M5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 06:19:02 +03:30
soroush.asadi 09eaf360a3 M4: agent-run job queue + worker drain (Increment 1)
SharedKernel: IWorkerModule seam (RegisterWorker runs in the worker host only).
Bootstrap: AddTeamUpWorkerServices; the worker host now wires it.

Assembler module (schema "assembler", InitialAssembler migration):
- Job (Pending→Processing→Done/Failed) + AgentRun (Queued→Running→Completed/Failed) entities.
- JobQueue: enqueue + ClaimNextAsync using `FOR UPDATE SKIP LOCKED` in a transaction.
- AgentRunExecutor (Increment-1 placeholder — real assemble/model/parse lands in Increment 2).
- JobProcessor BackgroundService drains the queue on the worker host (web off the model path).
- POST /api/assembler/runs enqueues a run; GET /api/assembler/runs/{id} reads it.

Verified: build green; ArchitectureTests 8/8 (Assembler references only SharedKernel);
IntegrationTests 28/28 incl. enqueue→claim(SKIP LOCKED)→process→Completed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 01:16:37 +03:30
soroush.asadi e202246a1c M3: Agent bound to a seat — configure an AI seat
OrgBoard: Agent entity (name, monogram, autonomy dial, ApiConfigId + optional fallback,
skill keys, docs) + AddAgents migration; one agent per seat. References Skills by key and
the BYOK config by id — never reaches into those modules.

Endpoints: POST/GET /api/orgboard/seats (create/list seats), POST/GET
/api/orgboard/seats/{id}/agent (configure/read the agent) — ConfigureAgents at [team, org].
Configuring an agent flips the seat to the AI state and points it at the agent; audited.

Verified: build green; ArchitectureTests 8/8; IntegrationTests 27/27 incl. the M3 acceptance
flow — owner adds a BYOK config, then configures "Aria" (gated autonomy, skills, that config)
on a seat, flipping it to AI, with the key never exposed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:49:28 +03:30
soroush.asadi 1559975518 M3: BYOK — encrypted owner-only API configs + model adapters
SharedKernel: Autonomy dial enum; IModelClient (ModelRequest/ModelCompletion);
IApiConfigResolver (+ ApiConfigSummary/ResolvedApiConfig) — server-side, decrypted.

Integrations module:
- ApiConfig entity (org-scoped) + IntegrationsDbContext (schema "integrations") +
  InitialIntegrations migration; the key is AES-256-GCM encrypted at rest (key derived from
  Encryption:MasterKey) and never returned to a client.
- Model adapters: StubModelClient (no-network, provider "stub"/"echo"), an OpenAI-compatible
  HTTP adapter, and a ModelClientRouter; ApiConfigResolver decrypts server-side only.
- Endpoints: POST/GET/DELETE /api/integrations/api-configs and POST .../{id}/test. Create/
  test/delete require ManageApiKeys (owner); listing requires ConfigureAgents (assign-only,
  no key). Dev master key in appsettings; override Encryption__MasterKey in prod.

Verified: build green; ArchitectureTests 8/8 (Integrations references only SharedKernel);
IntegrationTests 26/26 incl. a BYOK flow — key never appears in any response, the connection
test succeeds (stub), and a Member is 403'd from create + list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:26:28 +03:30
soroush.asadi e987e33c0a M2: eval harness — golden tests gated on edit distance
- SkillEvaluator (internal to Skills): runs each golden test through an ISkillExecutor and
  passes only if normalized edit distance <= threshold (the north-star metric). The executor
  is a stub in M2 (no model runtime); M4's assembler supplies the real one and publishing is
  gated on the report. The indexer's structural gate (roles + >=1 golden test) stands until then.
- InternalsVisibleTo the integration tests so the harness is exercised directly.

Verified: build green; ArchitectureTests 8/8; IntegrationTests 25/25 (+3 eval-harness unit
tests: pass on match, fail on divergence, fail with no golden tests).
2026-06-09 18:42:19 +03:30
soroush.asadi bfcd223374 M2: the four V1 atoms + Git sync (Gitea / filesystem)
- Author the four V1 skill atoms in skills/ (Git is the source of truth): spec-writing &
  story-breakdown (product-owner), test-plan-generation & diff-review (qa) — each with
  risk-tagged actions, golden tests, and a body.
- SharedKernel: IGitProvider seam (read-only, provider-agnostic) + GitFile.
- Integrations module (its first real code): FileSystemGitProvider (dogfood/local) and a
  GiteaGitProvider (Gitea REST: recursive tree → SKILL.md blobs → base64 contents); the
  provider is chosen by GitSource:Provider config.
- Skills: SkillSyncService consumes IGitProvider (never Integrations) and indexes each file;
  POST /api/skills/sync and a POST /api/skills/webhook/gitea (re-sync on push; signature
  verification + changed-file-only + queue offload come later).

Verified: build green; ArchitectureTests 8/8 (Skills & Integrations reference only
SharedKernel; the Git seam lives in SharedKernel); IntegrationTests 22/22 incl. a sync that
indexes the four real atoms from skills/, published and queryable by role.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:34:53 +03:30
soroush.asadi 401e3e69af M2: skill index — SKILL.md parsing, pgvector index, query by role
Skills module (references SharedKernel only):
- Skill entity + SkillsDbContext (schema "skills") + InitialSkills migration: roles/tools/
  context as text[], risk-tagged actions and golden tests as jsonb, a nullable vector(384)
  embedding, unique (SkillKey, Version).
- SkillMarkdownParser: YAML frontmatter (YamlDotNet) + markdown body → SkillManifest.
- HashingSkillEmbedder: placeholder deterministic embedder so the pgvector path is real now;
  swapped for ONNX/BYOK embeddings at M3-M4 (384-dim to match MiniLM/bge).
- SkillIndexer: parse → hash → embed → upsert; structural publish gate (roles + >=1 golden
  test). Executing golden tests against a model + gating on edit distance lands at M4.
- Endpoints: GET /api/skills (filter by role/visibility), GET /api/skills/{key},
  POST /api/skills/index (manual/admin) — all authenticated.

Verified: build green; ArchitectureTests 8/8 (Skills references only SharedKernel);
IntegrationTests 21/21 incl. a new skill-registry flow — index a SKILL.md, it publishes,
is queryable by role (and not under others), re-index dedups, malformed is 400, catalogue
needs auth.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:01:37 +03:30
soroush.asadi fa9046a03e M1: audit log (Governance) + edit-distance metric
SharedKernel:
- IAuditLog/AuditEvent — append-only audit contract any module writes through.
- EditDistance (Levenshtein + normalized) — the north-star metric, available from day
  one; consumed at edit-and-approve in M5.

Governance module (references SharedKernel only):
- AuditEntry entity; internal GovernanceDbContext (schema "governance") +
  InitialGovernance migration; AuditLog implements IAuditLog.
- GET /api/governance/audit — owner-only (ViewAuditLog), returns recent entries.

Wiring (via the SharedKernel IAuditLog interface — no module references Governance):
- OrgBoard records team.created, task.created, task.moved, task.assigned.
- Identity records invitation.created, member.joined.

Verified: build green; ArchitectureTests 8/8 (Governance references only SharedKernel;
audit flows through the shared interface); IntegrationTests 20/20 — board flow now
asserts task.created/task.moved appear in the audit log, plus EditDistance unit tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 12:18:30 +03:30
soroush.asadi e1911f58b1 M1: OrgBoard — organizations, teams, seats, the board & cartable
OrgBoard module (references SharedKernel only; RBAC via ICurrentUser/IPermissionService):
- Organization, Team, Seat (human/open/ai), WorkItem (board task: type, status, assignee,
  parent) entities; internal OrgBoardDbContext (schema "orgboard") + InitialOrgBoard
  migration; design-time factory. (WorkItem avoids the System.Threading.Tasks.Task clash.)
- Endpoints under /api/orgboard, every mutation permission-checked at the scope chain
  [team, org]: POST /organizations, POST/GET /teams, POST /tasks, GET /board (columns
  backlog->in progress->in review->done), PATCH /tasks/{id}/move, /assign, GET /cartable.

Test isolation: integration tests now use IClassFixture so each class gets its own
pgvector container (the bootstrap-once rule made a shared container collide).

Verified: build green; ArchitectureTests 8/8 (OrgBoard references only SharedKernel);
IntegrationTests 12/12 incl. a new board flow — owner sets up org+team, creates/moves/
assigns a task, sees it on the board and in the cartable; an invited Member can view the
board but is 403'd from creating a team.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 11:58:20 +03:30
soroush.asadi 61991bf6cd M1: Identity & access — members, RBAC, JWT auth, invitations
Adds the access foundation everything else enforces against.

SharedKernel (shared access contracts, no Identity dependency for consumers):
- ScopeRef/ScopeType, RoleType, Capability, AccessPolicy (role x capability matrix),
  ICurrentUser, IPermissionService (scope-chain evaluation).

Identity module:
- Member, Membership, Invitation entities; internal IdentityDbContext (schema
  "identity") + InitialIdentity migration; design-time factory.
- JWT auth (HS256) issuing membership claims; PasswordHasher<Member>; CurrentUser
  (claims -> ICurrentUser) and PermissionService implementations.
- Public IMemberDirectory contract for other modules to resolve member display info.
- Endpoints: POST /bootstrap (first owner), /auth/login, GET /me, POST /invitations,
  POST /invitations/accept. Owner-only actions enforced via IPermissionService.
- Web host wires UseAuthentication/UseAuthorization and string-enum JSON.

Verified: build green; ArchitectureTests 8/8 (Identity references only SharedKernel);
IntegrationTests 11/11 incl. a new end-to-end flow — bootstrap -> login -> /me ->
invite -> accept -> login as invitee, and a Member is 403'd from inviting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 07:59:57 +03:30
soroush.asadi 36fe158b43 Scaffold the Before-M1 repo skeleton
Stand up the modular-monolith skeleton per docs/V1_BUILD_PLAN.md: one .NET 10
solution with web + worker hosts sharing seven interface-bounded module projects,
PostgreSQL 17 + pgvector via EF Core 10, a React 19 + Vite SPA built into wwwroot,
and Docker Compose for one-command local dev. Skeleton only — no feature code.

Architecture
- One project per module (OrgBoard, Identity, Skills, Assembler, Governance,
  Memory, Integrations); each is its own assembly so non-public types (entities,
  DbContext) are invisible across modules at compile time.
- TeamUp.Bootstrap is the only library that references all modules; both hosts
  reference only Bootstrap. SharedKernel/Infrastructure never reference modules.
- IModule seam: Register(...) runs in both hosts; MapEndpoints(...) only in web.
- PlatformDbContext owns the pgvector extension + the seven module schemas
  (InitialPlatform migration); MigrationRunner applies it then any module context.
- One image, two roles selected by RUN_MODE at the Docker entrypoint.

Verified
- dotnet build green (nullable + warnings-as-errors).
- ArchitectureTests 8/8 — reflection-based boundary rules (no module -> module,
  -> Infrastructure, -> Bootstrap, or -> host references).
- IntegrationTests 10/10 — Testcontainers boots the host against real pgvector:
  migration applies, vector extension + 7 schemas exist, /health 200, every
  /api/<module>/ping 200, /openapi/v1.json served.
- client builds clean (Vite 6 — pinned for Node 22.3.0; Vite 8 needs Node >=22.12).

Packages and base images route through the Nexus mirror (mirror.soroushasadi.com),
reachable from Iran when nuget.org / Docker Hub / MCR are not. CI is intentionally
deferred to a later session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 06:41:28 +03:30