@juzser/blacksmith 0.4.0 → 0.5.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 (131) hide show
  1. package/.claude/agents/{auditor.md → bs-auditor.md} +5 -5
  2. package/.claude/agents/{coder.md → bs-coder.md} +8 -7
  3. package/.claude/agents/{grader.md → bs-grader.md} +4 -4
  4. package/.claude/agents/{merger.md → bs-merger.md} +4 -4
  5. package/.claude/agents/{planner.md → bs-planner.md} +20 -6
  6. package/.claude/agents/{researcher.md → bs-researcher.md} +6 -5
  7. package/.claude/agents/{reviewer.md → bs-reviewer.md} +13 -5
  8. package/.claude/agents/{scribe.md → bs-scribe.md} +11 -2
  9. package/.claude/agents/{security-reviewer.md → bs-security-reviewer.md} +6 -6
  10. package/.claude/agents/{spec-reviewer.md → bs-spec-reviewer.md} +15 -10
  11. package/.claude/agents/{tester.md → bs-tester.md} +40 -12
  12. package/.claude/agents/bs-uiux.md +257 -0
  13. package/.claude/agents/{verifier.md → bs-verifier.md} +4 -4
  14. package/.claude/agents/{wave-runner.md → bs-wave-runner.md} +14 -5
  15. package/.claude/skills/bs/SKILL.md +99 -21
  16. package/.claude/skills/bs/audit.md +34 -27
  17. package/.claude/skills/bs/dispatch.md +100 -50
  18. package/.claude/skills/bs/lessons.md +28 -14
  19. package/.claude/skills/bs/mcp.md +4 -4
  20. package/.claude/skills/bs/new.md +3 -3
  21. package/.claude/skills/bs/plan.md +27 -18
  22. package/.claude/skills/bs/report.md +11 -11
  23. package/.claude/skills/bs/run.md +181 -49
  24. package/.claude/skills/bs/status.md +14 -7
  25. package/.claude/skills/bs/ui.md +21 -5
  26. package/.claude/skills/bs/waivers.md +78 -2
  27. package/.claude/skills/bs/wave.md +109 -53
  28. package/README.md +53 -32
  29. package/factory/orchestrator/dist/agentNames.js +43 -0
  30. package/factory/orchestrator/dist/agents-registry.js +11 -4
  31. package/factory/orchestrator/dist/agentsSync.js +48 -14
  32. package/factory/orchestrator/dist/artifacts.js +31 -0
  33. package/factory/orchestrator/dist/attribution.js +4 -0
  34. package/factory/orchestrator/dist/audit.js +125 -17
  35. package/factory/orchestrator/dist/budgetAlarm.js +132 -37
  36. package/factory/orchestrator/dist/budgets.js +302 -84
  37. package/factory/orchestrator/dist/claims.js +9 -19
  38. package/factory/orchestrator/dist/cli.js +790 -100
  39. package/factory/orchestrator/dist/cliName.js +27 -0
  40. package/factory/orchestrator/dist/crosscheck.js +136 -8
  41. package/factory/orchestrator/dist/daemon.js +154 -36
  42. package/factory/orchestrator/dist/db/projector.js +278 -29
  43. package/factory/orchestrator/dist/db/queries.js +1831 -94
  44. package/factory/orchestrator/dist/db/schema.js +57 -4
  45. package/factory/orchestrator/dist/declaredArtifactLine.js +18 -0
  46. package/factory/orchestrator/dist/delegation.js +86 -19
  47. package/factory/orchestrator/dist/diffstat.js +15 -8
  48. package/factory/orchestrator/dist/dispatchLint.js +13 -37
  49. package/factory/orchestrator/dist/effort.js +14 -2
  50. package/factory/orchestrator/dist/effortTiers.js +13 -0
  51. package/factory/orchestrator/dist/env.js +26 -0
  52. package/factory/orchestrator/dist/epic.js +323 -38
  53. package/factory/orchestrator/dist/epicBudget.js +93 -0
  54. package/factory/orchestrator/dist/epicWidth.js +22 -5
  55. package/factory/orchestrator/dist/errorIssues.js +10 -4
  56. package/factory/orchestrator/dist/escalation.js +2 -2
  57. package/factory/orchestrator/dist/events.js +105 -3
  58. package/factory/orchestrator/dist/feedback.js +169 -0
  59. package/factory/orchestrator/dist/findingContext.js +26 -0
  60. package/factory/orchestrator/dist/findings.js +83 -12
  61. package/factory/orchestrator/dist/gate.js +120 -10
  62. package/factory/orchestrator/dist/goalCheck.js +2 -2
  63. package/factory/orchestrator/dist/harness.js +25 -17
  64. package/factory/orchestrator/dist/hookDecision.js +92 -3
  65. package/factory/orchestrator/dist/integration.js +9 -7
  66. package/factory/orchestrator/dist/judgePreflight.js +25 -4
  67. package/factory/orchestrator/dist/judgeRoles.js +47 -0
  68. package/factory/orchestrator/dist/judgeStopHook.js +90 -32
  69. package/factory/orchestrator/dist/judges.js +419 -21
  70. package/factory/orchestrator/dist/lessons.js +190 -11
  71. package/factory/orchestrator/dist/mcp.js +6 -6
  72. package/factory/orchestrator/dist/paths.js +10 -7
  73. package/factory/orchestrator/dist/plan.js +269 -33
  74. package/factory/orchestrator/dist/planQuorum.js +5 -2
  75. package/factory/orchestrator/dist/policy.js +1275 -30
  76. package/factory/orchestrator/dist/projectName.js +25 -0
  77. package/factory/orchestrator/dist/promptCapture.js +306 -0
  78. package/factory/orchestrator/dist/promptHook.js +33 -0
  79. package/factory/orchestrator/dist/promptImport.js +281 -0
  80. package/factory/orchestrator/dist/prompts.js +13 -0
  81. package/factory/orchestrator/dist/providers/schema-validate.js +2 -2
  82. package/factory/orchestrator/dist/providers/types.js +1 -1
  83. package/factory/orchestrator/dist/queue.js +4 -4
  84. package/factory/orchestrator/dist/quorum.js +18 -5
  85. package/factory/orchestrator/dist/results.js +25 -7
  86. package/factory/orchestrator/dist/roadmap.js +10 -5
  87. package/factory/orchestrator/dist/run-cli.js +15 -10
  88. package/factory/orchestrator/dist/runner.js +2 -2
  89. package/factory/orchestrator/dist/schemas.js +56 -8
  90. package/factory/orchestrator/dist/severity.js +8 -4
  91. package/factory/orchestrator/dist/spec.js +177 -13
  92. package/factory/orchestrator/dist/specChange.js +13 -9
  93. package/factory/orchestrator/dist/stack.js +6 -6
  94. package/factory/orchestrator/dist/storeRoot.js +90 -0
  95. package/factory/orchestrator/dist/symbols.js +261 -6
  96. package/factory/orchestrator/dist/taskEvents.js +179 -27
  97. package/factory/orchestrator/dist/taskStatus.js +14 -0
  98. package/factory/orchestrator/dist/testerAudit.js +31 -12
  99. package/factory/orchestrator/dist/testgate.js +16 -13
  100. package/factory/orchestrator/dist/uiuxGate.js +83 -0
  101. package/factory/orchestrator/dist/usage.js +97 -40
  102. package/factory/orchestrator/dist/waivers.js +39 -5
  103. package/factory/orchestrator/dist/waveBudget.js +2 -0
  104. package/factory/orchestrator/dist/waveConcurrency.js +143 -7
  105. package/factory/orchestrator/dist/workroot.js +1 -1
  106. package/factory/orchestrator/dist/worktree.js +61 -19
  107. package/factory/orchestrator/drizzle/0013_operator_feedback.sql +19 -0
  108. package/factory/orchestrator/drizzle/0014_misty_madripoor.sql +2 -0
  109. package/factory/orchestrator/drizzle/0015_gifted_omega_flight.sql +1 -0
  110. package/factory/orchestrator/drizzle/0016_milestones_project_default.sql +30 -0
  111. package/factory/orchestrator/drizzle/0017_events_raw_cli_session_id.sql +2 -0
  112. package/factory/orchestrator/drizzle/meta/_journal.json +35 -0
  113. package/factory/policies/budgets.yml +123 -33
  114. package/factory/policies/crosscheck.yml +51 -13
  115. package/factory/policies/delegation.yml +3 -3
  116. package/factory/policies/effort.yml +18 -10
  117. package/factory/policies/guardrails.yml +8 -6
  118. package/factory/policies/harness.yml +5 -5
  119. package/factory/policies/lessons.md +53 -2
  120. package/factory/policies/scheduler.yml +4 -4
  121. package/factory/policies/sensitive-paths.yml +1 -1
  122. package/factory/policies/severity.yml +13 -1
  123. package/factory/policies/stack.yml +6 -6
  124. package/factory/policies/taxonomy.yml +9 -3
  125. package/factory/policies/worktree.yml +15 -9
  126. package/factory/specs/roadmap.md +7 -7
  127. package/factory/specs/schema/event.schema.json +8 -1
  128. package/factory/specs/schema/result.schema.json +21 -8
  129. package/factory/specs/schema/task-spec.schema.json +12 -0
  130. package/package.json +16 -12
  131. package/.claude/agents/uiux.md +0 -133
