@ericrisco/rsc 3.7.5 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +16 -9
  2. package/manifest.json +46 -39
  3. package/package.json +1 -1
  4. package/scripts/lib/domains.js +1 -1
  5. package/scripts/lib/onboarding-apply.js +1 -1
  6. package/scripts/lib/onboarding.js +7 -7
  7. package/scripts/lib/retired-skills.js +4 -0
  8. package/scripts/lib/review-findings.js +3 -0
  9. package/scripts/lib/review-ledger.js +185 -0
  10. package/scripts/lib/risk.js +65 -0
  11. package/scripts/lib/spec-gate.js +10 -1
  12. package/scripts/rsc.js +91 -6
  13. package/skills/analyze/SKILL.md +3 -3
  14. package/skills/asdd/SKILL.md +159 -0
  15. package/skills/{sdd → asdd}/evals/README.md +10 -10
  16. package/skills/{sdd → asdd}/evals/cases.yaml +4 -4
  17. package/skills/{sdd → asdd}/references/model-routing.md +2 -2
  18. package/skills/building-agents/SKILL.md +1 -1
  19. package/skills/clarify/SKILL.md +48 -55
  20. package/skills/clarify/evals/cases.yaml +6 -7
  21. package/skills/constitution/SKILL.md +21 -8
  22. package/skills/constitution/evals/README.md +1 -1
  23. package/skills/debug/SKILL.md +1 -1
  24. package/skills/fastapi/SKILL.md +1 -1
  25. package/skills/flutter/SKILL.md +1 -1
  26. package/skills/ftd/SKILL.md +26 -10
  27. package/skills/ftd/evals/cases.yaml +1 -1
  28. package/skills/ftd/evals/routing.json +4 -4
  29. package/skills/go/SKILL.md +1 -1
  30. package/skills/implement/SKILL.md +126 -113
  31. package/skills/implement/evals/cases.yaml +4 -2
  32. package/skills/implement/references/per-task-review.md +19 -26
  33. package/skills/nextjs/SKILL.md +1 -1
  34. package/skills/parallel/SKILL.md +1 -1
  35. package/skills/plan/SKILL.md +2 -2
  36. package/skills/postgresdb/SKILL.md +1 -1
  37. package/skills/react/SKILL.md +1 -1
  38. package/skills/review/SKILL.md +58 -31
  39. package/skills/sdd-init/SKILL.md +6 -6
  40. package/skills/ship/SKILL.md +38 -18
  41. package/skills/specify/SKILL.md +32 -33
  42. package/skills/specify/evals/cases.yaml +1 -1
  43. package/skills/specify/references/spec-template.md +27 -5
  44. package/skills/suggest/SKILL.md +4 -4
  45. package/skills/tasks/SKILL.md +37 -33
  46. package/skills/tasks/evals/README.md +1 -1
  47. package/skills/tasks/evals/cases.yaml +2 -2
  48. package/skills/verify/SKILL.md +30 -15
  49. package/skills/verify/evals/README.md +2 -2
  50. package/skills/verify/evals/cases.yaml +1 -0
  51. package/skills/worktrees/SKILL.md +1 -1
  52. package/targets/clone-bootstrap.mjs +1 -1
  53. package/targets/hook-once.mjs +2 -2
  54. package/targets/sello.mjs +1 -1
  55. package/skills/sdd/SKILL.md +0 -250
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: review
3
- description: "Use when a green diff needs adversarial judgment in the rsc SDD chain, between verify and ship — give a review keyed to the spec/plan/constitution in 02-DOCS/wiki/sdd/, or receive one and verify each comment before agreeing. NOT running lint/type/tests (that is `verify`), NOT the spec-less standalone pass (that is `code-review`), NOT merging (that is `ship`)."
4
- tags: [sdd, review, code-review]
3
+ description: "Use when a diff needs adversarial judgment in rsc ASDD — per task inside implement's loop (depth set by `rsc risk`: none, one fresh-context reviewer, or the three-refuter panel) or as the closing review of the whole branch against the spec — keyed to the spec/plan/constitution in 02-DOCS/wiki/sdd/ and the project's REVIEW.md; or receive a review and verify each comment before agreeing. Converges via `rsc review ledger` (max 3 rounds, no repeated finding). NOT running lint/type/tests (that is `verify`), NOT the spec-less standalone pass (that is `code-review`), NOT merging (that is `ship`)."
4
+ tags: [asdd, review, code-review, risk]
5
5
  recommends: [ship]
6
6
  profiles: [core, full]
7
7
  origin: risco
@@ -9,7 +9,10 @@ origin: risco
9
9
 
10
10
  # Review — adversarial code review, both directions
11
11
 
12
- Review is the **penultimate gate** of the rsc SDD chain: `constitution → specify → clarify → plan → tasks → analyze → implement → verify → **review** → ship`. By the time a diff reaches review, the `verify` phase has already proven that lint, types, tests and the stack `verify.sh` are green. Review answers a different question: **is the green diff actually correct, well-scoped, and faithful to the spec — or did it pass for the wrong reasons?**
12
+ Tests prove the code works, not that it does what was asked. Review answers that other question: **is the diff correct, well-scoped and faithful to the spec — or did it pass for the wrong reasons?** In ASDD (`../asdd/SKILL.md`) it runs in two modes:
13
+
14
+ - **Per task** — called from `implement`'s loop after each task's self-check. Depth comes from `npx @ericrisco/rsc risk [--base <ref>] [paths…]` (same path table as the sello, works with the sello off): **tier 0** → no review; **tier 1** → one fresh-context reviewer (a subagent that never saw the implementation conversation); **tier 2** → the three-refuter panel below, and the branch will wait for human approval at ship.
15
+ - **Closing review** — once, before `ship`: the whole branch against the spec. Forty individually-correct tasks can still fail to be the feature together: a criterion nobody owned, two tasks that each assumed the other handled an edge, scope that crept in a slice at a time. Read the spec's acceptance criteria and "must respect" list against `git diff <base>...HEAD`, not the task reviews.
13
16
 
14
17
  This skill owns **two roles that share one discipline**:
15
18
 
@@ -22,9 +25,10 @@ The discipline is the same in both directions: **every finding and every rebutta
22
25
 
23
26
  ## Read these first
24
27
 
25
- 1. `02-DOCS/wiki/sdd/specs/<slug>.md` and `02-DOCS/wiki/sdd/plans/<slug>.md` — what the diff was *supposed* to do. A review with no spec is a review of vibes.
26
- 2. `02-DOCS/wiki/sdd/constitution.md` — the project's non-negotiables (stack canon, quality bars, conventions). Constitution violations are findings even when the code "works".
27
- 3. `02-DOCS/wiki/harness/user-profile.md` — `technical_level`, the register (see "How a review reads" below).
28
+ 1. `REVIEW.md` at the project root, if present (drafted by `constitution`): what failure looks like in this repo, paths to skip, the nit cap, and that the summary starts with a count ("3 findings: 1 blocker, 2 nits"). Where it differs from this skill's defaults, it wins.
29
+ 2. `02-DOCS/wiki/sdd/specs/<slug>.md` and `02-DOCS/wiki/sdd/plans/<slug>.md` — what the diff was *supposed* to do. A review with no spec is a review of vibes.
30
+ 3. `02-DOCS/wiki/sdd/constitution.md` — the project's non-negotiables (stack canon, quality bars, conventions). Constitution violations are findings even when the code "works".
31
+ 4. `02-DOCS/wiki/harness/user-profile.md` — `technical_level`, the register (see "How a review reads" below).
28
32
 
