@juzser/blacksmith 0.3.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 (141) hide show
  1. package/.claude/agents/{auditor.md → bs-auditor.md} +8 -5
  2. package/.claude/agents/{coder.md → bs-coder.md} +8 -7
  3. package/.claude/agents/{grader.md → bs-grader.md} +16 -9
  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} +16 -5
  8. package/.claude/agents/{scribe.md → bs-scribe.md} +11 -2
  9. package/.claude/agents/{security-reviewer.md → bs-security-reviewer.md} +9 -6
  10. package/.claude/agents/{spec-reviewer.md → bs-spec-reviewer.md} +18 -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} +7 -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 +45 -31
  17. package/.claude/skills/bs/dispatch.md +133 -47
  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 +57 -16
  22. package/.claude/skills/bs/report.md +11 -11
  23. package/.claude/skills/bs/run.md +190 -52
  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 +143 -50
  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 +70 -13
  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 +245 -20
  35. package/factory/orchestrator/dist/autonomy.js +6 -2
  36. package/factory/orchestrator/dist/budgetAlarm.js +132 -37
  37. package/factory/orchestrator/dist/budgets.js +302 -84
  38. package/factory/orchestrator/dist/claims.js +9 -19
  39. package/factory/orchestrator/dist/cli.js +1087 -116
  40. package/factory/orchestrator/dist/cliName.js +27 -0
  41. package/factory/orchestrator/dist/crossFinding.js +20 -0
  42. package/factory/orchestrator/dist/crosscheck.js +136 -8
  43. package/factory/orchestrator/dist/daemon.js +352 -67
  44. package/factory/orchestrator/dist/db/projector.js +327 -54
  45. package/factory/orchestrator/dist/db/queries.js +1831 -94
  46. package/factory/orchestrator/dist/db/schema.js +57 -4
  47. package/factory/orchestrator/dist/declaredArtifactLine.js +18 -0
  48. package/factory/orchestrator/dist/delegation.js +86 -19
  49. package/factory/orchestrator/dist/diffstat.js +15 -8
  50. package/factory/orchestrator/dist/dispatchLint.js +108 -0
  51. package/factory/orchestrator/dist/effort.js +14 -2
  52. package/factory/orchestrator/dist/effortTiers.js +13 -0
  53. package/factory/orchestrator/dist/env.js +26 -0
  54. package/factory/orchestrator/dist/epic.js +494 -42
  55. package/factory/orchestrator/dist/epicBudget.js +93 -0
  56. package/factory/orchestrator/dist/epicWidth.js +22 -5
  57. package/factory/orchestrator/dist/errorIssues.js +182 -11
  58. package/factory/orchestrator/dist/escalation.js +2 -2
  59. package/factory/orchestrator/dist/events.js +164 -14
  60. package/factory/orchestrator/dist/feedback.js +169 -0
  61. package/factory/orchestrator/dist/findingContext.js +26 -0
  62. package/factory/orchestrator/dist/findings.js +108 -14
  63. package/factory/orchestrator/dist/gate.js +120 -10
  64. package/factory/orchestrator/dist/gh.js +19 -2
  65. package/factory/orchestrator/dist/git.js +20 -2
  66. package/factory/orchestrator/dist/goalCheck.js +2 -2
  67. package/factory/orchestrator/dist/harness.js +25 -17
  68. package/factory/orchestrator/dist/hookDecision.js +411 -39
  69. package/factory/orchestrator/dist/integration.js +20 -8
  70. package/factory/orchestrator/dist/issueReporter.js +81 -16
  71. package/factory/orchestrator/dist/judgePreflight.js +25 -4
  72. package/factory/orchestrator/dist/judgeRoles.js +47 -0
  73. package/factory/orchestrator/dist/judgeStopHook.js +239 -0
  74. package/factory/orchestrator/dist/judges.js +419 -21
  75. package/factory/orchestrator/dist/lessons.js +190 -11
  76. package/factory/orchestrator/dist/logCache.js +291 -0
  77. package/factory/orchestrator/dist/mcp.js +6 -6
  78. package/factory/orchestrator/dist/paths.js +20 -7
  79. package/factory/orchestrator/dist/plan.js +399 -33
  80. package/factory/orchestrator/dist/planQuorum.js +5 -2
  81. package/factory/orchestrator/dist/policy.js +1275 -30
  82. package/factory/orchestrator/dist/projectName.js +25 -0
  83. package/factory/orchestrator/dist/projects.js +12 -1
  84. package/factory/orchestrator/dist/promptCapture.js +306 -0
  85. package/factory/orchestrator/dist/promptHook.js +33 -0
  86. package/factory/orchestrator/dist/promptImport.js +281 -0
  87. package/factory/orchestrator/dist/prompts.js +13 -0
  88. package/factory/orchestrator/dist/providers/schema-validate.js +2 -2
  89. package/factory/orchestrator/dist/providers/types.js +1 -1
  90. package/factory/orchestrator/dist/queue.js +471 -30
  91. package/factory/orchestrator/dist/quorum.js +18 -5
  92. package/factory/orchestrator/dist/results.js +25 -7
  93. package/factory/orchestrator/dist/roadmap.js +10 -5
  94. package/factory/orchestrator/dist/run-cli.js +15 -10
  95. package/factory/orchestrator/dist/runner.js +2 -2
  96. package/factory/orchestrator/dist/scheduler.js +3 -3
  97. package/factory/orchestrator/dist/schemas.js +56 -8
  98. package/factory/orchestrator/dist/severity.js +8 -4
  99. package/factory/orchestrator/dist/spec.js +246 -12
  100. package/factory/orchestrator/dist/specChange.js +13 -9
  101. package/factory/orchestrator/dist/stack.js +6 -6
  102. package/factory/orchestrator/dist/storeRoot.js +90 -0
  103. package/factory/orchestrator/dist/symbols.js +261 -6
  104. package/factory/orchestrator/dist/taskEvents.js +192 -31
  105. package/factory/orchestrator/dist/taskStatus.js +14 -0
  106. package/factory/orchestrator/dist/taxonomy.js +11 -0
  107. package/factory/orchestrator/dist/testerAudit.js +31 -12
  108. package/factory/orchestrator/dist/testgate.js +20 -16
  109. package/factory/orchestrator/dist/uiuxGate.js +83 -0
  110. package/factory/orchestrator/dist/usage.js +113 -48
  111. package/factory/orchestrator/dist/waivers.js +99 -12
  112. package/factory/orchestrator/dist/waveBudget.js +2 -0
  113. package/factory/orchestrator/dist/waveConcurrency.js +143 -7
  114. package/factory/orchestrator/dist/waveSchedule.js +84 -0
  115. package/factory/orchestrator/dist/workroot.js +1 -1
  116. package/factory/orchestrator/dist/worktree.js +76 -21
  117. package/factory/orchestrator/drizzle/0013_operator_feedback.sql +19 -0
  118. package/factory/orchestrator/drizzle/0014_misty_madripoor.sql +2 -0
  119. package/factory/orchestrator/drizzle/0015_gifted_omega_flight.sql +1 -0
  120. package/factory/orchestrator/drizzle/0016_milestones_project_default.sql +30 -0
  121. package/factory/orchestrator/drizzle/0017_events_raw_cli_session_id.sql +2 -0
  122. package/factory/orchestrator/drizzle/meta/_journal.json +35 -0
  123. package/factory/policies/budgets.yml +123 -33
  124. package/factory/policies/crosscheck.yml +51 -13
  125. package/factory/policies/delegation.yml +3 -3
  126. package/factory/policies/effort.yml +18 -10
  127. package/factory/policies/guardrails.yml +8 -6
  128. package/factory/policies/harness.yml +5 -5
  129. package/factory/policies/lessons.md +53 -2
  130. package/factory/policies/scheduler.yml +4 -4
  131. package/factory/policies/sensitive-paths.yml +1 -1
  132. package/factory/policies/severity.yml +13 -1
  133. package/factory/policies/stack.yml +6 -6
  134. package/factory/policies/taxonomy.yml +9 -3
  135. package/factory/policies/worktree.yml +15 -9
  136. package/factory/specs/roadmap.md +27 -3
  137. package/factory/specs/schema/event.schema.json +8 -1
  138. package/factory/specs/schema/result.schema.json +21 -8
  139. package/factory/specs/schema/task-spec.schema.json +12 -0
  140. package/package.json +16 -12
  141. package/.claude/agents/uiux.md +0 -133
