@ericrisco/rsc 3.7.5 → 4.0.1

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 (62) hide show
  1. package/README.md +16 -9
  2. package/manifest.json +48 -40
  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 +187 -0
  10. package/scripts/lib/risk.js +68 -0
  11. package/scripts/lib/spec-gate.js +10 -1
  12. package/scripts/rsc.js +104 -6
  13. package/scripts/spec-gate.js +13 -3
  14. package/skills/analyze/SKILL.md +3 -3
  15. package/skills/asdd/SKILL.md +183 -0
  16. package/skills/{sdd → asdd}/evals/README.md +10 -10
  17. package/skills/{sdd → asdd}/evals/cases.yaml +5 -5
  18. package/skills/{sdd → asdd}/references/model-routing.md +2 -2
  19. package/skills/building-agents/SKILL.md +1 -1
  20. package/skills/clarify/SKILL.md +59 -55
  21. package/skills/clarify/evals/cases.yaml +6 -7
  22. package/skills/constitution/SKILL.md +30 -16
  23. package/skills/constitution/evals/README.md +1 -1
  24. package/skills/constitution/evals/cases.yaml +1 -1
  25. package/skills/debug/SKILL.md +3 -3
  26. package/skills/fastapi/SKILL.md +1 -1
  27. package/skills/flutter/SKILL.md +1 -1
  28. package/skills/ftd/SKILL.md +26 -10
  29. package/skills/ftd/evals/cases.yaml +1 -1
  30. package/skills/ftd/evals/routing.json +4 -4
  31. package/skills/go/SKILL.md +1 -1
  32. package/skills/implement/SKILL.md +147 -132
  33. package/skills/implement/evals/cases.yaml +4 -2
  34. package/skills/implement/references/per-task-review.md +26 -24
  35. package/skills/implement/scripts/task-brief +28 -22
  36. package/skills/nextjs/SKILL.md +1 -1
  37. package/skills/parallel/SKILL.md +1 -1
  38. package/skills/plan/SKILL.md +3 -3
  39. package/skills/plan/evals/cases.yaml +1 -1
  40. package/skills/plan/references/plan-template.md +2 -2
  41. package/skills/postgresdb/SKILL.md +1 -1
  42. package/skills/react/SKILL.md +1 -1
  43. package/skills/review/SKILL.md +80 -41
  44. package/skills/sdd-init/SKILL.md +11 -9
  45. package/skills/ship/SKILL.md +58 -30
  46. package/skills/specify/SKILL.md +42 -41
  47. package/skills/specify/evals/cases.yaml +2 -2
  48. package/skills/specify/references/eliciting-requirements.md +1 -1
  49. package/skills/specify/references/spec-template.md +28 -6
  50. package/skills/suggest/SKILL.md +4 -4
  51. package/skills/tasks/SKILL.md +37 -33
  52. package/skills/tasks/evals/README.md +1 -1
  53. package/skills/tasks/evals/cases.yaml +3 -3
  54. package/skills/verify/SKILL.md +34 -18
  55. package/skills/verify/evals/README.md +2 -2
  56. package/skills/verify/evals/cases.yaml +2 -1
  57. package/skills/verify/references/generic-gate.md +43 -0
  58. package/skills/worktrees/SKILL.md +1 -1
  59. package/targets/clone-bootstrap.mjs +1 -1
  60. package/targets/hook-once.mjs +2 -2
  61. package/targets/sello.mjs +9 -3
  62. package/skills/sdd/SKILL.md +0 -250
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: tasks
3
- description: "Use when an approved plan needs slicing into an ordered task list before any code — the SDD `tasks` phase. Each row carries a literal done-check, dependencies, a parallel-safe marker and a spec trace, appended to the plan artifact. NOT writing the plan (that is `plan`), NOT the consistency gate (that is `analyze`), NOT executing the tasks (that is `implement`)."
4
- tags: [sdd, tasks, breakdown]
3
+ description: "Use when an approved plan needs slicing into an ordered task list before any code — the `tasks` step of ASDD stage 3 (Plan). Each row carries the spec criterion it covers, a risk forecast (tier 0/1/2), a literal done-check, dependencies and a parallel-safe marker, appended to the plan artifact. NOT writing the plan (that is `plan`), NOT the consistency gate (that is `analyze`), NOT executing the tasks (that is `implement`)."
4
+ tags: [asdd, sdd, tasks, breakdown]
5
5
  recommends: [analyze, implement]
6
6
  profiles: [core, full]
7
7
  origin: risco
@@ -28,7 +28,7 @@ appended to the plan it was built from.
28
28
  This phase's default tier is **`balanced`**: decomposing an approved plan is
29
29
  structured work, not architecture. Routing is off unless `models.enabled: true`
30
30
  in `02-DOCS/wiki/sdd/config.yaml`; when on, resolve the tier and apply it per
31
- `../sdd/references/model-routing.md`, which owns the resolution order and the
31
+ `../asdd/references/model-routing.md`, which owns the resolution order and the
32
32
  announcement rules. Routing off or no profile → honor the session model silently.
33
33
 
34
34
  ## Read the harness profile first
@@ -45,8 +45,8 @@ You break down a **plan**, not an intent. Before slicing, confirm all three exis
45
45
 
46
46
  1. **The plan** — `02-DOCS/wiki/sdd/plans/<slug>.md`, written by the `plan`
47
47
  phase. It must contain the architecture, interfaces, and testing strategy.