@@ -10,7 +10,7 @@ until the hard stop in step 7**, and the operator decides it there. The
10
10
  output of a clean audit is one roadmap milestone and one epic spec, which
11
11
  `/bs plan` then plans like any other; the audit itself writes nothing into
12
12
  the project's working tree. The mechanics are the seven `audit` verbs of
13
- `smith` and the judgment is this file — an audit outlives the turn that starts it, so keep
13
+ `bs` and the judgment is this file — an audit outlives the turn that starts it, so keep
14
14
  the `audit_id` the way you keep a session id.
15
15
 
16
16
  ## 1. Ask for the project directory once
@@ -20,14 +20,14 @@ nothing reads a cwd and nothing keeps a "current audit" on the side, so two
20
20
  audits on two projects cannot be confused. The project must be a git
21
21
  checkout; a directory this factory did not build is as auditable as one it
22
22
  did. Then `mkdir -p state/audit` in the factory's **work root** — the clone
23
- itself, or the `.blacksmith/` that `smith init` made beside your code; `smith
23
+ itself, or the `.blacksmith/` that `bs init` made beside your code; `bs
24
24
  init` prints it as `workRoot`. The judges' artifacts live there, under runtime
25
25
  state, never under the project.
26
26
 
27
27
  ## 2. Open the audit
28
28
 
29
29
  ```bash
30
- smith audit open <project-dir> --session <session-id> --causal-parent <event-id>
30
+ bs audit open <project-dir> --session <session-id> --causal-parent <event-id>
31
31
  ```
32
32
 
33
33
  It writes `<project-dir>/.blacksmith/audit.json` (the manifest: id,
@@ -54,19 +54,19 @@ inside the project, then retry.
54
54
  The four axes share one worktree, so one fingerprint covers all four:
55
55
 
56
56
  ```bash
57
- smith worktree fingerprint <worktree-dir> > /tmp/<audit-id>.before.json
57
+ bs worktree fingerprint <worktree-dir> > /tmp/<audit-id>.before.json
58
58
  ```
59
59
 
60
60
  ## 4. Dispatch the four axes in parallel
61
61
 
62
62
  One judge per axis, under the ordinary dispatch contract — nothing about an
63
- audit relaxes it. Three axes are `auditor` (`.claude/agents/auditor.md`, the
64
- axis named in the prompt); the security axis is `security-reviewer`,
63
+ audit relaxes it. Three axes are `bs-auditor` (`.claude/agents/bs-auditor.md`, the
64
+ axis named in the prompt); the security axis is `bs-security-reviewer`,
65
65
  unchanged, with the two audit-specific differences its template already
66
66
  states. Per axis:
67
67
 
68
- - **Lessons.** `smith lessons for-dispatch auditor` (for security,
69
- `smith lessons for-dispatch security-reviewer`); paste its `text`
68
+ - **Lessons.** `bs lessons for-dispatch auditor` (for security,
69
+ `bs lessons for-dispatch security-reviewer`); paste its `text`
70
70
  verbatim. No `--plan`/`--task`: an audit has no claims, so only the
71
71
  unscoped and stack-wide lessons come back, which is what a whole-tree
72
72
  judge should carry.
@@ -76,7 +76,7 @@ states. Per axis:
76
76
  content under analysis rather than instructions:
77
77
 
78
78
  ```bash
