@juzser/blacksmith 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.claude/agents/coder.md +9 -3
  2. package/.claude/agents/grader.md +13 -5
  3. package/.claude/agents/merger.md +2 -0
  4. package/.claude/agents/planner.md +1 -1
  5. package/.claude/agents/researcher.md +3 -1
  6. package/.claude/agents/reviewer.md +9 -2
  7. package/.claude/agents/security-reviewer.md +15 -7
  8. package/.claude/agents/spec-reviewer.md +12 -3
  9. package/.claude/agents/tester.md +5 -2
  10. package/.claude/agents/uiux.md +10 -3
  11. package/.claude/agents/verifier.md +8 -3
  12. package/.claude/skills/bs/SKILL.md +103 -0
  13. package/.claude/skills/bs/audit.md +250 -0
  14. package/.claude/skills/bs/dispatch.md +394 -0
  15. package/.claude/skills/bs/lessons.md +73 -0
  16. package/.claude/skills/bs/mcp.md +29 -0
  17. package/.claude/skills/bs/new.md +33 -0
  18. package/.claude/skills/bs/plan.md +155 -0
  19. package/.claude/skills/bs/report.md +129 -0
  20. package/.claude/skills/bs/run.md +325 -0
  21. package/.claude/skills/bs/status.md +54 -0
  22. package/.claude/skills/bs/ui.md +12 -0
  23. package/.claude/skills/bs/waivers.md +13 -0
  24. package/.claude/skills/bs/wave.md +261 -0
  25. package/README.md +119 -65
  26. package/factory/orchestrator/dist/actors.js +4 -3
  27. package/factory/orchestrator/dist/agents-registry.js +96 -19
  28. package/factory/orchestrator/dist/artifacts.js +36 -4
  29. package/factory/orchestrator/dist/budgets.js +7 -5
  30. package/factory/orchestrator/dist/cli.js +107 -7
  31. package/factory/orchestrator/dist/crosscheck.js +11 -0
  32. package/factory/orchestrator/dist/daemon.js +14 -0
  33. package/factory/orchestrator/dist/db/projector.js +272 -90
  34. package/factory/orchestrator/dist/db/queries.js +83 -14
  35. package/factory/orchestrator/dist/db/schema.js +7 -0
  36. package/factory/orchestrator/dist/events.js +38 -4
  37. package/factory/orchestrator/dist/harness.js +367 -0
  38. package/factory/orchestrator/dist/judges.js +49 -8
  39. package/factory/orchestrator/dist/lessons.js +4 -4
  40. package/factory/orchestrator/dist/mcp.js +4 -3
  41. package/factory/orchestrator/dist/paths.js +167 -15
  42. package/factory/orchestrator/dist/plan.js +1 -1
  43. package/factory/orchestrator/dist/planQuorum.js +36 -8
  44. package/factory/orchestrator/dist/providers/api-transport.js +62 -5
  45. package/factory/orchestrator/dist/providers/index.js +1 -0
  46. package/factory/orchestrator/dist/providers/schema-validate.js +58 -7
  47. package/factory/orchestrator/dist/providers/types.js +5 -0
  48. package/factory/orchestrator/dist/roadmap.js +3 -3
  49. package/factory/orchestrator/dist/scaffold.js +3 -2
  50. package/factory/orchestrator/dist/stack.js +10 -2
  51. package/factory/orchestrator/dist/templateFragments.js +178 -0
  52. package/factory/orchestrator/dist/usage.js +24 -5
  53. package/factory/orchestrator/dist/workroot.js +113 -0
  54. package/factory/orchestrator/drizzle/0011_findings_spec_ref.sql +3 -0
  55. package/factory/orchestrator/drizzle/meta/_journal.json +7 -0
  56. package/factory/policies/budgets.yml +6 -3
  57. package/factory/policies/crosscheck.yml +10 -8
  58. package/factory/policies/lessons.md +122 -0
  59. package/factory/policies/taxonomy.yml +6 -2
  60. package/factory/specs/roadmap.md +154 -0
  61. package/package.json +8 -6