29
33
  If there is no spec/plan (someone jumped straight to code), say so and review against the constitution + the diff's own stated intent. Don't pretend a spec exists.
30
34
 
@@ -126,12 +130,14 @@ Every state transition is deterministic CLI, never tokens:
126
130
  gap deliberately with `--accept-partial-lenses` (it is recorded). Every approval also appends to
127
131
  `.rsc/sello-log.jsonl`, so what shipped under which verdict survives the next freeze.
128
132
 
129
- **Lenses by risk tier** — tier 0 never reaches you (the gate passes docs/copy silently);
130
- tier 1 → run the single most relevant pass from the table above yourself; tier 2 → dispatch
131
- **three parallel fresh-context subagents** (`refuter-correctness` · `refuter-security` ·
132
- `refuter-tests`), each told
133
- to *refute* readiness, not confirm it. Fresh context is the point: a reviewer who inherits the
134
- implementer's context inherits its blind spots.
133
+ ### Depth by risk — who reviews
134
+
135
+ `rsc risk` and `sello freeze` read the same path table, so the tier is the same with the sello on
136
+ or off. Tier 0 never reaches you (docs/copy pass silently); tier 1 → **one fresh-context
137
+ reviewer** running the most relevant pass from the table above; tier 2 → **three parallel
138
+ fresh-context subagents** (`refuter-correctness` · `refuter-security` · `refuter-tests`), each
139
+ told to *refute* readiness, not confirm it. Fresh context is the point: a reviewer who inherits the
140
+ implementer's context inherits its blind spots. The closing review is always at least tier 1.
135
141
 
136
142
  **Give each refuter exactly four inputs — no more, no less.** The diff alone is not enough, and
137
143
  that is not a detail: with only the diff, the one failure class no test suite can structurally
@@ -182,8 +188,8 @@ steps, in this order:
182
188
 
183
189
  Report the consolidated count honestly ("11 reported → 4 unique → 2 confirmed"), and list what was
184
190
  refuted and why: a refuted finding hidden from the author is a review nobody can audit. At tier 1
185
- (one pass, run by you) there is nothing to group, but a blocker still goes to `finding-verifier`
186
- before it blocks.
191
+ (one reviewer) there is nothing to group, but a blocker still goes to `finding-verifier` before it
192
+ blocks.
187
193
 
188
194
  **A finding blocks only if it survives all three filters** — no exceptions, and eagerness to
189
195
  find something is not evidence:
@@ -195,11 +201,33 @@ find something is not evidence:
195
201
  3. **Evidence** — `finding-verifier` returned **CONFIRMED** with its proof. REFUTED is dropped (with
196
202
  its reason in the report); UNPROVEN is a `[question]`, and questions don't block.
197
203
 
198
- **Fixing a blocker is budgeted, one attempt.** Before touching anything: estimate the fix and
199
- declare it — `npx @ericrisco/rsc sello budget --lines <N>`. After the fix:
200
- `sello budget-check` (over budget → justify with `--justify "…"` or shrink; an unexplained
201
- overrun is how over-engineering enters disguised as a fix). Then `sello freeze` + re-review
202
- **only the divergence**, and approve. Still broken after one attempt → stop, hand it to the human.
204
+ **Fixing a blocker is budgeted.** Before touching anything: estimate the fix and declare it —
205
+ `npx @ericrisco/rsc sello budget --lines <N>`. After the fix: `sello budget-check` (over budget →
206
+ justify with `--justify "…"` or shrink; an unexplained overrun is how over-engineering enters
207
+ disguised as a fix). Then `sello freeze` + re-review **only the divergence**, and approve. Each
208
+ fix-and-re-review is one round of the loop below.
209
+
210
+ ### Convergence — the fix loop ends
211
+
212
+ A review loop that never converges is worse than none: Anthropic saw a single PR take 50 rounds.
213
+ Four rules, enforced by `npx @ericrisco/rsc review ledger` (`.rsc/review-ledger.jsonl`) rather
214
+ than by memory:
215
+
216
+ 1. **Max 3 rounds per task.** Start each round with `review ledger round <slug>/<task>`; it exits non-zero
217
+ on the 4th. That is not "try harder": rethink the approach, or park the task in the decision
218
+ queue and continue with independent work.
219
+ 2. **From round 2, re-review only the new diff** — what changed since the last round, not the task
220
+ again from scratch.
221
+ 3. **Flag the bug class, not the line.** "Every handler in this file trusts `req.params.id`" — so
222
+ one fix covers every instance and round 3 does not find the sibling.
223
+ 4. **Never repeat a finding.** Record each round's findings with `review ledger record
224
+ <findings.json>` (the consolidate output works). A finding already seen — same file, class and
225
+ normalized title — comes back marked `repeat`; it is never re-raised as new. A repeat means the
226
+ last fix missed the class, so say that instead.
227
+
228
+ **Every finding closes with an outcome**: `review ledger outcome <id> fixed|false|intentional|irrelevant`.
229
+ `review ledger stats` then shows totals per outcome, the false rate and mean rounds per task — the
230
+ only way to tell whether a change to the reviewer made it better or worse.
203
231
 
204
232
  ---
205
233
 
@@ -276,26 +304,25 @@ In the `orient` voice: the verdict first, then the blocker list — e.g. `CHANGE
276
304
 
277
305
  ## Model tier — `heavy` (opt-in routing)
278
306
 
279
- This phase's default model tier is **`heavy`** — adversarial diff reading is where the strongest model pays off most. Routing is **off** unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`. When on: resolve this phase's tier (`models.overrides` wins over `models.phases`), map it to a model via `models.tiers`, and apply per `../sdd/references/model-routing.md` — announce the switch in one line when it differs from the session model, and dispatch any `Task`/`parallel` subagents on that model. Routing off or no profile → honor the session model silently. Never fake a switch a tool can't make; skip routing on a one-line change.
280
-
281
- ## Where this writes
307
+ This phase's default model tier is **`heavy`** — adversarial diff reading is where the strongest model pays off most. Routing is **off** unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`. When on: resolve this phase's tier (`models.overrides` wins over `models.phases`), map it to a model via `models.tiers`, and apply per `../asdd/references/model-routing.md` — announce the switch in one line when it differs from the session model, and dispatch any `Task`/`parallel` subagents on that model. Routing off or no profile → honor the session model silently. Never fake a switch a tool can't make; skip routing on a one-line change.
282
308
 
283
- Review is mostly a conversation, but two artifacts persist into the harness wiki so the knowledge model grows:
309
+ ## Where this writes — and how the reviewer learns
284
310
 
285
311
  - **Accepted/declined findings** of consequence → append to `02-DOCS/wiki/sdd/decisions.md` (e.g. "declined NPE finding on documents.py:42 — guarded by require_auth; added regression test"). This is the same append-only log `implement` writes to.