48
- 2. **The spec** — `02-DOCS/wiki/sdd/specs/<slug>.md`, so every task traces to a
49
- requirement. A task with no spec line behind it is scope you are inventing.
48
+ 2. **The spec** — `02-DOCS/wiki/sdd/specs/<slug>.md`, so every task traces to an
49
+ acceptance criterion. A task with no criterion behind it is scope you are inventing.
50
50
  3. **The constitution** — `02-DOCS/wiki/sdd/constitution.md`, the non-negotiable
51
51
  bars (stack canon, quality gates) every task must respect.
52
52
  4. **The SDD config** — `02-DOCS/wiki/sdd/config.yaml`, if present. Use its
@@ -61,7 +61,7 @@ exact thing `analyze` will catch next, and you will have wasted the round trip.
61
61
 
62
62
  ## What makes a task well-formed
63
63
 
64
- Every task is one row. A row is well-formed only when all six fields hold:
64
+ Every task is one row. A row is well-formed only when all seven fields hold:
65
65
 
66
66
  | Field | Rule |
67
67
  | --- | --- |
@@ -70,7 +70,12 @@ Every task is one row. A row is well-formed only when all six fields hold:
70
70
  | **Title** | One imperative verb + object. "Implement", "Add", "Wire", "Migrate" — not "Handle" or "Support". |
71
71
  | **Done-check** | The **literal** command or observation that proves done. Runnable, not a feeling. See below. |
72
72
  | **Depends-on** | The task IDs that must finish first, or `—` for none. Drives the ordering and the parallel markers. |
73
- | **Trace** | The spec section / acceptance criterion this task satisfies. Every task traces to one; if it can't, it's out of scope. |
73
+ | **Trace** | The spec acceptance criterion this task covers (`AC3`). Every task traces to one; if it can't, it's out of scope. |
74
+ | **Risk forecast** | Tier `0`/`1`/`2` from the paths the task expects to touch: `npx @ericrisco/rsc risk <expected paths…>`. Never guessed. |
75
+
76
+ **Coverage runs both ways.** A criterion with no task, or a task with no criterion, is a defect
77
+ `analyze` flags. The forecast budgets review; `implement` recomputes the tier on the task's actual
78
+ diff, and that one wins.
74
79
 
75
80
  Put `[P]` (parallel-safe) in its own field only when the task shares **no files
76
81
  and no state** with another unblocked task. When in doubt, leave it blank — a
@@ -125,12 +130,12 @@ will execute.
125
130
  service → API → UI. A task never precedes what it depends on.
126
131
  4. MARK [P] only on tasks that share no files/state with another
127
132
  unblocked task. Default to sequential.
128
- 5. TRACE each task back to a spec line. No trace → cut it (scope creep)
129
- or send the gap to `clarify`/`specify`.
130
- 6. ADD the cross-cutting closers: a final "all done-checks pass" task
131
- and a "verify.sh green" task that hands off to `verify`.
132
- 7. ADD a review workload + delivery forecast before implementation.
133
- 8. APPEND the table to the plan artifact (see below). Do not start a
133
+ 5. TRACE each task to a spec criterion, and each criterion to ≥1 task.
134
+ No trace → cut it (scope creep) or send the gap to `clarify`/`specify`.
135
+ 6. FORECAST risk per task: `npx @ericrisco/rsc risk <expected paths…>`.
136
+ 7. ADD the closer: "all done-checks pass + verify.sh green" (stage 5).
137
+ 8. ADD a review workload + delivery forecast before implementation.
138
+ 9. APPEND the table to the plan artifact (see below). Do not start a
134
139
  new file; the list lives with the plan it came from.
135
140
  ```
136
141
 
@@ -160,14 +165,14 @@ delete a user's map entry.
160
165
  ## Tasks
161
166
  <!-- generated by tasks on 2026-06-01; IDs are stable, do not renumber -->
162
167
 
163
- | ID | [P] | Task | Done-check | Depends-on | Trace |
164
- | --- | --- | --- | --- | --- | --- |
165
- | T001 | | Add `users` table migration | `alembic upgrade head` applies clean; `\d users` shows email UNIQUE | — | spec §3 Data |
166
- | T002 | [P] | Add `sessions` table migration | migration applies; FK to users present | T001 | spec §3 Data |
167
- | T003 | | Write failing test for `POST /login` | `pytest tests/auth/test_login.py` fails: no route | T001 | spec §4 Auth |
168
- | T004 | | Implement `POST /login` | T003 test green; bad pw → 401 | T003 | spec §4 Auth |
169
- | ... | ... | ... | ... | ... | ... |
170
- | T0NN | | All done-checks pass + `verify.sh` green | every row above checked; `scripts/verify.sh` exits 0 | all | spec §Acceptance |
168
+ | ID | [P] | Task | Done-check | Depends-on | Trace | Risk |
169
+ | --- | --- | --- | --- | --- | --- | --- |
170
+ | T001 | | Add `users` table migration | `alembic upgrade head` applies clean; `\d users` shows email UNIQUE | — | AC1 | 2 |
171
+ | T002 | [P] | Add `sessions` table migration | migration applies; FK to users present | T001 | AC2 | 2 |
172
+ | T003 | | Write failing test for `POST /login` | `pytest tests/auth/test_login.py` fails: no route | T001 | AC3 | 1 |
173
+ | T004 | | Implement `POST /login` | T003 test green; bad pw → 401 | T003 | AC3 | 2 |
174
+ | ... | ... | ... | ... | ... | ... | ... |
175
+ | T0NN | | All done-checks pass + `verify.sh` green | every row above checked; `scripts/verify.sh` exits 0 | all | all ACs | — |
171
176
  ```