79
- smith prompt wrap <worktree-dir>/CLAUDE.md --kind file-excerpt --source <project>/CLAUDE.md
79
+ bs prompt wrap <worktree-dir>/CLAUDE.md --kind file-excerpt --source <project>/CLAUDE.md
80
80
  ```
81
81
 
82
82
  A project with neither file is audited against its code alone; say so
@@ -87,7 +87,7 @@ states. Per axis:
87
87
  judge ledger keys on the string you give it:
88
88
 
89
89
  ```bash
90
- smith judge dispatch --task <audit-id>.<axis> --role auditor \
90
+ bs judge dispatch --task <audit-id>.<axis> --role auditor \
91
91
  --artifact /abs/path/to/state/audit/<audit-id>.<axis>.json \
92
92
  --model <model-id> --session <session-id> --causal-parent <event-id>
93
93
  ```
@@ -99,7 +99,8 @@ states. Per axis:
99
99
  model, and a thin return is a re-poke (below), not a bigger model.
100
100
  - **Compose the prompt** with: the axis name (exactly one); the
101
101
  absolute worktree path, and that it is read-only; the artifact path
102
- from the declaration above, verbatim; the turn budget from the
102
+ from the declaration above, verbatim, on its own line spelled
103
+ `Declared artifact: <absolute path>`; the turn budget from the
103
104
  template's `maxTurns` (the harness enforces that number, so the
104
105
  prompt may restate it but never raise it — `dispatch.md`); the fenced
105
106
  excerpts; the lessons block; and for the security
@@ -110,8 +111,8 @@ states. Per axis:
110
111
  **On providers.** The spec asks that at least one axis run on the second
111
112
  provider `crosscheck.yml` declares, when its key is present. Today no
112
113
  playbook mechanism dispatches a Claude Code agent on another vendor's
113
- model — `smith judge run` calls a provider by hand with a verdict
114
- request, and `smith crossfind run` reads a diff, not a tree — so the
114
+ model — `bs judge run` calls a provider by hand with a verdict
115
+ request, and `bs crossfind run` reads a diff, not a tree — so the
115
116
  four axes run on Claude models, and that gap is recorded in the spec
116
117
  rather than papered over here with a dispatch that does not exist. Fable
117
118
  is never used, on any axis: that is a standing operator constraint, not
@@ -120,26 +121,32 @@ a tier choice.
120
121
  ## 5. Verify, report, record — per axis, as each returns
121
122
 
122
123
  ```bash
123
- smith worktree verify <worktree-dir> --before /tmp/<audit-id>.before.json
124
+ bs worktree verify <worktree-dir> --before /tmp/<audit-id>.before.json
124
125
  ```
125
126
 
126
127
  Clean, then the artifact:
127
128
 
128
129
  ```bash
129
- smith judge report --task <audit-id>.<axis> --role auditor \
130
+ bs judge report --task <audit-id>.<axis> --role auditor \
130
131
  --session <session-id> --causal-parent <event-id>
131
- smith audit record <project-dir> --axis <axis> --evidence state/audit/<audit-id>.<axis>.json \
132
+ bs audit record <project-dir> --axis <axis> --evidence state/audit/<audit-id>.<axis>.json \
132
133
  --session <session-id> --causal-parent <event-id>
133
134
  ```
134
135
 
136
+ `judge report`'s own JSON carries the `event_id` of the `judge-reported`
137
+ event it just appended — pass that straight through as `audit record`'s
138
+ `--causal-parent` on the second line, rather than reading the log for it.
139
+
135
140
  `--role security-reviewer` on both lines for the security axis, as at
136
- dispatch. `judge report` refuses three ways — `judges.artifact-missing` (the judge
141
+ dispatch. `judge report` refuses four ways — `judges.artifact-missing` (the judge
137
142
  ended on a plan and wrote nothing: re-poke it, and
138
- `smith judge outstanding --session <session-id> --task <audit-id>.<axis>`
143
+ `bs judge outstanding --session <session-id> --task <audit-id>.<axis>`
139
144
  exits 1 while the file is still owed, so it is the loop condition),
140
145
  `judges.artifact-unparseable` (it narrated), `judges.artifact-not-a-list`
141
146
  (some other shape; only `--role grader` may hand in a verdict document
142
- instead of a list). `audit record` then refuses the evidence itself —
147
+ instead of a list), `judges.artifact-stale` (the file predates this turn's
148
+ dispatch, or sat unchanged since it: a previous audit's file at the same
149
+ path is not this one's report — re-poke the judge). `audit record` then refuses the evidence itself —
143
150
  `audit.evidence-carries-identity` (the judge set `fingerprint`,
144
151
  `status`, `axis`, `audit_id`, `ts`, `epic` or `same_as`, which are the
145
152
  store's to mint), `audit.evidence-incomplete` (a `failure_scenario` leg
@@ -159,8 +166,8 @@ return not yet verified**, since none of them can now be attributed to
159
166
  the HEAD the audit opened on. Then
160
167
 
161
168
  ```bash
162
- smith audit close <project-dir> --force --session <session-id> --causal-parent <event-id>
163
- smith audit open <project-dir> --session <session-id> --causal-parent <event-id>
169
+ bs audit close <project-dir> --force --session <session-id> --causal-parent <event-id>
170
+ bs audit open <project-dir> --session <session-id> --causal-parent <event-id>
164
171
  ```
165
172
 
166
173
  `--force` is right here and only here — the drift is the known reason
@@ -172,7 +179,7 @@ was recorded against a verified tree — so a clean axis is not re-run.
172
179
  ## 6. Consolidate once all four axes are recorded
173
180
 
174
181
  ```bash
175
- smith audit consolidate <project-dir>
182
+ bs audit consolidate <project-dir>
176
183
  ```
177
184
 
178
185
  A pure read, no envelope. It folds the store and prints `clusters` —
@@ -197,9 +204,9 @@ is yes, the operator adds the line, and the question is not asked again.
197
204
  Then persist each answer:
198
205
 
199
206
  ```bash
