@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
@@ -1,8 +1,8 @@
1
1
  # `/bs new <project> [--ui]` — scaffold a target project
2
2
 
3
- 1. Ask where the project should live, then run `smith new <project> [--ui]
3
+ 1. Ask where the project should live, then run `bs new <project> [--ui]
4
4
  [--target-dir <dir>]`. Without `--target-dir` it lands beside the clone, in
5
- `<repo-parent>/<project>` — or, when `smith` is an installed package rather
5
+ `<repo-parent>/<project>` — or, when `bs` is an installed package rather
6
6
  than a clone, in the directory you run it from, never inside the package.
7
7
  Either way that is a default rather than a requirement —
8
8
  the answer becomes the `<project-dir>` every later command takes, so take
@@ -15,7 +15,7 @@
15
15
  bootstrap milestone in `factory/specs/roadmap.md` — all in one call.
16
16
  An answer the templates cannot build (`frontend: react`) makes it **refuse
17
17
  before creating anything**, rather than quietly handing over the frontend
18
- they do ship. `smith stack check` says in advance which answers are
18
+ they do ship. `bs stack check` says in advance which answers are
19
19
  honoured, merely recorded, or refused.
20
20
  2. The gate run is `pnpm install` then `lint`, `typecheck`, `test:coverage`,
21
21
  `build` — `ci.yml`'s order, so the lockfile lands in the first commit and no
@@ -11,8 +11,8 @@ one — and it scales steps 3–4 below plus most of `/bs run`. Ask, do not
11
11
  recall:
12
12
 
13
13
  ```bash
14
- smith effort show --effort <small|medium|huge> # no plan file exists yet
15
- smith effort show --plan factory/specs/active/<epic>/plan-vN.json
14
+ bs effort show --effort <small|medium|huge> # no plan file exists yet
15
+ bs effort show --plan factory/specs/active/<epic>/plan-vN.json
16
16
  ```
17
17
 
18
18
  `profile` is what this epic buys, and every step below that a tier touches
@@ -34,13 +34,13 @@ there means "not looked at", not "looked at and clean".
34
34
 
35
35
  1. **Static analysis for claims first.** Before any decomposition, get the
36
36
  target repo's import/dependency graph so claims are computed, not
37
- guessed (architecture §5) — dispatch `researcher`
38
- (`.claude/agents/researcher.md`) for an unfamiliar repo, or do it
37
+ guessed (architecture §5) — dispatch `bs-researcher`
38
+ (`.claude/agents/bs-researcher.md`) for an unfamiliar repo, or do it
39
39
  directly with Grep/Read for a small one.
40
- 2. Dispatch a **`planner`** session (`.claude/agents/planner.md`, frontier
40
+ 2. Dispatch a **`bs-planner`** session (`.claude/agents/bs-planner.md`, frontier
41
41
  tier) with the goal + the claims analysis. It drafts the epic spec and
42
42
  task specs (`factory/specs/schema/task-spec.schema.json` shape).
43
- 3. Dispatch a **`spec-reviewer`** session (`.claude/agents/spec-reviewer.md`)
43
+ 3. Dispatch a **`bs-spec-reviewer`** session (`.claude/agents/bs-spec-reviewer.md`)
44
44
  — a *different model* than the planner's, per its own frontmatter — to
45
45
  hunt spec gaps/ambiguities/missing-nonfunctional clauses
46
46
  (`docs/standards/agent-constraints.md` "planner"; `severity.yml`: most
@@ -49,7 +49,7 @@ there means "not looked at", not "looked at and clean".
49
49
  back tagged to one of them:
50
50
 
51
51
  1. Claims and dependency edges, cross-checked against
52
- `smith claims impact`.
52
+ `bs claims impact`.
53
53
  2. TDD-ability -- a named failing test per task, before its code.
54
54
  3. Nonfunctional and security clauses.
55
55
  4. Budget and diff-cap fit.
@@ -57,8 +57,13 @@ there means "not looked at", not "looked at and clean".
57
57
  6. From round 2 on, only the previous round's findings plus any task
58
58
  that changed since then.
59
59
 
60
- When `size.small` reads true on the draft (`smith wave schedule` or
61
- `smith plan ingest`'s `parallelism.size`, run against the draft plan
60
+ The prompt also carries, inline on its own line,
61
+ `Declared artifact: /abs/path/to/state/results/<epic-id>.spec-review.json`
62
+ — the absolute form of the path `bs-spec-reviewer.md` names. The PreToolUse
63
+ guard refuses a judge dispatch without it.
64
+
65
+ When `size.small` reads true on the draft (`bs wave schedule` or
66
+ `bs plan ingest`'s `parallelism.size`, run against the draft plan
62
67
  file), recommend effort tier `small` -- single-pass spec review -- to
63
68
  the operator unless a security trigger floors it first, and say why in
64
69
  the same message: a plan that cannot spread past a few tasks and a
@@ -74,9 +79,10 @@ there means "not looked at", not "looked at and clean".
74
79
  confidence) for you:
75
80
 
76
81
  ```bash
77
- smith plan quorum --plan <the draft's plan.json> --plan-version <n> \
82
+ bs plan quorum --plan <the draft's plan.json> --plan-version <n> \
78
83
  --session <session-id> --causal-parent <event-id> \
