shapeup-sdlc 1.6.3 → 3.0.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 (118) hide show
  1. package/.claude/settings.local.example.json +5 -5
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/AGENTS.md +54 -107
  4. package/README.md +165 -151
  5. package/SECURITY.md +49 -27
  6. package/bin/init.mjs +93 -108
  7. package/bin/lib/grant.mjs +145 -0
  8. package/commands/build.md +20 -0
  9. package/commands/eval.md +5 -4
  10. package/commands/scopes.md +5 -4
  11. package/commands/shape.md +1 -1
  12. package/commands/ship.md +53 -7
  13. package/commands/wire.md +1 -1
  14. package/hooks/dispatch-receipt.mjs +195 -0
  15. package/hooks/gate-intake.mjs +16 -17
  16. package/hooks/gate-zerowork.mjs +107 -25
  17. package/hooks/hooks.json +9 -48
  18. package/hooks/lib/decision.mjs +38 -19
  19. package/hooks/safety-spine.mjs +4 -4
  20. package/hooks/sandbox-guard.mjs +130 -50
  21. package/{skills/tech-lead/scripts/compile-order.mjs → kernel/compile.mjs} +344 -55
  22. package/{skills/tech-lead/scripts/gate-answers.mjs → kernel/gate.mjs} +69 -26
  23. package/kernel/harness.mjs +134 -0
  24. package/{skills/tech-lead/scripts/fit-check.mjs → kernel/init/fit.mjs} +25 -22
  25. package/kernel/init/run.mjs +489 -0
  26. package/{skills/tech-lead/scripts → kernel}/lib/argv.mjs +52 -31
  27. package/{skills/tech-lead/scripts/lib/contract-md.mjs → kernel/lib/contract.mjs} +261 -17
  28. package/kernel/lib/paths.mjs +491 -0
  29. package/kernel/probe/concurrency.mjs +510 -0
  30. package/{skills/tech-lead/scripts/aegis-digest.mjs → kernel/probe/digest.mjs} +10 -11
  31. package/kernel/probe/eval.mjs +77 -0
  32. package/kernel/probe/leg.mjs +125 -0
  33. package/kernel/probe/resume.mjs +528 -0
  34. package/{skills/tech-lead/scripts → kernel/probe}/stats.mjs +102 -21
  35. package/kernel/probe/t0.mjs +66 -0
  36. package/{skills/ba-pitch-analyzer/scripts/board-derive.mjs → kernel/reduce/board.mjs} +104 -28
  37. package/kernel/reduce/graph.mjs +437 -0
  38. package/kernel/reduce/hill.mjs +152 -0
  39. package/kernel/reduce/ingest.mjs +633 -0
  40. package/{hooks/slop-cleaner.mjs → kernel/reduce/leftovers.mjs} +40 -57
  41. package/{skills/tech-lead/scripts/ship-report.mjs → kernel/reduce/ship.mjs} +93 -14
  42. package/{skills/tech-lead/scripts/run-snapshot.mjs → kernel/reduce/snapshot.mjs} +22 -21
  43. package/{skills/spec-evaluator/scripts/verdict-ledger.mjs → kernel/reduce/verdict.mjs} +14 -8
  44. package/kernel/report/export.mjs +325 -0
  45. package/kernel/report/facts.mjs +347 -0
  46. package/{skills/tech-lead/scripts/budget-check.mjs → kernel/verify/budget.mjs} +22 -25
  47. package/kernel/verify/dispatch.mjs +114 -0
  48. package/{skills/tech-lead/scripts/validate-envelope.mjs → kernel/verify/envelope.mjs} +20 -15
  49. package/{skills/tech-lead/scripts/lib → kernel/verify}/ratchet-tree.mjs +36 -14
  50. package/kernel/verify/skills.mjs +125 -0
  51. package/kernel/verify/spec.mjs +559 -0
  52. package/{skills/tech-lead/scripts/t0-verify.mjs → kernel/verify/t0.mjs} +138 -28
  53. package/{skills/tech-lead/scripts/trace-lint.mjs → kernel/verify/trace.mjs} +28 -13
  54. package/oracles/_shared.mjs +1 -1
  55. package/oracles/http-oracle.mjs +2 -2
  56. package/oracles/index.mjs +1 -1
  57. package/oracles/process-oracle.mjs +2 -2
  58. package/oracles/snapshot-oracle.mjs +2 -2
  59. package/oracles/test-oracle.mjs +2 -2
  60. package/package.json +11 -13
  61. package/skills/ba-pitch-analyzer/SKILL.md +24 -19
  62. package/skills/ba-pitch-analyzer/assets/templates/_index.tmpl.md +1 -1
  63. package/skills/ba-pitch-analyzer/assets/templates/api-feasibility.tmpl.md +12 -7
  64. package/skills/ba-pitch-analyzer/assets/templates/contracts/third-party-api.contract.tmpl.md +6 -3
  65. package/skills/ba-pitch-analyzer/assets/templates/cross-context/migration-plan.tmpl.md +5 -4
  66. package/skills/ba-pitch-analyzer/assets/templates/cross-context/team-handoff.tmpl.md +23 -13
  67. package/skills/ba-pitch-analyzer/assets/templates/scope-summary.tmpl.md +17 -12
  68. package/skills/ba-pitch-analyzer/assets/templates/synthesis.tmpl.md +17 -38
  69. package/skills/ba-pitch-analyzer/assets/templates/task.tmpl.md +3 -0
  70. package/skills/ba-pitch-analyzer/references/contract-patterns.md +8 -4
  71. package/skills/ba-pitch-analyzer/references/doc-schemas.md +3 -1
  72. package/skills/ba-pitch-analyzer/references/task-generation.md +8 -8
  73. package/skills/ba-pitch-analyzer/references/test-surface.md +1 -1
  74. package/skills/coach/SKILL.md +3 -3
  75. package/skills/orient/SKILL.md +2 -1
  76. package/skills/qa-edge-hunter/SKILL.md +15 -41
  77. package/skills/scope-architect/SKILL.md +57 -18
  78. package/skills/scope-hammer/SKILL.md +5 -5
  79. package/skills/shapeup/SKILL.md +3 -3
  80. package/skills/shapeup/resources/context-compaction.md +4 -3
  81. package/skills/solution-architect/SKILL.md +37 -15
  82. package/skills/spec-evaluator/SKILL.md +24 -7
  83. package/skills/spec-evaluator/references/dimensions/_registry.md +2 -2
  84. package/skills/spec-evaluator/references/dimensions/completeness.md +1 -1
  85. package/skills/spec-evaluator/references/dimensions/integration.md +98 -67
  86. package/skills/spec-evaluator/references/dimensions/spec-conformance.md +4 -4
  87. package/skills/spec-evaluator/references/dimensions/test-surface-conformance.md +1 -1
  88. package/skills/spec-evaluator/references/probing.md +42 -23
  89. package/skills/spec-evaluator/references/report-schema.md +2 -2
  90. package/skills/task-executor/SKILL.md +32 -18
  91. package/skills/tech-lead/SKILL.md +127 -438
  92. package/skills/tech-lead/references/gates.md +140 -49
  93. package/skills/tech-lead/references/protocol.md +832 -0
  94. package/skills/tech-lead/schemas/domain.schema.json +645 -198
  95. package/skills/tech-lead/schemas/gate-answers.schema.json +2 -2
  96. package/skills/tech-lead/schemas/work-order.schema.json +11 -2
  97. package/skills/tech-lead/schemas/work-result.schema.json +56 -18
  98. package/skills/tech-lead/workflows/shapeup-run.js +1474 -0
  99. package/skills/translator/SKILL.md +2 -2
  100. package/hooks/anti-rationalization.mjs +0 -244
  101. package/hooks/compact-snapshot.mjs +0 -47
  102. package/hooks/gate-deadline.mjs +0 -151
  103. package/hooks/gate-l2.mjs +0 -161
  104. package/hooks/session-rehydrate.mjs +0 -109
  105. package/skills/advisor-protocol/SKILL.md +0 -171
  106. package/skills/ba-pitch-analyzer/scripts/spec-lint.mjs +0 -239
  107. package/skills/spec-evaluator/README.md +0 -93
  108. package/skills/tech-lead/README.md +0 -71
  109. package/skills/tech-lead/references/delegation.md +0 -254
  110. package/skills/tech-lead/references/invocation.md +0 -45
  111. package/skills/tech-lead/references/ledger-schema.md +0 -214
  112. package/skills/tech-lead/references/round-protocol.md +0 -184
  113. package/skills/tech-lead/references/state-model.md +0 -66
  114. package/skills/tech-lead/scripts/ingest-result.mjs +0 -270
  115. package/skills/tech-lead/scripts/init-run.mjs +0 -326
  116. package/skills/tech-lead/scripts/lib/is-main.mjs +0 -82
  117. package/skills/tech-lead/scripts/lib/paths.mjs +0 -278
  118. package/skills/translator/README.md +0 -66
@@ -3,14 +3,57 @@
3
3
  "title": "HarnessDomain",
4
4
  "description": "The CENTRAL domain registry of the pure-skill architecture. Every shared field that crosses a skill boundary is defined here exactly once, with its meaning, owner, and storage tier annotated — work-order.schema.json and work-result.schema.json $ref into $defs below instead of re-defining records inline, and no skill may introduce a cross-boundary field that is not registered here first. Annotation keys: x-tier = SHARED (committed under shapeup/, survives clone+crash) | LOCAL (gitignored under .shapeup/, regenerable run-trace) | EMBEDDED (a record type that only lives inside another entity); x-location = the canonical path; x-writer = the single component allowed to write it; x-readers = who consumes it. The x-erd block is the machine-readable entity-relationship map; x-payload-by-worker is the authoritative table of which WorkOrder payload fields each worker may rely on.",