200
- smith audit decide <project-dir> --fingerprint <fp> --decision accept \
207
+ bs audit decide <project-dir> --fingerprint <fp> --decision accept \
201
208
  --session <session-id> --causal-parent <event-id>
202
- smith audit decide <project-dir> --fingerprint <fp> --decision merge --same-as <survivor-fp> \
209
+ bs audit decide <project-dir> --fingerprint <fp> --decision merge --same-as <survivor-fp> \
203
210
  --note "<why>" --session <session-id> --causal-parent <event-id>
204
211
  ```
205
212
 
@@ -213,7 +220,7 @@ re-raises as fresh rather than silently holding.
213
220
  ## 8. Cut one epic from what was accepted
214
221
 
215
222
  ```bash
216
- smith audit cut <project-dir> --epic <epic-id> --title "<milestone title>" \
223
+ bs audit cut <project-dir> --epic <epic-id> --title "<milestone title>" \
217
224
  --session <session-id> --causal-parent <event-id>
218
225
  ```
219
226
 
@@ -238,7 +245,7 @@ cut belong to the next audit's cut, not to a second cut from this one.
238
245
  ## 9. Close the audit
239
246
 
240
247
  ```bash
241
- smith audit close <project-dir> --session <session-id> --causal-parent <event-id>
248
+ bs audit close <project-dir> --session <session-id> --causal-parent <event-id>
242
249
  ```
243
250
 
244
251
  It verifies the worktree one last time against the manifest's
@@ -254,7 +261,7 @@ is already gone closes with `verified: false` and no refusal.
254
261
  The epic is now an ordinary roadmap milestone: run `/bs plan` on it
255
262
  ([`plan.md`](plan.md)), then `/bs run`. When that run reaches its epic-close
256
263
  step, it runs
257
- `smith audit resolve <project-dir> --epic <epic-id> --session <session-id> --causal-parent <event-id> [--specs-dir <dir>]`,
264
+ `bs audit resolve <project-dir> --epic <epic-id> --session <session-id> --causal-parent <event-id> [--specs-dir <dir>]`,
258
265
  which reads the epic's newest plan and appends `fixed` only for a carried
259
266
  finding some task in it still claims — the rest come back `deferred`, still
260
267
  `accepted`, because the plan scoped them out on purpose. Repeatable;
@@ -2,7 +2,11 @@
2
2
 
3
3
  Applies to every agent you dispatch, in every `/bs` playbook. This is the
4
4
  per-dispatch envelope, not a role prompt — the templates still own those
5
- (`.claude/agents/<role>.md`), and nothing here restates them.
5
+ (`.claude/agents/bs-<role>.md`), and nothing here restates them.
6
+
7
+ The agent you dispatch is named `bs-<role>` (`bs-coder`, `bs-spec-reviewer`);
8
+ the **role** stays bare (`coder`) wherever it is data: `--role`, `--agent`,
9
+ `--actor`, event payloads, policies and lessons scopes.
6
10
 
7
11
  ## Carry into the prompt
8
12
 
@@ -19,14 +23,14 @@ with nothing written). So state the template's number, never a higher one,
19
23
  and tell every role that owes a file to write it before it refines it. A
20
24
  role that keeps running out is a template to raise, not a prompt to inflate.
21
25
 
22
- Read that number from `.claude/agents/<role>.md` as the file stands now, at
26
+ Read that number from `.claude/agents/bs-<role>.md` as the file stands now, at
23
27
  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
28
+ it. `bs agents sync` may have rewritten it locally from
29
+ `BS_MAXTURNS_<ROLE>` (an uncommitted, per-box edit), and Claude Code
26
30
  enforces what the file says. A sync reaches only agents spawned after it.
27
31
 
28
32
  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
33
+ dispatch, the declared-artifact line below verbatim too. `bs dispatch
30
34
  lint <prompt-file> --role <role> --task <id> --session <id>` reads a
31
35
  composed prompt back and checks both against the template and the ledger,
32
36
  before the agent ever runs. It fails closed: a number that does not match
@@ -35,12 +39,22 @@ lower — a missing turn-budget line, a template it cannot read at all, and a
35
39
  judge prompt missing or mismatching its artifact line are each a lint
36
40
  failure, not a maybe.
37
41
 
42
+ In this clone a PreToolUse guard also refuses, at the `Agent` call, a judge-role
43
+ dispatch (`bs-reviewer`, `bs-verifier`, `bs-grader`, `bs-spec-reviewer`,
44
+ `bs-security-reviewer`, `bs-auditor`, with or without the
45
+ `blacksmith:` namespace; the bare pre-prefix names too) whose prompt lacks an absolute
46
+ `Declared artifact: <path>` line. It reads the prompt itself, so a dispatch
47
+ that points the agent at a brief file still carries the line inline. It checks
48
+ the line only; `dispatch lint` still checks it against the ledger. An ad-hoc
49
+ reviewer or verifier dispatch must therefore declare an artifact too, or use
50
+ `general-purpose`.
51
+
38
52
  ## Splice the compiled lessons into every prompt (agent-interviews.md N-9, P9-2)
39
53
 
40
54
  Before you dispatch, run
41
55
 
42
56
  ```
43
- smith lessons for-dispatch <role> [--plan factory/specs/active/<epic>/plan-vN.json --task <task-id>]
57
+ bs lessons for-dispatch <role> [--plan factory/specs/active/<epic>/plan-vN.json --task <task-id>]
44
58
  ```
45
59
 
46
60
  and paste its `text` verbatim into the prompt you compose. That is the only
@@ -66,14 +80,14 @@ matched nothing" must not look like "never ran". Every failure is loud: an
66
80
  unknown role, a template with no marker, a marker naming a scope outside
67
81
  taxonomy.yml, or a missing compiled file all exit 1 rather than hand you a
68
82
  silently empty block. Do not paper over one by dispatching without lessons —
69
- fix the template or run `smith lessons compile`.
83
+ fix the template or run `bs lessons compile`.
70
84
 
71
85
  ## Splice the open findings into every worktree dispatch (P9-15)
72
86
 
73
87
  Before you dispatch a role that touches files, run
74
88
 
75
89
  ```