79
84
  --confidence <your own 0–1 confidence in this plan> \
85
+ --project <project-dir> \
80
86
  --out <scratch>/quorum-v<n>.json
81
87
  ```
82
88
 
@@ -86,7 +92,10 @@ there means "not looked at", not "looked at and clean".
86
92
  exist. (`--epic <epic>` instead of `--plan` critiques a version already
87
93
  filed.) The draft's own `epic_id`/`version` must be the ones the command
88
94
  names, or it refuses (`plan.identity-mismatch`) rather than record one
89
- plan's identity with another plan's triggers. `--out` keeps the outcome
95
+ plan's identity with another plan's triggers. `--project <project-dir>`
96
+ has no worktree to derive from at this step — no task has one yet — so
97
+ name it directly, or a `<project-dir>/.blacksmith/crosscheck.yml` overlay
98
+ never reaches the providers this quorum weighs. `--out` keeps the outcome
90
99
  — three rationales of several kB each — where step 5 can hand it to the
91
100
  operator whole instead of quoting a terminal.
92
101
 
@@ -109,10 +118,10 @@ there means "not looked at", not "looked at and clean".
109
118
  "planner: autonomy — spec sign-off per epic").
110
119
  6. On approval, write task specs to
111
120
  `factory/specs/active/<epic>/plan-v1.json` and validate:
112
- `smith plan validate factory/specs/active/<epic>/plan-v1.json` must
121
+ `bs plan validate factory/specs/active/<epic>/plan-v1.json` must
113
122
  report `{"valid":true}` before anything else touches this plan. Write the
114
123
  tier onto the plan file as a top-level `"effort": "<tier>"` — that is
115
- where `/bs run` reads it, and `smith plan amend` carries it into
124
+ where `/bs run` reads it, and `bs plan amend` carries it into
116
125
  v(n+1) so a re-plan is not a fresh choice. Omit the field to take the
117
126
  policy default; a value that is not `small`/`medium`/`huge` fails
118
127
  validation rather than degrading to it.
@@ -121,7 +130,7 @@ there means "not looked at", not "looked at and clean".
121
130
  against it:
122
131
 
123
132
  ```bash
124
- smith wave schedule factory/specs/active/<epic>/plan-v1.json \
133
+ bs wave schedule factory/specs/active/<epic>/plan-v1.json \
125
134
  --session <session-id> --repo <project-dir>
126
135
  ```
127
136
 
@@ -154,7 +163,7 @@ there means "not looked at", not "looked at and clean".
154
163
  Beside the schedule, the read that names what it only counts:
155
164
 
156
165
  ```bash
157
- smith claims impact --plan factory/specs/active/<epic>/plan-v1.json \
166
+ bs claims impact --plan factory/specs/active/<epic>/plan-v1.json \
158
167
  <task-id>... --repo <project-dir>
159
168
  ```
160
169
 
@@ -162,8 +171,8 @@ there means "not looked at", not "looked at and clean".
162
171
  at; `promised` holds the ones a producer's `keeps_exports` already
163
172
  answers, which the schedule admits and the diff is later checked against.
164
173
  7. Log the sign-off itself as a decision checkpoint (this is exactly what
165
- `smith dream`'s "plan sign-off" extraction looks for, `lessons.ts`):
166
- `smith event append '{"session_id":"...","actor":"operator",
174
+ `bs dream`'s "plan sign-off" extraction looks for, `lessons.ts`):
175
+ `bs event append '{"session_id":"...","actor":"operator",
167
176
  "event_type":"plan-version-created","plan_version":1,
168
177
  "causal_parent":"...","payload":{"epic_id":"<epic>","version":1,
169
178
  "note":"<operator's own words>"}}'`.
@@ -172,7 +181,7 @@ there means "not looked at", not "looked at and clean".
172
181
  the sign-off event:
173
182
 
174
183
  ```bash
175
- smith plan ingest factory/specs/active/<epic>/plan-v1.json \
184
+ bs plan ingest factory/specs/active/<epic>/plan-v1.json --project <project-dir> \
176
185
  --session <session-id> --plan-version 1 --causal-parent <sign-off event id>
177
186
  ```
178
187
 
@@ -8,8 +8,8 @@ duties, v3.2)"), plus running the scheduler first — a report that omits
8
8
  pending proposals lets them sit invisible instead of surfacing them.
9
9
 
10
10
  1. **Ask what the factory is answerable for, then run the scheduler.**
11
- `smith projects list` reads the roadmap's `- project:` bullets — the
12
- register `smith new` writes to — and prints the factory itself plus every
11
+ `bs projects list` reads the roadmap's `- project:` bullets — the
12
+ register `bs new` writes to — and prints the factory itself plus every
13
13
  project it built that has a checkout, ending in the `--project` line to
14
14
  pass on. Do not assemble that list from memory: the maintenance pass
15
15
  reads one lockfile per flag and reports one proposal per repo, so a repo