5
5
  "x-erd": {
6
- "description": "Relationship map. Cardinality reads left-to-right (WorkOrder 1:1 WorkResult = every order has exactly one result). Join keys: <slug> (the feature) is the aggregate root every path is keyed off; scope_id is the STABLE cross-machine key (boards regenerate; hill shards, T0 history and branches join on it); TASK-NNN ids are machine-local and must never be joined across machines (generate-board joins on scope, not task id).",
6
+ "description": "Relationship map. Cardinality reads left-to-right (WorkOrder 1:1 WorkResult = every order has exactly one result). Join keys: <slug> (the feature) is the aggregate root every path is keyed off; scope_id is the STABLE cross-machine key (boards regenerate; hill shards, T0 history and branches join on it); TASK-NNN ids are machine-local and must never be joined across machines (board regeneration joins on scope, not task id).",
7
+ "external": {
8
+ "WorkOrder": "work-order.schema.json",
9
+ "WorkResult": "work-result.schema.json"
10
+ },
11
+ "projection": {
12
+ "description": "How `kernel/reduce/graph.mjs`'s node vocabulary maps onto this catalog. The projection uses short names on purpose — a graph is read by hand — so the mapping is declared rather than inferred. A `type` of null is a KNOWN GAP: the projection emits that node and this schema has never typed it. The structural suite asserts this map is TOTAL against the node types the projection actually emits, so a new node type cannot appear without either a type or an explicit gap.",
13
+ "nodes": {
14
+ "Run": {
15
+ "type": null,
16
+ "gap": "the run receipt's own record — emitted by reduce graph, never typed here"
17
+ },
18
+ "Order": {
19
+ "type": "WorkOrder",
20
+ "external": true
21
+ },
22
+ "Result": {
23
+ "type": "WorkResult",
24
+ "external": true
25
+ },
26
+ "Verdict": {
27
+ "type": "Verdict"
28
+ },
29
+ "Trial": {
30
+ "type": "TrialRow"
31
+ },
32
+ "GateDecision": {
33
+ "type": null,
34
+ "gap": "a gate decision row — emitted by reduce graph, never typed here"
35
+ },
36
+ "Scope": {
37
+ "type": "ScopeContract"
38
+ },
39
+ "UseCase": {
40
+ "type": "UseCase"
41
+ },
42
+ "Requirement": {
43
+ "type": "RequirementClause"
44
+ },
45
+ "Seam": {
46
+ "type": "Seam"
47
+ }
48
+ }
49
+ },
7
50
  "relationships": [
8
51
  {
9
52
  "from": "WorkOrder",
10
53
  "to": "WorkResult",
11
54
  "cardinality": "1:1",
12
55
  "via": "order_id",
13
- "note": "the envelope port — compile-order.mjs emits, worker answers, ingest-result.mjs applies"
56
+ "note": "the envelope port — harness compile emits, worker answers, harness reduce ingest applies"
14
57
  },
15
58
  {
16
59
  "from": "WorkOrder",
@@ -28,28 +71,35 @@
28
71
  },
29
72
  {
30
73
  "from": "TaskRef",
31
- "to": "UseCase (SHARED spec doc)",
74
+ "to": "UseCase",
32
75
  "cardinality": "N:1..N",
33
76
  "via": "use_case_refs[]",
34
77
  "note": "the LOCAL→SHARED traceability anchor (single-anchor rule on reconcile) — spec-evaluator's traceability source. Tier direction: persisted links flow LOCAL→SHARED only; a committed spec doc never references a machine-local task id back (spec-lint TIER-DIRECTION/UC-ANCHOR)"
35
78
  },
36
79
  {
37
- "from": "AcResult (LOCAL board AC)",
38
- "to": "RequirementClause (SHARED registry)",
80
+ "from": "AcResult",
81
+ "to": "RequirementClause",
39
82
  "cardinality": "N:0..N",
40
83
  "via": "acceptance_criteria[].covers[]",
41
84
  "note": "the covers-closure anchor — same sanctioned LOCAL→SHARED direction as use_case_refs. trace-lint asserts every status:covered REQ-id is named by ≥1 AC's covers[]; a REQ neither covered nor CUT is red. REQ-ids are stable cross-machine keys (supersede-never-delete), never renumbered like TASK-NNN"
42
85
  },
43
86
  {
44
- "from": "CriterionVerdict / Discovery",
45
- "to": "RequirementClause (SHARED registry)",
87
+ "from": "CriterionVerdict",
88
+ "to": "RequirementClause",
46
89
  "cardinality": "N:0..N",
47
90
  "via": "traces_to[]",
48
- "note": "optional finding→REQ back-link (LOCAL→SHARED) so an edge case maps to a business requirement by id, not by grepping the ledger; the lint only checks the id resolves in the registry — purely a navigation anchor"
91
+ "note": "optional finding→REQ back-link (LOCAL→SHARED) so an edge case maps to a business requirement by id, not by grepping the ledger; the lint only checks the id resolves in the registry — purely a navigation anchor split from a compound ERD node; CriterionVerdict carries its own traces_to[]"
92
+ },
93
+ {
94
+ "from": "Discovery",
95
+ "to": "RequirementClause",
96
+ "cardinality": "N:0..N",
97
+ "via": "traces_to[]",
98
+ "note": "optional finding→REQ back-link (LOCAL→SHARED) so an edge case maps to a business requirement by id, not by grepping the ledger; the lint only checks the id resolves in the registry — purely a navigation anchor split from a compound ERD node; Discovery carries its own traces_to[]"
49
99
  },
50
100
  {
51
101
  "from": "WiringMap",
52
- "to": "UseCase (SHARED spec doc)",
102
+ "to": "UseCase",
53
103
  "cardinality": "1:N",
54
104
  "via": "entries[].use_case",
55
105
  "note": "reachability map: per UC engine → wiring seam → entry-point call site → player-visible affordance. trace-lint asserts each UC's engine reaches ProjectProfile.entry_point via the import graph; an unreachable engine (0 import sites) is red"
@@ -89,7 +139,7 @@
89
139
  },
90
140
  {
91
141
  "from": "TaskResult",
92
- "to": "Task (LOCAL board file)",
142
+ "to": "TaskRef",
93
143
  "cardinality": "N:1",
94
144
  "via": "task_id",
95
145
  "note": "ingest ticks AC boxes, flips frontmatter status, appends the Execution Log"
@@ -101,20 +151,6 @@
101
151
  "via": "discoveries[]",
102
152
  "note": "ingest appends to discovery/ledger.md — three producers (executor +, QA ~, planner overflow), zero direct writers"
103
153
  },
104
- {
105
- "from": "WorkResult",
106
- "to": "Escalate",
107
- "cardinality": "1:N",
108
- "via": "escalates[]",
109
- "note": "ingest queues to escalates/<order>.json; advisor-protocol adjudicates into a Decision"
110
- },
111
- {
112
- "from": "Escalate",
113
- "to": "Decision",
114
- "cardinality": "1:0..1",
115
- "via": "advisor-protocol adjudication",
116
- "note": "persisted to the committed round-ledger the instant it is answered (zero-memory handoff)"
117
- },
118
154
  {
119
155
  "from": "WorkResult",
120
156
  "to": "Verdict",
@@ -150,11 +186,33 @@
150
186
  "via": "path + sha256",
151
187
  "note": "content-addressed evidence the generator cannot fabricate"
152
188
  },
189
+ {
190
+ "from": "ScopeContract",
191
+ "to": "UseCase",
192
+ "cardinality": "1:N",
193
+ "via": "use_cases[]",
194
+ "note": "the scope's anchor into the committed spec — the SCOPE-ANCHOR mirror of a task's use_case_refs"
195
+ },
153
196
  {
154
197
  "from": "ScopeContract",
155
198
  "to": "TaskRef",
156
199
  "cardinality": "1:N",
157
- "via": "tasks[] (scope_id is the stable key)"
200
+ "via": "DERIVED — the board tasks whose use_case_refs meet this scope's use_cases (lib/contract.mjs tasksForScope)",
201
+ "note": "never stored: task ids are machine-local and the contract is committed, so the relation is re-derived per machine from two artifacts that are each valid alone. scope_id is the stable key on this side"
202
+ },
203
+ {
204
+ "from": "ScopeContract",
205
+ "to": "RequirementClause",
206
+ "cardinality": "1:N",
207
+ "via": "covers[]",
208
+ "note": "REQ-ids, the same stable key an AC's covers: clause uses"
209
+ },
210
+ {
211
+ "from": "ScopeContract",
212
+ "to": "ScopeContract",
213
+ "cardinality": "N:N",
214
+ "via": "depends_on[]",
215
+ "note": "build order, in the same tier as the contracts it orders — what scope_waves/scope_deps level"
158
216
  },
159
217
  {
160
218
  "from": "ScopeContract",
@@ -181,7 +239,7 @@
181
239
  "to": "ScopeContract",
182
240
  "cardinality": "1:N",
183
241
  "via": "superseded_by[]",
184
- "note": "set by split-scope — supersede-never-delete keeps branch and T0 history attributable"
242
+ "note": "set by map-scopes — supersede-never-delete keeps branch and T0 history attributable"
185
243
  },
186
244
  {
187
245
  "from": "T0Artifact",
@@ -231,6 +289,13 @@
231
289
  "cardinality": "1:N",
232
290
  "via": "scopes[].scope_id",
233
291
  "note": "every FINISHED scope's fixtures re-run on each later attempt"
292
+ },
293
+ {
294
+ "from": "UseCase",
295
+ "to": "Seam",
296
+ "cardinality": "1:N",
297
+ "via": "wiring-map entries[].wiring_seam",
298
+ "note": "projected by reduce graph as UseCase -DEPENDS_ON-> Seam. NOTE: the projection reads `row.seam`/`row.entry_point` while WiringEntry declares `wiring_seam`/`entry_call_site` — see harness-defects"
234
299
  }
235
300
  ]
236
301
  },
@@ -254,15 +319,12 @@
254
319
  "spec_folder",
255
320
  "feature",
256
321
  "discovered_ledger",
257
- "kb_rules_path",
258
- "requirements"
322
+ "kb_rules_path"
259
323
  ],
260
324
  "scope-architect": [
261
325
  "feature",
262
326
  "spec_folder",
263
- "tasks",
264
- "discovered_ledger",
265
- "scope_id"
327
+ "tasks"
266
328
  ],
267
329
  "solution-architect": [
268
330
  "feature",
@@ -304,12 +366,6 @@
304
366
  ],
305
367
  "coach": [
306
368
  "feedback"
307
- ],
308
- "advisor-protocol": [
309
- "ledger",
310
- "escalate",
311
- "scope_id",
312
- "round"
313
369
  ]
314
370
  },
315
371
  "x-result-by-worker": {
@@ -317,7 +373,6 @@
317
373
  "task-executor": [
318
374
  "task_results",
319
375
  "files_touched",
320
- "escalates",
321
376
  "discoveries",
322
377
  "artifacts",
323
378
  "assumptions",
@@ -332,21 +387,18 @@
332
387
  ],
333
388
  "ba-pitch-analyzer": [
334
389
  "discoveries",
335
- "escalates",
336
390
  "files_touched",
337
391
  "artifacts",
338
392
  "assumptions",
339
393
  "deviations"
340
394
  ],
341
395
  "scope-architect": [
342
- "escalates",
343
396
  "files_touched",
344
397
  "artifacts",
345
398
  "assumptions",
346
399
  "deviations"
347
400
  ],
348
401
  "solution-architect": [
349
- "escalates",
350
402
  "files_touched",
351
403
  "artifacts",
352
404
  "assumptions",
@@ -382,12 +434,6 @@
382
434
  "artifacts",
383
435
  "assumptions",
384
436
  "deviations"
385
- ],
386
- "advisor-protocol": [
387
- "files_touched",
388
- "artifacts",
389
- "assumptions",
390
- "deviations"
391
437
  ]
392
438
  },
393
439
  "$defs": {
@@ -404,40 +450,34 @@
404
450
  "qa-edge-hunter",
405
451
  "translator",
406
452
  "scope-hammer",
407
- "coach",
408
- "advisor-protocol"
453
+ "coach"
409
454
  ]
410
455
  },
411
456
  "Operation": {
412
- "description": "The full operation vocabulary. Replaces lifecycle flags (--tasks-only, --from-discovered, --remap …): the caller knows the pipeline position; the worker never re-derives it. Ownership: execute/fix/spike = task-executor · analyze/generate-board/reconcile/retrofit-surface/coverage = ba-pitch-analyzer · map-scopes/remap/split-scope = scope-architect · wire = solution-architect · evaluate = spec-evaluator · orient = orient · hunt/recheck = qa-edge-hunter · translate = translator · hammer = scope-hammer · coach = coach · adjudicate = advisor-protocol.",
457
+ "description": "The full operation vocabulary. Replaces lifecycle flags (--tasks-only, --from-discovered, --map-scopes …): the caller knows the pipeline position; the worker never re-derives it. Ownership: execute/fix/spike = task-executor · analyze/reconcile/retrofit-surface/coverage = ba-pitch-analyzer · map-scopes = scope-architect · wire = solution-architect · evaluate = spec-evaluator · orient = orient · hunt = qa-edge-hunter · translate = translator · hammer = scope-hammer · coach = coach.",
413
458
  "type": "string",
414
459
  "enum": [
415
460
  "execute",
416
461
  "fix",
417
462
  "spike",
418
463
  "analyze",
419
- "generate-board",
420
464
  "reconcile",
421
465
  "retrofit-surface",
422
466
  "coverage",
423
467
  "map-scopes",
424
- "remap",
425
- "split-scope",
426
468
  "wire",
427
469
  "evaluate",
428
470
  "orient",
429
471
  "hunt",
430
- "recheck",
431
472
  "translate",
432
473
  "hammer",
433
- "coach",
434
- "adjudicate"
474
+ "coach"
435
475
  ]
436
476
  },
