@juzser/blacksmith 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.claude/agents/auditor.md +3 -0
  2. package/.claude/agents/grader.md +12 -5
  3. package/.claude/agents/reviewer.md +3 -0
  4. package/.claude/agents/security-reviewer.md +3 -0
  5. package/.claude/agents/spec-reviewer.md +3 -0
  6. package/.claude/agents/verifier.md +3 -0
  7. package/.claude/skills/bs/audit.md +25 -9
  8. package/.claude/skills/bs/dispatch.md +60 -7
  9. package/.claude/skills/bs/plan.md +33 -1
  10. package/.claude/skills/bs/run.md +18 -13
  11. package/.claude/skills/bs/wave.md +68 -10
  12. package/README.md +4 -4
  13. package/factory/orchestrator/dist/agentsSync.js +178 -0
  14. package/factory/orchestrator/dist/audit.js +134 -17
  15. package/factory/orchestrator/dist/autonomy.js +25 -0
  16. package/factory/orchestrator/dist/budgets.js +91 -2
  17. package/factory/orchestrator/dist/claims.js +8 -4
  18. package/factory/orchestrator/dist/cli.js +394 -26
  19. package/factory/orchestrator/dist/crossFinding.js +20 -0
  20. package/factory/orchestrator/dist/daemon.js +230 -41
  21. package/factory/orchestrator/dist/db/projector.js +93 -36
  22. package/factory/orchestrator/dist/db/queries.js +58 -35
  23. package/factory/orchestrator/dist/db/schema.js +24 -0
  24. package/factory/orchestrator/dist/dispatchAudit.js +10 -2
  25. package/factory/orchestrator/dist/dispatchLint.js +132 -0
  26. package/factory/orchestrator/dist/epic.js +192 -39
  27. package/factory/orchestrator/dist/epicWidth.js +25 -20
  28. package/factory/orchestrator/dist/errorIssues.js +209 -24
  29. package/factory/orchestrator/dist/eventOrder.js +102 -0
  30. package/factory/orchestrator/dist/events.js +73 -91
  31. package/factory/orchestrator/dist/findingContext.js +1 -13
  32. package/factory/orchestrator/dist/findings.js +132 -11
  33. package/factory/orchestrator/dist/gh.js +29 -6
  34. package/factory/orchestrator/dist/git.js +20 -2
  35. package/factory/orchestrator/dist/harness.js +92 -10
  36. package/factory/orchestrator/dist/hookDecision.js +320 -37
  37. package/factory/orchestrator/dist/impact.js +24 -2
  38. package/factory/orchestrator/dist/integration.js +11 -1
  39. package/factory/orchestrator/dist/issueReporter.js +415 -0
  40. package/factory/orchestrator/dist/judgeStopHook.js +181 -0
  41. package/factory/orchestrator/dist/judges.js +1 -1
  42. package/factory/orchestrator/dist/lessons.js +46 -18
  43. package/factory/orchestrator/dist/logCache.js +291 -0
  44. package/factory/orchestrator/dist/paths.js +10 -0
  45. package/factory/orchestrator/dist/plan.js +138 -4
  46. package/factory/orchestrator/dist/planQuorum.js +1 -1
  47. package/factory/orchestrator/dist/policy.js +57 -11
  48. package/factory/orchestrator/dist/projects.js +12 -1
  49. package/factory/orchestrator/dist/queue.js +477 -29
  50. package/factory/orchestrator/dist/scheduler.js +51 -0
  51. package/factory/orchestrator/dist/severity.js +40 -8
  52. package/factory/orchestrator/dist/spec.js +70 -0
  53. package/factory/orchestrator/dist/taskEvents.js +13 -4
  54. package/factory/orchestrator/dist/taskStatus.js +74 -0
  55. package/factory/orchestrator/dist/taxonomy.js +11 -0
  56. package/factory/orchestrator/dist/testgate.js +32 -3
  57. package/factory/orchestrator/dist/usage.js +42 -9
  58. package/factory/orchestrator/dist/waivers.js +60 -9
  59. package/factory/orchestrator/dist/waveConcurrency.js +29 -0
  60. package/factory/orchestrator/dist/waveNext.js +12 -10
  61. package/factory/orchestrator/dist/waveSchedule.js +84 -0
  62. package/factory/orchestrator/dist/worktree.js +19 -6
  63. package/factory/orchestrator/drizzle/0012_issue_reports_error_issues.sql +20 -0
  64. package/factory/orchestrator/drizzle/meta/_journal.json +7 -0
  65. package/factory/policies/budgets.yml +9 -2
  66. package/factory/policies/crosscheck.yml +2 -2
  67. package/factory/policies/lessons.md +298 -0
  68. package/factory/policies/scheduler.yml +27 -2
  69. package/factory/policies/taxonomy.yml +10 -3
  70. package/factory/specs/roadmap.md +70 -23
  71. package/package.json +8 -8