76
- smith findings for-dispatch --plan factory/specs/active/<epic>/plan-vN.json --task <task-id>
90
+ bs findings for-dispatch [--plan factory/specs/active/<epic>/plan-vN.json --task <task-id>]
77
91
  ```
78
92
 
79
93
  and paste its `text` verbatim into the prompt, next to the lessons block. It
@@ -81,8 +95,11 @@ intersects the session's still-open findings (`raised`, `confirmed`) against
81
95
  that task's claims and hands back the overlap as **context, not scope**
82
96
  (D-26). The task's
83
97
  own findings are left out: those reach it as scope through its fix round.
84
- Unlike `lessons for-dispatch`, `--plan` is **required** — the claims come from
85
- the plan, and an empty answer that was never computed is worse than an error.
98
+ `--plan` and `--task` come as a **pair** — the claims come from the plan, and
99
+ either one alone is refused, because an empty answer that was never computed
100
+ is worse than an error. Omit both only for an epic-level role (below): with
101
+ no task there are no claims to join, and the block lists every open finding
102
+ in the epic, path-less ones included.
86
103
  The block is delimited by `<!-- BEGIN OPEN FINDINGS -->` /
87
104
  `<!-- END OPEN FINDINGS -->` with the same one-line escaping, renders even
88
105
  when nothing matches, and names any open finding it could not check at all
@@ -101,15 +118,15 @@ sessions, not throttling the wave.
101
118
  ## Splitting an epic across sessions (P9-7)
102
119
 
103
120
  A new session's `session-start` may name an event in the previous session as
104
- its `causal_parent` — `smith session start <new-id> --continues <old-id>#<n>` —
121
+ its `causal_parent` — `bs session start <new-id> --continues <old-id>#<n>` —
105
122
  and that is the only place a cross-session edge is allowed: everything after
106
123
  the root chains locally, so each session has exactly one entry edge and the log