286
- - If a finding reveals a **missing constitution rule** (a defect class the project keeps hitting), propose adding it to `02-DOCS/wiki/sdd/constitution.md` so the next review catches it earlier.
312
+ - **Escapes become cases.** `npx @ericrisco/rsc review escapes [--since <ref>]` lists every revert commit and whether it has a case at `02-DOCS/wiki/sdd/review-evals/<shortsha>.md`. Write the missing ones: the diff that shipped, where the bug was, and what the reviewer should have said. Only `Revert "…"` / `revert:` commits are seen; a hand-made revert is invisible.
313
+ - **A finding that keeps repeating becomes a rule** — a lint rule, a constitution principle (`02-DOCS/wiki/sdd/constitution.md`, amended via `constitution`) or a line in `REVIEW.md`. Ask what the model didn't have in front of it: a repeat is almost always missing context, not a careless reviewer.
287
314
 
288
- Index both in `02-DOCS/wiki/index.md` (the Knowledge map; root `CLAUDE.md` keeps only a short pointer) under the `sdd/` topic — the harness owns that map; this skill just keeps its rows honest.
315
+ Index them in `02-DOCS/wiki/index.md` (the Knowledge map; root `CLAUDE.md` keeps only a short pointer) under the `sdd/` topic — the harness owns that map; this skill just keeps its rows honest.
289
316
 
290
317
  ## Result envelope
291
318
 
292
- End with the parseable block every SDD phase shares, so the dispatcher can chain without
293
- interpreting prose (contract: `../sdd/SKILL.md`):
319
+ End with the parseable block every phase shares, so the dispatcher can chain without
320
+ interpreting prose (contract: `../asdd/SKILL.md`):
294
321
 
295
322
  ```json result-envelope
296
323
  {
297
324
  "status": "complete|blocked|failed",
298
- "executive_summary": "Adversarial review against spec/plan/constitution; verdict and blocking findings.",
325
+ "executive_summary": "Per-task or closing review against spec/plan/constitution/REVIEW.md; tier, round, verdict and blocking findings.",
299
326
  "artifact": "02-DOCS/wiki/sdd/reviews/<slug>.md",
300
327
  "next_recommended": "ship",
301
328
  "risk": "low|medium|high",
@@ -303,12 +330,12 @@ interpreting prose (contract: `../sdd/SKILL.md`):
303
330
  "used": ["review"],
304
331
  "missing": [],
305
332
  "fallback": [],
306
- "compact_rules": ["Rank or it is noise: blocker / should-fix / nit.", "A finding without a repro is a question, not a defect."]
333
+ "compact_rules": ["Rank or it is noise: blocker / should-fix / nit.", "A finding without a repro is a question, not a defect.", "Max 3 rounds; never repeat a finding."]
307
334
  },
308
- "evidence": ["verdict stated (APPROVE | APPROVE WITH NITS | CHANGES REQUESTED)", "each blocker carries location + repro + fix"]
335
+ "evidence": ["rsc risk tier", "verdict stated (APPROVE | APPROVE WITH NITS | CHANGES REQUESTED)", "each blocker carries location + repro + fix", "every finding has a ledger outcome"]
309
336
  }
310
337
  ```
311
338
 
312
339
  ## Next in the chain
313
340
 
314
- When the diff carries an **APPROVE** / **APPROVE WITH NITS** verdict and every blocker is resolved, hand off to **ship** — close the branch via PR / merge / cleanup, with **git authorship as Eric, never Claude**. If the review came back **CHANGES REQUESTED**, the loop goes back to **implement** (fix), then **verify** (re-prove green), then back here for re-review. Don't ship a diff that hasn't earned its verdict.
341
+ Per task: **APPROVE** returns to `implement`'s loop for the next task; **CHANGES REQUESTED** goes back to the fix step of the same task, as the next round. Closing review: **APPROVE** / **APPROVE WITH NITS** with every blocker resolved hands off to **ship**, with **git authorship as the human, never an AI**; anything else goes back to `implement`. Don't ship a diff that hasn't earned its verdict.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sdd-init
3
- description: "Use when calibrating an existing repo for the rsc SDD chain: detect stack, package manager, test runners and apply/verify commands, refresh the skill registry, and write the runtime config at 02-DOCS/wiki/sdd/config.yaml. NOT the dispatcher that routes SDD phases (that is `sdd`), NOT first-contact user/workspace bootstrap (that is `init`)."
3
+ description: "Use when calibrating an existing repo for the rsc SDD chain: detect stack, package manager, test runners and apply/verify commands, refresh the skill registry, and write the runtime config at 02-DOCS/wiki/sdd/config.yaml. NOT the dispatcher that routes SDD phases (that is `asdd`), NOT first-contact user/workspace bootstrap (that is `init`)."
4
4
  tags: [sdd, init, config, testing, registry]
5
5
  recommends: [sdd, specify, implement, verify]
6
6
  profiles: [core, full]
@@ -15,7 +15,7 @@ Step zero for technical SDD work: read the repo, detect how it should be built a
15
15
  02-DOCS/wiki/sdd/config.yaml
16
16
  ```
17
17
 
18
- That config is the runtime contract later phases read before choosing commands, TDD strictness, artifact paths, review budget or skill briefs. `sdd` dispatches the phases that consume it, `init` owns first contact and user profiling, `harness` owns `01-TOOLS/` and `02-DOCS/` scaffolding. This skill only calibrates.
18
+ That config is the runtime contract later phases read before choosing commands, TDD strictness, artifact paths, review budget or skill briefs. `asdd` dispatches the phases that consume it, `init` owns first contact and user profiling, `harness` owns `01-TOOLS/` and `02-DOCS/` scaffolding. This skill only calibrates.
19
19
 
20
20
  ## Inputs
21
21
 
@@ -39,7 +39,7 @@ Ask only when the answer changes behavior; explain each trade-off in one line, i
39
39
  | `review_budget.line_budget` | `400` | Lower for solo tight review; higher only with explicit approval. |
40
40
  | `delivery_strategy.default` | `ask-on-risk` | `ask-on-risk`, `single-pr`, `autochain`, `exception`. |
41
41
  | `models.enabled` | `false` | Per-phase model routing is opt-in; leave off unless the user asks for it. |
42
- | `models.provider` | `anthropic` | Which provider the tiers resolve to. Set from the detected assistant when obvious, else `anthropic`. See `../sdd/references/model-routing.md` for other providers. |
42
+ | `models.provider` | `anthropic` | Which provider the tiers resolve to. Set from the detected assistant when obvious, else `anthropic`. See `../asdd/references/model-routing.md` for other providers. |
43
43
 
44
44
  ## Detection
45
45
 
@@ -145,7 +145,7 @@ models:
145
145
  overrides: {} # per-phase tier overrides set by the user
146
146
  ```
147
147
 
148
- The `models` block is the **per-phase model routing** profile: each phase declares a tier (`heavy`/`balanced`/`light`) and the tiers resolve to concrete models for `provider`. The full protocol — how phases apply it, the per-assistant switch mechanism, the provider→model table — lives in `../sdd/references/model-routing.md`. Keep this block byte-for-byte in sync with that reference.
148
+ The `models` block is the **per-phase model routing** profile: each phase declares a tier (`heavy`/`balanced`/`light`) and the tiers resolve to concrete models for `provider`. The full protocol — how phases apply it, the per-assistant switch mechanism, the provider→model table — lives in `../asdd/references/model-routing.md`. Keep this block byte-for-byte in sync with that reference.
149
149
 
