@juzser/blacksmith 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/.claude/agents/{auditor.md → bs-auditor.md} +8 -5
  2. package/.claude/agents/{coder.md → bs-coder.md} +8 -7
  3. package/.claude/agents/{grader.md → bs-grader.md} +16 -9
  4. package/.claude/agents/{merger.md → bs-merger.md} +4 -4
  5. package/.claude/agents/{planner.md → bs-planner.md} +20 -6
  6. package/.claude/agents/{researcher.md → bs-researcher.md} +6 -5
  7. package/.claude/agents/{reviewer.md → bs-reviewer.md} +16 -5
  8. package/.claude/agents/{scribe.md → bs-scribe.md} +11 -2
  9. package/.claude/agents/{security-reviewer.md → bs-security-reviewer.md} +9 -6
  10. package/.claude/agents/{spec-reviewer.md → bs-spec-reviewer.md} +18 -10
  11. package/.claude/agents/{tester.md → bs-tester.md} +40 -12
  12. package/.claude/agents/bs-uiux.md +257 -0
  13. package/.claude/agents/{verifier.md → bs-verifier.md} +7 -4
  14. package/.claude/agents/{wave-runner.md → bs-wave-runner.md} +14 -5
  15. package/.claude/skills/bs/SKILL.md +99 -21
  16. package/.claude/skills/bs/audit.md +45 -31
  17. package/.claude/skills/bs/dispatch.md +133 -47
  18. package/.claude/skills/bs/lessons.md +28 -14
  19. package/.claude/skills/bs/mcp.md +4 -4
  20. package/.claude/skills/bs/new.md +3 -3
  21. package/.claude/skills/bs/plan.md +57 -16
  22. package/.claude/skills/bs/report.md +11 -11
  23. package/.claude/skills/bs/run.md +190 -52
  24. package/.claude/skills/bs/status.md +14 -7
  25. package/.claude/skills/bs/ui.md +21 -5
  26. package/.claude/skills/bs/waivers.md +78 -2
  27. package/.claude/skills/bs/wave.md +143 -50
  28. package/README.md +53 -32
  29. package/factory/orchestrator/dist/agentNames.js +43 -0
  30. package/factory/orchestrator/dist/agents-registry.js +11 -4
  31. package/factory/orchestrator/dist/agentsSync.js +70 -13
  32. package/factory/orchestrator/dist/artifacts.js +31 -0
  33. package/factory/orchestrator/dist/attribution.js +4 -0
  34. package/factory/orchestrator/dist/audit.js +245 -20
  35. package/factory/orchestrator/dist/autonomy.js +6 -2
  36. package/factory/orchestrator/dist/budgetAlarm.js +132 -37
  37. package/factory/orchestrator/dist/budgets.js +302 -84
  38. package/factory/orchestrator/dist/claims.js +9 -19
  39. package/factory/orchestrator/dist/cli.js +1087 -116
  40. package/factory/orchestrator/dist/cliName.js +27 -0
  41. package/factory/orchestrator/dist/crossFinding.js +20 -0
  42. package/factory/orchestrator/dist/crosscheck.js +136 -8
  43. package/factory/orchestrator/dist/daemon.js +352 -67
  44. package/factory/orchestrator/dist/db/projector.js +327 -54
  45. package/factory/orchestrator/dist/db/queries.js +1831 -94
  46. package/factory/orchestrator/dist/db/schema.js +57 -4
  47. package/factory/orchestrator/dist/declaredArtifactLine.js +18 -0
  48. package/factory/orchestrator/dist/delegation.js +86 -19
  49. package/factory/orchestrator/dist/diffstat.js +15 -8
  50. package/factory/orchestrator/dist/dispatchLint.js +108 -0
  51. package/factory/orchestrator/dist/effort.js +14 -2
  52. package/factory/orchestrator/dist/effortTiers.js +13 -0
  53. package/factory/orchestrator/dist/env.js +26 -0
  54. package/factory/orchestrator/dist/epic.js +494 -42
  55. package/factory/orchestrator/dist/epicBudget.js +93 -0
  56. package/factory/orchestrator/dist/epicWidth.js +22 -5
  57. package/factory/orchestrator/dist/errorIssues.js +182 -11
  58. package/factory/orchestrator/dist/escalation.js +2 -2
  59. package/factory/orchestrator/dist/events.js +164 -14
  60. package/factory/orchestrator/dist/feedback.js +169 -0
  61. package/factory/orchestrator/dist/findingContext.js +26 -0
  62. package/factory/orchestrator/dist/findings.js +108 -14
  63. package/factory/orchestrator/dist/gate.js +120 -10
  64. package/factory/orchestrator/dist/gh.js +19 -2
  65. package/factory/orchestrator/dist/git.js +20 -2
  66. package/factory/orchestrator/dist/goalCheck.js +2 -2
  67. package/factory/orchestrator/dist/harness.js +25 -17
  68. package/factory/orchestrator/dist/hookDecision.js +411 -39
  69. package/factory/orchestrator/dist/integration.js +20 -8
  70. package/factory/orchestrator/dist/issueReporter.js +81 -16
  71. package/factory/orchestrator/dist/judgePreflight.js +25 -4
  72. package/factory/orchestrator/dist/judgeRoles.js +47 -0
  73. package/factory/orchestrator/dist/judgeStopHook.js +239 -0
  74. package/factory/orchestrator/dist/judges.js +419 -21
  75. package/factory/orchestrator/dist/lessons.js +190 -11
  76. package/factory/orchestrator/dist/logCache.js +291 -0
  77. package/factory/orchestrator/dist/mcp.js +6 -6
  78. package/factory/orchestrator/dist/paths.js +20 -7
  79. package/factory/orchestrator/dist/plan.js +399 -33
  80. package/factory/orchestrator/dist/planQuorum.js +5 -2
  81. package/factory/orchestrator/dist/policy.js +1275 -30
  82. package/factory/orchestrator/dist/projectName.js +25 -0
  83. package/factory/orchestrator/dist/projects.js +12 -1
  84. package/factory/orchestrator/dist/promptCapture.js +306 -0
  85. package/factory/orchestrator/dist/promptHook.js +33 -0
  86. package/factory/orchestrator/dist/promptImport.js +281 -0
  87. package/factory/orchestrator/dist/prompts.js +13 -0
  88. package/factory/orchestrator/dist/providers/schema-validate.js +2 -2
  89. package/factory/orchestrator/dist/providers/types.js +1 -1
  90. package/factory/orchestrator/dist/queue.js +471 -30
  91. package/factory/orchestrator/dist/quorum.js +18 -5
  92. package/factory/orchestrator/dist/results.js +25 -7
  93. package/factory/orchestrator/dist/roadmap.js +10 -5
  94. package/factory/orchestrator/dist/run-cli.js +15 -10
  95. package/factory/orchestrator/dist/runner.js +2 -2
  96. package/factory/orchestrator/dist/scheduler.js +3 -3
  97. package/factory/orchestrator/dist/schemas.js +56 -8
  98. package/factory/orchestrator/dist/severity.js +8 -4
  99. package/factory/orchestrator/dist/spec.js +246 -12
  100. package/factory/orchestrator/dist/specChange.js +13 -9
  101. package/factory/orchestrator/dist/stack.js +6 -6
  102. package/factory/orchestrator/dist/storeRoot.js +90 -0
  103. package/factory/orchestrator/dist/symbols.js +261 -6
  104. package/factory/orchestrator/dist/taskEvents.js +192 -31
  105. package/factory/orchestrator/dist/taskStatus.js +14 -0
  106. package/factory/orchestrator/dist/taxonomy.js +11 -0
  107. package/factory/orchestrator/dist/testerAudit.js +31 -12
  108. package/factory/orchestrator/dist/testgate.js +20 -16
  109. package/factory/orchestrator/dist/uiuxGate.js +83 -0
  110. package/factory/orchestrator/dist/usage.js +113 -48
  111. package/factory/orchestrator/dist/waivers.js +99 -12
  112. package/factory/orchestrator/dist/waveBudget.js +2 -0
  113. package/factory/orchestrator/dist/waveConcurrency.js +143 -7
  114. package/factory/orchestrator/dist/waveSchedule.js +84 -0
  115. package/factory/orchestrator/dist/workroot.js +1 -1
  116. package/factory/orchestrator/dist/worktree.js +76 -21
  117. package/factory/orchestrator/drizzle/0013_operator_feedback.sql +19 -0
  118. package/factory/orchestrator/drizzle/0014_misty_madripoor.sql +2 -0
  119. package/factory/orchestrator/drizzle/0015_gifted_omega_flight.sql +1 -0
  120. package/factory/orchestrator/drizzle/0016_milestones_project_default.sql +30 -0
  121. package/factory/orchestrator/drizzle/0017_events_raw_cli_session_id.sql +2 -0
  122. package/factory/orchestrator/drizzle/meta/_journal.json +35 -0
  123. package/factory/policies/budgets.yml +123 -33
  124. package/factory/policies/crosscheck.yml +51 -13
  125. package/factory/policies/delegation.yml +3 -3
  126. package/factory/policies/effort.yml +18 -10
  127. package/factory/policies/guardrails.yml +8 -6
  128. package/factory/policies/harness.yml +5 -5
  129. package/factory/policies/lessons.md +53 -2
  130. package/factory/policies/scheduler.yml +4 -4
  131. package/factory/policies/sensitive-paths.yml +1 -1
  132. package/factory/policies/severity.yml +13 -1
  133. package/factory/policies/stack.yml +6 -6
  134. package/factory/policies/taxonomy.yml +9 -3
  135. package/factory/policies/worktree.yml +15 -9
  136. package/factory/specs/roadmap.md +27 -3
  137. package/factory/specs/schema/event.schema.json +8 -1
  138. package/factory/specs/schema/result.schema.json +21 -8
  139. package/factory/specs/schema/task-spec.schema.json +12 -0
  140. package/package.json +16 -12
  141. package/.claude/agents/uiux.md +0 -133