@@ -5,6 +5,9 @@ model: sonnet
5
5
  effort: high
6
6
  tools: Read, Grep, Glob, Bash
7
7
  maxTurns: 15
8
+ hooks:
9
+ Stop:
10
+ - hooks: [{type: command, command: "[ -f \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" ] && \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" || exit 0"}]
8
11
  ---
9
12
 
10
13
  # Auditor
@@ -5,6 +5,9 @@ model: sonnet
5
5
  effort: low
6
6
  tools: Read, Grep, Glob, Bash
7
7
  maxTurns: 15
8
+ hooks:
9
+ Stop:
10
+ - hooks: [{type: command, command: "[ -f \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" ] && \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" || exit 0"}]
8
11
  ---
9
12
 
10
13
  # Grader
@@ -43,7 +46,9 @@ edit does not save a round-trip, it costs the whole one.
43
46
  rubric — nothing broader.
44
47
  - Bounce back to the worker with specific, itemized gaps (not vibes).
45
48
  - **Max 2 rounds.** After that, pass whatever exists through to the gates —
46
- the gates, not the grader, decide final pass/fail.
49
+ the gates, not the grader, decide final pass/fail. The re-scope path after a
50
+ round-2 `fail` is `.claude/skills/bs/dispatch.md`'s "Round counting and
51
+ escalation" — read it there rather than here.
47
52
  - Grader never edits code and never talks to the operator.
48
53
  - **Never compact your context** (`budgets.yml` `context_window`,
49
54
  `narrowing_roles`). You grade evidence rather than build on it; a
@@ -101,10 +106,12 @@ harness counts the tokens; the dispatcher stamps them.
101
106
  {"status": "done", "overall": "fail", "round": 2, "artifact_path": "state/results/<task-id>.grader-r2.json"}
102
107
  ```
103
108
 
104
- Two rounds is a hard stop, not a suggestion. A `fail` at round 2 goes back to
105
- the planner for re-scoping; you do not open round 3, and you do not soften the
106
- verdict to avoid the stop. The schema caps `round` at 2, so a round-3 file
107
- does not quietly pass the gate — it blocks it as invalid.
109
+ Two rounds is a hard stop, not a suggestion. A `fail` at round 2 is re-scoped
110
+ through dispatch.md's "Round counting and escalation" (plan propose/approve a
111
+ successor task id, then `worktree create --from`); you do not open round 3 on
112
+ this id, and you do not soften the verdict to avoid the stop. The schema caps
113
+ `round` at 2, so a round-3 file does not quietly pass the gate — it blocks it
114
+ as invalid.
108
115
 
109
116
  ## What the gate does with this file
110
117
 
@@ -5,6 +5,9 @@ model: sonnet
5
5
  effort: medium
6
6
  tools: Read, Grep, Glob, Bash
7
7
  maxTurns: 15
8
+ hooks:
9
+ Stop:
10
+ - hooks: [{type: command, command: "[ -f \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" ] && \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" || exit 0"}]
8
11
  ---
9
12
 
10
13
  # Reviewer
@@ -5,6 +5,9 @@ model: sonnet
5
5
  effort: high
6
6
  tools: Read, Grep, Glob, Bash
7
7
  maxTurns: 15
8
+ hooks:
9
+ Stop:
10
+ - hooks: [{type: command, command: "[ -f \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" ] && \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" || exit 0"}]
8
11
  ---
9
12
 
10
13
  # Security Reviewer
@@ -5,6 +5,9 @@ model: sonnet
5
5
  effort: high
6
6
  tools: Read, Grep, Glob, Bash
7
7
  maxTurns: 15
8
+ hooks:
9
+ Stop:
10
+ - hooks: [{type: command, command: "[ -f \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" ] && \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" || exit 0"}]
8
11
  ---
9
12
 
10
13
  # Spec Reviewer
@@ -5,6 +5,9 @@ model: opus
5
5
  effort: xhigh
6
6
  tools: Read, Grep, Glob, Bash
7
7
  maxTurns: 15
8
+ hooks:
9
+ Stop:
10
+ - hooks: [{type: command, command: "[ -f \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" ] && \"$CLAUDE_PROJECT_DIR/.claude/hooks/judge-stop.sh\" || exit 0"}]
8
11
  ---
9
12
 
10
13
  # Verifier
@@ -218,10 +218,19 @@ smith audit cut <project-dir> --epic <epic-id> --title "<milestone title>" \
218
218
  ```
219
219
 