150
150
  Preserve user edits if the file exists: update detected facts and leave comments/custom policy fields intact when possible. **Never flip `models.enabled` or drop `models.overrides` on re-calibration** — a calibration pass that silently switches the user's models is the one failure they cannot detect from the diff, so preserve both verbatim and only refresh `tiers`/`provider` if the user asks. If preservation is risky, write a proposed replacement next to it as `config.proposed.yaml` and ask.
151
151
 
@@ -181,9 +181,9 @@ End with the standard SDD result envelope:
181
181
  | "I'll skip config and remember the commands in chat." | Chat is not source of truth. Write `config.yaml`. |
182
182
  | "No test command detected, but I'll still say strict TDD is active." | Strict TDD needs a runner. Record the gap. |
183
183
  | "Load all skills so the agent has context." | That pollutes context. Use registry -> selected skills -> compact rules. |
184
- | "This is the same as init." | No. `init` profiles user/workspace, `sdd` dispatches the phases, `sdd-init` calibrates the technical SDD runtime they both rely on. |
184
+ | "This is the same as init." | No. `init` profiles user/workspace, `asdd` dispatches the phases, `sdd-init` calibrates the technical SDD runtime they both rely on. |
185
185
  | "Create openspec/ because Gentle does." | RSC uses `02-DOCS/wiki/sdd/` as source of truth. |
186
186
 
187
187
  ## Next
188
188
 
189
- After `sdd-init`, return to `sdd`. If no spec exists, route to `specify`. If the work is ambiguous or architectural, write a proposal first under `02-DOCS/wiki/sdd/proposals/`.
189
+ After `sdd-init`, return to `asdd`. If no spec exists, route to `specify`. If the work is ambiguous or architectural, write a proposal first under `02-DOCS/wiki/sdd/proposals/`.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: ship
3
- description: "Use when a verified, review-approved branch has to land — the close of the rsc SDD chain: safety checks, then three landing options (merge, PR, park/discard), authorship always Eric and never an AI trailer. NOT running the gates (that is `verify`), NOT reading the diff for defects (that is `review`), NOT shipping to a server (that is `deployment`)."
4
- tags: [sdd, ship, release, pr]
3
+ description: "Use when a verified branch that passed its closing review has to land — the close of rsc ASDD: safety checks, an explainer for the human (what changed, what the checks proved and would not catch), then three landing options (merge, PR with Testing/Risk/How to revert, park/discard). Risk tier 0-1 with CI green may self-merge; tier 2 waits for human approval. Authorship always the human, never an AI trailer. NOT running the gates (that is `verify`), NOT reading the diff for defects (that is `review`), NOT shipping to a server (that is `deployment`)."
4
+ tags: [asdd, ship, release, pr, risk]
5
5
  recommends: []
6
6
  profiles: [core, full]
7
7
  origin: risco
@@ -9,7 +9,7 @@ origin: risco
9
9
 
10
10
  # Ship — close the development branch
11
11
 
12
- Ship is the **last gate** of the rsc SDD chain: `constitution → specify → clarify → plan → tasks → analyze → implement → verify → review → **ship**`. Everything upstream proved the work is *correct* and *green*; ship is the act of **landing it** — turning an approved branch into merged history, a pull request, or a clean parked branch — without breaking the trunk and without ever forging the author.
12
+ Ship is the **last step** of ASDD's closing stage (`../asdd/SKILL.md`), after the closing review. Everything upstream proved the work is *correct* and *green*; ship is the act of **landing it** — turning an approved branch into merged history, a pull request, or a clean parked branch — without breaking the trunk and without ever forging the author.
13
13
 
14
14
  This skill owns one decision and its safe execution: **how does this work integrate?** It does not write code, run test gates, or read the diff for defects — those phases already happened. It is the **close, not the open**: creating the isolated branch or worktree is `../worktrees/SKILL.md`, taking the gates green is `../verify/SKILL.md`, reading the diff adversarially is `../review/SKILL.md`, and putting merged code onto a server is `../deployment/SKILL.md`.
15
15
 
@@ -36,7 +36,7 @@ git log -1 --format='%an <%ae>%n%n%b' | grep -iE 'co-authored-by.*(claude|anthro
36
36
  ## Read these first
37
37
 
38
38
  1. `02-DOCS/wiki/harness/user-profile.md` — `technical_level`, the register. It sets the words only, never whether you run the safety checklist.
39
- 2. The **review verdict** for this branch — ship runs only on `APPROVE` or `APPROVE WITH NITS`. `CHANGES REQUESTED` loops back to `implement`, not forward to ship. If there is no verdict on record, say so and treat it as a red flag: do not ship a diff that skipped `../review/SKILL.md` — offer to run it first.
39
+ 2. The **closing review verdict** for this branch — ship runs only on `APPROVE` or `APPROVE WITH NITS`. `CHANGES REQUESTED` loops back to `implement`, not forward to ship. If there is no verdict on record, say so and treat it as a red flag: do not ship a diff that skipped `../review/SKILL.md` — offer to run it first.
40
40
  3. `02-DOCS/wiki/sdd/decisions.md` and the spec/plan slug — so the commit message and PR body describe *what shipped against which spec*, not a vague "various changes".
41
41
 
42
42
  ## The pre-ship safety checklist
@@ -46,7 +46,9 @@ Run this before presenting the landing options. Any unchecked item is a stop —
46
46
  - [ ] **Review verdict is APPROVE / APPROVE WITH NITS** (not CHANGES REQUESTED, not absent).
47
47
  - [ ] **Working tree is clean** — `git status --short` is empty. No stray edits sneaking into the merge.
48
48
  - [ ] **On a feature branch, not the trunk** — `git rev-parse --abbrev-ref HEAD` is not `main`/`master`. If work landed directly on the trunk, that is its own problem; flag it, don't paper over it.
49
- - [ ] **Rebased / up to date with the base** — branch is on top of latest `main`; conflicts resolved locally, not punted to the merge.
49
+ - [ ] **Rebased / up to date with the base, and tested there** — branch is on top of latest `main`; conflicts resolved locally, not punted to the merge; the checks re-ran on the rebased branch.
50
+ - [ ] **Risk tier known** — `npx @ericrisco/rsc risk --base main` run; its tier, classes and `human approval` line go into the PR's Risk section.
51
+ - [ ] **Decision queue empty** — no task parked for a human decision in this branch, or the PR says which ones remain.
50
52
  - [ ] **No secrets in the diff** — scan the staged/branch diff for keys, tokens, `.env` values (`git diff main... | grep -iE 'api[_-]?key|secret|password|token|BEGIN .*PRIVATE KEY'`). A hit is a blocker; remove it, and rotate it if it was ever pushed.
51
53
  - [ ] **Authorship is Eric** — `git config user.email` is Eric's; no AI trailer in any commit on the branch (run the grep above across `main..HEAD`).
52
54
  - [ ] **Commit history is intelligible** — squashed or organized so the history reads as deliberate, not "wip wip fix fix".
@@ -59,8 +61,17 @@ echo "behind: $(git rev-list --count HEAD..origin/main 2>/dev/null || echo '?'
59
61
  git log main..HEAD --format='%an <%ae>' | sort -u # authors on this branch — expect only Eric
