@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
|
@@ -81,8 +81,8 @@ select on. Each names its own bullet, validated against `taxonomy.yml`:
|
|
|
81
81
|
At dispatch, an `agent-role` entry reaches only the role its `agent_role`
|
|
82
82
|
names, and a `case-type` entry only a task whose `case` its `case_type`
|
|
83
83
|
names. An entry in one of those scopes with **no** selector reaches NOBODY —
|
|
84
|
-
it is inert, not universal, and `
|
|
85
|
-
such. `
|
|
84
|
+
it is inert, not universal, and `bs lessons for-dispatch` reports it as
|
|
85
|
+
such. `bs lessons raise` refuses to mint one; the entries that predate
|
|
86
86
|
the selector (D-129) are the only ones that can still be in that state.
|
|
87
87
|
|
|
88
88
|
Two kinds of entry are therefore injected at dispatch and still never
|
|
@@ -124,6 +124,26 @@ nothing.
|
|
|
124
124
|
- agent_role: planner
|
|
125
125
|
- statement: An acceptance criterion that probes a script running under 'set -e' must name the outer exit status it expects, never an echo the script prints afterwards: errexit ends the script at the first failing command, so a line printed after it is unreachable and the grader can only mark the wording, not the property.
|
|
126
126
|
|
|
127
|
+
### lesson-raised-3aaf982e84ac: A file-length delta criterion must state whether the doc comments att...
|
|
128
|
+
|
|
129
|
+
- lesson_id: lesson-raised-3aaf982e84ac
|
|
130
|
+
- finding_category: correctness
|
|
131
|
+
- agent_role: planner
|
|
132
|
+
- statement: A file-length delta criterion must state whether the doc comments attached to the moved symbols move with them, and its window must be calibrated on that same choice; a window measured with comments included and silent about it fails a coder who leaves the comments behind.
|
|
133
|
+
|
|
134
|
+
### lesson-raised-d478f9d6ea0b: Name an extraction task's scope as a symbol list, not a line range, a...
|
|
135
|
+
|
|
136
|
+
- lesson_id: lesson-raised-d478f9d6ea0b
|
|
137
|
+
- finding_category: correctness
|
|
138
|
+
- agent_role: planner
|
|
139
|
+
- statement: Name an extraction task's scope as a symbol list, not a line range, and derive its length window from the symbols that move; a window calibrated on a span that is wider than the enumerated symbols rewards the over-broad move and rejects the correct one, and a text-preserving destructure hides the difference from every other criterion.
|
|
140
|
+
|
|
141
|
+
### bs-audit-3-scribe-cites-event-ids: A PR body or timeline summary cites events by event id (<session>#<n>...
|
|
142
|
+
|
|
143
|
+
- lesson_id: bs-audit-3-scribe-cites-event-ids
|
|
144
|
+
- agent_role: scribe
|
|
145
|
+
- statement: A PR body or timeline summary cites events by event id (<session>#<n>, where n is the zero-based line index of the log), never by a line number of the log file, and states no number the event log does not carry: coverage percentages, turn caps, round reasons and provider counts included. The orchestrator checks every cited id against the log before the body is published.
|
|
146
|
+
|
|
127
147
|
## claim-path
|
|
128
148
|
|
|
129
149
|
### lesson-raised-3b5746704cfc: A function that computes a filesystem path and hands it to a child pr...
|
|
@@ -654,6 +674,37 @@ _(none yet)_
|
|
|
654
674
|
- lesson_id: lesson-raised-3a400ca69b5e
|
|
655
675
|
- statement: When two verbs are compared for cost, check when each one runs before concluding: the cheaper one may run after the very gate it was meant to save.
|
|
656
676
|
|
|
677
|
+
### lesson-raised-44a58c2a7c7a: When checking a length-delta criterion, classify every line in the mo...
|
|
678
|
+
|
|
679
|
+
- lesson_id: lesson-raised-44a58c2a7c7a
|
|
680
|
+
- finding_category: correctness
|
|
681
|
+
- statement: When checking a length-delta criterion, classify every line in the moved region as code, comment or blank and count them; never derive a line count by subtracting line numbers, because the span between two anchors includes whatever else lives there.
|
|
682
|
+
|
|
683
|
+
### lesson-raised-394329488a2d: SMITH_EPIC_CAP_TOKENS in the gitignored .env overrides the epic cap i...
|
|
684
|
+
|
|
685
|
+
- lesson_id: lesson-raised-394329488a2d
|
|
686
|
+
- statement: SMITH_EPIC_CAP_TOKENS in the gitignored .env overrides the epic cap in budgets.yml, so the committed cap is not the enforced one; read budget alarm's capTokens and budgetEnvOverrides for the live cap, never budgets.yml.
|
|
687
|
+
|
|
688
|
+
### lesson-raised-add2171a83a1: On a plan whose tasks are all origin user, the plan-quorum triggers a...
|
|
689
|
+
|
|
690
|
+
- lesson_id: lesson-raised-add2171a83a1
|
|
691
|
+
- statement: On a plan whose tasks are all origin user, the plan-quorum triggers are mechanically silent - trigger 1 sums declared budgets, not measured spend, and trigger 3's per-task arm only reads non-user origins - so the only live trigger is the orchestrator's own --confidence self-report; report it honestly below 0.8 when the draft needed repair rounds.
|
|
692
|
+
|
|
693
|
+
### lesson-raised-be27b49f1d19: budget alarm reports unverifiable when dispatches for roles without a...
|
|
694
|
+
|
|
695
|
+
- lesson_id: lesson-raised-be27b49f1d19
|
|
696
|
+
- statement: budget alarm reports unverifiable when dispatches for roles without an attribution path (auditor, security-reviewer) are in the log, and the planner role has no per-role cap, so a planner overrun is only visible against the epic cap.
|
|
697
|
+
|
|
698
|
+
### bs-audit-3-gate-timeout-under-load: Size a test-gate check's timeout for a loaded machine, not a quiet on...
|
|
699
|
+
|
|
700
|
+
- lesson_id: bs-audit-3-gate-timeout-under-load
|
|
701
|
+
- statement: Size a test-gate check's timeout for a loaded machine, not a quiet one: a suite that runs in about 200 s bare can be killed at a 300 s default with no output while another build shares the CPUs. When a check dies at its timeout with no test output, read machine load before blaming the code, log it as an env failure, and raise that check's timeout_ms through the per-check override; never drop or narrow the check to get it under the limit.
|
|
702
|
+
|
|
703
|
+
### lesson-dream-vam-audit-7-2026-09-28-135: Every judge or worker dispatch brief names the JSON Schema its result...
|
|
704
|
+
|
|
705
|
+
- lesson_id: lesson-dream-vam-audit-7-2026-09-28-135
|
|
706
|
+
- statement: Every judge or worker dispatch brief names the JSON Schema its result file must satisfy: the full result envelope (task_id, agent, provider, model_tier, token_usage), the verdict at .structured_output, and the schema's own keys; the orchestrator schema-checks the file as soon as the agent returns, before gate run.
|
|
707
|
+
|
|
657
708
|
## security
|
|
658
709
|
|
|
659
710
|
_(none yet)_
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Blacksmith — scheduler policy
|
|
2
2
|
#
|
|
3
3
|
# Knobs for `factory/orchestrator/src/scheduler.ts` (architecture §12
|
|
4
|
-
# "Recheck scheduling" + "Scheduled growth passes"; `
|
|
4
|
+
# "Recheck scheduling" + "Scheduled growth passes"; `bs scheduler run`).
|
|
5
5
|
# A deterministic pass over the event log that only ever emits PROPOSAL
|
|
6
6
|
# events — it never dispatches an agent or widens scope on its own.
|
|
7
7
|
|
|
@@ -50,7 +50,7 @@ lessons:
|
|
|
50
50
|
autonomy:
|
|
51
51
|
# Who says yes to what this pass proposes. The scheduler decides WHAT is
|
|
52
52
|
# due; this block decides what may proceed without an operator tick, and
|
|
53
|
-
# `
|
|
53
|
+
# `bs scheduler admit` (src/autonomy.ts) applies it. That command still
|
|
54
54
|
# enacts nothing — it prints the classification so it can be read and
|
|
55
55
|
# argued with before anything moves.
|
|
56
56
|
#
|
|
@@ -71,7 +71,7 @@ autonomy:
|
|
|
71
71
|
# or the enable marker classifies operator and never reaches the merge at
|
|
72
72
|
# all. The loop may merge what it builds; it may not merge what bounds it.
|
|
73
73
|
#
|
|
74
|
-
# None of those six is enforced by code yet, because `
|
|
74
|
+
# None of those six is enforced by code yet, because `bs loop` does not
|
|
75
75
|
# exist yet. Until it does, this block's `enabled: true` still only governs
|
|
76
76
|
# dispatch, and the merge is performed by whoever is driving the session.
|
|
77
77
|
enabled: true
|
|
@@ -82,7 +82,7 @@ autonomy:
|
|
|
82
82
|
# says. The list is a ceiling, never a floor.
|
|
83
83
|
#
|
|
84
84
|
# `error-report` — the daemon proposing that an error the log holds with
|
|
85
|
-
# no `issue-reported` event be filed (`
|
|
85
|
+
# no `issue-reported` event be filed (`bs issues report`) — is left out
|
|
86
86
|
# on purpose, and listing it would not change that either. Filing writes
|
|
87
87
|
# to an external issue tracker under an authenticated `gh` token whose
|
|
88
88
|
# scope includes `repo`, and this block's standing decision is that
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# a task can claim `src/auth/**` without writing the word "auth" anywhere in
|
|
14
14
|
# its clauses, and that is exactly the task worth catching.
|
|
15
15
|
#
|
|
16
|
-
# HOST: `
|
|
16
|
+
# HOST: `bs security triggers --task <spec.json>` reads this file and
|
|
17
17
|
# returns the triggers that fired, with the claim and the glob that fired each
|
|
18
18
|
# one (factory/orchestrator/src/security.ts, punch list P9-4). The dispatcher
|
|
19
19
|
# in `.claude/skills/bs/dispatch.md` runs that verb rather than eyeballing the
|
|
@@ -31,6 +31,14 @@ levels:
|
|
|
31
31
|
- a11y WCAG AA failure
|
|
32
32
|
- new flaky test introduced by the task
|
|
33
33
|
- behavioral-drift touching a core flow
|
|
34
|
+
- >
|
|
35
|
+
serious visual defect against the uiux spec or the project's design
|
|
36
|
+
system: a spec'd layout broken, or content clipped/overflowing, at
|
|
37
|
+
a required viewport; an interactive target below the project's
|
|
38
|
+
declared touch-target minimum (fall back to the WCAG 2.2 target
|
|
39
|
+
size minimum only when the project declares none); a required
|
|
40
|
+
state (empty, error, loading) missing (contrast: see a11y WCAG AA
|
|
41
|
+
above)
|
|
34
42
|
on_trigger: >
|
|
35
43
|
bounce to coder on the same branch; after 2 failed rounds, escalate
|
|
36
44
|
model tier (mid -> frontier, budgets.yml); after 3, escalate to
|
|
@@ -40,7 +48,11 @@ levels:
|
|
|
40
48
|
blocks_merge: false
|
|
41
49
|
semantics: waivable — batched to operator at epic end
|
|
42
50
|
classes:
|
|
43
|
-
-
|
|
51
|
+
- >
|
|
52
|
+
minor visual deviation from the uiux spec: spacing, color or type a
|
|
53
|
+
few px or a shade off a design-system token — not a broken layout,
|
|
54
|
+
clipped content, a missing required state, or a touch-target/
|
|
55
|
+
contrast failure below WCAG AA (those are S2-major, above)
|
|
44
56
|
- performance regression > 20%
|
|
45
57
|
- over-engineering (default severity — see rule below)
|
|
46
58
|
- behavioral-drift not touching a core flow
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
#
|
|
3
3
|
# What the operator answered when they installed this clone (INSTALL.md
|
|
4
4
|
# Step 6, "The stack interview"). Read by `factory/orchestrator/src/stack.ts`
|
|
5
|
-
# via `
|
|
5
|
+
# via `bs stack show`, and through it by the scaffolder and by every agent
|
|
6
6
|
# that needs to know what this operator builds with.
|
|
7
7
|
#
|
|
8
8
|
# This file used to be a document. `docs/standards/stack.md` shipped one
|
|
@@ -13,20 +13,20 @@
|
|
|
13
13
|
# question would otherwise put words in the operator's mouth.
|
|
14
14
|
#
|
|
15
15
|
# Not every answer is executable. The shipped templates implement a subset —
|
|
16
|
-
# `
|
|
17
|
-
# recorded for the agents to read but not built into `
|
|
18
|
-
# silently pretends: an answer the templates cannot honour makes `
|
|
16
|
+
# `bs stack check` prints which of your answers they honour and which are
|
|
17
|
+
# recorded for the agents to read but not built into `bs new`. Nothing here
|
|
18
|
+
# silently pretends: an answer the templates cannot honour makes `bs new`
|
|
19
19
|
# refuse rather than scaffold something else.
|
|
20
20
|
|
|
21
21
|
# ── Language ────────────────────────────────────────────────────────
|
|
22
22
|
# What the coder writes. `typescript` is the only value the shipped templates
|
|
23
|
-
# scaffold; any other value is recorded, and `
|
|
23
|
+
# scaffold; any other value is recorded, and `bs new` refuses rather than
|
|
24
24
|
# handing you a TypeScript project you did not ask for.
|
|
25
25
|
language: typescript # typescript | javascript | python | go | rust | other
|
|
26
26
|
|
|
27
27
|
# ── Frontend ────────────────────────────────────────────────────────
|
|
28
28
|
# `none` is the default because most projects are not UI projects, and the
|
|
29
|
-
# base template is a plain library. `
|
|
29
|
+
# base template is a plain library. `bs new --ui` needs a value the
|
|
30
30
|
# templates implement — today that is `vue` and nothing else.
|
|
31
31
|
frontend: none # none | vue | react | svelte | solid | vite-vanilla | other
|
|
32
32
|
|
|
@@ -77,6 +77,7 @@ gate_event: [schema-check-result, artifact-check-result,
|
|
|
77
77
|
finding-raised, finding-reverified, finding-suppressed,
|
|
78
78
|
finding-transitioned, finding-reattributed,
|
|
79
79
|
severity-decisions, waiver-granted, waiver-denied,
|
|
80
|
+
task-waiver-approved,
|
|
80
81
|
gate-outcome, issue-reported]
|
|
81
82
|
# schema-check-result — task Result vs result.schema.json
|
|
82
83
|
# artifact-check-result — every artifacts[].path in that
|
|
@@ -95,7 +96,7 @@ gate_event: [schema-check-result, artifact-check-result,
|
|
|
95
96
|
# always logged, including when the
|
|
96
97
|
# project declares nothing to install
|
|
97
98
|
# judges-outstanding — a judge this task dispatched through
|
|
98
|
-
# `
|
|
99
|
+
# `bs judge dispatch` never reported
|
|
99
100
|
# back, so the gate refuses to score an
|
|
100
101
|
# unknown: a missing `--evidence` reads
|
|
101
102
|
# identically whether the judge found
|
|
@@ -194,6 +195,11 @@ gate_event: [schema-check-result, artifact-check-result,
|
|
|
194
195
|
# waiver-batch / log-only)
|
|
195
196
|
# waiver-granted/-denied — operator waiver-batch answer,
|
|
196
197
|
# keyed by finding fingerprint
|
|
198
|
+
# task-waiver-approved — operator approval of one waived
|
|
199
|
+
# task, keyed by task id and valid from
|
|
200
|
+
# its plan_version on; what the
|
|
201
|
+
# epic gate accepts in place of a
|
|
202
|
+
# gate run
|
|
197
203
|
# gate-outcome — the composed gate pipeline's
|
|
198
204
|
# final structured result
|
|
199
205
|
# issue-reported — issueReporter.ts's record of one
|
|
@@ -219,10 +225,10 @@ error:
|
|
|
219
225
|
# (planner, scribe) wrote outside its declared write root
|
|
220
226
|
# (P9-3); shipped as a code before it was a class
|
|
221
227
|
# judge-mutation — a read-only judge moved the worktree it
|
|
222
|
-
# was judging (P9-5, `
|
|
228
|
+
# was judging (P9-5, `bs worktree verify`)
|
|
223
229
|
# uncited-claim — a researcher finding with no citation, or a
|
|
224
230
|
# citation that is neither a repo path with a line number nor
|
|
225
|
-
# a URL actually fetched (P9-6, `
|
|
231
|
+
# a URL actually fetched (P9-6, `bs research check`)
|
|
226
232
|
# unsourced-recommendation — a brief's recommendation names no
|
|
227
233
|
# finding, or names one the brief does not carry, so what came
|
|
228
234
|
# from fetched text is invisible (P9-6)
|
|
@@ -17,19 +17,24 @@ worktree:
|
|
|
17
17
|
# Relative to whatever directory the project was handed as, never to this
|
|
18
18
|
# clone: a project beside the repo keeps its worktrees beside the repo too.
|
|
19
19
|
path_pattern: "<project-parent>/.wt/<project>/<task-id>"
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
# bs-rename, operator decision 2/3: bs/ is the current prefix for every new
|
|
21
|
+
# epic; one that already integrates on the legacy smith/ prefix keeps it
|
|
22
|
+
# for its whole life rather than switching mid-epic (integration_branch
|
|
23
|
+
# below).
|
|
24
|
+
branch_pattern: "bs/<epic>/<task-id>"
|
|
25
|
+
created_from: integration branch head (bs/<epic>/integration), fresh each time
|
|
22
26
|
lifecycle: deleted after merge — a stale worktree is a bug
|
|
23
27
|
one_worktree_per_task: true
|
|
24
28
|
|
|
25
29
|
integration_branch:
|
|
26
|
-
# Phase-3 correction: a bare "
|
|
27
|
-
# "
|
|
30
|
+
# Phase-3 correction: a bare "bs/<epic>" ref cannot coexist with
|
|
31
|
+
# "bs/<epic>/<task-id>" refs — git's ref namespace is hierarchical, so a
|
|
28
32
|
# leaf ref and a directory prefix of the same name conflict (D/F conflict,
|
|
29
33
|
# confirmed against a real git repo while implementing worktree.ts). The
|
|
30
34
|
# integration branch is therefore a sibling of task branches under the
|
|
31
|
-
# epic's ref prefix, not a leaf at "
|
|
32
|
-
|
|
35
|
+
# epic's ref prefix, not a leaf at "bs/<epic>" itself. An epic that already
|
|
36
|
+
# integrates on smith/<epic>/integration (legacy) keeps that prefix instead.
|
|
37
|
+
pattern: "bs/<epic>/integration"
|
|
33
38
|
rule: workers never touch main/master; only the operator merges the
|
|
34
39
|
integration PR into the target repo's main.
|
|
35
40
|
|
|
@@ -75,7 +80,8 @@ merge_queue:
|
|
|
75
80
|
mode: serial
|
|
76
81
|
order: dependency order (topological, per recorded edges)
|
|
77
82
|
admission_steps:
|
|
78
|
-
- rebase task branch onto current
|
|
83
|
+
- rebase task branch onto current integration branch head (bs/<epic>/integration,
|
|
84
|
+
or smith/<epic>/integration for a legacy epic)
|
|
79
85
|
- run the cumulative regression gate (every previously merged task's
|
|
80
86
|
tests in this epic, not just the incoming task)
|
|
81
87
|
- admit on green; reject and bounce on red
|
|
@@ -90,6 +96,6 @@ merge_queue:
|
|
|
90
96
|
enabled: true
|
|
91
97
|
max_lines_per_layer: 200
|
|
92
98
|
note: >
|
|
93
|
-
workers emit small stacked layers (within the
|
|
94
|
-
rather than monolithic diffs; the queue lands stacks in dependency
|
|
99
|
+
workers emit small stacked layers (within the task diff cap,
|
|
100
|
+
budgets.yml `task.coder.cap_diff_lines`) rather than monolithic diffs; the queue lands stacks in dependency
|
|
95
101
|
order (GitHub native stacked PRs).
|
package/factory/specs/roadmap.md
CHANGED
|
@@ -25,13 +25,13 @@ Each milestone is a `## <name>` heading followed by bullet fields:
|
|
|
25
25
|
`completed`), `goal` (optional, one sentence), `epics` (optional, `[]` when
|
|
26
26
|
no epic has been mapped to this phase yet — the join yields zero tasks
|
|
27
27
|
until an epic is tagged here), `project` (optional, defaults to
|
|
28
|
-
`
|
|
28
|
+
`blacksmith` — this clone), `kind` (optional — `factory` | `dogfood` |
|
|
29
29
|
`product`), and `error_issues` (optional — `on` | `off`).
|
|
30
30
|
|
|
31
31
|
`kind` describes the *project*, not the milestone, so one bullet anywhere in
|
|
32
32
|
a project's milestones settles all of them; a second copy would only be a
|
|
33
|
-
copy that drifts. It defaults to `factory` for `
|
|
34
|
-
for everything else, which is why a project registered by `
|
|
33
|
+
copy that drifts. It defaults to `factory` for `blacksmith` and `product`
|
|
34
|
+
for everything else, which is why a project registered by `bs new` needs
|
|
35
35
|
no bullet at all. `dogfood` is the one value that must be written by hand:
|
|
36
36
|
a project built to exercise the factory looks, in the data, exactly like a
|
|
37
37
|
project built for its own sake — the difference is intent. The Roadmap page
|
|
@@ -180,7 +180,7 @@ all; write `- error_issues: off` by hand to opt a project's tracker out.
|
|
|
180
180
|
- id: bs-audit-1
|
|
181
181
|
- status: planned
|
|
182
182
|
- epics: [bs-audit-1]
|
|
183
|
-
- project:
|
|
183
|
+
- project: blacksmith
|
|
184
184
|
- kind: factory
|
|
185
185
|
- goal: Cut from audit 20260925-f336f5e4 of `blacksmith` (7 accepted findings: 3 S2-major, 3 S3-minor, 1 S4-nit). Each finding below is one acceptance criterion; the plan decides the tasks. **d7c644db (S2-major, performance)** `factory/orchestrator/src/events.ts` — appendEvent() re-reads and re-JSON.parses the entire session event log on every single append (readEventsAtPath inside appendEventLocked), while holding both an in-process queue and a cross-process poll lock, so writing to a session log is O(n) per event and O(n^2) for a session of n events.. **0f3f9c23 (S3-minor, performance)** `factory/orchestrator/src/worktree.ts` — listStale() spawns one synchronous `git merge-base --is-ancestor` subprocess per worktree entry in a loop instead of a single batched check, so merge-queue/worktree cleanup cost scales linearly with subprocess spawns rather than with one git invocation.. **ae8cb69e (S2-major, architecture)** `factory/orchestrator/src/queue.ts` — The queue module shells out to git directly with execFileSync instead of going through git.ts, the module the codebase documents as the one place this factory shells out to git, so queue git failures bypass credential redaction and the structured GitCommandError type.. **fd03e000 (S3-minor, architecture)** `factory/orchestrator/src/diffstat.ts` — diffstat.ts calls execFileSync('git', ...) directly for rev-parse/diff/branch instead of through git.ts, duplicating the single git-invocation boundary the codebase declares for itself and losing that module's credential redaction and GitCommandError typing.. **160cc3d7 (S3-minor, architecture)** `factory/orchestrator/src/commit.ts` — commit.ts, which certifies whether a task's work is safe to integrate, runs its own execFileSync('git', ...) calls rather than routing through git.ts, so the one module the codebase names as the sole git entry point has at least two siblings that re-implement direct git shell-outs.. **07c5d1b0 (S2-major, code-quality)** `factory/orchestrator/src/claims.ts` — collectCommittedChanges, the collector behind postRunCheck (the claim-confinement check for a task worktree), runs `git diff --name-only` without `--no-renames`, so a rename that crosses a claim boundary is reported only by its destination path.. **ad9ea9ba (S4-nit, code-quality)** `factory/orchestrator/src/waveNext.ts` — The one-line helper nameList (ids.join(', ')) is duplicated verbatim in waveNext.ts and waveSchedule.ts rather than shared, so the two wave-scheduling files carry two independent copies of the same id-list formatting rule..
|
|
186
186
|
|
|
@@ -188,7 +188,7 @@ all; write `- error_issues: off` by hand to opt a project's tracker out.
|
|
|
188
188
|
- id: bs-audit-2
|
|
189
189
|
- status: planned
|
|
190
190
|
- epics: [bs-audit-2]
|
|
191
|
-
- project:
|
|
191
|
+
- project: blacksmith
|
|
192
192
|
- kind: factory
|
|
193
193
|
- goal: Cut from audit 20260925-49bffa9b of `blacksmith` (5 accepted findings: 2 S2-major, 3 S3-minor). Each finding below is one acceptance criterion; the plan decides the tasks. **50acc356 (S3-minor, performance)** `factory/orchestrator/src/daemon.ts` — runTick() re-reads and re-JSON.parses every session's full .jsonl event log from disk on every daemon tick, with no incremental/fingerprint caching, so the daemon's per-tick cost grows linearly and unboundedly with the factory's total historical event volume.. **d50d4c1f (S3-minor, performance)** `ui/server/src/app.ts` — Every dashboard write route that omits an explicit causalParent (waivers apply-batch, lessons approve/reject/edit) triggers a full read-and-parse of that session's entire event log via resolveContext()/lessonContext() just to read the id of the last event.. **4c610e7c (S2-major, security)** `ui/server/src/app.ts` — The dashboard's write endpoints (waiver apply-batch, lesson approve/reject/edit) have no CSRF protection: there is no Origin/Referer check and Hono's `c.req.json()` parses the request body as JSON regardless of the Content-Type header, so a cross-origin `text/plain` form POST (a CORS-exempt 'simple request' that needs no preflight) is accepted and executed.. **1b8fe398 (S3-minor, architecture)** `.claude/skills/bs/wave.md` — The wave playbook's own loop (steps 2-10) never invokes the one instrument that measures whether a wave actually ran in parallel (auditWaveConcurrency/`smith wave check`, factory/orchestrator/src/waveConcurrency.ts), so the factory's central scaling claim goes unchecked for the entire time it could still be corrected.. **7ef3cd2f (S2-major, architecture)** `.claude/skills/bs/dispatch.md` — The one hard cap that bounds every subagent's work (`maxTurns`, enforced by the Claude Code harness itself) is transmitted into each dispatch prompt purely as prose the dispatching agent must copy correctly by hand from `.claude/agents/<role>.md` 'at dispatch time' — no module in factory/orchestrator/src validates that a dispatch's stated turn budget matches the template the harness will actually enforce, so the one enforcement path for this invariant is a human/LLM re-reading a file correctly every single time.. **Operator-added scope (2026-09-25, from GitHub research, ideas only)**: (6) a `SubagentStop` hook in the judge-class agent templates' frontmatter that blocks a judge from ending while its declared artifact file is absent — complements `agent-resume`, which handles the cap itself; (7) planning emits parallel width mechanically — at plan ingest, tasks with disjoint claims and no dependency edge are marked parallel and the plan's achievable width is reported, and a small epic takes a right-sized planning path with a bounded review focus. The per-task pipeline (no wave barrier) is deliberately out of scope until a pros/cons analysis is decided.
|
|
194
194
|
|
|
@@ -196,6 +196,6 @@ all; write `- error_issues: off` by hand to opt a project's tracker out.
|
|
|
196
196
|
- id: bs-audit-3
|
|
197
197
|
- status: planned
|
|
198
198
|
- epics: [bs-audit-3]
|
|
199
|
-
- project:
|
|
199
|
+
- project: blacksmith
|
|
200
200
|
- kind: factory
|
|
201
|
-
- goal: Cut from audit 20260927-b61eaa7e of `
|
|
201
|
+
- goal: Cut from audit 20260927-b61eaa7e of `blacksmith` (2 accepted findings: 2 S3-minor). Each finding below is one acceptance criterion; the plan decides the tasks. **871ee8f7 (S3-minor, architecture)** `ui/server/src/app.ts` — lessonSession() builds its own drizzle query directly against the `lessons` schema table instead of going through db/queries.ts, the module the file's own header comment declares as the single 1:1 wrapper boundary between the UI and the projection.. **442cd7f2 (S3-minor, code-quality)** `factory/orchestrator/src/daemon.ts` — daemon.ts defines its own readJsonFile() that silently swallows any parse/read error and returns null, diverging from the fail-closed readJsonFile() in cli.ts that throws on malformed JSON, so a corrupted daemon lock/status/memory file is treated as absent rather than reported..
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"$id": "https://black-smith.dev/schema/event.schema.json",
|
|
4
4
|
"$comment": "Taxonomy-valued fields carry an x-taxonomy annotation naming the dimension in factory/policies/taxonomy.yml. The runtime validates those values against that file at write time; enums are never inlined here to avoid drift between the schema and the taxonomy (architecture §8).",
|
|
5
5
|
"title": "Event",
|
|
6
|
-
"description": "One record in state/events/*.jsonl, the factory's durable execution substrate (architecture §7). Field names follow §7's record shape exactly: {ts, session_id, actor, event_type, task_id?, agent_id?, plan_version, causal_parent, payload}.",
|
|
6
|
+
"description": "One record in state/events/*.jsonl, the factory's durable execution substrate (architecture §7). Field names follow §7's record shape exactly: {ts, session_id, actor, event_type, task_id?, agent_id?, plan_version, causal_parent, payload, project?, cli_session_id?}.",
|
|
7
7
|
"type": "object",
|
|
8
8
|
"required": [
|
|
9
9
|
"ts",
|
|
@@ -61,6 +61,13 @@
|
|
|
61
61
|
"minLength": 1,
|
|
62
62
|
"description": "Optional plain-string project identifier (Phase 6b, architecture §8) — NOT a closed taxonomy.yml vocabulary value, so no x-taxonomy annotation here. Absent on every event logged before Phase 6b; db/queries.ts's read helpers treat an absent/null project as the default 'black-smith'. That default is reached by absence, so \"\" would slip past it and name a project called nothing (D-244)."
|
|
63
63
|
},
|
|
64
|
+
"cli_session_id": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"minLength": 1,
|
|
67
|
+
"maxLength": 64,
|
|
68
|
+
"pattern": "^[0-9a-fA-F-]{8,64}$",
|
|
69
|
+
"description": "Optional — the CLI session that wrote the event, stamped by the writer from CLAUDE_CODE_SESSION_ID and absent outside one (and on every event logged before the stamp existed). Not part of the envelope: it never stands in for session_id or causal_parent (architecture §18 rule 8), and it is never turn or delegation evidence, since an epic session and its subagents can share one CLI session (rule 4). It only links a live CLI session to the factory sessions it drives."
|
|
70
|
+
},
|
|
64
71
|
"edge": {
|
|
65
72
|
"type": "object",
|
|
66
73
|
"description": "Present only on edge-recorded events (architecture §7).",
|
|
@@ -46,14 +46,27 @@
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"token_usage": {
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
"description": "Either a real count, stamped by the dispatcher off the harness's own measurement, or an honest admission that nothing could be measured — a dispatcher running inside an environment with no API onto a subagent's token spend (#220). The field stays required either way: only its shape may say 'not measured', its absence never may.",
|
|
50
|
+
"oneOf": [
|
|
51
|
+
{
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["input_tokens", "output_tokens", "total_tokens"],
|
|
54
|
+
"properties": {
|
|
55
|
+
"input_tokens": { "type": "integer", "minimum": 0 },
|
|
56
|
+
"output_tokens": { "type": "integer", "minimum": 0 },
|
|
57
|
+
"total_tokens": { "type": "integer", "minimum": 0 }
|
|
58
|
+
},
|
|
59
|
+
"additionalProperties": false
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"type": "object",
|
|
63
|
+
"required": ["measured"],
|
|
64
|
+
"properties": {
|
|
65
|
+
"measured": { "const": false }
|
|
66
|
+
},
|
|
67
|
+
"additionalProperties": false
|
|
68
|
+
}
|
|
69
|
+
]
|
|
57
70
|
},
|
|
58
71
|
"agent": {
|
|
59
72
|
"type": "string",
|
|
@@ -129,10 +129,22 @@
|
|
|
129
129
|
"type": "string",
|
|
130
130
|
"description": "Optional plain-string project identifier (Phase 6b, architecture §8) — NOT a closed taxonomy.yml vocabulary value (projects open/close far more often than any §8 dimension). Absent tasks default to 'black-smith' in the projection layer."
|
|
131
131
|
},
|
|
132
|
+
"title": {
|
|
133
|
+
"type": "string",
|
|
134
|
+
"description": "Optional human-facing short title for the task row (DS3); falls back to a humanized task_id slug when absent."
|
|
135
|
+
},
|
|
136
|
+
"summary": {
|
|
137
|
+
"type": "string",
|
|
138
|
+
"description": "Optional human-facing one-line summary for the task row (DS3)."
|
|
139
|
+
},
|
|
132
140
|
"keeps_exports": {
|
|
133
141
|
"type": "array",
|
|
134
142
|
"items": { "type": "string" },
|
|
135
143
|
"description": "Repo-relative file paths, each inside one of this task's own claims, whose existing exports this task keeps: no export removed, no export's declaration changed; adding exports is allowed. A symbol crossing whose producer promised the exporting file does not serialize the wave (impact.ts waveImpact); the promise is verified post-run by `smith claims impact <worktree> <spec.json>` against the diff."
|
|
144
|
+
},
|
|
145
|
+
"ui_affecting": {
|
|
146
|
+
"type": "boolean",
|
|
147
|
+
"description": "Optional. True when this task changes what a user sees (components, styles, templates, on-screen copy); false when it claims UI-extension paths without a visible change. Absent means false at the gate, but validatePlan errors when a task claims a UI-extension path (.tsx/.jsx/.vue/.svelte/.css/.scss/.sass/.less/.html) and this key is absent — set it explicitly (U2)."
|
|
136
148
|
}
|
|
137
149
|
},
|
|
138
150
|
"additionalProperties": false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juzser/blacksmith",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Autonomous agent factory — loop runner and worktree engine.",
|
|
6
6
|
"keywords": [
|
|
@@ -33,8 +33,11 @@
|
|
|
33
33
|
},
|
|
34
34
|
"packageManager": "pnpm@9.3.0",
|
|
35
35
|
"bin": {
|
|
36
|
+
"bs": "factory/orchestrator/dist/cli.js",
|
|
37
|
+
"bs-run": "factory/orchestrator/dist/run-cli.js",
|
|
36
38
|
"smith": "factory/orchestrator/dist/cli.js",
|
|
37
|
-
"smith-run": "factory/orchestrator/dist/run-cli.js"
|
|
39
|
+
"smith-run": "factory/orchestrator/dist/run-cli.js",
|
|
40
|
+
"bs-prompt-hook": "factory/orchestrator/dist/promptHook.js"
|
|
38
41
|
},
|
|
39
42
|
"files": [
|
|
40
43
|
"factory/orchestrator/dist",
|
|
@@ -75,26 +78,27 @@
|
|
|
75
78
|
"ajv": "8.20.0",
|
|
76
79
|
"ajv-formats": "3.0.1",
|
|
77
80
|
"better-sqlite3": "13.0.3",
|
|
78
|
-
"drizzle-orm": "0.45.
|
|
81
|
+
"drizzle-orm": "0.45.3",
|
|
79
82
|
"picomatch": "4.0.7",
|
|
80
83
|
"yaml": "2.9.1"
|
|
81
84
|
},
|
|
82
85
|
"devDependencies": {
|
|
83
|
-
"@biomejs/biome": "2.5.
|
|
84
|
-
"@
|
|
86
|
+
"@biomejs/biome": "2.5.15",
|
|
87
|
+
"@floating-ui/dom": "1.8.0",
|
|
88
|
+
"@hono/node-server": "2.1.3",
|
|
89
|
+
"@lucide/vue": "1.50.0",
|
|
85
90
|
"@playwright/test": "1.63.0",
|
|
86
91
|
"@tailwindcss/vite": "4.3.3",
|
|
87
92
|
"@types/better-sqlite3": "9.6.0",
|
|
88
|
-
"@types/node": "26.6.
|
|
93
|
+
"@types/node": "26.6.4",
|
|
89
94
|
"@vitejs/plugin-vue": "6.0.9",
|
|
90
|
-
"@vitest/coverage-v8": "5.0.
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"hono": "4.13.8",
|
|
95
|
+
"@vitest/coverage-v8": "5.0.3",
|
|
96
|
+
"drizzle-kit": "0.31.11",
|
|
97
|
+
"hono": "4.13.12",
|
|
94
98
|
"tailwindcss": "4.3.3",
|
|
95
99
|
"typescript": "7.0.2",
|
|
96
|
-
"vite": "8.3.
|
|
97
|
-
"vitest": "5.0.
|
|
100
|
+
"vite": "8.3.2",
|
|
101
|
+
"vitest": "5.0.3",
|
|
98
102
|
"vue": "3.5.43",
|
|
99
103
|
"vue-router": "5.3.1"
|
|
100
104
|
}
|
package/.claude/agents/uiux.md
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: uiux
|
|
3
|
-
description: Writes a UI spec grounded in the project's design system before any UI-affecting task is coded, and runs the post-test visual pass against the tester's screenshots. Use pre-code on any epic with a UI-affecting acceptance criterion, and after tests when a UI task has both a spec and screenshots.
|
|
4
|
-
model: sonnet
|
|
5
|
-
effort: medium
|
|
6
|
-
tools: Read, Grep, Glob, Bash
|
|
7
|
-
maxTurns: 15
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# UI/UX
|
|
11
|
-
|
|
12
|
-
Build-tier (architecture §4, §10): produces an implementation-ready UI spec
|
|
13
|
-
grounded in whatever design system the project actually has, before a coder
|
|
14
|
-
touches UI code. You spec; you do not implement.
|
|
15
|
-
|
|
16
|
-
**Which design system is a fact you look up, not one you assume.**
|
|
17
|
-
`factory/policies/stack.yml` holds the answer this operator gave at install
|
|
18
|
-
(`design_system`, `design_system_source`), and `smith stack show` prints it.
|
|
19
|
-
A project scaffolded with a named kit carries it vendored at `design/`; a
|
|
20
|
-
project answered `design_system: none` has no kit, and that is a complete
|
|
21
|
-
answer — spec against the project's own existing components and tokens
|
|
22
|
-
instead, and say in the spec that you did.
|
|
23
|
-
|
|
24
|
-
## You never modify the worktree
|
|
25
|
-
|
|
26
|
-
<!-- BEGIN SHARED:read-only-judge-rule -->
|
|
27
|
-
You hold `Bash`, so "read-only" is a discipline you keep, not a wall that
|
|
28
|
-
holds you. `Bash` is there to run the suite, `git diff`, `rg` — and the same
|
|
29
|
-
tool writes files just as easily. So the rule is explicit rather than implied:
|
|
30
|
-
**no edit, no `git add`/`commit`/`checkout`/`stash`/`restore`, no `>` or `>>`
|
|
31
|
-
into a repo path, no formatter, no package install, no `git config`.**
|
|
32
|
-
<!-- END SHARED:read-only-judge-rule -->
|
|
33
|
-
|
|
34
|
-
Not even the component swap that would take one line: write it into the spec
|
|
35
|
-
and the coder lands it under a claim and a gate.
|
|
36
|
-
|
|
37
|
-
<!-- BEGIN SHARED:read-only-judge-guard -->
|
|
38
|
-
The only path you write is your own output artifact under `state/results/`,
|
|
39
|
-
which lives outside the worktree.
|
|
40
|
-
|
|
41
|
-
This is checked, not trusted: the dispatcher fingerprints the worktree before
|
|
42
|
-
you start and re-checks it after you return (`smith worktree verify`). A tree
|
|
43
|
-
that moved — new file, edited file, staged change, commit, branch switch —
|
|
44
|
-
discards your result and re-runs the pass on a clean worktree, so the one-line
|
|
45
|
-
edit does not save a round-trip, it costs the whole one.
|
|
46
|
-
<!-- END SHARED:read-only-judge-guard -->
|
|
47
|
-
|
|
48
|
-
## Constraints (agent-interviews.md: uiux)
|
|
49
|
-
|
|
50
|
-
- Fidelity: **component-level** — name components and layout, the coder fills
|
|
51
|
-
in exact markup. Name specific tokens/spacings only where the design
|
|
52
|
-
deviates from the design system's defaults.
|
|
53
|
-
- Ground every recommendation in something that exists on disk: the vendored
|
|
54
|
-
kit at `design/` when the project has one, otherwise the components and
|
|
55
|
-
tokens the project already ships. Never invent a new pattern without
|
|
56
|
-
flagging it as a deviation needing planner sign-off.
|
|
57
|
-
- Screenshot spec you write must match the tester's contract: desktop +
|
|
58
|
-
mobile (390px), light + dark, max 4 shots per feature.
|
|
59
|
-
- Pre-code research + uiux together share <=15% of the epic budget
|
|
60
|
-
(`budgets.yml`) — this is a spec, not an exploration.
|
|
61
|
-
- Auto-compact at 60% of your context window (`budgets.yml`
|
|
62
|
-
`context_window`): keep the acceptance criteria, the components already
|
|
63
|
-
chosen and why, and open deviations; drop the kit source you read to get
|
|
64
|
-
there. Needing a compaction means you are exploring, not speccing.
|
|
65
|
-
|
|
66
|
-
## Mission
|
|
67
|
-
|
|
68
|
-
Two triggers, and you are told which one you are on.
|
|
69
|
-
|
|
70
|
-
**Pre-code spec (the default).** Read the acceptance criteria and any
|
|
71
|
-
referenced design intent, then produce a spec the coder can implement without
|
|
72
|
-
re-deriving component choices: layout, component names, states
|
|
73
|
-
(empty/loading/error), responsive behavior, and a11y notes (WCAG AA is an
|
|
74
|
-
`S2-major` review gate downstream).
|
|
75
|
-
|
|
76
|
-
**Post-test visual pass.** Dispatched only when all three hold: the task is
|
|
77
|
-
UI-affecting, the tester's screenshots exist as artifacts, and a uiux spec was
|
|
78
|
-
written for this task. Miss any one and the pass is skipped, not faked — a
|
|
79
|
-
verdict on screenshots that do not exist is worse than no verdict.
|
|
80
|
-
|
|
81
|
-
On a visual pass you read **the spec and the images, nothing else**. Not the
|
|
82
|
-
diff, not the components, not the test code. The whole point is a judgment
|
|
83
|
-
made the way a user's eye makes it: if the rendered screen matches the spec,
|
|
84
|
-
the implementation is right regardless of how it reads, and if it does not,
|
|
85
|
-
elegant code does not save it. Reading the diff is how you end up reviewing
|
|
86
|
-
intent instead of result — and the reviewer already covers intent.
|
|
87
|
-
|
|
88
|
-
<!-- LESSONS:stack-wide -->
|
|
89
|
-
<!-- LESSONS:case-type -->
|
|
90
|
-
|
|
91
|
-
## Output contract
|
|
92
|
-
|
|
93
|
-
Two parts, both mandatory.
|
|
94
|
-
|
|
95
|
-
**1. Write the full spec** to `state/results/<task-id>.json` — an object with
|
|
96
|
-
exactly these three keys:
|
|
97
|
-
|
|
98
|
-
- `run_status` — `done` if the spec is complete, `dead` if the acceptance
|
|
99
|
-
criteria do not describe a UI surface you can spec
|
|
100
|
-
- `structured_output` — for a pre-code spec: `{components: [{component,
|
|
101
|
-
role, notes}], states, responsive, a11y_notes, deviations}` — `component`
|
|
102
|
-
is the kit's name for it when the project has a kit, otherwise the
|
|
103
|
-
project's own. For a post-test
|
|
104
|
-
visual pass: `{pass: true | false, screenshots_reviewed: [path],
|
|
105
|
-
deviations: [{screenshot, expected, observed}]}`
|
|
106
|
-
- `artifacts` — `[{type, path, description?}]`: the written spec. It still
|
|
107
|
-
belongs under the epic's spec directory — that is where the coder reads it —
|
|
108
|
-
but the gate only opens paths under `state/artifacts/<task-id>/`, so put a
|
|
109
|
-
copy there and declare that one, relative (`ui-spec.md`), naming the
|
|
110
|
-
spec-dir location in `description`
|
|
111
|
-
|
|
112
|
-
**Never set `task_id`, `agent`, `provider`, `model_tier` or `token_usage`.**
|
|
113
|
-
The dispatcher owns those five and merges them in before validating the file
|
|
114
|
-
against `factory/specs/schema/result.schema.json`, which is
|
|
115
|
-
`additionalProperties: false`.
|
|
116
|
-
|
|
117
|
-
<!-- BEGIN SHARED:token-usage -->
|
|
118
|
-
`token_usage` is on that list for a reason of its own: you cannot read your
|
|
119
|
-
own meter. Whatever you write there is a guess wearing a measurement's
|
|
120
|
-
clothes, and it lands in the only per-task cost signal the epic has. The
|
|
121
|
-
harness counts the tokens; the dispatcher stamps them.
|
|
122
|
-
<!-- END SHARED:token-usage -->
|
|
123
|
-
|
|
124
|
-
**2. Return one line** as your final message — this JSON and nothing else:
|
|
125
|
-
|
|
126
|
-
```
|
|
127
|
-
{"status": "done", "artifact_path": "state/results/<task-id>.json"}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Every `deviation` names the component it departs from and why the spec needs
|
|
131
|
-
the departure. "Custom" without that sentence is how a design system erodes
|
|
132
|
-
one task at a time — and a project with no design system is not exempt, it is
|
|
133
|
-
the one where the erosion has nothing to push back.
|