@@ -10,8 +10,9 @@ bind every dispatch below: the project directory is an answer you ask for
10
10
  rather than a path anything here knows, every write carries
11
11
  `--session <id> --plan-version <n> --causal-parent <event-id>`, every
12
12
  dispatch carries the compiled lessons block and that task's open-findings
13
- block, and fetched text is wrapped before it is quoted. None of it is
14
- restated here; a second copy is a copy that drifts.
13
+ block, every `dispatch_decision` carries a one-line plain-words `reason`
14
+ (dispatch.md's payload contract), and fetched text is wrapped before it is
15
+ quoted. None of it is restated here; a second copy is a copy that drifts.
15
16
 
16
17
  The step numbers are the loop's own. They start at 2 because step 1 — which
17
18
  tasks, and how wide — was answered before you were handed anything, and they
@@ -26,15 +27,17 @@ both:
26
27
  - **Inline**, in the epic session, when the wave is small enough that the
27
28
  epic's window can carry it. Nothing changes: the session id you were
28
29
  handed is the one you write to.
29
- - **In a session of its own** when it is not. Open one with `smith session
30
+ - **In a session of its own** when it is not. Open one with `bs session
30
31
  start <wave-id> --continues <event-id>` and write every dispatch below
31
32
  there. The epic session then carries the admission and the result rather
32
33
  than every turn in between — which is the point, because an epic outlives
33
34
  its waves, and a window spent on this wave's dispatches is a window the
34
- epic does not have for the wave after next (D13).
35
+ epic does not have for the wave after next (D13). Every event you write in
36
+ that session carries `--actor wave-runner` — `.claude/agents/bs-wave-runner.md`'s
37
+ Constraints has the rule and the check it satisfies.
35
38
 
36
39
  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`
40
+ that failed, a gate refusal recorded as an error — or a `bs gate run`
38
41
  that came back `blocked` (its `gate-outcome` event with outcome `blocked`),
39
42
  or a task the wave marks `failed` (its `task-added` event with payload
40
43
  `task_status: failed`), the wave runs the reporting verb once, per
@@ -46,9 +49,9 @@ not cosmetic — it is the edge an audit walks:
46
49
 
47
50
  - **The epic ran you** and you opened a wave session yourself: continue from
48
51
  the epic event that admitted this wave.
49
- - **You were dispatched as a `wave-runner`**: continue from the
52
+ - **You were dispatched as a `bs-wave-runner`**: continue from the
50
53
  `dispatch_decision` that dispatched you, the event id handed to you with
51
- the wave. `smith delegation check` resolves your session by matching a
54
+ the wave. `bs delegation check` resolves your session by matching a
52
55
  `session-start`'s `causal_parent` against that dispatch, and it is the
53
56
  only thing that proves the dispatches you are about to write are your
54
57
  own log's rather than an agent talking about itself. Continue from
@@ -57,12 +60,12 @@ not cosmetic — it is the edge an audit walks:
57
60
  for the same reason: a grant is earned by owning the log first.
58
61
  - **You are taking over a wave whose runner died or capped**: continue from
59
62
  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
63
+ log. One session has one author, and `bs delegation check` reads exactly
61
64
  that: dispatches appended to a dead delegate's log by whoever picked the
62
65
  wave up are reported as violations, correctly, because from the log's side
63
66
  they cannot be told apart from an agent narrating its own work. Open your
64
67
  own instead:
65
- `smith session start <wave-id>-takeover --continues <dead-session>#<n>`.
68
+ `bs session start <wave-id>-takeover --continues <dead-session>#<n>`.
66
69
  That keeps the chain whole — the epic's folded reads still see every
67
70
  dispatch on both sides of the handover — and it makes the takeover a fact
68
71
  in the log rather than something a reader has to infer from two agents and
@@ -114,18 +117,22 @@ the difference between a factory and a queue with extra steps:
114
117
  keep the wave moving.
115
118
 
116
119
  The single place the wave rejoins is step 10's merge queue, which is
117
- serial on purpose — `smith queue run` rebases one task at a time onto
118
- `smith/<epic>/integration` and runs the cumulative tests between. Merging
120
+ serial on purpose — `bs queue run` rebases one task at a time onto
121
+ `bs/<epic>/integration` and runs the cumulative tests between. Merging
119
122
  is the one thing that cannot be done in parallel, and it is already the
120
123
  one thing this playbook never asks you to.
121
124
 
122
125
  ## The steps
123
126
 
124
- 2. Per admitted task, and for all of them together: `smith worktree create
127
+ 2. Per admitted task, and for all of them together: `bs worktree create
125
128
  <project-dir> <epic> <task-id>`.
126
- 3. Pre-code, if the task needs it: dispatch `researcher` for an unknown, or
127
- `uiux` (`.claude/agents/uiux.md`) for any UI-affecting acceptance
128
- criterion — before the coder starts, not after.
129
+ 3. Pre-code, if the task needs it: dispatch `bs-researcher` for an unknown, or
130
+ `bs-uiux` (`.claude/agents/bs-uiux.md`) for any UI-affecting acceptance
131
+ criterion — before the coder starts, not after. Bracket the uiux spec
132
+ turn with `bs judge dispatch --role uiux --kind spec` / `bs judge report
133
+ --role uiux --kind spec`, the same way every judge turn in steps 5–7 is
134
+ bracketed — the gate's uiux stage (step 7) reads this turn back and
135
+ blocks a flagged task that never closed it.
129
136
  - `profile.preCodeResearch` gates the researcher: `when-needed` is the
130
137
  line above, `never` (`small`) means the coder reading the repo *is* the
131
138
  brief. An epic that genuinely cannot start without a research brief is
@@ -134,44 +141,108 @@ one thing this playbook never asks you to.
134
141
  every tier: no tier ships a UI change without a spec to review it
135
142
  against. It is listed as a knob so the answer is asked for rather than
136
143
  assumed, not because a tier can turn it off.
137
- - A returned brief goes through `smith research check --brief <path>`
144
+ - A returned brief goes through `bs research check --brief <path>`
138
145
  before it is attached to a task spec ("Fetched and quoted text goes into
139
146
  a prompt fenced" above). Exit 1 sends it back; on exit 0, carry
140
147
  `recommendation.provenance` into the coder's prompt so the task knows
141
148
  whether its research rests on this repo or on a fetched page. Any raw
142
- fetched text you quote alongside it is wrapped with `smith prompt wrap`.
143
- 4. Dispatch **`coder`** (`.claude/agents/coder.md`) in that worktree.
144
- Token/diff caps (`budgets.yml`: 150k tokens, 400 diff lines) and YAGNI
149
+ fetched text you quote alongside it is wrapped with `bs prompt wrap`.
150
+ 4. Dispatch **`bs-coder`** (`.claude/agents/bs-coder.md`) in that worktree.
151
+ Token/diff caps (`budgets.yml`, sized per effort tier) and YAGNI
145
152
  are the coder's own constraints — don't restate them here, the template
146
153
  does. What the template does not read is the schema, so when the spec
147
154
  carries `keeps_exports`, restate the promise in the dispatch in one line:
148
155
  the file list, and "keep every existing export's name and declaration;
149
156
  adding is fine".
150
- 5. Dispatch **`tester`** (`.claude/agents/tester.md`) for missing unit
157
+
158
+ Once every coder of the wave has been dispatched — this fan-out has gone
159
+ out for every admitted task — audit the wave's own parallelism while it
160
+ can still be corrected: `bs wave audit --session <id> --epic <epic>`.
161
+ `wave check` is the step-1 gate that certified, before any dispatch, that
162
+ these tasks could run together; `wave audit` reads the log back after
163
+ dispatch and says whether they did.
164
+ It is a read, so it costs nothing against "No lockstep barrier" above and
165
+ waits for no task. Read the `verdict` field (`WAVE_VERDICTS`,
166
+ `waveConcurrency.ts`), not only the exit code — `partial` also exits 0:
167
+ - `serialized` or `partial` — the wave is not running the width it was
168
+ admitted at, and steps 5–7 have not gone out yet, so it can still be
169
+ fixed: dispatch every remaining phase of this wave as one message of
170
+ parallel calls, per "One message, many dispatches" above, and record
171
+ the verdict as an operator-visible event — `error-logged` with error
172
+ `contract.constraint-decay` (`taxonomy.yml`): the wave-runner stopped
173
+ honoring that section's standing constraint.
174
+ - `unobserved` — the log holds no dispatch for any task this wave
175
+ admitted. That is a defect in the log, not in how the wave ran; record
176
+ it the same way, as `error-logged` with error `execution.env-failure`.
177
+ - `unlinked` — the wave's work merged, or was dispatched, off the epic's
178
+ own lineage (a wave-runner session started without `--continues`).
179
+ Real work, not a defect in dispatch, but its width is unmeasured —
180
+ record it as `error-logged` with error `contract.constraint-decay` so
181
+ the session gets linked under `--continues` next time.
182
+ - `parallel` or `single` — nothing further; the wave ran as wide as it
183
+ was admitted.
184
+ 5. Dispatch **`bs-tester`** (`.claude/agents/bs-tester.md`) for missing unit
151
185
  coverage and epic-level e2e/screenshots.
186
+ - The moment it returns, project its result: `bs results record --task
187
+ <task-id> --result <tester-result.json> --agent tester --provider <name>
188
+ --model-tier <tier> --worktree <dir> --session ... --plan-version N
189
+ --causal-parent ...`. `--agent` stamps the envelope, token_usage
190
+ included, as `gate run` does; `--worktree` is the coder's worktree the
191
+ tester shares, and records the HEAD that proves the screenshots fresh
192
+ (D2) — step 7's uiux stage blocks `screenshots-stale` when this HEAD
193
+ doesn't match the gate's. The tester has no tests of its own to run, so
194
+ it never reaches `gate run` itself — without this, its screenshots sit
195
+ in the result file and never reach the task page.
152
196
  - Then the **uiux visual pass**, but only when all three hold: the task
153
197
  is UI-affecting, the tester actually returned screenshot artifacts, and
154
- step 3 wrote a uiux spec for this task (agent-interviews.md N-6). Miss
155
- any one and skip it — say so; a "visual pass" over screenshots that do
156
- not exist is worse than no pass. Dispatch it with the spec path and the
198
+ step 3 wrote a uiux spec for this task (agent-interviews.md N-6). For a
199
+ UI-affecting task, miss any one and stop, do not skip — step 7's gate
200
+ blocks a flagged task with no fresh, reviewed screenshot set, so a
201
+ missing condition here is not a quiet pass-through to the gate, it is a
202
+ reason to fix the condition first. Dispatch it with the spec path and the
157
203
  image paths and **nothing else**: no diff, no components, no test code.
158
204
  The pass is a judgment about the rendered screen, and a uiux session
159
205
  that reads the diff ends up reviewing intent, which the reviewer
160
206
  already covers.
161
- 6. Dispatch **`grader`** (`.claude/agents/grader.md`) — bounded rubric
207
+ Bracket it with `bs judge dispatch --role uiux --kind visual` / `bs
208
+ judge report --role uiux --kind visual`: a dispatch with no `--kind` is
209
+ refused (`judges.kind-required`), because a kindless turn is one the gate's
210
+ uiux stage never reads.
211
+ - **Blast radius.** When the task's claims touch a shared primitive (a
212
+ design-system/kit component or a global stylesheet), a screenshot of
213
+ the task's own screens is not the full surface: the tester re-shoots
214
+ and the uiux visual pass reviews every screen that renders that
215
+ primitive, not only the task's screens. The tester determines that
216
+ list — from the screenshot suite already on disk or from the
217
+ primitive's importers — and records it alongside the screenshots it
218
+ captures. The "max 4 per feature" screenshot cap (bs-tester.md) still applies per
219
+ screen; it is the screen count that grows, not the shots on each one.
220
+ 6. Dispatch **`bs-grader`** (`.claude/agents/bs-grader.md`) — bounded rubric
162
221
  loop, `profile.graderRounds` rounds (2 at `huge`, 1 below; never more
163
222
  than 2, agent-constraints.md "grader (v3)"), before any gate
164
223
  runs; it never decides pass/fail itself. At one round the grader scores
165
224
  once and a failed criterion is the coder's bounce, not a re-grade. Its result file is an **input to
166
225
  step 7**, not a note to the operator: pass it as `--grader` or the rubric
167
- gates nothing (D-34).
226
+ gates nothing (D-34). A round-2 `fail` re-scopes via dispatch.md's "Round
227
+ counting and escalation", not a round 3 on this id.
168
228
  7. Run the gate pipeline:
169
- `smith gate run <task-id> --worktree <dir> --checks checks.json --result
229
+ `bs gate run <task-id> --worktree <dir> --checks checks.json --result
170
230
  result.json --grader state/results/<task-id>.grader-r<round>.json
171
- --findings findings.json --session ... --plan-version N
231
+ --findings findings.json --plan <plan.json> --uiux-visual
232
+ state/results/<task-id>.uiux-visual.json --session ... --plan-version N
172
233
  --causal-parent ...` (schema check → grader verdict → tests → coverage
173
234
  evidence → findings intake → severity decision,
174
235
  `docs/guide/operator-guide/queue-and-gate.md` §5).
236
+ - `--plan` is what lets the gate's uiux stage read the task's
237
+ `ui_affecting` flag; without it the stage records "unverifiable" and
238
+ does not block. `--uiux-visual` hands over the visual pass's result
239
+ file when step 5 ran one — omit it for a task that is not UI-affecting.
240
+ - `checks.json`'s unit check must be the project's full test command,
241
+ never a hand-picked list of files — `gate run` runs each check command
242
+ literally, with no narrowing of its own. Narrowing by changed files is
243
+ `bs queue run --select-test-cmd` (`factory/orchestrator/src/testSelect.ts`),
244
+ a queue-tier flag `--batch` refuses; it does not reach `checks.json`. A
245
+ scoped-down unit check is how red tests have merged before.
175
246
  - `--grader` takes step 6's file, latest round. A criterion that came back
176
247
  `fail` or `partial` blocks before any check command runs — the outcome is
177
248
  `blocked` with `reason: "grader-fail"`, and the payload's
@@ -182,11 +253,11 @@ one thing this playbook never asks you to.
182
253
  add (D-40). `blocked` with `reason: "coverage-evidence"` means the run
183
254
  produced no per-file number for a file this task's claims name; fix the
184
255
  reporter or the include glob, not the code. Ask the same question
185
- outside a gate run with `smith coverage check <worktree-dir> --plan
256
+ outside a gate run with `bs coverage check <worktree-dir> --plan
186
257
  <plan.json> --task <task-id>` (§5c).
187
- - Findings come from dispatching **`reviewer`**
188
- (`.claude/agents/reviewer.md`, fresh context, read-only) then
189
- **`verifier`** (`.claude/agents/verifier.md`, adversarial refute
258
+ - Findings come from dispatching **`bs-reviewer`**
259
+ (`.claude/agents/bs-reviewer.md`, fresh context, read-only) then
260
+ **`bs-verifier`** (`.claude/agents/bs-verifier.md`, adversarial refute
190
261
  mandate) — never skip the adversarial stage on an S1/S2 finding
191
262
  (`crosscheck.yml` `asymmetric_roles`). Which findings that covers is
192
263
  `profile.verifierSeverities`, plus a
@@ -196,25 +267,26 @@ one thing this playbook never asks you to.
196
267
  thing a cheaper tier drops; `S1-stop-the-line` is in every tier's list
197
268
  and no tier may remove it. A finding outside the tier's list still goes
198
269
  to the coder — it is verified by the coder's fix, not waived.
199
- - Dispatch **`security-reviewer`** only when its conditional triggers
270
+ - Dispatch **`bs-security-reviewer`** only when its conditional triggers
200
271
  fire — never per-task by default. Ask, do not recall:
201
- `smith security triggers --task <spec.json>` and dispatch iff
272
+ `bs security triggers --task <spec.json>` and dispatch iff
202
273
  `dispatchSecurityReviewer` is true ("Dispatching the security-reviewer"
203
274
  above).
204
275
  - Every judge dispatched in steps 5–7 — uiux visual pass, grader,
205
276
  reviewer, verifier, security-reviewer — is bracketed by
206
- `smith worktree fingerprint` / `smith worktree verify`
277
+ `bs worktree fingerprint` / `bs worktree verify`
207
278
  ("Fingerprint the worktree around every judge" above). Exit 1 discards
208
279
  that judge's result; it does not become a finding against the coder.
209
- - Each of those is also bracketed by `smith judge dispatch` /
210
- `smith judge report` ("Declare each judge's artifact before you dispatch
211
- it" above). `smith judge outstanding --task <task-id>` exits 1 while any
280
+ - Each of those is also bracketed by `bs judge dispatch` /
281
+ `bs judge report` ("Declare each judge's artifact before you dispatch
282
+ it" above); paste `judge dispatch`'s `expected_line` inline in the
283
+ judge's prompt, or the `Agent` call is refused. `bs judge outstanding --task <task-id>` exits 1 while any
212
284
  judge still owes its file — re-poke it and report before running the
213
285
  gate, because the gate now refuses to score with a non-empty outstanding
214
286
  set (`reason: judges-outstanding`) rather than reading a silent judge as
215
287
  zero findings.
216
288
  - The gate scores tests it did not write, so once it has run, make the log
217
- say who did: `smith tester check <session-id> --task <task-id>`
289
+ say who did: `bs tester check <session-id> --task <task-id>`
218
290
  (`crosscheck.yml` `role_isolation`, operator-guide/dispatch-audits.md §2d). Exit 1
219
291
  means no `tester` dispatch precedes this task's `testgate-result`, the
220
292
  coder and tester dispatches share one `agent_id`, or the answer is
@@ -223,9 +295,9 @@ one thing this playbook never asks you to.
223
295
  downstream still goes green — step 5 is what prevents that, and this is
224
296
  the log checking that step 5 happened.
225
297
  8. Every dispatch in steps 3–7 carries the compiled lessons block for that
226
- role (`smith lessons for-dispatch <role> --plan … --task …`), every
298
+ role (`bs lessons for-dispatch <role> --plan … --task …`), every
227
299
  worktree dispatch also carries the open-findings block for that task
228
- (`smith findings for-dispatch --plan … --task …`) — both under "Dispatch
300
+ (`bs findings for-dispatch --plan … --task …`) — both under "Dispatch
229
301
  contract" above — and each is a `dispatch_decision` event with a `causal_parent`
230
302
  chaining back to the prompt/decision that caused it (architecture §7) —
231
303
  this is the timeline, not optional bookkeeping.
@@ -236,12 +308,25 @@ one thing this playbook never asks you to.
236
308
  rung, never loop past one. Count the rounds from this task's
237
309
  `dispatch_decision` events, not from the agent's own account of itself
238
310
  — see "Round counting and escalation" above. Then have the log check
239
- you: `smith escalation check <session-id> --task <task-id>`, which
311
+ you: `bs escalation check <session-id> --task <task-id>`, which
240
312
  exits 1 if the rung you just climbed is not evidenced.
241
- 10. Gate outcome `pass`/`pass-with-waivers-pending` → before admitting,
242
- ask what the diff did to everyone outside the claims, and whether it
313
+ 10. Gate outcome `pass`/`pass-with-waivers-pending` → before admitting, audit
314
+ the wave's own parallelism: `bs wave audit --session <id> --epic
315
+ <epic>`. It reads the log back and says whether the tasks admitted
316
+ together actually ran together — `parallel` when every admitted task was
317
+ in flight at once, `partial` when two or more overlapped but never all,
318
+ `serialized` when work is recorded but no two tasks ever overlapped,
319
+ `single` when the wave admitted one task (nothing to be parallel about),
320
+ `unlinked` when the work merged or dispatched but off the epic's own
321
+ lineage (width unmeasured, not zero — usually a wave-runner session
322
+ started without `--continues`), and `unobserved` when the wave was
323
+ admitted and the log shows no work for any of its tasks. Exit 1 on
324
+ `serialized`, exit 2 on `unobserved`, exit 0 on `unlinked` — each
325
+ is a fact for the wave-loop's hand-back, not a reason to hold the merge:
326
+ report the verdict and keep going to admission. Then ask what the diff
327
+ did to everyone outside the claims, and whether it
243
328
  kept what the spec promised:
244
- `smith claims impact <worktree-dir> <spec.json>`. Exit 1 means a
329
+ `bs claims impact <worktree-dir> <spec.json>`. Exit 1 means a
245
330
  `proven` break — this task removed an export a file outside its claims
246
331
  still imports — **or a broken promise**: a file the spec's
247
332
  `keeps_exports` names lost an export or changed one's declaration.
@@ -249,10 +334,14 @@ one thing this playbook never asks you to.
249
334
  entries in the bounce. A `possible` / `signature-changed` entry in
250
335
  `breaks` exits 0 and is a note: the scanner reads text, not types
251
336
  (operator-guide/wave.md §2). Then admit into the
252
- merge queue: `smith queue run <epic> --project <project-dir>
253
- --test-cmd "<cumulative test command>" --tasks tasks.json`. On a
254
- `rebase-conflict` outcome, dispatch **`merger`**
255
- (`.claude/agents/merger.md`) with both diffs + specs, and note three
337
+ merge queue: `bs queue run <epic> --project <project-dir>
338
+ --test-cmd "<cumulative test command>" --tasks tasks.json --plan
339
+ <plan.json> --session <id> --plan-version <n> --causal-parent
340
+ <event-id>` — the same write triple as every other dispatch above; a
341
+ merge that lands with no session to log it into is a fact the
342
+ timeline never gets. On a
343
+ `rebase-conflict` outcome, dispatch **`bs-merger`**
344
+ (`.claude/agents/bs-merger.md`) with both diffs + specs, and note three
256
345
  things the queue's own behaviour forces (agent-interviews.md N-12):
257
346
  - **Into the failing task's existing worktree** — a fresh one would not
258
347
  reproduce the conflict. The result gives you only `taskId` and
@@ -263,7 +352,7 @@ one thing this playbook never asks you to.
263
352
  - The queue already ran `git rebase --abort` (`queue.ts`), so the
264
353
  merger arrives at a clean tree and **replays** the rebase rather than
265
354
  resuming it — and it **never lands the merge**. When it returns
266
- `resolution: "mechanical"`, re-run `smith queue run`; the queue stays
355
+ `resolution: "mechanical"`, re-run `bs queue run`; the queue stays
267
356
  the single place a merge can happen.
268
357
 
269
358
  `resolution: "escalated"` — low confidence, or both sides changed the
@@ -271,7 +360,11 @@ one thing this playbook never asks you to.
271
360
  auto-resolve a semantic conflict silently (`worktree.yml`
272
361
  `conflict_resolution_ladder`).
273
362
 
274
- Once the wave is merged, check the epic's spend — `smith budget alarm
363
+ An `integration-dirty` outcome names a `worktree` holding
364
+ `bs/<epic>/integration` with uncommitted tracked changes (`dirty`):
365
+ commit or stash those there, then re-run `bs queue run`.
366
+
367
+ Once the wave is merged, check the epic's spend — `bs budget alarm
275
368
  <session-id> [--epic <epic>]`. Exit 1 means either the epic crossed
276
369
  `alarm_ratio` (re-plan the remaining work to fit, or ask the operator —
277
370
  extension is always an operator question) **or** the log is too holey to
package/README.md CHANGED
@@ -125,7 +125,7 @@ provider.
125
125
  **The log is the source of truth**
126
126
 
127
127
  Every prompt, dispatch, gate result and error is an append-only event on disk.
128
- The dashboard is a projection of that log, and `smith db rebuild` reconstructs
128
+ The dashboard is a projection of that log, and `bs db rebuild` reconstructs
129
129
  the entire database from the log alone. Nothing the factory did exists only in
130
130
  a chat transcript.
131
131
 
@@ -168,18 +168,27 @@ and two inside Claude Code:
168
168
  /plugin install blacksmith@blacksmith
169
169
  ```
170
170
 
171
- Then `smith init` in the project you want it to work on. That creates
171
+ Two optional plugin modules come from the same marketplace, bs-mod (a live
172
+ HUD of your epic above the prompt) and pr-mod (the repo's open pull requests),
173
+ and [`docs/guide/mods.md`](docs/guide/mods.md) has what each needs:
174
+
175
+ ```
176
+ /plugin install bs-mod@blacksmith
177
+ /plugin install pr-mod@blacksmith
178
+ ```
179
+
180
+ Then `bs init` in the project you want it to work on. That creates
172
181
  `.blacksmith/` beside your code — the event log, your epic plans, a roadmap and
173
- a `stack.yml` to answer — and writes nothing anywhere else; `SMITH_HOME` moves
182
+ a `stack.yml` to answer — and writes nothing anywhere else; `BS_HOME` moves
174
183
  that root if you want one home for several projects. You now have `/bs`.
175
184
 
176
185
  **Both halves are required.** The package
177
186
  ([`@juzser/blacksmith`](https://www.npmjs.com/package/@juzser/blacksmith)) is the
178
- deterministic `smith` CLI; the plugin is `/bs` and the fourteen agent roles it
179
- dispatches. `smith` alone never gives you `/bs`, because Claude Code loads
187
+ deterministic `bs` CLI; the plugin is `/bs` and the fourteen agent roles it
188
+ dispatches. `bs` alone never gives you `/bs`, because Claude Code loads
180
189
  skills from a project's `.claude/`, your `~/.claude/`, or a plugin — never from
181
- `node_modules` — and a `/bs` with no `smith` on PATH can run nothing. Take
182
- `latest`: `0.1.0` predates `smith init` and keeps state inside its own install
190
+ `node_modules` — and a `/bs` with no `bs` on PATH can run nothing. Take
191
+ `latest`: `0.1.0` predates `bs init` and keeps state inside its own install
183
192
  directory, which the next `npm i` replaces.
184
193
 
185
194
  <details>
@@ -197,17 +206,21 @@ bash scripts/check.sh # the gate CI runs; ends in `== PASS ==`
197
206
 
198
207
  Open a Claude Code session in the clone and you have `/bs` already, from the
199
208
  checkout's own `.claude/` — **do not also install the plugin there.** You would
200
- get two of everything (`bs` and `blacksmith:bs`, `auditor` and
201
- `blacksmith:auditor`, once per role), pay the always-on cost twice, and the two
209
+ get two of everything (`bs` and `blacksmith:bs`, `bs-auditor` and
210
+ `blacksmith:bs-auditor`, once per role), pay the always-on cost twice, and the two
202
211
  copies are free to disagree: the plugin's is a pinned checkout of `main`, the
203
212
  project's is whatever branch you have out. `claude plugin disable blacksmith`
204
- settles it.
213
+ settles it. It also turns off the prompt recorder in that clone, which only
214
+ the plugin registers; a clone that wants its prompts recorded keeps the plugin
215
+ enabled and accepts the double listing
216
+ ([INSTALL.md](INSTALL.md#the-install--bs-and-bs)).
205
217
 
206
218
  The dashboard is clone-only because `ui/` is in neither the tarball nor the
207
- plugin, so `smith ui serve` answers `ui.not-built` in an install and means it.
219
+ plugin, so `bs ui serve` answers `ui.not-built` in an install and means it.
208
220
  Enforcement is clone-only because this repo's `.claude/settings.json` deny
209
- rules and its policy hook resolve paths against a checkout; the plugin ships no
210
- `hooks/hooks.json` and loads neither (`Hooks (0)`), since a `/bs` that asked
221
+ rules and its policy hook resolve paths against a checkout; the plugin ships one
222
+ `hooks/hooks.json`, holding only the non-blocking prompt-capture hook, and
223
+ loads neither the deny rules nor the policy guard, since a `/bs` that asked
211
224
  you about every Bash command would be worse than one that asks about none. The
212
225
  rest of that port is scoped in
213
226
  [`docs/specs/plugin-port-scope.md`](docs/specs/plugin-port-scope.md).
@@ -233,9 +246,9 @@ would rather drive it yourself, it is three commands:
233
246
  ```bash
234
247
  $EDITOR factory/policies/stack.yml # your stack answers: language, frontend,
235
248
  # database, deploy target. `none` is fine.
236
- smith stack check # which answers the templates honour,
249
+ bs stack check # which answers the templates honour,
237
250
  # which they only record, which they refuse
238
- smith new my-app --target-dir ~/code/my-app # add --ui for a frontend
251
+ bs new my-app --target-dir ~/code/my-app # add --ui for a frontend
239
252
  ```
240
253
 
241
254
  The last call scaffolds (TS strict, Biome, Vitest, CI), installs, runs the
@@ -278,7 +291,7 @@ it is the ordinary loop: `/bs plan` against that epic, then `/bs run`, and
278
291
  closing the epic marks its findings fixed. There is no audit-specific run
279
292
  path; the command's value is the insight and the ranking.
280
293
 
281
- Underneath it is the `smith audit` family — `open`, `record`, `consolidate`,
294
+ Underneath it is the `bs audit` family — `open`, `record`, `consolidate`,
282
295
  `decide`, `cut`, `resolve`, `close` — and
283
296
  [`docs/specs/audit-command-scope.md`](docs/specs/audit-command-scope.md) is the
284
297
  contract each of them keeps.
@@ -300,10 +313,10 @@ Day to day, from a Claude Code session opened in this repo:
300
313
  | `/bs lessons` | Review pending lesson candidates |
301
314
  | `/bs report` | Render the scribe's progress digest |
302
315
 
303
- Each of those is a playbook, not a script: the deterministic half is a `smith`
304
- command you can run yourself — `smith --help` lists all of them — and the
316
+ Each of those is a playbook, not a script: the deterministic half is a `bs`
317
+ command you can run yourself — `bs --help` lists all of them — and the
305
318
  judgment half is a Claude Code session the playbook dispatches. That is also
306
- the line between the two installs: the `smith` half travels in the package, the
319
+ the line between the two installs: the `bs` half travels in the package, the
307
320
  playbooks are read from a clone.
308
321
 
309
322
  → **[The operator loop](docs/guide/operator-loop.md)** — the six steps, in the
@@ -319,7 +332,7 @@ v1. From there the loop admits a wave whose path claims do not overlap, sends
319
332
  researcher and UI/UX work ahead of code where the epic needs it, runs a coder
320
333
  and a tester in a worktree, grades the result against its own acceptance
321
334
  criteria, then puts it through the gates and a serial merge queue into
322
- `smith/<epic>/integration`. One epic, one integration PR, merged by you.
335
+ `bs/<epic>/integration`. One epic, one integration PR, merged by you.
323
336
 
324
337
  → The pipeline diagram and the reasoning behind each stage:
325
338
  **[architecture §3 — The loop](docs/specs/black-smith-architecture.md#3-the-loop)**.
@@ -339,8 +352,10 @@ runs, plus branch protection — not by trust. Full rules:
339
352
  branch solely through the serial merge queue.
340
353
  - **No autonomous deploy or outbound sends.** Deploys, publishes and message
341
354
  sends each need per-invocation approval.
342
- - **Budgets are declared per role.** 4M tokens per epic with an alarm at 70%;
343
- 150K tokens and 400 diff lines per coder task. Fan-out is bounded by the
355
+ - **Budgets are declared per role and sized per effort tier.** A medium epic
356
+ gets 16M tokens with an alarm at 70%, and a coder task 220K tokens and 700
357
+ diff lines; a small epic gets half the task numbers and 4M, a huge one
358
+ double and 32M. Fan-out is bounded by the
344
359
  claim graph, and `max_in_flight_tasks` is available on top of it, off by
345
360
  default. Which of these *block* versus *report* is spelled out in
346
361
  [`factory/policies/budgets.yml`](factory/policies/budgets.yml) — the task cap
@@ -351,11 +366,14 @@ a public issue.
351
366
 
352
367
  ## The dashboard
353
368
 
354
- Optional, and deliberately small: `/bs ui` (or `smith ui serve`) binds eleven
355
- read-only pages to `127.0.0.1`. They are a projection of the event log and
356
- nothing else — `smith db rebuild` reconstructs them from it, `smith stats`
357
- prints the same facts in a terminal, and nothing you click there dispatches an
358
- agent. The factory runs without ever opening it.
369
+ Optional, and deliberately small: `/bs ui` (or `bs ui serve`) binds eleven
370
+ read-only pages to `127.0.0.1`. They are a projection of the event log —
371
+ `bs db rebuild` reconstructs them from it, `bs stats` prints the same facts in
372
+ a terminal, and nothing you click there dispatches an agent. It also reads
373
+ the local Claude Code session registry, transcripts and prompt history (read-only;
374
+ `--claude-config-dir`, then `$CLAUDE_CONFIG_DIR`, then Claude Code's default),
375
+ served as `GET /api/cli-sessions` to loopback requests only. The factory runs
376
+ without ever opening the dashboard.
359
377
 
360
378
  <table>
361
379
  <tr valign="top">
@@ -386,14 +404,16 @@ shows you. It is part of the clone, not of the package.
386
404
 
387
405
  Phases 1–9 are built and merged: loop runner, worktree engine, gates, state and
388
406
  analytics, dashboard, self-extension, cross-provider judges, hardening. Phase 10
389
- is half in: `smith daemon` watches the factory in the background and its ops
407
+ is half in: `bs daemon` watches the factory in the background and its ops
390
408
  runbook is written; the hosted UI stays deferred. Beside the phases, `/bs audit`
391
409
  is built: an existing project can be read on four axes and one epic cut from
392
410
  what you accept.
393
411
 
394
- The CLI is on npm as `@juzser/blacksmith`, at `0.3.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
412
+ The CLI is on npm as `@juzser/blacksmith`, at `0.5.0`. Two binaries are
413
+ the ones you run: `bs`, and `bs-run` (since `0.2.0`), which runs a rendered
414
+ turn on a `cli` harness such as Codex. `bs-prompt-hook` is the lean hook
415
+ entry that records operator prompts. `smith` and `smith-run` remain as
416
+ deprecated aliases. `0.1.1` was the first version that ran beside
397
417
  you and knew it was a package, rather than out of the clone-shaped install
398
418
  `0.1.0` was. The package is the binaries and what they
399
419
  read. The dashboard, the docs, the test suite and `/bs` itself come from a
@@ -423,8 +443,9 @@ version in
423
443
  | [`docs/specs/audit-command-scope.md`](docs/specs/audit-command-scope.md) | What `/bs audit` promises an audited project, and why |
424
444
  | [`docs/specs/plugin-port-scope.md`](docs/specs/plugin-port-scope.md) | How `/bs` runs without a clone, and what the plugin leaves behind |
425
445
  | [`docs/guide/dashboard.md`](docs/guide/dashboard.md) | The dashboard tour |
446
+ | [`docs/guide/mods.md`](docs/guide/mods.md) | The optional bs-mod HUD and pr-mod |
426
447
  | [`docs/runbooks/providers.md`](docs/runbooks/providers.md) | Setting up the cross-provider judges |
427
- | [`docs/runbooks/ops.md`](docs/runbooks/ops.md) | Running `smith daemon` unattended |
448
+ | [`docs/runbooks/ops.md`](docs/runbooks/ops.md) | Running `bs daemon` unattended |
428
449
  | [`docs/README.md`](docs/README.md) | Everything else, one line each |
429
450
 
430
451
  Agents read [`AGENTS.md`](AGENTS.md) and [`CLAUDE.md`](CLAUDE.md) instead — this
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The one place a role becomes a Claude Code subagent name, a template file
3
+ * name, and back. The role (`coder`) is data everywhere else: events,
4
+ * policies, ledgers, env names. Only the subagent carries the `bs-` prefix,
5
+ * so that it cannot collide with another plugin's or a user's `coder`.
6
+ */
7
+ export const AGENT_PREFIX = 'bs-';
8
+ /** `coder` -> `bs-coder`: what `subagent_type` and a template's `name:` carry. */
9
+ export function agentNameFor(role) {
10
+ return `${AGENT_PREFIX}${role}`;
11
+ }
12
+ /** `coder` -> `bs-coder.md`: the template file under `.claude/agents/`. */
13
+ export function templateFileFor(role) {
14
+ return `${agentNameFor(role)}.md`;
15
+ }
16
+ /** `bs-coder.md` -> `coder`; null for a file that is not a `bs-<role>.md` template. */
17
+ export function roleOfTemplateFile(file) {
18
+ if (!file.endsWith('.md'))
19
+ return null;
20
+ return roleOfAgentType(file.slice(0, -'.md'.length), { bareAccepted: false });
21
+ }
22
+ /**
23
+ * What Claude Code puts in a hook's `agent_type` or a dispatch's
24
+ * `subagent_type` -> the role. Strips an optional `<namespace>:` and then the
25
+ * `bs-` prefix. For one transition the bare pre-prefix name (`reviewer`,
26
+ * `blacksmith:reviewer`) is accepted too, because a newer CLI can run with an
27
+ * older plugin still installed. Returns a *candidate* role, not a verified one:
28
+ * any other non-empty name (`general-purpose`) comes back as itself, so the
29
+ * caller must check the result against its own role set. Null only for an
30
+ * empty name, a bare `bs-` or `<ns>:`, or (with `bareAccepted: false`) a name
31
+ * without the prefix.
32
+ */
33
+ export function roleOfAgentType(agentType, options = {}) {
34
+ const colon = agentType.lastIndexOf(':');
35
+ const name = colon === -1 ? agentType : agentType.slice(colon + 1);
36
+ if (name.startsWith(AGENT_PREFIX)) {
37
+ const role = name.slice(AGENT_PREFIX.length);
38
+ return role.length > 0 ? role : null;
39
+ }
40
+ if (options.bareAccepted === false)
41
+ return null;
42
+ return name.length > 0 ? name : null;
43
+ }
@@ -289,15 +289,22 @@ export function foldAgents(events) {
289
289
  if (record.event_type === ERROR_EVENT_TYPE) {
290
290
  const payload = record.payload;
291
291
  // `task_ref` is this event's own spelling; eventTaskId covers the two
292
- // the rest of the log uses (D-245).
292
+ // the rest of the log uses (D-245). `agent_role` is the dispatch's own
293
+ // key and this event's usual spelling, but a hand-written one may only
294
+ // carry `agent` (the Result file's spelling), so either key alone still
295
+ // finds the role -- `agent_role` tried first because it was already
296
+ // this fold's only key before `agent` was added as a fallback, same
297
+ // reason task-result-recorded above tries `agent` first for its own
298
+ // history; not the same order, because each fold keeps its own.
293
299
  const taskId = eventTaskId(record) ?? payload.task_ref;
300
+ const role = payload.agent_role ?? payload.agent ?? null;
294
301
  if (taskId) {
295
- closeOpen(open, taskId, payload.agent_role ?? null, event_id, record.ts, 'error');
302
+ closeOpen(open, taskId, role, event_id, record.ts, 'error');
296
303
  }
297
- else if (payload.agent_role) {
304
+ else if (role) {
298
305
  // Only with a role: a session-level error names nobody, and every
299
306
  // epic-level agent stays where it was.
300
- closeOpenEpicLevel(openEpicLevel, record.session_id, payload.agent_role, payload.epic_id, event_id, record.ts, 'error');
307
+ closeOpenEpicLevel(openEpicLevel, record.session_id, role, payload.epic_id, event_id, record.ts, 'error');
301
308
  }
302
309
  continue;
303
310
  }