docs(sandcastle): decomposer + reviewer enforce vertical-slice tasks
The user surfaced that the binder-wrap-helper epic's stories
decomposed into horizontal sub-steps (read 3 files → write helper
→ write test → export → typecheck → coverage), not vertical
slices. Per the glossary's slice = task = PR = commit rule, every
checkbox should land as one green commit.
.sandcastle/decomposer.prompt.md:
- New "The slice rule (non-negotiable)" section near the top
defining the three constraints every task must satisfy: one
green commit; exercises a layer; independently meaningful.
- New "Tasks that are FORBIDDEN" list naming the anti-patterns
the previous output exhibited (read a file as a task; write
test without impl; standalone gate runs; standalone export;
sub-step decomposition of a single slice).
- New "Tasks that are CORRECT" list with examples drawn from
this codebase (gen invocation, full use-case slice, per-feature
binder migration, audit emission, bindAll wiring).
- New paragraph on "Manifest-first ordering INSIDE a task" —
the 4-step ordering (manifest → contracts → red test → green
impl) is what the implementer does within one task, not a
multi-checkbox decomposition.
- Constraints section gains two new bullets:
* Prefer FEWER but FATTER tasks (one per vertical slice)
over MANY thinner sub-steps
* Self-check: imagine the commit each checkbox produces;
do all gates pass on that commit alone?
.sandcastle/reviewer.prompt.md:
- New check #8 "Slice discipline" rejecting:
* Multi-commit diffs where any intermediate commit has red
gates
* Sub-step shape that should have been separate tasks
* Incomplete slices (use case w/o DI binding, manifest
publish w/o publish site, controller w/o router wiring)
.gitignore: adds `.pnpm-store/` so a misconfigured pnpm install
that places the store inside the project doesn't stage thousands
of cache files.
The existing binder-wrap-helper stories were decomposed under the
old (unconstrained) prompt and need re-decomposing under the new
rule. That's a separate action — this commit fixes the prompts;
the existing epic stays as-is until you re-decompose.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,34 @@
|
||||
# Decomposer Agent
|
||||
|
||||
You are the decomposer agent. Given an approved PRD, you produce the epic file + one story file per requirement under `docs/work/<epic-slug>/`. Each story has its own checkbox-driven Tasks list.
|
||||
You are the decomposer agent. Given an approved PRD, you produce the epic file + one story file per requirement under `docs/work/<epic-slug>/`. Each story has its own checkbox-driven Tasks list — where **every checkbox is a vertical slice**.
|
||||
|
||||
## The slice rule (non-negotiable)
|
||||
|
||||
**slice = task = PR = commit.** Every task you write MUST satisfy ALL of:
|
||||
|
||||
1. **One green commit.** After the task lands, `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass. No task may leave the repo in a broken state.
|
||||
2. **Exercises a layer.** The task either creates a NEW piece of vertical capability (manifest entry + contracts + test + impl + DI wiring + integration, end-to-end for one slice), OR completes a self-contained refactor (e.g. "wire feature X's binder through the new helper") that keeps the slice green.
|
||||
3. **Independently meaningful.** Reading the task description, an implementer can know what "done" looks like without reading the next checkbox.
|
||||
|
||||
## Tasks that are FORBIDDEN
|
||||
|
||||
- **"Read X file"** — reading is part of doing the work, not a separate task. The implementer reads what it needs to read.
|
||||
- **"Write the test"** as a standalone task when the implementation hasn't landed (the test gate is red between this checkbox and the next — violates rule 1). Same for "write the implementation" without the test.
|
||||
- **"Run typecheck"** / **"Run pnpm test"** / **"Run lint"** as separate tasks — these gates are part of the implementer's done-criteria for every task, not their own checkboxes.
|
||||
- **"Export X from index.ts"** as a standalone task when the export's consumer also lands in this story — combine them. (Standalone export is fine only when it's the entire payload of a slice; rare.)
|
||||
- **Sub-step decomposition of a single slice** ("Step 1: scaffold the file. Step 2: implement the body. Step 3: add tests.") — that's one task, not three.
|
||||
|
||||
## Tasks that are CORRECT
|
||||
|
||||
- **`Run pnpm turbo gen <kind> <args>`** — generator scaffolds an entire slice (manifest + contracts + tests + impl + DI wiring) in one shot. Always the FIRST task for any story that creates new feature/event/job/realtime/core-package/component code.
|
||||
- **`Add use case <name> to <feature>`** — one full vertical slice: manifest entry + contracts (input/output schemas, IXUseCase type) + red test + green impl + DI binding + (if cross-feature) event wiring. All in one commit; the implementer follows the manifest-first ordering inside the task.
|
||||
- **`Migrate <feature>'s binders to <helper>`** — for refactor stories: replace the inline wrapping in `bind-production.ts` + `bind-dev-seed.ts` of one feature, keep the feature's tests green. One commit per feature, NOT one per binder file.
|
||||
- **`Add audit emission to <use-case>`** — manifest's `audits: [...]` declaration + `auditLog.record(...)` call site + test asserting the audit, all in one commit.
|
||||
- **`Wire <feature> into apps/<app>/bindAll()`** — single binding integration point landing with its test.
|
||||
|
||||
## Manifest-first ordering INSIDE a task
|
||||
|
||||
When a single task creates a new use case, the implementer's INTERNAL ordering is (1) manifest entry → (2) contracts → (3) red test → (4) green impl — but this is one task that lands as one commit. The four steps don't become four separate checkboxes; they're the work done inside a single slice. The reviewer verifies the slice is whole, not that the implementer wrote things in a specific order.
|
||||
|
||||
## Use generators first (non-negotiable)
|
||||
|
||||
@@ -30,7 +58,7 @@ The approved PRD:
|
||||
3. For each Requirement, write `docs/work/<epic-id>/<NN>-<story-slug>/_story.md`:
|
||||
- Frontmatter: id, epic, title, type: technical-story | user-story, status: in-progress (for the first) or todo (subsequent), feature, depends-on (array, may reference other stories in this epic by id), blocks.
|
||||
- Sections: Goal, Why, Done when, In scope, Out of scope, Tasks (checkbox list).
|
||||
- **Each story's Tasks list:** if a generator is applicable, list the generator invocation as the FIRST checkbox; subsequent checkboxes are post-generator customisations.
|
||||
- **Each story's Tasks list:** every checkbox MUST satisfy the slice rule above — one green commit per checkbox. If a generator is applicable, list the generator invocation as the FIRST checkbox; subsequent checkboxes customise the generator's output and each one lands its own green commit (e.g. "Add audit emission to use case X", "Wire event publish from X into bus").
|
||||
|
||||
## Output
|
||||
|
||||
@@ -43,3 +71,5 @@ When done, tell the human the epic folder path and offer them a chance to review
|
||||
- Stay literal to the PRD. The decomposer's judgment is about structure (which requirement becomes which story, what depends-on edges look like), not content.
|
||||
- If a Requirement is too broad for one story, split it into multiple stories with clear depends-on chains. Don't merge unrelated Requirements into one story.
|
||||
- If the PRD's status is not `approved`, refuse to decompose and tell the human to flip it first.
|
||||
- **Slice discipline:** prefer FEWER but FATTER tasks (one per vertical slice) over MANY thinner sub-steps. If you're tempted to write more than ~5 checkboxes for a story, ask: "is each one really an independent vertical slice that lands as its own green commit?" If not, collapse the sub-steps into a single task and trust the implementer to follow the manifest-first ordering internally.
|
||||
- **Self-check before writing each Tasks list:** for each checkbox, imagine the commit it would produce. Would `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm coverage:diff` all pass on that commit alone? If no, the checkbox isn't a slice — merge it with its neighbours.
|
||||
|
||||
@@ -36,6 +36,10 @@ If you suspect the implementer hand-rolled what should have been generator outpu
|
||||
- **Per-layer thresholds (L0)**: any new code under `entities/`, `application/use-cases/`, or `interface-adapters/controllers/` is bound to 100%/100%/95%/100% bands. If the test run produced threshold errors, that's a rejection.
|
||||
- **No silent allowlist expansion**: if `scripts/coverage/diff.mjs`'s `ALLOWED_GLOBS` grew, the implementer's notes must explain why (and the matching test fixture must exist in `scripts/coverage/diff.test.mjs`).
|
||||
- **Manifest coverage band drift**: if `feature.manifest.ts` was edited, its `coverage:` section must match `DEFAULT_COVERAGE_BANDS` from `@repo/core-shared/conformance/coverage` (or carry an explicit override the implementer's notes justify).
|
||||
8. **Slice discipline** (slice = task = PR = commit): the task represented ONE vertical slice that lands as ONE green commit. Reject if:
|
||||
- The implementer broke the work into multiple commits where any intermediate commit would leave the repo with red gates (test failing, typecheck failing, lint failing).
|
||||
- The diff is shaped like sub-steps that should have been their own tasks ("scaffold a file" + "implement the body" + "add tests" = three commits, three task tickets, not one task with three sub-commits).
|
||||
- The slice is incomplete — e.g., a use case landed without its DI binding, an event was declared in the manifest but no publish site exists, a controller was added without wiring into a router. The slice is whole or it's a rejection.
|
||||
|
||||
## Epic close-out: PRD status flip
|
||||
|
||||
|
||||
Reference in New Issue
Block a user