@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.
Files changed (72) 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 +22 -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 +119 -0
  13. package/.claude/skills/bs/audit.md +252 -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 +35 -0
  18. package/.claude/skills/bs/plan.md +172 -0
  19. package/.claude/skills/bs/report.md +129 -0
  20. package/.claude/skills/bs/run.md +327 -0
  21. package/.claude/skills/bs/status.md +54 -0
  22. package/.claude/skills/bs/ui.md +19 -0
  23. package/.claude/skills/bs/waivers.md +13 -0
  24. package/.claude/skills/bs/wave.md +267 -0
  25. package/README.md +136 -79
  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/claims.js +27 -1
  31. package/factory/orchestrator/dist/cli.js +196 -27
  32. package/factory/orchestrator/dist/crosscheck.js +11 -0
  33. package/factory/orchestrator/dist/daemon.js +30 -5
  34. package/factory/orchestrator/dist/db/projector.js +272 -90
  35. package/factory/orchestrator/dist/db/queries.js +83 -14
  36. package/factory/orchestrator/dist/db/schema.js +7 -0
  37. package/factory/orchestrator/dist/events.js +88 -4
  38. package/factory/orchestrator/dist/harness.js +516 -0
  39. package/factory/orchestrator/dist/impact.js +76 -5
  40. package/factory/orchestrator/dist/judges.js +49 -8
  41. package/factory/orchestrator/dist/lessons.js +4 -4
  42. package/factory/orchestrator/dist/mcp.js +4 -3
  43. package/factory/orchestrator/dist/paths.js +169 -15
  44. package/factory/orchestrator/dist/plan.js +61 -1
  45. package/factory/orchestrator/dist/planQuorum.js +36 -8
  46. package/factory/orchestrator/dist/providers/api-transport.js +62 -5
  47. package/factory/orchestrator/dist/providers/cli-transport.js +7 -73
  48. package/factory/orchestrator/dist/providers/index.js +1 -0
  49. package/factory/orchestrator/dist/providers/schema-validate.js +58 -7
  50. package/factory/orchestrator/dist/providers/types.js +5 -0
  51. package/factory/orchestrator/dist/roadmap.js +3 -3
  52. package/factory/orchestrator/dist/run-cli.js +130 -0
  53. package/factory/orchestrator/dist/runner.js +264 -0
  54. package/factory/orchestrator/dist/scaffold.js +3 -2
  55. package/factory/orchestrator/dist/spawn.js +84 -0
  56. package/factory/orchestrator/dist/stack.js +10 -2
  57. package/factory/orchestrator/dist/templateFragments.js +178 -0
  58. package/factory/orchestrator/dist/usage.js +30 -9
  59. package/factory/orchestrator/dist/waveNext.js +42 -14
  60. package/factory/orchestrator/dist/waveSchedule.js +7 -1
  61. package/factory/orchestrator/dist/workroot.js +113 -0
  62. package/factory/orchestrator/drizzle/0011_findings_spec_ref.sql +3 -0
  63. package/factory/orchestrator/drizzle/meta/_journal.json +7 -0
  64. package/factory/policies/budgets.yml +7 -4
  65. package/factory/policies/crosscheck.yml +10 -8
  66. package/factory/policies/harness.yml +82 -0
  67. package/factory/policies/lessons.md +122 -0
  68. package/factory/policies/severity.yml +1 -1
  69. package/factory/policies/taxonomy.yml +6 -2
  70. package/factory/specs/roadmap.md +154 -0
  71. package/factory/specs/schema/task-spec.schema.json +5 -0
  72. package/package.json +10 -7
