@juzser/blacksmith 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/.claude/agents/auditor.md +146 -0
  2. package/.claude/agents/coder.md +164 -0
  3. package/.claude/agents/grader.md +110 -0
  4. package/.claude/agents/merger.md +107 -0
  5. package/.claude/agents/planner.md +92 -0
  6. package/.claude/agents/researcher.md +105 -0
  7. package/.claude/agents/reviewer.md +171 -0
  8. package/.claude/agents/scribe.md +72 -0
  9. package/.claude/agents/security-reviewer.md +130 -0
  10. package/.claude/agents/spec-reviewer.md +215 -0
  11. package/.claude/agents/tester.md +121 -0
  12. package/.claude/agents/uiux.md +126 -0
  13. package/.claude/agents/verifier.md +103 -0
  14. package/.claude/agents/wave-runner.md +74 -0
  15. package/LICENSE +21 -0
  16. package/README.md +389 -0
  17. package/factory/orchestrator/dist/actors.js +46 -0
  18. package/factory/orchestrator/dist/agents-registry.js +308 -0
  19. package/factory/orchestrator/dist/args.js +84 -0
  20. package/factory/orchestrator/dist/artifacts.js +111 -0
  21. package/factory/orchestrator/dist/attribution.js +160 -0
  22. package/factory/orchestrator/dist/audit.js +812 -0
  23. package/factory/orchestrator/dist/autonomy.js +151 -0
  24. package/factory/orchestrator/dist/budgetAlarm.js +366 -0
  25. package/factory/orchestrator/dist/budgets.js +196 -0
  26. package/factory/orchestrator/dist/claims.js +633 -0
  27. package/factory/orchestrator/dist/cli.js +3185 -0
  28. package/factory/orchestrator/dist/commit.js +152 -0
  29. package/factory/orchestrator/dist/coverage.js +188 -0
  30. package/factory/orchestrator/dist/crossFinding.js +479 -0
  31. package/factory/orchestrator/dist/crosscheck.js +396 -0
  32. package/factory/orchestrator/dist/daemon.js +707 -0
  33. package/factory/orchestrator/dist/db/projector.js +1317 -0
  34. package/factory/orchestrator/dist/db/queries.js +1714 -0
  35. package/factory/orchestrator/dist/db/schema.js +305 -0
  36. package/factory/orchestrator/dist/delegation.js +400 -0
  37. package/factory/orchestrator/dist/diffstat.js +164 -0
  38. package/factory/orchestrator/dist/dispatchAudit.js +275 -0
  39. package/factory/orchestrator/dist/dotenv.js +72 -0
  40. package/factory/orchestrator/dist/effort.js +202 -0
  41. package/factory/orchestrator/dist/epic.js +1018 -0
  42. package/factory/orchestrator/dist/epicWidth.js +238 -0
  43. package/factory/orchestrator/dist/errorIssues.js +249 -0
  44. package/factory/orchestrator/dist/errors.js +11 -0
  45. package/factory/orchestrator/dist/escalation.js +500 -0
  46. package/factory/orchestrator/dist/events.js +1015 -0
  47. package/factory/orchestrator/dist/findingAge.js +91 -0
  48. package/factory/orchestrator/dist/findingContext.js +103 -0
  49. package/factory/orchestrator/dist/findings.js +1156 -0
  50. package/factory/orchestrator/dist/gate.js +911 -0
  51. package/factory/orchestrator/dist/gh.js +146 -0
  52. package/factory/orchestrator/dist/git.js +87 -0
  53. package/factory/orchestrator/dist/goalCheck.js +380 -0
  54. package/factory/orchestrator/dist/graph.js +61 -0
  55. package/factory/orchestrator/dist/hookDecision.js +106 -0
  56. package/factory/orchestrator/dist/immutability.js +171 -0
  57. package/factory/orchestrator/dist/impact.js +280 -0
  58. package/factory/orchestrator/dist/integration.js +141 -0
  59. package/factory/orchestrator/dist/judgePreflight.js +155 -0
  60. package/factory/orchestrator/dist/judges.js +310 -0
  61. package/factory/orchestrator/dist/lessonAudit.js +399 -0
  62. package/factory/orchestrator/dist/lessons.js +1329 -0
  63. package/factory/orchestrator/dist/mcp.js +555 -0
  64. package/factory/orchestrator/dist/paths.js +82 -0
  65. package/factory/orchestrator/dist/plan.js +502 -0
  66. package/factory/orchestrator/dist/planQuorum.js +421 -0
  67. package/factory/orchestrator/dist/policy.js +1308 -0
  68. package/factory/orchestrator/dist/policyHook.js +28 -0
  69. package/factory/orchestrator/dist/preconditions.js +47 -0
  70. package/factory/orchestrator/dist/projects.js +134 -0
  71. package/factory/orchestrator/dist/prompts.js +57 -0
  72. package/factory/orchestrator/dist/provenance.js +201 -0
  73. package/factory/orchestrator/dist/providers/api-transport.js +117 -0
  74. package/factory/orchestrator/dist/providers/cli-transport.js +208 -0
  75. package/factory/orchestrator/dist/providers/index.js +54 -0
  76. package/factory/orchestrator/dist/providers/schema-validate.js +152 -0
  77. package/factory/orchestrator/dist/providers/types.js +49 -0
  78. package/factory/orchestrator/dist/queue.js +263 -0
  79. package/factory/orchestrator/dist/quorum.js +347 -0
  80. package/factory/orchestrator/dist/quorumEscalations.js +132 -0
  81. package/factory/orchestrator/dist/results.js +65 -0
  82. package/factory/orchestrator/dist/roadmap.js +437 -0
  83. package/factory/orchestrator/dist/runtime.js +86 -0
  84. package/factory/orchestrator/dist/sameMistakeKpi.js +243 -0
  85. package/factory/orchestrator/dist/sandbox.js +165 -0
  86. package/factory/orchestrator/dist/scaffold.js +373 -0
  87. package/factory/orchestrator/dist/scheduler.js +408 -0
  88. package/factory/orchestrator/dist/schemas.js +195 -0
  89. package/factory/orchestrator/dist/security.js +222 -0
  90. package/factory/orchestrator/dist/severity.js +293 -0
  91. package/factory/orchestrator/dist/spec.js +376 -0
  92. package/factory/orchestrator/dist/specChange.js +497 -0
  93. package/factory/orchestrator/dist/stack.js +324 -0
  94. package/factory/orchestrator/dist/symbols.js +966 -0
  95. package/factory/orchestrator/dist/taskEvents.js +399 -0
  96. package/factory/orchestrator/dist/taskId.js +126 -0
  97. package/factory/orchestrator/dist/taxonomy.js +113 -0
  98. package/factory/orchestrator/dist/testSelect.js +129 -0
  99. package/factory/orchestrator/dist/testerAudit.js +264 -0
  100. package/factory/orchestrator/dist/testgate.js +171 -0
  101. package/factory/orchestrator/dist/usage.js +874 -0
  102. package/factory/orchestrator/dist/waivers.js +228 -0
  103. package/factory/orchestrator/dist/waveBudget.js +223 -0
  104. package/factory/orchestrator/dist/waveConcurrency.js +250 -0
  105. package/factory/orchestrator/dist/waveNext.js +210 -0
  106. package/factory/orchestrator/dist/waveSchedule.js +146 -0
  107. package/factory/orchestrator/dist/worktree.js +207 -0
  108. package/factory/orchestrator/drizzle/0000_broken_rockslide.sql +183 -0
  109. package/factory/orchestrator/drizzle/0001_luxuriant_zaladane.sql +1 -0
  110. package/factory/orchestrator/drizzle/0002_yellow_wild_pack.sql +8 -0
  111. package/factory/orchestrator/drizzle/0003_damp_riptide.sql +12 -0
  112. package/factory/orchestrator/drizzle/0004_broad_lady_vermin.sql +1 -0
  113. package/factory/orchestrator/drizzle/0005_romantic_jocasta.sql +2 -0
  114. package/factory/orchestrator/drizzle/0006_bent_madelyne_pryor.sql +16 -0
  115. package/factory/orchestrator/drizzle/0007_overjoyed_goliath.sql +1 -0
  116. package/factory/orchestrator/drizzle/0008_smart_iron_patriot.sql +2 -0
  117. package/factory/orchestrator/drizzle/0009_agents_epic_id.sql +2 -0
  118. package/factory/orchestrator/drizzle/0010_milestone_kind.sql +1 -0
  119. package/factory/orchestrator/drizzle/meta/_journal.json +83 -0
  120. package/factory/policies/budgets.yml +262 -0
  121. package/factory/policies/crosscheck.yml +308 -0
  122. package/factory/policies/delegation.yml +44 -0
  123. package/factory/policies/effort.yml +142 -0
  124. package/factory/policies/guardrails.yml +306 -0
  125. package/factory/policies/lessons.md +239 -0
  126. package/factory/policies/scheduler.yml +90 -0
  127. package/factory/policies/sensitive-paths.yml +148 -0
  128. package/factory/policies/severity.yml +83 -0
  129. package/factory/policies/stack.yml +60 -0
  130. package/factory/policies/taxonomy.yml +340 -0
  131. package/factory/policies/worktree.yml +95 -0
  132. package/factory/scaffold/base/.github/workflows/ci.yml +34 -0
  133. package/factory/scaffold/base/.gitignore.tmpl +11 -0
  134. package/factory/scaffold/base/AGENTS.md +33 -0
  135. package/factory/scaffold/base/README.md +25 -0
  136. package/factory/scaffold/base/biome.json.tmpl +30 -0
  137. package/factory/scaffold/base/package.json +25 -0
  138. package/factory/scaffold/base/pnpm-workspace.yaml.tmpl +16 -0
  139. package/factory/scaffold/base/src/index.ts +4 -0
  140. package/factory/scaffold/base/test/index.test.ts +8 -0
  141. package/factory/scaffold/base/tsconfig.json +20 -0
  142. package/factory/scaffold/base/tsconfig.test.json +9 -0
  143. package/factory/scaffold/base/vitest.config.ts +29 -0
  144. package/factory/scaffold/mcp/mcp.manifest.json +22 -0
  145. package/factory/scaffold/mcp/package.fragment.json +9 -0
  146. package/factory/scaffold/mcp/src/mcp/README.md +63 -0
  147. package/factory/scaffold/mcp/src/mcp/guard.ts +110 -0
  148. package/factory/scaffold/mcp/src/mcp/redact.ts +69 -0
  149. package/factory/scaffold/mcp/src/mcp/server.ts +55 -0
  150. package/factory/scaffold/mcp/src/mcp/tools/health.ts +60 -0
  151. package/factory/scaffold/mcp/test/mcp/guard.test.ts +108 -0
  152. package/factory/scaffold/mcp/test/mcp/redact.test.ts +61 -0
  153. package/factory/scaffold/mcp/test/mcp/surface.test.ts +100 -0
  154. package/factory/scaffold/ui/index.html +12 -0
  155. package/factory/scaffold/ui/package.fragment.json +13 -0
  156. package/factory/scaffold/ui/src/App.vue +10 -0
  157. package/factory/scaffold/ui/src/main.ts +5 -0
  158. package/factory/scaffold/ui/src/shims.d.ts +11 -0
  159. package/factory/scaffold/ui/vite.config.ts +20 -0
  160. package/factory/scaffold/ui-tailwind/package.fragment.json +6 -0
  161. package/factory/scaffold/ui-tailwind/vite.config.ts +26 -0
  162. package/factory/specs/schema/event.schema.json +82 -0
  163. package/factory/specs/schema/finding-evidence.schema.json +45 -0
  164. package/factory/specs/schema/finding.schema.json +115 -0
  165. package/factory/specs/schema/grader-verdict.schema.json +50 -0
  166. package/factory/specs/schema/judge-verdict.schema.json +19 -0
  167. package/factory/specs/schema/lesson.schema.json +85 -0
  168. package/factory/specs/schema/mcp-manifest.schema.json +230 -0
  169. package/factory/specs/schema/result.schema.json +81 -0
  170. package/factory/specs/schema/spec-change-request.schema.json +46 -0
  171. package/factory/specs/schema/task-spec.schema.json +134 -0
  172. package/package.json +98 -0