220
220
  It stamps the epic id onto every accepted finding, emits `audit-cut`,
221
- and prints two strings; **it edits no file**. Paste `milestone` into
222
- `factory/specs/roadmap.md` as the next milestone block — it already
223
- carries `- project: <basename>` and `- kind: product` — and write `spec`
224
- to `factory/specs/active/<epic-id>/epic-spec.md`. `audit.nothing-accepted`
221
+ and prints two strings; **it edits no file**. Both belong to the audited
222
+ project, so both are written under the **work root** of step 1 and never
223
+ into the factory's own checkout: paste `milestone` into
224
+ `<work-root>/factory/specs/roadmap.md` as the next milestone block — it
225
+ already carries `- project: <basename>` and `- kind: product` — and write
226
+ `spec` to `<work-root>/factory/specs/active/<epic-id>/epic-spec.md`. If
227
+ that roadmap does not exist yet, create it: the parser
228
+ (`factory/orchestrator/src/roadmap.ts`) asks only that milestones be `##`
229
+ blocks, so a title line above the first one is a whole file. A milestone
230
+ for someone else's project is a declaration *they* made, and a factory
231
+ that keeps those in its own repository ships them to everyone who clones
232
+ it — which is also what step 7's `.gitignore` question is about, since
233
+ the work root is where these two files now live. `audit.nothing-accepted`
225
234
  means step 7 accepted nothing: there is no epic to cut, and closing is
226
235
  the right next move. One audit cuts one epic; findings accepted after a
227
236
  cut belong to the next audit's cut, not to a second cut from this one.
@@ -245,8 +254,15 @@ is already gone closes with `verified: false` and no refusal.
245
254
  The epic is now an ordinary roadmap milestone: run `/bs plan` on it
246
255
  ([`plan.md`](plan.md)), then `/bs run`. When that run reaches its epic-close
247
256
  step, it runs
248
- `smith audit resolve <project-dir> --epic <epic-id> --session <session-id> --causal-parent <event-id>`,
249
- which appends `fixed` for every finding the epic carried — repeatable, and
250
- `audit.unknown-epic` if the id was never cut from this store. That is the
251
- only step of an audit that runs outside this playbook, and it is why the
252
- store, not this transcript, is where a finding's life is kept.
257
+ `smith audit resolve <project-dir> --epic <epic-id> --session <session-id> --causal-parent <event-id> [--specs-dir <dir>]`,
258
+ which reads the epic's newest plan and appends `fixed` only for a carried
259
+ finding some task in it still claims — the rest come back `deferred`, still
260
+ `accepted`, because the plan scoped them out on purpose. Repeatable;
261
+ `audit.unknown-epic` if the id was never cut from this store, `audit.no-plan`
262
+ if no plan can be found (refuses rather than guess). The default search is
263
+ the work root's specs dir; when this project's plans live elsewhere, pass
264
+ `--specs-dir` naming the same specs dir the epic was run from. `--except
265
+ <fp[,fp...]>` forces specific fingerprints into `deferred` even when a task
266
+ claims them. That is the only step of an audit that runs outside this
267
+ playbook, and it is why the store, not this transcript, is where a finding's
268
+ life is kept.
@@ -11,7 +11,7 @@ task spec or the one question · the **absolute** worktree path · the path
11
11
  claims it may touch · its token cap · **its turn budget, copied verbatim
12
12
  from the template's `maxTurns`**. That last one is why it is listed: Claude
13
13
  Code enforces the template's `maxTurns` on every `Agent`-tool dispatch
14
- (dogfood-csb-audit-1 FD-7, FD-14),
14
+ (measured on two dogfood runs, 2026-09-07 and 2026-09-11),
15
15
  and the agent cannot see how many turns it has left. A prompt that promises
16
16
  more than the template is a fiction the agent plans against and gets cut in
17
17
  the middle of (a planner told 40 under a template saying 20 stopped twice
@@ -19,6 +19,22 @@ with nothing written). So state the template's number, never a higher one,
19
19
  and tell every role that owes a file to write it before it refines it. A
20
20
  role that keeps running out is a template to raise, not a prompt to inflate.
21
21
 