@@ -0,0 +1,325 @@
1
+ # `/bs run <epic>` — the epic tier
2
+
3
+ [`dispatch.md`](dispatch.md) binds every agent this playbook dispatches;
4
+ none of it is restated here.
5
+
6
+ The loop playbook (architecture §3, §5, §11): one wave at a time, and every
7
+ task in the wave at once.
8
+
9
+ **Two tiers, two logs.** This file is the epic tier. It admits a wave (step
10
+ 1), hands it to [`wave.md`](wave.md) — the wave playbook, steps 2-10 — and
11
+ picks the loop back up at step 11 once the wave has landed. That is D13's
12
+ fix: each tier owns the log for what it dispatches, so an epic session stops
13
+ carrying every wave's turns in a window it needs to reach the end of the
14
+ epic. "Hand the wave over" below says how, and says the one thing that makes
15
+ it safe.
16
+
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
19
+ factory/specs/active/<epic>/plan-vN.json`. Step 13 here, and steps 3, 6 and 7
20
+ of the wave playbook, each name the `profile` field that scales them; nothing
21
+ else in either file moves. Read `effective`, not `requested`: a plan whose
22
+ live tasks fire a security trigger is floored, and running it at the tier the
23
+ file asked for would be running it below the floor.
24
+
25
+ 1. Ask the graph how wide this wave can be, then ask the gate whether it may
26
+ be that wide. Two commands, two different questions — do not skip to the
27
+ second with a set you picked by eye.
28
+
29
+ ```bash
30
+ smith wave next factory/specs/active/<epic>/plan-vN.json \
31
+ --session <session-id> --repo <project-dir>
32
+ ```
33
+
34
+ `wave` is the widest set the plan admits right now: dependencies landed,
35
+ claims pairwise disjoint, no shared hotspot, producers ordered ahead of
36
+ the consumers that import them. Every task it left out is in `deferred`
37
+ with a reason (`dependency-pending`, `claim-overlap`, `serialize-hotspot`,
38
+ `symbol-coupled`) and the ids that held it back, so a short wave is always
39
+ a wave with an explanation. It reads and writes nothing — ask it again
40
+ whenever you want a fresh answer. `--session` is what lets it see live
41
+ task status and the follow-ups `findings raise` minted into the log but
42
+ into no plan file; without it you are scheduling from the plan as written
43
+ hours ago. Exit 1 means work remains and none of it can start — a stall
44
+ to report, not an empty answer. An empty `wave` with `remaining: 0` is the
45
+ epic finished.
46
+
47
+ Then put that proposal through the gate that actually admits it:
48
+
49
+ `smith wave check factory/specs/active/<epic>/plan-vN.json <task-id>...`
50
+ — claims must be pairwise disjoint and share no `worktree.yml`
51
+ `serialize_always_globs` hotspot. A violation means cut a dependency
52
+ edge and run the pair serially instead of forcing the wave.
53
+ - The same verdict carries `symbolImpact`. `valid: true` with
54
+ `symbolImpact.status: "coupled"` means the claims really are disjoint
55
+ and that is not enough: the two tasks sit on either end of an import
56
+ edge the plan declared no dependency for. Split the wave and run the
57
+ producer first — there is no override for a crossing, because the
58
+ declared-edge case is already refused above it
59
+ (operator-guide/wave.md §2).
60
+ - `wave check` is also where the wave is priced and where
61
+ `max_in_flight_tasks` is enforced (`budgets.yml`, `over-fan-out`).
62
+ `wave next` deliberately asks neither question: a proposer that also
63
+ priced the wave would have to choose which task to drop, and that is an
64
+ operator's call. So a proposed wave can still be refused here. That is
65
+ the gate working, not the proposal being wrong — drop tasks from the
66
+ tail of `wave` and re-check.
67
+ - Never narrow a wave because a narrow one feels safer. A wave of one
68
+ passes every check in this file — one task is disjoint with nothing,
69
+ shares a hotspot with nothing, crosses no import edge — so `valid: true`
70
+ on a singleton is not evidence the wave was right, only that nothing
71
+ refused it. That is the failure this step exists to prevent: a factory
72
+ whose whole claim is parallel execution, running its plan one task at a
73
+ time and passing every gate while it does.
74
+
75
+ ## Steps 2-10 — hand the wave over
76
+
77
+ The admitted wave now goes to [`wave.md`](wave.md), the wave playbook:
78
+ worktrees, pre-code, coder, tester, grader, the gate pipeline, bounces, and
79
+ the merge queue. Three shapes, in order of how much of this window the wave
80
+ would cost:
81
+
82
+ - **Inline**, in this session, when the wave is small enough for this
83
+ window. Read `wave.md` and work it here.
84
+ - **In a session of its own**, when it is not, but you still want to drive
85
+ it yourself: `smith session start <wave-id> --continues <session-id>#<n>`,
86
+ where `<n>` is the index of the event that admitted this wave.
87
+ - **Dispatched**, as the **`wave-runner`** agent, when the wave would eat
88
+ the window this epic needs to reach its own end. It is the only role
89
+ `factory/policies/delegation.yml` grants `Agent`, and it earns that by
90
+ opening its own log first — so hand it the event id of *your dispatch*,
91
+ not the admission, because that is the edge the audit walks.
92
+
93
+ Whichever shape, hand over the project directory, the epic id, the live plan
94
+ path, the admitted task ids, the effort `profile` resolved above, and the
95
+ session id and event id the wave writes from. Take back each task's terminal
96
+ state, the merge-queue outcome, the findings raised, any escalation, and the
97
+ wave session's last event id if it opened one — chain the next command off
98
+ that.
99
+
100
+ Splitting the tier does not split the log. Every read in this playbook folds
101
+ the lineage, so a dispatch a wave session recorded is one this session can
102
+ still audit; that is what `--continues` buys, and it is why the split is safe
103
+ to make (D13, D-266). What it does not buy is a shortcut. A wave session
104
+ opened without `--continues` writes into a log nothing here reads, and the
105
+ verbs below then report a wave that appears never to have run rather than
106
+ failing — which is the same silence, arriving as a green. When you dispatched
107
+ the wave rather than ran it, `smith delegation check <session-id>` is what
108
+ turns that silence into a red: a `wave-runner` that dispatched before opening
109
+ its log, or against the wrong event, is `unverifiable` and exits 1.
110
+
111
+ **Then check that it did.** Once the wave's tasks reach a terminal state,
112
+ `smith wave audit --session <id> --epic <epic>` reads the log back and
113
+ reports the width the wave actually ran at, not the width it was admitted
114
+ at. Ask it with *this* session: it folds the lineage, so it sees the
115
+ dispatches a wave session made under the admission this one wrote. Exit `1`
116
+ is a wave whose tasks never once overlapped — the wave playbook's three
117
+ rules were not followed, and the log will say so long after this session has
118
+ forgotten. Exit `2` means the wave was admitted and no dispatch was recorded
119
+ under it, which is the wrong `--state-dir`, or a wave session opened without
120
+ `--continues`, rather than a stalled run. Ask before reporting the wave
121
+ done; a green gate on serialized work is exactly the outcome the two
122
+ playbooks are written to prevent.
123
+
124
+ 11. Repeat until every task in the live plan version is
125
+ `completed`/`superseded`/`waived`. Dispatch the **`planner`** again for
126
+ the epic verdict against acceptance criteria — gaps found → a NEW
127
+ `plan-v(n+1)` with inferred tasks (never a live-graph mutation,
128
+ architecture §12), auto-scheduled at confidence ≥0.8 else parked for an
129
+ operator tick. **Every new version is ingested before the loop resumes
130
+ on it**, whichever step cut it:
131
+
132
+ ```bash
133
+ smith plan ingest factory/specs/active/<epic>/plan-v<n+1>.json \
134
+ --session <session-id> --plan-version <n+1> --causal-parent <event-id>
135
+ ```
136
+
137
+ Cutting a version writes `plan-version-created` and moves the findings
138
+ it cites; it does not write the tasks it added. Until they are ingested
139
+ (`/bs plan` step 7) the new task is on no board and carries no claims,
140
+ budget or edges in the DB, and the first gate event to name its id
141
+ creates it as a bare row (D-46, D-254). It is idempotent: the tasks the
142
+ version carried forward are skipped, and the output's `added` should
143
+ equal what the amendment's `diff` added.
144
+ 12. Run the full check suite **at the project root, on the assembled
145
+ branch**. Every gate up to here ran inside a task worktree, so every
146
+ green you have so far is a green about a worktree — the envkit epic
147
+ shipped six green lint gates on a branch whose `pnpm lint` exited 1
148
+ (D-42). Check out `smith/<epic>/integration` in the project first (this
149
+ command refuses to move your working tree, and refuses a dirty one):
150
+
151
+ ```bash
152
+ smith integration check --epic <epic> --project <project-dir> \
153
+ --checks <checks.json> \
154
+ --session <session-id> --plan-version <n> --causal-parent <event-id>
155
+ ```
156
+
157
+ Exit 1 means the assembled branch is broken. Raise a finding, fix it as
158
+ a task, and run this again — it pins its result to the head sha, so any
159
+ merge after it lands makes the record stale and the verdict below will
160
+ say so.
161
+
162
+ `--plan-version <n>` is the live version, here and on every record steps
163
+ 13-16 write. The envelope leaves the flag optional, and an omitted one
164
+ stamps the record `plan_version: 1` — so on an epic that amended, the
165
+ check, the review and the close all claim to have verified a plan that
166
+ no longer exists. csb-signing-policy-1 closed at v3 with its integration
167
+ check and its closing spec review both stamped v1
168
+ (`csb-signing-policy-1-2026-09-11#197`, `#202`); the goal check, passed
169
+ the flag, says v3. Read the version off `smith plan ingest`'s receipt and
170
+ pass it every time.
171
+ 13. Dispatch the **`spec-reviewer`** again — this time against the code.
172
+ The pre-code review at `/bs plan` step 3 read the spec against nothing;
173
+ this one reads it against the assembled branch, which is the only reading
174
+ that can see a criterion the finished code proves wrong. The envkit epic
175
+ deadlocked on exactly that: a correct S2 whose fix criterion 3 mandated
176
+ and criterion 1 forbade (D-33). It writes to a close-specific path so it
177
+ cannot clobber the pre-code artifact; hand that file — each item naming the
178
+ `criterion_ref` it is against — to:
179
+
180
+ ```bash
181
+ smith epic spec-review --epic <epic> --project <project-dir> \
182
+ --plan factory/specs/active/<epic>/plan-vN.json \
183
+ --reviewed-by spec-reviewer \
184
+ [--evidence state/results/<epic>.spec-review-close-vN.json] \
185
+ --session <session-id> --plan-version <n> --causal-parent <event-id>
186
+ ```
187
+
188
+ Its `dispatch_decision` — and step 14's, and the planner's when it
189
+ renders a verdict — goes against the reserved `<epic>/integration` ref,
190
+ never a `<epic>/spec-review-rN` you coin for the occasion
191
+ ([`dispatch.md`](dispatch.md), "Dispatch a role with no claims against
192
+ the reserved ref").
193
+
194
+ `profile.closingSpecReview` decides whether this step runs at all:
195
+ `always` (`huge`, `medium`), or `when-plan-amended` (`small`) — a closing
196
+ review reads the spec against the code that now exists, which earns its
197
+ call when the spec moved. An epic that ran its whole plan without a
198
+ `plan amend` did not move. When the tier skips it, say so to the operator;
199
+ a skipped review and a clean one are not the same fact either.
200
+
201
+ When it does run, run it even when the review is clean — "ran and found
202
+ nothing" and "never ran" are different facts, and the verdict below
203
+ distinguishes them. It
204
+ exits 0 even when it raises findings: what it found blocks the plan, not
205
+ this command. A spec finding is unwaivable at S1/S2 and no task's diff can
206
+ contain the fix, so the answer is `smith plan amend --plan … --findings …
207
+ --rationale … --sites …` (§6a of the operator guide). `--sites` is every
208
+ place that shape occurs, not only the file the finding was reported
209
+ against — answer it before writing the changes, because it is the question
210
+ that decides how much the amendment fixes (D-123). It cuts plan v(n+1):
211
+ ingest it (step 11's `smith plan ingest`), go back to step 11 with the new
212
+ version, and re-run this review against the branch that results. Never
213
+ record a spec defect as a coder failure; that is
214
+ the deadlock this step exists to end.
215
+ 14. Check the plan against the **goal it was cut from**. Every gate up to
216
+ here reads text the planner produced, so all of them go green on a plan
217
+ that decomposes the wrong problem. This one reads the `- goal:` line of
218
+ the roadmap milestone that owns the epic — the one reference the planner
219
+ did not write. Get the clause list first (read-only, no event):
220
+
221
+ ```bash
222
+ smith epic goal --epic <epic>
223
+ ```
224
+
225
+ Hand those clauses and the live plan to a **`spec-reviewer`** session —
226
+ a fresh one, never the planner's, and never the same dispatch as step 13:
227
+ `smith dispatch check` refuses to let one dispatch answer for both.
228
+ Dispatch it *after* step 13's record is written, not alongside it — a
229
+ dispatch that predates the previous record has already answered for that
230
+ one, so two sessions fired up front leave this record unaccounted for and
231
+ `smith dispatch check` reports that record `unverifiable`. Take back one
232
+ verdict per clause, in the goal's order, and record it:
233
+
234
+ ```bash
235
+ smith epic goal-check --epic <epic> \
236
+ --plan factory/specs/active/<epic>/plan-vN.json \
237
+ --coverage state/results/<epic>.goal-coverage-vN.json \
238
+ --checked-by spec-reviewer \
239
+ --session <session-id> --plan-version <n> --causal-parent <event-id>
240
+ ```
241
+
242
+ `covered` must name live plan task ids; `out-of-scope` must give a reason,
243
+ and that reason is quoted back to the epic judge. `uncovered` mints an
244
+ S2-major spec finding against the plan file — no task diff can contain
245
+ that fix, so the answer is `smith plan amend`, which cuts v(n+1) and sends
246
+ you back to step 11 — through `smith plan ingest`, as in step 13. The
247
+ command exits 0 even when it raises findings.
248
+
249
+ **It refuses (`cli.no-epic-goal`) when the owning milestone states no
250
+ goal, and the epic then cannot close.** That is deliberate — there is no
251
+ `not-required` escape hatch, because "no goal is stated" is the absence of
252
+ the only text this gate can grade against. Fix the roadmap, do not work
253
+ around it: give the milestone a `- goal:` line, or add the epic to the
254
+ `- epics:` list of one that has it. Tell the operator you edited the
255
+ roadmap; it is a scope surface (`/bs plan` step 8).
256
+ 15. Before opening the PR, run the epic-final verdict — the last
257
+ `quorum_triggers` gate, and the one that decides whether this epic is
258
+ integrable at all:
259
+
260
+ ```bash
261
+ smith epic verdict --epic <epic> --project <project-dir> \
262
+ --session <session-id> --plan-version <n> --causal-parent <event-id>
263
+ ```
264
+
265
+ Mechanical oracles first: non-terminal tasks, open blocking findings, a
266
+ missing/failed/stale integration-root check, a missing/stale closing spec
267
+ review, or a missing/stale spec-vs-goal check return `hold` (exit 1)
268
+ without spending a judge call. `--project` is required — the verdict
269
+ reads the integration branch head to decide whether the records from
270
+ steps 12, 13 and 14 still cover it. On `hold`, do not open the PR — report
271
+ the `blockers` to the operator and go back to step 11.
272
+
273
+ Once the oracles pass, the judge call is real money when an external
274
+ provider is `active` in `crosscheck.yml`: codex spends the operator's
275
+ ChatGPT quota, DeepSeek spends prepaid credit, and step 16 runs the
276
+ verdict again before it writes, so a clean close is two spends, not one
277
+ (csb-signing-policy-1 wrote two `quorum-decision`s against its
278
+ integration ref, `#210` from the verdict and `#215` from the close).
279
+ Nothing in the `quorum-decision` payload names the purse, so say which
280
+ one it is to the operator before the first call, not after the second.
281
+ 16. Record the close. The verdict above is a read-only probe and writes
282
+ nothing; `epic close` is what makes it a fact in the log (D-43):
283
+
284
+ ```bash
285
+ smith epic close --epic <epic> --project <project-dir> \
286
+ --session <session-id> --plan-version <n> --causal-parent <event-id>
287
+ ```
288
+
289
+ It re-runs the verdict, then emits `epic-closed` on `go` and refuses
290
+ (exit 1, no event) on `hold`. **Never pass `--override-rationale`
291
+ yourself** — closing over a hold is the operator's call; ask for it and
292
+ quote the blockers. If the epic was cut by `/bs audit`
293
+ ([`audit.md`](audit.md) step 10), follow the close with
294
+ `smith audit resolve <project-dir> --epic <epic> --session <session-id> --causal-parent <event-id>`
295
+ so every finding the epic carried is marked `fixed` in the project's
296
+ audit store — the store, not the epic, is where a finding's life ends.
297
+ 17. Open **one integration PR per epic** with the scribe-written body
298
+ (`/bs report`'s playbook, [`report.md`](report.md)) — screenshots, test
299
+ results, reviewer verdict, waivers granted, timeline link. The head is
300
+ `smith/<epic>/integration`. The base is the target repo's `main` unless
301
+ the epic was cut from another epic's integration branch, in which case
302
+ the PR stacks on that branch and the operator merges the parent's PR
303
+ first — csb-signing-policy-1 was cut from `smith/csb-audit-1/integration`
304
+ and opened PR #54 against it, behind PR #53; once the parent has merged
305
+ and its branch is deleted, GitHub retargets the child to `main`. The
306
+ operator reviews on GitHub; this session never merges to `main`
307
+ (`docs/standards/guardrails.md`).
308
+
309
+ Before the first push, read the authorship the branch is about to make
310
+ public — `git log --format=%ae <base>..HEAD | sort -u` — and stop on any
311
+ address that is not a noreply one; a coder's worktree commit carries
312
+ whatever `user.email` its session inherited.
313
+
314
+ Then write the PR down. No verb records it for you: nothing in `src/`
315
+ opens a PR — `gh` runs in your session, never in the factory — so a
316
+ timeline that ends at `epic-closed` never shows what the close was for.
317
+ Append it by hand, under the reserved ref and the live plan version:
318
+
319
+ ```bash
320
+ 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>}}'
321
+ ```
322
+
323
+ The receipt says whether the timeline reads the type; it does, and it
324
+ titles the row `<owner>/<repo>#<n> (<head> → <base>)` — the one line
325
+ that shows a stacked PR's base before anyone merges in the wrong order.
@@ -0,0 +1,54 @@
1
+ # `/bs status` — live agent count, budget burn, epic phase
2
+
3
+ Two questions, in this order: **was anything watching while no session was
4
+ open**, and **what does the log say now**. Step 2 alone has always answered
5
+ the second; it cannot answer the first, because a session that folds the log
6
+ on demand knows only what is in the log, never whether anyone was reading it.
7
+
8
+ ## 1. Ask the watcher
9
+
10
+ `smith daemon status [--dir <dir>]`. It prints JSON and **exits 1 whenever a
11
+ current daemon is not watching** — that is the health check doing its job,
12
+ not a command that failed, so never report the non-zero exit as an error.
13
+ Three readings:
14
+
15
+ - `running: false` — nothing holds the lock. A fine steady state if the
16
+ operator never started one (`smith daemon start` if they want one): say so
17
+ in a clause and go to step 2, which folds the log live anyway. What you
18
+ must not do is render `lastTick` as though it were current.
19
+ - `stale: true` — a daemon holds the lock and has published nothing for
20
+ three of its own intervals. This is worse than stopped, because the pid
21
+ file makes the box look tended while something is wedged mid-tick. Lead
22
+ with it, say how long the silence has run (`reportAgeSeconds`, or "never
23
+ published" when it is `null`), and point the operator at
24
+ `<dir>/daemon.log`.
25
+ - `running: true, stale: false` — the numbers are fresh. Say how fresh
26
+ (`reportAgeSeconds`) rather than implying "now".
27
+
28
+ When there is a `lastTick`, render its triage split before the digest below,
29
+ because it is the only record of what happened while nobody was watching:
30
+ `attention` open findings, of which `newAttention` are ones no earlier tick
31
+ had seen; then `autoAdmitted` against `operatorHeld` — the second is the
32
+ operator's actual queue, the first is what the scheduler already admitted on
33
+ its own and needs no decision.
34
+
35
+ ## 2. Fold the log
36
+
37
+ `smith stats overview [--session <id>]`. Render the JSON as a short digest,
38
+ field for field — never invent numbers the query didn't return:
39
+
40
+ - **Live agents**: `liveAgents` grouped by role/model/provider,
41
+ `liveAgentCount` as the headline (with `liveAgentCountDelta5m` as a
42
+ ▲/▼ note).
43
+ - **Budget**: `tokensByEpic`, called out as "X% of the 4,000,000-token epic
44
+ cap" (`budgets.yml`) — flag any epic ≥70% (`budgets.yml` `alarm_ratio`).
45
+ `tokensByEpic` counts recorded spend only, so it is a floor: say "at least
46
+ X%", and run `smith budget alarm <session>` for the projection that prices
47
+ the dispatches nothing recorded a Result for.
48
+ - **In flight**: `epicsInFlight`, `recentDispatches` (last 10).
49
+ - **Alerts**: `alerts.escalations` and `alerts.pendingWaivers` — call these
50
+ out first if non-zero, they're what the operator came here to see.
51
+ - **Milestones**: `milestoneProgress` — one line each, `%` complete.
52
+
53
+ If the UI is already running, just point the operator at it instead of
54
+ re-rendering the same data as text.
@@ -0,0 +1,12 @@
1
+ # `/bs ui` — serve the local dashboard
2
+
3
+ `smith ui serve [--port 4680] [--db state/smith.db] [--state-dir …]
4
+ [--roadmap-path …]`. If it errors `ui.not-built`, run `pnpm build:ui`
5
+ first (builds `ui/server/dist` + `ui/dist`), then retry. Print the local
6
+ URL (`http://127.0.0.1:<port>`).
7
+
8
+ `--roadmap-path` travels with `--db`/`--state-dir`: the read path
9
+ re-projects each session on the first request, and that rebuilds the
10
+ milestones table from the roadmap file it was given — unset, it falls
11
+ back to black-smith's own `factory/specs/roadmap.md`, so a dashboard
12
+ pointed at another project's db would show this repo's roadmap.
@@ -0,0 +1,13 @@
1
+ # `/bs waivers` — answer the pending waiver batch
2
+
3
+ 1. `smith waivers pending <epic> --session <session-id>` — every S3/S4
4
+ finding for the epic with no waiver decision yet (a decided fingerprint
5
+ never resurfaces).
6
+ 2. Present the whole batch to the operator as **one** question ("ignore
7
+ these?") — never ask per-finding (architecture §11, `severity.yml`).
8
+ 3. Write the answers to `decisions.json`
9
+ (`Array<{fingerprint, decision: "granted"|"denied", operatorNote}>`) and
10
+ run `smith waivers apply decisions.json --session ... --plan-version ...
11
+ --causal-parent ... --actor operator`. S1/S2 findings are rejected by
12
+ the command itself if attempted — they go through the escalation ladder
13
+ instead, never a waiver.