@@ -0,0 +1,73 @@
1
+ # `/bs lessons` — review pending lesson candidates
2
+
3
+ [`dispatch.md`](dispatch.md) binds every agent this playbook dispatches;
4
+ none of it is restated here.
5
+
6
+ 1. `smith lessons candidates [--session <id>] [--db state/smith.db]` —
7
+ pending candidates with their statement, type, scope, and evidence/
8
+ provenance event ids.
9
+ 2. Present each to the operator. Approve, edit, or reject is **always**
10
+ their call — nothing here self-modifies an agent
11
+ (architecture §9.4, the memory-poisoning safety boundary).
12
+ - **Approve**: `smith lessons approve <lesson-id> --session <id>
13
+ --plan-version <n> --causal-parent <event-id> --actor operator
14
+ [--note "<why>"]`.
15
+ - **Edit then approve**: add `--statement`, `--lesson-type` and/or
16
+ `--lesson-scope` to the same call — it writes the `lesson-edited`
17
+ first and chains the approval onto it, one commit. A `--statement`
18
+ goes through **the same novelty gate `raise` uses** (P9-34): if the
19
+ new text duplicates another lesson the call exits 1 with
20
+ `lessons.edit-not-novel` and writes **nothing**. Report that to the
21
+ operator with the id it duplicates and let them choose — approve the
22
+ lesson it duplicates, reword, or re-run with `--accept-duplicate`,
23
+ which lands it and records `novelty_override` on the event. Never add
24
+ `--accept-duplicate` on your own initiative.
25
+ - **Read the `novelty` block on every approval** (P9-35). The gate
26
+ catches copy-paste and one-word re-raises — since P9-35 (a) the
27
+ threshold is corrected per pair for the shorter statement's length —
28
+ but two changed words, containment and any real rewording all pass it,
29
+ so the printed `mostSimilarLessonId` and score are the real
30
+ near-duplicate check, and the operator is the one making it. Quote the
31
+ score with the bar the block reports, not the configured threshold;
32
+ they differ whenever the correction applied. An
33
+ approval that lands non-novel text exits 1 *with the transition
34
+ applied*: surface it, do not retry it. The UI's Edit action runs the
35
+ same gate since P9-36 — but it has no `--accept-duplicate` control, so
36
+ a duplicate the operator decides to keep has to come back to the CLI.
37
+ - **Reject**: `smith lessons reject <lesson-id> …` — transitions to
38
+ `invalidated`, the same status the UI's reject action uses
39
+ (`ui/server/src/app.ts`'s `/reject` route); there is no separate
40
+ operator-rejection status in taxonomy.yml's `lesson_status`.
41
+ - `invalidated`, `novelty-rejected` and `superseded` are **terminal**
42
+ (`LEGAL_LESSON_TRANSITIONS`, `lessons.ts`). Reviving a rejected lesson
43
+ means raising it again with its own provenance — the command refuses
44
+ `lessons.illegal-transition` rather than rewriting the decision.
45
+ 3. Rebuild the projection so the change is visible:
46
+ `smith db apply --db state/smith.db --session <id>`.
47
+ 4. Periodically run `smith dream [--since <iso-date>]` to extract new raw
48
+ candidates from decision checkpoints (plan sign-offs, waiver decisions,
49
+ escalations, gate blocks) before this review — it needs the same
50
+ `--session/--plan-version/--causal-parent` envelope. Candidates it
51
+ raises carry `needs_distillation: true`; dispatch **`scribe`**
52
+ (`.claude/agents/scribe.md`) to turn a promising raw one into a
53
+ checkable, principle-level statement before presenting it, rather than
54
+ showing the operator raw event text.
55
+ 5. Once a batch is approved, recompile the committed file:
56
+ `smith lessons compile [--session <id>] [--db state/smith.db]` —
57
+ regenerates `factory/policies/lessons.md` from every `approved` lesson,
58
+ sectioned by scope (architecture §9.5). Commit the regenerated file — it
59
+ is the file every later dispatch reads (`smith lessons for-dispatch`), so
60
+ an approved-but-uncompiled lesson reaches nobody.
61
+ 6. After a compile — or whenever `lessons.md` has grown enough that nobody
62
+ reads it — ask which entries still earn their place:
63
+ `smith lessons audit <session-id> [--lessons <file>] [--state-dir <dir>]`.
64
+ It **recommends only**; every removal is still the operator's call, the
65
+ same boundary step 2 draws. Read the two evidence classes apart
66
+ (operator-guide/lessons-and-daemon.md §10b): `retire`/`unreachable` is
67
+ structural — an earlier entry shadows this one, provable from the corpus
68
+ text alone — while `idle`/`rescope` needs a log showing the entry was
69
+ actually loaded and still did not fire. `no-evidence` means the audit
70
+ could not see enough, and is **never** a reason to drop an entry.
71
+ Contradictions are reported,
72
+ not resolved: two entries that disagree are a question for the operator,
73
+ not something a tool should pick a winner in.
@@ -0,0 +1,29 @@
1
+ # `/bs mcp <project>` — layer the MCP surface on
2
+
3
+ Every project leaving the factory ships an MCP surface
4
+ (`docs/standards/mcp.md`). It is due **late** — at the mandatory
5
+ `<project> — mcp surface` milestone, once the tools worth exposing are known.
6
+ Running this at `smith new` time would produce a manifest declaring nothing,
7
+ which is the rubber stamp the standard exists to prevent.
8
+
9
+ 1. Run `smith mcp init <project>`. It layers `src/mcp/`, `test/mcp/`, and a
10
+ root `mcp.manifest.json` onto the already-scaffolded project, merges the
11
+ `mcp:serve` script and the pinned SDK dependency into its `package.json`,
12
+ and appends the `mcp surface` milestone to `factory/specs/roadmap.md`.
13
+ It refuses if the target has no `package.json` (run `/bs new` first) or if
14
+ a manifest already exists — re-scaffolding would discard declared tools and
15
+ their operator sign-off.
16
+ 2. The scaffold ships one read-only tool (`project_health`) as the worked
17
+ example. Every tool added after it is **declared in the manifest first**,
18
+ with a mutation class and matching annotations; anything that is not
19
+ `read-only` needs `approval: { operator, date, milestone }` — an operator
20
+ signature, taken at this milestone. Never sign one yourself.
21
+ 3. Tools that wrap a guardrail-gated action (deploy, outbound send, writes to
22
+ `main`) are not allowed at all (mcp.md MCP-S3). A signed `destructive` tool
23
+ is permission to act inside the project, never a way around an operator
24
+ gate.
25
+ 4. Run `smith mcp check <project>` before closing anything under that
26
+ milestone. Exit 0 means green; exit 1 prints one `{ rule, path, message }`
27
+ per violation. `smith epic close` runs the same check and refuses while it
28
+ is red — the only way past it is `--override-rationale`, which is the
29
+ operator's call and their name in the event log, never yours.
@@ -0,0 +1,35 @@
1
+ # `/bs new <project> [--ui]` — scaffold a target project
2
+
3
+ 1. Ask where the project should live, then run `smith new <project> [--ui]
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
6
+ than a clone, in the directory you run it from, never inside the package.
7
+ Either way that is a default rather than a requirement —
8
+ the answer becomes the `<project-dir>` every later command takes, so take
9
+ it here rather than assuming it six steps in. The call
10
+ copies `factory/scaffold/` (TS strict, Biome, Vitest, CI) and layers
11
+ whatever `factory/policies/stack.yml` answered for: `--ui` adds the
12
+ frontend, generates `src/styles/main.css`, and vendors the named design
13
+ system into `design/` if there is one. Then it installs and runs the
14
+ project's own gates, commits it on a `setup` branch, and registers a
15
+ bootstrap milestone in `factory/specs/roadmap.md` — all in one call.
16
+ An answer the templates cannot build (`frontend: react`) makes it **refuse
17
+ before creating anything**, rather than quietly handing over the frontend
18
+ they do ship. `smith stack check` says in advance which answers are
19
+ honoured, merely recorded, or refused.
20
+ 2. The gate run is `pnpm install` then `lint`, `typecheck`, `test:coverage`,
21
+ `build` — `ci.yml`'s order, so the lockfile lands in the first commit and no
22
+ epic ever needs a serial `task-0-toolchain` (P9-19). It takes a minute or
23
+ two. Read `toolchain` in the JSON result:
24
+ - `verified` — every gate passed; plan the first epic against it.
25
+ - `failed` — `failedStep` names the gate, its `output` is the tail, and
26
+ `command` is the exact line to re-run by hand. The command exits **1** and
27
+ the tree is left in place to fix. Do not plan an epic against it.
28
+ - `skipped` — only when `--skip-toolchain` was passed (offline). Nothing was
29
+ proven; say so rather than reporting a green.
30
+ 3. The JSON result's `commands.ghRepoCreate` and `commands.push` are for the
31
+ **operator to run themselves** — never execute them from this session
32
+ (guardrails.md: only the operator pushes a brand-new remote/creates the
33
+ repo; this skill only prints what to run).
34
+ 4. Tell the operator: target dir, branch (`setup`), the `toolchain` verdict, and
35
+ the two commands.
@@ -0,0 +1,172 @@
1
+ # `/bs plan <goal>` — draft or re-plan an epic
2
+
3
+ [`dispatch.md`](dispatch.md) binds every agent this playbook dispatches;
4
+ none of it is restated here.
5
+
6
+ The planner-session playbook (architecture §3, §12):
7
+
8
+ **Pick the effort tier before step 1.** It is chosen per *epic*, not per
9
+ project — the same repo runs a `small` internal-tool epic and a `huge`
10
+ one — and it scales steps 3–4 below plus most of `/bs run`. Ask, do not
11
+ recall:
12
+
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
16
+ ```
17
+
18
+ `profile` is what this epic buys, and every step below that a tier touches
19
+ says which field decides it. `invariants` is what no tier may touch: the
20
+ gate pipeline, the integration check, claim disjointness and worktree
21
+ isolation, the security-reviewer's own triggers, the operator's sign-off,
22
+ the epic verdict, the event log and the escalation ladder run identically at
23
+ every tier. A `small` epic is faster because it deliberates less, never
24
+ because it verifies less — a tier that skipped tests would not be cheap,
25
+ just unverified.
26
+
27
+ Omit `--effort` and the epic takes `effort.yml`'s `default_tier`. Read
28
+ `effective`, not `requested`: an epic whose live tasks fire `crosscheck.yml`'s
29
+ `plan_quorum` security triggers is floored at `security_floor` no matter what
30
+ was asked for, and `floorApplied: true` says so. An internal tool that
31
+ touches auth is not a small epic. Without `--plan` the floor is *not
32
+ evaluated* — `securityFloorEvaluated: false`, and an empty `securityTriggers`
33
+ there means "not looked at", not "looked at and clean".
34
+
35
+ 1. **Static analysis for claims first.** Before any decomposition, get the
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
39
+ directly with Grep/Read for a small one.
40
+ 2. Dispatch a **`planner`** session (`.claude/agents/planner.md`, frontier
41
+ tier) with the goal + the claims analysis. It drafts the epic spec and
42
+ task specs (`factory/specs/schema/task-spec.schema.json` shape).
43
+ 3. Dispatch a **`spec-reviewer`** session (`.claude/agents/spec-reviewer.md`)
44
+ — a *different model* than the planner's, per its own frontmatter — to
45
+ hunt spec gaps/ambiguities/missing-nonfunctional clauses
46
+ (`docs/standards/agent-constraints.md` "planner"; `severity.yml`: most
47
+ spec gaps land S2, blocking sign-off).
48
+ 4. Planner fixes flagged issues; repeat 2–3 until the spec-reviewer has
49
+ nothing S1/S2 left — or exactly once when `profile.specReviewRounds` is
50
+ `single-pass` (`small`), where the planner fixes what came back and
51
+ anything still S1/S2 goes to the operator with the spec at step 5 instead
52
+ of round-tripping. Then run the plan quorum before sign-off — it
53
+ evaluates `factory/policies/crosscheck.yml`'s `plan_quorum.triggers`
54
+ (budget ≥50% of the epic cap, `case: infra`, security-sensitive, low
55
+ confidence) for you:
56
+
57
+ ```bash
58
+ smith plan quorum --plan <the draft's plan.json> --plan-version <n> \
59
+ --session <session-id> --causal-parent <event-id> \
60
+ --confidence <your own 0–1 confidence in this plan> \
61
+ --out <scratch>/quorum-v<n>.json
62
+ ```
63
+
64
+ `--plan` hands over the draft itself: at this step nothing has been
65
+ written to `factory/specs/active/<epic>/` yet — that is step 6, on
66
+ approval — so the verb cannot read a `plan-v<n>.json` that does not
67
+ exist. (`--epic <epic>` instead of `--plan` critiques a version already
68
+ filed.) The draft's own `epic_id`/`version` must be the ones the command
69
+ names, or it refuses (`plan.identity-mismatch`) rather than record one
70
+ plan's identity with another plan's triggers. `--out` keeps the outcome
71
+ — three rationales of several kB each — where step 5 can hand it to the
72
+ operator whole instead of quoting a terminal.
73
+
74
+ **Nothing runs this for you — you run it here** when
75
+ `profile.planQuorum` is `always` (`huge`). At `when-triggered` (`medium`,
76
+ `small`) run it only when you can already name the trigger from the plan
77
+ you just wrote — budget ≥50% of the epic cap, `case: infra`, a
78
+ security-sensitive clause, or your own confidence below 0.8. The command
79
+ is local and free either way; what the tier is buying back is the step,
80
+ not the tokens, because the two cross-provider judges are trigger-gated
81
+ inside `runPlanQuorum` at every tier. Exit 0 = nothing needs
82
+ the operator. Exit 1 (`critiqued`/`escalated`) is critique-only: never
83
+ rewrite the plan silently, surface the critique to the operator at step
84
+ 5. If the output is `endorsed` with a non-empty `triggers` array, a
85
+ trigger fired but no external provider is `enabled` in `crosscheck.yml`
86
+ — say that plainly to the operator rather than signing off as if a
87
+ cross-provider quorum happened.
88
+ 5. **Present the epic spec + acceptance criteria to the operator.** Nothing
89
+ executes before they sign off (architecture §1; agent-constraints.md
90
+ "planner: autonomy — spec sign-off per epic").
91
+ 6. On approval, write task specs to
92
+ `factory/specs/active/<epic>/plan-v1.json` and validate:
93
+ `smith plan validate factory/specs/active/<epic>/plan-v1.json` must
94
+ report `{"valid":true}` before anything else touches this plan. Write the
95
+ tier onto the plan file as a top-level `"effort": "<tier>"` — that is
96
+ where `/bs run` reads it, and `smith plan amend` carries it into
97
+ v(n+1) so a re-plan is not a fresh choice. Omit the field to take the
98
+ policy default; a value that is not `small`/`medium`/`huge` fails
99
+ validation rather than degrading to it.
100
+
101
+ Then read the plan's parallelism ceiling before anyone is dispatched
102
+ against it:
103
+
104
+ ```bash
105
+ smith wave schedule factory/specs/active/<epic>/plan-v1.json \
106
+ --session <session-id> --repo <project-dir>
107
+ ```
108
+
109
+ `valid: true` says the plan is well-formed; this says whether it can ever
110
+ run wide. It replays the wave loop to exhaustion and reports `depth` (how
111
+ many sequential rounds) and `widest` (the most tasks any round starts —
112
+ the ceiling beyond which more agents buy nothing). Exit 1 means the plan
113
+ **stalls** and cannot be finished as written: fix it here, not by watching
114
+ `/bs run` hang. Exit 2 means it runs but loses width to `constraints` —
115
+ tasks whose dependencies were satisfied and were held back only by how you
116
+ drew the claims. That is a plan-time cost and this is the last step that
117
+ can pay it cheaply; a `widest: 1` plan serializes the whole epic while
118
+ every gate downstream reports a healthy wave of one. Deferrals for
119
+ `dependency-pending` never appear there, because a chain of real
120
+ dependencies is the shape of the work and not a defect. Exit 2 whose
121
+ `constraints` name `symbol-coupled` is a **re-plan round**, not
122
+ information: hand the planner the constraint list — the file pairs from
123
+ the deferral detail — and ask, per pair, for a dependency edge, a
124
+ `keeps_exports` promise on the producer, or a re-slice.
125
+ `claim-overlap` / `serialize-hotspot` constraints are the re-slice case
126
+ above. Exit 2 with no such constraint remains information, not a stop —
127
+ decide whether to re-slice, and say which you chose when you present the
128
+ plan.
129
+
130
+ Beside the schedule, the read that names what it only counts:
131
+
132
+ ```bash
133
+ smith claims impact --plan factory/specs/active/<epic>/plan-v1.json \
134
+ <task-id>... --repo <project-dir>
135
+ ```
136
+
137
+ Its `crossings` are the file pairs a `symbol-coupled` constraint points
138
+ at; `promised` holds the ones a producer's `keeps_exports` already
139
+ answers, which the schedule admits and the diff is later checked against.
140
+ 7. Log the sign-off itself as a decision checkpoint (this is exactly what
141
+ `smith dream`'s "plan sign-off" extraction looks for, `lessons.ts`):
142
+ `smith event append '{"session_id":"...","actor":"operator",
143
+ "event_type":"plan-version-created","plan_version":1,
144
+ "causal_parent":"...","payload":{"epic_id":"<epic>","version":1,
145
+ "note":"<operator's own words>"}}'`.
146
+
147
+ Then write the backlog that signature approved into the log, hung off
148
+ the sign-off event:
149
+
150
+ ```bash
151
+ smith plan ingest factory/specs/active/<epic>/plan-v1.json \
152
+ --session <session-id> --plan-version 1 --causal-parent <sign-off event id>
153
+ ```
154
+
155
+ This is where a task starts existing as far as the log — and so the DB,
156
+ the Kanban, the Flow graph and every dashboard number — is concerned
157
+ (D-46, D-254): one `task-added` per task, one `edges-recorded` for the
158
+ DAG. Nothing downstream runs it for you, and nothing refuses to run
159
+ without it: `wave next` reads the plan file, so an un-ingested plan still
160
+ runs, and its task rows then spring into being as a side effect of the
161
+ first wave or gate event to name an id — with no epic, no claims, no
162
+ budget and no edges, which is the flat, half-empty board the 2026-09-14
163
+ UI check found behind two epics. It is idempotent, so run it again on a
164
+ resumed session; read `added` and `edges` back, and say so when either is
165
+ 0 on a plan that has tasks or edges.
166
+ 8. If this epic opens a new roadmap milestone, add it to
167
+ `factory/specs/roadmap.md` (planner-maintained, architecture §12) — a
168
+ roadmap change is itself a scope change and needs the same operator nod.
169
+ Give it a `- goal:` line and list the epic under `- epics:`. That line is
170
+ load-bearing, not documentation: it is the only text the spec-vs-goal gate
171
+ can grade the plan against (`/bs run` step 14), and a milestone without one
172
+ holds every epic it owns.
@@ -0,0 +1,129 @@
1
+ # `/bs report` — the scribe's progress digest
2
+
3
+ [`dispatch.md`](dispatch.md) binds every agent this playbook dispatches;
4
+ none of it is restated here.
5
+
6
+ The scribe-template digest playbook (agent-constraints.md "scribe (digest
7
+ duties, v3.2)"), plus running the scheduler first — a report that omits
8
+ pending proposals lets them sit invisible instead of surfacing them.
9
+
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
13
+ project it built that has a checkout, ending in the `--project` line to
14
+ pass on. Do not assemble that list from memory: the maintenance pass
15
+ reads one lockfile per flag and reports one proposal per repo, so a repo
16
+ left off the line is not reported as missing, it is simply never
17
+ mentioned. (The daemon raises `unwatched-project` for a child project with
18
+ a checkout that is left off the line — not for the factory itself, which is in
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
21
+ off the printed list. The two surfaces no longer answer the same
22
+ question.) Then run `smith scheduler run
23
+ [--dry] --session <id> [--project <dir>...]` with those flags — a
24
+ deterministic pass over the event log, it never dispatches an agent
25
+ itself (`factory/policies/scheduler.yml`, architecture §12). Read its
26
+ `proposals` and route each kind before writing the digest:
27
+ - `recheck-proposed` → don't act on it silently; tick it into the next
28
+ `/bs plan` sign-off as an `origin: recheck` task, same spec/sign-off
29
+ flow as any other. Re-running the scheduler never duplicates an
30
+ already-pending proposal for the same task (idempotent by design) —
31
+ it re-proposes only after that recheck is resolved (a real recheck
32
+ task created for it, or explicitly declined).
33
+ - `maintenance-proposed` → read `projectDir` first: one pass can raise
34
+ several of these, one per `--project`, and they are not
35
+ interchangeable. Then check `autoSchedulable`: `true` (patch/
36
+ minor-only version bumps, `scheduler.yml`'s
37
+ `auto_schedule_confidence`) can go straight into the next `/bs plan`
38
+ without a fresh conversation; `false` (a major-version bump is
39
+ present) still needs one.
40
+ - `growth-review-due` → this is a TRIGGER only, never a scope proposal
41
+ itself (architecture §12). Open a fresh `/bs plan <goal>` session
42
+ where the planner reads the living spec/analytics/recheck outcomes
43
+ and proposes new epics — the operator's tick is required regardless
44
+ of confidence, same as any other plan sign-off.
45
+ 2. **Ask who may say yes.** `smith scheduler admit --session <id>
46
+ [--project <dir>...]`, with the same flag line step 1 printed, re-reads
47
+ the same proposals and classifies
48
+ each one `auto` or `operator` against `scheduler.yml`'s `autonomy:`
49
+ block (`src/autonomy.ts`). It enacts nothing — the split is printed so
50
+ it can be read and argued with before anything moves.
51
+ - `decision: auto` → dispatchable through `/bs run`'s ordinary wave
52
+ flow without a fresh sign-off. It is still a wave: same worktrees,
53
+ same gates, same reviewer, and the **PR is still merged by a
54
+ person**. That merge is the backstop that makes auto-dispatch safe
55
+ to widen, so never bypass it because the scheduler said `auto`.
56
+ - `decision: operator` → it waits, and `code` names the rule that held
57
+ it: `autonomy-disabled`, `growth-never-auto`, `kind-not-whitelisted`,
58
+ `reason-not-whitelisted`, `below-confidence-floor`, or
59
+ `security-surface`. Report the `reason` verbatim in the digest — it
60
+ is written in the terms an operator would use to argue with it.
61
+ - Every rule in that file can only **deny**. There is no branch that
62
+ promotes something the whitelist did not name, so "what runs without
63
+ me?" is answerable by reading `scheduler.yml` alone.
64
+ - **Never widen the whitelist to clear a denial mid-run.** Editing
65
+ `autonomy.auto_dispatch_kinds`, `auto_dispatch_recheck_reasons` or
66
+ `confidence_floor` is an operator decision about standing policy, not
67
+ a way past one proposal — and a `security-surface` denial is the one
68
+ nothing in the file can lift.
69
+ 3. Gather `smith stats overview [--session <id>]` and `smith stats roadmap
70
+ [--session <id>]` (milestone progress + budget burn per milestone).
71
+ 4. **Read back the disagreements.** `smith judge escalations --session
72
+ <id>` folds the lineage for `quorum-decision` events whose latest word
73
+ was `escalate` — a gate finding, an epic verdict or a plan critique the
74
+ cross-provider quorum could not settle. It exits `1` on an open
75
+ `disagreement` and `2` when the only thing open is
76
+ `insufficient-providers`, so **exit 2 is not a failure to fix here**: it
77
+ is one fact about `crosscheck.yml`'s gating arithmetic, already collapsed
78
+ to `ungated.count` plus a hint, and it belongs in the digest as a single
79
+ line. Report each `disagreement` individually — two providers read the
80
+ same thing and said different words, and nothing else in the pipeline
81
+ will raise it again. An entry with `held: false` goes first: the quorum
82
+ did not settle it and the work went ahead regardless (that includes a
83
+ `mode: shadow` disagreement, which the gate outcome never reported to
84
+ anyone). Resolving one is an operator reading, never this session
85
+ deciding on its own which provider was right.
86
+ 5. **Read back the parallelism.** `smith wave audit --session <id>
87
+ [--epic <id>]` folds every `wave-admitted` against the
88
+ `dispatch_decision` intervals underneath it and says whether a wave
89
+ admitted N wide actually *ran* N wide. `wave check` only ever certified
90
+ that it *could*, so without this the digest reports a factory running
91
+ many agents in parallel on the strength of a permission slip. Exit `1`
92
+ is a wave whose tasks never once overlapped — report it with the epic
93
+ id, because a serialized run is the failure this whole design exists to
94
+ prevent, not a slow day. Exit `2` is a wave admitted with no dispatch
95
+ under it at all: that is "cannot tell", not "ran narrow", and it usually
96
+ means the wrong `--state-dir` or a run whose agents wrote elsewhere —
97
+ check before reporting it as a stall. A `partial` verdict is **not** a
98
+ finding; put `widest` in the digest as the one number that says how wide
99
+ this factory has ever actually run.
100
+ 6. Dispatch **`scribe`** (`.claude/agents/scribe.md`, small tier) with that
101
+ JSON plus step 2's admissions as input: shipped / in-flight / blocked /
102
+ budget burn / next milestone / **N rechecks pending, M maintenance
103
+ bumps auto-schedulable** (never silently dropped), ≤300 words, linking
104
+ into the dashboard (`smith ui serve`'s URL) — never paraphrase numbers
105
+ the query didn't return.
106
+ 7. This fires automatically on a weekly cadence and immediately on any
107
+ milestone completion (architecture §12) — for an ad hoc `/bs report`,
108
+ the same digest, just on demand.
109
+ 8. Sending it to Slack is an **outbound send** — the permission layer
110
+ prompts on the `curl`/webhook call; never fire it without that prompt
111
+ resolving (`docs/standards/guardrails.md` "Deploy + outbound"). Print
112
+ the digest to the operator regardless, whether or not the Slack send is
113
+ approved.
114
+
115
+ **Provider calibration note (Phase 8).** While any provider in
116
+ `factory/policies/crosscheck.yml` is still `mode: shadow`, run
117
+ `smith stats providers [--session <id>] [--since <iso-date>]` alongside the
118
+ scheduler pass and fold a one-line mention into the digest when a provider
119
+ has accumulated enough runs to review (`runs`, `verdicts`, `agreementRate`,
120
+ `schemaFailureRate`, `transportFailureRate`, `failuresByCode` per provider —
121
+ `docs/runbooks/providers.md`'s calibration procedure has the promotion
122
+ threshold). Report `verdicts` alongside `agreementRate`: a `null` rate means
123
+ no run ever answered, which is a transport problem to fix, not a provider
124
+ that disagrees (D-168). When the rate is `null`, name the top entry in
125
+ `failuresByCode` in the digest line — `provider.missing-api-key` is an unset
126
+ environment variable and the whole repair, where `provider.invalid-output`
127
+ is the prompt (D-253). Promoting a provider to
128
+ `mode: active` is always an **operator edit** of `crosscheck.yml`, never
129
+ something this session does on the digest's say-so.