@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
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
# task-brief PLAN_FILE N
|
|
3
|
-
# Extracts task
|
|
4
|
-
#
|
|
3
|
+
# Extracts task N from a plan/tasks markdown file into a brief file in the sdd-workspace dir, and
|
|
4
|
+
# prints its path. N may be written 2, 002 or T002: ids compare as numbers.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
6
|
+
# Two task shapes are read, because `tasks` prescribes a table and older plans used headings:
|
|
7
|
+
# - a table row `| T002 | ... |` → the table's header, its separator and that row;
|
|
8
|
+
# - a heading `### T2 …` / `## T002 …` → the block up to the next ## / ### heading.
|
|
9
|
+
# Both are printed when both exist (the row is the contract, the heading its detail).
|
|
8
10
|
# If task N is not found, exits nonzero with a clear message.
|
|
9
11
|
|
|
10
12
|
# Resolve the directory of this script portably.
|
|
@@ -13,7 +15,7 @@ _SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
13
15
|
usage() {
|
|
14
16
|
printf 'Usage: task-brief <PLAN_FILE> <N>\n' >&2
|
|
15
17
|
printf ' PLAN_FILE path to the tasks markdown file\n' >&2
|
|
16
|
-
printf ' N task
|
|
18
|
+
printf ' N task id to extract: 2, 002 or T002\n' >&2
|
|
17
19
|
exit 1
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -23,41 +25,44 @@ if [ $# -lt 2 ]; then
|
|
|
23
25
|
fi
|
|
24
26
|
|
|
25
27
|
PLAN_FILE="$1"
|
|
26
|
-
N="$2"
|
|
28
|
+
N="${2#T}"
|
|
29
|
+
N="${N#t}"
|
|
27
30
|
|
|
28
31
|
if [ ! -f "$PLAN_FILE" ]; then
|
|
29
32
|
printf 'error: plan file not found: %s\n' "$PLAN_FILE" >&2
|
|
30
33
|
exit 1
|
|
31
34
|
fi
|
|
32
35
|
|
|
33
|
-
# Validate N is a positive integer.
|
|
34
36
|
case "$N" in
|
|
35
37
|
''|*[!0-9]*)
|
|
36
|
-
printf 'error: N must be a
|
|
38
|
+
printf 'error: N must be a task number (2, 002 or T002), got: %s\n' "$2" >&2
|
|
37
39
|
exit 1
|
|
38
40
|
;;
|
|
39
41
|
esac
|
|
40
42
|
|
|
41
|
-
# Use awk to extract the task block.
|
|
42
|
-
# Match start: a heading line (## or ###) followed by T<N> then space, (, —, or end of line.
|
|
43
|
-
# Match end: the next heading line starting with ## (which includes ###).
|
|
44
|
-
# Exit code: 0 if found, 1 if not found.
|
|
45
43
|
CONTENT="$(awk -v n="$N" '
|
|
46
|
-
|
|
44
|
+
function idnum(s) { sub(/^[Tt]/, "", s); return s + 0 }
|
|
45
|
+
BEGIN { n = n + 0; printing = 0; found = 0; head = ""; sep = "" }
|
|
46
|
+
# Table rows: remember the latest header + separator, print the matching row.
|
|
47
|
+
/^\|/ {
|
|
48
|
+
split($0, cell, "|"); id = cell[2]; gsub(/[[:space:]]/, "", id)
|
|
49
|
+
if (id ~ /^[Tt][0-9]+$/) {
|
|
50
|
+
if (idnum(id) == n) { if (!printing) { if (head != "") print head; if (sep != "") print sep }; print; found = 1; next }
|
|
51
|
+
} else if ($0 ~ /^\|[-:| ]+\|[[:space:]]*$/) { sep = $0 }
|
|
52
|
+
else { head = $0; sep = "" }
|
|
53
|
+
if (!printing) next
|
|
54
|
+
}
|
|
47
55
|
/^#{2,3}[[:space:]]/ {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
printing=1; found=1; print; next
|
|
56
|
+
if (printing) { printing = 0 }
|
|
57
|
+
if (match($0, /^#{2,3}[[:space:]]+[Tt][0-9]+([[:space:](]|-|$)/)) {
|
|
58
|
+
t = $0; sub(/^#{2,3}[[:space:]]+/, "", t); sub(/[^Tt0-9].*$/, "", t)
|
|
59
|
+
if (idnum(t) == n) { printing = 1; found = 1; print ""; print; next }
|
|
53
60
|
}
|
|
54
|
-
# Any other ## / ### heading stops printing
|
|
55
|
-
if (printing) { exit }
|
|
56
61
|
}
|
|
57
62
|
{ if (printing) print }
|
|
58
63
|
END { if (!found) exit 1 }
|
|
59
64
|
' "$PLAN_FILE")" || {
|
|
60
|
-
printf 'error: task T%s not found in %s\n' "$N" "$PLAN_FILE" >&2
|
|
65
|
+
printf 'error: task T%s not found in %s (looked for a "| T%s |" table row and a "### T%s" heading)\n' "$N" "$PLAN_FILE" "$N" "$N" >&2
|
|
61
66
|
exit 1
|
|
62
67
|
}
|
|
63
68
|
|
|
@@ -71,7 +76,8 @@ SCRATCH="$(sh "$_SCRIPT_DIR/sdd-workspace")"
|
|
|
71
76
|
|
|
72
77
|
# Derive a readable unique name from the plan file basename and task number.
|
|
73
78
|
PLAN_BASENAME="$(basename "$PLAN_FILE" .md)"
|
|
74
|
-
|
|
79
|
+
NUM="$(printf %s "$N" | sed "s/^0*//")"
|
|
80
|
+
OUT="$SCRATCH/brief-${PLAN_BASENAME}-T${NUM:-0}.txt"
|
|
75
81
|
|
|
76
82
|
printf '%s\n' "$CONTENT" > "$OUT"
|
|
77
83
|
printf '%s\n' "$OUT"
|
package/skills/nextjs/SKILL.md
CHANGED
|
@@ -14,7 +14,7 @@ origin: risco
|
|
|
14
14
|
> behaviour change** and there is **no approved spec + plan** under `02-DOCS/wiki/sdd/`, STOP and
|
|
15
15
|
> hand off to `../specify/SKILL.md` (brainstorm → spec → plan → tasks); it routes back here once the
|
|
16
16
|
> plan is approved. Build directly only for a genuinely one-line / low-risk change. Method:
|
|
17
|
-
> `../
|
|
17
|
+
> `../asdd/SKILL.md`.
|
|
18
18
|
|
|
19
19
|
**Not this skill:** Pages Router (`pages/`) — note the difference, defer to the Next.js Pages docs.
|
|
20
20
|
A pure React SPA (Vite/CRA) → `../react/SKILL.md`; React Native / Expo → `../react-native/SKILL.md`;
|
package/skills/parallel/SKILL.md
CHANGED
|
@@ -73,7 +73,7 @@ SUBAGENT BRIEF (one per unit)
|
|
|
73
73
|
|
|
74
74
|
Each subagent still owns its own discipline inside its scope — TDD via `implement`, the stack skill's test mechanics, decision logging. This skill does not relax any of that; it just runs several of them at once.
|
|
75
75
|
|
|
76
|
-
**Per-unit model tier (when routing is enabled).** `parallel` has *no fixed tier* — this is the most concrete place per-phase model routing pays off. When `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`, give each unit the tier of the *kind of work it does*, not one tier for the whole fan-out: an implement-type unit → `balanced`, a scan/research/boilerplate unit → `light`, a unit doing genuine design or root-cause reasoning → `heavy`. Resolve the tier to a concrete model via `models.tiers` and **dispatch that subagent on that model** (e.g. Claude Code's `model` field on the Task/subagent) — real routing, independent of the session model. If routing is off or no profile exists, dispatch on the session model and say nothing. Full protocol: `../
|
|
76
|
+
**Per-unit model tier (when routing is enabled).** `parallel` has *no fixed tier* — this is the most concrete place per-phase model routing pays off. When `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`, give each unit the tier of the *kind of work it does*, not one tier for the whole fan-out: an implement-type unit → `balanced`, a scan/research/boilerplate unit → `light`, a unit doing genuine design or root-cause reasoning → `heavy`. Resolve the tier to a concrete model via `models.tiers` and **dispatch that subagent on that model** (e.g. Claude Code's `model` field on the Task/subagent) — real routing, independent of the session model. If routing is off or no profile exists, dispatch on the session model and say nothing. Full protocol: `../asdd/references/model-routing.md`.
|
|
77
77
|
|
|
78
78
|
**Set the model on every dispatch — an omitted model silently inherits the session's, usually the most expensive one, and that is how a fan-out's cost quietly explodes.** rsc installs a `developer` subagent pinned to the **balanced** tier (Sonnet by default; the user's onboarding choice in `.rsc/developer.json`, never `light`). For implement-type units, **dispatch to the `developer` agent** (e.g. Claude Code `subagent_type: developer`) — that's the deliberate cost cap the user asked for. Only escalate a genuinely heavy unit (real design / root-cause) to a heavy model, and only when routing is enabled; otherwise balanced is the floor and the ceiling. If you dispatch a raw subagent instead, name its tier explicitly.
|
|
79
79
|
|
package/skills/plan/SKILL.md
CHANGED
|
@@ -85,7 +85,7 @@ Three of them carry the weight and fail quietly:
|
|
|
85
85
|
|
|
86
86
|
Write `02-DOCS/wiki/sdd/plans/<slug>.md`, `<slug>` matching the spec's slug exactly — one plan per
|
|
87
87
|
spec, same name, because that is how `tasks`, `analyze` and `implement` find it. Then index it in
|
|
88
|
-
`02-DOCS/wiki/index.md` (the Knowledge map
|
|
88
|
+
`02-DOCS/wiki/index.md` (the Knowledge map) — never the root `CLAUDE.md`. If a plan for
|
|
89
89
|
this slug already exists, update it in place and note what changed — never fork a `-v2`.
|
|
90
90
|
|
|
91
91
|
## Model tier — `heavy` (opt-in routing)
|
|
@@ -93,7 +93,7 @@ this slug already exists, update it in place and note what changed — never for
|
|
|
93
93
|
Architecture, interfaces, data flow and risk are the heaviest reasoning in the chain, so this
|
|
94
94
|
phase's default tier is **`heavy`**. Routing is off unless `models.enabled: true` in
|
|
95
95
|
`02-DOCS/wiki/sdd/config.yaml`; the resolution order, the announce rule and the model table live in
|
|
96
|
-
`../
|
|
96
|
+
`../asdd/references/model-routing.md`. Routing off or no profile → session model, silently.
|
|
97
97
|
|
|
98
98
|
## Talking about the plan
|
|
99
99
|
|
|
@@ -140,7 +140,7 @@ regardless. (`implement` re-checks this as a hard gate before its first commit.)
|
|
|
140
140
|
## Result envelope
|
|
141
141
|
|
|
142
142
|
End with the parseable block every SDD phase shares, so the dispatcher can chain without
|
|
143
|
-
interpreting prose (contract: `../
|
|
143
|
+
interpreting prose (contract: `../asdd/SKILL.md`):
|
|
144
144
|
|
|
145
145
|
```json result-envelope
|
|
146
146
|
{
|
|
@@ -46,4 +46,4 @@ capability:
|
|
|
46
46
|
- "Testing strategy maps each acceptance criterion to a test level (unit/contract/integration/e2e), states what each asserts and what it mocks (e.g. the payments provider), and defers the test runner/tooling to the stack skill"
|
|
47
47
|
- "Risks section is ranked with trigger + impact + mitigation per risk (e.g. payments timeout, double-reservation race); does NOT claim zero risk"
|
|
48
48
|
- "Stays stack-neutral above the seam — no framework route decorators, ORM session code, or test-runner flags in the plan"
|
|
49
|
-
- "Ends by pointing to the tasks phase and notes indexing the plan in the root CLAUDE.md
|
|
49
|
+
- "Ends by pointing to the tasks phase and notes indexing the plan in 02-DOCS/wiki/index.md (the Knowledge map), never the root CLAUDE.md"
|
|
@@ -145,8 +145,8 @@ unit / contract / integration / e2e. Tooling is the stack skill's job — name t
|
|
|
145
145
|
defend it, not to enumerate every option without choosing.
|
|
146
146
|
- **Empty section = defect.** Every one of the seven must say something real before `tasks` runs.
|
|
147
147
|
If a section genuinely has no content (e.g. no migration), write "none — <why>", not silence.
|
|
148
|
-
- **Index it.** After writing, add the plan to
|
|
149
|
-
harness wiki and later phases can find it.
|
|
148
|
+
- **Index it.** After writing, add the plan to `02-DOCS/wiki/index.md` (the Knowledge map) so the
|
|
149
|
+
harness wiki and later phases can find it. Never edit the root `CLAUDE.md` — that is `harness`'s, and tier 2.
|
|
150
150
|
- **Global Constraints carry the verbatim values.** §0 is not a summary of §1 — §1 explains *which*
|
|
151
151
|
constraints shape the design and *why*; §0 lists the exact values a blind implementer and the
|
|
152
152
|
reviewer must honor. A value that lives only in prose ("use the project's naming rule") is a value
|
|
@@ -13,7 +13,7 @@ fix slow SQL, run zero-downtime migrations, and operate/secure the database. Too
|
|
|
13
13
|
example is runnable.
|
|
14
14
|
|
|
15
15
|
New, non-trivial feature with no approved spec + plan under `02-DOCS/wiki/sdd/`? Hand off to
|
|
16
|
-
[`specify`](../specify/SKILL.md) before writing feature code (method: [`
|
|
16
|
+
[`specify`](../specify/SKILL.md) before writing feature code (method: [`asdd`](../asdd/SKILL.md)); build
|
|
17
17
|
straight from here only for a genuinely one-line, low-risk change.
|
|
18
18
|
|
|
19
19
|
Deep dives: [schema-and-indexing](references/schema-and-indexing.md) (types, constraints, every index
|
package/skills/react/SKILL.md
CHANGED
|
@@ -17,7 +17,7 @@ or screen, and its motion layer, is `../design/SKILL.md`'s.
|
|
|
17
17
|
|
|
18
18
|
> Build or review a fast, typed, correctly-architected **client-side** React single-page app bundled by Vite. No server runtime, no RSC tree, no framework router. Server-rendered React (App Router, server actions, SSR/SSG) is not this skill — that is `../nextjs/SKILL.md`.
|
|
19
19
|
|
|
20
|
-
**SDD gate — before writing feature code.** If this skill fired on a **new, non-trivial feature or behaviour change** and there is **no approved spec + plan** under `02-DOCS/wiki/sdd/`, stop and hand off to `../specify/SKILL.md`: it runs brainstorm → spec → plan → tasks, then routes back here once the plan is approved. Build directly only for a genuinely one-line / low-risk change. Method: `../
|
|
20
|
+
**SDD gate — before writing feature code.** If this skill fired on a **new, non-trivial feature or behaviour change** and there is **no approved spec + plan** under `02-DOCS/wiki/sdd/`, stop and hand off to `../specify/SKILL.md`: it runs brainstorm → spec → plan → tasks, then routes back here once the plan is approved. Build directly only for a genuinely one-line / low-risk change. Method: `../asdd/SKILL.md`.
|
|
21
21
|
|
|
22
22
|
## First: confirm it's a Vite SPA, not a framework
|
|
23
23
|
|
package/skills/review/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review
|
|
3
|
-
description: "Use when a
|
|
4
|
-
tags: [
|
|
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
|
-
|
|
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. Its record — tier, counts, verdict, each finding with its outcome — goes to `02-DOCS/wiki/sdd/reviews/<slug>.md`.
|
|
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. `
|
|
26
|
-
2. `02-DOCS/wiki/sdd/
|
|
27
|
-
3. `02-DOCS/wiki/
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
@@ -169,21 +175,28 @@ Two extra finding classes the four inputs unlock, neither reachable from a diff:
|
|
|
169
175
|
report one defect three ways, and a lens hunting for something can overstate what it found. Two
|
|
170
176
|
steps, in this order:
|
|
171
177
|
|
|
172
|
-
1. **Group (deterministic).** Each lens ends its report with a ` ```json findings ` block
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
178
|
+
1. **Group (deterministic).** Each lens ends its report with a ` ```json findings ` block — an
|
|
179
|
+
array of objects in exactly this shape (give it to every reviewer verbatim):
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{"severity":"blocker|should-fix|nit|question","file":"path","line":N,"claim":"…","failure_scenario":"concrete input → wrong result","evidence":"quoted code","class":"bug class"}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Save the reports and run `npx @ericrisco/rsc review consolidate <reports…>`. It merges findings
|
|
186
|
+
on the same place that say the same thing, or that quote the same evidence; keeps the highest
|
|
187
|
+
severity and every lens that saw it; and **downgrades a blocker or should-fix with no
|
|
188
|
+
`file:line` or no `failure_scenario` to a question**. Several lenses on one finding is a reason
|
|
189
|
+
to look first, never a reason to raise its severity.
|
|
190
|
+
2. **Verify (judgement).** Consolidate marks `verify` on **every blocker and every should-fix**.
|
|
191
|
+
For each, dispatch the **`finding-verifier`** agent with that one finding plus the same four
|
|
192
|
+
inputs, in parallel. It starts from "this is false" and returns **CONFIRMED** (reproduced or
|
|
193
|
+
traced, with the proof), **REFUTED** (cites the guard that makes it impossible), or **UNPROVEN**
|
|
194
|
+
(becomes a question). It never gets the lens's reasoning.
|
|
182
195
|
|
|
183
196
|
Report the consolidated count honestly ("11 reported → 4 unique → 2 confirmed"), and list what was
|
|
184
197
|
refuted and why: a refuted finding hidden from the author is a review nobody can audit. At tier 1
|
|
185
|
-
(one
|
|
186
|
-
|
|
198
|
+
(one reviewer) there is nothing to group, but run `review consolidate` on its one report anyway:
|
|
199
|
+
it applies the downgrade, and every blocker and should-fix still goes to `finding-verifier`.
|
|
187
200
|
|
|
188
201
|
**A finding blocks only if it survives all three filters** — no exceptions, and eagerness to
|
|
189
202
|
find something is not evidence:
|
|
@@ -195,11 +208,38 @@ find something is not evidence:
|
|
|
195
208
|
3. **Evidence** — `finding-verifier` returned **CONFIRMED** with its proof. REFUTED is dropped (with
|
|
196
209
|
its reason in the report); UNPROVEN is a `[question]`, and questions don't block.
|
|
197
210
|
|
|
198
|
-
**Fixing a blocker is budgeted
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
211
|
+
**Fixing a blocker is budgeted.** Before touching anything: estimate the fix and declare it —
|
|
212
|
+
`npx @ericrisco/rsc sello budget --lines <N>`. After the fix: `sello budget-check` (over budget →
|
|
213
|
+
justify with `--justify "…"` or shrink; an unexplained overrun is how over-engineering enters
|
|
214
|
+
disguised as a fix). Then `sello freeze` + re-review **only the divergence**, and approve. Each
|
|
215
|
+
fix-and-re-review is one round of the loop below.
|
|
216
|
+
|
|
217
|
+
### Convergence — the fix loop ends
|
|
218
|
+
|
|
219
|
+
A review loop that never converges is worse than none: Anthropic saw a single PR take 50 rounds.
|
|
220
|
+
Four rules, enforced by `npx @ericrisco/rsc review ledger` (`.rsc/review-ledger.jsonl`) rather
|
|
221
|
+
than by memory:
|
|
222
|
+
|
|
223
|
+
1. **Max 3 rounds per task.** Call `review ledger round <slug>/<task>` before **every** review of
|
|
224
|
+
the task, the first included: round 1 is the task's first review, not its first fix. It exits
|
|
225
|
+
non-zero on the 4th. That is not "try harder": rethink the approach, or park the task in the
|
|
226
|
+
decision queue (a `blocking` entry) and continue with independent work.
|
|
227
|
+
2. **From round 2, re-review only the new diff** — what changed since the last round, not the task
|
|
228
|
+
again from scratch.
|
|
229
|
+
3. **Flag the bug class, not the line.** "Every handler in this file trusts `req.params.id`" — so
|
|
230
|
+
one fix covers every instance and round 3 does not find the sibling.
|
|
231
|
+
4. **Never repeat a finding.** Record each round's findings with `review ledger record
|
|
232
|
+
<findings.json>` (the consolidate output works). A finding already seen — same file, class and
|
|
233
|
+
normalized title — comes back marked `repeat`; it is never re-raised as new. A repeat means the
|
|
234
|
+
last fix missed the class, so say that instead.
|
|
235
|
+
|
|
236
|
+
**Every finding closes with an outcome**: `review ledger outcome <id> fixed|false|intentional|irrelevant|parked`
|
|
237
|
+
— `parked` when the finding's task went to the decision queue (neither fixed nor dismissed; a human
|
|
238
|
+
decides). `review ledger stats` then shows totals per outcome, the false rate and mean rounds per
|
|
239
|
+
task — the only way to tell whether a change to the reviewer made it better or worse.
|
|
240
|
+
|
|
241
|
+
The ledger is **local**: it lives in `.rsc/` and is never committed, so it does not travel between
|
|
242
|
+
machines or clones. What lasts is what you write from it — review-evals cases and rules (below).
|
|
203
243
|
|
|
204
244
|
---
|
|
205
245
|
|
|
@@ -276,26 +316,25 @@ In the `orient` voice: the verdict first, then the blocker list — e.g. `CHANGE
|
|
|
276
316
|
|
|
277
317
|
## Model tier — `heavy` (opt-in routing)
|
|
278
318
|
|
|
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 `../
|
|
280
|
-
|
|
281
|
-
## Where this writes
|
|
319
|
+
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
320
|
|
|
283
|
-
|
|
321
|
+
## Where this writes — and how the reviewer learns
|
|
284
322
|
|
|
285
323
|
- **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
|
-
-
|
|
324
|
+
- **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.
|
|
325
|
+
- **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
326
|
|
|
288
|
-
Index
|
|
327
|
+
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
328
|
|
|
290
329
|
## Result envelope
|
|
291
330
|
|
|
292
|
-
End with the parseable block every
|
|
293
|
-
interpreting prose (contract: `../
|
|
331
|
+
End with the parseable block every phase shares, so the dispatcher can chain without
|
|
332
|
+
interpreting prose (contract: `../asdd/SKILL.md`):
|
|
294
333
|
|
|
295
334
|
```json result-envelope
|
|
296
335
|
{
|
|
297
336
|
"status": "complete|blocked|failed",
|
|
298
|
-
"executive_summary": "
|
|
337
|
+
"executive_summary": "Per-task or closing review against spec/plan/constitution/REVIEW.md; tier, round, verdict and blocking findings.",
|
|
299
338
|
"artifact": "02-DOCS/wiki/sdd/reviews/<slug>.md",
|
|
300
339
|
"next_recommended": "ship",
|
|
301
340
|
"risk": "low|medium|high",
|
|
@@ -303,12 +342,12 @@ interpreting prose (contract: `../sdd/SKILL.md`):
|
|
|
303
342
|
"used": ["review"],
|
|
304
343
|
"missing": [],
|
|
305
344
|
"fallback": [],
|
|
306
|
-
"compact_rules": ["Rank or it is noise: blocker / should-fix / nit.", "A finding without a repro is a question, not a defect."]
|
|
345
|
+
"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
346
|
},
|
|
308
|
-
"evidence": ["verdict stated (APPROVE | APPROVE WITH NITS | CHANGES REQUESTED)", "each blocker carries location + repro + fix"]
|
|
347
|
+
"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
348
|
}
|
|
310
349
|
```
|
|
311
350
|
|
|
312
351
|
## Next in the chain
|
|
313
352
|
|
|
314
|
-
|
|
353
|
+
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.
|
package/skills/sdd-init/SKILL.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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 `
|
|
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
|
-
recommends: [
|
|
5
|
+
recommends: [asdd, constitution, specify, verify]
|
|
6
6
|
profiles: [core, full]
|
|
7
7
|
origin: risco
|
|
8
8
|
---
|
|
@@ -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. `
|
|
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
|
|
|
@@ -28,18 +28,20 @@ Read-only first:
|
|
|
28
28
|
|
|
29
29
|
If `02-DOCS/` does not exist, create only the `02-DOCS/wiki/sdd/` path needed for the config. Do not run full harness scaffolding unless the user asked for `harness`.
|
|
30
30
|
|
|
31
|
+
**Empty repo — nothing to detect?** Then there is nothing to calibrate yet: `constitution` runs first and decides the stack, and this skill calibrates against that decision (or re-runs once the first manifest exists). Never invent runners for an empty tree.
|
|
32
|
+
|
|
31
33
|
## Preflight Choices
|
|
32
34
|
|
|
33
35
|
Ask only when the answer changes behavior; explain each trade-off in one line, in the register `technical_level` sets.
|
|
34
36
|
|
|
35
37
|
| Setting | Default | Options |
|
|
36
38
|
| --- | --- | --- |
|
|
37
|
-
| `execution_mode` | `interactive` | `interactive` pauses at review-risk gates; `automatic` chains phases until a blocker/risk appears. |
|
|
39
|
+
| `execution_mode` | `interactive` | `interactive` pauses at review-risk gates; `automatic` chains phases until a blocker/risk appears. `sdd.execution_mode: automatic` is the switch `asdd`'s autopilot reads. |
|
|
38
40
|
| `artifact_store` | `02-DOCS/wiki/sdd` | Keep RSC artifacts in `02-DOCS`; do not create an `openspec/` parallel tree. |
|
|
39
41
|
| `review_budget.line_budget` | `400` | Lower for solo tight review; higher only with explicit approval. |
|
|
40
42
|
| `delivery_strategy.default` | `ask-on-risk` | `ask-on-risk`, `single-pr`, `autochain`, `exception`. |
|
|
41
43
|
| `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 `../
|
|
44
|
+
| `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
45
|
|
|
44
46
|
## Detection
|
|
45
47
|
|
|
@@ -145,7 +147,7 @@ models:
|
|
|
145
147
|
overrides: {} # per-phase tier overrides set by the user
|
|
146
148
|
```
|
|
147
149
|
|
|
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 `../
|
|
150
|
+
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
151
|
|
|
150
152
|
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
153
|
|
|
@@ -158,7 +160,7 @@ End with the standard SDD result envelope:
|
|
|
158
160
|
"status": "complete",
|
|
159
161
|
"executive_summary": "SDD config calibrated and registry refreshed.",
|
|
160
162
|
"artifact": "02-DOCS/wiki/sdd/config.yaml",
|
|
161
|
-
"next_recommended": "
|
|
163
|
+
"next_recommended": "constitution|specify",
|
|
162
164
|
"risk": "low",
|
|
163
165
|
"skill_resolution": {
|
|
164
166
|
"used": ["sdd-init"],
|
|
@@ -181,9 +183,9 @@ End with the standard SDD result envelope:
|
|
|
181
183
|
| "I'll skip config and remember the commands in chat." | Chat is not source of truth. Write `config.yaml`. |
|
|
182
184
|
| "No test command detected, but I'll still say strict TDD is active." | Strict TDD needs a runner. Record the gap. |
|
|
183
185
|
| "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, `
|
|
186
|
+
| "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
187
|
| "Create openspec/ because Gentle does." | RSC uses `02-DOCS/wiki/sdd/` as source of truth. |
|
|
186
188
|
|
|
187
189
|
## Next
|
|
188
190
|
|
|
189
|
-
After `sdd-init`, return to `
|
|
191
|
+
After `sdd-init`, return to `asdd` with `next_recommended` set to the next phase: `constitution` if there is none (or it is `status: draft`), else `specify` when no spec exists. If the work is ambiguous or architectural, write a proposal first under `02-DOCS/wiki/sdd/proposals/`.
|