22
+ Read that number from `.claude/agents/<role>.md` as the file stands now, at
23
+ dispatch time — not from memory, an earlier dispatch, or a doc that quotes
24
+ it. `smith agents sync` may have rewritten it locally from
25
+ `SMITH_MAXTURNS_<ROLE>` (an uncommitted, per-box edit), and Claude Code
26
+ enforces what the file says. A sync reaches only agents spawned after it.
27
+
28
+ State it on its own line, verbatim — `Turn budget: 40` — and, on a judge
29
+ dispatch, the declared-artifact line below verbatim too. `smith dispatch
30
+ lint <prompt-file> --role <role> --task <id> --session <id>` reads a
31
+ composed prompt back and checks both against the template and the ledger,
32
+ before the agent ever runs. It fails closed: a number that does not match
33
+ the template — higher (the promise-more-than-the-template mistake above) or
34
+ lower — a missing turn-budget line, a template it cannot read at all, and a
35
+ judge prompt missing or mismatching its artifact line are each a lint
36
+ failure, not a maybe.
37
+
22
38
  ## Splice the compiled lessons into every prompt (agent-interviews.md N-9, P9-2)
23
39
 
24
40
  Before you dispatch, run
@@ -145,10 +161,30 @@ memory of its previous attempt and will happily report round 1 forever.
145
161
  reason). An unlogged escalation is a cost you cannot attribute later — and
146
162
  `model_tier` alone cannot even tell you *which* frontier model you escalated
147
163
  to, since opus and fable share the tier.
148
- - **The grader's 2-round cap is a hard stop, not a ladder rung.** At a round-2
149
- `fail` the task goes back to the planner for re-scoping. Do not re-dispatch
150
- the grader, and do not escalate the *grader* — the gates decide pass/fail,
151
- and a third grading round only buys a more expensive opinion.
164
+ - **The grader's 2-round cap is a hard stop, not a ladder rung.** At a
165
+ round-2 `fail` the operator re-scopes through a concrete, logged path, not
166
+ a bare re-dispatch:
167
+ 1. `smith plan propose` with a `supersede` pairing the failing task id to a
168
+ new one (`PlanChanges.supersede`, spec.ts's `amendPlan`) — the operator
169
+ approves the re-scoping, this command only records the proposal.
170
+ 2. `smith plan approve` the proposed version. This is what writes the
171
+ `successors{old->new}` pairing (spec.ts's `taskSuccessors`) that
172
+ everything below checks against.
173
+ 3. `smith worktree create --from <old-task-id> --session <id> ...` to cut
174
+ the successor's branch from the predecessor's HEAD instead of
175
+ integration — refused as `worktree.not-a-successor` unless step 2 logged
176
+ that exact pairing.
177
+ 4. Rerun wave steps 5-7 (dispatch, review, gate) under the new task id. The
178
+ grader starts that id at round 1 — the ledger's round counter is per
179
+ `(task_id, role)`, and a new id has never opened a round.
180
+ 5. Gate with `--plan` naming the new plan version.
181
+
182
+ A same-id re-dispatch of round 3 is never legal, superseded or not: the cap
183
+ binds the id, and a kept-id supersede (same task_id, new claims/criteria)
184
+ does not reset the round either — only a **new** task id starts a fresh
185
+ ledger. Do not re-dispatch the grader on the old id, and do not escalate the
186
+ *grader* — the gates decide pass/fail, and a third grading round on the same
187
+ id only buys a more expensive opinion.
152
188
  - **Never escalate a judge to break a tie with another judge.** That is what
153
189
  the cross-check quorum is for (`crosscheck.yml`); a bigger critic is still
154
190
  one critic.
@@ -210,8 +246,8 @@ stamp) and `<epic>/plan-v<n>` (a plan version, the ref `plan quorum`
210
246
  stamps). Anything else — `<epic>/plan-draft`, `<epic>/spec-review-r27` —
211
247
  folds a real row that nothing can ever move on, because no planner or
212
248
  spec-reviewer dispatch produces the `wave-merged` or `gate-outcome` that
213
- would: twenty-one of csb-audit-1's twenty-four non-terminal rows were these
214
- (`docs/specs/dogfood-csb-audit-1-findings.md` FD-27).
249
+ would: on one dogfooded product epic, twenty-one of its twenty-four
250
+ non-terminal rows were these.
215
251
 
216
252
  So: the closing spec-reviewer, the goal-check spec-reviewer, the planner
217
253
  rendering a verdict and the scribe are dispatched against
@@ -318,6 +354,12 @@ true one on the record. `--no-findings` records an operator
318
354
  *attestation* rather than a review — use it only for a judge that ran outside
319
355
  the factory; a judge that genuinely found nothing writes `[]` and reports.
320
356
 
357
+ Tell the judge the exact path on its own line, verbatim —
358
+ `Declared artifact: /abs/path/<task-id>.reviewer.json` — the spelling `smith
359
+ dispatch lint` checks the composed prompt against once the dispatch above
360
+ has landed in the ledger. Missing, relative, or a path that does not match
361
+ what `--artifact` declared are each a lint failure, not a maybe.
362
+
321
363
  ## Dispatching the security-reviewer (agent-interviews.md N-7)
322
364
 
323
365
  Do not eyeball the path list — ask:
@@ -347,6 +389,17 @@ exceptions — today one, `wave-runner` — and each of them earns it by opening
347
389
  session of its own against your dispatch's event id before it dispatches
348
390
  anything.
349
391
 
392
+ Run `smith issues report --session <session-id> --state-dir <dir> [--epic
393
+ <id>]` right after any of the three things this log can hold: the
394
+ `error-logged` you just wrote, a `gate-outcome` that `smith gate run` records
395
+ with outcome `blocked`, or a `task-added` whose payload sets `task_status:
396
+ failed`. The reporting verb re-reads the whole session lineage log and folds
397
+ all three source event types, so calling it once after any of them covers
398
+ the others too; a repeated call over the same error finds the recorded
399
+ `issue-reported` outcome and moves on, so calling it again is never a
400
+ mistake. A non-zero exit is logged and does not fail the dispatch that owns
401
+ this log; the run keeps going.
402
+
350
403
  ## The task id goes on the event, not in the payload
351
404
 
352
405
  `smith event append` reads `task_id` at the top level of the JSON, beside
@@ -44,7 +44,26 @@ there means "not looked at", not "looked at and clean".
44
44
  — a *different model* than the planner's, per its own frontmatter — to
45
45
  hunt spec gaps/ambiguities/missing-nonfunctional clauses
46
46
  (`docs/standards/agent-constraints.md` "planner"; `severity.yml`: most
47
- spec gaps land S2, blocking sign-off).
47
+ spec gaps land S2, blocking sign-off). Name the review focus in the
48
+ dispatch itself, as a short list of at most six items, so findings come
49
+ back tagged to one of them:
50
+
51
+ 1. Claims and dependency edges, cross-checked against
52
+ `smith claims impact`.
53
+ 2. TDD-ability -- a named failing test per task, before its code.
54
+ 3. Nonfunctional and security clauses.
55
+ 4. Budget and diff-cap fit.
56
+ 5. Acceptance-criterion coverage -- every epic criterion maps to a task.
57
+ 6. From round 2 on, only the previous round's findings plus any task
58
+ that changed since then.
59
+
60
+ When `size.small` reads true on the draft (`smith wave schedule` or
61
+ `smith plan ingest`'s `parallelism.size`, run against the draft plan
62
+ file), recommend effort tier `small` -- single-pass spec review -- to
63
+ the operator unless a security trigger floors it first, and say why in
64
+ the same message: a plan that cannot spread past a few tasks and a
65
+ handful of claim paths does not need a second review round to find what
66
+ one pass already covers.
48
67
  4. Planner fixes flagged issues; repeat 2–3 until the spec-reviewer has