60
62
  git diff main...HEAD | grep -icE 'api[_-]?key|secret|password|token|BEGIN .*PRIVATE KEY' \
61
63
  | sed 's/^/secret-hits: /'
64
+ npx @ericrisco/rsc risk --base main # tier, classes, reviewers, human approval
62
65
  ```
63
66
 
67
+ ### The explainer — before the PR, for the human
68
+
69
+ Before any landing option, give the human a short explainer, in their register: **what the change
70
+ does** (behaviour, not files), **what the checks proved** (which tests, criteria, review tier), and
71
+ **what they would NOT catch** — the untested path, the environment only production has, the
72
+ judgement no test encodes. The last part is the point: it tells the human where to look. It also
73
+ goes into the PR's Testing section.
74
+
64
75
  ### Automated guard (PreToolUse) — you cannot quietly abandon a feature
65
76
 
66
77
  When rsc is installed for Claude Code, a `PreToolUse` hook (`.rsc/ship-guard.mjs`) enforces this
@@ -99,7 +110,7 @@ local merge is a commit on the default branch. Present option 2 (the recommendat
99
110
  and say in one line why option 1 is absent and that «desbloquea main» (`npx @ericrisco/rsc main
100
111
  unlock`) would bring it back. The branch guard denies the local merge anyway.
101
112
 
102
- Recommend based on repo signals: protected `main` or an existing PR culture (look for `.github/`, prior PRs via `gh pr list`) → recommend **option 2**. A solo project with no protection and a passed review → **option 1** is honest and faster. Never default to a PR ceremony the repo doesn't use, and never force-merge a repo that gates `main`.
113
+ Recommend based on repo signals: protected `main` or an existing PR culture (look for `.github/`, prior PRs via `gh pr list`) → recommend **option 2**. A solo project with no protection and a passed review → **option 1** is honest and faster. Never default to a PR ceremony the repo doesn't use, and never force-merge a repo that gates `main`. A tier-2 change takes option 1 only after human approval, same as a PR.
103
114
 
104
115
  ### Delivery strategy from SDD config
105
116
 
@@ -157,19 +168,27 @@ PR body shape (no generated-with line, ever):
157
168
 
158
169
  Implements `02-DOCS/wiki/sdd/specs/<slug>.md`. <the user-facing reason>
159
170
 
160
- ## How
171
+ ## Testing
172
+
173
+ - <what ran: lint / types / tests / verify record; acceptance criteria met, each with its evidence>
174
+ - Closing review: APPROVE (<N> findings, all with an outcome).
175
+ - Not covered: <what these checks would NOT catch — from the explainer>
176
+
177
+ ## Risk
161
178
 
162
- - <key implementation point>
163
- - <key implementation point>
179
+ Tier <0|1|2> (`rsc risk`): <classes touched>. Human approval: <required|not required>.
164
180
 
165
- ## Verification
181
+ ## How to revert
166
182
 
167
- - `verify` phase: lint / types / tests green (see the verification record).
168
- - Acceptance criteria from the spec: all met.
169
- - Review verdict: APPROVE.
183
+ <`git revert <merge-sha>` / the squash commit; plus anything a revert alone does not undo — a migration, a flag, data written>
170
184
  ```
171
185
 
172
- Then either let the gate run (team/CI) or self-merge once green: `gh pr merge --squash --delete-branch` (or `--merge` to preserve the history). Squash when the branch history is noisy; preserve when each commit is meaningful.
186
+ **Merge policy — by risk tier.** Tier 0-1, CI green and the decision queue empty → you may
187
+ self-merge (autopilot): `gh pr merge --squash --delete-branch` (or `--merge` to preserve the
188
+ history; squash when the branch history is noisy, preserve when each commit is meaningful). **Tier
189
+ 2 waits for human approval** — a human's approving review on the PR, or their explicit yes in the
190
+ conversation. An agent's approval never counts as a human's: no refuter, reviewer subagent or bot
191
+ review stands in for it, however clean.
173
192
 
174
193
  Once it is merged, pull the trunk — `git switch main && git pull --ff-only` — and the cleanup rides along with that pull: `post-merge` fires on a fast-forward too, verified. A squashed pull request is exactly the case the reaper judges by content rather than by commit identity, so it is recognised as landed; the local branch is kept, because git will not delete a squashed branch safely and while it exists the work is recoverable.
175
194
 
@@ -205,13 +224,13 @@ The commit is the durable record. Make it describe the change and tie it to the
205
224
 
206
225
  ## Model tier — `light` (opt-in routing)
207
226
 
208
- Closing the branch (PR / merge / cleanup) is mechanical, so this phase's default tier is **`light`**. Routing is **off** unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`; when it is on, follow `../sdd/references/model-routing.md` for resolving and announcing the switch rather than from memory. Routing off or no profile → honor the session model silently, and skip routing on a one-line change. The Eric-only authorship rule is independent of the model and never relaxes.
227
+ Closing the branch (PR / merge / cleanup) is mechanical, so this phase's default tier is **`light`**. Routing is **off** unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`; when it is on, follow `../asdd/references/model-routing.md` for resolving and announcing the switch rather than from memory. Routing off or no profile → honor the session model silently, and skip routing on a one-line change. The Eric-only authorship rule is independent of the model and never relaxes.
209
228
 
210
229
  ## What ship shows
211
230
 
212
231
  Read `technical_level` from `02-DOCS/wiki/harness/user-profile.md`. It changes the words, **never** the safety checklist or the authorship rule. No profile → use analogies and proceed; don't stall the ship to ask.
213
232
 
214
- In the `orient` voice: the checklist result in one line, the three options as one-liners, the recommendation and its *why*, then execute on a yes — e.g. `Clean, rebased, authorship Eric. Recommend PR (main is protected). Open it?`. For a `non-technical` owner, explain the recommended option with an analogy: a protected `main` wants a PR ("asking permission before changing the shared copy"). Explain fast-forward vs `--no-ff` or what squashing trades away only when asked.
233
+ In the `orient` voice: the explainer, the checklist result in one line, the three options as one-liners, the recommendation and its *why*, then execute on a yes — e.g. `Clean, rebased, authorship Eric. Recommend PR (main is protected). Open it?`. For a `non-technical` owner, explain the recommended option with an analogy: a protected `main` wants a PR ("asking permission before changing the shared copy"). Explain fast-forward vs `--no-ff` or what squashing trades away only when asked.
215
234
 
216
235
  ## Anti-patterns → STOP
217
236
 
@@ -219,6 +238,7 @@ In the `orient` voice: the checklist result in one line, the three options as on
219
238
  | --- | --- |
220
239
  | "I'll add `Co-Authored-By: Claude` / a 'Generated with Claude Code' footer to be transparent" | It forges the record. The work is Eric's — no AI trailer, ever. Strip it. |
221
240
  | "Review didn't formally approve but it's obviously fine" | No verdict = not ready. Ship runs on APPROVE only. Route back to review. |
241
+ | "Tier 2, but the three refuters approved — I'll self-merge" | An agent's approval never counts as a human's. Tier 2 waits for human approval. |
222
242
  | "The tree has a couple of stray edits, they're harmless" | A dirty tree means the merge is not the reviewed diff. Clean it or stash it first. |