437
477
  "Substrate": {
438
478
  "description": "The write contract of one order — mode/flag differences ARE write-contract differences (one whitelist template per operation, compile-order substrateFor()). Enforced mechanically by the sandbox hook; a worker needing a file outside it must ESCALATE (substrate-expansion), never write.",
439
479
  "x-tier": "EMBEDDED",
440
- "x-writer": "compile-order.mjs (templates) / scope-architect (per-scope globs)",
480
+ "x-writer": "harness compile (templates) / scope-architect (per-scope globs)",
441
481
  "type": "object",
442
482
  "properties": {
443
483
  "allowed": {
@@ -477,7 +517,7 @@
477
517
  "properties": {
478
518
  "pause_gates": {
479
519
  "type": "boolean",
480
- "description": "true (standalone default): pause at the worker's phase checkpoints. false: run straight through, surfacing questions as assumptions[]/escalates[]."
520
+ "description": "true (standalone default): pause at the worker's phase checkpoints. false: run straight through, surfacing questions as assumptions[] — or, when one genuinely blocks, deviations[] with status 'escalated'."
481
521
  },
482
522
  "max_questions": {
483
523
  "type": "integer",
@@ -488,7 +528,7 @@
488
528
  "TaskRef": {
489
529
  "description": "One task as carried inside a WorkOrder — parsed from the LOCAL board file (.shapeup/<slug>/tasks/TASK-*.md frontmatter + AC checkboxes) by compile-order parseTaskFile(). NOTE: task ids are machine-local (boards regenerate and renumber); never join on id across machines — the scope is the stable key.",
490
530
  "x-tier": "EMBEDDED",
491
- "x-writer": "compile-order.mjs (from the board ba-pitch-analyzer generated)",
531
+ "x-writer": "harness compile (from the board ba-pitch-analyzer generated)",
492
532
  "type": "object",
493
533
  "required": [
494
534
  "id"
@@ -549,7 +589,7 @@
549
589
  "items": {
550
590
  "type": "string"
551
591
  },
552
- "description": "Explicit dependencies. The inverse (unlocks) is ALWAYS derived by board-derive.mjs, never hand-authored (KB-BA-001)."
592
+ "description": "Explicit dependencies. The inverse (unlocks) is ALWAYS derived by harness reduce board, never hand-authored — hand-authored inverses drift asymmetric, observed repeatedly."
553
593
  },
554
594
  "use_case_refs": {
555
595
  "type": "array",
@@ -557,6 +597,10 @@
557
597
  "type": "string"
558
598
  },
559
599
  "description": "UC ids this task implements (e.g. UC-CreateOrder) — the task's LOCAL→SHARED anchor into the committed spec and spec-evaluator's traceability source. Links flow one direction: the LOCAL task cites the SHARED UC; a committed doc never links a task id back (ids are machine-local)."
600
+ },
601
+ "scope_id": {
602
+ "type": "string",
603
+ "description": "OPTIONAL: the scope that builds this task. The DISAMBIGUATOR, not the primary link — dispatch normally derives a scope's tasks by intersecting the contract's use_cases[] with this task's use_case_refs[], and that is a partition only while each scope owns its own use cases. When several scopes implement one use case (a four-scope, one-UC cut is ordinary) the UC anchor cannot say who builds what and every scope claims every task; spec-lint SCOPE-PARTITION reds that, and this field resolves it. Same sanctioned LOCAL→SHARED direction as use_case_refs: the volatile board names the stable scope_id, never the reverse."
560
604
  }
561
605
  }
562
606
  },
@@ -564,7 +608,7 @@
564
608
  "description": "One adjudicated ESCALATE answer as carried inside a WorkOrder — binding precedent the worker must apply. Compiled from the committed round-ledger.md Decisions table (columns: Round | Scope | Kind | Question | Answer | Resolved by) filtered to this scope. The full row lives in the ledger; the envelope carries the minimal {id, answer} projection.",
565
609
  "x-tier": "EMBEDDED (projection of the SHARED round-ledger.md Decisions row)",
566
610
  "x-location": ".shapeup/<slug>/round-ledger.md",
567
- "x-writer": "advisor-protocol (via tech-lead) — promoted the instant an answer is given",
611
+ "x-writer": "tech-lead — promoted the instant the PO gives an answer",
568
612
  "type": "object",
569
613
  "required": [
570
614
  "id",
@@ -582,17 +626,24 @@
582
626
  }
583
627
  },
584
628
  "AegisTriple": {
585
- "description": "One digested failure — aegis-digest.mjs distills raw build/test/Playwright logs into these so the next attempt gets a few lines of signal instead of a stack-trace dump. Appears as T0Artifact.discovered_tasks[] (red attempts) and as WorkOrder payload.digested_errors[] (the next attempt's starting bug list).",
629
+ "description": "One digested failure — harness probe digest distills raw build/test/Playwright logs into these so the next attempt gets a few lines of signal instead of a stack-trace dump. Appears as T0Artifact.discovered_tasks[] (red attempts) and as WorkOrder payload.digested_errors[] (the next attempt's starting bug list).",
586
630
  "x-tier": "EMBEDDED",
587
- "x-writer": "aegis-digest.mjs (regex pass; Sonnet fallback only on unrecognized formats)",
631
+ "x-writer": "harness probe digest (regex pass; Sonnet fallback only on unrecognized formats)",
588
632
  "type": "object",
589
633
  "properties": {
590
634
  "file": {
591
- "type": "string",
635
+ "type": [
636
+ "string",
637
+ "null"
638
+ ],
592
639
  "description": "Source file of the failure; null/absent when the log line carried no location (raw triple — never invented)."
593
640
  },
594
641
  "line": {
595
- "type": "integer"
642
+ "type": [
643
+ "integer",
644
+ "null"
645
+ ],
646
+ "description": "Line of the failure; null/absent when the log line carried no location. Paired with `file` — a triple has both or neither, and neither is ever invented to satisfy a shape."
596
647
  },
597
648
  "core_message": {
598
649
  "type": "string",
@@ -661,12 +712,40 @@
661
712
  }
662
713
  }
663
714
  },
715
+ "UseCase": {
716
+ "description": "One use case — the STABLE anchor both tiers name, and the only identifier the scope↔task relation is re-derived through. A UC id is assigned once and frozen: it behaves like scope_id and REQ-id, never like TASK-NNN. Referenced three ways across the tree (`UC-x`, `[[UC-x]]`, `[[usecases/UC-x]]`); `kernel/lib/contract.mjs` ucId() is the single normaliser, and both sides of any join must pass through it or they disagree about a UC they both hold.",
717
+ "x-tier": "SHARED",
718
+ "x-location": "shapeup/<slug>/spec/usecases/UC-*.md",
719
+ "x-writer": "ba-pitch-analyzer (operation: analyze; reconcile MAY append, never renumber)",
720
+ "x-readers": "scope-architect (use_cases[]), solution-architect (wiring-map entries), spec-evaluator (the grading criterion), harness verify spec (SCOPE-ANCHOR + UC-ANCHOR), harness reduce graph",
721
+ "type": "object",
722
+ "required": [
723
+ "id"
724
+ ],
725
+ "properties": {
726
+ "id": {
727
+ "type": "string",
728
+ "description": "`UC-*`, matching the filename stem. Frozen once assigned — every use_cases[] and use_case_refs[] link rots if it is renumbered."
729
+ },
730
+ "title": {
731
+ "type": "string",
732
+ "description": "What the user can do."
733
+ },
734
+ "invariants": {
735
+ "type": "array",
736
+ "items": {
737
+ "type": "string"
738
+ },
739
+ "description": "`[INV-NN]` markers declared in the body; the source of regression tasks and Test Surface rows."
740
+ }
741
+ }
742
+ },
664
743
  "ScopeContract": {
665
- "description": "A vertical scope as a committed, mechanically enforceable contract — Shape Up's 'map the scopes' as data. Sliced by business flow (one call chain: UI screen + API route + use case + repository), NEVER by directory (PA1). The sandbox hook enforces the substrate, t0-verify runs the fixtures, the evaluator asserts against the manifest. Superseded contracts are kept, never deleted (branch and T0 history stay attributable). ON-DISK FORMAT IS MARKDOWN (ADR-0001): frontmatter carries the scalars and [a, b] lists, a markdown table carries the one array-of-objects field, and prose carries the rationale a reviewer reads. THIS SCHEMA DESCRIBES THE PARSED OBJECT — the wire form embedded in the WorkOrder envelope — not the file. `skills/tech-lead/scripts/lib/contract-md.mjs` is the only reader/writer of the file form; everything downstream of that parse sees exactly this shape.",
744
+ "description": "A vertical scope as a committed, mechanically enforceable contract — Shape Up's 'map the scopes' as data. Sliced by business flow (one call chain: UI screen + API route + use case + repository), NEVER by directory (PA1). The sandbox hook enforces the substrate, t0-verify runs the fixtures, the evaluator asserts against the manifest. Superseded contracts are kept, never deleted (branch and T0 history stay attributable). ON-DISK FORMAT IS MARKDOWN (ADR-0001): frontmatter carries the scalars and [a, b] lists, a markdown table carries the one array-of-objects field, and prose carries the rationale a reviewer reads. THIS SCHEMA DESCRIBES THE PARSED OBJECT — the wire form embedded in the WorkOrder envelope — not the file. `kernel/lib/contract.mjs` is the only reader/writer of the file form; everything downstream of that parse sees exactly this shape.",
666
745
  "x-tier": "SHARED",
667
746
  "x-location": "shapeup/<slug>/scopes/<scope-id>.md",
668
747
  "x-writer": "scope-architect (SOLE writer — a hand-edit anywhere else is a defect)",
669
- "x-readers": "compile-order.mjs, sandbox-guard hook, t0-verify.mjs, spec-lint.mjs, spec-evaluator, scope-hammer, tech-lead",
748
+ "x-readers": "harness compile, sandbox-guard hook, harness verify t0, harness verify spec, spec-evaluator, scope-hammer, tech-lead",
670
749
  "type": "object",
671
750
  "required": [
672
751
  "scope_id"
@@ -685,12 +764,26 @@
685
764
  ],
686
765
  "description": "LAYER_CAKE = thin balanced UI+backend · ICEBERG = complexity on one side · CHOWDER = true strays with no shared flow (the one deliberate exception to cross-layer slicing)."
687
766
  },
688
- "tasks": {
767
+ "use_cases": {
768
+ "type": "array",
769
+ "items": {
770
+ "type": "string"
771
+ },
772
+ "description": "The COMMITTED use cases this scope implements (`UC-*`, resolving to spec/usecases/UC-*.md) — the scope's anchor into the spec and the join the scope↔task relation is re-derived through. Mirrors the board's own `use_case_refs` in the sanctioned LOCAL→SHARED direction. This REPLACES the former `tasks[]` id list: a contract is committed and a board is not, so naming task ids put a pointer into a gitignored, per-machine tier that resolved to nothing on every other clone without a single check going red. spec-lint SCOPE-ANCHOR reds an empty or unresolvable anchor."
773
+ },
774
+ "covers": {
689
775
  "type": "array",
690
776
  "items": {
691
777
  "type": "string"
692
778
  },
693
- "description": "Task ids this scope names (machine-local ids; the scope↔task grouping is re-derived on regeneration)."
779
+ "description": "Optional REQ-ids from requirements.md this scope answers for — the same stable, never-renumbered key an AC's `covers:` clause uses. Projected as the Scope→Requirement COVERS edge in the run graph."
780
+ },
781
+ "depends_on": {
782
+ "type": "array",
783
+ "items": {
784
+ "type": "string"
785
+ },
786
+ "description": "scope_ids this scope must build AFTER. Build order lives in the same tier as the contracts it orders: this was previously derived from the LOCAL board (contract tasks → task depends_on → owning scope), which collapsed to no edges whenever the board was absent and silently degraded the scheduler to an unscheduled fan-out. An id naming a scope not in the run is dropped by the scheduler (fail-open) and reported by spec-lint SCOPE-DEPS."
694
787
  },
695
788
  "allowed_file_substrate": {
696
789
  "type": "array",
@@ -718,7 +811,7 @@
718
811
  "items": {
719
812
  "type": "string"
720
813
  },
721
- "description": "Command(s) that drive this scope end-to-end (the T0 layer). Too speculative to fixture → an explicit TBD flag, never an invented fixture."
814
+ "description": "Command(s) that drive this scope end-to-end (the T0 layer). EACH MUST EXIT 0 WHEN THE SCOPE IS CORRECT — `harness verify t0` scores a fixture as failed on any non-zero exit, so a fixture written as a bare error-path invocation (`todo done abc # exit 1`) can never pass and the scope can never go T0-green however correct the code is. Assert error paths INSIDE a test file that itself exits 0 (`node --test test/commands/done.test.js`), which is what the expected non-zero exit belongs in. Too speculative to fixture → an explicit TBD flag, never an invented fixture."
722
815
  },
723
816
  "db_probe": {
724
817
  "type": "string",
@@ -732,14 +825,14 @@
732
825
  "DOWNHILL_EXECUTION",
733
826
  "FINISHED"
734
827
  ],
735
- "description": "ALWAYS authored as UPHILL_UNKNOWN (DD-10). The live phase is DERIVED from T0/T1/seesaw facts into hill/<scope-id>.yml — facts move dots, not authors."
828
+ "description": "ALWAYS authored as UPHILL_UNKNOWN — self-reported confidence is the risk this closes. The live phase is DERIVED from T0/T1/seesaw facts into hill/<scope-id>.yml — facts move dots, not authors."
736
829
  },
737
830
  "superseded_by": {
738
831
  "type": "array",
739
832
  "items": {
740
833
  "type": "string"
741
834
  },
742
- "description": "Set by split-scope: the replacing scope ids. Supersede-never-delete."
835
+ "description": "Set by map-scopes: the replacing scope ids. Supersede-never-delete."
743
836
  },
744
837
  "business_goal": {
745
838
  "type": "string",
@@ -747,6 +840,23 @@
747
840
  }
748
841
  }
749
842
  },