49
68
  nothing S1/S2 left — or exactly once when `profile.specReviewRounds` is
50
69
  `single-pass` (`small`), where the planner fixes what came back and
@@ -127,6 +146,11 @@ there means "not looked at", not "looked at and clean".
127
146
  decide whether to re-slice, and say which you chose when you present the
128
147
  plan.
129
148
 
149
+ The same JSON also carries `size` -- `{tasks, claims, small}` over live
150
+ tasks only -- the block `plan ingest` prints again under `parallelism`
151
+ at step 7; `small` is true at or under 3 live tasks and 10 distinct
152
+ claim paths, and this is where the step 3 tier read gets confirmed.
153
+
130
154
  Beside the schedule, the read that names what it only counts:
131
155
 
132
156
  ```bash
@@ -163,6 +187,14 @@ there means "not looked at", not "looked at and clean".
163
187
  UI check found behind two epics. It is idempotent, so run it again on a
164
188
  resumed session; read `added` and `edges` back, and say so when either is
165
189
  0 on a plan that has tasks or edges.
190
+
191
+ Read `parallelism.widest` back from that same output, too. `parallel_with`
192
+ underneath it is claims-and-edges only -- import-graph crossings stay
193
+ `wave schedule --repo`'s job, not this key's -- so it can only ever name
194
+ a width that job would not contradict. A `widest` of 1 on a
195
+ plan of two or more tasks means every task the wave loop will ever run
196
+ sits behind some claim or edge that serializes it: flag that to the
197
+ operator now, before a run discovers it as a stalled wave.
166
198
  8. If this epic opens a new roadmap milestone, add it to
167
199
  `factory/specs/roadmap.md` (planner-maintained, architecture §12) — a
168
200
  roadmap change is itself a scope change and needs the same operator nod.
@@ -123,8 +123,9 @@ under it, which is the wrong `--state-dir`, or a wave session opened without
123
123
  done; a green gate on serialized work is exactly the outcome the two
124
124
  playbooks are written to prevent.
125
125
 
126
- 11. Repeat until every task in the live plan version is
127
- `completed`/`superseded`/`waived`. Dispatch the **`planner`** again for
126
+ 11. Repeat until every task in the live plan version is `completed`/`waived`,
127
+ or `superseded` by a successor that is itself terminal-OK (recursively —
128
+ a successor chain counts too). Dispatch the **`planner`** again for
128
129
  the epic verdict against acceptance criteria — gaps found → a NEW
129
130
  `plan-v(n+1)` with inferred tasks (never a live-graph mutation,
130
131
  architecture §12), auto-scheduled at confidence ≥0.8 else parked for an
@@ -165,10 +166,9 @@ playbooks are written to prevent.
165
166
  13-16 write. The envelope leaves the flag optional, and an omitted one
166
167
  stamps the record `plan_version: 1` — so on an epic that amended, the
167
168
  check, the review and the close all claim to have verified a plan that
168
- no longer exists. csb-signing-policy-1 closed at v3 with its integration
169
- check and its closing spec review both stamped v1
170
- (`csb-signing-policy-1-2026-09-11#197`, `#202`); the goal check, passed
171
- the flag, says v3. Read the version off `smith plan ingest`'s receipt and
169
+ no longer exists. One dogfooded product epic closed at v3 with its
170
+ integration check and its closing spec review both stamped v1; the goal
171
+ check, passed the flag, said v3. Read the version off `smith plan ingest`'s receipt and
172
172
  pass it every time.