223
243
  | "`main` is protected but I'll just force-merge, I'm sure" | Protected means PR. Don't bypass the gate the repo deliberately set. |
224
244
  | "I'll rebase and resolve conflicts during the merge" | Resolve before. A conflicted merge commit hides what actually shipped. |
@@ -259,13 +279,13 @@ End with:
259
279
  "fallback": [],
260
280
  "compact_rules": ["Keep exactly three landing options.", "Archive the final state."]
261
281
  },
262
- "evidence": ["review verdict", "verification record", "PR/merge/park/discard reference"]
282
+ "evidence": ["closing review verdict", "rsc risk tier", "verification record", "PR/merge/park/discard reference", "human approval when tier 2"]
263
283
  }
264
284
  ```
265
285
 
266
286
  ## Next in the chain
267
287
 
268
- Ship is the end of the SDD loop for a feature. Two onward paths: the merged code still has to reach a **server / release** → hand off to **deployment** (`../deployment/SKILL.md`); or the next feature restarts the loop at **specify** (`../specify/SKILL.md`), or at **constitution** if the project's principles changed. The `sdd` dispatcher (`../sdd/SKILL.md`) routes whichever comes next.
288
+ Ship is the end of the ASDD loop for a feature. Two onward paths: the merged code still has to reach a **server / release** → hand off to **deployment** (`../deployment/SKILL.md`); or the next feature restarts the loop at **specify** (`../specify/SKILL.md`), or at **constitution** if the project's principles changed. The `asdd` dispatcher (`../asdd/SKILL.md`) routes whichever comes next. Later, a revert of what shipped is an escape: `review` turns it into a case (`rsc review escapes`).
269
289
 
270
290
  ## Orientación (siempre)
271
291
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: specify
3
- description: "Use when a feature, change, or product idea is still fuzzy and must become an approved WHAT/WHY spec before any planning or code — the brainstorming front door of SDD. Turns a one-line intent into problem, goals, users, scope, behaviour and acceptance criteria, via frontier-round dialogue and 2-3 approaches with a recommendation, carrying zero implementation detail. Fires whenever someone jumps to HOW before WHAT is agreed, in any language. NOT the technical plan (that is `plan`), NOT the de-risking ambiguity sweep (that is `clarify`), NOT project-wide principles (that is `constitution`)."
3
+ description: "Use when a feature, change, or product idea is still fuzzy and must become an approved WHAT/WHY spec before any planning or code — the brainstorming front door of ASDD stage 2 (Spec). Turns a one-line intent into problem, goals, users, out-of-scope, the four ASDD questions (what the user can do, what result they get, what it must respect, how it proves it) and acceptance criteria, via frontier-round dialogue and 2-3 approaches with a recommendation, carrying zero implementation detail. Fires whenever someone jumps to HOW before WHAT is agreed, in any language. NOT the technical plan (that is `plan`), NOT the de-risking ambiguity sweep (that is `clarify`), NOT project-wide principles (that is `constitution`)."
4
4
  tags: [sdd, spec, requirements]
5
5
  recommends: [clarify, plan]
6
6
  profiles: [core, full]
@@ -9,7 +9,7 @@ origin: risco
9
9
 
10
10
  # Specify — intent becomes a spec
11
11
 
12
- This is the **specify** phase of the rsc-sdd chain: `constitution` → **`specify`** → `clarify` → `plan` → `tasks` → `analyze` → `implement` → `verify` → `review` → `ship`. Its single job is to turn a fuzzy intent into a written specification that states **WHAT** the change is and **WHY** it matters — and nothing about **HOW** it gets built.
12
+ This is the drafting half of ASDD stage 2, **Spec** (`../asdd/SKILL.md`): **`specify`** writes the draft → `clarify` hunts its gaps → the human approves. Its single job is to turn a fuzzy intent into a written specification that states **WHAT** the change is and **WHY** it matters — and nothing about **HOW** it gets built.
13
13
 
14
14
  A spec is a contract about behaviour and outcomes, readable by a non-technical stakeholder and precise enough that a `plan` can be derived from it. The output is one file: `02-DOCS/wiki/sdd/specs/<slug>.md`, indexed in `02-DOCS/wiki/index.md` (the Knowledge map; root `CLAUDE.md` keeps only a short pointer).
15
15
 
@@ -40,7 +40,7 @@ If, having looked, there is no real objection, **say so in one line and move on
40
40
  **Offer autopilot once, right here.** At this boundary, propose how to run the rest of the chain:
41
41
  > *"¿Quieres que lo lleve hasta el final yo solo — spec → plan → código → verify, parando solo si algo es ambiguo — o prefieres que pare a que apruebes en cada fase?"*
42
42
 
43
- A **yes engages autopilot** (`../sdd/SKILL.md`): you still write the spec and every artifact, but you auto-advance through the phases without re-asking — that up-front yes is the approval that satisfies the hard gate above, for the whole run. A **no** (or silence) keeps the default gated flow: write the spec, hand to `clarify`/`plan`, stop for approval before code. Either way the spec gets written; autopilot only changes whether you pause *between* phases — and it still stops for genuine ambiguity, hard failures, or destructive/outward actions (ship still confirms). If `sdd.autopilot: true` in config, autopilot is the default — still surface it once.
43
+ A **yes engages autopilot** (`../asdd/SKILL.md`): you still write the spec and every artifact, but you auto-advance through the phases without re-asking — that up-front yes is the approval that satisfies the hard gate above, for the whole run. A **no** (or silence) keeps the default gated flow: write the spec, hand to `clarify`/`plan`, stop for approval before code. Either way the spec gets written; autopilot only changes whether you pause *between* phases — and it still stops for genuine ambiguity, hard failures, or destructive/outward actions (ship still confirms). If `sdd.autopilot: true` in config, autopilot is the default — still surface it once.
44
44
 
45
45
  ## The one rule that defines this skill
46
46
 
@@ -52,7 +52,7 @@ If you cannot state a requirement without naming the technology, that is a real
52
52
 
53
53
  ## Model tier — `balanced` (opt-in routing)
54
54
 
55
- This phase's default model tier is **`balanced`** — it drafts the what/why spec through dialogue, not architecture. Routing is **off** unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`. When on: resolve this phase's tier (`models.overrides` wins over `models.phases`), map it to a model via `models.tiers`, and apply per `../sdd/references/model-routing.md` — announce the switch in one line when it differs from the session model, and dispatch any `Task`/`parallel` subagents on that model. Routing off or no profile → honor the session model silently. Never fake a switch a tool can't make; skip routing on a one-line change.
55
+ This phase's default model tier is **`balanced`** — it drafts the what/why spec through dialogue, not architecture. Routing is **off** unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`. When on: resolve this phase's tier (`models.overrides` wins over `models.phases`), map it to a model via `models.tiers`, and apply per `../asdd/references/model-routing.md` — announce the switch in one line when it differs from the session model, and dispatch any `Task`/`parallel` subagents on that model. Routing off or no profile → honor the session model silently. Never fake a switch a tool can't make; skip routing on a one-line change.
56
56
 
57
57
  ## Read the room first (register)
58
58
 
