@juzser/blacksmith 0.1.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/auditor.md +146 -0
- package/.claude/agents/coder.md +164 -0
- package/.claude/agents/grader.md +110 -0
- package/.claude/agents/merger.md +107 -0
- package/.claude/agents/planner.md +92 -0
- package/.claude/agents/researcher.md +105 -0
- package/.claude/agents/reviewer.md +171 -0
- package/.claude/agents/scribe.md +72 -0
- package/.claude/agents/security-reviewer.md +130 -0
- package/.claude/agents/spec-reviewer.md +215 -0
- package/.claude/agents/tester.md +121 -0
- package/.claude/agents/uiux.md +126 -0
- package/.claude/agents/verifier.md +103 -0
- package/.claude/agents/wave-runner.md +74 -0
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/factory/orchestrator/dist/actors.js +46 -0
- package/factory/orchestrator/dist/agents-registry.js +308 -0
- package/factory/orchestrator/dist/args.js +84 -0
- package/factory/orchestrator/dist/artifacts.js +111 -0
- package/factory/orchestrator/dist/attribution.js +160 -0
- package/factory/orchestrator/dist/audit.js +812 -0
- package/factory/orchestrator/dist/autonomy.js +151 -0
- package/factory/orchestrator/dist/budgetAlarm.js +366 -0
- package/factory/orchestrator/dist/budgets.js +196 -0
- package/factory/orchestrator/dist/claims.js +633 -0
- package/factory/orchestrator/dist/cli.js +3185 -0
- package/factory/orchestrator/dist/commit.js +152 -0
- package/factory/orchestrator/dist/coverage.js +188 -0
- package/factory/orchestrator/dist/crossFinding.js +479 -0
- package/factory/orchestrator/dist/crosscheck.js +396 -0
- package/factory/orchestrator/dist/daemon.js +707 -0
- package/factory/orchestrator/dist/db/projector.js +1317 -0
- package/factory/orchestrator/dist/db/queries.js +1714 -0
- package/factory/orchestrator/dist/db/schema.js +305 -0
- package/factory/orchestrator/dist/delegation.js +400 -0
- package/factory/orchestrator/dist/diffstat.js +164 -0
- package/factory/orchestrator/dist/dispatchAudit.js +275 -0
- package/factory/orchestrator/dist/dotenv.js +72 -0
- package/factory/orchestrator/dist/effort.js +202 -0
- package/factory/orchestrator/dist/epic.js +1018 -0
- package/factory/orchestrator/dist/epicWidth.js +238 -0
- package/factory/orchestrator/dist/errorIssues.js +249 -0
- package/factory/orchestrator/dist/errors.js +11 -0
- package/factory/orchestrator/dist/escalation.js +500 -0
- package/factory/orchestrator/dist/events.js +1015 -0
- package/factory/orchestrator/dist/findingAge.js +91 -0
- package/factory/orchestrator/dist/findingContext.js +103 -0
- package/factory/orchestrator/dist/findings.js +1156 -0
- package/factory/orchestrator/dist/gate.js +911 -0
- package/factory/orchestrator/dist/gh.js +146 -0
- package/factory/orchestrator/dist/git.js +87 -0
- package/factory/orchestrator/dist/goalCheck.js +380 -0
- package/factory/orchestrator/dist/graph.js +61 -0
- package/factory/orchestrator/dist/hookDecision.js +106 -0
- package/factory/orchestrator/dist/immutability.js +171 -0
- package/factory/orchestrator/dist/impact.js +280 -0
- package/factory/orchestrator/dist/integration.js +141 -0
- package/factory/orchestrator/dist/judgePreflight.js +155 -0
- package/factory/orchestrator/dist/judges.js +310 -0
- package/factory/orchestrator/dist/lessonAudit.js +399 -0
- package/factory/orchestrator/dist/lessons.js +1329 -0
- package/factory/orchestrator/dist/mcp.js +555 -0
- package/factory/orchestrator/dist/paths.js +82 -0
- package/factory/orchestrator/dist/plan.js +502 -0
- package/factory/orchestrator/dist/planQuorum.js +421 -0
- package/factory/orchestrator/dist/policy.js +1308 -0
- package/factory/orchestrator/dist/policyHook.js +28 -0
- package/factory/orchestrator/dist/preconditions.js +47 -0
- package/factory/orchestrator/dist/projects.js +134 -0
- package/factory/orchestrator/dist/prompts.js +57 -0
- package/factory/orchestrator/dist/provenance.js +201 -0
- package/factory/orchestrator/dist/providers/api-transport.js +117 -0
- package/factory/orchestrator/dist/providers/cli-transport.js +208 -0
- package/factory/orchestrator/dist/providers/index.js +54 -0
- package/factory/orchestrator/dist/providers/schema-validate.js +152 -0
- package/factory/orchestrator/dist/providers/types.js +49 -0
- package/factory/orchestrator/dist/queue.js +263 -0
- package/factory/orchestrator/dist/quorum.js +347 -0
- package/factory/orchestrator/dist/quorumEscalations.js +132 -0
- package/factory/orchestrator/dist/results.js +65 -0
- package/factory/orchestrator/dist/roadmap.js +437 -0
- package/factory/orchestrator/dist/runtime.js +86 -0
- package/factory/orchestrator/dist/sameMistakeKpi.js +243 -0
- package/factory/orchestrator/dist/sandbox.js +165 -0
- package/factory/orchestrator/dist/scaffold.js +373 -0
- package/factory/orchestrator/dist/scheduler.js +408 -0
- package/factory/orchestrator/dist/schemas.js +195 -0
- package/factory/orchestrator/dist/security.js +222 -0
- package/factory/orchestrator/dist/severity.js +293 -0
- package/factory/orchestrator/dist/spec.js +376 -0
- package/factory/orchestrator/dist/specChange.js +497 -0
- package/factory/orchestrator/dist/stack.js +324 -0
- package/factory/orchestrator/dist/symbols.js +966 -0
- package/factory/orchestrator/dist/taskEvents.js +399 -0
- package/factory/orchestrator/dist/taskId.js +126 -0
- package/factory/orchestrator/dist/taxonomy.js +113 -0
- package/factory/orchestrator/dist/testSelect.js +129 -0
- package/factory/orchestrator/dist/testerAudit.js +264 -0
- package/factory/orchestrator/dist/testgate.js +171 -0
- package/factory/orchestrator/dist/usage.js +874 -0
- package/factory/orchestrator/dist/waivers.js +228 -0
- package/factory/orchestrator/dist/waveBudget.js +223 -0
- package/factory/orchestrator/dist/waveConcurrency.js +250 -0
- package/factory/orchestrator/dist/waveNext.js +210 -0
- package/factory/orchestrator/dist/waveSchedule.js +146 -0
- package/factory/orchestrator/dist/worktree.js +207 -0
- package/factory/orchestrator/drizzle/0000_broken_rockslide.sql +183 -0
- package/factory/orchestrator/drizzle/0001_luxuriant_zaladane.sql +1 -0
- package/factory/orchestrator/drizzle/0002_yellow_wild_pack.sql +8 -0
- package/factory/orchestrator/drizzle/0003_damp_riptide.sql +12 -0
- package/factory/orchestrator/drizzle/0004_broad_lady_vermin.sql +1 -0
- package/factory/orchestrator/drizzle/0005_romantic_jocasta.sql +2 -0
- package/factory/orchestrator/drizzle/0006_bent_madelyne_pryor.sql +16 -0
- package/factory/orchestrator/drizzle/0007_overjoyed_goliath.sql +1 -0
- package/factory/orchestrator/drizzle/0008_smart_iron_patriot.sql +2 -0
- package/factory/orchestrator/drizzle/0009_agents_epic_id.sql +2 -0
- package/factory/orchestrator/drizzle/0010_milestone_kind.sql +1 -0
- package/factory/orchestrator/drizzle/meta/_journal.json +83 -0
- package/factory/policies/budgets.yml +262 -0
- package/factory/policies/crosscheck.yml +308 -0
- package/factory/policies/delegation.yml +44 -0
- package/factory/policies/effort.yml +142 -0
- package/factory/policies/guardrails.yml +306 -0
- package/factory/policies/lessons.md +239 -0
- package/factory/policies/scheduler.yml +90 -0
- package/factory/policies/sensitive-paths.yml +148 -0
- package/factory/policies/severity.yml +83 -0
- package/factory/policies/stack.yml +60 -0
- package/factory/policies/taxonomy.yml +340 -0
- package/factory/policies/worktree.yml +95 -0
- package/factory/scaffold/base/.github/workflows/ci.yml +34 -0
- package/factory/scaffold/base/.gitignore.tmpl +11 -0
- package/factory/scaffold/base/AGENTS.md +33 -0
- package/factory/scaffold/base/README.md +25 -0
- package/factory/scaffold/base/biome.json.tmpl +30 -0
- package/factory/scaffold/base/package.json +25 -0
- package/factory/scaffold/base/pnpm-workspace.yaml.tmpl +16 -0
- package/factory/scaffold/base/src/index.ts +4 -0
- package/factory/scaffold/base/test/index.test.ts +8 -0
- package/factory/scaffold/base/tsconfig.json +20 -0
- package/factory/scaffold/base/tsconfig.test.json +9 -0
- package/factory/scaffold/base/vitest.config.ts +29 -0
- package/factory/scaffold/mcp/mcp.manifest.json +22 -0
- package/factory/scaffold/mcp/package.fragment.json +9 -0
- package/factory/scaffold/mcp/src/mcp/README.md +63 -0
- package/factory/scaffold/mcp/src/mcp/guard.ts +110 -0
- package/factory/scaffold/mcp/src/mcp/redact.ts +69 -0
- package/factory/scaffold/mcp/src/mcp/server.ts +55 -0
- package/factory/scaffold/mcp/src/mcp/tools/health.ts +60 -0
- package/factory/scaffold/mcp/test/mcp/guard.test.ts +108 -0
- package/factory/scaffold/mcp/test/mcp/redact.test.ts +61 -0
- package/factory/scaffold/mcp/test/mcp/surface.test.ts +100 -0
- package/factory/scaffold/ui/index.html +12 -0
- package/factory/scaffold/ui/package.fragment.json +13 -0
- package/factory/scaffold/ui/src/App.vue +10 -0
- package/factory/scaffold/ui/src/main.ts +5 -0
- package/factory/scaffold/ui/src/shims.d.ts +11 -0
- package/factory/scaffold/ui/vite.config.ts +20 -0
- package/factory/scaffold/ui-tailwind/package.fragment.json +6 -0
- package/factory/scaffold/ui-tailwind/vite.config.ts +26 -0
- package/factory/specs/schema/event.schema.json +82 -0
- package/factory/specs/schema/finding-evidence.schema.json +45 -0
- package/factory/specs/schema/finding.schema.json +115 -0
- package/factory/specs/schema/grader-verdict.schema.json +50 -0
- package/factory/specs/schema/judge-verdict.schema.json +19 -0
- package/factory/specs/schema/lesson.schema.json +85 -0
- package/factory/specs/schema/mcp-manifest.schema.json +230 -0
- package/factory/specs/schema/result.schema.json +81 -0
- package/factory/specs/schema/spec-change-request.schema.json +46 -0
- package/factory/specs/schema/task-spec.schema.json +134 -0
- package/package.json +98 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Blacksmith — budget policy
|
|
2
|
+
#
|
|
3
|
+
# Token/turn caps per tier (architecture §2, §4, agent-constraints.md
|
|
4
|
+
# "budgets (factory-wide)").
|
|
5
|
+
#
|
|
6
|
+
# What is mechanical, and what is a request (P9-18, 2026-08-08; `failed_rounds`
|
|
7
|
+
# added P9-32 and `alarm_ratio` P9-33, both 2026-08-10; `max_in_flight_tasks`
|
|
8
|
+
# added 2026-08-26, the same day `epic.cap_tokens` grew a reader that actually
|
|
9
|
+
# blocks). Exactly six numbers here reach a runtime host:
|
|
10
|
+
#
|
|
11
|
+
# * `epic.cap_tokens` — three readers now, and only the newest of them
|
|
12
|
+
# blocks. planQuorum's budget trigger sums *declared* task budgets against
|
|
13
|
+
# it before any dispatch and only ever surfaces the finding for a human to
|
|
14
|
+
# act on; `smith budget alarm` compares it against spend counted from the
|
|
15
|
+
# event log, after wave merge, and also stops nothing itself. `smith wave
|
|
16
|
+
# check` (src/waveBudget.ts) is the reader that blocks: at wave admission —
|
|
17
|
+
# before any task in the wave has been dispatched — it sums the wave's
|
|
18
|
+
# declared task budgets against headroom under the cap (measured spend plus
|
|
19
|
+
# a declared price for every dispatch nothing has measured yet, the same
|
|
20
|
+
# projection `smith budget alarm` computes) and refuses to admit a wave
|
|
21
|
+
# that would cross it. It is not a stricter version of the other two
|
|
22
|
+
# readers' advice; it is the only one of the three standing upstream of the
|
|
23
|
+
# spend it is judging, so it is the only one that can refuse the spend
|
|
24
|
+
# instead of merely describing it afterward.
|
|
25
|
+
# * `epic.alarm_ratio` — `smith budget alarm` (src/budgetAlarm.ts), which
|
|
26
|
+
# reports each epic's measured and projected spend against
|
|
27
|
+
# `alarm_ratio * cap_tokens` and exits 1 on a crossing. Measured spend is a
|
|
28
|
+
# floor, not a total: a judge returns findings rather than a Result, so its
|
|
29
|
+
# tokens are in no `task-result-recorded` event and are priced from the
|
|
30
|
+
# caps below instead. A role with no cap here cannot be priced at all, and
|
|
31
|
+
# the epic is reported unverifiable rather than under — which is why the
|
|
32
|
+
# `task:` block's coverage of the roles the factory actually dispatches is
|
|
33
|
+
# load-bearing and not merely documentary.
|
|
34
|
+
# * a task's `tokens` — compared at the gate against the result's
|
|
35
|
+
# `token_usage.total_tokens`.
|
|
36
|
+
# * a task's `diff_lines` — compared at the gate against a real
|
|
37
|
+
# `git diff --numstat` (src/diffstat.ts), lockfiles and generated output
|
|
38
|
+
# excluded.
|
|
39
|
+
# * `escalation_ladder.rungs[].failed_rounds` — counted from `gate-outcome`
|
|
40
|
+
# events by `smith escalation check` (src/escalation.ts), which asserts the
|
|
41
|
+
# rung's `enforce` obligation against what the log shows happened next.
|
|
42
|
+
# * `epic.max_in_flight_tasks` — `smith wave check`, which refuses to admit a
|
|
43
|
+
# wave that would put more of the epic's tasks in flight at once (admitted,
|
|
44
|
+
# not yet merged) than this. Null by default; see the field's own comment
|
|
45
|
+
# below for why it is not the mechanism that bounds cost.
|
|
46
|
+
#
|
|
47
|
+
# The task gate still only records an overrun and reports it; it still does
|
|
48
|
+
# not block on one. An overrun there is as often evidence the plan
|
|
49
|
+
# under-estimated the task as evidence the task overran, and a cap the worker
|
|
50
|
+
# polices itself becomes a pressure on the very work it is supposed to be
|
|
51
|
+
# measuring (D-29: a coder spent its tail shaving lines to land exactly on
|
|
52
|
+
# `diff_lines`, stopping mid-procedure to do it).
|
|
53
|
+
#
|
|
54
|
+
# The epic cap no longer works that way: `smith wave check` blocks on one.
|
|
55
|
+
# The difference is not that an epic overrun matters more than a task overrun
|
|
56
|
+
# — it is *when* each is checked. The task gate polices work already
|
|
57
|
+
# underway, so refusing there distorts the work it is judging. Wave admission
|
|
58
|
+
# asks its question before any task in the wave has started, when there is
|
|
59
|
+
# nothing in flight yet for a refusal to put pressure on. A decision made
|
|
60
|
+
# before the work exists cannot become pressure on the work.
|
|
61
|
+
#
|
|
62
|
+
# Every other number below is prompt-level: read by agents from their own
|
|
63
|
+
# template and by the operator from the /bs playbook. They are requests, not
|
|
64
|
+
# limits, and the sections that follow say which is which. `max_turns` is no
|
|
65
|
+
# longer among them at all — no dispatch harness can enforce a turn limit
|
|
66
|
+
# (agent-interviews.md M-4), so plan validation now rejects it as a task budget
|
|
67
|
+
# field and the number belongs in the dispatch prompt.
|
|
68
|
+
#
|
|
69
|
+
# No concurrency cap on cost grounds, by design (removed 2026-08-05). Workers
|
|
70
|
+
# run in isolated worktrees on disjoint path claims, so parallelism is bounded
|
|
71
|
+
# by the claim graph, not by a worker count — and fan-out buys wall-clock
|
|
72
|
+
# without buying extra tokens (architecture §15.3). Hundreds of concurrent
|
|
73
|
+
# subagents is a supported shape. That reasoning holds only as long as the
|
|
74
|
+
# token cap it leans on actually binds, which — before `smith wave check` —
|
|
75
|
+
# `epic.cap_tokens` did not. `epic.max_in_flight_tasks` below is not that
|
|
76
|
+
# removed cap come back: it defaults to null, and cost stays bounded by
|
|
77
|
+
# `cap_tokens`, not by a worker count. It exists for an operator who wants a
|
|
78
|
+
# wall-clock or rate-limit bound for a reason of their own — a provider's
|
|
79
|
+
# concurrent-request limit, a laptop's CPU count — and setting it changes
|
|
80
|
+
# nothing about what bounds spend.
|
|
81
|
+
|
|
82
|
+
epic:
|
|
83
|
+
cap_tokens: 4000000 # planner + all workers + judges, one epic.
|
|
84
|
+
# Raised from 2,000,000 by operator decision on
|
|
85
|
+
# 2026-08-11 (dogfood-mcp-1), which is the
|
|
86
|
+
# operator answer the `alarm_ratio` note below
|
|
87
|
+
# requires — an extension, not a silent widening.
|
|
88
|
+
# The number the envkit-mcp-surface dogfood
|
|
89
|
+
# measured: 1,529,963 tokens for two of four
|
|
90
|
+
# tasks, both of them the two smallest in the
|
|
91
|
+
# plan. The old cap could not have carried that
|
|
92
|
+
# epic to close, and the alarm said so at 109%
|
|
93
|
+
# before the third task was ever dispatched.
|
|
94
|
+
# Two things this does NOT fix, both worth a
|
|
95
|
+
# follow-up: (1) there is no per-epic override,
|
|
96
|
+
# so a number raised for one epic is raised for
|
|
97
|
+
# every epic black-smith will ever run; (2) a
|
|
98
|
+
# large share of that 1.5M was rework — stopped
|
|
99
|
+
# dispatches and re-dispatches that `smith
|
|
100
|
+
# escalation check` recorded as ladder
|
|
101
|
+
# violations — so this cap now accommodates
|
|
102
|
+
# waste as well as work. A cap sized from a
|
|
103
|
+
# badly-run epic is a ceiling measured at the
|
|
104
|
+
# wrong place.
|
|
105
|
+
alarm_ratio: 0.7 # at 2.8M tokens: planner must re-plan remaining
|
|
106
|
+
# work to fit, or ask the operator. Epics that
|
|
107
|
+
# cannot fit are split at spec time, never
|
|
108
|
+
# silently extended (extension is always an
|
|
109
|
+
# operator question). Checked by `smith budget
|
|
110
|
+
# alarm <session>`, which the /bs run loop runs at
|
|
111
|
+
# each wave merge — before P9-33 this number's
|
|
112
|
+
# only reader was a human remembering to look.
|
|
113
|
+
max_in_flight_tasks: null # Off by default. "Fan-out buys wall-clock
|
|
114
|
+
# without buying extra tokens" above is the
|
|
115
|
+
# standing decision, and it only holds while the
|
|
116
|
+
# token cap actually binds — which `smith wave
|
|
117
|
+
# check` now makes true. So this is NOT the
|
|
118
|
+
# mechanism that bounds cost; `cap_tokens` is,
|
|
119
|
+
# and it is checked regardless of how this field
|
|
120
|
+
# is set. This knob exists for an operator who
|
|
121
|
+
# wants a wall-clock or rate-limit bound for a
|
|
122
|
+
# reason of their own — a provider's
|
|
123
|
+
# concurrent-request limit, a laptop's CPU count
|
|
124
|
+
# — and setting it makes `smith wave check`
|
|
125
|
+
# refuse to admit a wave that would put more of
|
|
126
|
+
# the epic's tasks in flight (admitted, not yet
|
|
127
|
+
# merged) than this number at once.
|
|
128
|
+
|
|
129
|
+
task:
|
|
130
|
+
# Defaults a planner sizes a task's own `budget` block against. The task's
|
|
131
|
+
# declared numbers are what the gate checks; these are the shape of a
|
|
132
|
+
# reasonable declaration, not a second cap applied on top.
|
|
133
|
+
coder:
|
|
134
|
+
cap_tokens: 150000 # Second reader, added 2026-08-26: `smith wave
|
|
135
|
+
# check` prices a task that exists only in the
|
|
136
|
+
# event log at this number. A follow-up minted by
|
|
137
|
+
# `findings raise` has claims but no declared
|
|
138
|
+
# budget (D-48/P9-31) — no plan ever had a field
|
|
139
|
+
# for one — so the wave's sum would otherwise be
|
|
140
|
+
# unverifiable and the follow-up inadmissible,
|
|
141
|
+
# which would leave the factory's own repair path
|
|
142
|
+
# reachable only by hand-editing the plan the
|
|
143
|
+
# follow-up exists to avoid. This is a charge,
|
|
144
|
+
# not a waiver: it is the most such a task may
|
|
145
|
+
# spend, and a wave of follow-ups is refused when
|
|
146
|
+
# that many coder caps will not fit under the
|
|
147
|
+
# epic headroom. A task the PLAN declares with no
|
|
148
|
+
# `budget.tokens` is a different silence and is
|
|
149
|
+
# still refused — the schema requires that field,
|
|
150
|
+
# so its absence is a plan that never passed
|
|
151
|
+
# `smith plan validate`.
|
|
152
|
+
cap_diff_lines: 400 # excludes lockfiles/generated files
|
|
153
|
+
researcher:
|
|
154
|
+
cap_tokens: 60000
|
|
155
|
+
judges: # spec-reviewer, reviewer, verifier, grader
|
|
156
|
+
cap_tokens: 40000
|
|
157
|
+
# Not priced here, and dispatched anyway: security-reviewer, merger, tester,
|
|
158
|
+
# uiux, planner, scribe. `smith budget alarm` cannot put a number on those
|
|
159
|
+
# dispatches, so an epic that used one is reported unverifiable rather than
|
|
160
|
+
# under — the omission is visible instead of free. Left unpriced on purpose
|
|
161
|
+
# until there is real spend data to size them from; inventing a cap would
|
|
162
|
+
# make the projection look complete without making it true.
|
|
163
|
+
|
|
164
|
+
pre_code_budget:
|
|
165
|
+
# researcher + uiux together, before any code is written for the epic
|
|
166
|
+
share_of_epic_budget_max: 0.15
|
|
167
|
+
|
|
168
|
+
context_window:
|
|
169
|
+
# Working-memory discipline (agent-constraints.md "context window
|
|
170
|
+
# (factory-wide)"). Distinct from the caps above: those are about cost, this
|
|
171
|
+
# is about the quality of what a session still holds. Prompt-level today
|
|
172
|
+
# (baked into .claude/agents/*); mechanical with the phase-9 loop daemon.
|
|
173
|
+
auto_compact_ratio: 0.6 # compact at 60% of the model's context window.
|
|
174
|
+
# The other 40% is the room needed to ACT on the
|
|
175
|
+
# summary: a session that compacts at 90% knows
|
|
176
|
+
# what to do and has no budget left to do it,
|
|
177
|
+
# which is what produces re-read loops, dropped
|
|
178
|
+
# constraints and half-applied edits.
|
|
179
|
+
max_compactions_per_task: 1 # a second one means the task is over-scoped:
|
|
180
|
+
# stop and return economy.budget-exceeded to the
|
|
181
|
+
# planner for splitting, exactly as a token-cap
|
|
182
|
+
# hit does. Chained compactions are how a session
|
|
183
|
+
# quietly loses the contract it was given.
|
|
184
|
+
counts_against_budget: true # tokens spent before a compaction still count
|
|
185
|
+
# against the caps above — compacting is not a
|
|
186
|
+
# way around the 150k coder cap.
|
|
187
|
+
compacting_roles: [planner, coder, tester, researcher, uiux, scribe]
|
|
188
|
+
narrowing_roles: [reviewer, verifier, grader, spec-reviewer, security-reviewer, merger, auditor]
|
|
189
|
+
# narrowing_roles hold evidence rather than build on it: a judge that compacts
|
|
190
|
+
# summarizes away the diff it is judging, and a merger that compacts loses one
|
|
191
|
+
# side of the conflict. At 60% they narrow scope and return a verdict on what
|
|
192
|
+
# they hold (judges) or escalate to the operator (merger) — they never compact.
|
|
193
|
+
keep:
|
|
194
|
+
- epic/task ids and the live plan version
|
|
195
|
+
- claimed globs
|
|
196
|
+
- decisions taken and why
|
|
197
|
+
- what is done vs. what remains
|
|
198
|
+
- open questions and blocking unknowns
|
|
199
|
+
drop:
|
|
200
|
+
- raw file contents
|
|
201
|
+
- raw tool/command output
|
|
202
|
+
- superseded reasoning
|
|
203
|
+
note: >
|
|
204
|
+
Durable state belongs in the event log, not the context window
|
|
205
|
+
(architecture §7). Anything that must survive a compaction is an event or a
|
|
206
|
+
result artifact; the window is working memory, the log is memory.
|
|
207
|
+
|
|
208
|
+
escalation_ladder:
|
|
209
|
+
# architecture §12, agent-constraints.md "budgets (factory-wide)"
|
|
210
|
+
#
|
|
211
|
+
# `failed_rounds` and `enforce` are the machine-readable half, added P9-32.
|
|
212
|
+
# src/escalation.ts reads them and `smith escalation check` asserts them
|
|
213
|
+
# against the log; `trigger`/`action` stay as the human contract and must say
|
|
214
|
+
# the same thing. A failed round is one `gate-outcome` with outcome
|
|
215
|
+
# `blocked` for the task.
|
|
216
|
+
#
|
|
217
|
+
# enforce values (closed set — an unrecognised one is reported unverifiable,
|
|
218
|
+
# never skipped):
|
|
219
|
+
# model-tier — the next builder dispatch for the task must be a strictly
|
|
220
|
+
# higher model_tier than the one that failed.
|
|
221
|
+
# operator — the ladder stops here: no further builder dispatch until the
|
|
222
|
+
# operator has answered (a `user_prompt`, or a waiver granted
|
|
223
|
+
# or denied for the task).
|
|
224
|
+
# A rung with no `action` declares no obligation and asserts nothing; a rung
|
|
225
|
+
# with an `action` and no `enforce` is a rule nothing can check, and the
|
|
226
|
+
# audit says so rather than passing it.
|
|
227
|
+
rungs:
|
|
228
|
+
- rung: 1
|
|
229
|
+
trigger: bounded retry on the same contract
|
|
230
|
+
failed_rounds: 1
|
|
231
|
+
- rung: 2
|
|
232
|
+
trigger: 2 failed rounds on the same task
|
|
233
|
+
failed_rounds: 2
|
|
234
|
+
enforce: model-tier
|
|
235
|
+
action: escalate model tier automatically (sonnet -> opus), logged
|
|
236
|
+
- rung: 3
|
|
237
|
+
trigger: 3 failed rounds on the same task
|
|
238
|
+
failed_rounds: 3
|
|
239
|
+
enforce: operator
|
|
240
|
+
action: escalate to operator
|
|
241
|
+
note: >
|
|
242
|
+
Rungs are never skipped and never looped past their bound; doing so is a
|
|
243
|
+
coordination error (deadlock/livelock/starvation, taxonomy.yml).
|
|
244
|
+
|
|
245
|
+
effort_scaling:
|
|
246
|
+
# architecture §15.3, §17 (equal-budget study: extra agents buy wall-clock
|
|
247
|
+
# + isolation, not reasoning)
|
|
248
|
+
#
|
|
249
|
+
# How many agents a step gets. *Which steps run at all* is the other half,
|
|
250
|
+
# and it lives in `factory/policies/effort.yml`: a per-epic tier
|
|
251
|
+
# (small/medium/huge) that scales the deliberation steps — pre-code briefs,
|
|
252
|
+
# spec-review rounds, the plan quorum, grader rounds, verifier severities,
|
|
253
|
+
# the closing spec review — and nothing else. Ask `smith effort show`; the
|
|
254
|
+
# two files do not overlap, and neither may touch a mechanical gate.
|
|
255
|
+
- rule: small fixes get a single coder, not a fan-out.
|
|
256
|
+
- rule: >
|
|
257
|
+
wave fan-out (concurrent dispatch of multiple coder/tester pairs) only
|
|
258
|
+
when claims are disjoint per static analysis (architecture §5); claims
|
|
259
|
+
that overlap get a dependency edge and run serially instead.
|
|
260
|
+
- rule: >
|
|
261
|
+
the scheduler prefers the cheapest shape that meets the acceptance
|
|
262
|
+
criteria — fan-out is a wall-clock optimization, not a default.
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# Blacksmith — multi-provider cross-check policy
|
|
2
|
+
#
|
|
3
|
+
# architecture §6 "Multi-provider cross-check (anti tunnel vision)". The
|
|
4
|
+
# judge tier is provider-agnostic by contract; the config seam has existed
|
|
5
|
+
# since Phase 1, and Phase 8 (`factory/orchestrator/src/providers/`,
|
|
6
|
+
# `src/quorum.ts`) wires it up for real: `transport` selects which
|
|
7
|
+
# transport module runs a provider (cli-transport.ts / api-transport.ts),
|
|
8
|
+
# `mode` gates how much power its verdict has (see below). `enabled: false`
|
|
9
|
+
# means never invoked at all, regardless of `mode`, and both external
|
|
10
|
+
# providers ship that way. Not a judgement about them: `enabled` is the one
|
|
11
|
+
# field in this file that describes a MACHINE rather than the contract — it
|
|
12
|
+
# answers "does this box have that judge", and for a box the repo has never
|
|
13
|
+
# seen the honest answer is no. Switched on where the precondition is unmet a
|
|
14
|
+
# provider does not fail loudly; it spends a doomed call on every quorum
|
|
15
|
+
# trigger, has it caught, and leaves a failure row that reads like an outage.
|
|
16
|
+
# So turn on the one you actually have — a one-line edit, runbook §2, and
|
|
17
|
+
# `smith judge preflight` says beforehand whether it will work.
|
|
18
|
+
# Full setup/calibration/promotion procedure: docs/runbooks/providers.md.
|
|
19
|
+
#
|
|
20
|
+
# Shadow mode (Phase 8 design decision): `mode: shadow` means a provider's
|
|
21
|
+
# verdicts are RECORDED (judge-verdict events, `smith stats providers`) but
|
|
22
|
+
# have ZERO gating power — quorum.ts falls back to the native verdict alone
|
|
23
|
+
# whenever no provider in a case is `mode: active`. Flipping a provider to
|
|
24
|
+
# `mode: active` in this file (an operator edit, never a runtime write) is
|
|
25
|
+
# the entire promotion step; `mode: shadow` or `enabled: false` is the
|
|
26
|
+
# entire rollback step.
|
|
27
|
+
|
|
28
|
+
providers:
|
|
29
|
+
claude:
|
|
30
|
+
kind: native
|
|
31
|
+
enabled: true
|
|
32
|
+
codex:
|
|
33
|
+
kind: api
|
|
34
|
+
transport: cli # `codex exec` headless — ChatGPT-subscription auth (`codex login` once), no API key spend. See docs/runbooks/providers.md.
|
|
35
|
+
command: codex
|
|
36
|
+
# NOT `--json` (D-118): that wraps the verdict as an escaped string inside
|
|
37
|
+
# an `item.completed` event, where no JSON scanner can reach it, and puts a
|
|
38
|
+
# `thread.started` banner ahead of it. Plain `exec` writes the agent's final
|
|
39
|
+
# message — and only that — to stdout.
|
|
40
|
+
#
|
|
41
|
+
# Re-derived against the live binary on 2026-08-17, codex-cli 0.147.0, on a
|
|
42
|
+
# subscribed account (D-118 was originally derived from two calls made while
|
|
43
|
+
# the account was near its quota limit). Both layers still hold, unchanged:
|
|
44
|
+
# `--json` still nests the verdict in `item.completed.item.text`, and plain
|
|
45
|
+
# `exec` still echoes the whole prompt to stderr — which the transport merges
|
|
46
|
+
# into its extraction buffer by design — so a prompt containing `[]` or `{}`
|
|
47
|
+
# still plants decoy JSON ahead of the answer. Over that real buffer the two
|
|
48
|
+
# extractor generations diverge exactly as D-118 claimed: first-parseable
|
|
49
|
+
# returns `[]`, first-validating returns the verdict. If you change these
|
|
50
|
+
# args, re-run that comparison rather than reasoning about it.
|
|
51
|
+
args: ["exec", "--color", "never"]
|
|
52
|
+
model_tier: mid # taxonomy.yml model_tier — judge tier, architecture §4
|
|
53
|
+
# PRECONDITION: `codex` on PATH, and `codex login` run on every machine
|
|
54
|
+
# that dispatches a quorum trigger. Without either the CLI transport still
|
|
55
|
+
# spawns, fails, and quorum.ts records a provider error per trigger —
|
|
56
|
+
# caught, so nothing gates on it, but it is latency and noise.
|
|
57
|
+
# `smith judge preflight` checks the first half locally; only a real call
|
|
58
|
+
# can check the second.
|
|
59
|
+
# `auto`, not `true`. This is the one field in the file that describes a
|
|
60
|
+
# MACHINE, and the file is checked in, so `true` here would be a claim
|
|
61
|
+
# about every clone: the day this was written `true`, CI — which has no
|
|
62
|
+
# `codex` binary — started failing `judge preflight`'s soundness check,
|
|
63
|
+
# correctly, for a line that was true where it was typed.
|
|
64
|
+
#
|
|
65
|
+
# `auto` resolves against the precondition above, on the box reading the
|
|
66
|
+
# file: `codex` on PATH means enabled, no `codex` means silently off, and
|
|
67
|
+
# neither box needs to edit this line. What it cannot know is the second
|
|
68
|
+
# half — whether `codex login` was run — so a box with the binary and no
|
|
69
|
+
# session still spends a call per trigger and records the failure.
|
|
70
|
+
#
|
|
71
|
+
# Turned on for THIS box, 2026-09-02, by the operator who owns it: `codex`
|
|
72
|
+
# resolves on PATH and `codex login` is done, both checked by `smith judge
|
|
73
|
+
# preflight` and by a real `codex exec` call. Rollback for one box is
|
|
74
|
+
# `enabled: false` here, which `auto` never overrides.
|
|
75
|
+
enabled: auto
|
|
76
|
+
# `active`, not `shadow`: the operator's call (docs/runbooks/providers.md
|
|
77
|
+
# §3 offers shadow-then-calibrate and this box declined it).
|
|
78
|
+
#
|
|
79
|
+
# Be exact about what that buys today, because it is less than it looks:
|
|
80
|
+
# a shadow provider is still CALLED and still recorded as a participant
|
|
81
|
+
# (quorum.ts header note) — shadow costs the same call and forfeits only
|
|
82
|
+
# gating power. With one external judge promoted and `min_providers: 2`,
|
|
83
|
+
# a finding claude raised escalates either way. The difference is where
|
|
84
|
+
# codex's rationale lands: under `gating.rationales` when active, only
|
|
85
|
+
# under `participants` when shadow.
|
|
86
|
+
#
|
|
87
|
+
# So the promotion is not what makes the cross-check real — a second
|
|
88
|
+
# provider is (see quorum_rule.accept_non_gating_actives). What it does
|
|
89
|
+
# is make this box gate the moment one is enabled, with no second edit
|
|
90
|
+
# and no second decision to remember. Rollback is `mode: shadow` here.
|
|
91
|
+
mode: active
|
|
92
|
+
deepseek:
|
|
93
|
+
kind: api
|
|
94
|
+
transport: api
|
|
95
|
+
base_url: https://api.deepseek.com
|
|
96
|
+
model: deepseek-reasoner
|
|
97
|
+
response_format_json_object: true
|
|
98
|
+
api_key_env: DEEPSEEK_API_KEY
|
|
99
|
+
model_tier: mid
|
|
100
|
+
# PRECONDITION: DEEPSEEK_API_KEY must be readable by the dispatching
|
|
101
|
+
# process -- exported, or in the clone's gitignored `.env`, which the CLI
|
|
102
|
+
# loads at start without overriding anything already exported (D-270).
|
|
103
|
+
# Without it api-transport.ts throws `Environment variable "DEEPSEEK_API_KEY"
|
|
104
|
+
# is not set` on every quorum trigger; quorum.ts catches it, so the gate
|
|
105
|
+
# holds on the native verdict alone, but each trigger records a provider
|
|
106
|
+
# error. `smith judge preflight` reports the name it looked for and never
|
|
107
|
+
# the value it found, so it is safe to run anywhere.
|
|
108
|
+
#
|
|
109
|
+
# `auto`, not `true`, for the same reason codex is: this file is checked in
|
|
110
|
+
# and the key is a fact about one box. A box holding the key gets a second
|
|
111
|
+
# vendor; a box without one -- CI included -- silently gets none, and
|
|
112
|
+
# neither has to edit this line.
|
|
113
|
+
#
|
|
114
|
+
# Funded for THIS box, 2026-09-04, by the operator who owns the key.
|
|
115
|
+
enabled: auto
|
|
116
|
+
# `shadow`, deliberately, and the difference from codex above is the point:
|
|
117
|
+
# this box declined shadow for codex and chose it here. A shadow provider
|
|
118
|
+
# is still called and still recorded as a participant -- it forfeits only
|
|
119
|
+
# gating power -- so what the mode buys is a run of recorded disagreement
|
|
120
|
+
# to read before a second vendor gets a vote (docs/runbooks/providers.md
|
|
121
|
+
# §3). Promote by editing this to `active`; that is the calibration pass,
|
|
122
|
+
# and it is an operator decision, not a default.
|
|
123
|
+
mode: shadow
|
|
124
|
+
|
|
125
|
+
quorum_triggers:
|
|
126
|
+
# architecture §6, agent-constraints.md "cross-check (phase 8)" — all four,
|
|
127
|
+
# nothing more.
|
|
128
|
+
- any S1/S2 finding, before it blocks a task
|
|
129
|
+
- epic-level final verdict, before the integration PR opens
|
|
130
|
+
- same-mistake findings (judgment.same-mistake)
|
|
131
|
+
- planner verdicts below the confidence threshold
|
|
132
|
+
|
|
133
|
+
plan_quorum:
|
|
134
|
+
# architecture §6 "Plan quorum" — critique-only, never committee writes.
|
|
135
|
+
triggers:
|
|
136
|
+
- epic budget ≥ 50% of budgets.yml per-epic cap
|
|
137
|
+
- epic case is infra, or carries a security-sensitive tag
|
|
138
|
+
- planner verdict confidence below threshold
|
|
139
|
+
judges: 2 providers distinct from the planner's
|
|
140
|
+
on_disagreement: surface both rationales to the operator with the plan
|
|
141
|
+
# Machine-readable form of the three triggers above, read by
|
|
142
|
+
# src/crosscheck.ts's parsePlanQuorum() -> src/planQuorum.ts's
|
|
143
|
+
# evaluatePlanQuorumTriggers(). The prose above is the human contract;
|
|
144
|
+
# these fields are what the code actually gates on.
|
|
145
|
+
budget_ratio: 0.5 # trigger 1: sum(plan.tasks[].budget.tokens) >= this * budgets.yml epic.cap_tokens
|
|
146
|
+
# Same line task-spec.schema.json's `confidence` field and scheduler.yml's
|
|
147
|
+
# maintenance.auto_schedule_confidence both draw (>=0.8 auto-schedules).
|
|
148
|
+
# Repeated here (not imported) because this is a crosscheck trigger, not a
|
|
149
|
+
# scheduling decision — the two policies are allowed to diverge later.
|
|
150
|
+
confidence_threshold: 0.8 # trigger 3
|
|
151
|
+
security_cases: [infra] # trigger 2, taxonomy.yml `case` vocabulary
|
|
152
|
+
security_roles: [security-reviewer] # trigger 2, taxonomy.yml `agent` vocabulary
|
|
153
|
+
# trigger 2's third arm: contract.nonfunctional_clauses text matched
|
|
154
|
+
# case-insensitively as a substring (task-spec.schema.json has no `tags`
|
|
155
|
+
# field to check instead).
|
|
156
|
+
security_keywords:
|
|
157
|
+
- auth
|
|
158
|
+
- authz
|
|
159
|
+
- authentication
|
|
160
|
+
- authorization
|
|
161
|
+
- credential
|
|
162
|
+
- secret
|
|
163
|
+
- token
|
|
164
|
+
- password
|
|
165
|
+
- crypto
|
|
166
|
+
- encryption
|
|
167
|
+
- tls
|
|
168
|
+
- injection
|
|
169
|
+
- xss
|
|
170
|
+
- csrf
|
|
171
|
+
- sandbox
|
|
172
|
+
- permission
|
|
173
|
+
- privilege
|
|
174
|
+
- sanitize
|
|
175
|
+
- escalation
|
|
176
|
+
|
|
177
|
+
quorum_rule:
|
|
178
|
+
agreement: "2-of-3"
|
|
179
|
+
min_providers: 2
|
|
180
|
+
tie_break: a third provider breaks the tie, or the case escalates to the
|
|
181
|
+
operator with both rationales side by side.
|
|
182
|
+
# This box promotes exactly one external judge (see providers.codex), and one
|
|
183
|
+
# cannot meet min_providers 2. On a finding claude raised — which is nearly
|
|
184
|
+
# all of them, since gate.ts defaults a finding's finder to the native
|
|
185
|
+
# provider — claude is excluded as the finder, the gating pool is [codex]
|
|
186
|
+
# alone, and the case escalates for insufficient-providers just as it did
|
|
187
|
+
# before the promotion, now having paid for the call.
|
|
188
|
+
#
|
|
189
|
+
# Set true because that is still the better trade here, not because the
|
|
190
|
+
# arithmetic is in doubt: the escalation now carries a second model's
|
|
191
|
+
# rationale instead of none, and the day a second provider is enabled the
|
|
192
|
+
# pool meets the rule with no further edit. What it buys today is a human
|
|
193
|
+
# deciding with two opinions in front of them rather than one — which is the
|
|
194
|
+
# whole point of the file — not an automatic override.
|
|
195
|
+
#
|
|
196
|
+
# It silences one advisory in `smith judge preflight` and nothing else. A
|
|
197
|
+
# provider enabled where it cannot be called is still a problem, and
|
|
198
|
+
# `gating.canDecide` still reports false, because it is false.
|
|
199
|
+
accept_non_gating_actives: true
|
|
200
|
+
|
|
201
|
+
asymmetric_roles:
|
|
202
|
+
finder_ne_critic: true
|
|
203
|
+
critic_mandate: refute, not confirm
|
|
204
|
+
# The machine-readable form of finder_ne_critic (P9-23). Each pair says
|
|
205
|
+
# "this critic must not run on that finder's own model"; `smith dispatch
|
|
206
|
+
# check <session>` asserts it against the event log after the fact, using
|
|
207
|
+
# the `model` field every dispatch_decision now carries. Without this list
|
|
208
|
+
# the rule was unenforceable prose: it named no roles.
|
|
209
|
+
pairs:
|
|
210
|
+
- finder: planner
|
|
211
|
+
critic: spec-reviewer
|
|
212
|
+
- finder: reviewer
|
|
213
|
+
critic: verifier
|
|
214
|
+
note: >
|
|
215
|
+
adversarial stage-gating is expected to kill most candidate findings
|
|
216
|
+
before they cost a round-trip (Refute-or-Promote, §17); reviewer quality
|
|
217
|
+
is tracked as kill-rate (findings that survive verification and land as
|
|
218
|
+
verified fixes), never findings volume.
|
|
219
|
+
|
|
220
|
+
role_isolation:
|
|
221
|
+
# The other half of "a worker never grades its own work". `finder_ne_critic`
|
|
222
|
+
# above compares MODELS, which is the wrong question for a tester: a tester
|
|
223
|
+
# may legitimately run on the coder's model, and forcing a second vendor onto
|
|
224
|
+
# it would buy nothing. What it may not do is run inside the coder's own
|
|
225
|
+
# turn — a coder that writes its own tests grades itself, and every gate
|
|
226
|
+
# downstream still goes green.
|
|
227
|
+
#
|
|
228
|
+
# The only evidence of a separate turn the event log can hold is a separate
|
|
229
|
+
# `dispatch_decision`, written by the node that dispatched, never by an agent
|
|
230
|
+
# about itself. `smith tester check <session>` asserts that against the log
|
|
231
|
+
# after the fact, per `testgate-result`.
|
|
232
|
+
#
|
|
233
|
+
# That reading survives delegation.yml granting `Agent` because of what the
|
|
234
|
+
# grant costs: a grantee must open its own session first, so its dispatches
|
|
235
|
+
# are its own log's, and no grant may hand a role its own auditor from the
|
|
236
|
+
# `pairs` below. `smith delegation check` enforces both, which is what keeps
|
|
237
|
+
# a widened grant from quietly disarming this file.
|
|
238
|
+
#
|
|
239
|
+
# One entry, and it is meant to stay one until a second role starts grading
|
|
240
|
+
# work it could have written: every other grader in the pipeline is a judge,
|
|
241
|
+
# and a judge cannot write at all (`role_write_scopes` in guardrails.yml).
|
|
242
|
+
pairs:
|
|
243
|
+
- worker: coder
|
|
244
|
+
auditor: tester
|
|
245
|
+
note: >
|
|
246
|
+
the audit answers "was a tester dispatched separately, and did it report,
|
|
247
|
+
before this task's tests were graded?" — not "did that tester write the
|
|
248
|
+
tests". Nothing in the log distinguishes a test file the tester authored
|
|
249
|
+
from one it merely ran; `role-write-scope` fences where a leased tester may
|
|
250
|
+
write, and the two checks are complementary, not substitutes.
|
|
251
|
+
|
|
252
|
+
independent_finder:
|
|
253
|
+
# Everything else on this page is SUBTRACTIVE. quorum.ts's vocabulary is
|
|
254
|
+
# `confirm | refute`, a critic is handed one claim and told to kill it, and
|
|
255
|
+
# the strongest thing a quorum can do is drop a finding the native reviewer
|
|
256
|
+
# already raised. That makes the external tier a brake and never an eye: a
|
|
257
|
+
# bug the native reviewer's context did not surface is a bug no amount of
|
|
258
|
+
# cross-checking will ever reach, because nothing outside that context is
|
|
259
|
+
# ever asked to look.
|
|
260
|
+
#
|
|
261
|
+
# This block is the other direction. A finder from a different vendor reads
|
|
262
|
+
# the same diff in a fresh context, returns its own evidence, and
|
|
263
|
+
# crossFinding.ts reconciles the two lists. A quorum can now RAISE.
|
|
264
|
+
#
|
|
265
|
+
# Three things it deliberately does not do:
|
|
266
|
+
#
|
|
267
|
+
# 1. Silence is not a refutation. A native finding the independent finder did
|
|
268
|
+
# not raise stays exactly as it was. The finder was never asked about that
|
|
269
|
+
# claim — it was asked to read a diff — so its not mentioning something is
|
|
270
|
+
# absence of evidence. Subtracting on it would let a lazy or truncated
|
|
271
|
+
# second opinion delete real findings.
|
|
272
|
+
# 2. Same file and category with different wording is not the same finding.
|
|
273
|
+
# That is `co-located`: recorded for the operator, never merged and never
|
|
274
|
+
# auto-raised, because nothing here can tell one bug described twice from
|
|
275
|
+
# two bugs in one function.
|
|
276
|
+
# 3. An independent-only finding is minted, not privileged. It enters the
|
|
277
|
+
# gate as an ordinary finding and, at S1/S2, meets the same
|
|
278
|
+
# `quorum_triggers` critic as any other — so a third-party vendor's
|
|
279
|
+
# unshared opinion still has to survive a refute pass before it blocks.
|
|
280
|
+
enabled: false # ships off: this is the one judge call that costs a diff
|
|
281
|
+
mode: shadow # reconciliations are recorded and gate nothing; flip to `active` only after a calibration pass (docs/runbooks/providers.md)
|
|
282
|
+
# No code default: crosscheck.ts falls back to an EMPTY list, so this line is
|
|
283
|
+
# the only place a vendor is ever chosen. Delete it and the finder refuses by
|
|
284
|
+
# name rather than picking one for you.
|
|
285
|
+
providers: [codex] # must be external and must not be the native provider — a "second opinion" from the same model is one opinion billed twice
|
|
286
|
+
# THE OPERATOR MANDATE. A critic judges a CLAIM, so quorum.ts can send the
|
|
287
|
+
# summary and the failure scenario and never the source. A finder cannot: it
|
|
288
|
+
# has nothing to read but the diff. Shipping worktree source to a third-party
|
|
289
|
+
# API is a decision no code in this repo has standing to make quietly, so it
|
|
290
|
+
# is a switch, it ships false, and `smith crossfind run` REFUSES rather than
|
|
291
|
+
# falling back to a diffless "find bugs" prompt that would invent findings.
|
|
292
|
+
send_diff: false
|
|
293
|
+
max_diff_bytes: 120000 # refuse rather than truncate: half a diff produces confident findings about code that is not there
|
|
294
|
+
# How a fingerprint both sides raised resolves when they disagree on how bad
|
|
295
|
+
# it is. `highest-wins` takes the worse of the two; `native-wins` keeps the
|
|
296
|
+
# native severity and records the disagreement without acting on it.
|
|
297
|
+
severity_resolution: highest-wins
|
|
298
|
+
|
|
299
|
+
mechanical_oracles_first: >
|
|
300
|
+
wherever a deterministic check exists (schema validation, mutation
|
|
301
|
+
testing, coverage, type checks), it runs first and its verdict is final —
|
|
302
|
+
LLM judgment, cross-provider or not, is reserved for what machines can't
|
|
303
|
+
decide.
|
|
304
|
+
|
|
305
|
+
trust_boundary: >
|
|
306
|
+
external providers judge; they never gain write access to worktrees or the
|
|
307
|
+
factory. Their findings are data, not commands. All external calls go
|
|
308
|
+
through the same event log and budget accounting as native dispatches.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Blacksmith — who may hold `Agent` (dispatch topology, architecture §4)
|
|
2
|
+
#
|
|
3
|
+
# Every other policy here tunes how work is judged. This one decides who may
|
|
4
|
+
# create work at all, so it lives in its own file: an operator widening a
|
|
5
|
+
# provider list in crosscheck.yml should not be one keystroke away from
|
|
6
|
+
# widening a dispatch grant.
|
|
7
|
+
#
|
|
8
|
+
# The rule the factory has always run on is "the dispatching node owns the
|
|
9
|
+
# event log for what it dispatches" (docs/standards/agent-constraints.md).
|
|
10
|
+
# Until D13 step 3 that was enforced by granting `Agent` to nobody, which also
|
|
11
|
+
# capped an epic at one operator session. A grantee that opens its own session
|
|
12
|
+
# with `smith session start <id> --continues <dispatch-event-id>` is a real
|
|
13
|
+
# dispatching node with a real log, so the cap can lift without the rule
|
|
14
|
+
# bending.
|
|
15
|
+
#
|
|
16
|
+
# What a grant may not do — asserted by `smith delegation check` and by
|
|
17
|
+
# delegation.test.ts against the shipped files, so a widened grant fails CI:
|
|
18
|
+
#
|
|
19
|
+
# - dispatch itself (a role that dispatches itself writes dispatches about
|
|
20
|
+
# itself, and `smith tester check` reads a second dispatch as proof of a
|
|
21
|
+
# second turn),
|
|
22
|
+
# - dispatch its own auditor from crosscheck.yml `role_isolation.pairs`
|
|
23
|
+
# (a coder that picks and prompts its own tester grades itself), or
|
|
24
|
+
# - dispatch its own critic from crosscheck.yml `asymmetric_roles.pairs`
|
|
25
|
+
# (a finder that picks its critic's model closes the tunnel-vision hole
|
|
26
|
+
# crosscheck.yml exists to open).
|
|
27
|
+
#
|
|
28
|
+
# `wave-runner` satisfies all three by construction: it is nobody's worker and
|
|
29
|
+
# nobody's finder. It is the only grant, and it is meant to stay the only one
|
|
30
|
+
# until a second role has an epic-shaped reason to outlive a window.
|
|
31
|
+
|
|
32
|
+
version: 1
|
|
33
|
+
|
|
34
|
+
grants:
|
|
35
|
+
wave-runner:
|
|
36
|
+
# One wave of `.claude/skills/bs/wave.md`, steps 2-10. Not planner and not
|
|
37
|
+
# spec-reviewer: planning is the epic tier's, and a wave that could re-plan
|
|
38
|
+
# itself could plan its way out of the plan it was admitted under.
|
|
39
|
+
may_dispatch: [coder, tester, grader, reviewer, verifier,
|
|
40
|
+
security-reviewer, uiux, researcher, merger, scribe]
|
|
41
|
+
# The whole point. A wave-runner that dispatches without opening a log
|
|
42
|
+
# writes its wave's dispatches into the epic's log under its own name, and
|
|
43
|
+
# every check that reads a dispatch as a separate turn starts lying.
|
|
44
|
+
must_open_session: true
|