173
173
  13. Dispatch the **`spec-reviewer`** again — this time against the code.
174
174
  The pre-code review at `/bs plan` step 3 read the spec against nothing;
@@ -276,8 +276,8 @@ playbooks are written to prevent.
276
276
  provider is `active` in `crosscheck.yml`: codex spends the operator's
277
277
  ChatGPT quota, DeepSeek spends prepaid credit, and step 16 runs the
278
278
  verdict again before it writes, so a clean close is two spends, not one
279
- (csb-signing-policy-1 wrote two `quorum-decision`s against its
280
- integration ref, `#210` from the verdict and `#215` from the close).
279
+ (one dogfooded epic wrote two `quorum-decision`s against its
280
+ integration ref, one from the verdict and one from the close).
281
281
  Nothing in the `quorum-decision` payload names the purse, so say which
282
282
  one it is to the operator before the first call, not after the second.
283
283
  16. Record the close. The verdict above is a read-only probe and writes
@@ -293,17 +293,22 @@ playbooks are written to prevent.
293
293
  yourself** — closing over a hold is the operator's call; ask for it and
294
294
  quote the blockers. If the epic was cut by `/bs audit`
295
295
  ([`audit.md`](audit.md) step 10), follow the close with
296
- `smith audit resolve <project-dir> --epic <epic> --session <session-id> --causal-parent <event-id>`
297
- so every finding the epic carried is marked `fixed` in the project's
298
- audit store — the store, not the epic, is where a finding's life ends.
296
+ `smith audit resolve <project-dir> --epic <epic> --session <session-id> --causal-parent <event-id> [--specs-dir <dir>]`
297
+ so every finding this plan still claims is marked `fixed` in the
298
+ project's audit store — the rest come back `deferred`, left exactly as
299
+ they were, because the plan scoped them out on purpose. The store, not
300
+ the epic, is where a finding's life ends. `resolve` reads the epic's
301
+ newest plan from the work root's specs dir by default; when this
302
+ project's plans live elsewhere, pass `--specs-dir` naming the same specs
303
+ dir the epic was run from, or it refuses rather than guess.
299
304
  17. Open **one integration PR per epic** with the scribe-written body