843
+ "ScopeBoard": {
844
+ "description": "The scope board — a HUMAN-READABLE PROJECTION of the contracts, not a source. Every column restates something `scopes/*.md` already declares, so it can be regenerated from them and deleting it loses nothing. It deliberately carries no `wave` column: waves are Kahn levels of `depends_on` and `probe resume` derives them, and a hand-authored copy of a derived value is the same defect as the hand-authored `unlocks` that `deriveUnlocks` exists to prevent. Nothing parses this file — that is only safe while it stays a view; the moment it holds a fact of its own it needs a reader and a lint like every other contract.",
845
+ "x-tier": "SHARED",
846
+ "x-location": "shapeup/<slug>/scope-board.md",
847
+ "x-writer": "scope-architect (regenerated whenever the contracts change)",
848
+ "x-readers": "humans, in a pull request. No code reads it — by design.",
849
+ "type": "object",
850
+ "properties": {
851
+ "rows": {
852
+ "type": "array",
853
+ "description": "One row per scope: scope_id · topology_type · use_cases · depends_on · substrate size · lint status. All projected from the contract.",
854
+ "items": {
855
+ "type": "object"
856
+ }
857
+ }
858
+ }
859
+ },
750
860
  "AcResult": {
751
861
  "description": "One acceptance criterion's outcome with evidence. 'ac' carries the checkbox text VERBATIM — ingest ticks boxes by substring-matching it. No recorded evidence = the worker reports fail by its own hand.",
752
862
  "x-tier": "EMBEDDED",
@@ -831,87 +941,12 @@
831
941
  }
832
942
  }
833
943
  },
834
- "Escalate": {
835
- "description": "The worker's ONE outward port: a decision it cannot make alone. Ingest queues these to escalates/<order>.json; advisor-protocol adjudicates (budget ≤3/scope/round) and the answer returns as a Decision in the next order. The advisor-side grammar adds scope_id/round/attempt/options/default_if_silent (see EscalateBlock).",
836
- "x-tier": "EMBEDDED",
837
- "type": "object",
838
- "required": [
839
- "kind",
840
- "question"
841
- ],
842
- "properties": {
843
- "kind": {
844
- "type": "string",
845
- "enum": [
846
- "design-decision",
847
- "spec-ambiguity",
848
- "substrate-expansion"
849
- ],
850
- "description": "Three kinds, three resolution paths: design/spec go to the PO (or default_if_silent unattended); substrate-expansion is NEVER silently approved — it routes to a scope-architect remap order."
851
- },
852
- "question": {
853
- "type": "string",
854
- "description": "One checkable question."
855
- },
856
- "blocked_ac": {
857
- "type": "string",
858
- "description": "Which AC waits on this — work on unrelated ACs continues."
859
- },
860
- "context": {
861
- "type": "string",
862
- "description": "The two interpretations / the file and why it's needed."
863
- }
864
- }
865
- },
866
- "EscalateBlock": {
867
- "description": "The advisor-protocol adjudication grammar — an Escalate enriched with run coordinates and options, as passed to /advisor-protocol --escalate. The answer is persisted to the committed round-ledger.md Decisions table the instant it's given (zero-memory handoff: escalation memory lives in files, not chat).",
868
- "x-tier": "EMBEDDED",
869
- "x-readers": "advisor-protocol",
870
- "type": "object",
871
- "required": [
872
- "kind",
873
- "question"
874
- ],
875
- "properties": {
876
- "scope_id": {
877
- "type": "string"
878
- },
879
- "round": {
880
- "type": "integer"
881
- },
882
- "attempt": {
883
- "type": "integer"
884
- },
885
- "kind": {
886
- "type": "string",
887
- "enum": [
888
- "design-decision",
889
- "spec-ambiguity",
890
- "substrate-expansion"
891
- ]
892
- },
893
- "question": {
894
- "type": "string"
895
- },
896
- "options": {
897
- "type": "array",
898
- "items": {
899
- "type": "string"
900
- },
901
- "description": "Proposed answers, each with its rationale."
902
- },
903
- "default_if_silent": {
904
- "type": "string",
905
- "description": "Optional low-risk fallback — applied under --unattended (logged + flagged for GATE H); the advisor is never forced to accept it."
906
- }
907
- }
908
- },
909
944
  "Discovery": {
910
945
  "description": "The universal currency of surprise: one raw discovered line. Producers: task-executor (marker +, P3.7), qa-edge-hunter (always ~, repro required), ba-pitch-analyzer (overflow reports). NO producer writes the ledger — ingest-result appends every entry to discovery/ledger.md (single-writer, mechanically). Promotion ~ → must-have is always a human act at SHIP S.0/GATE H.",
911
946
  "x-tier": "EMBEDDED (projection into the LOCAL discovery ledger)",
912
947
  "x-location": ".shapeup/<slug>/discovery/ledger.md",
913
- "x-writer": "ingest-result.mjs",
914
- "x-readers": "ba-pitch-analyzer (reconcile), scope-architect (remap), scope-hammer (census), qa-edge-hunter (covered-territory context)",
948
+ "x-writer": "harness reduce ingest",
949
+ "x-readers": "ba-pitch-analyzer (reconcile), scope-architect (map-scopes), scope-hammer (census), qa-edge-hunter (covered-territory context)",
915
950
  "type": "object",
916
951
  "required": [
917
952
  "marker",
@@ -961,7 +996,7 @@
961
996
  }
962
997
  },