@@ -103,8 +103,12 @@ Write these sections into `02-DOCS/wiki/sdd/specs/<slug>.md` using `references/s
103
103
  | Cost of not building it | What concretely happens if nobody does this, and what it costs | Softening a genuinely small cost — "not much" IS the finding |
104
104
  | The cheapest alternative | What would solve most of it without building this, and why that is not enough | "Nothing" — there is always a manual workaround or a smaller version |
105
105
  | Goals | What success delivers, in outcome terms | A "goal" that's actually a HOW |
106
- | Non-goals / out of scope | What is explicitly NOT done now — adjacent work, deferred features | Silence — unsaid scope becomes assumed scope |
106
+ | Non-goals / out of scope | What is explicitly NOT done now — adjacent work, deferred features, and what an agent could add "just in case" (the dropped token, the QR nobody asked for) | Silence — unsaid scope becomes assumed scope, and a long run fills it |
107
107
  | Users & context | Who acts, their context, what they're trying to achieve | An imagined user no one asked for |
108
+ | What the user can do | The action, its starting conditions, who is allowed to do it | A capability with no actor or no precondition |
109
+ | What result they get | The observable result against a reference the **human approved**: a screenshot or sketch for UI, the expected response for an API | An agent judging that two screenshots "look alike" — that is not a reference |
110
+ | What it must respect | Constraints and prior behaviour that must keep working. Consult the project's brain if it has one, otherwise `02-DOCS/wiki/sdd/decisions.md` and existing specs, so the spec never steps on a prior decision | A constraint you remember but did not look up |
111
+ | How it proves it | How the check runs and what evidence remains afterwards | "Yes, I tested it" — that is a claim, not evidence |
108
112
  | Behaviour | What the system does, in observable terms, incl. main + edge + error paths | A verb that's actually a HOW ("queries", "caches") |
109
113
  | Acceptance criteria | Testable, binary checks that say "done" | Vague critera ("works well", "is fast") |
110
114
  | Points to clarify | The typed handoff to `clarify` — see the four types below | An untyped list, or pretending there are none |
@@ -121,7 +125,7 @@ Then they see an empty-cart message and the "pay" button is disabled
121
125
 
122
126
  A criterion that needs a human to "decide if it's good enough" is not done yet — sharpen it or move the soft part to *Points to clarify*.
123
127
 
124
- **Two of those sections are enforced, not suggested.** For a spec dated 2026-09-06 or later, `spec:gate` refuses the file if *Cost of not building it* or *The cheapest alternative* is missing or still holding the template's guidance. Specs written before that date are exempt — they cannot answer honestly after the fact. The two exist because every other gate in the chain compares artifacts to each other, so a spec that is wrong-but-coherent passes all of them; these are the only two lines that ask whether the thing should exist at all.
128
+ **Six of those sections are enforced, not suggested, each from its own date.** For a spec dated 2026-09-06 or later, `spec:gate` refuses the file if *Cost of not building it* or *The cheapest alternative* is missing or still holding the template's guidance; from 2026-11-03 it also refuses one missing any of the four ASDD questions — *What the user can do*, *What result they get*, *What it must respect*, *How it proves it* (Spanish headings accepted: *Qué podrá hacer el usuario*, *Qué resultado obtiene*, *Qué debe respetar*, *Cómo lo demuestra*). Specs written before each date are exempt — they cannot answer honestly after the fact. The first two ask whether the thing should exist at all, because every other gate compares artifacts to each other and a wrong-but-coherent spec passes all of them. The four make the spec something a long autonomous run can be checked against: tests prove the code works, not that it does what was asked.
125
129
 
126
130
  ### Points to clarify carry a type
127
131
 
@@ -155,7 +159,8 @@ saves you work.
155
159
  Run these in order. It is a collaborative dialogue, not a form you fill in silence — and **you do not skip to a design dump.** Track the steps with a todo list so none is dropped.
156
160
 
157
161
  ```text
