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>
A task → an AgentRun → a parsed output. Postgres job queue (FOR UPDATE SKIP LOCKED) drained
by the worker, the assembler (house-style + skills + task → prompt), the BYOK model call, and
output parsed into an action + risk tag captured on the run. Nothing executes yet (gate is M5).
Verified: ArchitectureTests 8/8, IntegrationTests 29/29.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
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>
Encrypted owner-only API configs (AES-256-GCM, key never returned), model adapters with a
connection test, the Agent bound to a seat (skills, autonomy dial, model config, docs) that
flips a seat to AI, and the seat-configurator UI. Verified: build green, ArchitectureTests
8/8, IntegrationTests 27/27, client build clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A "AI seats" page (shadcn, on the design language): manage BYOK model connections (add +
test; the key is write-only), create seats on a team, and configure an agent per seat — name,
the color-graded autonomy dial (draft slate / gated indigo / auto teal), a model connection,
skill toggles from the registry, and docs. Navigable AppShell sidebar (Board / AI seats).
Verified: client `npm run build` clean (1890 modules, tsc + vite).
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>
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>
Git-sourced SKILL.md indexed into Postgres + pgvector, queryable by role; the four V1
atoms (spec-writing, story-breakdown, test-plan-generation, diff-review); Gitea/filesystem
sync; and the edit-distance eval harness. Verified: build green, ArchitectureTests 8/8,
IntegrationTests 25/25.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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).
- 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>
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>
Brings the M1 milestone to main: Identity/RBAC + JWT, OrgBoard board+cartable,
Governance audit, the edit-distance metric, and the shadcn UI on the TeamUp design
language. Verified green: dotnet build (warnings-as-errors), ArchitectureTests 8/8,
IntegrationTests 20/20 (Testcontainers + real pgvector), client npm build, and a live
stack smoke (SPA + API served, bootstrap→board flow 200).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A functional React/Vite SPA exercising the M1 API end-to-end:
- Zustand auth store (persisted JWT) + a small fetch client that attaches the bearer
token and logs out on 401.
- LoginPage: sign in, or bootstrap the first owner on first run.
- BoardPage: set org name, create/select a team, create tasks, move them across the
backlog -> in progress -> in review -> done columns, assign to me, and a cartable panel.
- React Router guards routes on the presence of a token.
Mirrors the integration-tested API contracts exactly. Compiles clean (tsc + vite);
still needs a manual click-through (run the web host + Postgres, or `docker compose up
--build`). dnd-kit drag, TanStack Query, and an orval-generated typed client are M1+
polish — buttons/selects drive task moves for now.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
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>
Fixes found while validating `docker compose up`:
- Copy .editorconfig into the build context and stop ignoring it — the in-container
publish needs its analyzer suppressions (CA1848/CA1873) or warnings-as-errors fails.
- npm ci uses npmjs.org (the Nexus npm proxy 404s on some packages; npmjs.org is
reachable from this machine); base images + NuGet still go through the mirror.
- Parameterize the Postgres/web host ports (defaults 5432/8080) so the stack doesn't
clash with other local Postgres instances.
Verified: image builds; `compose up` brings up Postgres (healthy) + web + worker on one
image (RUN_MODE) — /health 200, /api/orgboard/ping ok, the SPA serves at /, and the
worker logs "DB health: Healthy" heartbeats.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>