@@ -17,9 +17,9 @@ pending proposals lets them sit invisible instead of surfacing them.
17
17
  mentioned. (The daemon raises `unwatched-project` for a child project with
18
18
  a checkout that is left off the line — not for the factory itself, which is in
19
19
  the pass by default, and not for a declared project with no checkout at
20
- all, which `smith projects list` marks with `?` instead of leaving it
20
+ all, which `bs projects list` marks with `?` instead of leaving it
21
21
  off the printed list. The two surfaces no longer answer the same
22
- question.) Then run `smith scheduler run
22
+ question.) Then run `bs scheduler run
23
23
  [--dry] --session <id> [--project <dir>...]` with those flags — a
24
24
  deterministic pass over the event log, it never dispatches an agent
25
25
  itself (`factory/policies/scheduler.yml`, architecture §12). Read its
@@ -42,7 +42,7 @@ pending proposals lets them sit invisible instead of surfacing them.
42
42
  where the planner reads the living spec/analytics/recheck outcomes
43
43
  and proposes new epics — the operator's tick is required regardless
44
44
  of confidence, same as any other plan sign-off.
45
- 2. **Ask who may say yes.** `smith scheduler admit --session <id>
45
+ 2. **Ask who may say yes.** `bs scheduler admit --session <id>
46
46
  [--project <dir>...]`, with the same flag line step 1 printed, re-reads
47
47
  the same proposals and classifies
48
48
  each one `auto` or `operator` against `scheduler.yml`'s `autonomy:`
@@ -66,9 +66,9 @@ pending proposals lets them sit invisible instead of surfacing them.
66
66
  `confidence_floor` is an operator decision about standing policy, not
67
67
  a way past one proposal — and a `security-surface` denial is the one
68
68
  nothing in the file can lift.
69
- 3. Gather `smith stats overview [--session <id>]` and `smith stats roadmap
69
+ 3. Gather `bs stats overview [--session <id>]` and `bs stats roadmap
70
70
  [--session <id>]` (milestone progress + budget burn per milestone).
71
- 4. **Read back the disagreements.** `smith judge escalations --session
71
+ 4. **Read back the disagreements.** `bs judge escalations --session
72
72
  <id>` folds the lineage for `quorum-decision` events whose latest word
73
73
  was `escalate` — a gate finding, an epic verdict or a plan critique the
74
74
  cross-provider quorum could not settle. It exits `1` on an open
@@ -83,7 +83,7 @@ pending proposals lets them sit invisible instead of surfacing them.
83
83
  `mode: shadow` disagreement, which the gate outcome never reported to
84
84
  anyone). Resolving one is an operator reading, never this session
85
85
  deciding on its own which provider was right.
86
- 5. **Read back the parallelism.** `smith wave audit --session <id>
86
+ 5. **Read back the parallelism.** `bs wave audit --session <id>
87
87
  [--epic <id>]` folds every `wave-admitted` against the
88
88
  `dispatch_decision` intervals underneath it and says whether a wave
89
89
  admitted N wide actually *ran* N wide. `wave check` only ever certified
@@ -97,11 +97,11 @@ pending proposals lets them sit invisible instead of surfacing them.
97
97
  check before reporting it as a stall. A `partial` verdict is **not** a
98
98
  finding; put `widest` in the digest as the one number that says how wide
99
99
  this factory has ever actually run.
100
- 6. Dispatch **`scribe`** (`.claude/agents/scribe.md`, small tier) with that
100
+ 6. Dispatch **`bs-scribe`** (`.claude/agents/bs-scribe.md`, small tier) with that
101
101
  JSON plus step 2's admissions as input: shipped / in-flight / blocked /
102
102
  budget burn / next milestone / **N rechecks pending, M maintenance
103
103
  bumps auto-schedulable** (never silently dropped), ≤300 words, linking
104
- into the dashboard (`smith ui serve`'s URL) — never paraphrase numbers
104
+ into the dashboard (`bs ui serve`'s URL) — never paraphrase numbers
105
105
  the query didn't return.
106
106
  7. This fires automatically on a weekly cadence and immediately on any
107
107
  milestone completion (architecture §12) — for an ad hoc `/bs report`,
@@ -114,7 +114,7 @@ pending proposals lets them sit invisible instead of surfacing them.
114
114
 
115
115
  **Provider calibration note (Phase 8).** While any provider in
116
116
  `factory/policies/crosscheck.yml` is still `mode: shadow`, run
117
- `smith stats providers [--session <id>] [--since <iso-date>]` alongside the
117
+ `bs stats providers [--session <id>] [--since <iso-date>]` alongside the
118
118
  scheduler pass and fold a one-line mention into the digest when a provider
119
119
  has accumulated enough runs to review (`runs`, `verdicts`, `agreementRate`,
120
120
  `schemaFailureRate`, `transportFailureRate`, `failuresByCode` per provider —
@@ -15,19 +15,33 @@ epic. "Hand the wave over" below says how, and says the one thing that makes
15
15
  it safe.
16
16
 
17
17
  Ask the epic's effort tier once, at the top of the run, and keep the answer
18
- for the whole epic — `smith effort show --plan
18
+ for the whole epic — `bs effort show --plan
19
19
  factory/specs/active/<epic>/plan-vN.json`. Step 13 here, and steps 3, 6 and 7
20
20
  of the wave playbook, each name the `profile` field that scales them; nothing
21
21
  else in either file moves. Read `effective`, not `requested`: a plan whose
