@juzser/blacksmith 0.4.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} +5 -5
- package/.claude/agents/{coder.md → bs-coder.md} +8 -7
- package/.claude/agents/{grader.md → bs-grader.md} +4 -4
- 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} +13 -5
- package/.claude/agents/{scribe.md → bs-scribe.md} +11 -2
- package/.claude/agents/{security-reviewer.md → bs-security-reviewer.md} +6 -6
- package/.claude/agents/{spec-reviewer.md → bs-spec-reviewer.md} +15 -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} +4 -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 +34 -27
- package/.claude/skills/bs/dispatch.md +100 -50
- 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 +27 -18
- package/.claude/skills/bs/report.md +11 -11
- package/.claude/skills/bs/run.md +181 -49
- 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 +109 -53
- 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 +48 -14
- package/factory/orchestrator/dist/artifacts.js +31 -0
- package/factory/orchestrator/dist/attribution.js +4 -0
- package/factory/orchestrator/dist/audit.js +125 -17
- 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 +790 -100
- package/factory/orchestrator/dist/cliName.js +27 -0
- package/factory/orchestrator/dist/crosscheck.js +136 -8
- package/factory/orchestrator/dist/daemon.js +154 -36
- package/factory/orchestrator/dist/db/projector.js +278 -29
- 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 +13 -37
- 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 +323 -38
- package/factory/orchestrator/dist/epicBudget.js +93 -0
- package/factory/orchestrator/dist/epicWidth.js +22 -5
- package/factory/orchestrator/dist/errorIssues.js +10 -4
- package/factory/orchestrator/dist/escalation.js +2 -2
- package/factory/orchestrator/dist/events.js +105 -3
- package/factory/orchestrator/dist/feedback.js +169 -0
- package/factory/orchestrator/dist/findingContext.js +26 -0
- package/factory/orchestrator/dist/findings.js +83 -12
- package/factory/orchestrator/dist/gate.js +120 -10
- package/factory/orchestrator/dist/goalCheck.js +2 -2
- package/factory/orchestrator/dist/harness.js +25 -17
- package/factory/orchestrator/dist/hookDecision.js +92 -3
- package/factory/orchestrator/dist/integration.js +9 -7
- package/factory/orchestrator/dist/judgePreflight.js +25 -4
- package/factory/orchestrator/dist/judgeRoles.js +47 -0
- package/factory/orchestrator/dist/judgeStopHook.js +90 -32
- package/factory/orchestrator/dist/judges.js +419 -21
- package/factory/orchestrator/dist/lessons.js +190 -11
- package/factory/orchestrator/dist/mcp.js +6 -6
- package/factory/orchestrator/dist/paths.js +10 -7
- package/factory/orchestrator/dist/plan.js +269 -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/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 +4 -4
- 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/schemas.js +56 -8
- package/factory/orchestrator/dist/severity.js +8 -4
- package/factory/orchestrator/dist/spec.js +177 -13
- 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 +179 -27
- package/factory/orchestrator/dist/taskStatus.js +14 -0
- package/factory/orchestrator/dist/testerAudit.js +31 -12
- package/factory/orchestrator/dist/testgate.js +16 -13
- package/factory/orchestrator/dist/uiuxGate.js +83 -0
- package/factory/orchestrator/dist/usage.js +97 -40
- package/factory/orchestrator/dist/waivers.js +39 -5
- package/factory/orchestrator/dist/waveBudget.js +2 -0
- package/factory/orchestrator/dist/waveConcurrency.js +143 -7
- package/factory/orchestrator/dist/workroot.js +1 -1
- package/factory/orchestrator/dist/worktree.js +61 -19
- 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 +7 -7
- 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
|
@@ -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).
|
|
@@ -10,8 +10,9 @@ bind every dispatch below: the project directory is an answer you ask for
|
|
|
10
10
|
rather than a path anything here knows, every write carries
|
|
11
11
|
`--session <id> --plan-version <n> --causal-parent <event-id>`, every
|
|
12
12
|
dispatch carries the compiled lessons block and that task's open-findings
|
|
13
|
-
block,
|
|
14
|
-
|
|
13
|
+
block, every `dispatch_decision` carries a one-line plain-words `reason`
|
|
14
|
+
(dispatch.md's payload contract), and fetched text is wrapped before it is
|
|
15
|
+
quoted. None of it is restated here; a second copy is a copy that drifts.
|
|
15
16
|
|
|
16
17
|
The step numbers are the loop's own. They start at 2 because step 1 — which
|
|
17
18
|
tasks, and how wide — was answered before you were handed anything, and they
|
|
@@ -26,15 +27,17 @@ both:
|
|
|
26
27
|
- **Inline**, in the epic session, when the wave is small enough that the
|
|
27
28
|
epic's window can carry it. Nothing changes: the session id you were
|
|
28
29
|
handed is the one you write to.
|
|
29
|
-
- **In a session of its own** when it is not. Open one with `
|
|
30
|
+
- **In a session of its own** when it is not. Open one with `bs session
|
|
30
31
|
start <wave-id> --continues <event-id>` and write every dispatch below
|
|
31
32
|
there. The epic session then carries the admission and the result rather
|
|
32
33
|
than every turn in between — which is the point, because an epic outlives
|
|
33
34
|
its waves, and a window spent on this wave's dispatches is a window the
|
|
34
|
-
epic does not have for the wave after next (D13).
|
|
35
|
+
epic does not have for the wave after next (D13). Every event you write in
|
|
36
|
+
that session carries `--actor wave-runner` — `.claude/agents/bs-wave-runner.md`'s
|
|
37
|
+
Constraints has the rule and the check it satisfies.
|
|
35
38
|
|
|
36
39
|
After any `error-logged` this wave writes — a worker that died, a judge turn
|
|
37
|
-
that failed, a gate refusal recorded as an error — or a `
|
|
40
|
+
that failed, a gate refusal recorded as an error — or a `bs gate run`
|
|
38
41
|
that came back `blocked` (its `gate-outcome` event with outcome `blocked`),
|
|
39
42
|
or a task the wave marks `failed` (its `task-added` event with payload
|
|
40
43
|
`task_status: failed`), the wave runs the reporting verb once, per
|
|
@@ -46,9 +49,9 @@ not cosmetic — it is the edge an audit walks:
|
|
|
46
49
|
|
|
47
50
|
- **The epic ran you** and you opened a wave session yourself: continue from
|
|
48
51
|
the epic event that admitted this wave.
|
|
49
|
-
- **You were dispatched as a `wave-runner`**: continue from the
|
|
52
|
+
- **You were dispatched as a `bs-wave-runner`**: continue from the
|
|
50
53
|
`dispatch_decision` that dispatched you, the event id handed to you with
|
|
51
|
-
the wave. `
|
|
54
|
+
the wave. `bs delegation check` resolves your session by matching a
|
|
52
55
|
`session-start`'s `causal_parent` against that dispatch, and it is the
|
|
53
56
|
only thing that proves the dispatches you are about to write are your
|
|
54
57
|
own log's rather than an agent talking about itself. Continue from
|
|
@@ -57,12 +60,12 @@ not cosmetic — it is the edge an audit walks:
|
|
|
57
60
|
for the same reason: a grant is earned by owning the log first.
|
|
58
61
|
- **You are taking over a wave whose runner died or capped**: continue from
|
|
59
62
|
that runner's last event, in a session of your own. Do not write into its
|
|
60
|
-
log. One session has one author, and `
|
|
63
|
+
log. One session has one author, and `bs delegation check` reads exactly
|
|
61
64
|
that: dispatches appended to a dead delegate's log by whoever picked the
|
|
62
65
|
wave up are reported as violations, correctly, because from the log's side
|
|
63
66
|
they cannot be told apart from an agent narrating its own work. Open your
|
|
64
67
|
own instead:
|
|
65
|
-
`
|
|
68
|
+
`bs session start <wave-id>-takeover --continues <dead-session>#<n>`.
|
|
66
69
|
That keeps the chain whole — the epic's folded reads still see every
|
|
67
70
|
dispatch on both sides of the handover — and it makes the takeover a fact
|
|
68
71
|
in the log rather than something a reader has to infer from two agents and
|
|
@@ -114,18 +117,22 @@ the difference between a factory and a queue with extra steps:
|
|
|
114
117
|
keep the wave moving.
|
|
115
118
|
|
|
116
119
|
The single place the wave rejoins is step 10's merge queue, which is
|
|
117
|
-
serial on purpose — `
|
|
118
|
-
`
|
|
120
|
+
serial on purpose — `bs queue run` rebases one task at a time onto
|
|
121
|
+
`bs/<epic>/integration` and runs the cumulative tests between. Merging
|
|
119
122
|
is the one thing that cannot be done in parallel, and it is already the
|
|
120
123
|
one thing this playbook never asks you to.
|
|
121
124
|
|
|
122
125
|
## The steps
|
|
123
126
|
|
|
124
|
-
2. Per admitted task, and for all of them together: `
|
|
127
|
+
2. Per admitted task, and for all of them together: `bs worktree create
|
|
125
128
|
<project-dir> <epic> <task-id>`.
|
|
126
|
-
3. Pre-code, if the task needs it: dispatch `researcher` for an unknown, or
|
|
127
|
-
`uiux` (`.claude/agents/uiux.md`) for any UI-affecting acceptance
|
|
128
|
-
criterion — before the coder starts, not after.
|
|
129
|
+
3. Pre-code, if the task needs it: dispatch `bs-researcher` for an unknown, or
|
|
130
|
+
`bs-uiux` (`.claude/agents/bs-uiux.md`) for any UI-affecting acceptance
|
|
131
|
+
criterion — before the coder starts, not after. Bracket the uiux spec
|
|
132
|
+
turn with `bs judge dispatch --role uiux --kind spec` / `bs judge report
|
|
133
|
+
--role uiux --kind spec`, the same way every judge turn in steps 5–7 is
|
|
134
|
+
bracketed — the gate's uiux stage (step 7) reads this turn back and
|
|
135
|
+
blocks a flagged task that never closed it.
|
|
129
136
|
- `profile.preCodeResearch` gates the researcher: `when-needed` is the
|
|
130
137
|
line above, `never` (`small`) means the coder reading the repo *is* the
|
|
131
138
|
brief. An epic that genuinely cannot start without a research brief is
|
|
@@ -134,14 +141,14 @@ one thing this playbook never asks you to.
|
|
|
134
141
|
every tier: no tier ships a UI change without a spec to review it
|
|
135
142
|
against. It is listed as a knob so the answer is asked for rather than
|
|
136
143
|
assumed, not because a tier can turn it off.
|
|
137
|
-
- A returned brief goes through `
|
|
144
|
+
- A returned brief goes through `bs research check --brief <path>`
|
|
138
145
|
before it is attached to a task spec ("Fetched and quoted text goes into
|
|
139
146
|
a prompt fenced" above). Exit 1 sends it back; on exit 0, carry
|
|
140
147
|
`recommendation.provenance` into the coder's prompt so the task knows
|
|
141
148
|
whether its research rests on this repo or on a fetched page. Any raw
|
|
142
|
-
fetched text you quote alongside it is wrapped with `
|
|
143
|
-
4. Dispatch **`coder`** (`.claude/agents/coder.md`) in that worktree.
|
|
144
|
-
Token/diff caps (`budgets.yml
|
|
149
|
+
fetched text you quote alongside it is wrapped with `bs prompt wrap`.
|
|
150
|
+
4. Dispatch **`bs-coder`** (`.claude/agents/bs-coder.md`) in that worktree.
|
|
151
|
+
Token/diff caps (`budgets.yml`, sized per effort tier) and YAGNI
|
|
145
152
|
are the coder's own constraints — don't restate them here, the template
|
|
146
153
|
does. What the template does not read is the schema, so when the spec
|
|
147
154
|
carries `keeps_exports`, restate the promise in the dispatch in one line:
|
|
@@ -150,7 +157,7 @@ one thing this playbook never asks you to.
|
|
|
150
157
|
|
|
151
158
|
Once every coder of the wave has been dispatched — this fan-out has gone
|
|
152
159
|
out for every admitted task — audit the wave's own parallelism while it
|
|
153
|
-
can still be corrected: `
|
|
160
|
+
can still be corrected: `bs wave audit --session <id> --epic <epic>`.
|
|
154
161
|
`wave check` is the step-1 gate that certified, before any dispatch, that
|
|
155
162
|
these tasks could run together; `wave audit` reads the log back after
|
|
156
163
|
dispatch and says whether they did.
|
|
@@ -167,20 +174,50 @@ one thing this playbook never asks you to.
|
|
|
167
174
|
- `unobserved` — the log holds no dispatch for any task this wave
|
|
168
175
|
admitted. That is a defect in the log, not in how the wave ran; record
|
|
169
176
|
it the same way, as `error-logged` with error `execution.env-failure`.
|
|
177
|
+
- `unlinked` — the wave's work merged, or was dispatched, off the epic's
|
|
178
|
+
own lineage (a wave-runner session started without `--continues`).
|
|
179
|
+
Real work, not a defect in dispatch, but its width is unmeasured —
|
|
180
|
+
record it as `error-logged` with error `contract.constraint-decay` so
|
|
181
|
+
the session gets linked under `--continues` next time.
|
|
170
182
|
- `parallel` or `single` — nothing further; the wave ran as wide as it
|
|
171
183
|
was admitted.
|
|
172
|
-
5. Dispatch **`tester`** (`.claude/agents/tester.md`) for missing unit
|
|
184
|
+
5. Dispatch **`bs-tester`** (`.claude/agents/bs-tester.md`) for missing unit
|
|
173
185
|
coverage and epic-level e2e/screenshots.
|
|
186
|
+
- The moment it returns, project its result: `bs results record --task
|
|
187
|
+
<task-id> --result <tester-result.json> --agent tester --provider <name>
|
|
188
|
+
--model-tier <tier> --worktree <dir> --session ... --plan-version N
|
|
189
|
+
--causal-parent ...`. `--agent` stamps the envelope, token_usage
|
|
190
|
+
included, as `gate run` does; `--worktree` is the coder's worktree the
|
|
191
|
+
tester shares, and records the HEAD that proves the screenshots fresh
|
|
192
|
+
(D2) — step 7's uiux stage blocks `screenshots-stale` when this HEAD
|
|
193
|
+
doesn't match the gate's. The tester has no tests of its own to run, so
|
|
194
|
+
it never reaches `gate run` itself — without this, its screenshots sit
|
|
195
|
+
in the result file and never reach the task page.
|
|
174
196
|
- Then the **uiux visual pass**, but only when all three hold: the task
|
|
175
197
|
is UI-affecting, the tester actually returned screenshot artifacts, and
|
|
176
|
-
step 3 wrote a uiux spec for this task (agent-interviews.md N-6).
|
|
177
|
-
any one and
|
|
178
|
-
|
|
198
|
+
step 3 wrote a uiux spec for this task (agent-interviews.md N-6). For a
|
|
199
|
+
UI-affecting task, miss any one and stop, do not skip — step 7's gate
|
|
200
|
+
blocks a flagged task with no fresh, reviewed screenshot set, so a
|
|
201
|
+
missing condition here is not a quiet pass-through to the gate, it is a
|
|
202
|
+
reason to fix the condition first. Dispatch it with the spec path and the
|
|
179
203
|
image paths and **nothing else**: no diff, no components, no test code.
|
|
180
204
|
The pass is a judgment about the rendered screen, and a uiux session
|
|
181
205
|
that reads the diff ends up reviewing intent, which the reviewer
|
|
182
206
|
already covers.
|
|
183
|
-
|
|
207
|
+
Bracket it with `bs judge dispatch --role uiux --kind visual` / `bs
|
|
208
|
+
judge report --role uiux --kind visual`: a dispatch with no `--kind` is
|
|
209
|
+
refused (`judges.kind-required`), because a kindless turn is one the gate's
|
|
210
|
+
uiux stage never reads.
|
|
211
|
+
- **Blast radius.** When the task's claims touch a shared primitive (a
|
|
212
|
+
design-system/kit component or a global stylesheet), a screenshot of
|
|
213
|
+
the task's own screens is not the full surface: the tester re-shoots
|
|
214
|
+
and the uiux visual pass reviews every screen that renders that
|
|
215
|
+
primitive, not only the task's screens. The tester determines that
|
|
216
|
+
list — from the screenshot suite already on disk or from the
|
|
217
|
+
primitive's importers — and records it alongside the screenshots it
|
|
218
|
+
captures. The "max 4 per feature" screenshot cap (bs-tester.md) still applies per
|
|
219
|
+
screen; it is the screen count that grows, not the shots on each one.
|
|
220
|
+
6. Dispatch **`bs-grader`** (`.claude/agents/bs-grader.md`) — bounded rubric
|
|
184
221
|
loop, `profile.graderRounds` rounds (2 at `huge`, 1 below; never more
|
|
185
222
|
than 2, agent-constraints.md "grader (v3)"), before any gate
|
|
186
223
|
runs; it never decides pass/fail itself. At one round the grader scores
|
|
@@ -189,12 +226,23 @@ one thing this playbook never asks you to.
|
|
|
189
226
|
gates nothing (D-34). A round-2 `fail` re-scopes via dispatch.md's "Round
|
|
190
227
|
counting and escalation", not a round 3 on this id.
|
|
191
228
|
7. Run the gate pipeline:
|
|
192
|
-
`
|
|
229
|
+
`bs gate run <task-id> --worktree <dir> --checks checks.json --result
|
|
193
230
|
result.json --grader state/results/<task-id>.grader-r<round>.json
|
|
194
|
-
--findings findings.json --
|
|
231
|
+
--findings findings.json --plan <plan.json> --uiux-visual
|
|
232
|
+
state/results/<task-id>.uiux-visual.json --session ... --plan-version N
|
|
195
233
|
--causal-parent ...` (schema check → grader verdict → tests → coverage
|
|
196
234
|
evidence → findings intake → severity decision,
|
|
197
235
|
`docs/guide/operator-guide/queue-and-gate.md` §5).
|
|
236
|
+
- `--plan` is what lets the gate's uiux stage read the task's
|
|
237
|
+
`ui_affecting` flag; without it the stage records "unverifiable" and
|
|
238
|
+
does not block. `--uiux-visual` hands over the visual pass's result
|
|
239
|
+
file when step 5 ran one — omit it for a task that is not UI-affecting.
|
|
240
|
+
- `checks.json`'s unit check must be the project's full test command,
|
|
241
|
+
never a hand-picked list of files — `gate run` runs each check command
|
|
242
|
+
literally, with no narrowing of its own. Narrowing by changed files is
|
|
243
|
+
`bs queue run --select-test-cmd` (`factory/orchestrator/src/testSelect.ts`),
|
|
244
|
+
a queue-tier flag `--batch` refuses; it does not reach `checks.json`. A
|
|
245
|
+
scoped-down unit check is how red tests have merged before.
|
|
198
246
|
- `--grader` takes step 6's file, latest round. A criterion that came back
|
|
199
247
|
`fail` or `partial` blocks before any check command runs — the outcome is
|
|
200
248
|
`blocked` with `reason: "grader-fail"`, and the payload's
|
|
@@ -205,11 +253,11 @@ one thing this playbook never asks you to.
|
|
|
205
253
|
add (D-40). `blocked` with `reason: "coverage-evidence"` means the run
|
|
206
254
|
produced no per-file number for a file this task's claims name; fix the
|
|
207
255
|
reporter or the include glob, not the code. Ask the same question
|
|
208
|
-
outside a gate run with `
|
|
256
|
+
outside a gate run with `bs coverage check <worktree-dir> --plan
|
|
209
257
|
<plan.json> --task <task-id>` (§5c).
|
|
210
|
-
- Findings come from dispatching **`reviewer`**
|
|
211
|
-
(`.claude/agents/reviewer.md`, fresh context, read-only) then
|
|
212
|
-
**`verifier`** (`.claude/agents/verifier.md`, adversarial refute
|
|
258
|
+
- Findings come from dispatching **`bs-reviewer`**
|
|
259
|
+
(`.claude/agents/bs-reviewer.md`, fresh context, read-only) then
|
|
260
|
+
**`bs-verifier`** (`.claude/agents/bs-verifier.md`, adversarial refute
|
|
213
261
|
mandate) — never skip the adversarial stage on an S1/S2 finding
|
|
214
262
|
(`crosscheck.yml` `asymmetric_roles`). Which findings that covers is
|
|
215
263
|
`profile.verifierSeverities`, plus a
|
|
@@ -219,25 +267,26 @@ one thing this playbook never asks you to.
|
|
|
219
267
|
thing a cheaper tier drops; `S1-stop-the-line` is in every tier's list
|
|
220
268
|
and no tier may remove it. A finding outside the tier's list still goes
|
|
221
269
|
to the coder — it is verified by the coder's fix, not waived.
|
|
222
|
-
- Dispatch **`security-reviewer`** only when its conditional triggers
|
|
270
|
+
- Dispatch **`bs-security-reviewer`** only when its conditional triggers
|
|
223
271
|
fire — never per-task by default. Ask, do not recall:
|
|
224
|
-
`
|
|
272
|
+
`bs security triggers --task <spec.json>` and dispatch iff
|
|
225
273
|
`dispatchSecurityReviewer` is true ("Dispatching the security-reviewer"
|
|
226
274
|
above).
|
|
227
275
|
- Every judge dispatched in steps 5–7 — uiux visual pass, grader,
|
|
228
276
|
reviewer, verifier, security-reviewer — is bracketed by
|
|
229
|
-
`
|
|
277
|
+
`bs worktree fingerprint` / `bs worktree verify`
|
|
230
278
|
("Fingerprint the worktree around every judge" above). Exit 1 discards
|
|
231
279
|
that judge's result; it does not become a finding against the coder.
|
|
232
|
-
- Each of those is also bracketed by `
|
|
233
|
-
`
|
|
234
|
-
it" above)
|
|
280
|
+
- Each of those is also bracketed by `bs judge dispatch` /
|
|
281
|
+
`bs judge report` ("Declare each judge's artifact before you dispatch
|
|
282
|
+
it" above); paste `judge dispatch`'s `expected_line` inline in the
|
|
283
|
+
judge's prompt, or the `Agent` call is refused. `bs judge outstanding --task <task-id>` exits 1 while any
|
|
235
284
|
judge still owes its file — re-poke it and report before running the
|
|
236
285
|
gate, because the gate now refuses to score with a non-empty outstanding
|
|
237
286
|
set (`reason: judges-outstanding`) rather than reading a silent judge as
|
|
238
287
|
zero findings.
|
|
239
288
|
- The gate scores tests it did not write, so once it has run, make the log
|
|
240
|
-
say who did: `
|
|
289
|
+
say who did: `bs tester check <session-id> --task <task-id>`
|
|
241
290
|
(`crosscheck.yml` `role_isolation`, operator-guide/dispatch-audits.md §2d). Exit 1
|
|
242
291
|
means no `tester` dispatch precedes this task's `testgate-result`, the
|
|
243
292
|
coder and tester dispatches share one `agent_id`, or the answer is
|
|
@@ -246,9 +295,9 @@ one thing this playbook never asks you to.
|
|
|
246
295
|
downstream still goes green — step 5 is what prevents that, and this is
|
|
247
296
|
the log checking that step 5 happened.
|
|
248
297
|
8. Every dispatch in steps 3–7 carries the compiled lessons block for that
|
|
249
|
-
role (`
|
|
298
|
+
role (`bs lessons for-dispatch <role> --plan … --task …`), every
|
|
250
299
|
worktree dispatch also carries the open-findings block for that task
|
|
251
|
-
(`
|
|
300
|
+
(`bs findings for-dispatch --plan … --task …`) — both under "Dispatch
|
|
252
301
|
contract" above — and each is a `dispatch_decision` event with a `causal_parent`
|
|
253
302
|
chaining back to the prompt/decision that caused it (architecture §7) —
|
|
254
303
|
this is the timeline, not optional bookkeeping.
|
|
@@ -259,22 +308,25 @@ one thing this playbook never asks you to.
|
|
|
259
308
|
rung, never loop past one. Count the rounds from this task's
|
|
260
309
|
`dispatch_decision` events, not from the agent's own account of itself
|
|
261
310
|
— see "Round counting and escalation" above. Then have the log check
|
|
262
|
-
you: `
|
|
311
|
+
you: `bs escalation check <session-id> --task <task-id>`, which
|
|
263
312
|
exits 1 if the rung you just climbed is not evidenced.
|
|
264
313
|
10. Gate outcome `pass`/`pass-with-waivers-pending` → before admitting, audit
|
|
265
|
-
the wave's own parallelism: `
|
|
314
|
+
the wave's own parallelism: `bs wave audit --session <id> --epic
|
|
266
315
|
<epic>`. It reads the log back and says whether the tasks admitted
|
|
267
316
|
together actually ran together — `parallel` when every admitted task was
|
|
268
317
|
in flight at once, `partial` when two or more overlapped but never all,
|
|
269
318
|
`serialized` when work is recorded but no two tasks ever overlapped,
|
|
270
319
|
`single` when the wave admitted one task (nothing to be parallel about),
|
|
271
|
-
|
|
272
|
-
|
|
320
|
+
`unlinked` when the work merged or dispatched but off the epic's own
|
|
321
|
+
lineage (width unmeasured, not zero — usually a wave-runner session
|
|
322
|
+
started without `--continues`), and `unobserved` when the wave was
|
|
323
|
+
admitted and the log shows no work for any of its tasks. Exit 1 on
|
|
324
|
+
`serialized`, exit 2 on `unobserved`, exit 0 on `unlinked` — each
|
|
273
325
|
is a fact for the wave-loop's hand-back, not a reason to hold the merge:
|
|
274
326
|
report the verdict and keep going to admission. Then ask what the diff
|
|
275
327
|
did to everyone outside the claims, and whether it
|
|
276
328
|
kept what the spec promised:
|
|
277
|
-
`
|
|
329
|
+
`bs claims impact <worktree-dir> <spec.json>`. Exit 1 means a
|
|
278
330
|
`proven` break — this task removed an export a file outside its claims
|
|
279
331
|
still imports — **or a broken promise**: a file the spec's
|
|
280
332
|
`keeps_exports` names lost an export or changed one's declaration.
|
|
@@ -282,10 +334,14 @@ one thing this playbook never asks you to.
|
|
|
282
334
|
entries in the bounce. A `possible` / `signature-changed` entry in
|
|
283
335
|
`breaks` exits 0 and is a note: the scanner reads text, not types
|
|
284
336
|
(operator-guide/wave.md §2). Then admit into the
|
|
285
|
-
merge queue: `
|
|
286
|
-
--test-cmd "<cumulative test command>" --tasks tasks.json
|
|
287
|
-
|
|
288
|
-
|
|
337
|
+
merge queue: `bs queue run <epic> --project <project-dir>
|
|
338
|
+
--test-cmd "<cumulative test command>" --tasks tasks.json --plan
|
|
339
|
+
<plan.json> --session <id> --plan-version <n> --causal-parent
|
|
340
|
+
<event-id>` — the same write triple as every other dispatch above; a
|
|
341
|
+
merge that lands with no session to log it into is a fact the
|
|
342
|
+
timeline never gets. On a
|
|
343
|
+
`rebase-conflict` outcome, dispatch **`bs-merger`**
|
|
344
|
+
(`.claude/agents/bs-merger.md`) with both diffs + specs, and note three
|
|
289
345
|
things the queue's own behaviour forces (agent-interviews.md N-12):
|
|
290
346
|
- **Into the failing task's existing worktree** — a fresh one would not
|
|
291
347
|
reproduce the conflict. The result gives you only `taskId` and
|
|
@@ -296,7 +352,7 @@ one thing this playbook never asks you to.
|
|
|
296
352
|
- The queue already ran `git rebase --abort` (`queue.ts`), so the
|
|
297
353
|
merger arrives at a clean tree and **replays** the rebase rather than
|
|
298
354
|
resuming it — and it **never lands the merge**. When it returns
|
|
299
|
-
`resolution: "mechanical"`, re-run `
|
|
355
|
+
`resolution: "mechanical"`, re-run `bs queue run`; the queue stays
|
|
300
356
|
the single place a merge can happen.
|
|
301
357
|
|
|
302
358
|
`resolution: "escalated"` — low confidence, or both sides changed the
|
|
@@ -305,10 +361,10 @@ one thing this playbook never asks you to.
|
|
|
305
361
|
`conflict_resolution_ladder`).
|
|
306
362
|
|
|
307
363
|
An `integration-dirty` outcome names a `worktree` holding
|
|
308
|
-
`
|
|
309
|
-
commit or stash those there, then re-run `
|
|
364
|
+
`bs/<epic>/integration` with uncommitted tracked changes (`dirty`):
|
|
365
|
+
commit or stash those there, then re-run `bs queue run`.
|
|
310
366
|
|
|
311
|
-
Once the wave is merged, check the epic's spend — `
|
|
367
|
+
Once the wave is merged, check the epic's spend — `bs budget alarm
|
|
312
368
|
<session-id> [--epic <epic>]`. Exit 1 means either the epic crossed
|
|
313
369
|
`alarm_ratio` (re-plan the remaining work to fit, or ask the operator —
|
|
314
370
|
extension is always an operator question) **or** the log is too holey to
|