300
305
  (`/bs report`'s playbook, [`report.md`](report.md)) — screenshots, test
301
306
  results, reviewer verdict, waivers granted, timeline link. The head is
302
307
  `smith/<epic>/integration`. The base is the target repo's `main` unless
303
308
  the epic was cut from another epic's integration branch, in which case
304
309
  the PR stacks on that branch and the operator merges the parent's PR
305
- first — csb-signing-policy-1 was cut from `smith/csb-audit-1/integration`
306
- and opened PR #54 against it, behind PR #53; once the parent has merged
310
+ first — a child epic cut from `smith/<parent>/integration` opens its PR
311
+ against that branch, behind the parent's PR; once the parent has merged
307
312
  and its branch is deleted, GitHub retargets the child to `main`. The
308
313
  operator reviews on GitHub; this session never merges to `main`
309
314
  (`docs/standards/guardrails.md`).
@@ -33,6 +33,14 @@ both:
33
33
  its waves, and a window spent on this wave's dispatches is a window the
34
34
  epic does not have for the wave after next (D13).
35
35
 
36
+ After any `error-logged` this wave writes — a worker that died, a judge turn
37
+ that failed, a gate refusal recorded as an error — or a `smith gate run`
38
+ that came back `blocked` (its `gate-outcome` event with outcome `blocked`),
39
+ or a task the wave marks `failed` (its `task-added` event with payload
40
+ `task_status: failed`), the wave runs the reporting verb once, per
41
+ [`dispatch.md`](dispatch.md)'s paragraph of this same heading, which owns
42
+ the rule.
43
+
36
44
  Which `<event-id>` depends on how you were started, and the difference is
37
45
  not cosmetic — it is the edge an audit walks:
38
46
 
@@ -47,14 +55,27 @@ not cosmetic — it is the edge an audit walks:
47
55
  anything else and the check reports you as `unverifiable` — not as a
48
56
  pass — however well the wave ran. Open it **before** your first dispatch,
49
57
  for the same reason: a grant is earned by owning the log first.
58
+ - **You are taking over a wave whose runner died or capped**: continue from
59
+ that runner's last event, in a session of your own. Do not write into its
60
+ log. One session has one author, and `smith delegation check` reads exactly
61
+ that: dispatches appended to a dead delegate's log by whoever picked the
62
+ wave up are reported as violations, correctly, because from the log's side
63
+ they cannot be told apart from an agent narrating its own work. Open your
64
+ own instead:
65
+ `smith session start <wave-id>-takeover --continues <dead-session>#<n>`.
66
+ That keeps the chain whole — the epic's folded reads still see every
67
+ dispatch on both sides of the handover — and it makes the takeover a fact
68
+ in the log rather than something a reader has to infer from two agents and
69
+ one timeline.
50
70
 
51
- Either way, one rule holds: **the log you write is the log the epic reads**,
52
- and `--continues` is the whole of what makes that true. Every deciding read
53
- at the epic tier folds the lineage rather than one session — `wave audit`,
54
- `budget alarm`, `tester check`, `judge outstanding`, `escalation check`,
55
- `dispatch check`, `delegation check` — so a dispatch recorded in a wave
56
- session is visible from the epic session that admitted it, and is *not*
57
- visible from a sibling wave's. Open a wave session without `--continues` and
71
+ Whatever the case, one rule holds: **the log you write is the log the
72
+ epic reads**, and `--continues` is the whole of what makes that true.
73
+ Every deciding read at the epic tier folds the lineage rather than one
74
+ session — `wave audit`, `budget alarm`, `tester check`,
75
+ `judge outstanding`, `escalation check`, `dispatch check`,
76
+ `delegation check` — so a dispatch recorded in a wave session is visible
77
+ from the epic session that admitted it, and is *not* visible from a
78
+ sibling wave's. Open a wave session without `--continues` and
58
79
  none of those verbs can see your work: they will not error, they will answer
59
80
  about a wave that appears never to have run.
60
81
 
@@ -126,6 +147,28 @@ one thing this playbook never asks you to.
126
147
  carries `keeps_exports`, restate the promise in the dispatch in one line:
127
148
  the file list, and "keep every existing export's name and declaration;
128
149
  adding is fine".
150
+
151
+ Once every coder of the wave has been dispatched — this fan-out has gone
152
+ out for every admitted task — audit the wave's own parallelism while it
153
+ can still be corrected: `smith wave audit --session <id> --epic <epic>`.
154
+ `wave check` is the step-1 gate that certified, before any dispatch, that
155
+ these tasks could run together; `wave audit` reads the log back after
156
+ dispatch and says whether they did.
157
+ It is a read, so it costs nothing against "No lockstep barrier" above and
158
+ waits for no task. Read the `verdict` field (`WAVE_VERDICTS`,
159
+ `waveConcurrency.ts`), not only the exit code — `partial` also exits 0:
160
+ - `serialized` or `partial` — the wave is not running the width it was
161
+ admitted at, and steps 5–7 have not gone out yet, so it can still be
162
+ fixed: dispatch every remaining phase of this wave as one message of
163
+ parallel calls, per "One message, many dispatches" above, and record
164
+ the verdict as an operator-visible event — `error-logged` with error
165
+ `contract.constraint-decay` (`taxonomy.yml`): the wave-runner stopped
166
+ honoring that section's standing constraint.
167
+ - `unobserved` — the log holds no dispatch for any task this wave
168
+ admitted. That is a defect in the log, not in how the wave ran; record
169
+ it the same way, as `error-logged` with error `execution.env-failure`.
170
+ - `parallel` or `single` — nothing further; the wave ran as wide as it
171
+ was admitted.
129
172
  5. Dispatch **`tester`** (`.claude/agents/tester.md`) for missing unit
130
173
  coverage and epic-level e2e/screenshots.
131
174
  - Then the **uiux visual pass**, but only when all three hold: the task
@@ -143,7 +186,8 @@ one thing this playbook never asks you to.
143
186
  runs; it never decides pass/fail itself. At one round the grader scores
144
187
  once and a failed criterion is the coder's bounce, not a re-grade. Its result file is an **input to
145
188
  step 7**, not a note to the operator: pass it as `--grader` or the rubric
146
- gates nothing (D-34).
189
+ gates nothing (D-34). A round-2 `fail` re-scopes via dispatch.md's "Round
190
+ counting and escalation", not a round 3 on this id.
147
191
  7. Run the gate pipeline:
148
192
  `smith gate run <task-id> --worktree <dir> --checks checks.json --result
149
193
  result.json --grader state/results/<task-id>.grader-r<round>.json
@@ -217,8 +261,18 @@ one thing this playbook never asks you to.
217
261
  — see "Round counting and escalation" above. Then have the log check
218
262
  you: `smith escalation check <session-id> --task <task-id>`, which
219
263
  exits 1 if the rung you just climbed is not evidenced.
220
- 10. Gate outcome `pass`/`pass-with-waivers-pending` → before admitting,
221
- ask what the diff did to everyone outside the claims, and whether it
264
+ 10. Gate outcome `pass`/`pass-with-waivers-pending` → before admitting, audit
265
+ the wave's own parallelism: `smith wave audit --session <id> --epic
266
+ <epic>`. It reads the log back and says whether the tasks admitted
267
+ together actually ran together — `parallel` when every admitted task was
268
+ in flight at once, `partial` when two or more overlapped but never all,
269
+ `serialized` when work is recorded but no two tasks ever overlapped,
270
+ `single` when the wave admitted one task (nothing to be parallel about),
271
+ and `unobserved` when the wave was admitted and the log shows no work for
272
+ any of its tasks. Exit 1 on `serialized`, exit 2 on `unobserved` — either
273
+ is a fact for the wave-loop's hand-back, not a reason to hold the merge:
274
+ report the verdict and keep going to admission. Then ask what the diff
275
+ did to everyone outside the claims, and whether it
222
276
  kept what the spec promised:
223
277
  `smith claims impact <worktree-dir> <spec.json>`. Exit 1 means a
224
278
  `proven` break — this task removed an export a file outside its claims
@@ -250,6 +304,10 @@ one thing this playbook never asks you to.
250
304
  auto-resolve a semantic conflict silently (`worktree.yml`
251
305
  `conflict_resolution_ladder`).
252
306
 
307
+ An `integration-dirty` outcome names a `worktree` holding
308
+ `smith/<epic>/integration` with uncommitted tracked changes (`dirty`):
309
+ commit or stash those there, then re-run `smith queue run`.
310
+
253
311
  Once the wave is merged, check the epic's spend — `smith budget alarm
254
312
  <session-id> [--epic <epic>]`. Exit 1 means either the epic crossed
255
313
  `alarm_ratio` (re-plan the remaining work to fit, or ask the operator —
package/README.md CHANGED
@@ -331,7 +331,7 @@ runs, plus branch protection — not by trust. Full rules:
331
331
  **[`docs/standards/guardrails.md`](docs/standards/guardrails.md)**.
332
332
 
333
333
  - **Secrets are environment-only.** `.env.example` is the only committed env
334
- file (variable names, never values), and the event logger redacts
334
+ file (variable names, never a secret's value), and the event logger redacts
335
335
  credential-shaped strings before write.
336
336
  - **Only you merge to `main`.** No agent may push or merge to a protected
337
337
  branch, and force-push is refused on every branch, protected or not — an
@@ -391,9 +391,9 @@ runbook is written; the hosted UI stays deferred. Beside the phases, `/bs audit`
391
391
  is built: an existing project can be read on four axes and one epic cut from
392
392
  what you accept.
393
393
 
394
- The CLI is on npm as `@juzser/blacksmith`, at `0.2.0` — two binaries now,
395
- `smith` and `smith-run`, the second being what runs a rendered turn on a
396
- `cli` harness such as Codex. `0.1.1` was the first version that ran beside
394
+ The CLI is on npm as `@juzser/blacksmith`, at `0.4.0` — two binaries,
395
+ `smith` and `smith-run`, the second (since `0.2.0`) being what runs a
396
+ rendered turn on a `cli` harness such as Codex. `0.1.1` was the first version that ran beside
397
397
  you and knew it was a package, rather than out of the clone-shaped install
398
398
  `0.1.0` was. The package is the binaries and what they
399
399
  read. The dashboard, the docs, the test suite and `/bs` itself come from a