22
22
  live tasks fire a security trigger is floored, and running it at the tier the
23
23
  file asked for would be running it below the floor.
24
24
 
25
+ Ask the cross-provider gate once too, at the top of the run and before any
26
+ wave is admitted — `bs judge preflight --project <project-dir>`, the same
27
+ call INSTALL.md makes at setup. When it reports the gating pool cannot
28
+ decide (fewer active external providers than `quorum_rule.min_providers`
29
+ after OpenRouter substitution, or a provider the project's own
30
+ `.blacksmith/crosscheck.yml` names but this box cannot reach), STOP and ask
31
+ the operator before admitting a wave — per `.claude/skills/bs/SKILL.md`'s
32
+ "Talking to the operator" rules (options carry a confidence %; not restated
33
+ here): add an OpenRouter key, add a model, pick which providers to run with,
34
+ or continue without cross-check. Skipping this is not a shortcut — steps 15
35
+ and 16 spend a real judge call to find the same gap mid-run, after the epic's
36
+ work is already done. Full substitution and precedence mechanism:
37
+ docs/runbooks/providers.md §9d.
38
+
25
39
  1. Ask the graph how wide this wave can be, then ask the gate whether it may
26
40
  be that wide. Two commands, two different questions — do not skip to the
27
41
  second with a set you picked by eye.
28
42
 
29
43
  ```bash
30
- smith wave next factory/specs/active/<epic>/plan-vN.json \
44
+ bs wave next factory/specs/active/<epic>/plan-vN.json \
31
45
  --session <session-id> --repo <project-dir>
32
46
  ```
33
47
 
@@ -46,7 +60,7 @@ file asked for would be running it below the floor.
46
60
 
47
61
  Then put that proposal through the gate that actually admits it:
48
62
 
49
- `smith wave check factory/specs/active/<epic>/plan-vN.json <task-id>...`
63
+ `bs wave check factory/specs/active/<epic>/plan-vN.json <task-id>...`
50
64
  — claims must be pairwise disjoint and share no `worktree.yml`
51
65
  `serialize_always_globs` hotspot. A violation means cut a dependency
52
66
  edge and run the pair serially instead of forcing the wave.
@@ -66,6 +80,16 @@ file asked for would be running it below the floor.
66
80
  operator's call. So a proposed wave can still be refused here. That is
67
81
  the gate working, not the proposal being wrong — drop tasks from the
68
82
  tail of `wave` and re-check.
83
+ - With `--session`, `wave check` admits only live tasks of an ingested
84
+ plan. A plan version whose tasks the log never saw is refused
85
+ `cli.plan-not-ingested`: run `bs plan ingest` on it, then re-check.
86
+ An id with no task row is refused `plan.unknown-task`, and a task
87
+ that is `completed`, `superseded` or `waived` is refused
88
+ `cli.task-not-live`, naming each task and its status. Drop those ids.
89
+ A `failed` or `escalated` task stays admissible: an operator still
90
+ holds it, and the escalation ladder re-admits it. A follow-up
91
+ `findings raise` minted stays admissible while it is live, and a wave
92
+ of follow-ups alone still needs the plan it names ingested first.
69
93
  - Never narrow a wave because a narrow one feels safer. A wave of one
70
94
  passes every check in this file — one task is disjoint with nothing,
71
95
  shares a hotspot with nothing, crosses no import edge — so `valid: true`
@@ -84,9 +108,9 @@ would cost:
84
108
  - **Inline**, in this session, when the wave is small enough for this
85
109
  window. Read `wave.md` and work it here.
86
110
  - **In a session of its own**, when it is not, but you still want to drive
87
- it yourself: `smith session start <wave-id> --continues <session-id>#<n>`,
111
+ it yourself: `bs session start <wave-id> --continues <session-id>#<n>`,
88
112
  where `<n>` is the index of the event that admitted this wave.
89
- - **Dispatched**, as the **`wave-runner`** agent, when the wave would eat
113
+ - **Dispatched**, as the **`bs-wave-runner`** agent, when the wave would eat
90
114
  the window this epic needs to reach its own end. It is the only role
91
115
  `factory/policies/delegation.yml` grants `Agent`, and it earns that by
92
116
  opening its own log first — so hand it the event id of *your dispatch*,
@@ -106,12 +130,12 @@ to make (D13, D-266). What it does not buy is a shortcut. A wave session
106
130
  opened without `--continues` writes into a log nothing here reads, and the
107
131
  verbs below then report a wave that appears never to have run rather than
108
132
  failing — which is the same silence, arriving as a green. When you dispatched
109
- the wave rather than ran it, `smith delegation check <session-id>` is what
133
+ the wave rather than ran it, `bs delegation check <session-id>` is what
110
134
  turns that silence into a red: a `wave-runner` that dispatched before opening
111
135
  its log, or against the wrong event, is `unverifiable` and exits 1.
112
136
 
113
137
  **Then check that it did.** Once the wave's tasks reach a terminal state,
114
- `smith wave audit --session <id> --epic <epic>` reads the log back and
138
+ `bs wave audit --session <id> --epic <epic>` reads the log back and
115
139
  reports the width the wave actually ran at, not the width it was admitted
