Bring docs/architecture/ in line with the current repo: - feature-conformance-explainer.html: drop the "proposed / not yet implemented" framing — the system is shipped. Four enforcement points become five (adds `pnpm fallow` as the whole-codebase audit). Manifest playground shows `coverage`, `analyticsEvents`, `rateLimit`, `requiresConsent`. Milestone / anchor / open-question sections kept but marked historical. - agent-first-workflow-and-conformance.md: four → five enforcement layers; layer table gains the Fallow row. - di-explainer.html: bind-production sample rewritten to show wireUseCase() + assertFeatureConformance() + the full wrapper stack (span → capture → audit? → analytics? → consent? → rateLimit?). - data-flow-explainer.html: same bind-production refresh for the data-flow narrative. - audit-and-compliance-explainer.html: AuditAction enum 6 → 10 values (CONSENT_GRANT / WITHDRAW / RESTRICT / UNRESTRICT); BindProductionContext example gains analytics, consentFactory, rateLimit. - vertical-feature-spec.md: §5 layout lists the 8 optional cores plus core-testing; §9.5 hedges the turbo.json snippet against the live file; §10.4 drops the dated "360 tests" metric for the ADR-020 coverage architecture; §11 gains a historical lead-in pointing at docs/decisions/ as the canonical 25-ADR set.
31 KiB
title, status, created, authors, related
| title | status | created | authors | related | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Agent-first development workflow + feature conformance | design | 2026-05-12 |
|
|
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:
- The feature-conformance enforcement system that gives agents tight, layered, machine-readable feedback on architectural drift.
- The agent-first workflow — manifest → contracts → tests → code — that the conformance system enforces.
- The local task system at
docs/work/that holds PRDs, epics, stories, and tasks as markdown, parseable by both humans and agents. - 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. 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
---
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:
- Human runs
pnpm work prd-new "<one-line idea>" - 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 - Agent drafts PRD with
status: draft - Human reviews, edits, flips to
status: approved - Decomposer refuses to run on
draftPRDs.
Epic — <epic-slug>/_epic.md
---
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
---
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
---
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
---
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/_system/_state.json
A derived, committed, orchestrator-written index. Markdown is source of truth; _state.json is a fast-to-query mirror.
{
"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
- Committed to git. Audit trail visible in PRs.
- Single writer: orchestrator + pre-commit hook only. No agent writes it directly.
- Derived from markdown. Regenerable any time via
pnpm work rebuild-state. - Canonical formatting. Sorted keys, stable indentation, no trailing whitespace. Pre-commit normalizes via Prettier.
- Merges serialized. Orchestrator merges PRs one at a time. Parallel implementation in sandboxes is fine; only merge step is sequential.
- Pre-commit regen. When any
.task.md/.story.md/_epic.mdis staged, the hook regenerates_state.jsonfrom the markdown, re-stages it, and lets the commit proceed. The hook only blocks the commit if regeneration itself fails (e.g. malformed frontmatter, brokendepends-onreference). 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 five enforcement layers (detailed in 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 |
Fallow audit (pnpm fallow) |
~30–60s | whole-codebase — dead exports, duplicate code, circular deps, complexity hotspots |
How conformance interacts with tasks
When a task adds an audit emission (e.g. audits: ["user.created"]):
- Agent edits
feature.manifest.ts - The binding's branded slot type now demands
Audited<F>— TS2322 if the wrapper is missing - Agent adds
withAudit(...)inbind-production.ts→ TS goes quiet - Agent adds
auditLogger.log(...)in the use-case factory → ESLint goes quiet - Pre-commit
pnpm conformanceconfirms all five layers pass - 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:
- Manifest — declare the use case (or update audits/publishes/consumes if the task adds them). Pure declaration.
- Contracts —
xInputSchema,xOutputSchema,IXUseCasetype alias in the use-case file. Factory body throws"not implemented"if not yet written. - Tests (red) — import contracts; write failing assertions that match the AC bullet.
- 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
playfunction. Story files become the shared visual contract between human, implementer, and reviewer. - Two test layers:
- Component tests (Vitest + Testing Library, or
playon stories) — pre-commit gate, <5s - Visual regression via Playwright screenshot tests — CI gate, 30–120s, blocks PR merge on unapproved visual diffs
- Component tests (Vitest + Testing Library, or
- Adapted four-step ordering for a pure UI slice:
- Story file (the visual spec — analogous to the manifest entry for backend work)
- Contracts — props interface, variant types
- Tests (red) — component test + a default story
- Implementation (green) — make the component render and pass tests
- Reviewer agent uses the existing Storybook MCP at
http://localhost:6006/mcpto 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:
Each
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: SignUpFormdepends-onedge 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 (viarequiredCoresin manifests). No conformance extensions required. - New infrastructure layer (Redis, CDN, alternative CMS, additional message bus, …) — ADR + integration PRD + multiple stories.
- New optional core package —
- ADR authoring flow:
- Human runs
pnpm work adr-new "<one-line proposal>" - 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)
- Agent drafts ADR at
docs/adr/NNN-<slug>.mdwithstatus: proposed - Human reviews, flips to
status: accepted(orrejected/superseded) - Accepted ADR(s) trigger integration PRD(s); the PRD flow proceeds normally
- Human runs
- Conformance extensions for infra:
core-package-shape-conforms-to-generator— extends milestone iv's scaffold-drift check to core packagesrequired-cores-in-workspace— manifest declarations must matchpnpm-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:
- Type-check — brand satisfaction, manifest typing
- Lint — manifest ↔ code rules, in-file shape rules, pattern restrictions
- Conformance script —
pnpm conformance(boot-style assertion at static scope) - Tests for changed feature —
pnpm test --filter @repo/<feature>passes _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.mdwithstatus: 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>.mdwithstatus: proposed - Hand-off: human reviews, flips to
status: accepted(orrejected/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.mdand one_story.mdper 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.jsondirectly (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 --filterandpnpm conformancelocally; commits; pushes the sandbox branch - Read-only on task markdown. Returns structured output via sandcastle:
{ "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 scopeis respected; verifies tests cover AC bullets; runspnpm conformanceandpnpm test --filter - Returns structured output:
{ "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
- Queries the Storybook MCP (
Orchestrator
- Plain TypeScript script (
scripts/work-dispatch.ts) - Reads
_state.jsonto find ready tasks (all depsdone) - For each ready task:
- Marks task
status: in-progress, regenerates_state.json, commits the marker - Dispatches sandcastle implementer
- On implementer return: dispatches sandcastle reviewer with task + diff
- On reviewer
approve: serial-merges tomain, 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 - On reviewer
reject: appends reviewer notes to the task's "Reviewer notes" section, incrementsattempts.implementer, re-dispatches (subject tomax-attempts) - On
attempts.implementer >= max-attempts: marksstatus: escalated, posts a summary, stops dispatching
- Marks task
- 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-lockfilebaked 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.mduses{{TASK_FILE_CONTENT}}and!`git log -1 --oneline`for contextreviewer.prompt.mduses{{TASK_FILE_CONTENT}}+{{DIFF}}decomposer.prompt.mduses{{PRD_FILE_CONTENT}}prd-eliciter.prompt.mduses{{INITIAL_BRIEF}}and runs the interview loop
Branch strategy: per-task feature branch (task/<task-id>), merged sequentially to main by the orchestrator.
Bootstrap order — conformance first
Tier 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):
- defineFeature helper + Instrumented brand — applied to
auth.signIn - Captured + Audited brands — wrappers updated
assertConformance+ boot wiring — rolled to all three apps- AST-aware ESLint rules — 5–6 new type-aware rules
- CI drift gate (
pnpm conformance) - Generator emits manifest + contracts + test stubs
- Documentation rewrite — agent-workflow.md, CLAUDE.md, AGENTS.md, tdd-workflow.md
- Migrate auth feature to the new pattern (reference)
Tier 2 — Work system (human-driven, bootstraps automation)
Once conformance is in place, build the dispatch substrate:
docs/work/skeleton, README, templates_state.jsonschema +pnpm work rebuild-state- Orchestrator script + DAG + retry-cap logic
.sandcastle/config (extends existing CI image)- PRD elicitation skill
- ADR elicitation skill (separate skill, similar interview shape)
- Decomposer agent + prompts
- Implementer + reviewer prompts (with Storybook MCP wiring for frontend reviewer)
pnpm workCLI surface (includingwork adr-new)- Pre-commit hooks (state regen, conformance gate)
- Playwright screenshot test infrastructure (CI gate for frontend tasks)
Tier 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 tier that needs them:
- Existing
task-createskill (ClickUp mirror) — coexist or retire. Decide once the work system is in place. Frontmatter is open-ended soclickup-idcan be added later if needed. - Existing CI Docker image identity — identify and document during the
.sandcastle/Dockerfilestory. - 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. - 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[]whencore-auditis 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.jsonschema, README- Conformance system v1 is implemented through all five enforcement layers
- All five feature packages have manifests
- All three apps run
assertConformanceat boot pnpm conformanceis a CI gateturbo gen featureemits 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/