Captures the design from the brainstorm session on agent-first development in this template. Architecture doc covers the four interlocking pillars: - Conformance engine (manifest + brands + ESLint + boot + CI gate) - Agent workflow (PRD -> Epic -> Story -> Task; manifest-first ordering; TDD per slice; in/out scope at every level) - Local task system at docs/work/ (filesystem markdown, derived committed _state.json, single-writer orchestrator rule) - Sandcastle orchestrator (implementer + reviewer loop, DAG-respecting, configurable retry cap) Work-shape guides extend the architecture doc with operational detail for frontend work (atomic design, Storybook-as-spec, component + Playwright screenshot test gates, Storybook MCP reviewer integration) and infrastructure work (ADR-first flow, dedicated ADR elicitation skill, optional core packages vs. new infrastructure layers). Phasing is conformance-first: build the enforcement system manually, then build the dispatch substrate, then migrate remaining features through it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
651 lines
29 KiB
Markdown
651 lines
29 KiB
Markdown
---
|
||
title: Agent-first development workflow + feature conformance
|
||
status: design
|
||
created: 2026-05-12
|
||
authors: [danijel, claude]
|
||
related:
|
||
- docs/architecture/feature-conformance-explainer.html
|
||
- docs/architecture/vertical-feature-spec.md
|
||
- docs/guides/tdd-workflow.md
|
||
- docs/guides/scaffolding-a-feature.md
|
||
- CLAUDE.md
|
||
---
|
||
|
||
# Agent-first development workflow + feature conformance
|
||
|
||
## Why this document exists
|
||
|
||
`template-vertical` is being shaped around the assumption that **AI coding agents will author most feature work**. Humans set direction, write PRDs (with agent help), review diffs, and intervene when escalation is needed; agents do the bulk of the coding. This document defines:
|
||
|
||
1. The **feature-conformance enforcement system** that gives agents tight, layered, machine-readable feedback on architectural drift.
|
||
2. The **agent-first workflow** — manifest → contracts → tests → code — that the conformance system enforces.
|
||
3. The **local task system** at `docs/work/` that holds PRDs, epics, stories, and tasks as markdown, parseable by both humans and agents.
|
||
4. The **sandcastle orchestrator** that dispatches implementer and reviewer agents per task, respecting a dependency DAG.
|
||
|
||
These four pillars are co-designed. The conformance system is the enforcement substrate; the workflow is the shape of work; the task system is the address space; sandcastle is the dispatch loop.
|
||
|
||
The conformance design is illustrated separately at [`docs/architecture/feature-conformance-explainer.html`](./feature-conformance-explainer.html). This document complements that with the surrounding workflow + tooling.
|
||
|
||
## Mental model
|
||
|
||
| Pillar | What it is | Primary artifact |
|
||
|---|---|---|
|
||
| **Conformance engine** | manifest + TS brands + ESLint + boot-time assertion + CI gate | `feature.manifest.ts` per feature; `_state.json` snapshot of compliance |
|
||
| **Agent workflow** | PRD → Epic → Story → Task; manifest-first ordering; TDD-per-slice | `docs/work/**/*.md` |
|
||
| **Local task system** | filesystem markdown, single state file, dispatchable | `docs/work/` |
|
||
| **Sandcastle orchestrator** | implementer + reviewer agents per task, DAG-respecting, retry-capped | `.sandcastle/` config + `scripts/work-*.ts` |
|
||
|
||
The same artifacts are read by humans, AI implementers, AI reviewers, the orchestrator, and the pre-commit hooks. There is no separate "process layer."
|
||
|
||
## Hierarchy
|
||
|
||
```
|
||
PRD (initiative — one .prd.md)
|
||
└── Epic (large body of work — one folder + _epic.md)
|
||
└── Story (one use case OR one technical capability)
|
||
└── Task (one vertical slice = one PR = one commit)
|
||
└── Subtask (rare; only for unexpectedly complex slices)
|
||
```
|
||
|
||
**Feature is metadata, not a hierarchy level.** The use-case identifier (`auth.signUp`) already encodes the feature. ClickUp/Linear/etc. tags can carry it; in this system it sits in frontmatter.
|
||
|
||
**Story type is metadata** — `user-story` or `technical-story` — same hierarchy slot, different body template:
|
||
|
||
- **User story:** `As a <role>, I want <action>, so that <outcome>`
|
||
- **Technical story:** `Goal / Why / Done when`
|
||
|
||
## File system
|
||
|
||
```
|
||
docs/work/
|
||
├── README.md # how this folder is used
|
||
├── _state.json # derived, committed, orchestrator-managed
|
||
├── _templates/
|
||
│ ├── prd.template.md
|
||
│ ├── epic.template.md
|
||
│ ├── user-story.template.md
|
||
│ ├── technical-story.template.md
|
||
│ └── task.template.md
|
||
├── prds/
|
||
│ ├── 2026-05-12-conformance-system.prd.md
|
||
│ └── ...
|
||
├── conformance-system-v1/ # one folder per epic
|
||
│ ├── _epic.md
|
||
│ ├── 01-define-feature-helper/ # one folder per story
|
||
│ │ ├── _story.md
|
||
│ │ ├── 01-define-feature-helper-exists.task.md
|
||
│ │ ├── 02-instrumented-brand-attached.task.md
|
||
│ │ └── ...
|
||
│ └── ...
|
||
└── work-system-v1/
|
||
└── ...
|
||
|
||
.sandcastle/
|
||
├── Dockerfile # extends existing CI image
|
||
├── implementer.prompt.md
|
||
├── reviewer.prompt.md
|
||
├── decomposer.prompt.md
|
||
├── prd-eliciter.prompt.md
|
||
└── .env.example
|
||
|
||
scripts/
|
||
├── work-prd-new.ts # invokes PRD elicitation skill
|
||
├── work-decompose.ts # PRD → epic + stories
|
||
├── work-decompose-tasks.ts # story → tasks
|
||
├── work-dispatch.ts # orchestrator loop
|
||
├── work-status.ts # human-readable status tree
|
||
└── work-rebuild-state.ts # regen _state.json from markdown
|
||
```
|
||
|
||
### Naming conventions
|
||
|
||
- **Underscored system files** (`_epic.md`, `_story.md`, `_state.json`, `_templates/`) — orchestrator-managed indexes or templates
|
||
- **Numeric prefix on filenames** (`01-`, `02-`) — execution order; doubles as sort key
|
||
- **`<slug>.task.md`** — individual tasks
|
||
- **`<date>-<slug>.prd.md`** — PRDs date-prefixed for chronological sort
|
||
|
||
## File formats
|
||
|
||
### PRD — `docs/work/prds/<date>-<slug>.prd.md`
|
||
|
||
```markdown
|
||
---
|
||
id: 2026-05-12-conformance-system
|
||
title: Feature Conformance System
|
||
type: prd
|
||
status: draft | in-review | approved | superseded
|
||
author: danijel
|
||
elicitation-session: <agent-session-id>
|
||
created: 2026-05-12
|
||
---
|
||
|
||
## Problem
|
||
What's broken or missing today? Who hurts because of it?
|
||
|
||
## Goal
|
||
What state are we trying to reach?
|
||
|
||
## In scope
|
||
- ...
|
||
|
||
## Out of scope
|
||
- ...
|
||
|
||
## Constraints
|
||
- ...
|
||
|
||
## Success criteria
|
||
- ...
|
||
|
||
## Requirements
|
||
- R1: ...
|
||
- R2: ...
|
||
|
||
## Open questions
|
||
- Q1: ...
|
||
```
|
||
|
||
**Authoring flow:**
|
||
1. Human runs `pnpm work prd-new "<one-line idea>"`
|
||
2. Agent invokes the **PRD elicitation skill** — runs a question-driven interview with the human (similar in shape to `superpowers:brainstorming`) until it has enough context across Problem / Goal / Scope / Constraints / Success / Requirements
|
||
3. Agent drafts PRD with `status: draft`
|
||
4. Human reviews, edits, flips to `status: approved`
|
||
5. **Decomposer refuses to run on `draft` PRDs.**
|
||
|
||
### Epic — `<epic-slug>/_epic.md`
|
||
|
||
```markdown
|
||
---
|
||
id: conformance-system-v1
|
||
prd: 2026-05-12-conformance-system
|
||
title: Conformance system v1
|
||
type: epic
|
||
status: todo | in-progress | done | cancelled
|
||
features: [cross-cutting]
|
||
created: 2026-05-12
|
||
target: 2026-Q3
|
||
---
|
||
|
||
## Goal
|
||
Build the feature-conformance enforcement system so AI agents get
|
||
layered, sub-second feedback on drift between manifest and code.
|
||
|
||
## Why
|
||
(brief — link to PRD for detail)
|
||
|
||
## In scope
|
||
- ...
|
||
|
||
## Out of scope
|
||
- ...
|
||
|
||
## Stories
|
||
- [ ] [01 — defineFeature helper + Instrumented brand](01-define-feature-helper/_story.md)
|
||
- [ ] [02 — Boot assertions](02-boot-assertions/_story.md)
|
||
- ...
|
||
```
|
||
|
||
### Story (technical) — `<epic>/<story>/_story.md`
|
||
|
||
```markdown
|
||
---
|
||
id: 01-define-feature-helper
|
||
epic: conformance-system-v1
|
||
title: defineFeature helper + Instrumented brand
|
||
type: technical-story
|
||
status: todo | in-progress | done
|
||
feature: core-shared
|
||
depends-on: []
|
||
blocks: [02-boot-assertions, 05-generator-updates]
|
||
---
|
||
|
||
## Goal
|
||
Manifest helper + brand types enable type-level enforcement that every
|
||
use-case binding is wrapped with `withSpan` + `withCapture`
|
||
(and `withAudit` when mutating).
|
||
|
||
## Why
|
||
Compile-time feedback is the cheapest layer and the foundation every other
|
||
milestone reads.
|
||
|
||
## Done when
|
||
Compile-time TS2322 fires at the IDE when an unwrapped factory is bound
|
||
through `ProductionUseCase<...>`.
|
||
|
||
## In scope
|
||
- `defineFeature` helper signature + tests
|
||
- Brand types: `Instrumented<F>`, `Captured<F>`, `Audited<F>`
|
||
- Wiring brands into existing wrappers (no API changes)
|
||
- `auth` as the reference feature using the new pattern
|
||
|
||
## Out of scope
|
||
- Migration of other features (each is its own story)
|
||
- Boot-time `assertConformance` (story 02)
|
||
- ESLint rules consuming the brands (story 03)
|
||
|
||
## Tasks
|
||
- [ ] [01 — defineFeature helper exists](01-define-feature-helper-exists.task.md)
|
||
- [ ] [02 — Instrumented brand attached via withSpan](02-instrumented-brand-attached.task.md)
|
||
- ...
|
||
```
|
||
|
||
### Story (user) — same skeleton, body uses As a / I want / So that
|
||
|
||
```markdown
|
||
---
|
||
id: 01-sign-up
|
||
epic: auth-v1
|
||
title: Sign up with email and password
|
||
type: user-story
|
||
status: todo
|
||
feature: auth
|
||
depends-on: []
|
||
---
|
||
|
||
## As a / I want / So that
|
||
**As a** visitor
|
||
**I want** to create an account with email and password
|
||
**So that** I can access member-only content
|
||
|
||
## In scope
|
||
- Email/password sign-up flow
|
||
- Password hashing
|
||
- Audit + event emission on success
|
||
- tRPC procedure exposure
|
||
|
||
## Out of scope
|
||
- OAuth sign-up (separate story)
|
||
- Email verification (separate story)
|
||
|
||
## Tasks
|
||
- [ ] [01 — reject invalid email format + scaffold](01-reject-invalid-email-format.task.md)
|
||
- [ ] [02 — reject duplicate email](02-reject-duplicate-email.task.md)
|
||
- ...
|
||
```
|
||
|
||
### Task — `<epic>/<story>/<slug>.task.md`
|
||
|
||
```markdown
|
||
---
|
||
id: 02-instrumented-brand-attached
|
||
story: 01-define-feature-helper
|
||
epic: conformance-system-v1
|
||
title: Attach Instrumented<F> brand via withSpan
|
||
type: task
|
||
status: todo | ready | in-progress | done | escalated
|
||
depends-on: [01-define-feature-helper-exists]
|
||
blocks: [06-signin-rebound-via-branded-slot]
|
||
sandbox: default
|
||
max-attempts: 3 # default; override per task
|
||
attempts: { implementer: 0, reviewer: 0 }
|
||
---
|
||
|
||
## Goal
|
||
Attach the `Instrumented<F>` brand to functions returned by `withSpan`.
|
||
|
||
## Why this matters
|
||
The brand is the type-level seam the binding signature checks. Without it,
|
||
the compiler can't tell a wrapped factory from an unwrapped one.
|
||
|
||
## Acceptance criteria
|
||
- [ ] `Instrumented<F>` = `F & { readonly __instrumented: true }`
|
||
- [ ] `withSpan` return type is `Instrumented<typeof fn>`
|
||
- [ ] Brand re-exported from `@repo/core-shared/conformance`
|
||
- [ ] Test asserts wrapped function carries brand at the type level
|
||
- [ ] All existing tests still pass
|
||
|
||
## Out of scope
|
||
- Updating `with-capture` and `with-audit` (separate tasks)
|
||
- Refactoring `withSpan`'s existing signature beyond adding the brand
|
||
- Adding runtime brand markers (type-only)
|
||
- Renaming existing types or symbols
|
||
|
||
## Files likely touched
|
||
- `packages/core-shared/src/instrumentation/with-span.ts`
|
||
- `packages/core-shared/src/instrumentation/with-span.test.ts`
|
||
- `packages/core-shared/src/conformance/index.ts`
|
||
|
||
## Reviewer notes
|
||
Reject if brand is implemented with runtime tag rather than pure type.
|
||
```
|
||
|
||
## State file — `docs/work/_state.json`
|
||
|
||
A derived, committed, orchestrator-written index. Markdown is source of truth; `_state.json` is a fast-to-query mirror.
|
||
|
||
```json
|
||
{
|
||
"updated_at": "2026-05-12T16:42:00Z",
|
||
"ready": ["02-instrumented-brand-attached"],
|
||
"in_progress": [],
|
||
"blocked": [],
|
||
"escalated": [],
|
||
"epics": {
|
||
"conformance-system-v1": {
|
||
"status": "in-progress",
|
||
"ac_total": 47,
|
||
"ac_completed": 8,
|
||
"stories": {
|
||
"01-define-feature-helper": {
|
||
"status": "in-progress",
|
||
"ac_total": 9,
|
||
"ac_completed": 4,
|
||
"tasks": {
|
||
"01-define-feature-helper-exists": {
|
||
"status": "done",
|
||
"depends_on": [],
|
||
"blocks": ["02-instrumented-brand-attached"],
|
||
"ac_total": 4,
|
||
"ac_completed": 4,
|
||
"attempts": { "implementer": 1, "reviewer": 1 },
|
||
"branch": "task/01-define-feature-helper-exists",
|
||
"completed_at": "2026-05-12T14:23:00Z"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Rules for `_state.json`
|
||
|
||
1. **Committed to git.** Audit trail visible in PRs.
|
||
2. **Single writer:** orchestrator + pre-commit hook only. No agent writes it directly.
|
||
3. **Derived from markdown.** Regenerable any time via `pnpm work rebuild-state`.
|
||
4. **Canonical formatting.** Sorted keys, stable indentation, no trailing whitespace. Pre-commit normalizes via Prettier.
|
||
5. **Merges serialized.** Orchestrator merges PRs one at a time. Parallel implementation in sandboxes is fine; only merge step is sequential.
|
||
6. **Pre-commit regen.** When any `.task.md` / `.story.md` / `_epic.md` is staged, the hook regenerates `_state.json` from the markdown, re-stages it, and lets the commit proceed. The hook only blocks the commit if regeneration itself fails (e.g. malformed frontmatter, broken `depends-on` reference). This makes the markdown the unambiguous source of truth: if humans edit checkboxes directly, the JSON quietly catches up.
|
||
|
||
## Scope guards
|
||
|
||
| Level | In scope | Out of scope |
|
||
|---|---|---|
|
||
| PRD | **required** | **required** |
|
||
| Story | **required** | **required** |
|
||
| Task | implicit (= AC list) | **optional but encouraged** |
|
||
|
||
The reviewer agent **explicitly checks the task's `Out of scope` section against the diff**. Rejects if the diff touches anything declared out of scope. This is the cheapest possible enforcement of "don't over-engineer" — pure text match, no AST needed.
|
||
|
||
## Conformance system integration
|
||
|
||
The four enforcement layers (detailed in [`feature-conformance-explainer.html`](./feature-conformance-explainer.html)):
|
||
|
||
| Layer | Latency | Catches |
|
||
|---|---|---|
|
||
| TypeScript brands | 0s | forgotten `withSpan` / `withAudit`; manifest ↔ binding-slot type mismatch |
|
||
| AST-aware ESLint | <1s | manifest ↔ code drift; undeclared `bus.publish` / `auditLogger.log`; required cores not installed |
|
||
| Boot assertion (`assertConformance`) | ~3s | binding type-casts that hid unwrapped factories; manifests edited without rebinding |
|
||
| CI drift gate (`pnpm conformance`) | ~120s | orphan event consumers; scaffold drift from generator; required-cores ↔ workspace mismatch |
|
||
|
||
### How conformance interacts with tasks
|
||
|
||
When a task adds an audit emission (e.g. `audits: ["user.created"]`):
|
||
|
||
1. Agent edits `feature.manifest.ts`
|
||
2. The binding's branded slot type *now* demands `Audited<F>` — TS2322 if the wrapper is missing
|
||
3. Agent adds `withAudit(...)` in `bind-production.ts` → TS goes quiet
|
||
4. Agent adds `auditLogger.log(...)` in the use-case factory → ESLint goes quiet
|
||
5. Pre-commit `pnpm conformance` confirms all four layers pass
|
||
6. PR submitted
|
||
|
||
Each step gives sub-second feedback. The agent's iteration loop is dominated by think + write, not by waiting for feedback.
|
||
|
||
## Workflow ordering (per task)
|
||
|
||
For any new use case or new behavior:
|
||
|
||
1. **Manifest** — declare the use case (or update audits/publishes/consumes if the task adds them). Pure declaration.
|
||
2. **Contracts** — `xInputSchema`, `xOutputSchema`, `IXUseCase` type alias in the use-case file. Factory body throws `"not implemented"` if not yet written.
|
||
3. **Tests (red)** — import contracts; write failing assertions that match the AC bullet.
|
||
4. **Implementation (green)** — fill factory body, repository, binding, until tests pass.
|
||
|
||
For incremental work on an existing use case, step 1 is often a no-op (manifest already declared). For the first slice of a new use case, all four steps happen in one commit.
|
||
|
||
## Work shapes
|
||
|
||
The Epic / Story / Task hierarchy holds for everything; the **inner workflow shape varies** with the kind of work. Three shapes are recognised:
|
||
|
||
| Shape | Default home | Manifest involvement | Test gates |
|
||
|---|---|---|---|
|
||
| **Backend** | feature packages | full (use cases, audits, publishes, consumes, jobs, realtime) | type-check + lint + conformance + unit/integration |
|
||
| **Frontend** | `@repo/core-ui` and `features/<feature>/src/ui/` | partial — pages consume use cases via controllers | type-check + lint + component tests + Playwright screenshot (CI) |
|
||
| **Infrastructure** | core packages, `apps/*/server/`, `docker-compose.yml`, `.github/`, ADRs | declarative — `requiredCores`, bind context | type-check + lint + conformance + ADR review |
|
||
|
||
The default shape is **backend** — what the rest of this doc describes. The two adapted shapes are summarised below; operational detail lives in their guides.
|
||
|
||
### Frontend (see `docs/guides/frontend-work-shape.md`)
|
||
|
||
- **Atomic design tiers** — atoms / molecules / organisms / templates / pages, generated via `pnpm turbo gen core-ui-component`. Tier-direction enforced by a new ESLint rule (`atomic-tier-import-direction`).
|
||
- **Storybook is the spec.** Each AC bullet on a UI task maps to a story variant or a Storybook `play` function. Story files become the shared visual contract between human, implementer, and reviewer.
|
||
- **Two test layers:**
|
||
- **Component tests** (Vitest + Testing Library, or `play` on stories) — pre-commit gate, <5s
|
||
- **Visual regression** via **Playwright screenshot tests** — CI gate, 30–120s, blocks PR merge on unapproved visual diffs
|
||
- **Adapted four-step ordering for a pure UI slice:**
|
||
1. **Story file** (the visual spec — analogous to the manifest entry for backend work)
|
||
2. **Contracts** — props interface, variant types
|
||
3. **Tests (red)** — component test + a default story
|
||
4. **Implementation (green)** — make the component render and pass tests
|
||
- **Reviewer agent** uses the existing Storybook MCP at `http://localhost:6006/mcp` to read existing components, list variants, and verify story coverage against the task's AC. Visual diff verdicts come from the Playwright screenshot CI step.
|
||
- **Story split for page-level features:**
|
||
```
|
||
Epic: auth-v1
|
||
├── Story (user): auth.signUp use case ← backend slices
|
||
├── Story (technical): SignUpForm component(s) ← depends-on: signUp use case
|
||
└── Story (technical): /sign-up page composition + E2E ← depends-on: SignUpForm
|
||
```
|
||
Each `depends-on` edge enforces sequential dispatch by the orchestrator.
|
||
|
||
### Infrastructure (see `docs/guides/infrastructure-work-shape.md`)
|
||
|
||
- **ADRs precede infrastructure work** the same way PRDs precede feature work — decision first, code second. ADRs live at `docs/adr/NNN-<slug>.md`.
|
||
- **Two categories:**
|
||
- **New optional core package** — `pnpm turbo gen core-package <name>`. Generator + conformance already accommodate (via `requiredCores` in manifests). No conformance extensions required.
|
||
- **New infrastructure layer** (Redis, CDN, alternative CMS, additional message bus, …) — ADR + integration PRD + multiple stories.
|
||
- **ADR authoring flow:**
|
||
1. Human runs `pnpm work adr-new "<one-line proposal>"`
|
||
2. **Dedicated ADR elicitation skill** interviews the human on Context / Drivers / Considered options / Trade-offs / Decision / Consequences (similar shape to the PRD elicitation skill but distinct template + heuristics)
|
||
3. Agent drafts ADR at `docs/adr/NNN-<slug>.md` with `status: proposed`
|
||
4. Human reviews, flips to `status: accepted` (or `rejected` / `superseded`)
|
||
5. Accepted ADR(s) trigger integration PRD(s); the PRD flow proceeds normally
|
||
- **Conformance extensions for infra:**
|
||
- `core-package-shape-conforms-to-generator` — extends milestone iv's scaffold-drift check to core packages
|
||
- `required-cores-in-workspace` — manifest declarations must match `pnpm-workspace.yaml` (already in milestone iv)
|
||
|
||
Two elicitation skills now sit at the funnel mouth — one for PRDs, one for ADRs. Same interview-style intake; different templates and decision frameworks.
|
||
|
||
## Pre-commit gates
|
||
|
||
When a commit lands in the sandbox or locally:
|
||
|
||
1. **Type-check** — brand satisfaction, manifest typing
|
||
2. **Lint** — manifest ↔ code rules, in-file shape rules, pattern restrictions
|
||
3. **Conformance script** — `pnpm conformance` (boot-style assertion at static scope)
|
||
4. **Tests for changed feature** — `pnpm test --filter @repo/<feature>` passes
|
||
5. **`_state.json` ↔ markdown sync** — pre-commit regen verifies consistency
|
||
|
||
Tests for *all* features are NOT required to pass at pre-commit (that's CI's job). The gate enforces local soundness without blocking work in unaffected areas.
|
||
|
||
## Agent roles
|
||
|
||
### PRD eliciter agent
|
||
|
||
- Skill: dedicated PRD elicitation (interview-style, similar shape to `superpowers:brainstorming`)
|
||
- Inputs: short brief from human (`pnpm work prd-new "<idea>"`)
|
||
- Behavior: asks questions one at a time, builds shared understanding across Problem / Goal / Scope / Constraints / Success / Requirements
|
||
- Output: `<date>-<slug>.prd.md` with `status: draft`
|
||
- Hand-off: human reviews, flips to `status: approved`
|
||
|
||
### ADR eliciter agent
|
||
|
||
- Skill: dedicated ADR elicitation (interview-style; distinct from PRD elicitation)
|
||
- Inputs: short brief from human (`pnpm work adr-new "<proposal>"`)
|
||
- Behavior: drives the conversation across Context / Drivers / Considered options / Trade-offs / Decision / Consequences. Pushes the human to articulate alternatives explicitly before settling on a decision.
|
||
- Output: `docs/adr/NNN-<slug>.md` with `status: proposed`
|
||
- Hand-off: human reviews, flips to `status: accepted` (or `rejected` / `superseded`)
|
||
- Accepted ADRs are the trigger for downstream integration PRDs
|
||
|
||
### Decomposer agent
|
||
|
||
- Skill: structured PRD-to-epic-and-stories decomposition
|
||
- Inputs: a PRD file with `status: approved`
|
||
- Behavior: produces `_epic.md` and one `_story.md` per requirement, with story-level AC bullets that hint at task decomposition
|
||
- **Default scope: stories only.** Task-level decomposition is a second pass: `pnpm work decompose-tasks <story>`
|
||
- Does NOT write `_state.json` directly (orchestrator does that on next dispatch tick)
|
||
|
||
### Implementer agent
|
||
|
||
- Sandcastle dispatch with `implementer.prompt.md`
|
||
- Inputs: a single task markdown file (full context)
|
||
- Behavior: writes code + tests to satisfy the AC; runs `pnpm test --filter` and `pnpm conformance` locally; commits; pushes the sandbox branch
|
||
- **Read-only on task markdown.** Returns structured output via sandcastle:
|
||
```json
|
||
{
|
||
"status": "complete" | "blocked" | "needs-clarification",
|
||
"ac_satisfied": [0, 1, 2, 3],
|
||
"files_changed": ["packages/.../with-span.ts", "..."],
|
||
"commit_sha": "abc123",
|
||
"notes": "..."
|
||
}
|
||
```
|
||
- Does NOT edit `.task.md`, `.story.md`, `_epic.md`, or `_state.json`. The orchestrator translates the structured output into markdown checkbox flips and JSON state updates in a single post-merge commit.
|
||
|
||
### Reviewer agent
|
||
|
||
- Sandcastle dispatch with `reviewer.prompt.md`
|
||
- Inputs: task markdown + diff from implementer
|
||
- Behavior: verifies each AC bullet against the diff; checks `Out of scope` is respected; verifies tests cover AC bullets; runs `pnpm conformance` and `pnpm test --filter`
|
||
- Returns structured output:
|
||
```json
|
||
{
|
||
"decision": "approve" | "reject",
|
||
"ac_verified": [0, 1, 2, 3],
|
||
"scope_violations": [],
|
||
"notes": "..."
|
||
}
|
||
```
|
||
- Does NOT edit anything in the repo.
|
||
- **For frontend tasks**, the reviewer additionally:
|
||
- Queries the Storybook MCP (`http://localhost:6006/mcp`) to verify story coverage and inspect rendered output
|
||
- Treats the Playwright screenshot CI step's verdict as a required input — unapproved visual diffs trigger `reject`
|
||
|
||
### Orchestrator
|
||
|
||
- Plain TypeScript script (`scripts/work-dispatch.ts`)
|
||
- Reads `_state.json` to find ready tasks (all deps `done`)
|
||
- For each ready task:
|
||
1. Marks task `status: in-progress`, regenerates `_state.json`, commits the marker
|
||
2. Dispatches sandcastle implementer
|
||
3. On implementer return: dispatches sandcastle reviewer with task + diff
|
||
4. On reviewer `approve`: serial-merges to `main`, in one commit flips task checkbox, increments parent story checkbox if all tasks done, increments parent epic checkbox if all stories done, regenerates `_state.json`
|
||
5. On reviewer `reject`: appends reviewer notes to the task's "Reviewer notes" section, increments `attempts.implementer`, re-dispatches (subject to `max-attempts`)
|
||
6. On `attempts.implementer >= max-attempts`: marks `status: escalated`, posts a summary, stops dispatching
|
||
- Continues until no ready tasks remain
|
||
|
||
## Sandcastle config
|
||
|
||
`.sandcastle/Dockerfile` **extends the existing CI image**. To be identified during the work-system-v1 epic. Must include:
|
||
|
||
- Node + pnpm at repo's pinned versions
|
||
- `pnpm install --frozen-lockfile` baked in
|
||
- Access to `pnpm conformance`, `pnpm test`, `pnpm lint`, `pnpm typecheck`
|
||
- Git config for agent commits
|
||
|
||
Prompt templates use sandcastle's `{{VAR}}` substitution + `` !`cmd` `` injection:
|
||
|
||
- `implementer.prompt.md` uses `{{TASK_FILE_CONTENT}}` and `` !`git log -1 --oneline` `` for context
|
||
- `reviewer.prompt.md` uses `{{TASK_FILE_CONTENT}}` + `{{DIFF}}`
|
||
- `decomposer.prompt.md` uses `{{PRD_FILE_CONTENT}}`
|
||
- `prd-eliciter.prompt.md` uses `{{INITIAL_BRIEF}}` and runs the interview loop
|
||
|
||
Branch strategy: per-task feature branch (`task/<task-id>`), merged sequentially to `main` by the orchestrator.
|
||
|
||
## Phasing — conformance-first bootstrap
|
||
|
||
### Phase 1 — Conformance system (human-driven)
|
||
|
||
Build the conformance system manually. `docs/work/conformance-system-v1/` markdown files capture the work (practising the task format on real work) but no `_state.json`, no sandcastle dispatch, no orchestrator.
|
||
|
||
Stories in order (each itself a vertical slice — system code + generator update + doc update + applied to one feature):
|
||
|
||
1. **defineFeature helper + Instrumented brand** — applied to `auth.signIn`
|
||
2. **Captured + Audited brands** — wrappers updated
|
||
3. **`assertConformance` + boot wiring** — rolled to all three apps
|
||
4. **AST-aware ESLint rules** — 5–6 new type-aware rules
|
||
5. **CI drift gate** (`pnpm conformance`)
|
||
6. **Generator emits manifest + contracts + test stubs**
|
||
7. **Documentation rewrite** — agent-workflow.md, CLAUDE.md, AGENTS.md, tdd-workflow.md
|
||
8. **Migrate auth feature** to the new pattern (reference)
|
||
|
||
### Phase 2 — Work system (human-driven, bootstraps automation)
|
||
|
||
Once conformance is in place, build the dispatch substrate:
|
||
|
||
1. `docs/work/` skeleton, README, templates
|
||
2. `_state.json` schema + `pnpm work rebuild-state`
|
||
3. Orchestrator script + DAG + retry-cap logic
|
||
4. `.sandcastle/` config (extends existing CI image)
|
||
5. PRD elicitation skill
|
||
6. ADR elicitation skill (separate skill, similar interview shape)
|
||
7. Decomposer agent + prompts
|
||
8. Implementer + reviewer prompts (with Storybook MCP wiring for frontend reviewer)
|
||
9. `pnpm work` CLI surface (including `work adr-new`)
|
||
10. Pre-commit hooks (state regen, conformance gate)
|
||
11. Playwright screenshot test infrastructure (CI gate for frontend tasks)
|
||
|
||
### Phase 3 — Migration + future work (dispatch-driven)
|
||
|
||
With both systems in place, remaining feature migrations and all future work flows through sandcastle:
|
||
|
||
- Migrate `blog`, `media`, `navigation`, `marketing-pages` (one story each)
|
||
- All new features authored via PRD → decompose → dispatch loop
|
||
|
||
## Deferred decisions
|
||
|
||
These are explicitly deferred until the phase that needs them:
|
||
|
||
1. **Existing `task-create` skill (ClickUp mirror)** — coexist or retire. Decide during Phase 2 or after. Frontmatter is open-ended so `clickup-id` can be added later if needed.
|
||
2. **Existing CI Docker image identity** — identify and document during the `.sandcastle/Dockerfile` story in Phase 2.
|
||
3. **Per-epic state files vs. one global** — start with one global `_state.json`. Move to per-epic only if serialized merges become a throughput bottleneck.
|
||
4. **Custom git merge driver for `_state.json`** — start without; serialized merges should suffice. Add if needed.
|
||
|
||
## Open questions (to revisit during implementation)
|
||
|
||
- **Q1: Single manifest registry per app, or per-feature?** (From the explainer §10.) Lean: per-feature, with a tiny app-side aggregator.
|
||
- **Q2: How much of the manifest is generated vs hand-written?** Lean: humans edit; ESLint flags mismatches without auto-fixing.
|
||
- **Q3: Inline symbol declaration in manifest, or registry mapping?** Lean: registry holds the mapping, manifest stays content-only.
|
||
- **Q4: What happens when an optional core is absent?** Lean: typed surface gates the field — `audits: readonly never[]` when `core-audit` is unbound.
|
||
- **Q5: Escape hatch for legitimate exceptions?** Lean: `// @conformance-skip: <rule> — <reason>` comment honoured by ESLint + boot assertion, with allowlist growth gated in CI.
|
||
|
||
## Acceptance criteria (for this whole design)
|
||
|
||
This design is "done" when:
|
||
|
||
- [ ] `docs/work/` exists with templates, `_state.json` schema, README
|
||
- [ ] Conformance system v1 is implemented through all four enforcement layers
|
||
- [ ] All five feature packages have manifests
|
||
- [ ] All three apps run `assertConformance` at boot
|
||
- [ ] `pnpm conformance` is a CI gate
|
||
- [ ] `turbo gen feature` emits manifest + contracts + test stubs
|
||
- [ ] `.sandcastle/` config exists with implementer/reviewer/decomposer/eliciter prompts
|
||
- [ ] PRD elicitation skill exists and is invocable
|
||
- [ ] ADR elicitation skill exists and is invocable
|
||
- [ ] Orchestrator (`pnpm work dispatch`) runs end-to-end on a real task
|
||
- [ ] Frontend ESLint rules cover atomic-tier direction and story/test sibling presence
|
||
- [ ] Playwright screenshot tests run as a CI gate for frontend work
|
||
- [ ] Documentation reflects the manifest-first workflow (CLAUDE.md, AGENTS.md, `docs/guides/`)
|
||
- [ ] Frontend + infrastructure work-shape guides exist at `docs/guides/`
|
||
|
||
## References
|
||
|
||
- [Feature conformance explainer (interactive)](./feature-conformance-explainer.html)
|
||
- [Vertical feature spec](./vertical-feature-spec.md)
|
||
- [Template tiers](./template-tiers.md)
|
||
- [Frontend work shape guide](../guides/frontend-work-shape.md)
|
||
- [Infrastructure work shape guide](../guides/infrastructure-work-shape.md)
|
||
- [TDD workflow guide](../guides/tdd-workflow.md)
|
||
- [Scaffolding a feature guide](../guides/scaffolding-a-feature.md)
|
||
- [Adding a feature guide](../guides/adding-a-feature.md)
|
||
- [Sandcastle (orchestration library)](https://github.com/mattpocock/sandcastle)
|