172
177
 
173
178
  ### Per-task Interfaces (for context-isolated implementers)
@@ -205,15 +210,13 @@ before a giant diff exists.
205
210
  | --- | --- | --- |
206
211
  | Estimated changed lines | <number or range> | based on task count + touched areas |
207
212
  | Files / areas | <count and names> | modules, migrations, UI, tests, docs |
208
- | Review risk | low / medium / high | complexity, cross-stack scope, security/data changes |
213
+ | Risk | max tier; count of tier-2 tasks | tier 2 tasks need three refuters and park in the decision queue for the human |
209
214
  | Suggested delivery | single-pr / ask-on-risk / autochain / exception | matched to config.review_budget |
210
215
  ```
211
216
 
212
- Pick the delivery strategy from the forecast: tiny change → `single-pr`; estimated
213
- lines over `config.sdd.review_budget.line_budget` (default 400) → recommend
214
- splitting or `ask-on-risk`; many areas or cross-stack contracts touched →
215
- `autochain` or a feature-track branch. A deadline or emergency that justifies a
216
- large review is marked `exception` and requires explicit user approval later.
217
+ Delivery: tiny → `single-pr`; over `config.sdd.review_budget.line_budget` (default
218
+ 400) → split or `ask-on-risk`; many areas or cross-stack contracts → `autochain`;
219
+ `exception` (deadline, emergency) needs explicit user approval.
217
220
 
218
221
  ## Anti-patterns → STOP
219
222
 
@@ -223,9 +226,10 @@ large review is marked `exception` and requires explicit user approval later.
223
226
  | "Done-check: 'feature works'" | Not runnable, not evidence. Quote the literal test/command, or it's not a done-check. |
224
227
  | "One giant task: 'build the backend'" | Unverifiable, un-handoffable. Split until each slice has its own runnable check. |
225
228
  | "Mark everything `[P]` so it goes faster" | Shared files = merge collisions. `[P]` only when scope is truly disjoint. |
226
- | "This task has no spec line but we obviously need it" | That's scope creep wearing a hat. Trace it or cut it; if it's real, send it to `clarify`. |
229
+ | "This task has no criterion but we obviously need it" | That's scope creep wearing a hat. Trace it or cut it; if it's real, send it to `clarify`. |
227
230
  | "The plan is thin here, I'll guess the slice" | `analyze` will reject the guess next phase. Tighten the plan first. |
228
231
  | "I'll start coding the easy task while I list the rest" | This phase writes zero runtime code. Implementation is `implement`. |
232
+ | "Risk is obviously low, I'll write 0" | The tier comes from `rsc risk` on the paths, never from the agent's judgement. |
229
233
  | "Renumber the IDs so they're contiguous" | Downstream phases cite IDs. They're stable once written. Append, don't renumber. |
230
234
 
231
235
  ## Result envelope
@@ -243,19 +247,19 @@ End with:
243
247
  "used": ["tasks"],
244
248
  "missing": [],
245
249
  "fallback": [],
246
- "compact_rules": ["Every task needs a runnable done-check.", "Forecast review load before implementation."]
250
+ "compact_rules": ["Every task needs a runnable done-check, a criterion trace and a risk forecast.", "Forecast review load before implementation."]
247
251
  },
248
252
  "evidence": ["task table appended", "review workload forecast appended"]
249
253
  }
250
254
  ```
251
255
 
252
- ## Next in the SDD chain
256
+ ## Next in ASDD
253
257
 
254
258
  The task list is now the contract for the rest of the build. Hand off to
255
259
  **`analyze`** — the consistency gate that cross-checks constitution ↔ spec ↔ plan
256
- ↔ **tasks** and surfaces orphan tasks and untraceable scope — and then to
257
- **`implement`**, which executes the list TDD-style, using `parallel` for the `[P]`
258
- tasks and `worktrees` for isolation.
260
+ ↔ **tasks** and flags uncovered criteria and untraced tasks — and then to
261
+ **`implement`** (stage 4), which runs each task through its build–review–fix loop,
262
+ using `parallel` for the `[P]` tasks and `worktrees` for isolation.
259
263
 
260
264
  Do not jump straight to `implement`. The whole point of writing a checkable list
261
265
  was to let `analyze` audit it cheaply *before* code exists.
@@ -20,7 +20,7 @@ human spot-check.
20
20
  Goal: the skill fires when it should and never on a near-miss.
21
21
 
22
22
  1. Configure an agent with the **full catalog of skill descriptions** available
