@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.
- package/README.md +16 -9
- package/manifest.json +48 -40
- package/package.json +1 -1
- package/scripts/lib/domains.js +1 -1
- package/scripts/lib/onboarding-apply.js +1 -1
- package/scripts/lib/onboarding.js +7 -7
- package/scripts/lib/retired-skills.js +4 -0
- package/scripts/lib/review-findings.js +3 -0
- package/scripts/lib/review-ledger.js +187 -0
- package/scripts/lib/risk.js +68 -0
- package/scripts/lib/spec-gate.js +10 -1
- package/scripts/rsc.js +104 -6
- package/scripts/spec-gate.js +13 -3
- package/skills/analyze/SKILL.md +3 -3
- package/skills/asdd/SKILL.md +183 -0
- package/skills/{sdd → asdd}/evals/README.md +10 -10
- package/skills/{sdd → asdd}/evals/cases.yaml +5 -5
- package/skills/{sdd → asdd}/references/model-routing.md +2 -2
- package/skills/building-agents/SKILL.md +1 -1
- package/skills/clarify/SKILL.md +59 -55
- package/skills/clarify/evals/cases.yaml +6 -7
- package/skills/constitution/SKILL.md +30 -16
- package/skills/constitution/evals/README.md +1 -1
- package/skills/constitution/evals/cases.yaml +1 -1
- package/skills/debug/SKILL.md +3 -3
- package/skills/fastapi/SKILL.md +1 -1
- package/skills/flutter/SKILL.md +1 -1
- package/skills/ftd/SKILL.md +26 -10
- package/skills/ftd/evals/cases.yaml +1 -1
- package/skills/ftd/evals/routing.json +4 -4
- package/skills/go/SKILL.md +1 -1
- package/skills/implement/SKILL.md +147 -132
- package/skills/implement/evals/cases.yaml +4 -2
- package/skills/implement/references/per-task-review.md +26 -24
- package/skills/implement/scripts/task-brief +28 -22
- package/skills/nextjs/SKILL.md +1 -1
- package/skills/parallel/SKILL.md +1 -1
- package/skills/plan/SKILL.md +3 -3
- package/skills/plan/evals/cases.yaml +1 -1
- package/skills/plan/references/plan-template.md +2 -2
- package/skills/postgresdb/SKILL.md +1 -1
- package/skills/react/SKILL.md +1 -1
- package/skills/review/SKILL.md +80 -41
- package/skills/sdd-init/SKILL.md +11 -9
- package/skills/ship/SKILL.md +58 -30
- package/skills/specify/SKILL.md +42 -41
- package/skills/specify/evals/cases.yaml +2 -2
- package/skills/specify/references/eliciting-requirements.md +1 -1
- package/skills/specify/references/spec-template.md +28 -6
- package/skills/suggest/SKILL.md +4 -4
- package/skills/tasks/SKILL.md +37 -33
- package/skills/tasks/evals/README.md +1 -1
- package/skills/tasks/evals/cases.yaml +3 -3
- package/skills/verify/SKILL.md +34 -18
- package/skills/verify/evals/README.md +2 -2
- package/skills/verify/evals/cases.yaml +2 -1
- package/skills/verify/references/generic-gate.md +43 -0
- package/skills/worktrees/SKILL.md +1 -1
- package/targets/clone-bootstrap.mjs +1 -1
- package/targets/hook-once.mjs +2 -2
- package/targets/sello.mjs +9 -3
- package/skills/sdd/SKILL.md +0 -250
package/skills/tasks/SKILL.md
CHANGED
|
@@ -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
|
|
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
|
-
`../
|
|
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
|
|
49
|
-
|
|
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
|
|
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
|
|
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
|
|
129
|
-
or send the gap to `clarify`/`specify`.
|
|
130
|
-
6.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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 | — |
|
|
166
|
-
| T002 | [P] | Add `sessions` table migration | migration applies; FK to users present | T001 |
|
|
167
|
-
| T003 | | Write failing test for `POST /login` | `pytest tests/auth/test_login.py` fails: no route | T001 |
|
|
168
|
-
| T004 | | Implement `POST /login` | T003 test green; bad pw → 401 | T003 |
|
|
169
|
-
| ... | ... | ... | ... | ... | ... |
|
|
170
|
-
| T0NN | | All done-checks pass + `verify.sh` green | every row above checked; `scripts/verify.sh` exits 0 | all |
|
|
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
|
-
|
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
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
|
|
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
|
|
257
|
-
**`implement
|
|
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 (`
|
|
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
|
|
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
|
-
- "
|
|
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
|
|
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."
|
package/skills/verify/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: verify
|
|
3
|
-
description: "Use when
|
|
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
|
|
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 `../
|
|
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
|
-
##
|
|
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`
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
- Determine which subprojects/stacks the
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
239
|
+
## Next in ASDD
|
|
224
240
|
|
|
225
|
-
A **PASS** record is the entry ticket to
|
|
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
|
|
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
|
|
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
|
|
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 `../
|
|
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.
|
package/targets/hook-once.mjs
CHANGED
|
@@ -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 ->
|
|
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: \`
|
|
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
|
-
|
|
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;
|