158
- 1. EXPLORE context → profile + sdd config + constitution + existing specs + wiki + recent git
162
+ 1. EXPLORE context → profile + sdd config + constitution + existing specs + decisions log (or the
163
+ project's brain) + wiki + recent git
159
164
  2. SCOPE check → if the request is several independent subsystems, DECOMPOSE into sub-specs first,
160
165
  then brainstorm the FIRST one; each gets its own spec → plan → build cycle
161
166
  3. RESTATE + OBJECT → one sentence of what you understood, plus the strongest objection you can
@@ -268,10 +273,25 @@ drop-off, because the friction is the password itself, not the reset.
268
273
  ## Non-goals / out of scope
269
274
  - Social login (Google/Apple) — deferred.
270
275
  - Replacing existing sessions for already-signed-in users.
276
+ - A QR code to sign in from another device — nobody asked for it.
271
277
 
272
278
  ## Users & context
273
279
  A returning user on a new device who does not remember a password.
274
280
 
281
+ ## What the user can do
282
+ Any registered user, signed out, can request a link to their own email.
283
+
284
+ ## What result they get
285
+ The sign-in screen matches the approved sketch (linked); following the link
286
+ lands them signed in on the page they started from.
287
+
288
+ ## What it must respect
289
+ Existing sessions keep working; the decisions log says unknown emails reveal nothing.
290
+
291
+ ## How it proves it
292
+ An end-to-end check per acceptance criterion; its run log and a screenshot of
293
+ the signed-in page stay in the verification file.
294
+
275
295
  ## Behaviour
276
296
  - Main: user enters email → receives a link → following it signs them in.
277
297
  - Edge: an expired link shows a "request a new link" path.
@@ -296,28 +316,7 @@ Note what is *absent*: no token format, no table, no email provider, no framewor
296
316
 
297
317
  ## Optional proposal / pre-execution briefing
298
318
 
299
- For a tiny feature, skip this. For ambiguous, architectural, high-risk, high-review-cost or research-heavy work, write a proposal before the spec:
300
-
301
- ```text
302
- 02-DOCS/wiki/sdd/proposals/<slug>.md
303
- ```
304
-
305
- Proposal grammar:
306
-
307
- ```markdown
308
- # Proposal — <slug>
309
-
310
- ## Problem
311
- ## Intent
312
- ## Scope / Non-scope
313
- ## Research input
314
- ## Alternatives considered
315
- ## Tradeoffs
316
- ## Risks
317
- ## Rollback
318
- ## Success criteria
319
- ## Recommendation
320
- ```
319
+ For a tiny feature, skip this. For ambiguous, architectural, high-risk, high-review-cost or research-heavy work, write `02-DOCS/wiki/sdd/proposals/<slug>.md` before the spec, with these sections: Problem · Intent · Scope / Non-scope · Research input · Alternatives considered · Tradeoffs · Risks · Rollback · Success criteria · Recommendation.
321
320
 
322
321
  The proposal is allowed to mention options and tradeoffs; the spec that follows still stays WHAT/WHY. If research came from a transcript, doc, spike, or external briefing, cite it in `Research input` so the decision trail survives the chat.
323
322
 
@@ -333,7 +332,7 @@ repeated here as prohibitions: naming a behaviour makes it more available, not l
333
332
  | Name a framework, table, endpoint, or library | That's HOW. Strip it; describe the behaviour instead, or log the open question. |
334
333
  | Write "it should work well / be fast / be intuitive" | Not testable. Make it a binary Given/When/Then or move it to Points to clarify. |
335
334
  | Hand to `plan`/`implement` before the user approved the written spec | The approval at step 9 is the gate. No design approved → nothing gets built. |
336
- | Skip non-goals because "it's obvious" | Unsaid scope becomes assumed scope. State what you are *not* doing. |
335
+ | Skip non-goals because "it's obvious" | Unsaid scope becomes assumed scope. State what you are *not* doing, including what an agent would add "just in case". |
337
336
  | Resolve every ambiguity yourself to look finished | Inventing answers is worse than naming gaps. List them in Points to clarify. |
338
337
  | Start designing the solution because it's clearer | Stay on WHAT/WHY. The plan is a later, separate phase. |
339
338
  | Write the spec somewhere other than 02-DOCS/wiki/sdd/specs/ | That's the canonical location the rest of the chain reads. Use it. |
@@ -370,11 +369,11 @@ End with:
370
369
 
371
370
  ## Next in the chain
372
371
 
373
- A spec is the input to **`clarify`**, not the finish line. End by pointing there:
372
+ A spec is the input to **`clarify`**, not the finish line — both are ASDD stage 2 (**Spec**), which closes only when the human approves. End by pointing there:
374
373
 
375
- > "Spec written to `02-DOCS/wiki/sdd/specs/<slug>.md` with N open points. Next: run **`clarify`** to resolve them and de-risk the spec before planning."
374
+ > "Spec written to `02-DOCS/wiki/sdd/specs/<slug>.md` with N open points. ASDD stage 2 (Spec) continues: run **`clarify`** to hunt its gaps before the human approves and `plan` starts."
376
375
 
377
- If `clarify` surfaces answers, they get baked back into this same spec file. Only once the spec is de-risked does `plan` derive the technical approach.
376
+ If `clarify` surfaces answers, they get baked back into this same spec file. Only once the spec is de-risked and approved does stage 3 (`plan`) derive the technical approach.
378
377
 
379
378
  ## See Also
380
379
 
@@ -65,7 +65,7 @@ capability:
65
65
  must_include:
66
66
  - "Reads technical_level from 02-DOCS/wiki/harness/user-profile.md and the constitution first, inferring everything it can before asking, then asks only contract-changing gaps — the whole FRONTIER in one numbered round, each with its recommendation, and nothing whose answer depends on another question open in that same round."
67
67
  - "Stays strictly WHAT/WHY: no framework, token format, database table, endpoint shape, email provider, library, or file path appears in the spec — implementation-laden answers are flagged as points to clarify instead of guessed."
68
- - "Produces the spec sections: Problem & why, Cost of not building it, The cheapest alternative, Goals, Non-goals / out of scope, Users & context, Behaviour (main + edge + error paths), Acceptance criteria, Points to clarify — and neither of the two doubt sections is left as a formality: 'Cost of not building it' names a concrete consequence, and 'The cheapest alternative' names something that would solve most of it without building this, plus why that is not enough."
68
+ - "Produces the spec sections: Problem & why, Cost of not building it, The cheapest alternative, Goals, Non-goals / out of scope (including what an agent might add 'just in case'), Users & context, What the user can do, What result they get (against a human-approved reference), What it must respect (checked against the decisions log or the project's brain), How it proves it (what evidence remains), Behaviour (main + edge + error paths), Acceptance criteria, Points to clarify — and neither of the two doubt sections is left as a formality: 'Cost of not building it' names a concrete consequence, and 'The cheapest alternative' names something that would solve most of it without building this, plus why that is not enough."
69
69
  - "Writes testable acceptance criteria in Given/When/Then form that verify could later check (e.g. expired-link refusal, unknown-email revealing nothing)."
70
70
  - "Writes the file to 02-DOCS/wiki/sdd/specs/<slug>.md (kebab-case slug), indexes it in the root CLAUDE.md Knowledge map under sdd/specs, and logs the spec creation to the canonical SDD decisions log at 02-DOCS/wiki/sdd/decisions.md."
71
71
  - "Lists honest Points to clarify as the handoff rather than inventing values, and TYPES each one (pregunta abierta / suposición tomada / decisión diferida / área no formulable) so clarify knows whether to ask it, validate it, or leave it alone; a suposición carries its basis."
@@ -41,13 +41,33 @@ version, or doing nothing. Required for specs dated 2026-09-06 or later.>
41
41
 
42
42
  ## Non-goals / out of scope
43
43
  - <What is explicitly NOT done in THIS change — adjacent features, deferred
44
- work, things a reader might assume are included. Be explicit, not silent:
45
- unsaid scope becomes assumed scope.>
44
+ work, things a reader might assume are included, and what an agent could be
45
+ tempted to add "just in case" (a feature nobody asked for). Be explicit, not
46
+ silent: unsaid scope becomes assumed scope.>
46
47
 
47
48
  ## Users & context
48
49
  <Who acts, the situation they're in, and what they're trying to achieve.
49
50
  Real users from the constitution/wiki, not invented personas.>
50
51
 
52
+ ## What the user can do
53
+ <The action, the starting conditions it needs, and who is (and is not)
54
+ allowed to do it. Required for specs dated 2026-11-03 or later.>
55
+
56
+ ## What result they get
57
+ <The observable result, against a reference the human approved: a screenshot
58
+ or sketch for UI, the expected response for an API. An agent's own judgement
59
+ that two screenshots "look alike" is not a reference. Required from 2026-11-03.>
60
+
61
+ ## What it must respect
62
+ <Constraints and prior behaviour that must keep working. Look them up in the
63
+ project's brain if it has one, otherwise in 02-DOCS/wiki/sdd/decisions.md and
64
+ the existing specs, so this spec never steps on a prior decision. Required from
65
+ 2026-11-03.>
66
+
67
+ ## How it proves it
68
+ <How the check runs and what evidence remains afterwards. "Yes, I tested it" is
69
+ not evidence. Required from 2026-11-03.>
70
+
51
71
  ## Behaviour
52
72
  - Main path: <what the system does, observably, in the common case>
53
73
  - Edge cases: <empty, maximum, boundary, concurrent, repeated…>
@@ -81,10 +101,12 @@ costliest of the four.>
81
101
  - **Before you call it written**, run `npm run spec:gate <path>`. It checks that
82
102
  every section here carries content or an explicit open point, and prints the
83
103
  clauses it cannot check (the unmarked assumption is yours).
84
- - **The two doubt sections are dated, not retroactive.** `Cost of not building
104
+ - **The enforced sections are dated, not retroactive.** `Cost of not building
85
105
  it` and `The cheapest alternative` are required only for specs whose
86
- frontmatter `timestamp` is `2026-09-06` or later. Specs written before that
87
- are exempt — they cannot answer honestly after the fact, and a gate that turns
106
+ frontmatter `timestamp` is `2026-09-06` or later; the four ASDD questions
107
+ (`What the user can do`, `What result they get`, `What it must respect`, `How
108
+ it proves it`, or their Spanish headings) from `2026-11-03`. Specs written
109
+ before are exempt — they cannot answer honestly after the fact, and a gate that turns
88
110
  the whole corpus red is a gate that gets switched off. A spec with no readable
89
111
  `timestamp` falls on the exempt side too.
90
112
  - **Sharpness, not difficulty**, sorts the last two types from the first: *can