116
140
  at. Ask it with *this* session: it folds the lineage, so it sees the
117
141
  dispatches a wave session made under the admission this one wrote. Exit `1`
@@ -125,7 +149,7 @@ playbooks are written to prevent.
125
149
 
126
150
  11. Repeat until every task in the live plan version is `completed`/`waived`,
127
151
  or `superseded` by a successor that is itself terminal-OK (recursively —
128
- a successor chain counts too). Dispatch the **`planner`** again for
152
+ a successor chain counts too). Dispatch the **`bs-planner`** again for
129
153
  the epic verdict against acceptance criteria — gaps found → a NEW
130
154
  `plan-v(n+1)` with inferred tasks (never a live-graph mutation,
131
155
  architecture §12), auto-scheduled at confidence ≥0.8 else parked for an
@@ -133,7 +157,7 @@ playbooks are written to prevent.
133
157
  on it**, whichever step cut it:
134
158
 
135
159
  ```bash
136
- smith plan ingest factory/specs/active/<epic>/plan-v<n+1>.json \
160
+ bs plan ingest factory/specs/active/<epic>/plan-v<n+1>.json --project <project-dir> \
137
161
  --session <session-id> --plan-version <n+1> --causal-parent <event-id>
138
162
  ```
139
163
 
@@ -148,11 +172,11 @@ playbooks are written to prevent.
148
172
  branch**. Every gate up to here ran inside a task worktree, so every
149
173
  green you have so far is a green about a worktree — the envkit epic
150
174
  shipped six green lint gates on a branch whose `pnpm lint` exited 1