963
998
  "T0Citation": {
964
- "description": "The judge's pointer to a mechanical T0 artifact. The evaluator RECOMPUTES sha256 from disk — a handed hash is never trusted; a verdict on a scoped spec without a citation is structurally invalid (PA4/DD-7: T0 is a machine fact the generator cannot fabricate).",
999
+ "description": "The judge's pointer to a mechanical T0 artifact. The evaluator RECOMPUTES sha256 from disk — a handed hash is never trusted; a verdict on a scoped spec without a citation is structurally invalid: T0 is a machine fact the generator cannot fabricate.",
965
1000
  "x-tier": "EMBEDDED",
966
1001
  "type": "object",
967
1002
  "required": [
@@ -1071,7 +1106,7 @@
1071
1106
  "evidence": {
1072
1107
  "type": "string",
1073
1108
  "pattern": "[\\w./\\-]+:[0-9]+",
1074
- "description": "A FAIL must carry a file:line locator. Was stated 5x in spec-evaluator/SKILL.md prose and enforced 0x; a measured run returned a correct FAIL verdict with bugs[] null and no locator anywhere, so the finding could not be acted on without re-investigating. Now mechanical: validate-envelope rejects it before ingest."
1109
+ "description": "A FAIL must carry a file:line locator. The rule used to live in prose and be enforced nowhere a correct FAIL verdict with no locator anywhere is unactionable without re-investigating (observed, not theorized). Now mechanical: the ingest-side schema validation rejects it before any write."
1075
1110
  },
1076
1111
  "traces_to": {
1077
1112
  "type": "array",
@@ -1187,11 +1222,11 @@
1187
1222
  }
1188
1223
  },
1189
1224
  "T0Artifact": {
1190
- "description": "The mechanical verification verdict for one build attempt — the evidence layer under the LLM judge. Written by t0-verify.mjs from actually running the scope's fixtures + DB probe + seesaw; zero LLM tokens. spec-evaluator must cite it (sha256) on scoped specs. Red artifacts carry AEGIS triples that become the next attempt's digested_errors.",
1225
+ "description": "The mechanical verification verdict for one build attempt — the evidence layer under the LLM judge. Written by harness verify t0 from actually running the scope's fixtures + DB probe + seesaw; zero LLM tokens. spec-evaluator must cite it (sha256) on scoped specs. Red artifacts carry AEGIS triples that become the next attempt's digested_errors.",
1191
1226
  "x-tier": "LOCAL",
1192
1227
  "x-location": ".shapeup/<slug>/t0/verdicts/r<N>-a<M>-t<T>.json (schema_version 2; the unsuffixed r<N>-a<M>.json of schema_version 1 is still readable)",
1193
- "x-writer": "t0-verify.mjs",
1194
- "x-readers": "compile-order.mjs (next attempt), spec-evaluator (citation), tech-lead (hill derivation, GATE L2.3), scope-hammer (stall census)",
1228
+ "x-writer": "harness verify t0",
1229
+ "x-readers": "harness compile (next attempt), spec-evaluator (citation), tech-lead (hill derivation, GATE L2.3), scope-hammer (stall census)",
1195
1230
  "type": "object",
1196
1231
  "properties": {
1197
1232
  "schema_version": {
@@ -1211,6 +1246,11 @@
1211
1246
  "type": "integer",
1212
1247
  "description": "schema_version 2+: the retry ordinal within (round, attempt). Written with flag 'wx', so a retry of the SAME attempt lands beside its predecessor instead of destroying it (invariant I4)."
1213
1248
  },
1249
+ "run_id": {
1250
+ "type": "string",
1251
+ "description": "OPTIONAL (v1.8) — the run key (mintRunId in lib/paths.mjs). (round, attempt, trial) addresses an artifact within a run and repeats in the next one, so the evidence layer needed this to be groupable by run.",
1252
+ "pattern": "^[a-z0-9][a-z0-9-]*-[0-9]{8}T[0-9]{6}Z-[0-9a-f]{8}$"
1253
+ },
1214
1254
  "at": {
1215
1255
  "type": "string",
1216
1256
  "description": "ISO timestamp."
@@ -1306,8 +1346,8 @@
1306
1346
  "description": "One row per T0 run — the ratchet's history, and the artifact that makes 'is attempt N+1 better than attempt N?' answerable. Append-only (I4: every superseded object remains addressable). baseline_trial is the parent link: that single field IS the experiment DAG (lineage, PARENT_OF, and a genuine SUPERSEDES edge) delivered without a graph store. Written by t0-verify, NOT ingest-result: a trial row is a mechanical fact produced by running commands, in the same class as the verdict artifact t0-verify already owns, and carries no worker claim to apply.",
1307
1347
  "x-tier": "LOCAL",
1308
1348
  "x-location": ".shapeup/<slug>/t0/trials.jsonl",
1309
- "x-writer": "t0-verify.mjs (append-only)",
1310
- "x-readers": "compile-order.mjs (payload.trial_history — the paper's inspect()), stats.mjs --ratchet (improvement_rate, monotone_rate, sawtooth_count)",
1349
+ "x-writer": "harness verify t0 (append-only)",
1350
+ "x-readers": "harness compile (payload.trial_history — the paper's inspect()), harness probe stats --ratchet (improvement_rate, monotone_rate, sawtooth_count)",
1311
1351
  "type": "object",
1312
1352
  "properties": {
1313
1353
  "schema_version": {
@@ -1321,6 +1361,11 @@
1321
1361
  "minimum": 1,
1322
1362
  "description": "Monotone ordinal across the run's whole ledger — what baseline_trial points at."
1323
1363
  },
1364
+ "run_id": {
1365
+ "type": "string",
1366
+ "description": "OPTIONAL (v1.8) — the run key (mintRunId in lib/paths.mjs), read off the receipt in the run root t0-verify was pointed at. `trial` is monotone WITHIN a run and restarts at 1 in the next one, so ratchet history could not be split by run without it.",
1367
+ "pattern": "^[a-z0-9][a-z0-9-]*-[0-9]{8}T[0-9]{6}Z-[0-9a-f]{8}$"
1368
+ },
1324
1369
  "round": {
1325
1370
  "type": "integer"
1326
1371
  },
@@ -1390,11 +1435,11 @@
1390
1435
  ]
1391
1436
  },
1392
1437
  "SeesawRegistry": {
1393
- "description": "The fixture registry of every FINISHED scope — what seesawCheck re-runs on each later attempt so a new scope cannot silently break a shipped one (PA5).",
1438
+ "description": "The fixture registry of every FINISHED scope — what seesawCheck re-runs on each later attempt so a new scope cannot silently break a shipped one — a regression mistaken for progress is the pathology the seesaw exists for.",
1394
1439
  "x-tier": "LOCAL",
1395
1440
  "x-location": ".shapeup/<slug>/seesaw/registry.json",
1396
1441
  "x-writer": "tech-lead (when a scope reaches FINISHED)",
1397
- "x-readers": "t0-verify.mjs",
1442
+ "x-readers": "harness verify t0",
1398
1443
  "type": "object",
1399
1444
  "properties": {
1400
1445
  "scopes": {
@@ -1420,8 +1465,8 @@
1420
1465
  "description": "One appended line of judge history (JSONL). Never rewritten — flips across runs are detected here and force confidence low. run auto-increments per append batch.",
1421
1466
  "x-tier": "LOCAL",
1422
1467
  "x-location": ".shapeup/<slug>/evaluation/.verdicts-<target>.jsonl",
1423
- "x-writer": "ingest-result.mjs (from Verdict.criteria[])",
1424
- "x-readers": "spec-evaluator (read-only flip detection), verdict-ledger.mjs",
1468
+ "x-writer": "harness reduce ingest (from Verdict.criteria[])",
1469
+ "x-readers": "spec-evaluator (read-only flip detection), harness reduce verdict",
1425
1470
  "type": "object",
1426
1471
  "properties": {
1427
1472
  "run": {
@@ -1460,10 +1505,10 @@
1460
1505
  }
1461
1506
  },
1462
1507
  "HillShard": {
1463
- "description": "One scope's hill position — DERIVED, never self-reported (DD-10): UPHILL_UNKNOWN (open unknowns > 0) → UPHILL_SOLVED (unknowns 0, no T0-green yet) → DOWNHILL_EXECUTION (≥1 T0-green; T1/seesaw pending) → FINISHED (T1 PASS ∧ seesaw green ∧ merged). Single-writer = whoever holds that scope's branch. Progress is reported by hill position, never task counts.",
1508
+ "description": "One scope's hill position — DERIVED, never self-reported: UPHILL_UNKNOWN (open unknowns > 0) → UPHILL_SOLVED (unknowns 0, no T0-green yet) → DOWNHILL_EXECUTION (≥1 T0-green; T1/seesaw pending) → FINISHED (T1 PASS ∧ seesaw green ∧ merged). Single-writer = whoever holds that scope's branch. Progress is reported by hill position, never task counts.",
1464
1509
  "x-tier": "SHARED",
1465
1510
  "x-location": "shapeup/<slug>/hill/<scope-id>.yml",
1466
- "x-writer": "tech-lead (GATE L2.4 derivation)",
1511
+ "x-writer": "tech-lead (GATE L2 derivation)",
1467
1512
  "x-readers": "tech-lead (GATE L3 hill report), scope-hammer (census), GATE L1b sequencing",
1468
1513
  "type": "object",
1469
1514
  "properties": {
@@ -1483,10 +1528,10 @@
1483
1528
  },
1484
1529
  "MetricsRow": {
1485
1530
  "description": "One SHIP-time harvest row (facts only — copied from structured output that already exists; never a newly computed verdict/score, which would be a second judge behind spec-evaluator). Sharded per machine so concurrent runs never merge-conflict.",
1486
- "x-tier": "SHARED",
1531
+ "x-tier": "LOCAL",
1487
1532
  "x-location": ".shapeup/metrics/<machine-id>.jsonl",
1488
1533
  "x-writer": "tech-lead (SHIP S.6)",
1489
- "x-readers": "tier-3 e2e benchmark tooling, stats.mjs (read-only projection)",
1534
+ "x-readers": "harness probe stats (read-only projection), harness report export (the run dimension)",
1490
1535
  "type": "object",
1491
1536
  "properties": {
1492
1537
  "schema_version": {
@@ -1497,7 +1542,12 @@
1497
1542
  },
1498
1543
  "at": {
1499
1544
  "type": "string",
1500
- "description": "OPTIONAL (v1.2) — ISO harvest timestamp; enables real trend-over-time in stats.mjs. Older rows lack it and fall back to shard append order."
1545
+ "description": "OPTIONAL (v1.2) — ISO harvest timestamp; enables real trend-over-time in harness probe stats. Older rows lack it and fall back to shard append order."
1546
+ },
1547
+ "run_id": {
1548
+ "type": "string",
1549
+ "description": "OPTIONAL (v1.8) — the run key (mintRunId in lib/paths.mjs), COPIED from the receipt at SHIP S.6. This is the harvest row's only link to the run trace that produced it: everything else here is a count, and `feature_slug` groups runs together rather than apart.",
1550
+ "pattern": "^[a-z0-9][a-z0-9-]*-[0-9]{8}T[0-9]{6}Z-[0-9a-f]{8}$"
1501
1551
  },
1502
1552
  "feature_slug": {
1503
1553
  "type": "string"
@@ -1525,7 +1575,7 @@
1525
1575
  },
1526
1576
  "attempt_exhaustions": {
1527
1577
  "type": "integer",
1528
- "description": "OPTIONAL (v1.2) — scopes whose inner attempt_budget tripped this run, COPIED at SHIP S.6 from the queued GATE H hammer proposals (a fact registered at the source; stats.mjs must never re-derive it)."
1578
+ "description": "OPTIONAL (v1.2) — scopes whose inner attempt_budget tripped this run, COPIED at SHIP S.6 from the queued GATE H hammer proposals (a fact registered at the source; harness probe stats must never re-derive it)."
1529
1579
  },
1530
1580
  "qa_findings": {
1531
1581
  "type": "object",
@@ -1547,7 +1597,7 @@
1547
1597
  },
1548
1598
  "ratchet": {
1549
1599
  "type": "object",
1550
- "description": "OPTIONAL (v1.5) — the DAY-1 exit measurement, COPIED from `stats.mjs --ratchet` over this run's t0/trials.jsonl before the LOCAL trace is wiped. This is the only place the ratchet numbers survive the run, and the only reason they can be compared across machines: `.shapeup/` is gitignored, so a measurement left there answers the question exactly once and then deletes itself. Facts only — every field is a count or a ratio over recorded trial rows, never a grade.",
1600
+ "description": "OPTIONAL (v1.5) — the DAY-1 exit measurement, COPIED from `harness probe stats --ratchet` over this run's t0/trials.jsonl before the LOCAL trace is wiped. This is the only place the ratchet numbers survive the run, and the only reason they can be compared across machines: `.shapeup/` is gitignored, so a measurement left there answers the question exactly once and then deletes itself. Facts only — every field is a count or a ratio over recorded trial rows, never a grade.",
1551
1601
  "properties": {
1552
1602
  "trials": {
1553
1603
  "type": "integer"
@@ -1578,7 +1628,7 @@
1578
1628
  },
1579
1629
  "hooks": {
1580
1630
  "type": "object",
1581
- "description": "OPTIONAL (v1.5) — the DAY-2 exit measurement, COPIED from `stats.mjs --hooks` over .shapeup/decisions.jsonl. Makes 'never had to fire' and 'never ran' separable across runs: before the decision ledger both produced exit 0 and empty stdout, which is why several enforcement tools were unscoreable.",
1631
+ "description": "OPTIONAL (v1.5) — the DAY-2 exit measurement, COPIED from `harness probe stats --hooks` over .shapeup/decisions.jsonl. Makes 'never had to fire' and 'never ran' separable across runs: before the decision ledger both produced exit 0 and empty stdout, which is why several enforcement tools were unscoreable.",
1582
1632
  "properties": {
1583
1633
  "evaluations": {
1584
1634
  "type": "integer"
@@ -1658,11 +1708,11 @@
1658
1708
  }
1659
1709
  },
1660
1710
  "RunSnapshot": {
1661
- "description": "Mechanical mid-run state digest, derived ONLY from files (active-scope pointer, harness-run.md frontmatter, board frontmatter, t0/verdicts filenames, orders/ vs results/) — never from conversation memory. The compaction-resilience reflex: persisted by the PreCompact hook as an audit anchor, re-derived fresh and injected as additionalContext by the SessionStart(startup|compact|resume|clear) hook so the orchestrator re-reads the files instead of trusting a lossy summary — or, on a cold startup, instead of re-opening a run that is already open.",
1711
+ "description": "Mechanical mid-run state digest, derived ONLY from files (active-scope pointer, harness-run.md frontmatter, board frontmatter, t0/verdicts filenames, orders/ vs results/) — never from conversation memory. The continuity reflex: after any context loss the orchestrator re-derives this from disk rather than trusting a lossy summary — or, on a cold startup, rather than re-opening a run that is already open.",
1662
1712
  "x-tier": "LOCAL",
1663
- "x-location": ".shapeup/<slug>/run-snapshot.json (persisted by the compact-snapshot hook; also emitted ephemerally on stdout by run-snapshot.mjs)",
1664
- "x-writer": "run-snapshot.mjs --write (invoked by the PreCompact hook — never a worker; this is LOCAL derived cache, ingest-result.mjs remains the sole writer of SHARED state)",
1665
- "x-readers": "session-rehydrate hook, tech-lead (--from resume aid), human",
1713
+ "x-location": ".shapeup/<slug>/run-snapshot.json (written by harness reduce snapshot --write; also emitted ephemerally on stdout by harness reduce snapshot)",
1714
+ "x-writer": "harness reduce snapshot --write (never a worker; this is LOCAL derived cache, harness reduce ingest remains the sole writer of SHARED state)",
1715
+ "x-readers": "tech-lead (--from resume aid), human",
1666
1716
  "type": "object",
1667
1717
  "required": [
1668
1718
  "schema_version",
@@ -1755,9 +1805,6 @@
1755
1805
  }
1756
1806
  }
1757
1807
  },
1758
- "open_escalates": {
1759
- "type": "integer"
1760
- },
1761
1808
  "pending_orders": {
1762
1809
  "type": "array",
1763
1810
  "items": {
@@ -1770,14 +1817,14 @@
1770
1817
  },
1771
1818
  "rehydrate_hint": {
1772
1819
  "type": "string",
1773
- "description": "The composed one-paragraph instruction injected after compaction: re-read run-state, trust the files, not the summary."
1820
+ "description": "The composed one-paragraph instruction for resuming after a context loss: re-read run-state, trust the files, not the summary."
1774
1821
  }
1775
1822
  }
1776
1823
  },
1777
1824
  "StatsReport": {
1778
1825
  "description": "The telemetry read-plane's output — an aggregate projection over the MetricsRow shards. Aggregates COPIED facts only (counts, rates, trends over already-recorded fields); it never grades, so it can never become a second judge. Malformed rows are skipped fail-open and counted; pathology rows (sandbox-guard/safety-spine denials share the shards) are partitioned, not errors.",
1779
1826
  "x-tier": "EMBEDDED (ephemeral projection — printed to stdout, never persisted; the metrics JSONL stays the only source of truth)",
1780
- "x-writer": "stats.mjs (read-only over metrics/ — writes nothing)",
1827
+ "x-writer": "harness probe stats (read-only over metrics/ — writes nothing)",
1781
1828
  "x-readers": "human / CLI / CI",
1782
1829
  "type": "object",
1783
1830
  "required": [
@@ -1923,8 +1970,8 @@
1923
1970
  }
1924
1971
  },
1925
1972
  "Lane": {
1926
- "description": "v1.2 DESIGN DRAFT reserves the name and makes the policy reviewable; no envelope references it yet and no runtime reads it. See docs/design/04-functional-design.md §4.7. When implemented, the lane is pipeline knowledge: the orchestrator compiles it away into existing registered knobs (payload.lens, payload.dimensions, interaction gate policy) — it NEVER rides in WorkOrderPayload, so workers stay pipeline-blind.",
1927
- "x-tier": "EMBEDDED (when implemented: recorded in harness-run.md frontmatter `lane:` — never a WorkOrderPayload field)",
1973
+ "description": "The run's lane (full|tiny), computed by harness init fit at GATE L0.3 and recorded in harness-run.md frontmatter `lane:` (with its evidence, and marked overridden when --lane/--tiny outranks the recommendation); RunArgs.lane carries it to the workflow launch. The lane is pipeline knowledge: the orchestrator compiles it away into existing registered knobs (lens, budgets, gate policy) — it NEVER rides in WorkOrderPayload, so workers stay pipeline-blind.",
1974
+ "x-tier": "EMBEDDED (recorded in harness-run.md frontmatter `lane:` — never a WorkOrderPayload field)",
1928
1975
  "x-writer": "tech-lead (GATE L0 lane selection)",
1929
1976
  "x-readers": "tech-lead only",
1930
1977
  "x-lane-policy": {
@@ -1970,11 +2017,11 @@
1970
2017
  ]
1971
2018
  },
1972
2019
  "RequirementClause": {
1973
- "description": "One atomic customer requirement, frozen with a STABLE id — the covers-closure input truth (spine v1.3). Extracted from the REQ source (Open Decision A: pitch / customer-requirements doc / use-case bodies) by ba-pitch-analyzer's `coverage` operation; extraction is judgment (LLM), but ids are assigned ONCE and never renumber (they behave like scope_id, not TASK-NNN — every covers: link would rot otherwise). Supersede-never-delete: a dropped clause is marked CUT (PO-approved), never removed, so the registry stays auditable on a fresh clone. The original REQ source is never edited — the registry is a separate derived, committed file.",
2020
+ "description": "One atomic customer requirement, frozen with a STABLE id — the covers-closure input truth (spine v1.3). Extracted from the REQ source (pitch / customer-requirements doc / use-case bodies) by the planner extraction is judgment (LLM), but ids are assigned ONCE and never renumber (they behave like scope_id, not TASK-NNN — every covers: link would rot otherwise). Supersede-never-delete: a dropped clause is marked CUT (PO-approved), never removed, so the registry stays auditable on a fresh clone. The original REQ source is never edited — the registry is a separate derived, committed file.",
1974
2021
  "x-tier": "SHARED",
1975
2022
  "x-location": "shapeup/<slug>/requirements.md",
1976
2023
  "x-writer": "ba-pitch-analyzer (operation: coverage) — extraction only; CUT status is a PO governance edit (same class as a round-ledger Decision)",
1977
- "x-readers": "trace-lint.mjs (covers-closure oracle), tech-lead, human (coverage/cut audit)",
2024
+ "x-readers": "harness verify trace (covers-closure oracle), tech-lead, human (coverage/cut audit)",
1978
2025
  "type": "object",
1979
2026
  "required": [
1980
2027
  "id",
@@ -2040,12 +2087,32 @@
2040
2087
  }
2041
2088
  }
2042
2089
  },
2090
+ "Seam": {
2091
+ "description": "How one use case's engine attaches to the entry point — the `wiring_seam` value of a WiringEntry, promoted to a first-class node by `reduce graph` so a verdict can be traced back through the seam to the objective. Not a file: it exists only as a cell in the committed wiring map and as a node in the derived run graph.",
2092
+ "x-tier": "EMBEDDED (a WiringEntry.wiring_seam value; promoted to a node by reduce graph)",
2093
+ "x-writer": "solution-architect (operation: wire), inside wiring-map.md",
2094
+ "type": "object",
2095
+ "properties": {
2096
+ "seam": {
2097
+ "type": "string",
2098
+ "description": "The attachment mechanism — a route registration, an event handler, a direct call from the composition root."
2099
+ },
2100
+ "engine": {
2101
+ "type": "string",
2102
+ "description": "The repo-relative module the seam attaches."
2103
+ },
2104
+ "entry_point": {
2105
+ "type": "string",
2106
+ "description": "The call site the seam reaches from."
2107
+ }
2108
+ }
2109
+ },
2043
2110
  "WiringMap": {
2044
- "description": "The reachability map — per UC, the chain from engine to a player-visible affordance (spine v1.3). Written DIRECTLY by solution-architect at gate L1a.5 (precedent: ScopeContract is written directly by scope-architect, bypassing ingest-result). Its purpose: no engine ships orphaned from the running application. trace-lint folds reachability into its covers-closure pass — a UC whose engine does not reach ProjectProfile.entry_point via the import graph is red. A Mermaid view is auto-rendered from this map (a view of a checked graph, so it cannot drift). ON-DISK FORMAT IS MARKDOWN (ADR-0001): frontmatter carries the scalars and [a, b] lists, a markdown table carries the one array-of-objects field, and prose carries the rationale a reviewer reads. THIS SCHEMA DESCRIBES THE PARSED OBJECT — the wire form embedded in the WorkOrder envelope — not the file. `skills/tech-lead/scripts/lib/contract-md.mjs` is the only reader/writer of the file form; everything downstream of that parse sees exactly this shape.",
2111
+ "description": "The reachability map — per UC, the chain from engine to a player-visible affordance (spine v1.3). Written DIRECTLY by solution-architect at gate L1a.5 (precedent: ScopeContract is written directly by scope-architect, bypassing ingest-result). Its purpose: no engine ships orphaned from the running application. trace-lint folds reachability into its covers-closure pass — a UC whose engine does not reach ProjectProfile.entry_point via the import graph is red. A Mermaid view is auto-rendered from this map (a view of a checked graph, so it cannot drift). ON-DISK FORMAT IS MARKDOWN (ADR-0001): frontmatter carries the scalars and [a, b] lists, a markdown table carries the one array-of-objects field, and prose carries the rationale a reviewer reads. THIS SCHEMA DESCRIBES THE PARSED OBJECT — the wire form embedded in the WorkOrder envelope — not the file. `kernel/lib/contract.mjs` is the only reader/writer of the file form; everything downstream of that parse sees exactly this shape.",
2045
2112
  "x-tier": "SHARED",
2046
2113
  "x-location": "shapeup/<slug>/wiring-map.md",
2047
2114
  "x-writer": "solution-architect (SOLE writer, direct — like scope-architect writes scopes/*.md)",
2048
- "x-readers": "trace-lint.mjs (reachability oracle), scope-architect (integration seam at slicing time), tech-lead",
2115
+ "x-readers": "harness verify trace (reachability oracle), scope-architect (integration seam at slicing time), tech-lead",
2049
2116
  "type": "object",
2050
2117
  "required": [
2051
2118
  "schema_version",
@@ -2076,11 +2143,11 @@
2076
2143
  }
2077
2144
  },
2078
2145
  "ProjectProfile": {
2079
- "description": "The archetype + entry-point declaration reachability depends on (spine v1.3). Exists for ONE load-bearing reason: reachability must know the entry point, and that is archetype-specific (main.js for a client-only game is not the seam for a web-service). SHARED — a declaration that must survive `rm -rf .shapeup/` + clone cannot live in the gitignored run-trace. Role-gating (skip SysRS/Auth/CI-CD) is explicitly OUT of scope — those roles are not workers; the profile is introduced only as the entry-point/archetype declaration. ON-DISK FORMAT IS MARKDOWN (ADR-0001): frontmatter carries the scalars and [a, b] lists, a markdown table carries the one array-of-objects field, and prose carries the rationale a reviewer reads. THIS SCHEMA DESCRIBES THE PARSED OBJECT — the wire form embedded in the WorkOrder envelope — not the file. `skills/tech-lead/scripts/lib/contract-md.mjs` is the only reader/writer of the file form; everything downstream of that parse sees exactly this shape.",
2146
+ "description": "The archetype + entry-point declaration reachability depends on (spine v1.3). Exists for ONE load-bearing reason: reachability must know the entry point, and that is archetype-specific (main.js for a client-only game is not the seam for a web-service). SHARED — a declaration that must survive `rm -rf .shapeup/` + clone cannot live in the gitignored run-trace. Role-gating (skip SysRS/Auth/CI-CD) is explicitly OUT of scope — those roles are not workers; the profile is introduced only as the entry-point/archetype declaration. ON-DISK FORMAT IS MARKDOWN (ADR-0001): frontmatter carries the scalars and [a, b] lists, a markdown table carries the one array-of-objects field, and prose carries the rationale a reviewer reads. THIS SCHEMA DESCRIBES THE PARSED OBJECT — the wire form embedded in the WorkOrder envelope — not the file. `kernel/lib/contract.mjs` is the only reader/writer of the file form; everything downstream of that parse sees exactly this shape.",
2080
2147
  "x-tier": "SHARED",
2081
2148
  "x-location": "shapeup/<slug>/project-profile.md",
2082
- "x-writer": "tech-lead (GATE L0 — not compile-order.mjs, which stays pipeline-blind)",
2083
- "x-readers": "trace-lint.mjs (reachability entry_point), solution-architect (wire), tech-lead",
2149
+ "x-writer": "tech-lead (GATE L0 — not harness compile, which stays pipeline-blind)",
2150
+ "x-readers": "harness verify trace (reachability entry_point), solution-architect (wire), tech-lead",
2084
2151
  "type": "object",
2085
2152
  "required": [
2086
2153
  "schema_version",
@@ -2167,7 +2234,7 @@
2167
2234
  },
2168
2235
  "bugs": {
2169
2236
  "type": "array",
2170
- "description": "task-executor (operation: fix): the EVAL report's bug entries for this task — touch nothing else."
2237
+ "description": "The previous round's FAIL verdict, addressed to the scope whose substrate contains each cited file — touch nothing else. Written by `harness compile` from results/evaluate-r<N-1>.json, never by a caller: a build order is addressed by scope/round/attempt and carries no caller payload, and the verdict must survive the relaunch that separates two rounds. Present on any r≥2 execute order whose predecessor failed; an entry matching no scope's substrate is marked `unowned` and sent to all of them rather than dropped."
2171
2238
  },
2172
2239
  "spec_folder": {
2173
2240
  "type": "string",
@@ -2200,11 +2267,11 @@
2200
2267
  },
2201
2268
  "discovered_ledger": {
2202
2269
  "type": "string",
2203
- "description": "ba-pitch-analyzer (reconcile) / scope-architect (remap): the discovery ledger path whose raw [+]/~ lines get folded in."
2270
+ "description": "ba-pitch-analyzer (reconcile) / scope-architect (map-scopes): the discovery ledger path whose raw [+]/~ lines get folded in."
2204
2271
  },
2205
2272
  "requirements": {
2206
2273
  "type": "string",
2207
- "description": "ba-pitch-analyzer (operation: coverage): the REQ source to extract atomic clauses from (Open Decision A — pitch / customer-requirements doc / use-case bodies). Absent → the planner defaults to the pitch and records the choice in assumptions[]. The written registry lands at shapeup/<slug>/requirements.md (RequirementClause rows)."
2274
+ "description": "ba-pitch-analyzer: the REQ source to extract atomic clauses from (Open Decision A — pitch / customer-requirements doc / use-case bodies). Absent → the planner defaults to the pitch and records the choice in assumptions[]. The written registry lands at shapeup/<slug>/requirements.md (RequirementClause rows)."
2208
2275
  },
2209
2276
  "project_profile": {
2210
2277
  "type": "string",
@@ -2212,7 +2279,7 @@
2212
2279
  },
2213
2280
  "scope_id": {
2214
2281
  "type": "string",
2215
- "description": "scope-architect (split-scope): the stuck scope · scope-hammer (--breaker inner): the exhausted scope · advisor-protocol: budget scope."
2282
+ "description": "scope-architect (map-scopes): the stuck scope · scope-hammer (--breaker inner): the exhausted scope."
2216
2283
  },
2217
2284
  "dimensions": {
2218
2285
  "type": "array",
@@ -2246,20 +2313,19 @@
2246
2313
  "description": "qa-edge-hunter: the PASS EVAL report — negative-space input; its covered territory is subtracted from charters."
2247
2314
  },
2248
2315
  "app_url": {
2249
- "type": "string",
2250
- "description": "qa-edge-hunter: the running app to hunt (one real request at Q0, not a ping)."
2316
+ "type": [
2317
+ "string",
2318
+ "null"
2319
+ ],
2320
+ "description": "qa-edge-hunter: the running app to hunt (one real request at Q0, not a ping). NULL for a deliverable that is not served over HTTP — a CLI, a library, a batch job — where Q0 drives the binary instead. It was a bare string, so the first genuinely non-UI run could not compile a QA order at all: the payload carries null, the order fails its own schema, and the hunt is skipped for a reason that looks nothing like 'this deliverable has no URL'."
2251
2321
  },
2252
2322
  "ledger": {
2253
2323
  "type": "string",
2254
- "description": "qa-edge-hunter: discovery ledger path (READ-ONLY context) · advisor-protocol: the committed round-ledger.md decisions persist to."
2255
- },
2256
- "escalate": {
2257
- "$ref": "#/$defs/EscalateBlock",
2258
- "description": "advisor-protocol: the worker's ESCALATE block to adjudicate."
2324
+ "description": "qa-edge-hunter: discovery ledger path (READ-ONLY context)."
2259
2325
  },
2260
2326
  "round": {
2261
2327
  "type": "integer",
2262
- "description": "advisor-protocol: the round the budget/decision is counted against."
2328
+ "description": "the round this order is counted against."
2263
2329
  },
2264
2330
  "baseline": {
2265
2331
  "type": "string",
@@ -2289,6 +2355,387 @@
2289
2355
  "description": "translator: existing glossary.md to reuse as the base term map."
2290
2356
  }
2291
2357
  }
2358
+ },
2359
+ "ResumeState": {
2360
+ "description": "The fast-forward derivation: which phase a launch resumes at, derived from artifacts on disk and NEVER from stored state or conversation memory. Produced by kernel/probe/resume.mjs on stdout and consumed by shapeup-run.js's preamble on every launch, fresh or relaunch alike. Every phase predicate is an artifact test — `has_orient_artifacts` exists because the ORIENT branch was once gated on the ledger's stored `status` instead, which a silent courier failure left stale, and a completed ORIENT phase was re-dispatched on resume. `next_phase` is a convenience derived from the same booleans, which all travel too: a caller is never forced to trust a summary it cannot re-derive.",
2361
+ "x-tier": "EMBEDDED",
2362
+ "x-location": "stdout of harness probe resume — never written to disk (the run's state IS the artifacts this record reports on)",
2363
+ "x-writer": "harness probe resume (deriving; the same script's --set-status / --set-active-scope operations write, and report their own outcome record instead)",
2364
+ "x-readers": "shapeup-run.js (the fast-forward preamble, one call per launch)",
2365
+ "type": "object",
2366
+ "required": [
2367
+ "intake_path",
2368
+ "status",
2369
+ "has_orient_artifacts",
2370
+ "has_spec_tree",
2371
+ "has_wiring_map",
2372
+ "scope_files",
2373
+ "pending_orders",
2374
+ "eval_rounds_done",
2375
+ "next_phase"
2376
+ ],
2377
+ "properties": {
2378
+ "intake_path": {
2379
+ "type": "string",
2380
+ "description": "Resolved path to the run's intake — the pitch a fresh ORIENT dispatch is compiled against."
2381
+ },
2382
+ "spec_folder": {
2383
+ "type": [
2384
+ "string",
2385
+ "null"
2386
+ ],
2387
+ "description": "From harness-run.md frontmatter; null before the ledger names one."
2388
+ },
2389
+ "status": {
2390
+ "type": [
2391
+ "string",
2392
+ "null"
2393
+ ],
2394
+ "description": "The ledger's stored status, reported for the readers that legitimately hold a MID_RUN set over it (harness reduce snapshot, and the ship report's census). NOT a resume predicate: no phase decision in shapeup-run.js may branch on this field."
2395
+ },
2396
+ "lens": {
2397
+ "type": [
2398
+ "string",
2399
+ "null"
2400
+ ],
2401
+ "description": "From harness-run.md frontmatter; forwarded to the analyze dispatch. Null when the ledger names none — an unknown value is an ABSENT key in the order, never a null."
2402
+ },
2403
+ "stack": {
2404
+ "type": [
2405
+ "string",
2406
+ "null"
2407
+ ],
2408
+ "description": "From harness-run.md frontmatter; forwarded to the orient dispatch. Null when the ledger names none."
2409
+ },
2410
+ "run_cmd": {
2411
+ "type": [
2412
+ "string",
2413
+ "null"
2414
+ ],
2415
+ "description": "How to run the feature, from harness-run.md frontmatter; forwarded to the evaluate dispatch so the judge exercises a running thing. Null when the ledger names none."
2416
+ },
2417
+ "app_url": {
2418
+ "type": [
2419
+ "string",
2420
+ "null"
2421
+ ],
2422
+ "description": "Where the feature answers, from harness-run.md frontmatter; forwarded to the QA hunt. Null when the ledger names none."
2423
+ },
2424
+ "eval_dimensions": {
2425
+ "type": "array",
2426
+ "items": {
2427
+ "type": "string"
2428
+ },
2429
+ "description": "The dimensions this run's EVAL grades, from harness-run.md frontmatter. Defaults to [\"spec-conformance\"] rather than empty, so a ledger that names none still produces a graded run — and the ship sign-off reports the complement as the dimensions the run did NOT cover."
2430
+ },
2431
+ "orient_dir": {
2432
+ "type": "string",
2433
+ "description": "The run's LOCAL orient directory, resolved for the analyze dispatch: ORIENT's spike and code-surface notes are working artifacts, not deliverables, so they live in the gitignored tier (ADR-0001)."
2434
+ },
2435
+ "has_orient_artifacts": {
2436
+ "type": "boolean",
2437
+ "description": "ORIENT finished: orient/ carries code-surface.md, discovered-seed.md, hill-signal.md and at least one spike-*.md (skills/orient/SKILL.md §Outputs). The ORIENT skip gates on THIS."
2438
+ },
2439
+ "has_spec_tree": {
2440
+ "type": "boolean",
2441
+ "description": "ANALYZE finished: the spec folder's usecases/ carries at least one use case that is not _index.md. WIRE reads these — one wiring-map entry per use case — which is why ANALYZE precedes WIRE in the phase chain: dispatched against an empty spec folder, WIRE escalates on every launch."
2442
+ },
2443
+ "has_wiring_map": {
2444
+ "type": "boolean",
2445
+ "description": "WIRE finished: shapeup/<slug>/wiring-map.md exists."
2446
+ },
2447
+ "project_profile_path": {
2448
+ "type": "string"
2449
+ },
2450
+ "has_project_profile": {
2451
+ "type": "boolean",
2452
+ "description": "tech-lead writes project-profile.md at GATE L0, before the launch; trace-lint resolves reachability by its archetype."
2453
+ },
2454
+ "scope_files": {
2455
+ "type": "array",
2456
+ "description": "MAP SCOPES finished when non-empty. Each entry carries a RESOLVED path, never a bare filename — harness compile and harness verify t0 resolve --scope against cwd, and a bare id makes compile-order exit 2, which the attempt loop reads as the stagnation breaker.",
2457
+ "items": {
2458
+ "type": "object",
2459
+ "required": [
2460
+ "scope_id",
2461
+ "path"
2462
+ ],
2463
+ "properties": {
2464
+ "scope_id": {
2465
+ "type": "string"
2466
+ },
2467
+ "path": {
2468
+ "type": "string"
2469
+ }
2470
+ }
2471
+ }
2472
+ },
2473
+ "scope_waves": {
2474
+ "type": "array",
2475
+ "description": "The same scopes as scope_files, grouped into dependency WAVES: no wave contains a scope that depends on one still in flight. Levelled by `probe resume` from each contract's own `depends_on` — declared in the same COMMITTED tier as the contracts it orders, so it survives the clone that a join through machine-local task ids never could. Under the edge-released scheduler this is an ORDER and a cross-check rather than a barrier: the orchestrator trusts `scope_deps` only when Kahn-levelling it reproduces this field, so an edge lost crossing the sub-agent boundary degrades to wave release instead of starting a scope early. ADDITIVE: a consumer that ignores it, or a tree where no contract declares `depends_on`, every declared id names a scope not in the run, or the relation is cyclic, gets a single wave containing everything, which is the unscheduled fixed-width fan-out this replaced.",
2476
+ "items": {
2477
+ "type": "array",
2478
+ "items": {
2479
+ "type": "string"
2480
+ }
2481
+ }
2482
+ },
2483
+ "scope_deps": {
2484
+ "type": "array",
2485
+ "description": "The dependency edges the waves were levelled from, as `[dependant, dependency]` pairs, so a scope can be released the moment ITS OWN dependencies are green rather than when its whole wave finishes. Same parse as scope_waves; the two validate each other. ADDITIVE — absent, unusable or disagreeing with scope_waves, release falls back to the wave boundary.",
2486
+ "items": {
2487
+ "type": "array",
2488
+ "items": {
2489
+ "type": "string"
2490
+ }
2491
+ }
2492
+ },
2493
+ "scope_exclusions": {
2494
+ "type": "array",
2495
+ "description": "Pairs of scopes that may not build AT THE SAME TIME because both declare a write to one path — `substrate.shared` is the sanctioned escape from the disjointness rule, so the lint passes it and the substrate wall permits that path to every live order. Measured: three concurrent writers to one entry point lost work in 20 of 20 trials, because an edit is read-modify-write and the later writer drops the earlier one silently. Unlike scope_deps these pairs are UNORDERED — neither scope waits for the other, they merely never overlap — and unlike a dependency they cost concurrency rather than buying correctness in sequence, which is why the release ceiling they imply is reported before a run spends anything.",
2496
+ "items": {
2497
+ "type": "array",
2498
+ "items": {
2499
+ "type": "string"
2500
+ }
2501
+ }
2502
+ },
2503
+ "pending_orders": {
2504
+ "type": "array",
2505
+ "items": {
2506
+ "type": "string"
2507
+ },
2508
+ "description": "Order files with no result beside them. Diagnostic only — build orders carry no scope id, so this set is not an audit trail of build dispatches (see harness compile's order-id note)."
2509
+ },
2510
+ "eval_rounds_done": {
2511
+ "type": "array",
2512
+ "items": {
2513
+ "type": "integer"
2514
+ },
2515
+ "description": "Round numbers with an evaluate-r<n>.json result — the resumed run's round counter starts one past the maximum."
2516
+ },
2517
+ "next_phase": {
2518
+ "type": "string",
2519
+ "enum": [
2520
+ "orient",
2521
+ "analyze",
2522
+ "wire",
2523
+ "map-scopes",
2524
+ "build"
2525
+ ],
2526
+ "description": "The first phase whose artifacts are incomplete. Derived from the booleans above, in that order. The same table (harness probe resume PHASE_ARTIFACT) answers `--require <phase>`, the post-condition shapeup-run.js checks after every phase dispatch — resume and completion are ONE predicate by construction."
2527
+ }
2528
+ }
2529
+ },
2530
+ "RunArgs": {
2531
+ "description": "C1 — the launch half of the workflow's only conversation. Compiled ONCE by tech-lead at GATE L0 from harness init run output + the L0.8 model matrix + budgets, written to .shapeup/<slug>/run-args.json and handed to the harness run launch as one JSON literal — the workflow cannot ask follow-ups and cannot read config files itself, so everything a run will ever need travels in this one record. A workflow script validates its own subset of this shape in code (no runtime schema check at the C1 boundary itself); this entry is the central-registry definition the workflow script and the tech-lead skill both read as the one true shape.",
2532
+ "x-tier": "EMBEDDED",
2533
+ "x-location": ".shapeup/<slug>/run-args.json — written fresh by tech-lead on every launch and relaunch; the workflow receives it as its args and never reads other config",
2534
+ "x-writer": "tech-lead (GATE L0, on every launch AND every relaunch after a paused gate)",
2535
+ "x-readers": "the Workflow runtime (shapeup-run, and shapeup-run's own inner round dispatch)",
2536
+ "x-not-here": "Run config the LEDGER already carries does NOT get a second home in RunArgs — eval_dimensions, lens, spec_folder, stack, run_cmd, app_url are read off harness-run.md frontmatter by resume-state on every launch AND every relaunch, so a copy here would be a second source that can disagree with the first. RunArgs carries what a workflow cannot derive from disk (identity, budgets, the model matrix, pluginRoot, startedAt) plus noEval, which no frontmatter line holds.",
2537
+ "type": "object",
2538
+ "required": [
2539
+ "slug",
2540
+ "autoLevel",
2541
+ "models",
2542
+ "budgets",
2543
+ "pluginRoot",
2544
+ "startedAt"
2545
+ ],
2546
+ "properties": {
2547
+ "slug": {
2548
+ "type": "string",
2549
+ "description": "The feature slug this run builds — the aggregate root every path is keyed off."
2550
+ },
2551
+ "runId": {
2552
+ "type": "string",
2553
+ "description": "OPTIONAL (v1.8) — the run key, COPIED from harness init run's `run_id` output. Carried here so harness run stamps every journal row without a receipt lookup per launch; a launch without it falls back to reading the receipt, so this is an optimisation, not the source of truth. This is identity, which x-not-here explicitly admits to RunArgs — it is not run config the ledger already carries.",
2554
+ "pattern": "^[a-z0-9][a-z0-9-]*-[0-9]{8}T[0-9]{6}Z-[0-9a-f]{8}$"
2555
+ },
2556
+ "autoLevel": {
2557
+ "type": "string",
2558
+ "enum": [
2559
+ "interactive",
2560
+ "auto",
2561
+ "unattended"
2562
+ ],
2563
+ "description": "Resolved once at GATE L0; governs which gates pre-approve and what on_missing does (harness gate)."
2564
+ },
2565
+ "answers": {
2566
+ "type": "string",
2567
+ "description": "A gate-answers preset name (ci|guarded|interactive) or a path to a gate-answers.json file — the ONLY thing that decides whether a gate crosses, pauses, or aborts."
2568
+ },
2569
+ "models": {
2570
+ "type": "object",
2571
+ "description": "The L0.8 role -> model matrix, resolved once. The model floor: sonnet or above on every role, including mechanical couriers — no exceptions.",
2572
+ "properties": {
2573
+ "exec": {
2574
+ "type": "string"
2575
+ },
2576
+ "eval": {
2577
+ "type": "string"
2578
+ },
2579
+ "qa": {
2580
+ "type": "string"
2581
+ }
2582
+ }
2583
+ },
2584
+ "budgets": {
2585
+ "type": "object",
2586
+ "description": "The three-level circuit breaker's own limits (AGENTS.md) — outer round_budget, inner attempt_budget, and the opt-in wall-clock breaker.",
2587
+ "properties": {
2588
+ "maxRounds": {
2589
+ "type": "integer"
2590
+ },
2591
+ "attemptBudget": {
2592
+ "type": "integer"
2593
+ },
2594
+ "wallClockS": {
2595
+ "type": "integer"
2596
+ }
2597
+ }
2598
+ },
2599
+ "pluginRoot": {
2600
+ "type": "string",
2601
+ "description": "${CLAUDE_PLUGIN_ROOT} — the only thing a workflow script may ever root a path in (a workflow has no filesystem of its own; every path it resolves is either rooted here or produced by a prior call's stdout)."
2602
+ },
2603
+ "startedAt": {
2604
+ "type": "string",
2605
+ "description": "ISO timestamp, passed in because Date.now() is unavailable inside a workflow script by design — the deadline breaker (harness verify budget) computes elapsed time itself, in Node, where the clock lives."
2606
+ },
2607
+ "lane": {
2608
+ "$ref": "#/$defs/Lane",
2609
+ "description": "The fit-check verdict (full|tiny) — compiles away into existing knobs; never rides in WorkOrderPayload."
2610
+ },
2611
+ "noEval": {
2612
+ "type": "boolean",
2613
+ "description": "If true, skips the EVAL phase completely. Rides here rather than in the ledger because there is no frontmatter line that carries it — contrast eval_dimensions, which does."
2614
+ },
2615
+ "noQa": {
2616
+ "type": "boolean",
2617
+ "description": "If true, skips the QA Edge Hunt (`--no-qa`). Rides here for the same reason as noEval: no frontmatter line carries it. It is declared because the workflow reads it — an arg the script consumes and this record omits is a switch the launcher never learns to send, which is a documented flag that does nothing."
2618
+ },
2619
+ "maxParallelScopes": {
2620
+ "type": "integer",
2621
+ "minimum": 1,
2622
+ "default": 4,
2623
+ "description": "How many scopes may build at once (`--parallel-scopes`). A dial rather than a constant because concurrency is a COST question before it is a speed one: every extra leg is another worker's full context. 1 restores sequential scope building, which is the mitigation when a worker archetype proves unsafe to run beside itself."
2624
+ },
2625
+ "adversarialVerify": {
2626
+ "type": "boolean",
2627
+ "description": "If true, each FAIL finding gets one independent skeptic before it costs a whole fix round (`--adversarial-verify`). Off by default: it is a pure addition that spends one sub-agent per finding, and the single-judge invariant is unaffected — a refutation retracts a finding, it never issues a verdict."
2628
+ }
2629
+ }
2630
+ },
2631
+ "RunReturn": {
2632
+ "description": "C1 — the return half of the workflow's only conversation. The ONLY way the workflow speaks back to tech-lead, so it is a tagged union keyed on `status` — the skill branches on the tag, never on prose (the same exit-code discipline harness gate already applies to gate sign-off, extended to the orchestrator's own return value). A gate resolved 'ask' (exit 4) always returns `paused`; a gate resolved 'abort' (exit 5) always returns `aborted`; any circuit breaker tripping (outer round_budget, inner attempt_budget, or the deadline breaker) returns `gate_h`; a full run whose final EVAL passes returns `shipped`. The union is written as a superset of what one inner round can itself produce (`paused`, `aborted`, `gate_h`, or a round-complete carrying a verdict) — `shipped` belongs only to a full run. It is deliberately NOT narrowed, because the round-scoped subset is what a `workflow()` child would return if the fan-out this design anticipates ever spends that nesting level.",
2633
+ "x-tier": "EMBEDDED",
2634
+ "x-location": "in-memory only — a Workflow return value, never written to disk",
2635
+ "x-writer": "the workflow runtime (shapeup-run — the sole writer)",
2636
+ "x-readers": "tech-lead (branches on `status`, emits `block` verbatim on a pause, never re-summarizes it)",
2637
+ "type": "object",
2638
+ "required": [
2639
+ "status"
2640
+ ],
2641
+ "properties": {
2642
+ "status": {
2643
+ "type": "string",
2644
+ "enum": [
2645
+ "shipped",
2646
+ "paused",
2647
+ "aborted",
2648
+ "gate_h",
2649
+ "ok"
2650
+ ],
2651
+ "description": "shipped = the happy ending (outer run only) · paused = a gate resolved 'ask', relaunch after a recorded decision · aborted = a gate resolved 'abort', attributable stop · gate_h = a circuit breaker tripped, ship what is green · ok = one inner round completed (build+eval), not itself a ship — the outer loop decides whether to loop again."
2652
+ },
2653
+ "verdict": {
2654
+ "type": "string",
2655
+ "enum": [
2656
+ "pass",
2657
+ "fail"
2658
+ ],
2659
+ "description": "shipped | ok: the round or run's EVAL verdict, lowercased from spec-evaluator's PASS|FAIL."
2660
+ },
2661
+ "rounds_used": {
2662
+ "type": "integer"
2663
+ },
2664
+ "dims_not_evaluated": {
2665
+ "type": "array",
2666
+ "items": {
2667
+ "type": "string"
2668
+ }
2669
+ },
2670
+ "qa_findings": {
2671
+ "type": "integer"
2672
+ },
2673
+ "report": {
2674
+ "type": "string",
2675
+ "description": "shipped: shapeup/<slug>/REPORT.md path."
2676
+ },
2677
+ "state_warnings": {
2678
+ "type": "array",
2679
+ "items": {
2680
+ "type": "string"
2681
+ },
2682
+ "description": "shipped | gate_h: bookkeeping writes that did not take (a ledger status that read back wrong), carried on the ONE channel a headless launch preserves. The workflow's own log() goes to the progress narrator and `claude -p` stdout carries only the final message, so a run that returned `shipped` over a ledger still reading `evaluating` could not be told from one that warned about it. Absent when every state write took. Diagnostic, never a status: a lost bookkeeping write is a degraded digest, not a corrupted build."
2683
+ },
2684
+ "round": {
2685
+ "type": "integer",
2686
+ "description": "ok: which round this return describes."
2687
+ },
2688
+ "paused_at": {
2689
+ "type": "string",
2690
+ "description": "paused: the gate id that resolved 'ask' (e.g. L1b, L2, L3)."
2691
+ },
2692
+ "block": {
2693
+ "type": "string",
2694
+ "description": "paused: the ⏸ gate block, emitted VERBATIM by the skill — the block is the handoff contract; re-phrasing it is the paraphrase channel this design exists to close."
2695
+ },
2696
+ "valid_decisions": {
2697
+ "type": "array",
2698
+ "items": {
2699
+ "type": "string"
2700
+ },
2701
+ "description": "paused: the decisions VALID_BY_GATE allows at this gate (harness gate)."
2702
+ },
2703
+ "context": {
2704
+ "type": "object",
2705
+ "description": "paused: whatever facts the paused gate needs the PO to see (round, scope counts, spec_lint status, …)."
2706
+ },
2707
+ "aborted_at": {
2708
+ "type": "string",
2709
+ "description": "aborted: the gate id that resolved 'abort'."
2710
+ },
2711
+ "reason": {
2712
+ "type": "string",
2713
+ "description": "aborted: the attributable reason (harness gate's own `reason` field, carried through verbatim)."
2714
+ },
2715
+ "breaker": {
2716
+ "type": "string",
2717
+ "enum": [
2718
+ "outer",
2719
+ "inner",
2720
+ "deadline"
2721
+ ],
2722
+ "description": "gate_h: which of the three breakers fired (AGENTS.md's three-level circuit breaker)."
2723
+ },
2724
+ "hammer_proposals": {
2725
+ "type": "array",
2726
+ "items": {
2727
+ "type": "string"
2728
+ },
2729
+ "description": "gate_h | ok: scope ids queued for GATE H's census (scope-hammer) — an exhausted scope never blocks the round, it proposes."
2730
+ },
2731
+ "green_scopes": {
2732
+ "type": "array",
2733
+ "items": {
2734
+ "type": "string"
2735
+ },
2736
+ "description": "gate_h | ok: scope ids that reached T0-green this round."
2737
+ }
2738
+ }
2292
2739
  }
2293
2740
  }
2294
2741
  }