@juzser/blacksmith 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/{auditor.md → bs-auditor.md} +8 -5
- package/.claude/agents/{coder.md → bs-coder.md} +8 -7
- package/.claude/agents/{grader.md → bs-grader.md} +16 -9
- package/.claude/agents/{merger.md → bs-merger.md} +4 -4
- package/.claude/agents/{planner.md → bs-planner.md} +20 -6
- package/.claude/agents/{researcher.md → bs-researcher.md} +6 -5
- package/.claude/agents/{reviewer.md → bs-reviewer.md} +16 -5
- package/.claude/agents/{scribe.md → bs-scribe.md} +11 -2
- package/.claude/agents/{security-reviewer.md → bs-security-reviewer.md} +9 -6
- package/.claude/agents/{spec-reviewer.md → bs-spec-reviewer.md} +18 -10
- package/.claude/agents/{tester.md → bs-tester.md} +40 -12
- package/.claude/agents/bs-uiux.md +257 -0
- package/.claude/agents/{verifier.md → bs-verifier.md} +7 -4
- package/.claude/agents/{wave-runner.md → bs-wave-runner.md} +14 -5
- package/.claude/skills/bs/SKILL.md +99 -21
- package/.claude/skills/bs/audit.md +45 -31
- package/.claude/skills/bs/dispatch.md +133 -47
- package/.claude/skills/bs/lessons.md +28 -14
- package/.claude/skills/bs/mcp.md +4 -4
- package/.claude/skills/bs/new.md +3 -3
- package/.claude/skills/bs/plan.md +57 -16
- package/.claude/skills/bs/report.md +11 -11
- package/.claude/skills/bs/run.md +190 -52
- package/.claude/skills/bs/status.md +14 -7
- package/.claude/skills/bs/ui.md +21 -5
- package/.claude/skills/bs/waivers.md +78 -2
- package/.claude/skills/bs/wave.md +143 -50
- package/README.md +53 -32
- package/factory/orchestrator/dist/agentNames.js +43 -0
- package/factory/orchestrator/dist/agents-registry.js +11 -4
- package/factory/orchestrator/dist/agentsSync.js +70 -13
- package/factory/orchestrator/dist/artifacts.js +31 -0
- package/factory/orchestrator/dist/attribution.js +4 -0
- package/factory/orchestrator/dist/audit.js +245 -20
- package/factory/orchestrator/dist/autonomy.js +6 -2
- package/factory/orchestrator/dist/budgetAlarm.js +132 -37
- package/factory/orchestrator/dist/budgets.js +302 -84
- package/factory/orchestrator/dist/claims.js +9 -19
- package/factory/orchestrator/dist/cli.js +1087 -116
- package/factory/orchestrator/dist/cliName.js +27 -0
- package/factory/orchestrator/dist/crossFinding.js +20 -0
- package/factory/orchestrator/dist/crosscheck.js +136 -8
- package/factory/orchestrator/dist/daemon.js +352 -67
- package/factory/orchestrator/dist/db/projector.js +327 -54
- package/factory/orchestrator/dist/db/queries.js +1831 -94
- package/factory/orchestrator/dist/db/schema.js +57 -4
- package/factory/orchestrator/dist/declaredArtifactLine.js +18 -0
- package/factory/orchestrator/dist/delegation.js +86 -19
- package/factory/orchestrator/dist/diffstat.js +15 -8
- package/factory/orchestrator/dist/dispatchLint.js +108 -0
- package/factory/orchestrator/dist/effort.js +14 -2
- package/factory/orchestrator/dist/effortTiers.js +13 -0
- package/factory/orchestrator/dist/env.js +26 -0
- package/factory/orchestrator/dist/epic.js +494 -42
- package/factory/orchestrator/dist/epicBudget.js +93 -0
- package/factory/orchestrator/dist/epicWidth.js +22 -5
- package/factory/orchestrator/dist/errorIssues.js +182 -11
- package/factory/orchestrator/dist/escalation.js +2 -2
- package/factory/orchestrator/dist/events.js +164 -14
- package/factory/orchestrator/dist/feedback.js +169 -0
- package/factory/orchestrator/dist/findingContext.js +26 -0
- package/factory/orchestrator/dist/findings.js +108 -14
- package/factory/orchestrator/dist/gate.js +120 -10
- package/factory/orchestrator/dist/gh.js +19 -2
- package/factory/orchestrator/dist/git.js +20 -2
- package/factory/orchestrator/dist/goalCheck.js +2 -2
- package/factory/orchestrator/dist/harness.js +25 -17
- package/factory/orchestrator/dist/hookDecision.js +411 -39
- package/factory/orchestrator/dist/integration.js +20 -8
- package/factory/orchestrator/dist/issueReporter.js +81 -16
- package/factory/orchestrator/dist/judgePreflight.js +25 -4
- package/factory/orchestrator/dist/judgeRoles.js +47 -0
- package/factory/orchestrator/dist/judgeStopHook.js +239 -0
- package/factory/orchestrator/dist/judges.js +419 -21
- package/factory/orchestrator/dist/lessons.js +190 -11
- package/factory/orchestrator/dist/logCache.js +291 -0
- package/factory/orchestrator/dist/mcp.js +6 -6
- package/factory/orchestrator/dist/paths.js +20 -7
- package/factory/orchestrator/dist/plan.js +399 -33
- package/factory/orchestrator/dist/planQuorum.js +5 -2
- package/factory/orchestrator/dist/policy.js +1275 -30
- package/factory/orchestrator/dist/projectName.js +25 -0
- package/factory/orchestrator/dist/projects.js +12 -1
- package/factory/orchestrator/dist/promptCapture.js +306 -0
- package/factory/orchestrator/dist/promptHook.js +33 -0
- package/factory/orchestrator/dist/promptImport.js +281 -0
- package/factory/orchestrator/dist/prompts.js +13 -0
- package/factory/orchestrator/dist/providers/schema-validate.js +2 -2
- package/factory/orchestrator/dist/providers/types.js +1 -1
- package/factory/orchestrator/dist/queue.js +471 -30
- package/factory/orchestrator/dist/quorum.js +18 -5
- package/factory/orchestrator/dist/results.js +25 -7
- package/factory/orchestrator/dist/roadmap.js +10 -5
- package/factory/orchestrator/dist/run-cli.js +15 -10
- package/factory/orchestrator/dist/runner.js +2 -2
- package/factory/orchestrator/dist/scheduler.js +3 -3
- package/factory/orchestrator/dist/schemas.js +56 -8
- package/factory/orchestrator/dist/severity.js +8 -4
- package/factory/orchestrator/dist/spec.js +246 -12
- package/factory/orchestrator/dist/specChange.js +13 -9
- package/factory/orchestrator/dist/stack.js +6 -6
- package/factory/orchestrator/dist/storeRoot.js +90 -0
- package/factory/orchestrator/dist/symbols.js +261 -6
- package/factory/orchestrator/dist/taskEvents.js +192 -31
- package/factory/orchestrator/dist/taskStatus.js +14 -0
- package/factory/orchestrator/dist/taxonomy.js +11 -0
- package/factory/orchestrator/dist/testerAudit.js +31 -12
- package/factory/orchestrator/dist/testgate.js +20 -16
- package/factory/orchestrator/dist/uiuxGate.js +83 -0
- package/factory/orchestrator/dist/usage.js +113 -48
- package/factory/orchestrator/dist/waivers.js +99 -12
- package/factory/orchestrator/dist/waveBudget.js +2 -0
- package/factory/orchestrator/dist/waveConcurrency.js +143 -7
- package/factory/orchestrator/dist/waveSchedule.js +84 -0
- package/factory/orchestrator/dist/workroot.js +1 -1
- package/factory/orchestrator/dist/worktree.js +76 -21
- package/factory/orchestrator/drizzle/0013_operator_feedback.sql +19 -0
- package/factory/orchestrator/drizzle/0014_misty_madripoor.sql +2 -0
- package/factory/orchestrator/drizzle/0015_gifted_omega_flight.sql +1 -0
- package/factory/orchestrator/drizzle/0016_milestones_project_default.sql +30 -0
- package/factory/orchestrator/drizzle/0017_events_raw_cli_session_id.sql +2 -0
- package/factory/orchestrator/drizzle/meta/_journal.json +35 -0
- package/factory/policies/budgets.yml +123 -33
- package/factory/policies/crosscheck.yml +51 -13
- package/factory/policies/delegation.yml +3 -3
- package/factory/policies/effort.yml +18 -10
- package/factory/policies/guardrails.yml +8 -6
- package/factory/policies/harness.yml +5 -5
- package/factory/policies/lessons.md +53 -2
- package/factory/policies/scheduler.yml +4 -4
- package/factory/policies/sensitive-paths.yml +1 -1
- package/factory/policies/severity.yml +13 -1
- package/factory/policies/stack.yml +6 -6
- package/factory/policies/taxonomy.yml +9 -3
- package/factory/policies/worktree.yml +15 -9
- package/factory/specs/roadmap.md +27 -3
- package/factory/specs/schema/event.schema.json +8 -1
- package/factory/specs/schema/result.schema.json +21 -8
- package/factory/specs/schema/task-spec.schema.json +12 -0
- package/package.json +16 -12
- package/.claude/agents/uiux.md +0 -133
package/.claude/skills/bs/run.md
CHANGED
|
@@ -15,19 +15,33 @@ epic. "Hand the wave over" below says how, and says the one thing that makes
|
|
|
15
15
|
it safe.
|
|
16
16
|
|
|
17
17
|
Ask the epic's effort tier once, at the top of the run, and keep the answer
|
|
18
|
-
for the whole epic — `
|
|
18
|
+
for the whole epic — `bs effort show --plan
|
|
19
19
|
factory/specs/active/<epic>/plan-vN.json`. Step 13 here, and steps 3, 6 and 7
|
|
20
20
|
of the wave playbook, each name the `profile` field that scales them; nothing
|
|
21
21
|
else in either file moves. Read `effective`, not `requested`: a plan whose
|
|
22
22
|
live tasks fire a security trigger is floored, and running it at the tier the
|
|
23
23
|
file asked for would be running it below the floor.
|
|
24
24
|
|
|
25
|
+
Ask the cross-provider gate once too, at the top of the run and before any
|
|
26
|
+
wave is admitted — `bs judge preflight --project <project-dir>`, the same
|
|
27
|
+
call INSTALL.md makes at setup. When it reports the gating pool cannot
|
|
28
|
+
decide (fewer active external providers than `quorum_rule.min_providers`
|
|
29
|
+
after OpenRouter substitution, or a provider the project's own
|
|
30
|
+
`.blacksmith/crosscheck.yml` names but this box cannot reach), STOP and ask
|
|
31
|
+
the operator before admitting a wave — per `.claude/skills/bs/SKILL.md`'s
|
|
32
|
+
"Talking to the operator" rules (options carry a confidence %; not restated
|
|
33
|
+
here): add an OpenRouter key, add a model, pick which providers to run with,
|
|
34
|
+
or continue without cross-check. Skipping this is not a shortcut — steps 15
|
|
35
|
+
and 16 spend a real judge call to find the same gap mid-run, after the epic's
|
|
36
|
+
work is already done. Full substitution and precedence mechanism:
|
|
37
|
+
docs/runbooks/providers.md §9d.
|
|
38
|
+
|
|
25
39
|
1. Ask the graph how wide this wave can be, then ask the gate whether it may
|
|
26
40
|
be that wide. Two commands, two different questions — do not skip to the
|
|
27
41
|
second with a set you picked by eye.
|
|
28
42
|
|
|
29
43
|
```bash
|
|
30
|
-
|
|
44
|
+
bs wave next factory/specs/active/<epic>/plan-vN.json \
|
|
31
45
|
--session <session-id> --repo <project-dir>
|
|
32
46
|
```
|
|
33
47
|
|
|
@@ -46,7 +60,7 @@ file asked for would be running it below the floor.
|
|
|
46
60
|
|
|
47
61
|
Then put that proposal through the gate that actually admits it:
|
|
48
62
|
|
|
49
|
-
`
|
|
63
|
+
`bs wave check factory/specs/active/<epic>/plan-vN.json <task-id>...`
|
|
50
64
|
— claims must be pairwise disjoint and share no `worktree.yml`
|
|
51
65
|
`serialize_always_globs` hotspot. A violation means cut a dependency
|
|
52
66
|
edge and run the pair serially instead of forcing the wave.
|
|
@@ -66,6 +80,16 @@ file asked for would be running it below the floor.
|
|
|
66
80
|
operator's call. So a proposed wave can still be refused here. That is
|
|
67
81
|
the gate working, not the proposal being wrong — drop tasks from the
|
|
68
82
|
tail of `wave` and re-check.
|
|
83
|
+
- With `--session`, `wave check` admits only live tasks of an ingested
|
|
84
|
+
plan. A plan version whose tasks the log never saw is refused
|
|
85
|
+
`cli.plan-not-ingested`: run `bs plan ingest` on it, then re-check.
|
|
86
|
+
An id with no task row is refused `plan.unknown-task`, and a task
|
|
87
|
+
that is `completed`, `superseded` or `waived` is refused
|
|
88
|
+
`cli.task-not-live`, naming each task and its status. Drop those ids.
|
|
89
|
+
A `failed` or `escalated` task stays admissible: an operator still
|
|
90
|
+
holds it, and the escalation ladder re-admits it. A follow-up
|
|
91
|
+
`findings raise` minted stays admissible while it is live, and a wave
|
|
92
|
+
of follow-ups alone still needs the plan it names ingested first.
|
|
69
93
|
- Never narrow a wave because a narrow one feels safer. A wave of one
|
|
70
94
|
passes every check in this file — one task is disjoint with nothing,
|
|
71
95
|
shares a hotspot with nothing, crosses no import edge — so `valid: true`
|
|
@@ -84,9 +108,9 @@ would cost:
|
|
|
84
108
|
- **Inline**, in this session, when the wave is small enough for this
|
|
85
109
|
window. Read `wave.md` and work it here.
|
|
86
110
|
- **In a session of its own**, when it is not, but you still want to drive
|
|
87
|
-
it yourself: `
|
|
111
|
+
it yourself: `bs session start <wave-id> --continues <session-id>#<n>`,
|
|
88
112
|
where `<n>` is the index of the event that admitted this wave.
|
|
89
|
-
- **Dispatched**, as the **`wave-runner`** agent, when the wave would eat
|
|
113
|
+
- **Dispatched**, as the **`bs-wave-runner`** agent, when the wave would eat
|
|
90
114
|
the window this epic needs to reach its own end. It is the only role
|
|
91
115
|
`factory/policies/delegation.yml` grants `Agent`, and it earns that by
|
|
92
116
|
opening its own log first — so hand it the event id of *your dispatch*,
|
|
@@ -106,12 +130,12 @@ to make (D13, D-266). What it does not buy is a shortcut. A wave session
|
|
|
106
130
|
opened without `--continues` writes into a log nothing here reads, and the
|
|
107
131
|
verbs below then report a wave that appears never to have run rather than
|
|
108
132
|
failing — which is the same silence, arriving as a green. When you dispatched
|
|
109
|
-
the wave rather than ran it, `
|
|
133
|
+
the wave rather than ran it, `bs delegation check <session-id>` is what
|
|
110
134
|
turns that silence into a red: a `wave-runner` that dispatched before opening
|
|
111
135
|
its log, or against the wrong event, is `unverifiable` and exits 1.
|
|
112
136
|
|
|
113
137
|
**Then check that it did.** Once the wave's tasks reach a terminal state,
|
|
114
|
-
`
|
|
138
|
+
`bs wave audit --session <id> --epic <epic>` reads the log back and
|
|
115
139
|
reports the width the wave actually ran at, not the width it was admitted
|
|
116
140
|
at. Ask it with *this* session: it folds the lineage, so it sees the
|
|
117
141
|
dispatches a wave session made under the admission this one wrote. Exit `1`
|
|
@@ -123,8 +147,9 @@ under it, which is the wrong `--state-dir`, or a wave session opened without
|
|
|
123
147
|
done; a green gate on serialized work is exactly the outcome the two
|
|
124
148
|
playbooks are written to prevent.
|
|
125
149
|
|
|
126
|
-
11. Repeat until every task in the live plan version is
|
|
127
|
-
`
|
|
150
|
+
11. Repeat until every task in the live plan version is `completed`/`waived`,
|
|
151
|
+
or `superseded` by a successor that is itself terminal-OK (recursively —
|
|
152
|
+
a successor chain counts too). Dispatch the **`bs-planner`** again for
|
|
128
153
|
the epic verdict against acceptance criteria — gaps found → a NEW
|
|
129
154
|
`plan-v(n+1)` with inferred tasks (never a live-graph mutation,
|
|
130
155
|
architecture §12), auto-scheduled at confidence ≥0.8 else parked for an
|
|
@@ -132,7 +157,7 @@ playbooks are written to prevent.
|
|
|
132
157
|
on it**, whichever step cut it:
|
|
133
158
|
|
|
134
159
|
```bash
|
|
135
|
-
|
|
160
|
+
bs plan ingest factory/specs/active/<epic>/plan-v<n+1>.json --project <project-dir> \
|
|
136
161
|
--session <session-id> --plan-version <n+1> --causal-parent <event-id>
|
|
137
162
|
```
|
|
138
163
|
|
|
@@ -147,11 +172,11 @@ playbooks are written to prevent.
|
|
|
147
172
|
branch**. Every gate up to here ran inside a task worktree, so every
|
|
148
173
|
green you have so far is a green about a worktree — the envkit epic
|
|
149
174
|
shipped six green lint gates on a branch whose `pnpm lint` exited 1
|
|
150
|
-
(D-42). Check out `
|
|
175
|
+
(D-42). Check out `bs/<epic>/integration` in the project first (this
|
|
151
176
|
command refuses to move your working tree, and refuses a dirty one):
|
|
152
177
|
|
|
153
178
|
```bash
|
|
154
|
-
|
|
179
|
+
bs integration check --epic <epic> --project <project-dir> \
|
|
155
180
|
--checks <checks.json> \
|
|
156
181
|
--session <session-id> --plan-version <n> --causal-parent <event-id>
|
|
157
182
|
```
|
|
@@ -167,25 +192,35 @@ playbooks are written to prevent.
|
|
|
167
192
|
check, the review and the close all claim to have verified a plan that
|
|
168
193
|
no longer exists. One dogfooded product epic closed at v3 with its
|
|
169
194
|
integration check and its closing spec review both stamped v1; the goal
|
|
170
|
-
check, passed the flag, said v3. Read the version off `
|
|
195
|
+
check, passed the flag, said v3. Read the version off `bs plan ingest`'s receipt and
|
|
171
196
|
pass it every time.
|
|
172
|
-
13. Dispatch the **`spec-reviewer`** again — this time against the code.
|
|
197
|
+
13. Dispatch the **`bs-spec-reviewer`** again — this time against the code.
|
|
173
198
|
The pre-code review at `/bs plan` step 3 read the spec against nothing;
|
|
174
199
|
this one reads it against the assembled branch, which is the only reading
|
|
175
200
|
that can see a criterion the finished code proves wrong. The envkit epic
|
|
176
201
|
deadlocked on exactly that: a correct S2 whose fix criterion 3 mandated
|
|
177
202
|
and criterion 1 forbade (D-33). It writes to a close-specific path so it
|
|
178
|
-
cannot clobber the pre-code artifact
|
|
203
|
+
cannot clobber the pre-code artifact. Put
|
|
204
|
+
`Declared artifact: /abs/path/to/state/results/<epic>.spec-review-close-vN.json`
|
|
205
|
+
inline on its own line in the dispatch prompt (the PreToolUse guard refuses
|
|
206
|
+
a judge dispatch without it); hand that file — each item naming the
|
|
179
207
|
`criterion_ref` it is against — to:
|
|
180
208
|
|
|
181
209
|
```bash
|
|
182
|
-
|
|
210
|
+
bs epic spec-review --epic <epic> --project <project-dir> \
|
|
183
211
|
--plan factory/specs/active/<epic>/plan-vN.json \
|
|
184
212
|
--reviewed-by spec-reviewer \
|
|
185
|
-
|
|
213
|
+
--evidence state/results/<epic>.spec-review-close-vN.json \
|
|
186
214
|
--session <session-id> --plan-version <n> --causal-parent <event-id>
|
|
187
215
|
```
|
|
188
216
|
|
|
217
|
+
`--evidence` is required unless you pass `--no-findings` instead, and
|
|
218
|
+
the two are exclusive: a reviewer that found nothing writes `[]` to that
|
|
219
|
+
path, and `--no-findings` is the operator attesting a review that ran
|
|
220
|
+
outside the factory — recorded as `attested_by: operator`, never read as
|
|
221
|
+
a file. A missing flag is refused (`cli.missing-flag`), because an empty
|
|
222
|
+
review read from an absent file is how a capped reviewer closed clean.
|
|
223
|
+
|
|
189
224
|
Its `dispatch_decision` — and step 14's, and the planner's when it
|
|
190
225
|
renders a verdict — goes against the reserved `<epic>/integration` ref,
|
|
191
226
|
never a `<epic>/spec-review-rN` you coin for the occasion
|
|
@@ -204,12 +239,12 @@ playbooks are written to prevent.
|
|
|
204
239
|
distinguishes them. It
|
|
205
240
|
exits 0 even when it raises findings: what it found blocks the plan, not
|
|
206
241
|
this command. A spec finding is unwaivable at S1/S2 and no task's diff can
|
|
207
|
-
contain the fix, so the answer is `
|
|
242
|
+
contain the fix, so the answer is `bs plan amend --plan … --findings …
|
|
208
243
|
--rationale … --sites …` (§6a of the operator guide). `--sites` is every
|
|
209
244
|
place that shape occurs, not only the file the finding was reported
|
|
210
245
|
against — answer it before writing the changes, because it is the question
|
|
211
246
|
that decides how much the amendment fixes (D-123). It cuts plan v(n+1):
|
|
212
|
-
ingest it (step 11's `
|
|
247
|
+
ingest it (step 11's `bs plan ingest`), go back to step 11 with the new
|
|
213
248
|
version, and re-run this review against the branch that results. Never
|
|
214
249
|
record a spec defect as a coder failure; that is
|
|
215
250
|
the deadlock this step exists to end.
|
|
@@ -220,20 +255,22 @@ playbooks are written to prevent.
|
|
|
220
255
|
did not write. Get the clause list first (read-only, no event):
|
|
221
256
|
|
|
222
257
|
```bash
|
|
223
|
-
|
|
258
|
+
bs epic goal --epic <epic>
|
|
224
259
|
```
|
|
225
260
|
|
|
226
|
-
Hand those clauses and the live plan to a **`spec-reviewer`** session —
|
|
261
|
+
Hand those clauses and the live plan to a **`bs-spec-reviewer`** session —
|
|
227
262
|
a fresh one, never the planner's, and never the same dispatch as step 13:
|
|
228
|
-
`
|
|
263
|
+
`bs dispatch check` refuses to let one dispatch answer for both.
|
|
229
264
|
Dispatch it *after* step 13's record is written, not alongside it — a
|
|
230
265
|
dispatch that predates the previous record has already answered for that
|
|
231
266
|
one, so two sessions fired up front leave this record unaccounted for and
|
|
232
|
-
`
|
|
233
|
-
verdict per clause, in the goal's order, and record it
|
|
267
|
+
`bs dispatch check` reports that record `unverifiable`. Take back one
|
|
268
|
+
verdict per clause, in the goal's order, and record it. Its prompt carries
|
|
269
|
+
`Declared artifact: /abs/path/to/state/results/<epic>.goal-coverage-vN.json`
|
|
270
|
+
inline on its own line, for the same reason:
|
|
234
271
|
|
|
235
272
|
```bash
|
|
236
|
-
|
|
273
|
+
bs epic goal-check --epic <epic> \
|
|
237
274
|
--plan factory/specs/active/<epic>/plan-vN.json \
|
|
238
275
|
--coverage state/results/<epic>.goal-coverage-vN.json \
|
|
239
276
|
--checked-by spec-reviewer \
|
|
@@ -243,8 +280,8 @@ playbooks are written to prevent.
|
|
|
243
280
|
`covered` must name live plan task ids; `out-of-scope` must give a reason,
|
|
244
281
|
and that reason is quoted back to the epic judge. `uncovered` mints an
|
|
245
282
|
S2-major spec finding against the plan file — no task diff can contain
|
|
246
|
-
that fix, so the answer is `
|
|
247
|
-
you back to step 11 — through `
|
|
283
|
+
that fix, so the answer is `bs plan amend`, which cuts v(n+1) and sends
|
|
284
|
+
you back to step 11 — through `bs plan ingest`, as in step 13. The
|
|
248
285
|
command exits 0 even when it raises findings.
|
|
249
286
|
|
|
250
287
|
**It refuses (`cli.no-epic-goal`) when the owning milestone states no
|
|
@@ -259,54 +296,155 @@ playbooks are written to prevent.
|
|
|
259
296
|
integrable at all:
|
|
260
297
|
|
|
261
298
|
```bash
|
|
262
|
-
|
|
299
|
+
bs epic verdict --epic <epic> --project <project-dir> \
|
|
263
300
|
--session <session-id> --plan-version <n> --causal-parent <event-id>
|
|
264
301
|
```
|
|
265
302
|
|
|
266
303
|
Mechanical oracles first: non-terminal tasks, open blocking findings, a
|
|
267
|
-
missing/failed/stale integration-root check, a
|
|
268
|
-
|
|
269
|
-
without spending a judge call.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
304
|
+
missing/failed/stale integration-root check, a closing spec review that's
|
|
305
|
+
stale or missing when the tier required one, or a missing/stale
|
|
306
|
+
spec-vs-goal check return `hold` (exit 1) without spending a judge call.
|
|
307
|
+
A tier that waived the review (step 13: `when-plan-amended` on a plan
|
|
308
|
+
still at v1) is not a hold on that ground — the verdict records the skip
|
|
309
|
+
as a fact instead of staying silent about it, same as step 13 asks. A
|
|
310
|
+
review that IS on record is still walked through every staleness check
|
|
311
|
+
regardless of tier. `--project` is required — the verdict reads the
|
|
312
|
+
integration branch head to decide whether the records from steps 12, 13
|
|
313
|
+
and 14 still cover it. On `hold`, do not open the PR — report the
|
|
314
|
+
`blockers` to the operator and go back to step 11.
|
|
273
315
|
|
|
274
316
|
Once the oracles pass, the judge call is real money when an external
|
|
275
317
|
provider is `active` in `crosscheck.yml`: codex spends the operator's
|
|
276
|
-
ChatGPT quota, DeepSeek spends prepaid credit
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
318
|
+
ChatGPT quota, DeepSeek spends prepaid credit. Say which purse it is to
|
|
319
|
+
the operator before this call, not after.
|
|
320
|
+
|
|
321
|
+
Step 16 no longer pays twice for the same integration head: the
|
|
322
|
+
`quorum-decision` this call writes now carries `integration_head`, and a
|
|
323
|
+
clean `go` against an unchanged head is reused rather than re-asked
|
|
324
|
+
(issue #270). Anything that invalidates it — a new commit, a `hold`, or a
|
|
325
|
+
decision from before `integration_head` existed — still re-runs.
|
|
282
326
|
16. Record the close. The verdict above is a read-only probe and writes
|
|
283
327
|
nothing; `epic close` is what makes it a fact in the log (D-43):
|
|
284
328
|
|
|
285
329
|
```bash
|
|
286
|
-
|
|
330
|
+
bs epic close --epic <epic> --project <project-dir> \
|
|
287
331
|
--session <session-id> --plan-version <n> --causal-parent <event-id>
|
|
288
332
|
```
|
|
289
333
|
|
|
290
|
-
It
|
|
291
|
-
(
|
|
334
|
+
It reuses step 15's `go` decision when the integration head hasn't moved
|
|
335
|
+
(see step 15), otherwise re-runs the verdict, then emits `epic-closed` on
|
|
336
|
+
`go` and refuses (exit 1, no event) on `hold`. **Never pass `--override-rationale`
|
|
292
337
|
yourself** — closing over a hold is the operator's call; ask for it and
|
|
293
338
|
quote the blockers. If the epic was cut by `/bs audit`
|
|
294
339
|
([`audit.md`](audit.md) step 10), follow the close with
|
|
295
|
-
`
|
|
296
|
-
so every finding
|
|
297
|
-
audit store — the
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
340
|
+
`bs audit resolve <project-dir> --epic <epic> --session <session-id> --causal-parent <event-id> [--specs-dir <dir>]`
|
|
341
|
+
so every finding this plan still claims is marked `fixed` in the
|
|
342
|
+
project's audit store — the rest come back `deferred`, left exactly as
|
|
343
|
+
they were, because the plan scoped them out on purpose. The store, not
|
|
344
|
+
the epic, is where a finding's life ends. `resolve` reads the epic's
|
|
345
|
+
newest plan from the work root's specs dir by default; when this
|
|
346
|
+
project's plans live elsewhere, pass `--specs-dir` naming the same specs
|
|
347
|
+
dir the epic was run from, or it refuses rather than guess.
|
|
348
|
+
17. Open **one integration PR per epic** with a scribe-written body. The head is
|
|
349
|
+
`bs/<epic>/integration`. The base is the target repo's `main` unless
|
|
302
350
|
the epic was cut from another epic's integration branch, in which case
|
|
303
351
|
the PR stacks on that branch and the operator merges the parent's PR
|
|
304
|
-
first — a child epic cut from `
|
|
352
|
+
first — a child epic cut from `bs/<parent>/integration` opens its PR
|
|
305
353
|
against that branch, behind the parent's PR; once the parent has merged
|
|
306
354
|
and its branch is deleted, GitHub retargets the child to `main`. The
|
|
307
355
|
operator reviews on GitHub; this session never merges to `main`
|
|
308
356
|
(`docs/standards/guardrails.md`).
|
|
309
357
|
|
|
358
|
+
**Build the fact pack first.** The scribe has no `Bash` and a small turn
|
|
359
|
+
cap, so it cannot run `git` or `bs`, and a scribe left to grep raw logs
|
|
360
|
+
caps and fills the gaps by invention — a wrong event id, a merged task
|
|
361
|
+
called dead, "no waivers" over two grants. Compute every fact in your own
|
|
362
|
+
session, from the epic session with `--lineage` so the waves' records
|
|
363
|
+
count. Every fact comes from the log or from git, never from the SQLite
|
|
364
|
+
projection: `stats kanban` and `stats timeline` read a projection that
|
|
365
|
+
can lag the log, and `db rebuild` clears it before it re-reads, so do
|
|
366
|
+
not refresh it for this.
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
# one pass over the log (pass an --n that covers the whole lineage)
|
|
370
|
+
bs event tail <session-id> --lineage --n 100000
|
|
371
|
+
# the fingerprint map: finding_id, task_id and fingerprint of the epic's findings
|
|
372
|
+
bs findings list --session <session-id> --epic <epic>
|
|
373
|
+
# the goal the summary states
|
|
374
|
+
bs epic goal --epic <epic> [--roadmap-path <file>]
|
|
375
|
+
# the branch itself
|
|
376
|
+
git log --oneline <base>..bs/<epic>/integration
|
|
377
|
+
git diff --shortstat <base>...bs/<epic>/integration
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
From the `event tail` pass keep, each with its `event_id`:
|
|
381
|
+
- `waiver-granted` and `waiver-denied` records whose
|
|
382
|
+
`payload.fingerprint` matches one of the epic's findings, mapped to
|
|
383
|
+
that finding's id and task. Match on the stored `fingerprint` that
|
|
384
|
+
`findings list` prints, and drop a decision that matches none: an
|
|
385
|
+
ancestor session can carry an earlier epic's decisions. Grants come
|
|
386
|
+
from here, not from
|
|
387
|
+
`findings list --status`, because a grant only moves a raised or
|
|
388
|
+
confirmed finding to `waived`; an amended or fix-verified one keeps
|
|
389
|
+
its status.
|
|
390
|
+
- `wave-merged` records: `payload.task_ids` and the event id.
|
|
391
|
+
- `epic-closed` from step 16: its event id, `machine_verdict`, and
|
|
392
|
+
`payload.summary.tasks` — each task's `task_status`, read from the log
|
|
393
|
+
at close. A follow-up cleared by closing all its findings (fixed, refuted
|
|
394
|
+
or waived) still reads `todo` there; cross-check it against the findings and never publish it
|
|
395
|
+
as an open task.
|
|
396
|
+
- `audit-resolved`, for an epic cut by `/bs audit`: every record whose
|
|
397
|
+
`payload.epic` is this epic — one epic can resolve more than once, and
|
|
398
|
+
the lineage carries a parent epic's resolve too. List each event id.
|
|
399
|
+
`fixed` and `deferred` are fingerprint arrays: fixed is the union of
|
|
400
|
+
every kept record's `fixed`; deferred is the last record's `deferred`
|
|
401
|
+
minus that union. Write the two counts into the pack yourself.
|
|
402
|
+
- `integration-check` records whose `payload.epic_id` is this epic and
|
|
403
|
+
whose `head_sha` equals the `epic-closed` record's
|
|
404
|
+
`summary.integration.check.head_sha`: each check's `name` and `pass`.
|
|
405
|
+
|
|
406
|
+
`epic goal` reads this repo's roadmap by default; when the epic's
|
|
407
|
+
roadmap lives elsewhere, pass `--roadmap-path` naming it, or the goal
|
|
408
|
+
you hand over is the wrong one. For a UI epic, take the screenshots
|
|
409
|
+
from each task's tester result, `state/results/<task-id>.tester*.json`,
|
|
410
|
+
field `artifacts` (the files sit under `state/artifacts/<task-id>/`);
|
|
411
|
+
[`docs/standards/stack.md`](../../../docs/standards/stack.md) wants
|
|
412
|
+
them on this PR. The pack carries a screenshot only as an image you
|
|
413
|
+
have uploaded or attached — never a local path, which no reviewer can
|
|
414
|
+
open; one you cannot attach stays out.
|
|
415
|
+
|
|
416
|
+
Write only these filtered facts into one pack file — never raw command
|
|
417
|
+
output, which puts the scribe back to grepping logs. Dispatch the scribe with that file as its **only source**, and the
|
|
418
|
+
PR-body scratch file as its write root, under one rule: **omit, never
|
|
419
|
+
infer**. A fact the pack does not hold stays out of the body; a count,
|
|
420
|
+
a status, or an event id is copied from the pack, never worked out. If
|
|
421
|
+
the pack is wrong, rebuild it and dispatch again; do not let the scribe
|
|
422
|
+
reach past it into `state/events/`.
|
|
423
|
+
|
|
424
|
+
The PR body opens with a short plain-language summary, written under
|
|
425
|
+
`.claude/skills/bs/SKILL.md`'s "Talking to the operator" section — cite
|
|
426
|
+
that section for how to write it, do not restate its rule here. In the
|
|
427
|
+
project's own terms, not Blacksmith's: what this epic changed, what is
|
|
428
|
+
still open, and what decision the operator must make, if any. The
|
|
429
|
+
existing fact sections below follow it as the technical part the rule
|
|
430
|
+
asks for.
|
|
431
|
+
|
|
432
|
+
The PR body has this shape and nothing else: the plain-language opening
|
|
433
|
+
above; **Summary** (the epic id
|
|
434
|
+
and its goal, one paragraph); **Tasks** (one line per task: id,
|
|
435
|
+
`task_status`, and its `wave-merged` event id when it has one);
|
|
436
|
+
**Waivers** (granted and denied, each with its finding id and event id,
|
|
437
|
+
or the word "none" only when both lists in the pack are empty);
|
|
438
|
+
**Verification** (the verdict outcome, the `epic-closed` event id, the
|
|
439
|
+
`integration-check` results, the `audit-resolved` event ids and the
|
|
440
|
+
fixed and deferred counts when there are any, the shortstat, and the
|
|
441
|
+
commit count from the log);
|
|
442
|
+
**Screenshots** (only when the pack carries them); **Timeline** (the
|
|
443
|
+
dashboard link, when you put one in the pack). The scribe's 300-word
|
|
444
|
+
cap binds the prose — Summary and the sentences around the lists; the
|
|
445
|
+
Tasks, Waivers and check lists are artifacts and are never cut to fit
|
|
446
|
+
it, since a dropped line reads as a task or waiver that was not there.
|
|
447
|
+
|
|
310
448
|
Before the first push, read the authorship the branch is about to make
|
|
311
449
|
public — `git log --format=%ae <base>..HEAD | sort -u` — and stop on any
|
|
312
450
|
address that is not a noreply one; a coder's worktree commit carries
|
|
@@ -318,7 +456,7 @@ playbooks are written to prevent.
|
|
|
318
456
|
Append it by hand, under the reserved ref and the live plan version:
|
|
319
457
|
|
|
320
458
|
```bash
|
|
321
|
-
|
|
459
|
+
bs event append '{"session_id":"<session-id>","actor":"orchestrator","event_type":"integration-pr-opened","task_id":"<epic>/integration","plan_version":<n>,"causal_parent":"<event-id>","payload":{"step":17,"pr_url":"<url>","pr_number":<n>,"repo":"<owner>/<repo>","base_ref":"<base>","head_ref":"bs/<epic>/integration","head_sha":"<sha>","base_sha":"<sha>","commits":<n>,"changed_files":<n>,"additions":<n>,"deletions":<n>}}'
|
|
322
460
|
```
|
|
323
461
|
|
|
324
462
|
The receipt says whether the timeline reads the type; it does, and it
|
|
@@ -7,13 +7,13 @@ on demand knows only what is in the log, never whether anyone was reading it.
|
|
|
7
7
|
|
|
8
8
|
## 1. Ask the watcher
|
|
9
9
|
|
|
10
|
-
`
|
|
10
|
+
`bs daemon status [--dir <dir>]`. It prints JSON and **exits 1 whenever a
|
|
11
11
|
current daemon is not watching** — that is the health check doing its job,
|
|
12
12
|
not a command that failed, so never report the non-zero exit as an error.
|
|
13
|
-
|
|
13
|
+
Four readings:
|
|
14
14
|
|
|
15
15
|
- `running: false` — nothing holds the lock. A fine steady state if the
|
|
16
|
-
operator never started one (`
|
|
16
|
+
operator never started one (`bs daemon start` if they want one): say so
|
|
17
17
|
in a clause and go to step 2, which folds the log live anyway. What you
|
|
18
18
|
must not do is render `lastTick` as though it were current.
|
|
19
19
|
- `stale: true` — a daemon holds the lock and has published nothing for
|
|
@@ -24,6 +24,10 @@ Three readings:
|
|
|
24
24
|
`<dir>/daemon.log`.
|
|
25
25
|
- `running: true, stale: false` — the numbers are fresh. Say how fresh
|
|
26
26
|
(`reportAgeSeconds`) rather than implying "now".
|
|
27
|
+
- an `error` object with `code: "daemon.unreadable-state"` — the daemon's
|
|
28
|
+
own state is unreadable, so nobody can say whether one is running. Lead
|
|
29
|
+
with it, name the file from `error.details.path`, and pass on the
|
|
30
|
+
recovery in `error.message`. Never render it as `running: false`.
|
|
27
31
|
|
|
28
32
|
When there is a `lastTick`, render its triage split before the digest below,
|
|
29
33
|
because it is the only record of what happened while nobody was watching:
|
|
@@ -34,20 +38,23 @@ its own and needs no decision.
|
|
|
34
38
|
|
|
35
39
|
## 2. Fold the log
|
|
36
40
|
|
|
37
|
-
`
|
|
41
|
+
`bs stats overview [--session <id>]`. Render the JSON as a short digest,
|
|
38
42
|
field for field — never invent numbers the query didn't return:
|
|
39
43
|
|
|
40
44
|
- **Live agents**: `liveAgents` grouped by role/model/provider,
|
|
41
45
|
`liveAgentCount` as the headline (with `liveAgentCountDelta5m` as a
|
|
42
46
|
▲/▼ note).
|
|
43
|
-
- **Budget**: `tokensByEpic`, called out as "X% of the
|
|
44
|
-
|
|
47
|
+
- **Budget**: `tokensByEpic`, called out as "X% of the epic cap" — the cap
|
|
48
|
+
for the epic's effort tier in `budgets.yml` (4M small / 16M medium / 32M
|
|
49
|
+
huge; `bs budget alarm` prints the cap it judged each epic against) — flag any epic ≥70% (`budgets.yml` `alarm_ratio`).
|
|
45
50
|
`tokensByEpic` counts recorded spend only, so it is a floor: say "at least
|
|
46
|
-
X%", and run `
|
|
51
|
+
X%", and run `bs budget alarm <session>` for the projection that prices
|
|
47
52
|
the dispatches nothing recorded a Result for.
|
|
48
53
|
- **In flight**: `epicsInFlight`, `recentDispatches` (last 10).
|
|
49
54
|
- **Alerts**: `alerts.escalations` and `alerts.pendingWaivers` — call these
|
|
50
55
|
out first if non-zero, they're what the operator came here to see.
|
|
56
|
+
`pendingWaivers` is narrower than `bs waivers pending`: only waivers on
|
|
57
|
+
finished work (completed tasks, closed epics).
|
|
51
58
|
- **Milestones**: `milestoneProgress` — one line each, `%` complete.
|
|
52
59
|
|
|
53
60
|
If the UI is already running, just point the operator at it instead of
|
package/.claude/skills/bs/ui.md
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
# `/bs ui` — serve the local dashboard
|
|
2
2
|
|
|
3
|
-
`
|
|
4
|
-
[--roadmap-path …]`. If it errors `ui.not-built`,
|
|
5
|
-
first (builds `ui/server/dist` + `ui/dist`), then retry,
|
|
6
|
-
URL (`http://127.0.0.1:<port>`).
|
|
3
|
+
`bs ui serve [--port 4680] [--db state/smith.db] [--state-dir …]
|
|
4
|
+
[--roadmap-path …] [--claude-config-dir …] [--store <dir>…]`. If it errors `ui.not-built`,
|
|
5
|
+
run `pnpm build:ui` first (builds `ui/server/dist` + `ui/dist`), then retry,
|
|
6
|
+
and print the local URL (`http://127.0.0.1:<port>`).
|
|
7
|
+
|
|
8
|
+
Besides the event log, the server reads — read-only — the local Claude Code
|
|
9
|
+
session registry, transcripts and prompt history, from `--claude-config-dir`, else
|
|
10
|
+
`$CLAUDE_CONFIG_DIR`, else Claude Code's default, and serves them as
|
|
11
|
+
`GET /api/cli-sessions`, loopback-only.
|
|
12
|
+
|
|
13
|
+
Home, Kanban and the project list also read every project with a live CLI
|
|
14
|
+
session: its git toplevel is a store when `<top>/.blacksmith/state/events` or
|
|
15
|
+
`<top>/state/events` exists (rechecked at most every 5 s), and `--store <dir>`
|
|
16
|
+
adds one by hand. Foreign stores are strictly read; their projection lives in
|
|
17
|
+
`state/ui-stores/<storeId>.db` of the served clone, kept for a grace period after a
|
|
18
|
+
store drops and pruned at startup when stale. In a foreign store an untagged row reads
|
|
19
|
+
as the store's label, any other explicit project as itself, so `?project=X` returns each
|
|
20
|
+
matching row once. These routes and `/api/tasks/*` are local-only, like
|
|
21
|
+
`/api/cli-sessions`. A foreign Kanban card opens through `?store=<storeId>`; its history,
|
|
22
|
+
artifacts and waivers stay empty. Details: `docs/guide/dashboard.md`.
|
|
7
23
|
|
|
8
24
|
**That build needs a clone.** `ui/` is not in the package's `files`, so an
|
|
9
25
|
install — npm or plugin — ships no dashboard at all and `ui.not-built` is the
|
|
10
26
|
permanent answer there, not a stale build. Check before you advise: if there
|
|
11
|
-
is no `ui/` beside the `
|
|
27
|
+
is no `ui/` beside the `bs` you are running, say the dashboard is
|
|
12
28
|
clone-only and stop, rather than sending the operator to a `pnpm` script they
|
|
13
29
|
do not have.
|
|
14
30
|
|
|
@@ -1,13 +1,89 @@
|
|
|
1
1
|
# `/bs waivers` — answer the pending waiver batch
|
|
2
2
|
|
|
3
|
-
1. `
|
|
3
|
+
1. `bs waivers pending <epic> --session <session-id>` — every S3/S4
|
|
4
4
|
finding for the epic with no waiver decision yet (a decided fingerprint
|
|
5
5
|
never resurfaces).
|
|
6
6
|
2. Present the whole batch to the operator as **one** question ("ignore
|
|
7
7
|
these?") — never ask per-finding (architecture §11, `severity.yml`).
|
|
8
8
|
3. Write the answers to `decisions.json`
|
|
9
9
|
(`Array<{fingerprint, decision: "granted"|"denied", operatorNote}>`) and
|
|
10
|
-
run `
|
|
10
|
+
run `bs waivers apply decisions.json --session ... --plan-version ...
|
|
11
11
|
--causal-parent ... --actor operator`. S1/S2 findings are rejected by
|
|
12
12
|
the command itself if attempted — they go through the escalation ladder
|
|
13
13
|
instead, never a waiver.
|
|
14
|
+
Task waivers are a separate step. A task whose plan row says `waived`
|
|
15
|
+
carries no gate run, and the epic close holds it as a blocker ("waived
|
|
16
|
+
without operator approval") until the operator's approval is recorded.
|
|
17
|
+
Ask the operator, then run `bs waivers approve-task <task-id> --note
|
|
18
|
+
"<why>" --session ... --causal-parent ... --actor operator`. Only the
|
|
19
|
+
operator actor is accepted; an approval written under any other actor is
|
|
20
|
+
refused, and one appended by hand under another actor is not honoured. An
|
|
21
|
+
approval is tied to the plan version it is given under (`--plan-version`):
|
|
22
|
+
a task waived again by a newer plan needs a fresh approval.
|
|
23
|
+
4. A denial discharges nothing — the finding stays open with no further
|
|
24
|
+
move of its own, and an open finding blocks the epic verdict. The
|
|
25
|
+
command's output carries `findingIdsToCarry`: every finding id this batch
|
|
26
|
+
denied. Carry the whole list forward until each id is closed — by the
|
|
27
|
+
route its **scope** allows (`bs findings list --session <id>` shows
|
|
28
|
+
the scope), by a later grant, or by `refuted`. The two scopes do not share
|
|
29
|
+
a route:
|
|
30
|
+
|
|
31
|
+
- **`spec` finding** (owned by `<epic>/integration`) — cut a
|
|
32
|
+
`bs plan amend`, and its `--findings` must name every spec id from
|
|
33
|
+
the list, or the finding stays open forever with no path back (issue
|
|
34
|
+
#221). The amendment moves it to `amend-pending`; epic close moves it to
|
|
35
|
+
`amended`. See `docs/guide/operator-guide/findings.md` §6a.
|
|
36
|
+
- **`diff` finding** — `plan amend` refuses it
|
|
37
|
+
(`plan.amendment-not-spec-scoped`): the code is wrong, not the plan. If
|
|
38
|
+
the owning task is still open, bounce the finding to it like any gate
|
|
39
|
+
finding. If the owner has already merged, fix it in a **follow-up
|
|
40
|
+
task**:
|
|
41
|
+
1. Write a draft, `Array<{filePath, finding}>` (the `gate run`
|
|
42
|
+
findings shape). `filePath` is the denied record's `file_path`;
|
|
43
|
+
`finding` is the **whole** record `bs findings list` prints,
|
|
44
|
+
minus the three fields raise derives and overwrites —
|
|
45
|
+
`fingerprint`, `file_path` and `finding_status`. Keep the rest:
|
|
46
|
+
the schema requires `failure_scenario` and `found_by` too, and a
|
|
47
|
+
draft without them fails with `findings.invalid-record`. Leave
|
|
48
|
+
`task_id`, `finding_id` and `epic_id` as they are; routing rewrites
|
|
49
|
+
them. Do not touch `finding_category` or `summary`: the follow-up
|
|
50
|
+
id is derived from the fingerprint they make.
|
|
51
|
+
2. Raise it against the plan with no `--task`:
|
|
52
|
+
`bs findings raise --plan <plan.json> --findings <draft.json>
|
|
53
|
+
--session <id> --plan-version <n> --causal-parent <event-id>
|
|
54
|
+
--actor operator`. Routing sees the owner is closed and mints
|
|
55
|
+
`<epic>/followup-<fp8>` — read `taskId` from the printed row
|
|
56
|
+
(`attribution: follow-up`). A denied fingerprint is not suppressed
|
|
57
|
+
on a re-raise; a granted one is.
|
|
58
|
+
3. Check its claims before dispatching. A follow-up **inherits the
|
|
59
|
+
owner's whole claim set** — and when ownership of the file was
|
|
60
|
+
ambiguous, the **union** of every candidate's claims — so it can
|
|
61
|
+
overlap tasks still in flight. `bs wave next <plan.json>
|
|
62
|
+
--session <id> --causal-parent <event-id>` shows it: it folds in
|
|
63
|
+
live status and logged follow-ups, and defers an overlapping task
|
|
64
|
+
with `claim-overlap`. `wave check` validates only the ids it is
|
|
65
|
+
given and cannot see the conflict.
|
|
66
|
+
4. Dispatch it like any task, with these gaps filled by hand. It
|
|
67
|
+
carries no acceptance criteria and no budget: **write the ACs into
|
|
68
|
+
the dispatch brief** (the finding's fix, plus the test that proves
|
|
69
|
+
it). `wave check` prices it at the coder cap, and `gate run` reports
|
|
70
|
+
its budget as not-declared. It is not in the plan, so
|
|
71
|
+
`lessons for-dispatch`, `findings for-dispatch` and `coverage check`
|
|
72
|
+
refuse it with `cli.task-not-in-plan`: splice
|
|
73
|
+
`bs lessons for-dispatch <role> --case-type bugfix` without
|
|
74
|
+
`--plan`/`--task` — at the cost of every claim-path lesson, since
|
|
75
|
+
claims are then `[]` and those lessons drop silently — and replace the findings block with
|
|
76
|
+
`bs findings list --session <id> --task <epic>/followup-<fp8>`.
|
|
77
|
+
At its gate, run `gate run` **without** `--plan`. With it, a judge
|
|
78
|
+
finding on the merged owner's file routes to yet another follow-up
|
|
79
|
+
(listed under `reattributedFindings`) instead of blocking this one,
|
|
80
|
+
and that stray follow-up then blocks the epic until it completes or
|
|
81
|
+
its findings are waived. Without it the finding blocks here, at the
|
|
82
|
+
cost of coverage checking the total rather than the follow-up's
|
|
83
|
+
claims.
|
|
84
|
+
5. Close **both** findings by hand once the follow-up merges — nothing
|
|
85
|
+
links the re-raised finding to the denied one, and neither moves on
|
|
86
|
+
its own. Walk each with `bs findings transition <finding-id>
|
|
87
|
+
<status> --session <id> --plan-version <n> --causal-parent
|
|
88
|
+
<event-id> --actor operator` through `confirmed`, `fix-pending`, `fix-landed`,
|
|
89
|
+
`fix-verified` (start at the first edge its status allows).
|