151
- (D-42). Check out `smith/<epic>/integration` in the project first (this
175
+ (D-42). Check out `bs/<epic>/integration` in the project first (this
152
176
  command refuses to move your working tree, and refuses a dirty one):
153
177
 
154
178
  ```bash
155
- smith integration check --epic <epic> --project <project-dir> \
179
+ bs integration check --epic <epic> --project <project-dir> \
156
180
  --checks <checks.json> \
157
181
  --session <session-id> --plan-version <n> --causal-parent <event-id>
158
182
  ```
@@ -168,25 +192,35 @@ playbooks are written to prevent.
168
192
  check, the review and the close all claim to have verified a plan that
169
193
  no longer exists. One dogfooded product epic closed at v3 with its
170
194
  integration check and its closing spec review both stamped v1; the goal
171
- check, passed the flag, said v3. Read the version off `smith plan ingest`'s receipt and
195
+ check, passed the flag, said v3. Read the version off `bs plan ingest`'s receipt and
172
196
  pass it every time.
173
- 13. Dispatch the **`spec-reviewer`** again — this time against the code.
197
+ 13. Dispatch the **`bs-spec-reviewer`** again — this time against the code.
174
198
  The pre-code review at `/bs plan` step 3 read the spec against nothing;
175
199
  this one reads it against the assembled branch, which is the only reading
176
200
  that can see a criterion the finished code proves wrong. The envkit epic
177
201
  deadlocked on exactly that: a correct S2 whose fix criterion 3 mandated
178
202
  and criterion 1 forbade (D-33). It writes to a close-specific path so it
179
- cannot clobber the pre-code artifact; hand that file — each item naming the
203
+ cannot clobber the pre-code artifact. Put
204
+ `Declared artifact: /abs/path/to/state/results/<epic>.spec-review-close-vN.json`
205
+ inline on its own line in the dispatch prompt (the PreToolUse guard refuses
206
+ a judge dispatch without it); hand that file — each item naming the
180
207
  `criterion_ref` it is against — to:
181
208
 
182
209
  ```bash
183
- smith epic spec-review --epic <epic> --project <project-dir> \
210
+ bs epic spec-review --epic <epic> --project <project-dir> \
184
211
  --plan factory/specs/active/<epic>/plan-vN.json \
185
212
  --reviewed-by spec-reviewer \
186
- [--evidence state/results/<epic>.spec-review-close-vN.json] \
213
+ --evidence state/results/<epic>.spec-review-close-vN.json \
187
214
  --session <session-id> --plan-version <n> --causal-parent <event-id>
188
215
  ```
189
216
 
217
+ `--evidence` is required unless you pass `--no-findings` instead, and
218
+ the two are exclusive: a reviewer that found nothing writes `[]` to that
219
+ path, and `--no-findings` is the operator attesting a review that ran
220
+ outside the factory — recorded as `attested_by: operator`, never read as
221
+ a file. A missing flag is refused (`cli.missing-flag`), because an empty
222
+ review read from an absent file is how a capped reviewer closed clean.
223
+
190
224
  Its `dispatch_decision` — and step 14's, and the planner's when it
191
225
  renders a verdict — goes against the reserved `<epic>/integration` ref,
192
226
  never a `<epic>/spec-review-rN` you coin for the occasion
@@ -205,12 +239,12 @@ playbooks are written to prevent.
205
239
  distinguishes them. It
206
240
  exits 0 even when it raises findings: what it found blocks the plan, not
207
241
  this command. A spec finding is unwaivable at S1/S2 and no task's diff can
208
- contain the fix, so the answer is `smith plan amend --plan … --findings …
242
+ contain the fix, so the answer is `bs plan amend --plan … --findings …
209
243
  --rationale … --sites …` (§6a of the operator guide). `--sites` is every
210
244
  place that shape occurs, not only the file the finding was reported
211
245
  against — answer it before writing the changes, because it is the question
212
246
  that decides how much the amendment fixes (D-123). It cuts plan v(n+1):
213
- ingest it (step 11's `smith plan ingest`), go back to step 11 with the new
247
+ ingest it (step 11's `bs plan ingest`), go back to step 11 with the new
214
248
  version, and re-run this review against the branch that results. Never
215
249
  record a spec defect as a coder failure; that is
216
250
  the deadlock this step exists to end.
@@ -221,20 +255,22 @@ playbooks are written to prevent.
221
255
  did not write. Get the clause list first (read-only, no event):
222
256
 
223
257
  ```bash
224
- smith epic goal --epic <epic>
258
+ bs epic goal --epic <epic>
225
259
  ```
226
260
 
227
- Hand those clauses and the live plan to a **`spec-reviewer`** session —
261
+ Hand those clauses and the live plan to a **`bs-spec-reviewer`** session —
228
262
  a fresh one, never the planner's, and never the same dispatch as step 13:
229
- `smith dispatch check` refuses to let one dispatch answer for both.
263
+ `bs dispatch check` refuses to let one dispatch answer for both.
230
264
  Dispatch it *after* step 13's record is written, not alongside it — a
231
265
  dispatch that predates the previous record has already answered for that
232
266
  one, so two sessions fired up front leave this record unaccounted for and
233
- `smith dispatch check` reports that record `unverifiable`. Take back one
234
- verdict per clause, in the goal's order, and record it:
267
+ `bs dispatch check` reports that record `unverifiable`. Take back one
268
+ verdict per clause, in the goal's order, and record it. Its prompt carries
269
+ `Declared artifact: /abs/path/to/state/results/<epic>.goal-coverage-vN.json`
270
+ inline on its own line, for the same reason:
235
271
 
236
272
  ```bash
237
- smith epic goal-check --epic <epic> \
273
+ bs epic goal-check --epic <epic> \
238
274
  --plan factory/specs/active/<epic>/plan-vN.json \
239
275
  --coverage state/results/<epic>.goal-coverage-vN.json \
240
276
  --checked-by spec-reviewer \
@@ -244,8 +280,8 @@ playbooks are written to prevent.
244
280
  `covered` must name live plan task ids; `out-of-scope` must give a reason,
245
281
  and that reason is quoted back to the epic judge. `uncovered` mints an
246
282
  S2-major spec finding against the plan file — no task diff can contain
247
- that fix, so the answer is `smith plan amend`, which cuts v(n+1) and sends
248
- you back to step 11 — through `smith plan ingest`, as in step 13. The
283
+ that fix, so the answer is `bs plan amend`, which cuts v(n+1) and sends
284
+ you back to step 11 — through `bs plan ingest`, as in step 13. The
249
285
  command exits 0 even when it raises findings.
250
286
 
251
287
  **It refuses (`cli.no-epic-goal`) when the owning milestone states no
@@ -260,40 +296,48 @@ playbooks are written to prevent.
260
296
  integrable at all:
261
297
 
262
298
  ```bash
263
- smith epic verdict --epic <epic> --project <project-dir> \
299
+ bs epic verdict --epic <epic> --project <project-dir> \
264
300
  --session <session-id> --plan-version <n> --causal-parent <event-id>
265
301
  ```
266
302
 
267
303
  Mechanical oracles first: non-terminal tasks, open blocking findings, a
268
- missing/failed/stale integration-root check, a missing/stale closing spec
269
- review, or a missing/stale spec-vs-goal check return `hold` (exit 1)
270
- without spending a judge call. `--project` is required — the verdict
271
- reads the integration branch head to decide whether the records from
272
- steps 12, 13 and 14 still cover it. On `hold`, do not open the PR — report
273
- the `blockers` to the operator and go back to step 11.
304
+ missing/failed/stale integration-root check, a closing spec review that's
305
+ stale or missing when the tier required one, or a missing/stale
306
+ spec-vs-goal check return `hold` (exit 1) without spending a judge call.
307
+ A tier that waived the review (step 13: `when-plan-amended` on a plan
308
+ still at v1) is not a hold on that ground — the verdict records the skip
309
+ as a fact instead of staying silent about it, same as step 13 asks. A
310
+ review that IS on record is still walked through every staleness check
311
+ regardless of tier. `--project` is required — the verdict reads the
312
+ integration branch head to decide whether the records from steps 12, 13
313
+ and 14 still cover it. On `hold`, do not open the PR — report the
314
+ `blockers` to the operator and go back to step 11.
274
315
 
275
316
  Once the oracles pass, the judge call is real money when an external
276
317
  provider is `active` in `crosscheck.yml`: codex spends the operator's
277
- ChatGPT quota, DeepSeek spends prepaid credit, and step 16 runs the
278
- verdict again before it writes, so a clean close is two spends, not one
279
- (one dogfooded epic wrote two `quorum-decision`s against its
280
- integration ref, one from the verdict and one from the close).
281
- Nothing in the `quorum-decision` payload names the purse, so say which
282
- one it is to the operator before the first call, not after the second.
318
+ ChatGPT quota, DeepSeek spends prepaid credit. Say which purse it is to
319
+ the operator before this call, not after.
320
+
321
+ Step 16 no longer pays twice for the same integration head: the
322
+ `quorum-decision` this call writes now carries `integration_head`, and a
323
+ clean `go` against an unchanged head is reused rather than re-asked
324
+ (issue #270). Anything that invalidates it — a new commit, a `hold`, or a
325
+ decision from before `integration_head` existed — still re-runs.
283
326
  16. Record the close. The verdict above is a read-only probe and writes
284
327
  nothing; `epic close` is what makes it a fact in the log (D-43):
285
328
 
286
329
  ```bash
287
- smith epic close --epic <epic> --project <project-dir> \
330
+ bs epic close --epic <epic> --project <project-dir> \
288
331
  --session <session-id> --plan-version <n> --causal-parent <event-id>
289
332
  ```
290
333
 
291
- It re-runs the verdict, then emits `epic-closed` on `go` and refuses
292
- (exit 1, no event) on `hold`. **Never pass `--override-rationale`
334
+ It reuses step 15's `go` decision when the integration head hasn't moved
335
+ (see step 15), otherwise re-runs the verdict, then emits `epic-closed` on
336
+ `go` and refuses (exit 1, no event) on `hold`. **Never pass `--override-rationale`
293
337
  yourself** — closing over a hold is the operator's call; ask for it and
294
338
  quote the blockers. If the epic was cut by `/bs audit`
295
339
  ([`audit.md`](audit.md) step 10), follow the close with
296
- `smith audit resolve <project-dir> --epic <epic> --session <session-id> --causal-parent <event-id> [--specs-dir <dir>]`
340
+ `bs audit resolve <project-dir> --epic <epic> --session <session-id> --causal-parent <event-id> [--specs-dir <dir>]`
297
341
  so every finding this plan still claims is marked `fixed` in the
298
342
  project's audit store — the rest come back `deferred`, left exactly as
299
343
  they were, because the plan scoped them out on purpose. The store, not
@@ -301,18 +345,106 @@ playbooks are written to prevent.
301
345
  newest plan from the work root's specs dir by default; when this
302
346
  project's plans live elsewhere, pass `--specs-dir` naming the same specs
303
347
  dir the epic was run from, or it refuses rather than guess.
304
- 17. Open **one integration PR per epic** with the scribe-written body
305
- (`/bs report`'s playbook, [`report.md`](report.md)) — screenshots, test
306
- results, reviewer verdict, waivers granted, timeline link. The head is
307
- `smith/<epic>/integration`. The base is the target repo's `main` unless
348
+ 17. Open **one integration PR per epic** with a scribe-written body. The head is
349
+ `bs/<epic>/integration`. The base is the target repo's `main` unless
308
350
  the epic was cut from another epic's integration branch, in which case
309
351
  the PR stacks on that branch and the operator merges the parent's PR
310
- first — a child epic cut from `smith/<parent>/integration` opens its PR
352
+ first — a child epic cut from `bs/<parent>/integration` opens its PR
311
353
  against that branch, behind the parent's PR; once the parent has merged
312
354
  and its branch is deleted, GitHub retargets the child to `main`. The
313
355
  operator reviews on GitHub; this session never merges to `main`
314
356
  (`docs/standards/guardrails.md`).
315
357
 
358
+ **Build the fact pack first.** The scribe has no `Bash` and a small turn
359
+ cap, so it cannot run `git` or `bs`, and a scribe left to grep raw logs
360
+ caps and fills the gaps by invention — a wrong event id, a merged task
361
+ called dead, "no waivers" over two grants. Compute every fact in your own
362
+ session, from the epic session with `--lineage` so the waves' records
363
+ count. Every fact comes from the log or from git, never from the SQLite
364
+ projection: `stats kanban` and `stats timeline` read a projection that
365
+ can lag the log, and `db rebuild` clears it before it re-reads, so do
366
+ not refresh it for this.
367
+
368
+ ```bash
369
+ # one pass over the log (pass an --n that covers the whole lineage)
370
+ bs event tail <session-id> --lineage --n 100000
371
+ # the fingerprint map: finding_id, task_id and fingerprint of the epic's findings
372
+ bs findings list --session <session-id> --epic <epic>
373
+ # the goal the summary states
374
+ bs epic goal --epic <epic> [--roadmap-path <file>]
375
+ # the branch itself
376
+ git log --oneline <base>..bs/<epic>/integration
377
+ git diff --shortstat <base>...bs/<epic>/integration
378
+ ```
379
+
380
+ From the `event tail` pass keep, each with its `event_id`:
381
+ - `waiver-granted` and `waiver-denied` records whose
382
+ `payload.fingerprint` matches one of the epic's findings, mapped to
383
+ that finding's id and task. Match on the stored `fingerprint` that
384
+ `findings list` prints, and drop a decision that matches none: an
385
+ ancestor session can carry an earlier epic's decisions. Grants come
386
+ from here, not from
387
+ `findings list --status`, because a grant only moves a raised or
388
+ confirmed finding to `waived`; an amended or fix-verified one keeps
389
+ its status.
390
+ - `wave-merged` records: `payload.task_ids` and the event id.
391
+ - `epic-closed` from step 16: its event id, `machine_verdict`, and
392
+ `payload.summary.tasks` — each task's `task_status`, read from the log
393
+ at close. A follow-up cleared by closing all its findings (fixed, refuted
394
+ or waived) still reads `todo` there; cross-check it against the findings and never publish it
395
+ as an open task.
396
+ - `audit-resolved`, for an epic cut by `/bs audit`: every record whose
397
+ `payload.epic` is this epic — one epic can resolve more than once, and
398
+ the lineage carries a parent epic's resolve too. List each event id.
399
+ `fixed` and `deferred` are fingerprint arrays: fixed is the union of
400
+ every kept record's `fixed`; deferred is the last record's `deferred`
401
+ minus that union. Write the two counts into the pack yourself.
402
+ - `integration-check` records whose `payload.epic_id` is this epic and
403
+ whose `head_sha` equals the `epic-closed` record's
404
+ `summary.integration.check.head_sha`: each check's `name` and `pass`.
405
+
406
+ `epic goal` reads this repo's roadmap by default; when the epic's
407
+ roadmap lives elsewhere, pass `--roadmap-path` naming it, or the goal
408
+ you hand over is the wrong one. For a UI epic, take the screenshots
409
+ from each task's tester result, `state/results/<task-id>.tester*.json`,
410
+ field `artifacts` (the files sit under `state/artifacts/<task-id>/`);
411
+ [`docs/standards/stack.md`](../../../docs/standards/stack.md) wants
412
+ them on this PR. The pack carries a screenshot only as an image you
413
+ have uploaded or attached — never a local path, which no reviewer can
414
+ open; one you cannot attach stays out.
415
+
416
+ Write only these filtered facts into one pack file — never raw command
417
+ output, which puts the scribe back to grepping logs. Dispatch the scribe with that file as its **only source**, and the
418
+ PR-body scratch file as its write root, under one rule: **omit, never
419
+ infer**. A fact the pack does not hold stays out of the body; a count,
420
+ a status, or an event id is copied from the pack, never worked out. If
421
+ the pack is wrong, rebuild it and dispatch again; do not let the scribe
422
+ reach past it into `state/events/`.
423
+
424
+ The PR body opens with a short plain-language summary, written under
425
+ `.claude/skills/bs/SKILL.md`'s "Talking to the operator" section — cite
426
+ that section for how to write it, do not restate its rule here. In the
427
+ project's own terms, not Blacksmith's: what this epic changed, what is
428
+ still open, and what decision the operator must make, if any. The
429
+ existing fact sections below follow it as the technical part the rule
430
+ asks for.
431
+
432
+ The PR body has this shape and nothing else: the plain-language opening
433
+ above; **Summary** (the epic id
434
+ and its goal, one paragraph); **Tasks** (one line per task: id,
435
+ `task_status`, and its `wave-merged` event id when it has one);
436
+ **Waivers** (granted and denied, each with its finding id and event id,
437
+ or the word "none" only when both lists in the pack are empty);
438
+ **Verification** (the verdict outcome, the `epic-closed` event id, the
439
+ `integration-check` results, the `audit-resolved` event ids and the
440
+ fixed and deferred counts when there are any, the shortstat, and the
441
+ commit count from the log);
442
+ **Screenshots** (only when the pack carries them); **Timeline** (the
443
+ dashboard link, when you put one in the pack). The scribe's 300-word
444
+ cap binds the prose — Summary and the sentences around the lists; the
445
+ Tasks, Waivers and check lists are artifacts and are never cut to fit
446
+ it, since a dropped line reads as a task or waiver that was not there.
447
+
316
448
  Before the first push, read the authorship the branch is about to make
317
449
  public — `git log --format=%ae <base>..HEAD | sort -u` — and stop on any
318
450
  address that is not a noreply one; a coder's worktree commit carries
@@ -324,7 +456,7 @@ playbooks are written to prevent.
324
456
  Append it by hand, under the reserved ref and the live plan version:
325
457
 
326
458
  ```bash
327
- smith event append '{"session_id":"<session-id>","actor":"orchestrator","event_type":"integration-pr-opened","task_id":"<epic>/integration","plan_version":<n>,"causal_parent":"<event-id>","payload":{"step":17,"pr_url":"<url>","pr_number":<n>,"repo":"<owner>/<repo>","base_ref":"<base>","head_ref":"smith/<epic>/integration","head_sha":"<sha>","base_sha":"<sha>","commits":<n>,"changed_files":<n>,"additions":<n>,"deletions":<n>}}'
459
+ bs event append '{"session_id":"<session-id>","actor":"orchestrator","event_type":"integration-pr-opened","task_id":"<epic>/integration","plan_version":<n>,"causal_parent":"<event-id>","payload":{"step":17,"pr_url":"<url>","pr_number":<n>,"repo":"<owner>/<repo>","base_ref":"<base>","head_ref":"bs/<epic>/integration","head_sha":"<sha>","base_sha":"<sha>","commits":<n>,"changed_files":<n>,"additions":<n>,"deletions":<n>}}'
328
460
  ```
329
461
 
330
462
  The receipt says whether the timeline reads the type; it does, and it