107
- stays a tree of sessions rather than a graph. `smith event lineage
108
- <session-id>` prints the chain root-first, and `smith event tail <session-id>
124
+ stays a tree of sessions rather than a graph. `bs event lineage
125
+ <session-id>` prints the chain root-first, and `bs event tail <session-id>
109
126
  --lineage` tails the whole epic instead of the session that happens to be
110
127
  running. A cross-session parent on a non-root event is
111
128
  `events.cross-session-parent-not-root`; a parent naming a session with no log
112
- is `events.unknown-causal-session` (usually a typo'd session id); `smith
129
+ is `events.unknown-causal-session` (usually a typo'd session id); `bs
113
130
  session start` on a session that already has one is
114
131
  `events.session-already-started`, and the message names the event you should
115
132
  have chained off.
@@ -121,7 +138,7 @@ A worker's result file carries only `run_status`, `structured_output` and
121
138
  `token_usage` by passing them to the gate:
122
139
 
123
140
  ```
124
- smith gate run <task-id> --result <agent-half.json> \
141
+ bs gate run <task-id> --result <agent-half.json> \
125
142
  --agent coder --provider claude --model-tier mid \
126
143
  --input-tokens <n> --output-tokens <n>
127
144
  ```
@@ -130,7 +147,18 @@ smith gate run <task-id> --result <agent-half.json> \
130
147
  `token_usage` left the agent's half in P9-17 for the same reason the other
131
148
  four never belonged there: an agent cannot read its own meter, so what it
132
149
  writes is invented. `stampResultEnvelope` throws
133
- `results.agent-wrote-owned-field` if the file carries any of the five. Without
150
+ `results.agent-wrote-owned-field` if the file carries any of the five.
151
+
152
+ `--input-tokens`/`--output-tokens` are optional, both-or-neither (#220): a
153
+ harness that ran the worker as a subprocess and parsed its usage always has
154
+ both, but you — a dispatcher running inside Claude Code — have no API onto a
155
+ subagent's token spend, so you have neither. Omit both flags rather than
156
+ invent a count; `stampResultEnvelope` then stamps `token_usage: {measured:
157
+ false}`, which the schema accepts as readily as a real count and every
158
+ consumer (budget checks, reports) reads as "not measured", never as zero.
159
+ Passing exactly one of the two flags is refused
160
+ (`results.partial-token-count`) — half a measurement is not an honest "not
161
+ measured" and not a count either. Without
134
162
  `--agent`, `--result` is taken as a complete document, which is the shape a
135
163
  replay or a fixture hands over. A judge returns *evidence* — no
136
164
  `finding_id`, no `fingerprint`, no `found_by` — and you mint the findings with
@@ -148,7 +176,7 @@ names the culprit.
148
176
  ## Round counting and escalation (agent-interviews.md N-4)
149
177
 
150
178
  A task's round number is not something an agent tells you — derive it by
151
- counting `dispatch_decision` events for the same `task_id` (`smith event tail
179
+ counting `dispatch_decision` events for the same `task_id` (`bs event tail
152
180
  <session-id> --task <task-id> -n 200`), because a re-dispatched agent has no
153
181
  memory of its previous attempt and will happily report round 1 forever.
154
182
 
@@ -164,13 +192,13 @@ memory of its previous attempt and will happily report round 1 forever.
164
192
  - **The grader's 2-round cap is a hard stop, not a ladder rung.** At a
165
193
  round-2 `fail` the operator re-scopes through a concrete, logged path, not
166
194
  a bare re-dispatch:
167
- 1. `smith plan propose` with a `supersede` pairing the failing task id to a
195
+ 1. `bs plan propose` with a `supersede` pairing the failing task id to a
168
196
  new one (`PlanChanges.supersede`, spec.ts's `amendPlan`) — the operator
169
197
  approves the re-scoping, this command only records the proposal.
170
- 2. `smith plan approve` the proposed version. This is what writes the
198
+ 2. `bs plan approve` the proposed version. This is what writes the
171
199
  `successors{old->new}` pairing (spec.ts's `taskSuccessors`) that
172
200
  everything below checks against.
173
- 3. `smith worktree create --from <old-task-id> --session <id> ...` to cut
201
+ 3. `bs worktree create --from <old-task-id> --session <id> ...` to cut
174
202
  the successor's branch from the predecessor's HEAD instead of
175
203
  integration — refused as `worktree.not-a-successor` unless step 2 logged
176
204
  that exact pairing.
@@ -193,7 +221,7 @@ The ladder above is now checkable rather than only remembered (P9-32). After a
193
221
  blocked round, assert it:
194
222
 
195
223
  ```bash
196
- smith escalation check <session-id> [--task <task-id>]
224
+ bs escalation check <session-id> [--task <task-id>]
197
225
  ```
198
226
 
199
227
  It counts failed rounds (`gate-outcome` with outcome `blocked`) against
@@ -220,7 +248,7 @@ git rev-parse HEAD
220
248
  and after it returns, run the check against its write root:
221
249
 
222
250
  ```
223
- smith claims check . --roots 'factory/specs/active/<epic-id>/**' --since <sha>
251
+ bs claims check . --roots 'factory/specs/active/<epic-id>/**' --since <sha>
224
252
  ```
225
253
 
226
254
  Exit 0 means every changed path is inside the root; exit 1 prints
@@ -252,9 +280,10 @@ non-terminal rows were these.
252
280
  So: the closing spec-reviewer, the goal-check spec-reviewer, the planner
253
281
  rendering a verdict and the scribe are dispatched against
254
282
  `<epic>/integration`; a plan critic against `<epic>/plan-v<n>`. Same for
255
- `smith judge dispatch --task` when the judge is epic-level. The lessons and
256
- findings splices above still take no `--task` for these roles — the ref is
257
- for the log, not for claim filtering.
283
+ `bs judge dispatch --task` when the judge is epic-level. The lessons and
284
+ findings splices above still take no `--plan`/`--task` for these roles — the
285
+ ref is for the log, not for claim filtering; the findings block comes back
286
+ epic-wide.
258
287
 
259
288
  ## Fingerprint the worktree around every judge (agent-interviews.md N-10, P9-5)
260
289
 
@@ -264,9 +293,9 @@ runs the suite writes files. Take the fingerprint before you dispatch, verify
264
293
  after it returns:
265
294
 
266
295
  ```bash
267
- smith worktree fingerprint <worktree-dir> > /tmp/<task-id>.before.json
296
+ bs worktree fingerprint <worktree-dir> > /tmp/<task-id>.before.json
268
297
  # ... dispatch the judge ...
269
- smith worktree verify <worktree-dir> --before /tmp/<task-id>.before.json
298
+ bs worktree verify <worktree-dir> --before /tmp/<task-id>.before.json
270
299
  ```
271
300
 
272
301
  Exit 1 means the judge moved what it was judging — `violation.paths` names it
@@ -287,7 +316,7 @@ receiving agent that it is reading *content under analysis* rather than
287
316
  prompt, splice the block, not the text:
288
317
 
289
318
  ```bash
290
- smith prompt wrap <file> --kind web-fetch --source <url> # or - for stdin
319
+ bs prompt wrap <file> --kind web-fetch --source <url> # or - for stdin
291
320
  ```
292
321
 
293
322
  Kinds are a closed list (`web-fetch`, `web-search`, `issue-text`,
@@ -302,7 +331,7 @@ cannot close the block early and continue as if it were your own prompt.
302
331
  A researcher brief is the one artifact where fetched text becomes advice:
303
332
 
304
333
  ```bash
305
- smith research check --brief state/results/<task-id>.json
334
+ bs research check --brief state/results/<task-id>.json
306
335
  ```
307
336
 
308
337
  Exit 1 means `contract.uncited-claim` (a claim with no repo `path:line` and no
@@ -321,19 +350,33 @@ completion here is "the file exists and parses", never "the agent said
321
350
  something":
322
351
 
323
352
  ```bash
324
- smith judge dispatch --task <task-id> --role reviewer --round 1 \
353
+ bs judge dispatch --task <task-id> --role reviewer --round 1 \
325
354
  --artifact /abs/path/<task-id>.reviewer.json \
326
355
  --model <model-id> --model-tier <frontier|mid|small> \
327
356
  --session ... --plan-version <n> --causal-parent ...
328
357
  # ... dispatch the judge, telling it to write exactly that path ...
329
- smith judge report --task <task-id> --role reviewer --session ... --causal-parent ...
330
- smith judge outstanding --task <task-id> --session ...
358
+ bs judge report --task <task-id> --role reviewer --session ... --causal-parent ...
359
+ bs judge outstanding --task <task-id> --session ...
331
360
  ```
332
361
 
333
- `judge report` reads the declared file and refuses it three ways —
334
- `judges.artifact-missing` (re-poke the agent; recovery was six for six),
335
- `judges.artifact-unparseable` (it narrated instead of reporting),
336
- `judges.artifact-not-a-list` (it wrote some other shape). `judge outstanding`
362
+ `judge dispatch` refuses a role that cannot open a judge turn with
363
+ `judges.non-judge-role` — accepted roles are reviewer, verifier, grader,
364
+ spec-reviewer, security-reviewer, auditor, and uiux (wave.md step 3's pre-code
365
+ spec and step 5's visual pass; each is bracketed with `judge dispatch`/`judge
366
+ report` the same as the other six, but also carries `--kind spec` or `--kind
367
+ visual` — the two are separate turns on the same task, and the gate's uiux
368
+ stage reads them apart; a uiux dispatch with no `--kind` is refused with
369
+ `judges.kind-required`). `judge report` reads the declared file and refuses it four
370
+ ways — `judges.artifact-missing` (re-poke the agent; recovery was six for
371
+ six), `judges.artifact-unparseable` (it narrated instead of reporting),
372
+ `judges.artifact-not-a-list` (it wrote some other shape), and
373
+ `judges.artifact-stale` (the file was not written during this turn: its mtime
374
+ is not later than the turn's `dispatch_decision`, or it was already on disk
375
+ at dispatch and nothing has rewritten it since — an earlier round's report,
376
+ or a placeholder; re-poke the judge, never `touch` the file). Declared paths
377
+ are reused across rounds, so `judge dispatch` does not refuse an existing
378
+ file; it records its mtime, and the report refuses that same mtime. The
379
+ same check runs when `gate run --evidence` closes the turn. `judge outstanding`
337
380
  prints what is still owed and **exits 1 while anything is**, so it is the loop
338
381
  condition for a re-poke, not just a report. Passing the file to
339
382
  `gate run --evidence <path> --found-by <role>` reports for you, so the normal
@@ -342,7 +385,7 @@ declared shape is not a list: for `--role grader` the artifact is its result
342
385
  document (`state/results/<task-id>.grader-r<round>.json`), `finding_count` is
343
386
  its non-`pass` criteria, and `gate run --grader <file>` closes its turn the
344
387
  way `--evidence` closes the others (FD-1). `--model` is required and has no
345
- default: this is an ordinary dispatch record, and `smith dispatch check` (P9-23)
388
+ default: this is an ordinary dispatch record, and `bs dispatch check` (P9-23)
346
389
  compares reviewer and verifier by model id, so a placeholder here would make
347
390
  that audit answer for a session nobody ran. `--model-tier` is optional and
348
391
  that is the trap: an omitted tier is recorded as `frontier`, so a judge you
@@ -355,17 +398,19 @@ true one on the record. `--no-findings` records an operator
355
398
  the factory; a judge that genuinely found nothing writes `[]` and reports.
356
399
 
357
400
  Tell the judge the exact path on its own line, verbatim —
358
- `Declared artifact: /abs/path/<task-id>.reviewer.json` — the spelling `smith
401
+ `Declared artifact: /abs/path/<task-id>.reviewer.json` — the spelling `bs
359
402
  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.
403
+ has landed in the ledger. `bs judge dispatch` prints it as `expected_line`:
404
+ paste that, inline in the prompt. Missing, relative, or a path that does not
405
+ match what `--artifact` declared are each a lint failure, not a maybe, and a
406
+ missing or relative one is refused at the `Agent` call before lint is reached.
362
407
 
363
408
  ## Dispatching the security-reviewer (agent-interviews.md N-7)
364
409
 
365
410
  Do not eyeball the path list — ask:
366
411
 
367
412
  ```bash
368
- smith security triggers --task <spec.json> [--epic-tag security] [--recheck]
413
+ bs security triggers --task <spec.json> [--epic-tag security] [--recheck]
369
414
  ```
370
415
 
371
416
  It matches the task's `claims[]` against `factory/policies/sensitive-paths.yml`
@@ -389,9 +434,9 @@ exceptions — today one, `wave-runner` — and each of them earns it by opening
389
434
  session of its own against your dispatch's event id before it dispatches
390
435
  anything.
391
436
 
392
- Run `smith issues report --session <session-id> --state-dir <dir> [--epic
437
+ Run `bs issues report --session <session-id> --state-dir <dir> [--epic
393
438
  <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
439
+ `error-logged` you just wrote, a `gate-outcome` that `bs gate run` records
395
440
  with outcome `blocked`, or a `task-added` whose payload sets `task_status:
396
441
  failed`. The reporting verb re-reads the whole session lineage log and folds
397
442
  all three source event types, so calling it once after any of them covers
@@ -402,30 +447,35 @@ this log; the run keeps going.
402
447
 
403
448
  ## The task id goes on the event, not in the payload
404
449
 
405
- `smith event append` reads `task_id` at the top level of the JSON, beside
450
+ `bs event append` reads `task_id` at the top level of the JSON, beside
406
451
  `session_id`; a copy inside `payload` is not a substitute (D-245: a task id in
407
452
  the payload opens an agent scoped to no task, which no task-scoped terminal
408
453
  event can close). The readers take it from either place, but only the
409
454
  top-level field is indexed — write it there.
410
455
 
411
456
  The `dispatch_decision` payload is `{agent_role, provider, model_tier, model}`
412
- — all four required, the write is rejected without them. `model` is the
413
- concrete id (`claude-opus-5`, `gpt-5-codex`, or `<command>:default` for a CLI
414
- provider that genuinely does not know), and it is what makes
457
+ — all four required, the write is rejected without them. Carry a `reason`
458
+ too: one line, plain words, on why this dispatch is happening. It is not
459
+ rejected without one, so writers drift onto `rationale`/`note`/`why` for the
460
+ same idea and the dashboard's Overview page falls back to a derived
461
+ role/task/round line the moment `reason` itself is empty — a fallback, not a
462
+ substitute for saying what actually happened. `model` is the concrete id
463
+ (`claude-opus-5`, `gpt-5-codex`, or `<command>:default` for a CLI provider
464
+ that genuinely does not know), and it is what makes
415
465
  crosscheck.yml's `finder_ne_critic` checkable instead of aspirational: the
416
466
  tier cannot distinguish opus from fable, so until this field existed *"did
417
467
  the spec-reviewer run on the planner's own model?"* had no answer in the log.
418
468
  After a plan or review round, assert it:
419
469
 
420
470
  ```bash
421
- smith dispatch check <session-id> [--task <task-id>]
471
+ bs dispatch check <session-id> [--task <task-id>]
422
472
  ```
423
473
 
424
474
  It exits 1 on a violation **and** on `unverifiable` — a critic dispatch with
425
475
  no model, or with no finder dispatch before it to compare against. A check
426
476
  that cannot answer must not read as a pass.
427
477
 
428
- Its sibling `smith tester check <session-id>` asks the other half of the same
478
+ Its sibling `bs tester check <session-id>` asks the other half of the same
429
479
  question — not *which model* graded, but *whose turn* did. `crosscheck.yml`'s
430
480
  `role_isolation` pairs `coder` with `tester`, and a `testgate-result` with no
431
481
  separate `tester` dispatch behind it is a **violation** there, where an absent
@@ -437,7 +487,7 @@ reading is only sound while every dispatcher owns its own log. Once a wave runs
437
487
  as a dispatched agent, assert that too:
438
488
 
439
489
  ```bash
440
- smith delegation check <session-id> [--task <task-id>]
490
+ bs delegation check <session-id> [--task <task-id>]
441
491
  ```
442
492
 
443
493
  It answers two questions in one report, because they fail apart: `grants` is
@@ -3,13 +3,16 @@
3
3
  [`dispatch.md`](dispatch.md) binds every agent this playbook dispatches;
4
4
  none of it is restated here.
5
5
 
6
- 1. `smith lessons candidates [--session <id>] [--db state/smith.db]` —
7
- pending candidates with their statement, type, scope, and evidence/
8
- provenance event ids.
6
+ 1. `bs lessons candidates [--session <id>] [--db state/smith.db]
7
+ [--state-dir <dir>]` — pending candidates with their statement, type,
8
+ scope, and evidence/provenance event ids. `--state-dir` matters whenever
9
+ `--session` is given: it is validated against that event log the same
10
+ way `raise`/`approve`/`reject`/`db rebuild` do, so a typoed session id
11
+ fails loudly instead of silently reading back an empty list.
9
12
  2. Present each to the operator. Approve, edit, or reject is **always**
10
13
  their call — nothing here self-modifies an agent
11
14
  (architecture §9.4, the memory-poisoning safety boundary).
12
- - **Approve**: `smith lessons approve <lesson-id> --session <id>
15
+ - **Approve**: `bs lessons approve <lesson-id> --session <id>
13
16
  --plan-version <n> --causal-parent <event-id> --actor operator
14
17
  [--note "<why>"]`.
15
18
  - **Edit then approve**: add `--statement`, `--lesson-type` and/or
@@ -34,7 +37,7 @@ none of it is restated here.
34
37
  applied*: surface it, do not retry it. The UI's Edit action runs the
35
38
  same gate since P9-36 — but it has no `--accept-duplicate` control, so
36
39
  a duplicate the operator decides to keep has to come back to the CLI.
37
- - **Reject**: `smith lessons reject <lesson-id> …` — transitions to
40
+ - **Reject**: `bs lessons reject <lesson-id> …` — transitions to
38
41
  `invalidated`, the same status the UI's reject action uses
39
42
  (`ui/server/src/app.ts`'s `/reject` route); there is no separate
40
43
  operator-rejection status in taxonomy.yml's `lesson_status`.
@@ -43,24 +46,35 @@ none of it is restated here.
43
46
  means raising it again with its own provenance — the command refuses
44
47
  `lessons.illegal-transition` rather than rewriting the decision.
45
48
  3. Rebuild the projection so the change is visible:
46
- `smith db apply --db state/smith.db --session <id>`.
47
- 4. Periodically run `smith dream [--since <iso-date>]` to extract new raw
49
+ `bs db apply --db state/smith.db --session <id>`.
50
+ 4. Periodically run `bs dream [--since <iso-date>]` to extract new raw
48
51
  candidates from decision checkpoints (plan sign-offs, waiver decisions,
49
52
  escalations, gate blocks) before this review — it needs the same
50
53
  `--session/--plan-version/--causal-parent` envelope. Candidates it
51
- raises carry `needs_distillation: true`; dispatch **`scribe`**
52
- (`.claude/agents/scribe.md`) to turn a promising raw one into a
54
+ raises carry `needs_distillation: true`; dispatch **`bs-scribe`**
55
+ (`.claude/agents/bs-scribe.md`) to turn a promising raw one into a
53
56
  checkable, principle-level statement before presenting it, rather than
54
57
  showing the operator raw event text.
55
58
  5. Once a batch is approved, recompile the committed file:
56
- `smith lessons compile [--session <id>] [--db state/smith.db]` —
59
+ `bs lessons compile [--session <id>] [--db state/smith.db]` —
57
60
  regenerates `factory/policies/lessons.md` from every `approved` lesson,
58
- sectioned by scope (architecture §9.5). Commit the regenerated file — it
59
- is the file every later dispatch reads (`smith lessons for-dispatch`), so
60
- an approved-but-uncompiled lesson reaches nobody.
61
+ sectioned by scope (architecture §9.5). Entry preservation is
62
+ insertions-only: an entry already there with no store row (hand-
63
+ authored, or raised before the store existed) is preserved verbatim, not
64
+ dropped. If an existing entry can't be parsed, or carries a `lesson_id`
65
+ under a `## ` heading compile doesn't recognize as a scope, compile
66
+ refuses rather than silently losing it — fix the heading or remove the
67
+ entry by hand, then recompile. The rest of the file is **not**
68
+ preserved verbatim: the header above the first scope section is always
69
+ regenerated from a fixed template, and any prose sitting between a
70
+ `## <scope>` heading and its first `### ` entry is dropped — don't
71
+ hand-edit either expecting it to survive a compile.
72
+ Commit the regenerated file — it is the file every later dispatch reads
73
+ (`bs lessons for-dispatch`), so an approved-but-uncompiled lesson
74
+ reaches nobody.
61
75
  6. After a compile — or whenever `lessons.md` has grown enough that nobody
62
76
  reads it — ask which entries still earn their place:
63
- `smith lessons audit <session-id> [--lessons <file>] [--state-dir <dir>]`.
77
+ `bs lessons audit <session-id> [--lessons <file>] [--state-dir <dir>]`.
64
78
  It **recommends only**; every removal is still the operator's call, the
65
79
  same boundary step 2 draws. Read the two evidence classes apart
66
80
  (operator-guide/lessons-and-daemon.md §10b): `retire`/`unreachable` is
@@ -3,10 +3,10 @@
3
3
  Every project leaving the factory ships an MCP surface
4
4
  (`docs/standards/mcp.md`). It is due **late** — at the mandatory
5
5
  `<project> — mcp surface` milestone, once the tools worth exposing are known.
6
- Running this at `smith new` time would produce a manifest declaring nothing,
6
+ Running this at `bs new` time would produce a manifest declaring nothing,
7
7
  which is the rubber stamp the standard exists to prevent.
8
8
 
9
- 1. Run `smith mcp init <project>`. It layers `src/mcp/`, `test/mcp/`, and a
9
+ 1. Run `bs mcp init <project>`. It layers `src/mcp/`, `test/mcp/`, and a
10
10
  root `mcp.manifest.json` onto the already-scaffolded project, merges the
11
11
  `mcp:serve` script and the pinned SDK dependency into its `package.json`,
12
12
  and appends the `mcp surface` milestone to `factory/specs/roadmap.md`.
@@ -22,8 +22,8 @@ which is the rubber stamp the standard exists to prevent.
22
22
  `main`) are not allowed at all (mcp.md MCP-S3). A signed `destructive` tool
23
23
  is permission to act inside the project, never a way around an operator
24
24
  gate.
25
- 4. Run `smith mcp check <project>` before closing anything under that
25
+ 4. Run `bs mcp check <project>` before closing anything under that
26
26
  milestone. Exit 0 means green; exit 1 prints one `{ rule, path, message }`
27
- per violation. `smith epic close` runs the same check and refuses while it
27
+ per violation. `bs epic close` runs the same check and refuses while it
28
28
  is red — the only way past it is `--override-rationale`, which is the
29
29
  operator's call and their name in the event log, never yours.