23
- for routing — the other rsc-sdd phases (`sdd`, `constitution`, `specify`,
23
+ for routing — the other rsc-sdd phases (`asdd`, `constitution`, `specify`,
24
24
  `clarify`, `plan`, `analyze`, `implement`, `verify`, `review`, `ship`,
25
25
  `debug`, `worktrees`, `parallel`) plus the stack/process skills (`fastapi`,
26
26
  `nextjs`, `go`, `postgresdb`, `flutter`, `harness`, …) — so routing competes
@@ -66,10 +66,10 @@ capability:
66
66
  must_include:
67
67
  - "Confirms the inputs first: plan, spec, AND constitution must exist; if the plan were missing it would STOP and route to `plan` rather than slice from intent."
68
68
  - "Reads technical_level from 02-DOCS/wiki/harness/user-profile.md and picks the register; the table is complete either way, and the chat adds only one line of why-this-slicing plus the risky tasks."
69
- - "Emits sequential zero-padded stable IDs (T001, T002… — no hyphen, GitHub Spec Kit canon) with the six well-formed fields: ID, a separate [P] marker field after the ID, imperative title, done-check, depends-on, and a trace to a spec section."
69
+ - "Emits sequential zero-padded stable IDs (T001, T002… — no hyphen, GitHub Spec Kit canon) with the seven well-formed fields: ID, a separate [P] marker field after the ID, imperative title, done-check, depends-on, a trace to the spec acceptance criterion it covers, and a risk forecast (tier 0/1/2 from `npx @ericrisco/rsc risk <expected paths>`, never guessed)."
70
70
  - "Every done-check is a LITERAL runnable command or checkable observation (e.g. `pytest tests/auth/test_login.py` green; `alembic upgrade head` applies; bad password → 401) — never 'login works'."
71
71
  - "Orders by dependency (migration/schema → data → service → API), so no task precedes what it depends on; marks `[P]` only on file/state-disjoint tasks and defaults to sequential when unsure."
72
72
  - "Uses TDD-shaped slicing: a failing-test task before its implementation task, whose done-check is 'the test that was red is now green'."
73
- - "Ensures every task traces to a spec line and cuts/flags any task with no trace as scope creep (route to clarify/specify), rather than inventing scope."
74
- - "Appends the table under a `## Tasks` heading INSIDE 02-DOCS/wiki/sdd/plans/auth.md (not a new file) and confirms the plan is indexed in the root CLAUDE.md Knowledge map under sdd/."
73
+ - "Checks coverage both ways: every task traces to a criterion and every criterion has a task; a task with no criterion is cut/flagged as scope creep (route to clarify/specify), and either gap is a defect analyze will flag."
74
+ - "Appends the table under a `## Tasks` heading INSIDE 02-DOCS/wiki/sdd/plans/auth.md (not a new file) and confirms the plan is indexed in 02-DOCS/wiki/index.md under sdd/ (never the root CLAUDE.md)."
75
75
  - "Adds a final closer task gating on all done-checks passing plus `scripts/verify.sh` green, and hands off to `analyze` next (then implement) — not straight to implementation."
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: verify
3
- description: "Use when implementation is finished and about to be called done or merged — the rsc-sdd evidence gate: runs the stack's scripts/verify.sh (lint, type, test, audit), walks every task done-check and acceptance criterion, records a dated verdict. NOT judging the diff by eye (that is `review`, spec-less `code-review`), NOT diagnosis (that is `debug`)."
4
- tags: [sdd, verify, test]
3
+ description: "Use when work is about to be called done — the ASDD evidence gate, in two modes: per task inside `implement`'s loop (tests, build, security, traceability) and at closing over the whole branch against main (stack scripts/verify.sh, every done-check and acceptance criterion, rollback plan, dated verdict). A check that did not execute is reported not run, never passed. NOT judging the diff by eye (that is `review`, spec-less `code-review`), NOT diagnosis (that is `debug`)."
4
+ tags: [asdd, sdd, verify, test]
5
5
  recommends: [review]
6
6
  profiles: [core, full]
7
7
  origin: risco
@@ -9,7 +9,7 @@ origin: risco
9
9
 
10
10
  # verify — the evidence gate before "done"
11
11
 
12
- `verify` is the post-implementation **gate** in the rsc-sdd chain. Implementation just finished; nobody is allowed to call it *done*, *fixed*, *working*, or *ready to merge* until the evidence exists and has been read. This skill produces that evidence: it runs the project's real quality gate, walks every task's done-check and every acceptance criterion from the spec, and records the result. Pass or fail, the verdict is grounded in command output you actually saw — never in a hunch.
12
+ `verify` is the evidence **gate** of ASDD. Nobody is allowed to call work *done*, *fixed*, *working*, or *ready to merge* until the evidence exists and has been read. This skill produces that evidence: it runs the project's real quality gate, walks every task's done-check and every acceptance criterion from the spec, and records the result. Pass or fail, the verdict is grounded in command output you actually saw — never in a hunch.
13
13
 
14
14
  The one rule everything else serves: **a claim of done is a claim about evidence. No evidence, no claim.** A green feeling is not green output.
15
15
 
@@ -19,7 +19,7 @@ Not this phase: still writing tests or production code → `../implement/SKILL.m
19
19
 
20
20
  ## Model tier — `balanced` (opt-in routing)
21
21
 
22
- This phase's default model tier is **`balanced`** — it runs the checks and interprets failures with judgment. 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.
22
+ This phase's default model tier is **`balanced`** — it runs the checks and interprets failures with judgment. 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.
23
23
 
24
24
  ## Read the room first (register)
25
25
 
@@ -27,12 +27,24 @@ Before running anything, read `technical_level` in `02-DOCS/wiki/harness/user-pr
27
27
 
28
28
  Report in the `orient` voice: pass/fail per gate, then one line of *why* per failing check, then what the user should decide next. For a `non-technical` reader, say what each failing check protects with an everyday analogy.
29
29
 
30
- ## The gate (run in this order)
30
+ ## Two modes
31
+
32
+ | Mode | When | Scope | Output |
33
+ | --- | --- | --- | --- |
34
+ | **Per task** | step e of `implement`'s loop, every tier | the task's diff: **tests** (affected suite), **build** (compile + types), **security** (stack audit; `../secure-coding/SKILL.md` on input/authz/secrets), **traceability** (every new behaviour traces to a spec criterion — untraced behaviour is a finding) | one line per control in the task's progress entry |
35
+ | **Closing** | ASDD stage 5, once, before `review` and `ship` | the whole branch against main: merge main in (or rebase) and run the full gate below on the result | the dated record, with a rollback plan |
36
+
37
+ **Both modes, one rule: a check that was not executed is listed as not run** — never as passed, never
38
+ omitted. A task with a control **not run** is not done; a closing record with one is a FAIL.
39
+
40
+ ## The closing gate (run in this order)
31
41
 
32
42
  ```text
33
43
  LOCATE → find the spec, the task done-checks, and which stack(s) changed
34
- RUN → execute the relevant stack scripts/verify.sh (lint, type, test+coverage, audit)
44
+ RUN → execute the relevant stack scripts/verify.sh (lint, type, test+coverage, audit),
45
+ or the generic gate from config.yaml when no stack ships one
35
46
  WALK → check every task done-check and every spec acceptance criterion against real output
47
+ ROLLBACK→ write how to undo the change (revert sha, migration down, flag off) into the record
36
48
  RECORD → write the verification record under 02-DOCS/wiki/sdd/, index it
37
49
  VERDICT → PASS only if every item has passing evidence; otherwise FAIL with the gaps + handoff
38
50
  ```
@@ -46,10 +58,10 @@ Never collapse a step. Never write the verdict before RUN and WALK have produced
46
58
  non-trivial, mark that as a verification risk and recommend `sdd-init`.
47
59
  - Read the spec at `02-DOCS/wiki/sdd/specs/<slug>.md` for its **acceptance criteria**.
48
60
  - Read the plan/task list at `02-DOCS/wiki/sdd/plans/<slug>.md` for each task's **done-check**.
49
- - Read `02-DOCS/wiki/sdd/progress/<slug>.md` if present for apply evidence and
50
- completed tasks. Missing progress does not automatically fail, but it is a
51
- traceability gap to record.
52
- - Determine which subprojects/stacks the change touched (from `git status`/`git diff --name-only` and the manifests). That tells you *which* stack `verify.sh` to run — possibly more than one in a monorepo.
61
+ - Read `02-DOCS/wiki/sdd/progress/<slug>.md` for apply evidence, per-task controls and the
62
+ `## Decision queue`. Missing progress is a traceability gap to record; parked tasks are listed
63
+ in the record, never counted as done.
64
+ - Determine which subprojects/stacks the branch touched (from `git diff --name-only main...HEAD` and the manifests). That tells you *which* stack `verify.sh` to run — possibly more than one in a monorepo.
53
65
  - If the constitution exists (`02-DOCS/wiki/sdd/constitution.md`), note its quality bars (coverage floor, lint level) — they are part of the gate.
54
66
 
55
67
  If the spec or task list is missing, stop: there is nothing to verify against. Say so and point back up the chain.
@@ -80,7 +92,7 @@ Rules for RUN:
80
92
  - A `GAP` is the third thing, and it is the one that looks most like a pass: **the layer ran and had nothing to fail with.** The fastapi gate reports it when coverage runs with no `--cov-fail-under` configured; the Go gate reports it always, because `go test -cover` takes no threshold. The script deliberately does not fail on a GAP — that judgement is yours. **Treat a GAP as an unverified criterion, which fails the verdict**, and record it (see the mapping below). A gate that reports and a gate that checks are indistinguishable on screen, and only the broken one is guaranteed to stay green.
81
93
  - A non-zero exit from a tool that actually ran is a hard FAIL. Record the failing tool and its output.
82
94
  - Run from the directory the stack skill documents (Go runs from the module root; others from the subproject root). Don't guess paths.
83
- - If no `scripts/verify.sh` exists for a touched stack, that's a gap — say the gate is incomplete and recommend the user add the stack skill, rather than hand-rolling a one-off check that drifts from the real gate.
95
+ - **No stack skill ships a `scripts/verify.sh` for this stack?** Run the **generic gate**: the test, lint, type, build and audit commands `sdd-init` recorded in `02-DOCS/wiki/sdd/config.yaml`, each recorded as pass, fail or **not run**. It is a full gate, not a lesser one. Procedure: `references/generic-gate.md`.
84
96
 
85
97
  #### A home-grown gate must prove it can fail — and that it can pass
86
98
 
@@ -95,7 +107,7 @@ The dangerous way a checker breaks is not a crash — it is **fail-open**: nothi
95
107
 
96
108
  **Over-blocking is not the safe side.** It feels like caution and it is not: a gate that fires on correct work gets muted, worked around, or wedges the pipeline that depends on it, which is the same damage as a gate that checks nothing with the sign reversed. And it is *harder* to notice, because the failure arrives dressed as diligence.
97
109
 
98
- Where this bites hardest is a check that matches text rather than structure — **"the path appears in the string" is not "the write targets that location"**. Our own integrity gate learned this twice in one day: it flagged a transcript that merely *named* a protected path (the skill body it had been handed named it), then flagged a sandbox directory whose path *contained* the protected one as a substring. Twelve mutants had proven it could fail; not one had asked whether it could pass, so the defect shipped and surfaced on first real use with the filesystem provably untouched. → `02-DOCS/wiki/harness/puertas-y-mecanismos.md`
110
+ Where this bites hardest is a check that matches text rather than structure — **"the path appears in the string" is not "the write targets that location"**. Our own integrity gate shipped that defect: twelve mutants proved it could fail, none asked whether it could pass — and the first real run blocked every reading of a protected file as if it were a write to it.
99
111
 
100
112
  So when you write the negative control, write the positive one beside it, and prefer matching **structure** (a parsed tool call, a resolved path, an exit code) over matching text that happens to contain the thing you care about.
101
113
 
@@ -138,6 +150,9 @@ source_state: 3f9a1c2 (clean)
138
150
  - [x] AC1 user can place an order — evidence: e2e test green
139
151
  - [ ] AC3 duplicate submit is a no-op — UNVERIFIED: no observable proof
140
152
 
153
+ ## Rollback plan
154
+ - `git revert <merge sha>`; migration `0042` has a tested down step; no flag involved.
155
+
141
156
  ## Capas no ejecutadas
142
157
  - **NO-APLICA** — mutation: no logic module changed, only templates.
143
158
  - **HERRAMIENTA-AUSENTE** — mypy: not installed in this environment; verify.sh printed SKIP and **nothing ran in its place**.
@@ -153,7 +168,7 @@ source_state: 3f9a1c2 (clean)
153
168
 
154
169
  **All numbers come from one fresh run made after the last edit.** A figure from mid-task is stale and does not go in the record, however true it was when you saw it.
155
170
 
156
- **Split "didn't run" three ways**, because one `SKIP` list collapses three different confidence claims and they read identically:
171
+ **Every not-run check is listed, labelled.** Split "didn't run" three ways, because one `SKIP` list collapses three different confidence claims and they read identically:
157
172
 
158
173
  | Label | Means | Reader should conclude |
159
174
  |---|---|---|
@@ -174,11 +189,11 @@ source_state: 3f9a1c2 (clean)
174
189
 
175
190
  **A dismissed finding carries evidence, one line each.** A fix is self-evidencing — the test now passes. A dismissal is not: "not a real problem" is indistinguishable from "did not check". Name the command, the `file:line`, or the test that disproves it, or write "ninguno".
176
191
 
177
- Append-only spirit: don't overwrite a prior run's record; a new run is a new dated file. The record is the receipt the `review` and `ship` phases trust.
192
+ Append-only spirit: don't overwrite a prior run's record; a new run is a new dated file — a re-run on the same day writes `<slug>-YYYY-MM-DD-r2.md` (then `-r3`, …). The record is the receipt the `review` and `ship` phases trust.
178
193
 
179
194
  ### 5 — VERDICT
180
195
 
181
- - **PASS** only when *every* stack gate that ran is green (no FAILs), *every* task done-check is met with evidence, and *every* acceptance criterion has observable proof. Then, and only then, say it's verified — and point to `review`.
196
+ - **PASS** only when *every* stack gate ran green (none **not run** but `NO-APLICA`), *every* task done-check is met with evidence, *every* acceptance criterion has observable proof, and the rollback plan is written. Then, and only then, say it's verified — and point to `review`. Parked tasks don't block the verdict; they travel to the human with it.
182
197
  - **FAIL** the moment any item lacks passing evidence. List the open items precisely (which check, which criterion, what's missing). Do not soften it. A single unverified acceptance criterion fails the whole gate.
183
198
  - Hand each failing kind to the right place: a failing test/type error → `debug`; a missing test for a criterion → back to `implement`; a spec ambiguity that surfaced → `clarify`.
184
199
 
@@ -187,7 +202,8 @@ Append-only spirit: don't overwrite a prior run's record; a new run is a new dat
187
202
  | Anti-pattern | Why it fails / fix |
188
203
  | --- | --- |
189
204
  | "The tests passed last run, I'll trust that." | Re-run now. Code changed since; stale green is not green. Evidence is current, or it isn't evidence. |
190
- | "verify.sh printed SKIP for the tests — close enough." | A SKIP is *unverified*, not passing. The criterion it covers is still open until a real run passes. |
205
+ | "verify.sh printed SKIP for the tests — close enough." | A SKIP is **not run**, not passing. The criterion it covers is still open until a real run passes. |
206
+ | "The security audit didn't run, but the task is clearly fine." | List it **not run**. The task is not done until it runs or the human accepts the gap. |
191
207
  | "I read the code and the criterion is obviously satisfied." | Reading is `review`. Verifying needs an observable result — a test, a response, a rendered page. |
192
208
  | "Lint and types are green, so it's done." | The stack gate proves clean code, not correct behavior. Walk the acceptance criteria too. |
193
209
  | "Coverage dipped below the floor but the feature works." | The constitution's coverage bar is part of the gate. Below floor = FAIL, not a footnote. |
@@ -220,9 +236,9 @@ End with:
220
236
  }
221
237
  ```
222
238
 
223
- ## Next in the chain
239
+ ## Next in ASDD
224
240
 
225
- A **PASS** record is the entry ticket to the next phase: **`../review/SKILL.md`** (adversarial read of the diff for what the gate can't catch), then **`../ship/SKILL.md`** (PR/merge). A **FAIL** routes back per the VERDICT step. Either way the verification record under `02-DOCS/wiki/sdd/verifications/` travels with the work as its proof.
241
+ Per-task mode returns to `implement`'s loop (step f). A closing **PASS** record is the entry ticket to **`../review/SKILL.md`** (whole-branch adversarial read against the spec), then **`../ship/SKILL.md`** (PR with Testing / Risk / How to revert). A **FAIL** routes back per the VERDICT step. Either way the verification record under `02-DOCS/wiki/sdd/verifications/` travels with the work as its proof.
226
242
 
227
243
  ## Orientación (siempre)
228
244
 
@@ -1,6 +1,6 @@
1
1
  # Eval harness — `verify` skill
2
2
 
3
- `verify` is the rsc-sdd post-implementation **gate**. These evals check two
3
+ `verify` is the ASDD evidence **gate** (per task and at closing). These evals check two
4
4
  things: that it **triggers** at the right moment (someone is about to claim
5
5
  "done"/"green"/"ready to merge") and stays quiet on near-misses, and that it
6
6
  **measurably changes the behavior** — running the real stack gate, walking the
@@ -28,7 +28,7 @@ pure shell runner.
28
28
  Goal: the skill fires when a "done" claim is imminent and never on a near-miss.
29
29
 
30
30
  1. Configure an agent with the **full catalog of skill descriptions** available
31
- for routing (verify + the rsc-sdd phase skills it chains to — implement,
31
+ for routing (verify + the ASDD phase skills it chains to — implement,
32
32
  debug, review, clarify, ship — plus the stack/process skills: fastapi, go,
33
33
  nextjs, flutter, postgresdb, secure-coding, deployment, harness, init) so
34
34
  routing competes realistically.
@@ -94,11 +94,12 @@ capability:
94
94
  - "Treats the mypy SKIP as UNVERIFIED rather than a pass, and does not let 'everything else was green' stand in for the skipped layer."
95
95
  - "WALKS all three done-checks and all three acceptance criteria, and catches that AC2/T2 (duplicate POST is a no-op) has NO test at all — the two named tests cover AC1 and AC3 only — marking it UNVERIFIED rather than passing it."
96
96
  - "Does NOT fix a failing test inline; if a check fails it records it and hands off to debug, and refuses to fix-then-claim."
97
- - "RECORDS a dated verification file under 02-DOCS/wiki/sdd/verifications/ and indexes it in the root CLAUDE.md Knowledge map."
97
+ - "RECORDS a dated verification file under 02-DOCS/wiki/sdd/verifications/ and indexes it in 02-DOCS/wiki/index.md (never the root CLAUDE.md)."
98
98
  - "Returns a VERDICT that is PASS only if EVERY item has passing evidence; a single unverified acceptance criterion makes the whole verdict FAIL — and refuses to call it 'done' otherwise."
99
99
  - "On PASS, points to the review phase; on FAIL, routes failing checks to debug and missing coverage back to implement."
100
100
  - "Treats a coverage GAP (the layer ran with no threshold configured, so it could not have failed) as an unverified criterion that fails the verdict — not as a pass — and records it as SUSTITUIDA: a report in place of a gate, unable to detect coverage regressing."
101
101
  - "Before trusting any home-grown gate that reports green, asks for it to be watched failing on a known-bad input, and states the limit: one negative control proves one bad case reaches the failure path, not that the gate covers the rule it claims to enforce."
102
102
  - "Splits layers that did not run into NO-APLICA / HERRAMIENTA-AUSENTE / SUSTITUIDA rather than one undifferentiated SKIP list, and never writes SUSTITUIDA as a pass."
103
103
  - "Binds the record to a source state (short commit SHA plus clean/dirty) and takes every number from one fresh run made after the last edit, refusing to reuse mid-task figures."
104
+ - "Lists every check that did not execute (the mypy SKIP) as 'not run', never as passed, and writes a rollback plan into the closing record."
104
105
  - "Gives any dismissed finding its own evidence line (command, file:line, or the test that disproves it) rather than asserting it was not a real problem."
@@ -0,0 +1,43 @@
1
+ # Generic gate — when no stack skill ships `scripts/verify.sh`
2
+
3
+ A project in a stack the catalog has no gate for (a plain Node CLI, a Rust crate, a shell tool) is
4
+ still verified. The gate is the commands the project itself declares, as `sdd-init` recorded them.
5
+ This is a first-class path, not a fallback to apologise for.
6
+
7
+ ## Where the commands come from
8
+
9
+ `02-DOCS/wiki/sdd/config.yaml` → `testing.commands.verify` (and `testing.commands.apply` for the
10
+ suite), filled by `sdd-init` from the manifests (`package.json` scripts, `Makefile`, `Cargo.toml`,
11
+ `pyproject.toml`…). No config → run `sdd-init` first; do not pick commands from memory.
12
+
13
+ ## The five layers
14
+
15
+ | Layer | What counts | Typical source |
16
+ | --- | --- | --- |
17
+ | test | the full suite | `npm test`, `cargo test`, `pytest` |
18
+ | lint | the project's linter | `npm run lint`, `cargo clippy` |
19
+ | type | the type checker | `tsc --noEmit`, `mypy` |
20
+ | build | the build or compile | `npm run build`, `cargo build` |
21
+ | audit | dependency vulnerabilities | `npm audit`, `cargo audit`, `pip-audit` |
22
+
23
+ ## Procedure
24
+
25
+ 1. List the five layers. For each, take the command from `config.yaml`; if it declares none, look
26
+ for one in the manifest and say where you found it.
27
+ 2. Run each command once, after the last edit, from the project root. Capture the exit code and the
28
+ relevant output verbatim.
29
+ 3. Record every layer as exactly one of:
30
+ - **pass** — the command ran and exited 0;
31
+ - **fail** — the command ran and exited non-zero (a hard FAIL of the verdict);
32
+ - **not run** — no command is declared or the tool is missing. Label it as `verify` does:
33
+ `NO-APLICA` (no such surface, e.g. no types in plain JS), `HERRAMIENTA-AUSENTE` (the surface
34
+ exists, nothing checked it).
35
+ 4. Write the layers under `## Stack gate` in the record, as `generic gate (config.yaml) → …`, one
36
+ line per layer, with the command that ran.
37
+
38
+ ## Rules that still apply
39
+
40
+ - A `not run` layer whose surface exists fails the verdict, exactly like a SKIP in a stack script.
41
+ - A command the project wrote (a custom check script) is a home-grown gate: watch it fail and pass
42
+ before trusting it.
43
+ - Suggest a stack skill if one fits — but the generic gate is the verdict for this run.
@@ -184,7 +184,7 @@ a linked worktree (`git rev-parse --git-dir` ≠ `--git-common-dir`), rule out a
184
184
 
185
185
  ## Model tier — `light` (opt-in routing)
186
186
 
187
- This phase's default model tier is **`light`** — isolating the workspace is mechanical git work. 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.
187
+ This phase's default model tier is **`light`** — isolating the workspace is mechanical git work. 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.
188
188
 
189
189
  ## Talking about it
190
190
 
@@ -95,7 +95,7 @@ export function readManifest(root) {
95
95
  const ids = (v) => (Array.isArray(v) ? v.filter((x) => typeof x === 'string' && ID.test(x)) : []);
96
96
  // Retired skills read as their successors. Canonical map: scripts/lib/retired-skills.js — inlined
97
97
  // because this file cannot import anything, and tests/retired-skills.test.js holds the two equal.
98
- const RETIRED = { eli5: 'orient', 'show-me': 'orient', bro: 'unslop', 'design-loop': 'design', 'design-dna': 'design', 'motion-craft': 'design', 'ui-engineering': 'ux' };
98
+ const RETIRED = { eli5: 'orient', 'show-me': 'orient', bro: 'unslop', 'design-loop': 'design', 'design-dna': 'design', 'motion-craft': 'design', 'ui-engineering': 'ux', sdd: 'asdd' };
99
99
  const skills = [...new Set(ids(parsed.skills).map((id) => (Object.hasOwn(RETIRED, id) ? RETIRED[id] : id)))];
100
100
  const own = ids(parsed.ownSkills);
101
101
  // A manifest with nothing declared is not a clone waiting to be equipped; there is nothing to offer.
@@ -97,10 +97,10 @@ Reply in the person's language, not this text's.
97
97
  question and stay read-only; ambiguity slows the lane, it never raises it.
98
98
  - Authorises a change? YOU pick the lane, say it and why: simple -> FTD (one
99
99
  feature document, tasks checked off against observed proof); big or
100
- interlocking decisions -> SDD (spec + clarify approved, then autopilot or not).
100
+ interlocking decisions -> ASDD (spec + clarify approved, review in every task).
101
101
  - Code change: the line below says where it goes in this project. Never open
102
102
  a branch on your own. Another session here: .worktrees/<branch>.
103
- Method: \`ftd\` · chain: \`sdd\` · full decisor in the always-on \`suggest\` body.
103
+ Method: \`ftd\` · chain: \`asdd\` · full decisor in the always-on \`suggest\` body.
104
104
  =================================================
105
105
  `;
106
106
 
package/targets/sello.mjs CHANGED
@@ -42,9 +42,14 @@ import { spawnSync } from 'node:child_process';
42
42
  // with no switch to stop it, so leaving it out made the lockout permanent.
43
43
  export const SELLO_STATE_PATHS = [
44
44
  '.rsc/sello.json', '.rsc/sello-config.json', '.rsc/sello-findings.md', '.rsc/sello-log.jsonl',
45
- '.rsc/automation-gaps.md',
45
+ '.rsc/automation-gaps.md', '.rsc/review-ledger.jsonl',
46
46
  ];
47
47
 
48
+ // Worktrees nested inside the project (the assistant's native `.claude/worktrees/`, rsc's own
49
+ // `.worktrees/`) are other checkouts, not part of this change. Untracked, they showed up as one
50
+ // `.claude/…` path, scored as harness — tier 2 for every task of a run that used isolation.
51
+ export const NESTED_WORKTREES = /^(?:\.claude\/worktrees|\.worktrees)(?:\/|$)/;
52
+
48
53
  export function selloPaths(root) {
49
54
  return {
50
55
  config: join(root, '.rsc', 'sello-config.json'),
@@ -241,7 +246,8 @@ export function computeCandidate(rawRoot, cfg = null) {
241
246
 
242
247
  const names = new Set();
243
248
  for (const n of nulList(git(root, 'diff', '--name-only', '-z', base))) names.add(n);
244
- for (const n of nulList(git(root, 'ls-files', '--others', '--exclude-standard', '-z'))) names.add(n);
249
+ // Untracked nested worktrees are other checkouts; committed paths under them are part of the change.
250
+ for (const n of nulList(git(root, 'ls-files', '--others', '--exclude-standard', '-z'))) if (!NESTED_WORKTREES.test(n)) names.add(n);
245
251
  for (const p of SELLO_STATE_PATHS) names.delete(p);
246
252
 
247
253
  const files = {};
@@ -266,7 +272,7 @@ export function computeCandidate(rawRoot, cfg = null) {
266
272
  // Untracked files never appear in numstat, so a fix hidden in a new file would
267
273
  // cost zero budget. Count their lines.
268
274
  for (const n of nulList(git(root, 'ls-files', '--others', '--exclude-standard', '-z'))) {
269
- if (SELLO_STATE_PATHS.includes(n) || numstat[n] !== undefined) continue;
275
+ if (SELLO_STATE_PATHS.includes(n) || NESTED_WORKTREES.test(n) || numstat[n] !== undefined) continue;
270
276
  try {
271
277
  const st = lstatSync(join(root, n));
272
278
  if (st.isFile()) numstat[n] = readFileSync(join(root, n), 'utf8').split('\n').length;