@@ -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
- `black-smith` — this clone), `kind` (optional — `factory` | `dogfood` |
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 `black-smith` and `product`
34
- for everything else, which is why a project registered by `smith new` needs
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
@@ -175,3 +175,27 @@ all; write `- error_issues: off` by hand to opt a project's tracker out.
175
175
  - status: planned
176
176
  - epics: []
177
177
  - goal: Stop spending judge calls on outputs that a free check could have rejected, and stop grading a capped output as though it were a finished one. Four pieces, independent enough to land separately. **Free critics ahead of paid judges**: an empty diff, a missing structured completion, and claims touched without a single test added are all decidable without a model, and each one currently reaches a judge that pays full price to say so. The OpenHands SDK runs exactly this tier first and classifies what it rejects as behaviour, infrastructure or unclear-request — a taxonomy worth having because "the agent failed" and "the environment failed" are different bugs with different owners, and today they land in the same place. **Grade a budget-capped output as worth less**: SWE-agent's reviewer discards out-of-range verdicts and tie-breaks on cost; here the sharper version is that an output produced by an agent that hit its turn cap is a partial output, and a judge that does not know that is being asked to grade a fragment as a whole — which is how a capped run earns a passing verdict for the third of the task it finished. **Submit-gating and an honest forfeit**: show the worker its own diff before it may report done, with a checklist that names the mistakes workers actually make — test files touched, debug output left, claims exceeded — and give it a structured way to give up. A forfeit is strictly more useful than a confident wrong answer: it costs one round instead of a judge round plus a verifier round plus a rework round, and it is information the lessons pass can learn from. **Budget as an admission check**: `min_budget_for_dispatch` per role, so a role is never dispatched into a budget that cannot finish it, and a ring-fenced judging reserve so a capped epic still gets graded rather than spending its last tokens on more coding. Two things stay open. **Whether a free critic may block or only warn** — a false rejection that costs a whole round is worse than the judge call it saved, so the first critics should probably warn while their precision is measured against `agent-bench`. **What a forfeit does to the escalation ladder** — `budgets.yml` already declares one, and a forfeit is either its first rung or a separate path, but it must not become a cheap exit an agent prefers to the work. Researched 2026-09-18 across SWE-agent and the OpenHands SDK.
178
+
179
+ ## Self-audit 1 — event-log append cost, rename-blind claim diffs, one git entry point
180
+ - id: bs-audit-1
181
+ - status: planned
182
+ - epics: [bs-audit-1]
183
+ - project: blacksmith
184
+ - kind: factory
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
+
187
+ ## Audit 2: bounded judges, parallel feedback, dashboard origin guard
188
+ - id: bs-audit-2
189
+ - status: planned
190
+ - epics: [bs-audit-2]
191
+ - project: blacksmith
192
+ - kind: factory
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
+
195
+ ## Self-audit 3: UI read boundary and fail-closed daemon state
196
+ - id: bs-audit-3
197
+ - status: planned
198
+ - epics: [bs-audit-3]
199
+ - project: blacksmith
200
+ - kind: factory
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
- "type": "object",
50
- "required": ["input_tokens", "output_tokens", "total_tokens"],
51
- "properties": {
52
- "input_tokens": { "type": "integer", "minimum": 0 },
53
- "output_tokens": { "type": "integer", "minimum": 0 },
54
- "total_tokens": { "type": "integer", "minimum": 0 }
55
- },
56
- "additionalProperties": false
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.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.2",
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.14",
84
- "@hono/node-server": "2.1.1",
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.1",
93
+ "@types/node": "26.6.4",
89
94
  "@vitejs/plugin-vue": "6.0.9",
90
- "@vitest/coverage-v8": "5.0.1",
91
- "@vue-flow/core": "1.48.2",
92
- "drizzle-kit": "0.31.10",
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.0",
97
- "vitest": "5.0.1",
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
  }
@@ -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.