@juzser/blacksmith 0.1.0 → 0.2.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.
- package/.claude/agents/coder.md +9 -3
- package/.claude/agents/grader.md +13 -5
- package/.claude/agents/merger.md +2 -0
- package/.claude/agents/planner.md +22 -1
- package/.claude/agents/researcher.md +3 -1
- package/.claude/agents/reviewer.md +9 -2
- package/.claude/agents/security-reviewer.md +15 -7
- package/.claude/agents/spec-reviewer.md +12 -3
- package/.claude/agents/tester.md +5 -2
- package/.claude/agents/uiux.md +10 -3
- package/.claude/agents/verifier.md +8 -3
- package/.claude/skills/bs/SKILL.md +119 -0
- package/.claude/skills/bs/audit.md +252 -0
- package/.claude/skills/bs/dispatch.md +394 -0
- package/.claude/skills/bs/lessons.md +73 -0
- package/.claude/skills/bs/mcp.md +29 -0
- package/.claude/skills/bs/new.md +35 -0
- package/.claude/skills/bs/plan.md +172 -0
- package/.claude/skills/bs/report.md +129 -0
- package/.claude/skills/bs/run.md +327 -0
- package/.claude/skills/bs/status.md +54 -0
- package/.claude/skills/bs/ui.md +19 -0
- package/.claude/skills/bs/waivers.md +13 -0
- package/.claude/skills/bs/wave.md +267 -0
- package/README.md +136 -79
- package/factory/orchestrator/dist/actors.js +4 -3
- package/factory/orchestrator/dist/agents-registry.js +96 -19
- package/factory/orchestrator/dist/artifacts.js +36 -4
- package/factory/orchestrator/dist/budgets.js +7 -5
- package/factory/orchestrator/dist/claims.js +27 -1
- package/factory/orchestrator/dist/cli.js +196 -27
- package/factory/orchestrator/dist/crosscheck.js +11 -0
- package/factory/orchestrator/dist/daemon.js +30 -5
- package/factory/orchestrator/dist/db/projector.js +272 -90
- package/factory/orchestrator/dist/db/queries.js +83 -14
- package/factory/orchestrator/dist/db/schema.js +7 -0
- package/factory/orchestrator/dist/events.js +88 -4
- package/factory/orchestrator/dist/harness.js +516 -0
- package/factory/orchestrator/dist/impact.js +76 -5
- package/factory/orchestrator/dist/judges.js +49 -8
- package/factory/orchestrator/dist/lessons.js +4 -4
- package/factory/orchestrator/dist/mcp.js +4 -3
- package/factory/orchestrator/dist/paths.js +169 -15
- package/factory/orchestrator/dist/plan.js +61 -1
- package/factory/orchestrator/dist/planQuorum.js +36 -8
- package/factory/orchestrator/dist/providers/api-transport.js +62 -5
- package/factory/orchestrator/dist/providers/cli-transport.js +7 -73
- package/factory/orchestrator/dist/providers/index.js +1 -0
- package/factory/orchestrator/dist/providers/schema-validate.js +58 -7
- package/factory/orchestrator/dist/providers/types.js +5 -0
- package/factory/orchestrator/dist/roadmap.js +3 -3
- package/factory/orchestrator/dist/run-cli.js +130 -0
- package/factory/orchestrator/dist/runner.js +264 -0
- package/factory/orchestrator/dist/scaffold.js +3 -2
- package/factory/orchestrator/dist/spawn.js +84 -0
- package/factory/orchestrator/dist/stack.js +10 -2
- package/factory/orchestrator/dist/templateFragments.js +178 -0
- package/factory/orchestrator/dist/usage.js +30 -9
- package/factory/orchestrator/dist/waveNext.js +42 -14
- package/factory/orchestrator/dist/waveSchedule.js +7 -1
- package/factory/orchestrator/dist/workroot.js +113 -0
- package/factory/orchestrator/drizzle/0011_findings_spec_ref.sql +3 -0
- package/factory/orchestrator/drizzle/meta/_journal.json +7 -0
- package/factory/policies/budgets.yml +7 -4
- package/factory/policies/crosscheck.yml +10 -8
- package/factory/policies/harness.yml +82 -0
- package/factory/policies/lessons.md +122 -0
- package/factory/policies/severity.yml +1 -1
- package/factory/policies/taxonomy.yml +6 -2
- package/factory/specs/roadmap.md +154 -0
- package/factory/specs/schema/task-spec.schema.json +5 -0
- package/package.json +10 -7
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# `/bs run` — the wave playbook
|
|
2
|
+
|
|
3
|
+
Steps 2-10 of the loop [`run.md`](run.md) opens at step 1. That file is
|
|
4
|
+
the epic tier: it admits one wave and closes the epic. This file is the
|
|
5
|
+
wave tier — worktrees to merge queue, one wave — and it is meant to be
|
|
6
|
+
thrown away when the wave lands.
|
|
7
|
+
|
|
8
|
+
**Read the console first.** `SKILL.md` and [`dispatch.md`](dispatch.md)
|
|
9
|
+
bind every dispatch below: the project directory is an answer you ask for
|
|
10
|
+
rather than a path anything here knows, every write carries
|
|
11
|
+
`--session <id> --plan-version <n> --causal-parent <event-id>`, every
|
|
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.
|
|
15
|
+
|
|
16
|
+
The step numbers are the loop's own. They start at 2 because step 1 — which
|
|
17
|
+
tasks, and how wide — was answered before you were handed anything, and they
|
|
18
|
+
stop at 10 because the epic's own gates resume there. Nothing in this file
|
|
19
|
+
decides what the next wave is, and nothing in it closes an epic.
|
|
20
|
+
|
|
21
|
+
## You own the log for what you dispatch
|
|
22
|
+
|
|
23
|
+
This playbook runs in one of two shapes, and is written to be correct in
|
|
24
|
+
both:
|
|
25
|
+
|
|
26
|
+
- **Inline**, in the epic session, when the wave is small enough that the
|
|
27
|
+
epic's window can carry it. Nothing changes: the session id you were
|
|
28
|
+
handed is the one you write to.
|
|
29
|
+
- **In a session of its own** when it is not. Open one with `smith session
|
|
30
|
+
start <wave-id> --continues <event-id>` and write every dispatch below
|
|
31
|
+
there. The epic session then carries the admission and the result rather
|
|
32
|
+
than every turn in between — which is the point, because an epic outlives
|
|
33
|
+
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
|
+
|
|
36
|
+
Which `<event-id>` depends on how you were started, and the difference is
|
|
37
|
+
not cosmetic — it is the edge an audit walks:
|
|
38
|
+
|
|
39
|
+
- **The epic ran you** and you opened a wave session yourself: continue from
|
|
40
|
+
the epic event that admitted this wave.
|
|
41
|
+
- **You were dispatched as a `wave-runner`**: continue from the
|
|
42
|
+
`dispatch_decision` that dispatched you, the event id handed to you with
|
|
43
|
+
the wave. `smith delegation check` resolves your session by matching a
|
|
44
|
+
`session-start`'s `causal_parent` against that dispatch, and it is the
|
|
45
|
+
only thing that proves the dispatches you are about to write are your
|
|
46
|
+
own log's rather than an agent talking about itself. Continue from
|
|
47
|
+
anything else and the check reports you as `unverifiable` — not as a
|
|
48
|
+
pass — however well the wave ran. Open it **before** your first dispatch,
|
|
49
|
+
for the same reason: a grant is earned by owning the log first.
|
|
50
|
+
|
|
51
|
+
Either way, one rule holds: **the log you write is the log the epic reads**,
|
|
52
|
+
and `--continues` is the whole of what makes that true. Every deciding read
|
|
53
|
+
at the epic tier folds the lineage rather than one session — `wave audit`,
|
|
54
|
+
`budget alarm`, `tester check`, `judge outstanding`, `escalation check`,
|
|
55
|
+
`dispatch check`, `delegation check` — so a dispatch recorded in a wave
|
|
56
|
+
session is visible from the epic session that admitted it, and is *not*
|
|
57
|
+
visible from a sibling wave's. Open a wave session without `--continues` and
|
|
58
|
+
none of those verbs can see your work: they will not error, they will answer
|
|
59
|
+
about a wave that appears never to have run.
|
|
60
|
+
|
|
61
|
+
**What you are handed**, and carry through every command below: the project
|
|
62
|
+
directory, the epic id, the live plan path, the admitted task ids, the
|
|
63
|
+
effort `profile` the epic resolved once at the top of its run, the session
|
|
64
|
+
id you write to, and the event id to hang the first dispatch off as
|
|
65
|
+
`--causal-parent`.
|
|
66
|
+
|
|
67
|
+
**What you hand back** when the wave lands: each task's terminal state, the
|
|
68
|
+
merge-queue outcome, every finding raised with its severity, any escalation
|
|
69
|
+
that reached the operator, and — if you opened one — the wave session id and
|
|
70
|
+
its last event id, so the epic session goes on chaining from where you
|
|
71
|
+
stopped.
|
|
72
|
+
|
|
73
|
+
## Run the whole wave at once
|
|
74
|
+
|
|
75
|
+
The wave is the unit of work, not the task. Three rules follow, and they are
|
|
76
|
+
the difference between a factory and a queue with extra steps:
|
|
77
|
+
|
|
78
|
+
- **One message, many dispatches.** When steps 3–7 reach a phase, issue
|
|
79
|
+
that phase for *every* task in the wave as parallel tool calls in a
|
|
80
|
+
single message. Five coders in one message is five agents working; five
|
|
81
|
+
messages of one coder is five agents waiting. The worktrees are
|
|
82
|
+
disjoint by construction — that is what step 1's `wave check` certified
|
|
83
|
+
before you were handed the wave — so there is nothing for them to
|
|
84
|
+
collide over.
|
|
85
|
+
- **No lockstep barrier.** Each task walks 3 → 7 at its own pace. Do not
|
|
86
|
+
hold a finished coder until its neighbours finish theirs; dispatch its
|
|
87
|
+
tester the moment it returns. Waiting for the slowest task at every
|
|
88
|
+
phase boundary costs the wave the difference between its longest task
|
|
89
|
+
and the sum of its phases, which is most of what parallelism buys.
|
|
90
|
+
- **A blocked task blocks itself.** Gate outcome `blocked` bounces to
|
|
91
|
+
*that* task's coder (step 9) while its neighbours carry on. One task
|
|
92
|
+
escalating to the operator does not stop the other four; report it and
|
|
93
|
+
keep the wave moving.
|
|
94
|
+
|
|
95
|
+
The single place the wave rejoins is step 10's merge queue, which is
|
|
96
|
+
serial on purpose — `smith queue run` rebases one task at a time onto
|
|
97
|
+
`smith/<epic>/integration` and runs the cumulative tests between. Merging
|
|
98
|
+
is the one thing that cannot be done in parallel, and it is already the
|
|
99
|
+
one thing this playbook never asks you to.
|
|
100
|
+
|
|
101
|
+
## The steps
|
|
102
|
+
|
|
103
|
+
2. Per admitted task, and for all of them together: `smith worktree create
|
|
104
|
+
<project-dir> <epic> <task-id>`.
|
|
105
|
+
3. Pre-code, if the task needs it: dispatch `researcher` for an unknown, or
|
|
106
|
+
`uiux` (`.claude/agents/uiux.md`) for any UI-affecting acceptance
|
|
107
|
+
criterion — before the coder starts, not after.
|
|
108
|
+
- `profile.preCodeResearch` gates the researcher: `when-needed` is the
|
|
109
|
+
line above, `never` (`small`) means the coder reading the repo *is* the
|
|
110
|
+
brief. An epic that genuinely cannot start without a research brief is
|
|
111
|
+
not a `small` epic — raise the tier rather than dispatching anyway.
|
|
112
|
+
- `profile.preCodeUiux` gates the uiux spec, and is `when-ui-criterion` at
|
|
113
|
+
every tier: no tier ships a UI change without a spec to review it
|
|
114
|
+
against. It is listed as a knob so the answer is asked for rather than
|
|
115
|
+
assumed, not because a tier can turn it off.
|
|
116
|
+
- A returned brief goes through `smith research check --brief <path>`
|
|
117
|
+
before it is attached to a task spec ("Fetched and quoted text goes into
|
|
118
|
+
a prompt fenced" above). Exit 1 sends it back; on exit 0, carry
|
|
119
|
+
`recommendation.provenance` into the coder's prompt so the task knows
|
|
120
|
+
whether its research rests on this repo or on a fetched page. Any raw
|
|
121
|
+
fetched text you quote alongside it is wrapped with `smith prompt wrap`.
|
|
122
|
+
4. Dispatch **`coder`** (`.claude/agents/coder.md`) in that worktree.
|
|
123
|
+
Token/diff caps (`budgets.yml`: 150k tokens, 400 diff lines) and YAGNI
|
|
124
|
+
are the coder's own constraints — don't restate them here, the template
|
|
125
|
+
does. What the template does not read is the schema, so when the spec
|
|
126
|
+
carries `keeps_exports`, restate the promise in the dispatch in one line:
|
|
127
|
+
the file list, and "keep every existing export's name and declaration;
|
|
128
|
+
adding is fine".
|
|
129
|
+
5. Dispatch **`tester`** (`.claude/agents/tester.md`) for missing unit
|
|
130
|
+
coverage and epic-level e2e/screenshots.
|
|
131
|
+
- Then the **uiux visual pass**, but only when all three hold: the task
|
|
132
|
+
is UI-affecting, the tester actually returned screenshot artifacts, and
|
|
133
|
+
step 3 wrote a uiux spec for this task (agent-interviews.md N-6). Miss
|
|
134
|
+
any one and skip it — say so; a "visual pass" over screenshots that do
|
|
135
|
+
not exist is worse than no pass. Dispatch it with the spec path and the
|
|
136
|
+
image paths and **nothing else**: no diff, no components, no test code.
|
|
137
|
+
The pass is a judgment about the rendered screen, and a uiux session
|
|
138
|
+
that reads the diff ends up reviewing intent, which the reviewer
|
|
139
|
+
already covers.
|
|
140
|
+
6. Dispatch **`grader`** (`.claude/agents/grader.md`) — bounded rubric
|
|
141
|
+
loop, `profile.graderRounds` rounds (2 at `huge`, 1 below; never more
|
|
142
|
+
than 2, agent-constraints.md "grader (v3)"), before any gate
|
|
143
|
+
runs; it never decides pass/fail itself. At one round the grader scores
|
|
144
|
+
once and a failed criterion is the coder's bounce, not a re-grade. Its result file is an **input to
|
|
145
|
+
step 7**, not a note to the operator: pass it as `--grader` or the rubric
|
|
146
|
+
gates nothing (D-34).
|
|
147
|
+
7. Run the gate pipeline:
|
|
148
|
+
`smith gate run <task-id> --worktree <dir> --checks checks.json --result
|
|
149
|
+
result.json --grader state/results/<task-id>.grader-r<round>.json
|
|
150
|
+
--findings findings.json --session ... --plan-version N
|
|
151
|
+
--causal-parent ...` (schema check → grader verdict → tests → coverage
|
|
152
|
+
evidence → findings intake → severity decision,
|
|
153
|
+
`docs/guide/operator-guide/queue-and-gate.md` §5).
|
|
154
|
+
- `--grader` takes step 6's file, latest round. A criterion that came back
|
|
155
|
+
`fail` or `partial` blocks before any check command runs — the outcome is
|
|
156
|
+
`blocked` with `reason: "grader-fail"`, and the payload's
|
|
157
|
+
`failed_criteria` is what you hand back to the coder.
|
|
158
|
+
- Name a check `coverage` and the gate reads
|
|
159
|
+
`coverage/coverage-summary.json` instead of scraping the text table,
|
|
160
|
+
which hides every file at 100% — including the file the task exists to
|
|
161
|
+
add (D-40). `blocked` with `reason: "coverage-evidence"` means the run
|
|
162
|
+
produced no per-file number for a file this task's claims name; fix the
|
|
163
|
+
reporter or the include glob, not the code. Ask the same question
|
|
164
|
+
outside a gate run with `smith coverage check <worktree-dir> --plan
|
|
165
|
+
<plan.json> --task <task-id>` (§5c).
|
|
166
|
+
- Findings come from dispatching **`reviewer`**
|
|
167
|
+
(`.claude/agents/reviewer.md`, fresh context, read-only) then
|
|
168
|
+
**`verifier`** (`.claude/agents/verifier.md`, adversarial refute
|
|
169
|
+
mandate) — never skip the adversarial stage on an S1/S2 finding
|
|
170
|
+
(`crosscheck.yml` `asymmetric_roles`). Which findings that covers is
|
|
171
|
+
`profile.verifierSeverities`, plus a
|
|
172
|
+
`profile.verifierS3SpotCheckRatio` sample of S3-minor: `[S1, S2]` + 0.2
|
|
173
|
+
at `huge`, `[S1, S2]` + 0 at `medium`, `[S1]` + 0 at `small`. The S3
|
|
174
|
+
sample is a calibration measurement on the reviewer, so it is the first
|
|
175
|
+
thing a cheaper tier drops; `S1-stop-the-line` is in every tier's list
|
|
176
|
+
and no tier may remove it. A finding outside the tier's list still goes
|
|
177
|
+
to the coder — it is verified by the coder's fix, not waived.
|
|
178
|
+
- Dispatch **`security-reviewer`** only when its conditional triggers
|
|
179
|
+
fire — never per-task by default. Ask, do not recall:
|
|
180
|
+
`smith security triggers --task <spec.json>` and dispatch iff
|
|
181
|
+
`dispatchSecurityReviewer` is true ("Dispatching the security-reviewer"
|
|
182
|
+
above).
|
|
183
|
+
- Every judge dispatched in steps 5–7 — uiux visual pass, grader,
|
|
184
|
+
reviewer, verifier, security-reviewer — is bracketed by
|
|
185
|
+
`smith worktree fingerprint` / `smith worktree verify`
|
|
186
|
+
("Fingerprint the worktree around every judge" above). Exit 1 discards
|
|
187
|
+
that judge's result; it does not become a finding against the coder.
|
|
188
|
+
- Each of those is also bracketed by `smith judge dispatch` /
|
|
189
|
+
`smith judge report` ("Declare each judge's artifact before you dispatch
|
|
190
|
+
it" above). `smith judge outstanding --task <task-id>` exits 1 while any
|
|
191
|
+
judge still owes its file — re-poke it and report before running the
|
|
192
|
+
gate, because the gate now refuses to score with a non-empty outstanding
|
|
193
|
+
set (`reason: judges-outstanding`) rather than reading a silent judge as
|
|
194
|
+
zero findings.
|
|
195
|
+
- The gate scores tests it did not write, so once it has run, make the log
|
|
196
|
+
say who did: `smith tester check <session-id> --task <task-id>`
|
|
197
|
+
(`crosscheck.yml` `role_isolation`, operator-guide/dispatch-audits.md §2d). Exit 1
|
|
198
|
+
means no `tester` dispatch precedes this task's `testgate-result`, the
|
|
199
|
+
coder and tester dispatches share one `agent_id`, or the answer is
|
|
200
|
+
unknowable. A
|
|
201
|
+
coder that writes and runs its own tests grades itself and every gate
|
|
202
|
+
downstream still goes green — step 5 is what prevents that, and this is
|
|
203
|
+
the log checking that step 5 happened.
|
|
204
|
+
8. Every dispatch in steps 3–7 carries the compiled lessons block for that
|
|
205
|
+
role (`smith lessons for-dispatch <role> --plan … --task …`), every
|
|
206
|
+
worktree dispatch also carries the open-findings block for that task
|
|
207
|
+
(`smith findings for-dispatch --plan … --task …`) — both under "Dispatch
|
|
208
|
+
contract" above — and each is a `dispatch_decision` event with a `causal_parent`
|
|
209
|
+
chaining back to the prompt/decision that caused it (architecture §7) —
|
|
210
|
+
this is the timeline, not optional bookkeeping.
|
|
211
|
+
9. Gate outcome `blocked` → bounce to the coder on the **same branch**.
|
|
212
|
+
After 2 failed rounds on the same task, escalate model tier
|
|
213
|
+
automatically (mid → frontier, logged — `budgets.yml`
|
|
214
|
+
`escalation_ladder`); after 3, escalate to the operator. Never skip a
|
|
215
|
+
rung, never loop past one. Count the rounds from this task's
|
|
216
|
+
`dispatch_decision` events, not from the agent's own account of itself
|
|
217
|
+
— see "Round counting and escalation" above. Then have the log check
|
|
218
|
+
you: `smith escalation check <session-id> --task <task-id>`, which
|
|
219
|
+
exits 1 if the rung you just climbed is not evidenced.
|
|
220
|
+
10. Gate outcome `pass`/`pass-with-waivers-pending` → before admitting,
|
|
221
|
+
ask what the diff did to everyone outside the claims, and whether it
|
|
222
|
+
kept what the spec promised:
|
|
223
|
+
`smith claims impact <worktree-dir> <spec.json>`. Exit 1 means a
|
|
224
|
+
`proven` break — this task removed an export a file outside its claims
|
|
225
|
+
still imports — **or a broken promise**: a file the spec's
|
|
226
|
+
`keeps_exports` names lost an export or changed one's declaration.
|
|
227
|
+
Either is a bounce to the coder, not a merge; quote the `promises`
|
|
228
|
+
entries in the bounce. A `possible` / `signature-changed` entry in
|
|
229
|
+
`breaks` exits 0 and is a note: the scanner reads text, not types
|
|
230
|
+
(operator-guide/wave.md §2). Then admit into the
|
|
231
|
+
merge queue: `smith queue run <epic> --project <project-dir>
|
|
232
|
+
--test-cmd "<cumulative test command>" --tasks tasks.json`. On a
|
|
233
|
+
`rebase-conflict` outcome, dispatch **`merger`**
|
|
234
|
+
(`.claude/agents/merger.md`) with both diffs + specs, and note three
|
|
235
|
+
things the queue's own behaviour forces (agent-interviews.md N-12):
|
|
236
|
+
- **Into the failing task's existing worktree** — a fresh one would not
|
|
237
|
+
reproduce the conflict. The result gives you only `taskId` and
|
|
238
|
+
`conflictingFiles`, so the path is the `worktreeDir` you wrote for
|
|
239
|
+
that task in `tasks.json`; carry it into the prompt yourself.
|
|
240
|
+
- **Claims = the queue's `conflictingFiles`**, verbatim. Anything else
|
|
241
|
+
the merger touches is a claim violation like any other.
|
|
242
|
+
- The queue already ran `git rebase --abort` (`queue.ts`), so the
|
|
243
|
+
merger arrives at a clean tree and **replays** the rebase rather than
|
|
244
|
+
resuming it — and it **never lands the merge**. When it returns
|
|
245
|
+
`resolution: "mechanical"`, re-run `smith queue run`; the queue stays
|
|
246
|
+
the single place a merge can happen.
|
|
247
|
+
|
|
248
|
+
`resolution: "escalated"` — low confidence, or both sides changed the
|
|
249
|
+
same logic — goes to the operator with the side-by-side. Never
|
|
250
|
+
auto-resolve a semantic conflict silently (`worktree.yml`
|
|
251
|
+
`conflict_resolution_ladder`).
|
|
252
|
+
|
|
253
|
+
Once the wave is merged, check the epic's spend — `smith budget alarm
|
|
254
|
+
<session-id> [--epic <epic>]`. Exit 1 means either the epic crossed
|
|
255
|
+
`alarm_ratio` (re-plan the remaining work to fit, or ask the operator —
|
|
256
|
+
extension is always an operator question) **or** the log is too holey to
|
|
257
|
+
rule a crossing out. Read the two numbers as what they are:
|
|
258
|
+
`measuredTokens` is a floor, never the bill (judges return findings, not
|
|
259
|
+
Results, so their tokens are in no event), and `projectedTokens` prices
|
|
260
|
+
every unmeasured dispatch at its `budgets.yml` cap. `unverifiable` with
|
|
261
|
+
`rolesWithoutCap` non-empty is a policy gap, not a spend problem: that
|
|
262
|
+
role has no cap to price it with. `unverifiable` with `tasksOverPrice`
|
|
263
|
+
non-empty means this epic has already been measured spending more on one
|
|
264
|
+
task than the projection charges an unmeasured dispatch, so its caps are
|
|
265
|
+
bounding nothing here (D-188) — the fix is to record the missing spend,
|
|
266
|
+
never to raise the cap until the number fits. Do not report an epic as
|
|
267
|
+
under budget on the strength of `measuredTokens` alone.
|
package/README.md
CHANGED
|
@@ -15,12 +15,15 @@ and hands you exactly one pull request.
|
|
|
15
15
|

|
|
16
16
|
|
|
17
17
|
**[Features](#features) · [Install](#install) · [Using it](#using-it) ·
|
|
18
|
-
[How it works](#how-it-works) · [Safety](#safety) ·
|
|
19
|
-
[Docs](#docs)**
|
|
18
|
+
[How it works](#how-it-works) · [Safety](#safety) ·
|
|
19
|
+
[Dashboard](#the-dashboard) · [Status](#status) · [Docs](#docs)**
|
|
20
20
|
|
|
21
|
-
<
|
|
21
|
+
<pre>
|
|
22
|
+
goal → contracts you sign → one worktree per contract, run in parallel
|
|
23
|
+
→ schema · tests · reviewer · verifier → one pull request you merge
|
|
24
|
+
</pre>
|
|
22
25
|
|
|
23
|
-
<sub>
|
|
26
|
+
<sub>Two touchpoints. Everything between them runs unattended.</sub>
|
|
24
27
|
|
|
25
28
|
</div>
|
|
26
29
|
|
|
@@ -55,12 +58,7 @@ the moment you sign it and versioned in the event log, so a worker cannot
|
|
|
55
58
|
quietly reinterpret the job. No contract, no dispatch.
|
|
56
59
|
|
|
57
60
|
</td>
|
|
58
|
-
<td width="50%"
|
|
59
|
-
</tr>
|
|
60
|
-
|
|
61
|
-
<tr valign="top">
|
|
62
|
-
<td><img src="ui/e2e/__screenshots__/phase-6b/flow-desktop-dark.png" width="100%" alt="Flow page: task cards arranged in three columns labelled Wave 0 (6 tasks), Wave 1 (2 tasks) and Wave 2 (1 task), joined by dashed dependency edges" /></td>
|
|
63
|
-
<td>
|
|
61
|
+
<td width="50%">
|
|
64
62
|
|
|
65
63
|
**Workers that cannot collide**
|
|
66
64
|
|
|
@@ -75,21 +73,6 @@ instead. An edit outside a claim fails the gate rather than reaching the queue.
|
|
|
75
73
|
<tr valign="top">
|
|
76
74
|
<td>
|
|
77
75
|
|
|
78
|
-
**A frontier planner, cheap workers**
|
|
79
|
-
|
|
80
|
-
Planning and judgment go to a frontier model; the bulk of the work goes to
|
|
81
|
-
small, fast tiers, many at once. Every session and every live agent is visible
|
|
82
|
-
with the tier that drew it, and analytics breaks cost down per task, per tier
|
|
83
|
-
and per provider.
|
|
84
|
-
|
|
85
|
-
</td>
|
|
86
|
-
<td><img src="ui/e2e/__screenshots__/phase-6b/sessions-desktop-dark.png" width="100%" alt="Sessions page: two session cards, sess-fixture and sess-multiproject-fixture, joined by dashed edges to six live agent cards labelled coder - small, coder - mid and planner - frontier, each marked working" /></td>
|
|
87
|
-
</tr>
|
|
88
|
-
|
|
89
|
-
<tr valign="top">
|
|
90
|
-
<td><img src="ui/e2e/__screenshots__/phase-6b/kanban-desktop-dark.png" width="100%" alt="Kanban board with Todo, In progress, Reviewing and Blocked columns; cards carry severity chips such as S2-major and agent chips such as coder - mid" /></td>
|
|
91
|
-
<td>
|
|
92
|
-
|
|
93
76
|
**Gates decide what merges, not confidence**
|
|
94
77
|
|
|
95
78
|
Schema check → tests → coverage evidence → a fresh-context reviewer that never
|
|
@@ -98,41 +81,46 @@ the reviewer. S1 stops the line, S2 bounces back to the same branch, S3 batches
|
|
|
98
81
|
into one waiver question per epic. Only S3 and S4 are ever waivable.
|
|
99
82
|
|
|
100
83
|
</td>
|
|
101
|
-
</tr>
|
|
102
|
-
|
|
103
|
-
<tr valign="top">
|
|
104
84
|
<td>
|
|
105
85
|
|
|
106
86
|
**A second opinion from another vendor**
|
|
107
87
|
|
|
108
88
|
The factory grades its own judgment calls against models from a different
|
|
109
89
|
vendor — Codex over its CLI, DeepSeek over its API, beside the native Claude
|
|
110
|
-
judge.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
90
|
+
judge. Both external judges ship `enabled: auto`: they join the quorum on a
|
|
91
|
+
machine that holds the credentials and are skipped on one that does not, so
|
|
92
|
+
which of them you get is a fact about your machine, not about this repo. A
|
|
93
|
+
judge earns its vote in **shadow mode** first — every verdict recorded, none
|
|
94
|
+
of them gating anything — and is promoted only once you have read the numbers.
|
|
115
95
|
|
|
116
96
|
</td>
|
|
117
|
-
<td><img src="ui/e2e/__screenshots__/phase-6b/analytics-desktop-dark.png" width="100%" alt="Analytics page with throughput, cost-per-task, same-mistake-rate and recheck-pass-rate cards, bar charts of cost by model tier and by provider, and a cross-check quorum panel" /></td>
|
|
118
97
|
</tr>
|
|
119
98
|
|
|
120
99
|
<tr valign="top">
|
|
121
|
-
<td><img src="ui/e2e/__screenshots__/phase-6b/lessons-desktop-dark.png" width="100%" alt="Lessons page listing lesson candidates with scope and status chips, each with approve and reject actions" /></td>
|
|
122
100
|
<td>
|
|
123
101
|
|
|
124
102
|
**It learns from its own errors**
|
|
125
103
|
|
|
126
104
|
Errors are classified against a taxonomy, and a scribe distills them into
|
|
127
105
|
lesson candidates you approve or reject. Approved lessons splice into later
|
|
128
|
-
prompts — and the same-mistake rate
|
|
129
|
-
|
|
106
|
+
prompts — and the same-mistake rate tells you whether that is actually
|
|
107
|
+
working. A loop you can audit, not a memory you have to trust.
|
|
108
|
+
|
|
109
|
+
</td>
|
|
110
|
+
<td>
|
|
111
|
+
|
|
112
|
+
**A frontier planner, cheap workers**
|
|
113
|
+
|
|
114
|
+
Planning and judgment go to a frontier model; the bulk of the work goes to
|
|
115
|
+
small, fast tiers, many at once. Every session and every live agent is visible
|
|
116
|
+
with the tier that drew it, and cost breaks down per task, per tier and per
|
|
117
|
+
provider.
|
|
130
118
|
|
|
131
119
|
</td>
|
|
132
120
|
</tr>
|
|
133
121
|
|
|
134
122
|
<tr valign="top">
|
|
135
|
-
<td>
|
|
123
|
+
<td colspan="2">
|
|
136
124
|
|
|
137
125
|
**The log is the source of truth**
|
|
138
126
|
|
|
@@ -142,67 +130,93 @@ the entire database from the log alone. Nothing the factory did exists only in
|
|
|
142
130
|
a chat transcript.
|
|
143
131
|
|
|
144
132
|
</td>
|
|
145
|
-
<td><img src="ui/e2e/__screenshots__/phase-6b/timeline-desktop-dark.png" width="100%" alt="Timeline page: an event list filtered by Prompts, Dispatches, Gate events, Scheduler and Errors chips, showing task-added, user_prompt and session-start entries with timestamps and task ids" /></td>
|
|
146
133
|
</tr>
|
|
147
134
|
</table>
|
|
148
135
|
|
|
149
136
|
**Also in the box**
|
|
150
137
|
|
|
151
|
-
- **Eleven dashboard pages**, dark and light, desktop and mobile: errors by
|
|
152
|
-
taxonomy category, roadmap progress joined to real task and token counts, and
|
|
153
|
-
per-project scoping. It binds to `127.0.0.1` and is read-only — clicking
|
|
154
|
-
nothing there dispatches an agent.
|
|
155
138
|
- **Project scaffolding.** `/bs new <project>` generates a target project from
|
|
156
139
|
the stack you answered for at install time; `/bs mcp` layers an MCP surface
|
|
157
140
|
onto it.
|
|
141
|
+
- **An audit that ends in an epic.** `/bs audit <project-dir>` reads a project
|
|
142
|
+
that already exists on four axes, ranks what it finds, and cuts one epic from
|
|
143
|
+
what you accept.
|
|
158
144
|
- **A factory that extends itself.** New agent roles, policies and taxonomy
|
|
159
145
|
values are data files, not code — see
|
|
160
146
|
[extending](docs/guide/extending.md).
|
|
161
147
|
- **One integration branch per epic**, one pull request at the end, merged by
|
|
162
148
|
you.
|
|
149
|
+
- **A local dashboard**, eleven read-only pages over the same event log —
|
|
150
|
+
optional, and [further down](#the-dashboard).
|
|
163
151
|
|
|
164
152
|
## Install
|
|
165
153
|
|
|
154
|
+
Say **"install Blacksmith"** to a Claude Code session and it does the whole
|
|
155
|
+
thing: [`INSTALL.md`](INSTALL.md) is an executable runbook, and it stops to ask
|
|
156
|
+
before anything that touches your machine.
|
|
157
|
+
|
|
158
|
+
By hand it is one command in a shell:
|
|
159
|
+
|
|
166
160
|
```bash
|
|
167
|
-
|
|
168
|
-
pnpm install --frozen-lockfile
|
|
169
|
-
pnpm run build # tsc -> factory/orchestrator/dist/
|
|
170
|
-
node factory/orchestrator/dist/cli.js --help
|
|
161
|
+
npm i -g @juzser/blacksmith
|
|
171
162
|
```
|
|
172
163
|
|
|
173
|
-
|
|
164
|
+
and two inside Claude Code:
|
|
174
165
|
|
|
175
|
-
```
|
|
176
|
-
|
|
166
|
+
```
|
|
167
|
+
/plugin marketplace add juzser/blacksmith
|
|
168
|
+
/plugin install blacksmith@blacksmith
|
|
177
169
|
```
|
|
178
170
|
|
|
179
|
-
|
|
180
|
-
|
|
171
|
+
Then `smith init` in the project you want it to work on. That creates
|
|
172
|
+
`.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
|
|
174
|
+
that root if you want one home for several projects. You now have `/bs`.
|
|
181
175
|
|
|
182
|
-
**
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
176
|
+
**Both halves are required.** The package
|
|
177
|
+
([`@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
|
|
180
|
+
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
|
|
183
|
+
directory, which the next `npm i` replaces.
|
|
187
184
|
|
|
188
|
-
|
|
185
|
+
<details>
|
|
186
|
+
<summary><b>A clone instead — the whole factory</b></summary>
|
|
189
187
|
|
|
190
|
-
|
|
191
|
-
|
|
188
|
+
A clone is for hacking on Blacksmith itself, and for the two things an install
|
|
189
|
+
does not carry: the dashboard and this repo's own enforcement.
|
|
192
190
|
|
|
193
191
|
```bash
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
git clone https://github.com/juzser/blacksmith.git && cd blacksmith
|
|
193
|
+
pnpm install --frozen-lockfile
|
|
194
|
+
pnpm run build # tsc -> factory/orchestrator/dist/
|
|
195
|
+
bash scripts/check.sh # the gate CI runs; ends in `== PASS ==`
|
|
196
196
|
```
|
|
197
197
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
198
|
+
Open a Claude Code session in the clone and you have `/bs` already, from the
|
|
199
|
+
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
|
|
202
|
+
copies are free to disagree: the plugin's is a pinned checkout of `main`, the
|
|
203
|
+
project's is whatever branch you have out. `claude plugin disable blacksmith`
|
|
204
|
+
settles it.
|
|
205
|
+
|
|
206
|
+
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.
|
|
208
|
+
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
|
|
211
|
+
you about every Bash command would be worse than one that asks about none. The
|
|
212
|
+
rest of that port is scoped in
|
|
213
|
+
[`docs/specs/plugin-port-scope.md`](docs/specs/plugin-port-scope.md).
|
|
214
|
+
|
|
215
|
+
`INSTALL.md` Part 2 is the long form of the clone above: per-platform
|
|
216
|
+
setup (macOS, Debian/Ubuntu, Fedora, Alpine, WSL2), the stack interview,
|
|
217
|
+
troubleshooting, and the known platform gaps stated rather than papered over.
|
|
218
|
+
|
|
219
|
+
</details>
|
|
206
220
|
|
|
207
221
|
## Using it
|
|
208
222
|
|
|
@@ -286,15 +300,16 @@ Day to day, from a Claude Code session opened in this repo:
|
|
|
286
300
|
| `/bs lessons` | Review pending lesson candidates |
|
|
287
301
|
| `/bs report` | Render the scribe's progress digest |
|
|
288
302
|
|
|
289
|
-
|
|
290
|
-
`smith --help` lists them
|
|
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
|
|
305
|
+
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
|
|
307
|
+
playbooks are read from a clone.
|
|
291
308
|
|
|
292
309
|
→ **[The operator loop](docs/guide/operator-loop.md)** — the six steps, in the
|
|
293
310
|
order you meet them.<br />
|
|
294
311
|
→ **[Operator guide](docs/guide/operator-guide.md)** — the same ground with real
|
|
295
|
-
commands and real output
|
|
296
|
-
→ **[The dashboard](docs/guide/dashboard.md)** — what each of the eleven pages
|
|
297
|
-
shows you.
|
|
312
|
+
commands and real output.
|
|
298
313
|
|
|
299
314
|
## How it works
|
|
300
315
|
|
|
@@ -334,6 +349,39 @@ runs, plus branch protection — not by trust. Full rules:
|
|
|
334
349
|
Found a vulnerability? [`SECURITY.md`](SECURITY.md) — report privately, not in
|
|
335
350
|
a public issue.
|
|
336
351
|
|
|
352
|
+
## The dashboard
|
|
353
|
+
|
|
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.
|
|
359
|
+
|
|
360
|
+
<table>
|
|
361
|
+
<tr valign="top">
|
|
362
|
+
<td width="50%"><img src="ui/e2e/__screenshots__/phase-6b/overview-desktop-dark.png" width="100%" alt="Blacksmith Overview page: a 'Needs you' banner reading '1 waiver pending, 1 task escalated', counters for active agents, budget used, epics in flight and alerts, and a 'Now running' list of two live sessions" /><br /><sub><b>Overview</b> — the one screen that asks something of you.</sub></td>
|
|
363
|
+
<td width="50%"><img src="ui/e2e/__screenshots__/phase-6b/task-detail-desktop-dark.png" width="100%" alt="Task detail page for epic-9/task-3 showing a Spec contract card with case, origin, epic, plan version and claims, and an Attempts list naming each agent, provider and outcome" /><br /><sub><b>Task detail</b> — the contract, and every attempt against it.</sub></td>
|
|
364
|
+
</tr>
|
|
365
|
+
<tr valign="top">
|
|
366
|
+
<td><img src="ui/e2e/__screenshots__/phase-6b/flow-desktop-dark.png" width="100%" alt="Flow page: task cards arranged in three columns labelled Wave 0 (6 tasks), Wave 1 (2 tasks) and Wave 2 (1 task), joined by dashed dependency edges" /><br /><sub><b>Flow</b> — waves and the dependency edges that shaped them.</sub></td>
|
|
367
|
+
<td><img src="ui/e2e/__screenshots__/phase-6b/kanban-desktop-dark.png" width="100%" alt="Kanban board with Todo, In progress, Reviewing and Blocked columns; cards carry severity chips such as S2-major and agent chips such as coder - mid" /><br /><sub><b>Kanban</b> — what is moving, and what is stuck and why.</sub></td>
|
|
368
|
+
</tr>
|
|
369
|
+
<tr valign="top">
|
|
370
|
+
<td><img src="ui/e2e/__screenshots__/phase-6b/sessions-desktop-dark.png" width="100%" alt="Sessions page: two session cards, sess-fixture and sess-multiproject-fixture, joined by dashed edges to six live agent cards labelled coder - small, coder - mid and planner - frontier, each marked working" /><br /><sub><b>Sessions</b> — every live agent and the tier that drew it.</sub></td>
|
|
371
|
+
<td><img src="ui/e2e/__screenshots__/phase-6b/analytics-desktop-dark.png" width="100%" alt="Analytics page with throughput, cost-per-task, same-mistake-rate and recheck-pass-rate cards, bar charts of cost by model tier and by provider, and a cross-check quorum panel" /><br /><sub><b>Analytics</b> — cost per task, per tier, per provider, and the same-mistake rate.</sub></td>
|
|
372
|
+
</tr>
|
|
373
|
+
<tr valign="top">
|
|
374
|
+
<td><img src="ui/e2e/__screenshots__/phase-6b/lessons-desktop-dark.png" width="100%" alt="Lessons page listing lesson candidates with scope and status chips, each with approve and reject actions" /><br /><sub><b>Lessons</b> — candidates waiting on your approve or reject.</sub></td>
|
|
375
|
+
<td><img src="ui/e2e/__screenshots__/phase-6b/timeline-desktop-dark.png" width="100%" alt="Timeline page: an event list filtered by Prompts, Dispatches, Gate events, Scheduler and Errors chips, showing task-added, user_prompt and session-start entries with timestamps and task ids" /><br /><sub><b>Timeline</b> — the append-only log itself, filtered.</sub></td>
|
|
376
|
+
</tr>
|
|
377
|
+
</table>
|
|
378
|
+
|
|
379
|
+
Dark and light, desktop and mobile; errors by taxonomy category, roadmap
|
|
380
|
+
progress joined to real task and token counts, and per-project scoping.
|
|
381
|
+
|
|
382
|
+
→ **[The dashboard](docs/guide/dashboard.md)** — what each of the eleven pages
|
|
383
|
+
shows you. It is part of the clone, not of the package.
|
|
384
|
+
|
|
337
385
|
## Status
|
|
338
386
|
|
|
339
387
|
Phases 1–9 are built and merged: loop runner, worktree engine, gates, state and
|
|
@@ -341,8 +389,16 @@ analytics, dashboard, self-extension, cross-provider judges, hardening. Phase 10
|
|
|
341
389
|
is half in: `smith daemon` watches the factory in the background and its ops
|
|
342
390
|
runbook is written; the hosted UI stays deferred. Beside the phases, `/bs audit`
|
|
343
391
|
is built: an existing project can be read on four axes and one epic cut from
|
|
344
|
-
what you accept.
|
|
345
|
-
|
|
392
|
+
what you accept.
|
|
393
|
+
|
|
394
|
+
The CLI is on npm as `@juzser/blacksmith`, at `0.2.0` — two binaries now,
|
|
395
|
+
`smith` and `smith-run`, the second being what runs a rendered turn on a
|
|
396
|
+
`cli` harness such as Codex. `0.1.1` was the first version that ran beside
|
|
397
|
+
you and knew it was a package, rather than out of the clone-shaped install
|
|
398
|
+
`0.1.0` was. The package is the binaries and what they
|
|
399
|
+
read. The dashboard, the docs, the test suite and `/bs` itself come from a
|
|
400
|
+
clone; a plugin is what would move `/bs`, and it is
|
|
401
|
+
[scoped, not cut](docs/specs/plugin-port-scope.md).
|
|
346
402
|
|
|
347
403
|
The one thing to know up front: **the daemon watches, it does not drive.** It
|
|
348
404
|
tells you what the factory needs — budget alarms, agents that never came back,
|
|
@@ -352,7 +408,7 @@ and nothing advances.
|
|
|
352
408
|
|
|
353
409
|
→ **[What is built, what is not](docs/guide/status.md)**, and the unflinching
|
|
354
410
|
version in
|
|
355
|
-
[Limitations today](docs/guide/operator-guide.md#limitations-today).
|
|
411
|
+
[Limitations today](docs/guide/operator-guide/limitations.md#limitations-today).
|
|
356
412
|
|
|
357
413
|
## Docs
|
|
358
414
|
|
|
@@ -361,11 +417,12 @@ version in
|
|
|
361
417
|
| [`INSTALL.md`](INSTALL.md) | Getting it running, per platform |
|
|
362
418
|
| [`docs/guide/operator-loop.md`](docs/guide/operator-loop.md) | The six steps you actually do |
|
|
363
419
|
| [`docs/guide/operator-guide.md`](docs/guide/operator-guide.md) | Every command, end to end, with output |
|
|
364
|
-
| [`docs/guide/dashboard.md`](docs/guide/dashboard.md) | The dashboard tour |
|
|
365
420
|
| [`docs/guide/status.md`](docs/guide/status.md) | What is real today |
|
|
366
421
|
| [`docs/guide/extending.md`](docs/guide/extending.md) | Adding agents, policies, taxonomy values |
|
|
367
422
|
| [`docs/specs/black-smith-architecture.md`](docs/specs/black-smith-architecture.md) | Why it is shaped this way |
|
|
368
423
|
| [`docs/specs/audit-command-scope.md`](docs/specs/audit-command-scope.md) | What `/bs audit` promises an audited project, and why |
|
|
424
|
+
| [`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
|
+
| [`docs/guide/dashboard.md`](docs/guide/dashboard.md) | The dashboard tour |
|
|
369
426
|
| [`docs/runbooks/providers.md`](docs/runbooks/providers.md) | Setting up the cross-provider judges |
|
|
370
427
|
| [`docs/runbooks/ops.md`](docs/runbooks/ops.md) | Running `smith daemon` unattended |
|
|
371
428
|
| [`docs/README.md`](docs/README.md) | Everything else, one line each |
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
// - 'user' — what waivers.ts, lessons.ts and prompts.ts default to when no
|
|
18
18
|
// actor is supplied, which is how every decision made through the UI is
|
|
19
19
|
// attributed (ui/server never passes one).
|
|
20
|
-
// - 'operator' — what docs/guide/operator-guide
|
|
21
|
-
// SKILL.md hand the operator, in
|
|
22
|
-
// including the `smith waivers apply` line
|
|
20
|
+
// - 'operator' — what docs/guide/operator-guide/*.md and .claude/skills/bs/
|
|
21
|
+
// SKILL.md hand the operator, in every `--actor` example they carry (three
|
|
22
|
+
// today), including the `smith waivers apply` line in
|
|
23
|
+
// severity-and-budget.md that writes waiver-granted.
|
|
23
24
|
// - 'operator-skill' — what the operator's own console passes; 467 of the 668
|
|
24
25
|
// recorded events carry it. It is a skill rather than a person, and it is
|
|
25
26
|
// here anyway: it acts only on a turn the operator took, and the decision
|