@@ -0,0 +1,230 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://black-smith.dev/schema/mcp-manifest.schema.json",
4
+ "$comment": "Shape contract for a project's mcp.manifest.json. The verdict that gates epic close comes from checkManifest() in factory/orchestrator/src/mcp.ts, which reports stable rule ids (MCP-P1, MCP-T4, ...) from the numbered baseline in docs/standards/mcp.md; this schema is the shape half of the same contract and its descriptions name the rule each field serves. Rules that are not decidable from a declaration (MCP-P3 session independence, MCP-S1 output redaction, MCP-X5 state-handle binding, MCP-X6 SSRF blocks) are enforced in code, not here.",
5
+ "title": "MCP Manifest",
6
+ "description": "The single declaration of what a project's MCP surface exposes: its identity, its wire revision, its transport, and every tool with its mutation class, annotations, and operator sign-off. This file is the one place an operator reads to know what an agent can do with this project.",
7
+ "type": "object",
8
+ "required": ["name", "version", "protocolRevision", "transport", "tools"],
9
+ "properties": {
10
+ "$schema": {
11
+ "type": "string"
12
+ },
13
+ "name": {
14
+ "type": "string",
15
+ "minLength": 1,
16
+ "description": "MCP-P2. Server identity as reported in the initialize result; conventionally the project name."
17
+ },
18
+ "version": {
19
+ "type": "string",
20
+ "pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$",
21
+ "description": "MCP-P2. Semver. A client caches a tool surface against this, so bump it when the surface changes."
22
+ },
23
+ "protocolRevision": {
24
+ "type": "string",
25
+ "const": "2025-11-25",
26
+ "description": "MCP-P1. The pinned wire revision — what the Tier-1 TypeScript SDK actually negotiates, not the newest revision on paper. Moves to 2026-07-28 when the SDK ships it; see docs/standards/mcp.md 'Forward compatibility'."
27
+ },
28
+ "transport": {
29
+ "$ref": "#/$defs/transport"
30
+ },
31
+ "tools": {
32
+ "type": "array",
33
+ "description": "MCP-T5. Every tool the server registers, and nothing it does not. A server that can do more than this array admits makes the manifest a lie.",
34
+ "items": { "$ref": "#/$defs/tool" }
35
+ }
36
+ },
37
+ "$defs": {
38
+ "transport": {
39
+ "description": "MCP-X1. stdio is the default and opens no port; http is a deliberate opt-in that must carry its own preconditions.",
40
+ "oneOf": [
41
+ {
42
+ "type": "object",
43
+ "required": ["kind"],
44
+ "properties": {
45
+ "kind": { "const": "stdio" }
46
+ }
47
+ },
48
+ {
49
+ "type": "object",
50
+ "required": ["kind", "access", "resourceServer"],
51
+ "properties": {
52
+ "kind": { "const": "http" },
53
+ "access": {
54
+ "type": "object",
55
+ "required": ["cloudflareAccess"],
56
+ "properties": {
57
+ "cloudflareAccess": {
58
+ "const": true,
59
+ "description": "MCP-X2 (mcp.md). Cloudflare Access in front: nothing is exposed beyond localhost without something authenticating for it."
60
+ }
61
+ }
62
+ },
63
+ "resourceServer": {
64
+ "type": "object",
65
+ "description": "MCP-X3/X4. The server is an OAuth 2.0 protected resource, and says so precisely enough to be checkable.",
66
+ "required": [
67
+ "canonicalUri",
68
+ "authorizationServers",
69
+ "resourceIndicators",
70
+ "tokenPassthrough"
71
+ ],
72
+ "properties": {
73
+ "canonicalUri": {
74
+ "type": "string",
75
+ "pattern": "^https://",
76
+ "description": "MCP-X3. The canonical resource URI. This is the token audience — every accepted token must name it."
77
+ },
78
+ "authorizationServers": {
79
+ "type": "array",
80
+ "minItems": 1,
81
+ "items": { "type": "string", "pattern": "^https://" },
82
+ "description": "MCP-X3. Issuers this resource trusts. Validate the returned `iss` against these (RFC 9207) to close the mix-up attack."
83
+ },
84
+ "resourceIndicators": {
85
+ "const": true,
86
+ "description": "MCP-X3. RFC 8707 resource indicators are mandatory: they are what binds an issued token to this server rather than to whoever asks next."
87
+ },
88
+ "tokenPassthrough": {
89
+ "const": false,
90
+ "description": "MCP-X4. Never allowed. The server MUST NOT accept any token that was not explicitly issued for it."
91
+ },
92
+ "scopes": {
93
+ "type": "array",
94
+ "items": { "type": "string" },
95
+ "description": "Least privilege. Wildcards ('*', 'all', 'full-access') defeat the point of asking."
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+ ]
102
+ },
103
+ "tool": {
104
+ "type": "object",
105
+ "required": ["name", "title", "description", "mutation", "annotations"],
106
+ "properties": {
107
+ "name": {
108
+ "type": "string",
109
+ "pattern": "^[a-z][a-z0-9_]{2,63}$",
110
+ "description": "MCP-T5. snake_case, unique within the manifest. This is what an agent types."
111
+ },
112
+ "title": {
113
+ "type": "string",
114
+ "minLength": 1,
115
+ "description": "Human-facing label, shown in consent prompts."
116
+ },
117
+ "description": {
118
+ "type": "string",
119
+ "minLength": 1,
120
+ "description": "What the tool does, written for the agent deciding whether to call it."
121
+ },
122
+ "mutation": {
123
+ "enum": ["read-only", "write", "destructive", "outbound"],
124
+ "description": "MCP-T2. What this tool actually does to the world. read-only is the default and the only class that needs no sign-off. The implementation passes this same class to requireDeclaredTool(), so a tool that quietly starts writing fails to boot instead of shipping under its old declaration."
125
+ },
126
+ "annotations": {
127
+ "type": "object",
128
+ "description": "MCP-T1/T2/T3. The hints an agent reads to decide whether a call is safe. They must agree with `mutation` — a read-only tool with destructiveHint true is a contradiction, not a nuance.",
129
+ "required": [
130
+ "readOnlyHint",
131
+ "destructiveHint",
132
+ "idempotentHint",
133
+ "openWorldHint"
134
+ ],
135
+ "properties": {
136
+ "readOnlyHint": { "type": "boolean" },
137
+ "destructiveHint": { "type": "boolean" },
138
+ "idempotentHint": { "type": "boolean" },
139
+ "openWorldHint": {
140
+ "type": "boolean",
141
+ "description": "MCP-T3. True when the tool reaches something outside this project's own state. Every outbound tool sets it."
142
+ }
143
+ }
144
+ },
145
+ "approval": {
146
+ "type": "object",
147
+ "description": "MCP-T4. Operator sign-off, required for every tool that is not read-only. A person decided, at the mcp surface milestone, that an agent may do this unattended.",
148
+ "required": ["operator", "date", "milestone"],
149
+ "properties": {
150
+ "operator": { "type": "string", "minLength": 1 },
151
+ "date": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
152
+ "milestone": { "type": "string", "minLength": 1 }
153
+ }
154
+ }
155
+ },
156
+ "allOf": [
157
+ {
158
+ "$comment": "MCP-T1. read-only implies its own annotations.",
159
+ "if": {
160
+ "properties": { "mutation": { "const": "read-only" } },
161
+ "required": ["mutation"]
162
+ },
163
+ "then": {
164
+ "properties": {
165
+ "annotations": {
166
+ "properties": {
167
+ "readOnlyHint": { "const": true },
168
+ "destructiveHint": { "const": false },
169
+ "openWorldHint": { "const": false }
170
+ }
171
+ }
172
+ }
173
+ }
174
+ },
175
+ {
176
+ "$comment": "MCP-T4. Anything that is not read-only carries a sign-off.",
177
+ "if": {
178
+ "properties": { "mutation": { "not": { "const": "read-only" } } },
179
+ "required": ["mutation"]
180
+ },
181
+ "then": { "required": ["approval"] }
182
+ },
183
+ {
184
+ "$comment": "MCP-T2. A mutating tool must not advertise itself as safe.",
185
+ "if": {
186
+ "properties": {
187
+ "mutation": { "enum": ["write", "destructive", "outbound"] }
188
+ },
189
+ "required": ["mutation"]
190
+ },
191
+ "then": {
192
+ "properties": {
193
+ "annotations": {
194
+ "properties": { "readOnlyHint": { "const": false } }
195
+ }
196
+ }
197
+ }
198
+ },
199
+ {
200
+ "$comment": "MCP-T2. destructive means destructive.",
201
+ "if": {
202
+ "properties": { "mutation": { "const": "destructive" } },
203
+ "required": ["mutation"]
204
+ },
205
+ "then": {
206
+ "properties": {
207
+ "annotations": {
208
+ "properties": { "destructiveHint": { "const": true } }
209
+ }
210
+ }
211
+ }
212
+ },
213
+ {
214
+ "$comment": "MCP-T3. outbound reaches beyond this project.",
215
+ "if": {
216
+ "properties": { "mutation": { "const": "outbound" } },
217
+ "required": ["mutation"]
218
+ },
219
+ "then": {
220
+ "properties": {
221
+ "annotations": {
222
+ "properties": { "openWorldHint": { "const": true } }
223
+ }
224
+ }
225
+ }
226
+ }
227
+ ]
228
+ }
229
+ }
230
+ }
@@ -0,0 +1,81 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://black-smith.dev/schema/result.schema.json",
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
+ "title": "Task Result",
6
+ "description": "Structured return from a worker run. Workers return schema-validated results, never transcripts (architecture §15.4); the final message is data, not prose.",
7
+ "type": "object",
8
+ "required": [
9
+ "task_id",
10
+ "run_status",
11
+ "structured_output",
12
+ "artifacts",
13
+ "token_usage",
14
+ "agent",
15
+ "provider",
16
+ "model_tier"
17
+ ],
18
+ "properties": {
19
+ "task_id": { "type": "string" },
20
+ "run_status": {
21
+ "type": "string",
22
+ "x-taxonomy": "run_status"
23
+ },
24
+ "structured_output": {
25
+ "type": "object",
26
+ "description": "Task-specific payload; shape is defined by the task spec's output_schema_ref, not by this envelope."
27
+ },
28
+ "artifacts": {
29
+ "type": "array",
30
+ "items": {
31
+ "type": "object",
32
+ "required": ["type", "path"],
33
+ "properties": {
34
+ "type": {
35
+ "type": "string",
36
+ "description": "e.g. screenshot, diff, log, coverage-report."
37
+ },
38
+ "path": {
39
+ "type": "string",
40
+ "minLength": 1,
41
+ "description": "Under the task's artifact home, state/artifacts/<task_id>/ — relative to it by preference. JSON Schema cannot express \"inside this directory\", so the gate is the enforcer: it resolves each path in the home and blocks with reason 'artifacts-missing' if one is elsewhere or absent. The empty string is the one part the schema can catch, and it does (D-193): a worker that names no path gets a validation error at the envelope rather than a gate block one step later."
42
+ },
43
+ "description": { "type": "string" }
44
+ },
45
+ "additionalProperties": false
46
+ }
47
+ },
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
57
+ },
58
+ "agent": {
59
+ "type": "string",
60
+ "x-taxonomy": "agent"
61
+ },
62
+ "provider": {
63
+ "type": "string",
64
+ "x-taxonomy": "provider"
65
+ },
66
+ "model_tier": {
67
+ "type": "string",
68
+ "x-taxonomy": "model_tier"
69
+ },
70
+ "diff_lines_changed": {
71
+ "type": "integer",
72
+ "minimum": 0,
73
+ "description": "Excludes lockfiles/generated files, per the coder diff cap (agent-constraints.md)."
74
+ },
75
+ "notes": {
76
+ "type": "string",
77
+ "description": "Short structured note, e.g. reason for stopping at a budget cap."
78
+ }
79
+ },
80
+ "additionalProperties": false
81
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://black-smith.dev/schema/spec-change-request.schema.json",
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
+ "title": "Spec Change Request",
6
+ "description": "A worker's third exit, returned in structured_output alongside research_request and never emitted as an event — a worker that stops mid-flight cannot emit anything, so signalling through a returned field is the only shape that survives its own failure case (architecture §15). It says the criterion is wrong and what it should say instead. It is a proposal, not an amendment: nothing here moves a plan file, and only an operator's approval reaches `plan amend`.",
7
+ "type": "object",
8
+ "required": ["criterion_ref", "assumption", "evidence", "changes", "sites", "blocking"],
9
+ "properties": {
10
+ "criterion_ref": {
11
+ "type": "string",
12
+ "minLength": 1,
13
+ "description": "Which acceptance criterion the worker read and found wrong. \"The plan is wrong\" has to say which clause — the same rule mintFindings enforces on spec-review evidence."
14
+ },
15
+ "assumption": {
16
+ "type": "string",
17
+ "minLength": 1,
18
+ "description": "The assumption that criterion makes, stated as the worker understood it before it broke. This is what the operator is being asked to overturn, so it is recorded in the worker's words rather than paraphrased from the diff."
19
+ },
20
+ "evidence": {
21
+ "type": "string",
22
+ "minLength": 1,
23
+ "description": "What the worker found that contradicts the assumption — the file it read, the signature it hit, the test that cannot be written. A proposal without it is an opinion, and the operator is the one who would have to go and check."
24
+ },
25
+ "changes": {
26
+ "type": "object",
27
+ "description": "The proposed diff, in PlanChanges shape: {added?, supersede?, newEdges?}. Validated at proposal time by drafting the next version and running the plan's own validator over it, so a proposal that could never be applied is refused before it reaches an operator's queue rather than after."
28
+ },
29
+ "sites": {
30
+ "type": "array",
31
+ "minItems": 1,
32
+ "items": { "type": "string", "minLength": 1 },
33
+ "description": "Every place the wrong assumption's shape occurs, not only where the worker hit it (D-123). The worker is better placed to enumerate these than the operator, which is the whole reason this field is asked for here rather than typed at approval."
34
+ },
35
+ "blocking": {
36
+ "type": "boolean",
37
+ "description": "Whether the worker can make any further progress without the amendment. Mirrors research_request.blocking. A blocking proposal left unreviewed is a stalled task, which is why the daemon reports it and a non-blocking one only waits."
38
+ },
39
+ "severity": {
40
+ "type": "string",
41
+ "x-taxonomy": "severity",
42
+ "description": "Optional. How badly the wrong criterion breaks the epic, if the worker has a view. Defaults at the dispatcher, never here: a schema default would make an absent field indistinguishable from a chosen one."
43
+ }
44
+ },
45
+ "additionalProperties": false
46
+ }
@@ -0,0 +1,134 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://black-smith.dev/schema/task-spec.schema.json",
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
+ "title": "Task Spec",
6
+ "description": "A dispatchable task contract: the unit the planner emits and a worker executes. Specs are contracts (architecture §1, §3) — no dispatch without objective, output schema, acceptance criteria, claims, and budget.",
7
+ "type": "object",
8
+ "required": [
9
+ "task_id",
10
+ "epic_id",
11
+ "plan_version",
12
+ "objective",
13
+ "output_schema_ref",
14
+ "acceptance_criteria",
15
+ "claims",
16
+ "budget",
17
+ "contract",
18
+ "case",
19
+ "origin",
20
+ "task_status"
21
+ ],
22
+ "properties": {
23
+ "task_id": {
24
+ "type": "string",
25
+ "description": "Stable identifier, e.g. epic-7/task-142."
26
+ },
27
+ "epic_id": {
28
+ "type": "string"
29
+ },
30
+ "plan_version": {
31
+ "type": "integer",
32
+ "minimum": 1,
33
+ "description": "Immutable plan version this task was cut under (architecture §12); never mutated in place."
34
+ },
35
+ "objective": {
36
+ "type": "string",
37
+ "minLength": 1,
38
+ "description": "One-paragraph statement of what the task must accomplish."
39
+ },
40
+ "output_schema_ref": {
41
+ "type": "string",
42
+ "description": "Path or $id of the JSON Schema the task's result.structured_output must validate against."
43
+ },
44
+ "acceptance_criteria": {
45
+ "type": "array",
46
+ "items": { "type": "string" },
47
+ "minItems": 1
48
+ },
49
+ "claims": {
50
+ "type": "array",
51
+ "items": { "type": "string" },
52
+ "minItems": 1,
53
+ "description": "File globs this task may touch, computed from static analysis over the target repo (architecture §5). Out-of-claim edits fail the gate."
54
+ },
55
+ "budget": {
56
+ "type": "object",
57
+ "description": "Declared caps for this task. Only fields something can mechanically read belong here — plan validation rejects the rest (src/budgets.ts TASK_BUDGET_FIELD_READERS).",
58
+ "required": ["tokens", "diff_lines"],
59
+ "properties": {
60
+ "tokens": {
61
+ "type": "integer",
62
+ "minimum": 1,
63
+ "description": "Compared at the gate against the result's token_usage.total_tokens."
64
+ },
65
+ "diff_lines": {
66
+ "type": "integer",
67
+ "minimum": 1,
68
+ "description": "Compared at the gate against the measured git diff, excluding lockfiles and generated files."
69
+ },
70
+ "max_turns": {
71
+ "type": "integer",
72
+ "minimum": 1,
73
+ "description": "Still schema-valid so plans written before 2026-08-08 load, but plan validation now rejects it: no dispatch harness can enforce a turn limit (agent-interviews.md M-4). The number belongs in the dispatch prompt, where it is a request."
74
+ }
75
+ },
76
+ "additionalProperties": false
77
+ },
78
+ "contract": {
79
+ "type": "object",
80
+ "description": "Explicit clauses the worker must satisfy. Nonfunctional/security clauses are always spelled out here — implicit clauses get dropped (error.spec.missing-nonfunctional).",
81
+ "required": ["functional_clauses", "nonfunctional_clauses"],
82
+ "properties": {
83
+ "functional_clauses": {
84
+ "type": "array",
85
+ "items": { "type": "string" },
86
+ "minItems": 1
87
+ },
88
+ "nonfunctional_clauses": {
89
+ "type": "array",
90
+ "items": { "type": "string" },
91
+ "description": "Explicit security/validation/authz/performance/a11y clauses, even when they seem implied by the objective."
92
+ },
93
+ "tool_allowlist": {
94
+ "type": "array",
95
+ "items": { "type": "string" }
96
+ }
97
+ },
98
+ "additionalProperties": false
99
+ },
100
+ "case": {
101
+ "type": "string",
102
+ "x-taxonomy": "case"
103
+ },
104
+ "origin": {
105
+ "type": "string",
106
+ "x-taxonomy": "origin"
107
+ },
108
+ "task_status": {
109
+ "type": "string",
110
+ "x-taxonomy": "task_status"
111
+ },
112
+ "agent_role": {
113
+ "type": "string",
114
+ "x-taxonomy": "agent",
115
+ "description": "Role of the agent this task is scoped to be dispatched to."
116
+ },
117
+ "confidence": {
118
+ "type": "number",
119
+ "minimum": 0,
120
+ "maximum": 1,
121
+ "description": "Set only for origin: inferred tasks (architecture §12); >=0.8 auto-schedules."
122
+ },
123
+ "dependency_edge_ids": {
124
+ "type": "array",
125
+ "items": { "type": "string" },
126
+ "description": "IDs of edge-recorded events this task depends on (architecture §7)."
127
+ },
128
+ "project": {
129
+ "type": "string",
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
+ }
132
+ },
133
+ "additionalProperties": false
134
+ }
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@juzser/blacksmith",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Autonomous agent factory — loop runner and worktree engine.",
6
+ "keywords": [
7
+ "agent",
8
+ "agents",
9
+ "ai",
10
+ "autonomous",
11
+ "claude-code",
12
+ "code-review",
13
+ "git-worktree",
14
+ "llm",
15
+ "multi-agent",
16
+ "orchestration"
17
+ ],
18
+ "homepage": "https://github.com/juzser/blacksmith#readme",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/juzser/blacksmith.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/juzser/blacksmith/issues"
25
+ },
26
+ "author": "juzser (https://github.com/juzser)",
27
+ "license": "MIT",
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "engines": {
32
+ "node": ">=22"
33
+ },
34
+ "packageManager": "pnpm@9.3.0",
35
+ "bin": {
36
+ "smith": "factory/orchestrator/dist/cli.js"
37
+ },
38
+ "files": [
39
+ "factory/orchestrator/dist",
40
+ "!factory/orchestrator/dist/**/*.d.ts",
41
+ "factory/orchestrator/drizzle",
42
+ "!factory/orchestrator/drizzle/meta/*_snapshot.json",
43
+ "factory/policies",
44
+ "factory/specs/schema",
45
+ "factory/scaffold",
46
+ ".claude/agents"
47
+ ],
48
+ "scripts": {
49
+ "prepare": "pnpm run build",
50
+ "check": "bash scripts/check.sh",
51
+ "build": "tsc -p tsconfig.json",
52
+ "typecheck": "tsc --noEmit",
53
+ "typecheck:test": "tsc -p tsconfig.test.json",
54
+ "lint": "biome check .",
55
+ "format": "biome check --write .",
56
+ "test": "vitest run",
57
+ "test:watch": "vitest",
58
+ "test:coverage": "vitest run --coverage",
59
+ "build:server": "pnpm build && tsc -p ui/server/tsconfig.json",
60
+ "typecheck:server": "tsc -p ui/server/tsconfig.test.json",
61
+ "test:server": "pnpm build:server && vitest run --config ui/server/vitest.config.ts",
62
+ "test:server:coverage": "pnpm build:server && vitest run --config ui/server/vitest.config.ts --coverage",
63
+ "build:ui": "vite build --config ui/vite.config.ts",
64
+ "dev:ui": "vite --config ui/vite.config.ts",
65
+ "typecheck:ui": "tsc -p ui/tsconfig.json",
66
+ "typecheck:ui:test": "tsc -p ui/tsconfig.test.json",
67
+ "test:ui": "vitest run --config ui/vitest.config.ts",
68
+ "test:ui:coverage": "vitest run --config ui/vitest.config.ts --coverage",
69
+ "test:e2e": "pnpm build:server && pnpm build:ui && playwright test --config ui/playwright.config.ts"
70
+ },
71
+ "dependencies": {
72
+ "ajv": "8.20.0",
73
+ "ajv-formats": "3.0.1",
74
+ "better-sqlite3": "13.0.3",
75
+ "drizzle-orm": "0.45.2",
76
+ "picomatch": "4.0.7",
77
+ "yaml": "2.9.0"
78
+ },
79
+ "devDependencies": {
80
+ "@biomejs/biome": "2.5.12",
81
+ "@hono/node-server": "2.1.1",
82
+ "@playwright/test": "1.63.0",
83
+ "@tailwindcss/vite": "4.3.3",
84
+ "@types/better-sqlite3": "9.6.0",
85
+ "@types/node": "26.4.1",
86
+ "@vitejs/plugin-vue": "6.0.8",
87
+ "@vitest/coverage-v8": "5.0.0",
88
+ "@vue-flow/core": "1.48.2",
89
+ "drizzle-kit": "0.31.10",
90
+ "hono": "4.13.7",
91
+ "tailwindcss": "4.3.3",
92
+ "typescript": "7.0.2",
93
+ "vite": "8.2.2",
94
+ "vitest": "5.0.0",
95
+ "vue": "3.5.42",
96
+ "vue-router": "5.3.1"
97
+ }
98
+ }