shapeup-sdlc 1.6.2 → 1.7.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 (85) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/AGENTS.md +36 -103
  3. package/README.md +60 -67
  4. package/SECURITY.md +31 -16
  5. package/bin/init.mjs +24 -69
  6. package/commands/build.md +19 -0
  7. package/commands/eval.md +5 -4
  8. package/commands/scopes.md +5 -4
  9. package/commands/shape.md +1 -1
  10. package/commands/ship.md +37 -4
  11. package/hooks/anti-rationalization.mjs +7 -13
  12. package/hooks/compact-snapshot.mjs +5 -5
  13. package/hooks/gate-deadline.mjs +12 -16
  14. package/hooks/gate-intake.mjs +13 -14
  15. package/hooks/gate-l2.mjs +3 -3
  16. package/hooks/gate-zerowork.mjs +97 -16
  17. package/hooks/lib/decision.mjs +21 -13
  18. package/hooks/safety-spine.mjs +1 -1
  19. package/hooks/sandbox-guard.mjs +83 -47
  20. package/hooks/session-rehydrate.mjs +8 -9
  21. package/hooks/slop-cleaner.mjs +1 -1
  22. package/oracles/_shared.mjs +1 -1
  23. package/oracles/http-oracle.mjs +1 -1
  24. package/oracles/index.mjs +1 -1
  25. package/oracles/process-oracle.mjs +1 -1
  26. package/oracles/snapshot-oracle.mjs +1 -1
  27. package/oracles/test-oracle.mjs +1 -1
  28. package/package.json +3 -7
  29. package/skills/ba-pitch-analyzer/SKILL.md +20 -15
  30. package/skills/ba-pitch-analyzer/references/doc-schemas.md +1 -1
  31. package/skills/ba-pitch-analyzer/references/task-generation.md +6 -6
  32. package/skills/ba-pitch-analyzer/references/test-surface.md +1 -1
  33. package/skills/ba-pitch-analyzer/scripts/board-derive.mjs +5 -5
  34. package/skills/ba-pitch-analyzer/scripts/spec-lint.mjs +2 -2
  35. package/skills/coach/SKILL.md +3 -3
  36. package/skills/orient/SKILL.md +2 -1
  37. package/skills/qa-edge-hunter/SKILL.md +9 -39
  38. package/skills/scope-architect/SKILL.md +17 -12
  39. package/skills/scope-hammer/SKILL.md +5 -5
  40. package/skills/shapeup/SKILL.md +3 -3
  41. package/skills/shapeup/resources/context-compaction.md +4 -3
  42. package/skills/solution-architect/SKILL.md +18 -11
  43. package/skills/spec-evaluator/SKILL.md +12 -5
  44. package/skills/spec-evaluator/references/dimensions/_registry.md +2 -2
  45. package/skills/spec-evaluator/references/dimensions/completeness.md +1 -1
  46. package/skills/spec-evaluator/references/dimensions/integration.md +98 -67
  47. package/skills/spec-evaluator/references/dimensions/spec-conformance.md +4 -4
  48. package/skills/spec-evaluator/references/dimensions/test-surface-conformance.md +1 -1
  49. package/skills/spec-evaluator/references/probing.md +42 -23
  50. package/skills/spec-evaluator/references/report-schema.md +2 -2
  51. package/skills/spec-evaluator/scripts/verdict-ledger.mjs +1 -1
  52. package/skills/task-executor/SKILL.md +25 -12
  53. package/skills/tech-lead/SKILL.md +115 -438
  54. package/skills/tech-lead/references/delegation.md +38 -31
  55. package/skills/tech-lead/references/gates.md +75 -33
  56. package/skills/tech-lead/references/hard-rules.md +34 -0
  57. package/skills/tech-lead/references/invocation.md +2 -2
  58. package/skills/tech-lead/references/ledger-schema.md +17 -18
  59. package/skills/tech-lead/references/round-protocol.md +77 -27
  60. package/skills/tech-lead/references/state-model.md +1 -1
  61. package/skills/tech-lead/schemas/domain.schema.json +320 -148
  62. package/skills/tech-lead/schemas/gate-answers.schema.json +2 -2
  63. package/skills/tech-lead/schemas/work-result.schema.json +55 -17
  64. package/skills/tech-lead/scripts/aegis-digest.mjs +1 -1
  65. package/skills/tech-lead/scripts/budget-check.mjs +7 -13
  66. package/skills/tech-lead/scripts/compile-order.mjs +69 -22
  67. package/skills/tech-lead/scripts/fit-check.mjs +10 -10
  68. package/skills/tech-lead/scripts/gate-answers.mjs +11 -12
  69. package/skills/tech-lead/scripts/hill-derive.mjs +133 -0
  70. package/skills/tech-lead/scripts/ingest-result.mjs +7 -19
  71. package/skills/tech-lead/scripts/init-run.mjs +60 -18
  72. package/skills/tech-lead/scripts/lib/argv.mjs +1 -1
  73. package/skills/tech-lead/scripts/lib/contract-md.mjs +10 -10
  74. package/skills/tech-lead/scripts/lib/is-main.mjs +11 -12
  75. package/skills/tech-lead/scripts/lib/paths.mjs +15 -13
  76. package/skills/tech-lead/scripts/resume-state.mjs +387 -0
  77. package/skills/tech-lead/scripts/run-snapshot.mjs +1 -6
  78. package/skills/tech-lead/scripts/run-workflow.mjs +381 -0
  79. package/skills/tech-lead/scripts/ship-report.mjs +33 -2
  80. package/skills/tech-lead/scripts/stats.mjs +9 -10
  81. package/skills/tech-lead/scripts/t0-verify.mjs +4 -4
  82. package/skills/tech-lead/scripts/trace-lint.mjs +2 -2
  83. package/skills/tech-lead/workflows/shapeup-run.js +911 -0
  84. package/skills/translator/SKILL.md +2 -2
  85. package/skills/advisor-protocol/SKILL.md +0 -171
@@ -24,16 +24,16 @@ Invoked as `--order <path>`. Fields you may rely on (absent = unknown; surface i
24
24
 
25
25
  | Field | What it is |
26
26
  |---|---|
27
- | `operation` | `analyze` (pitch → full spec tree + board) · `generate-board` (regenerate the LOCAL board from the committed spec) · `reconcile` (fold discovered-ledger items into the board + UC invariants) · `retrofit-surface` (append `## Test Surface` to a pre-surface spec) · `coverage` (extract atomic requirement clauses → the SHARED `requirements.md` registry) |
27
+ | `operation` | `analyze` (pitch → full spec tree + board) · `reconcile` (fold discovered-ledger items into the board + UC invariants) · `retrofit-surface` (append `## Test Surface` to a pre-surface spec) · `coverage` (extract atomic requirement clauses → the SHARED `requirements.md` registry) |
28
28
  | `payload.pitch` | The pitch/PRD path (analyze) |
29
29
  | `payload.requirements` | (coverage) the REQ source to extract atomic clauses from — pitch / a customer-requirements doc / the use-case bodies. Absent → default to the pitch and record the choice in `assumptions[]` |
30
- | `payload.lens` | `lite` \| `standard` \| `cross-context`. Absent → judge it: LITE for ≤2-week appetite, no third-party, ≤3 user-facing actions; STANDARD for multi-team, third-party, or bigger appetite; genuinely unclear → one binary question / one `escalates[]` entry |
30
+ | `payload.lens` | `lite` \| `standard` \| `cross-context`. Absent → judge it: LITE for ≤2-week appetite, no third-party, ≤3 user-facing actions; STANDARD for multi-team, third-party, or bigger appetite; genuinely unclear → one binary question, or `status: "escalated"` with the question in `deviations[]` |
31
31
  | `payload.orient_dir` | The Scout's artifacts — `code-surface.md` IS your codebase map (do not re-scan), `discovered-seed.md` seeds task gen, `spike-*.md` feeds feasibility |
32
32
  | `payload.spec_folder` / `payload.feature` | Where the committed tree lives / the slug |
33
33
  | `payload.discovered_ledger` | (reconcile) the ledger whose raw `[+]`/`~` lines you fold in |
34
34
  | `payload.kb_rules_path` | Team guidelines (read if present) — steering, never spec |
35
- | `substrate.allowed / append_only / frozen` | Your write contract for THIS operation. The old frozen-zone prose is now data the sandbox hook enforces: respect it, and when an operation genuinely needs a file outside it, ESCALATE — never widen |
36
- | `interaction.pause_gates` | Caller policy. `true` (standalone default): pause at the phase checkpoints below, max 2 questions each. `false`: run straight through, surfacing questions as `assumptions[]`/`escalates[]` instead |
35
+ | `substrate.allowed / append_only / frozen` | Your write contract for THIS operation. The old frozen-zone prose is now data the sandbox hook enforces (reading the order's envelope): respect it, and when an operation genuinely needs a file outside it, ESCALATE — never widen |
36
+ | `interaction.pause_gates` | Caller policy. `true` (standalone default): pause at the phase checkpoints below, max 2 questions each. `false`: run straight through, surfacing questions as `assumptions[]` (or `deviations[]` when they block) instead |
37
37
 
38
38
  ---
39
39
 
@@ -94,15 +94,13 @@ second path to green.
94
94
 
95
95
  ---
96
96
 
97
- ## The other four operations — same craft, different payload + whitelist
97
+ ## The other three operations — same craft, different payload + whitelist
98
98
 
99
99
  | Operation | Essence | Never |
100
100
  |---|---|---|
101
- | `generate-board` | Re-derive the full task set fresh from the committed `usecases/` + `domain-model.md` (+ scope contracts if present tasks respect their substrates). Numbering restarts at TASK-001. Initialize `status` from committed mechanical truth at SCOPE granularity (a scope with hill shard FINISHED its tasks start `done`) never join on task id; ids renumber per machine, the scope is the stable key. Then board-derive `--write` + regenerate scope-summary.md | touch the committed spec docs (frozen in your substrate) |
102
- | `reconcile` | Verify `ledger.feature == payload.feature` (mismatch → STOP). Map each `[+]` Keep item → its owning UC; new task continues numbering (never renumber); `~`/Cut → synthesis "Hammered Out" row, no file. A Keep item asserting a new invariant → APPEND `[INV-NN]` + TS-INV row to that UC (append-only sections in your substrate). A new actor/action with no UC → `escalates[]` (spec-ambiguity): spawning a UC mid-cycle is silent re-shaping, the PO decides. Finish with board-derive (appetite overflow → report) + spec-lint | re-run phases 1–5; edit UC Steps; resolve the appetite HAMMER yourself |
101
+ | `reconcile` | Verify `ledger.feature == payload.feature` (mismatch STOP). Map each `[+]` Keep item its owning UC; new task continues numbering (never renumber); `~`/Cut synthesis "Hammered Out" row, no file. A Keep item asserting a new invariant → APPEND `[INV-NN]` + TS-INV row to that UC (append-only sections in your substrate). A new actor/action with no UC → `status: "escalated"` + a `deviations[]` spec-ambiguity entry: spawning a UC mid-cycle is silent re-shaping, the PO decides. Finish with board-derive (appetite overflow → report) + spec-lint | re-run phases 1–5; edit UC Steps; resolve the appetite HAMMER yourself |
103
102
  | `retrofit-surface` | Append `## Test Surface` (derived rows only, after Error Cases) to each UC of a pre-surface spec; an all-sources-empty UC gets the explicit empty-sources line | touch anything else — append-only substrate |
104
- | `coverage` | Extract **atomic** customer requirement clauses from `payload.requirements` (default: the pitch) and write the SHARED `shapeup/<slug>/requirements.md` registry: one `\| REQ-id \| clause (verbatim) \| source \| status \| note \|` row per clause. Split compound sentences into one testable clause each — the audit's dropped clauses ("side-step OR lure enemies into traps", "low-res world textures") were *lost inside* a bigger sentence. **Assign REQ-ids ONCE and freeze them** (they behave like scope_id, never TASK-NNN — every `covers:` link rots otherwise): re-running, append new clauses with fresh ids, mark a removed clause `CUT (PO-approved)`, never renumber or delete. Status starts `covered` (a live requirement); only the PO sets `CUT`. The REQ source itself is frozen — the registry is a separate derived file | edit the REQ source; renumber existing REQ-ids; delete a dropped clause instead of marking it CUT; invent a requirement not in the source |
105
-
103
+ | `coverage` | Extract **atomic** customer requirement clauses from `payload.requirements` (default: the pitch) and write the SHARED `shapeup/<slug>/requirements.md` registry: one `\| REQ-id \| clause (verbatim) \| source \| status \| note \|` row per clause. Split compound sentences into one testable clause each — a clause lost *inside* a bigger sentence is a requirement nothing can be traced to. **Assign REQ-ids ONCE and freeze them** (they behave like scope_id, never TASK-NNN — every `covers:` link rots otherwise): re-running, append new clauses with fresh ids, mark a removed clause `CUT (PO-approved)`, never renumber or delete. Status starts `covered` (a live requirement); only the PO sets `CUT`. The REQ source itself is frozen — the registry is a separate derived file | edit the REQ source; renumber existing REQ-ids; delete a dropped clause instead of marking it CUT; invent a requirement not in the source |
106
104
  ---
107
105
 
108
106
  ## Anti-rationalization table
@@ -115,12 +113,19 @@ second path to green.
115
113
  | "My output looks complete, score it 92" | You don't grade yourself. spec-lint reports facts; the judge judges. |
116
114
  | "The appetite overflow is small, drop a nice-to-have myself" | Overflow is a HAMMER gate for the caller. You report the fact and the candidate cuts. |
117
115
  | "Re-scanning the codebase is safer than trusting orient" | code-surface.md IS the map. Re-scanning burns tokens and forks the truth. |
118
- | "unlocks is quick to fill in by hand" | Hand-authored unlocks produced 10 asymmetric edges (KB-BA-001). board-derive computes it. |
116
+ | "unlocks is quick to fill in by hand" | Hand-authored unlocks drift asymmetric observed, repeatedly. board-derive computes it. |
119
117
 
120
118
  ---
121
119
 
122
120
  ## Output contract — the WorkResult
123
121
 
122
+ **Escalation rule.** If you return `status: "escalated"`, the **first** entry in `deviations[]`
123
+ must be the blocker: one specific, answerable question plus the context needed to answer it.
124
+ Nothing else in the envelope carries it — there is no `escalates[]` field — so a vague entry, or
125
+ the question buried under other notes, reaches the human as "something went wrong" and costs a
126
+ round. Write it so someone without your context can answer it in one reply.
127
+
128
+
124
129
  Domain artifacts land inside your substrate (the committed spec tree + the LOCAL board). Then
125
130
  write `.shapeup/<slug>/results/<order-suffix>.json`:
126
131
 
@@ -129,9 +134,8 @@ write `.shapeup/<slug>/results/<order-suffix>.json`:
129
134
  "schema_version": 1, "order_id": "<copied>", "worker": "ba-pitch-analyzer",
130
135
  "status": "done | partial | escalated",
131
136
  "artifacts": ["shapeup/<slug>/spec/domain-model.md", "…"],
132
- "escalates": [ { "kind": "spec-ambiguity", "question": "New actor 'auditor' has no UC — add UC-07 or cut?" } ],
133
137
  "assumptions": ["lens=standard — third-party PSP present"],
134
- "deviations": [],
138
+ "deviations": [ "ESCALATE spec-ambiguity — New actor 'auditor' has no UC — add UC-07 or cut?" ],
135
139
  "discoveries": [ { "marker": "+", "line": "appetite overflow 12h — candidate cuts: TASK-014, TASK-017" } ]
136
140
  }
137
141
  ```
@@ -168,8 +172,9 @@ status flips for built work (ingest's job), scope contracts (scope-architect's),
168
172
  ```
169
173
 
170
174
  Standalone keeps exactly two flags: the pitch input and `--lens`. Every retired flag is now
171
- caller context: `--tasks-only` → a generate-board order, `--from-discovered` → a reconcile
172
- order, `--surface-only` → a retrofit-surface order, `--remap`/`--split` → scope-architect
173
- orders, `--status` → read `spec-lint.mjs`/`board-derive.mjs` output (zero LLM tokens),
175
+ caller context: `--tasks-only`/`--from-discovered` → a reconcile order, `--surface-only` → a
176
+ retrofit-surface order, `--coverage` → a coverage order, `--remap`/`--split` → a
177
+ scope-architect `map-scopes` order,
178
+ `--status` → read `spec-lint.mjs`/`board-derive.mjs` output (zero LLM tokens),
174
179
  `--auto`/`--skip-gate*` → `interaction.pause_gates`, `--upgrade` → an analyze order with the
175
180
  standard lens over an existing lite tree (reconciliation pass: extend, never overwrite Steps).
@@ -152,7 +152,7 @@ status: draft | ready
152
152
  > tasks carry `use_case_refs` (LOCAL, per-machine board); reverse lookup is always computed
153
153
  > live (synthesis S-01, audit coverage) — never stored on the committed UC. Rule: **never
154
154
  > declare a bidirectional field across the committed/local boundary** — task IDs renumber
155
- > per machine (generate-board bootstrap), so a stored back-link is wrong on every machine
155
+ > per machine (the board is regenerated locally), so a stored back-link is wrong on every machine
156
156
  > but one. Tolerate (ignore) the field on pre-v3.3 specs; do not emit or update it.
157
157
 
158
158
  Required sections: Summary (1 sentence), Preconditions, Input (TS interface), Steps
@@ -361,12 +361,12 @@ Slug rules:
361
361
  ## Link-Field Integrity (v3.3)
362
362
 
363
363
  **`depends_on` is the single authoritative edge; `unlocks` is derived, never hand-authored.**
364
- On EVERY board write — analyze, generate-board, reconcile — recompute (via
364
+ On EVERY board write — analyze, reconcile — recompute (via
365
365
  `board-derive.mjs --write`, never by hand) the `unlocks` field of every task on the board as the
366
366
  exact inverse of the full board's `depends_on` graph, then write it. Adding one task that
367
367
  declares `depends_on: [TASK-007]` therefore rewrites `TASK-007.unlocks` in the same pass.
368
368
  "Write both sides when you remember" allows drift; a derived field cannot drift
369
- (island-escape shipped 10 asymmetric edges under the old rule). Audit L3-06 fails the board
369
+ (the old rule shipped boards carrying ten asymmetric edges). Audit L3-06 fails the board
370
370
  on any asymmetric edge.
371
371
 
372
372
  The same discipline has a boundary: it applies only when both sides live in the SAME root
@@ -584,12 +584,12 @@ This is scope hammering at the gate boundary — the overflow is surfaced, never
584
584
  **Naming note:** the discovery ledger's "scope" sections (above) predate the formal Scope
585
585
  Contract artifact below and name the same thing — a `discovery/ledger.md` scope heading MUST
586
586
  match an existing `scope_id` from a `scopes/<scope-id>.md` contract, or (a Keep item
587
- introducing new flow) become the seed for a new one via a scope-architect remap order. One concept, two touch points.
587
+ introducing new flow) become the seed for a new one via a scope-architect `map-scopes` order. One concept, two touch points.
588
588
 
589
589
  **Import/flow slicing (PA1 countermeasure).** Build a lightweight import graph over the task
590
590
  board's touched files: for each file a task writes, note what it imports and what imports it
591
591
  (grep for `import .* from ['"](\.\.?/[^'"]+)`-class patterns is sufficient — a full AST parser
592
- is an optimization, not a prerequisite, per design spec R6). Two files are in the same flow if
592
+ is an optimization, not a prerequisite). Two files are in the same flow if
593
593
  they sit on one call chain: a UI component → the API route it calls → the use case that route
594
594
  invokes → the repository that use case depends on. Group tasks by flow, not by which top-level
595
595
  directory their file lives in. A scope whose `allowed_file_substrate` is entirely `apps/web/**`
@@ -614,8 +614,8 @@ or entirely `apps/api/**` with no cross-layer flow is the PA1 failure mode — r
614
614
  }
615
615
  ```
616
616
  `hill_phase` is always written `UPHILL_UNKNOWN` at generation time — it is derived later from
617
- mechanical T0/T1/seesaw facts, never declared by `ba` (design spec DD-10). `superseded_by` stays
618
- `null` until a scope-architect split-scope order retires this contract in favor of its replacements.
617
+ mechanical T0/T1/seesaw facts, never declared by `ba`. `superseded_by` stays
618
+ `null` until a scope-architect `map-scopes` order retires this contract in favor of its replacements.
619
619
 
620
620
  **PA2 size lint:** a scope whose `allowed_file_substrate` glob set resolves to more than ~15
621
621
  files gets a ⚠️ from spec-lint PA2 (hard-cap configurable via pitch frontmatter `scope_size_cap`, default
@@ -7,7 +7,7 @@ source row below, it does not belong here (it belongs to exploratory QA, post-bu
7
7
  **Why it exists.** `spec-conformance` grades the AC that were written; it cannot expand
8
8
  them. The Test Surface is the systematic expansion — boundary values, negative cases,
9
9
  error-code coverage — done once at spec time so the evaluator (`test-surface-conformance`
10
- dimension) can probe it. Division of labor settled at the QA meeting (2026-06-11):
10
+ dimension) can probe it. Division of labor, a settled design decision:
11
11
  derivable tests = BA + Evaluator (this file); exploratory edges = `/qa-edge-hunter`,
12
12
  post-PASS, on the running app.
13
13
 
@@ -2,7 +2,7 @@
2
2
  // Board derivation (pure-skill architecture v1.0, plan §8.2).
3
3
  //
4
4
  // The mechanical half of the old ba-pitch-analyzer Phase 7b + v3.3 link-field rules — pure
5
- // graph math a model should never re-derive (and could get wrong: KB-BA-001's 10 asymmetric
5
+ // graph math a model should never re-derive (and gets wrong: hand-authored inverses drift asymmetric
6
6
  // edges came from hand-authored `unlocks`):
7
7
  //
8
8
  // • `unlocks` = the depends_on inverse, recomputed over the WHOLE board (--write persists
@@ -28,9 +28,9 @@ import { readAllContracts, splitFrontmatter, SCOPE_CONTRACT } from "../../tech-l
28
28
  /**
29
29
  * Read a list field from a frontmatter string, inline `[a, b]` or YAML block sequence alike.
30
30
  *
31
- * HD-004 — the SECOND instance of HD-003, in a second parser. This repo had two hand-rolled
31
+ * The SECOND instance of the same defect, in a second parser. This repo had two hand-rolled
32
32
  * frontmatter readers: `contract-md.mjs` for the committed contracts, and this one for the board.
33
- * HD-003 was fixed in the first, and this one still silently dropped a block sequence — so a board
33
+ * It was fixed in the first, and this one still silently dropped a block sequence — so a board
34
34
  * written as
35
35
  * use_case_refs:
36
36
  * - UC-01
@@ -163,8 +163,8 @@ export function driftCheck(tasks, scopes) {
163
163
  * board, deleted before CLI output).
164
164
  */
165
165
  export function derive({ cwd, slug, appetiteHours = null }) {
166
- const tasksDir = tasksDir(cwd, slug);
167
- const tasks = parseBoard(tasksDir);
166
+ const boardDir = tasksDir(cwd, slug);
167
+ const tasks = parseBoard(boardDir);
168
168
  const unlocks = deriveUnlocks(tasks);
169
169
  const keepHours = tasks.filter((t) => t.status !== "cut").reduce((a, t) => a + t.hours, 0);
170
170
  const packages = {};
@@ -159,7 +159,7 @@ export function lintStructure({ specDir, tasks }) {
159
159
  }
160
160
  }
161
161
  }
162
- // Task frontmatter + graph integrity (edge symmetry = the mechanized KB-BA-001 check).
162
+ // Task frontmatter + graph integrity (edge symmetry the hand-authored-unlocks drift, mechanized).
163
163
  const ids = new Set(tasks.map((t) => t.id));
164
164
  const derived = deriveUnlocks(tasks);
165
165
  for (const t of tasks) {
@@ -203,7 +203,7 @@ export function lint({ cwd, slug }) {
203
203
  const tasks = parseBoard(tasksDir(cwd, slug));
204
204
  const repoFiles = walkFiles(cwd);
205
205
  const findings = [
206
- // HD-001. A contract whose table this parser cannot see reads as a contract that declared no
206
+ // A contract whose table this parser cannot see reads as a contract that declared no
207
207
  // table, and every rule below then passes for the part it could not read. Loud, not empty.
208
208
  ...contracts
209
209
  .map(({ contract, path }) => ({ reason: unreadableReason(contract), scope: contract.scope_id || path }))
@@ -41,8 +41,8 @@ they are the **complete** set of categories the gate may offer:
41
41
 
42
42
  | Category | File | The worker reads it at | Good for |
43
43
  |----------|------|------------------------|----------|
44
- | `task-executor` | `shapeup/knowledge-base/task-executor.md` | Phase 1 (Context Load) | implementation discipline, code style, surgical-change habits, recurring over/under-engineering |
45
- | `ba-pitch-analyzer` | `shapeup/knowledge-base/ba-pitch-analyzer.md` | Phase 1 (Ingest & Scan) | scoping, task decomposition, DDD/spec habits, missed test-surface patterns |
44
+ | `task-executor` | `shapeup/knowledge-base/task-executor.md` | PLAN (context load) | implementation discipline, code style, surgical-change habits, recurring over/under-engineering |
45
+ | `ba-pitch-analyzer` | `shapeup/knowledge-base/ba-pitch-analyzer.md` | Phase 1 (INGEST) | scoping, task decomposition, DDD/spec habits, missed test-surface patterns |
46
46
  | `qa-edge-hunter` | `shapeup/knowledge-base/qa-edge-hunter.md` | Phase Q1 (Charter Map) | recurring edge classes, lenses that keep finding bugs, areas worth probing |
47
47
 
48
48
  **Not coachable.** `spec-evaluator` is deliberately excluded — the harness has a **single-judge**
@@ -54,7 +54,7 @@ say so plainly — do **not** force-fit it into a coachable category.
54
54
  **Harness defect ≠ worker steering.** When the feedback's root cause is the *mechanism itself* —
55
55
  a hook that fail-opens, a gate that reads the wrong file, two skill contracts that contradict
56
56
  each other — no amount of steering a worker fixes it, and filing it as a KB rule misdiagnoses a
57
- defect as a habit (island-escape's KB-BA-002 filed an orchestration/hook defect as BA guidance,
57
+ defect as a habit (a real case: an orchestration/hook defect filed as BA guidance,
58
58
  on a premise the skill contracts contradict). That is what the `harness-defect` category below is
59
59
  for: the coach records it in the committed defect register as a drafted **raw idea** for the
60
60
  Betting Table — the debt-free path ("remaining findings + new feedback → new raw idea") — and it
@@ -42,7 +42,8 @@ from `tech-lead`; it never reads or writes a shared run-state file.
42
42
  ## Input contract (pure worker)
43
43
 
44
44
  Orchestrated, you are invoked as `--order <path>` (a WorkOrder): `payload.pitch` (the
45
- kicked-off pitch path), `payload.stack` (sweep hint), and `substrate.allowed` naming your one
45
+ kicked-off pitch path), `payload.stack` (sweep hint), `payload.spec_folder` (the SHARED spec
46
+ deliverable dir) and `payload.feature` (the run slug), plus `substrate.allowed` naming your one
46
47
  write surface — the orient output dir. Anything absent = unknown: confirm at GATE O-A
47
48
  (standalone) or report it in the result's `deviations`, never guess. Standalone, the
48
49
  `--pitch/--spec/--stack` flags below carry the same fields; the output dir derives from the
@@ -18,7 +18,7 @@ tech-lead: ... GATE L2 → EVAL → GATE L3 PASS ──► QA EDGE HUNT (you)
18
18
  GATE L3 FAIL → fix round (never reaches you — conformance first, edges after)
19
19
  ```
20
20
 
21
- **Division of labor (settled at the QA meeting, 2026-06-11):**
21
+ **Division of labor (a settled design decision, not a convention):**
22
22
 
23
23
  | Tier | Owner | When |
24
24
  |---|---|---|
@@ -36,11 +36,11 @@ tech-lead: ... GATE L2 → EVAL → GATE L3 PASS ──► QA EDGE HUNT (you)
36
36
  | Execute charters on the **running app** (session-based exploratory) | Read-only speculate from code ("this looks racy") — every finding needs a live repro |
37
37
  | Return each finding in the WorkResult's `discoveries[]`, **always `~`** | Promote `~` → must-have (PO/TL at SHIP S.0; severity-hint is advice, not a decision) |
38
38
  | Emit `qa/hunt-report.md` — charters run/cut, findings by lens | Render a verdict, score, or PASS/FAIL of any kind |
39
- | `--recheck`: re-probe ONLY items promoted+fixed after triage | Run a second full hunt in the same cycle; fix code; touch task files; keep run-state |
40
39
 
41
40
  Pure worker (harness rule: stateless workers, one stateful orchestrator). Its WorkOrder
42
41
  carries `payload.feature`, `payload.spec_folder`, `payload.eval_report`, `payload.app_url`,
43
- `payload.kb_rules_path` (+ read-only ledger path for covered-territory context); its write surface is
42
+ `payload.kb_rules_path`, and `payload.ledger` (the discovery ledger, READ-ONLY covered-territory
43
+ context so a hunt does not re-report what is already known); its write surface is
44
44
  `.shapeup/<feature>/qa/**` only. The Hunter never touches the discovery ledger itself —
45
45
  ingest appends its `discoveries[]` under a `## Discovered` section, preserving single-writer
46
46
  mechanically.
@@ -272,7 +272,6 @@ an order, the WorkResult envelope `.shapeup/<feature>/results/<order-suffix>.jso
272
272
 
273
273
  ```markdown
274
274
  # Hunt Report — [feature] (round [r], [date])
275
- mode: [full | degraded | recheck]
276
275
  charters: [run]/[approved] · session units spent: [n]
277
276
  out of bounds (excluded): […]
278
277
  hammered out at GATE Q1 (not hunted): […]
@@ -282,7 +281,8 @@ hammered out at GATE Q1 (not hunted): […]
282
281
  |---|---|---|---|
283
282
  | ① Boundary | C-01, C-05 | 2 | 0 |
284
283
  | … | | | |
285
- → details live in .shapeup/<feature>/discovery/ledger.md ## QA Edge Findings (round [r])
284
+ → details live in .shapeup/<feature>/discovery/ledger.md under the `## Discovered` section
285
+ ingest appends for this hunt's order
286
286
 
287
287
  ## Shaping-quality signal (advisory, for the PO — next cycle's input)
288
288
  - lens ⑤ findings ≈ No-gos written loosely (breach routes left open)
@@ -298,48 +298,18 @@ No verdict line exists in this file by design. The Hunter's last words:
298
298
 
299
299
  ---
300
300
 
301
- ## `--recheck` mode (after triage promoted + fixed items)
302
-
303
- ```
304
- Input: the promoted finding ids (from tech-lead) + the fix round's PASS EVAL report.
305
- Q0 : hard checks only (app up, new EVAL PASS); no soft check, no charter map.
306
- Hunt : re-run EXACTLY the recorded repro of each promoted finding — nothing else.
307
- fixed → discoveries[] entry `{ "marker": "~", "line": "[QA-NNN] ✦ fixed r[N], verified" }`
308
- (ingest annotates the ledger — annotate, never delete; the ledger is history)
309
- not fixed → `{ "marker": "~", "line": "[QA-NNN] ✦ NOT fixed r[N]", "repro": "<fresh evidence>" }`
310
- back to triage.
311
- Report: append a `## Recheck (round [r])` section to .shapeup/<feature>/qa/hunt-report.md.
312
- NEVER a second full hunt in the same cycle — new edges found while rechecking are
313
- recorded `~` like any finding and wait for triage; they don't restart the loop.
314
- ```
315
-
316
- ---
317
-
318
301
  ## Invocation
319
302
 
320
303
  ```bash
321
- # Orchestrated (how tech-lead calls it after first PASS) — the canonical form:
322
- # compile-order --operation hunt --slug checkout-vnpay --worker qa-edge-hunter \
323
- # --payload '{"eval_report": "…/EVAL-FEATURE-checkout-vnpay.md", "app_url": "http://localhost:3000"}'
324
- /qa-edge-hunter --order .shapeup/checkout-vnpay/orders/hunt.json
325
-
326
- # Standalone flags (the preamble shim compiles the same envelope)
327
- /qa-edge-hunter --feature checkout-vnpay --spec shapeup/checkout-vnpay/spec/ \
328
- --eval .shapeup/checkout-vnpay/evaluation/EVAL-FEATURE-checkout-vnpay.md \
329
- --ledger .shapeup/checkout-vnpay/discovery/ledger.md --app http://localhost:3000
330
-
331
- # Standalone (same arguments, human-invoked)
332
304
  /qa-edge-hunter --feature checkout-vnpay --spec shapeup/checkout-vnpay/spec/ --app http://localhost:3000
333
305
  # (--eval/--ledger default to the conventional paths under the LOCAL root .shapeup/<feature>/)
334
306
 
335
- # Recheck after triage promoted + fixed findings
336
- /qa-edge-hunter --recheck QA-001,QA-004 --feature checkout-vnpay --spec ... --app ...
337
-
338
- # Escape hatches
307
+ # Escape hatch
339
308
  --auto # skip GATE Q1 pause (charter list logged, not reviewed) — Q0 hard
340
309
  # checks and the out-of-bounds question are NEVER skipped
341
- --lenses-note # there is deliberately NO --lenses flag: the 6 lenses are fixed;
342
- # narrow by cutting charters at GATE Q1 (or --auto + report)
310
+
311
+ # There is deliberately NO --lenses flag: the 6 lenses are fixed; narrow by cutting
312
+ # charters at GATE Q1 (or --auto + report).
343
313
  ```
344
314
 
345
315
  ### Progress Markers
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: scope-architect
3
- description: "Use this skill to map, remap, or split the vertical scopes of a feature — Shape Up's \"map the scopes\" (step 8) as committed, mechanically enforceable contracts. Triggers on: \"map the scopes\", \"write the scope contracts\", \"scope contract\", \"remap this scope\", \"split this stuck scope\", \"the discovered tasks don't fit any scope\", \"re-slice the substrate\", or a tech-lead --order dispatch (operations map-scopes | remap | split-scope). Writes the committed scopes/*.md contracts by import-graph slicing along business flow, with write-whitelist substrates and e2e fixtures. NOT for decomposing a pitch into tasks (ba-pitch-analyzer) or cutting scope at ship time (scope-hammer)."
3
+ description: "Use this skill to map the vertical scopes of a feature — Shape Up's \"map the scopes\" (step 8) as committed, mechanically enforceable contracts. Triggers on: \"map the scopes\", \"write the scope contracts\", \"scope contract\", \"the discovered tasks don't fit any scope\", \"re-slice the substrate\" (operations map-scopes). Writes the committed scopes/*.md contracts by import-graph slicing along business flow, with write-whitelist substrates and e2e fixtures. NOT for decomposing a pitch into tasks (ba-pitch-analyzer) or cutting scope at ship time (scope-hammer)."
4
4
  ---
5
5
 
6
6
  # Scope Architect (pure worker v1.0)
@@ -19,11 +19,9 @@ anti-rationalization table.
19
19
 
20
20
  | Field | What it is |
21
21
  |---|---|
22
- | `operation` | `map-scopes` (first slicing after the board exists) · `remap` (fold discovered items into scope contracts) · `split-scope` (re-slice one stuck scope) |
22
+ | `operation` | `map-scopes` — the only operation this skill has. It covers first slicing after the board exists, folding discovered items in, and re-slicing a stuck scope; the payload says which of those you are doing |
23
23
  | `payload.feature` / `payload.spec_folder` | Slug + committed spec (read ux-behavior.md for manifests; usecases for flows) |
24
24
  | `payload.tasks[]` | The board's tasks with their touched files — the slicing input |
25
- | `payload.discovered_ledger` | (remap) items that fit no existing substrate |
26
- | `payload.scope_id` | (split-scope) the stuck scope (`rounds_at_position ≥ 3`, or an approved substrate-expansion) |
27
25
  | `substrate.allowed` | `scopes/*.md` + `scope-board.md` — your ONLY write surface |
28
26
 
29
27
  ## Core process
@@ -59,17 +57,17 @@ anti-rationalization table.
59
57
  a fixture for unbuilt behavior
60
58
  hill_phase: "UPHILL_UNKNOWN" — ALWAYS; phase is derived from
61
59
  T0/T1/seesaw facts later,
62
- never authored (DD-10)
60
+ never authored
63
61
  4 LINT node "${CLAUDE_PLUGIN_ROOT}/skills/ba-pitch-analyzer/scripts/spec-lint.mjs" --slug <slug>
64
62
  → PA1 (directory alignment), PA2 (>~15 files), DISJOINT (undeclared overlap).
65
63
  Fix reds by re-slicing, not by silencing.
66
64
  5 BOARD regenerate scope-board.md (scope_id, topology, task count, substrate size, lint)
67
65
  ```
68
66
 
69
- **remap:** a discovered item joins the nearest scope only if the flow matches (extend that
67
+ **Folding in a discovered item:** it joins the nearest scope only if the flow matches (extend that
70
68
  substrate minimally); otherwise propose a NEW scope — never silently widen an existing one.
71
- **split-scope:** re-run step 1 on just that scope's task+file set → N new contracts; mark the
72
- old one `superseded_by: [ids]` — never delete (branch and T0 history stay attributable).
69
+ **Re-slicing a stuck scope:** re-run step 1 on just that scope's task+file set → N new contracts;
70
+ mark the old one `superseded_by: [ids]` — never delete (branch and T0 history stay attributable).
73
71
 
74
72
  ## Anti-rationalization table
75
73
 
@@ -84,10 +82,17 @@ old one `superseded_by: [ids]` — never delete (branch and T0 history stay attr
84
82
 
85
83
  ## Output contract — the WorkResult
86
84
 
85
+ **Escalation rule.** If you return `status: "escalated"`, the **first** entry in `deviations[]`
86
+ must be the blocker: one specific, answerable question plus the context needed to answer it.
87
+ Nothing else in the envelope carries it — there is no `escalates[]` field — so a vague entry, or
88
+ the question buried under other notes, reaches the human as "something went wrong" and costs a
89
+ round. Write it so someone without your context can answer it in one reply.
90
+
91
+
87
92
  `scopes/*.md` + `scope-board.md` in your substrate, then
88
93
  `.shapeup/<slug>/results/<order-suffix>.json`: `status`, `artifacts[]` (the contracts
89
- written/superseded), `escalates[]` (e.g. a discovered item implying a new UC — the planner's
90
- territory), `deviations[]` (any lint warn left standing and why). You never touch task files,
94
+ written/superseded), `deviations[]` (e.g. a discovered item implying a new UC — the planner's
95
+ territory and any lint warn left standing, with why). You never touch task files,
91
96
  `tasks/_index.md`, spec docs, or run-state.
92
97
 
93
98
  ## Verification checklist
@@ -102,10 +107,10 @@ territory), `deviations[]` (any lint warn left standing and why). You never touc
102
107
  ## Invocation
103
108
 
104
109
  ```bash
105
- # Orchestrated — compile-order --operation map-scopes|remap|split-scope --worker scope-architect …
110
+ # Orchestrated — compile-order --operation map-scopes --worker scope-architect …
106
111
  /scope-architect --order .shapeup/checkout-vnpay/orders/map-scopes.json
107
112
 
108
113
  # Standalone shims (compile the same envelope)
109
114
  /scope-architect --map shapeup/checkout-vnpay/
110
- /scope-architect --split cart-creation shapeup/checkout-vnpay/
115
+ /scope-architect --map --split cart-creation shapeup/checkout-vnpay/ # re-slice one scope
111
116
  ```
@@ -11,7 +11,7 @@ live with today — not to a hypothetical perfect version. "Better than the base
11
11
  enough to ship; "not as good as I imagined" is not a reason to keep building.
12
12
 
13
13
  **Why this is its own skill, not tech-lead prose.** GATE H census pulls from several sources
14
- that accumulate over a whole run (QA findings, discovered-task ledger, advisor-protocol budget
14
+ that accumulate over a whole run (QA findings, discovered-task ledger, attempt-budget
15
15
  overflows, per-scope inner-breaker trips) — a genuinely separate synthesis step from
16
16
  orchestration. Splitting it out keeps `tech-lead` thin (it dispatches, this skill decides what
17
17
  survives the hammer) and gives the census/cut-list/verdict logic one owner instead of being
@@ -22,10 +22,10 @@ re-derived inline at every SHIP.
22
22
  ## When this fires
23
23
 
24
24
  ```
25
- 1. All scopes reach FINISHED on the hill (design spec B2) → normal stop, post-QA-hunt.
25
+ 1. All scopes reach FINISHED on the hill → normal stop, post-QA-hunt.
26
26
  2. Outer circuit breaker trips: round_budget reaches 0 with scopes still uphill/downhill.
27
27
  3. Inner circuit breaker trips: a scope's attempt_budget (default 5) is exhausted without a
28
- T0-green result → queued as a hammer PROPOSAL (design spec Blueprint A `hammer_proposals`),
28
+ T0-green result → queued as a hammer PROPOSAL (`hammer_proposals`),
29
29
  judged here rather than immediately blocking the round.
30
30
  ```
31
31
  Any of the three routes to this skill; the difference is only what's in the census (case 1 has
@@ -63,7 +63,7 @@ H0.1 Unresolved scopes (breaker cases only):
63
63
  with the T0 failure that stalled them (from the last red t0/verdicts/*.json)
64
64
  H0.2 QA findings (qa-edge-hunter's hunt-report.md, when present) — all `~` by default.
65
65
  H0.3 Discovered-task ledger entries still open (discovery/ledger.md, `[+]`/`~` unresolved).
66
- H0.4 advisor-protocol budget-overflow flags (auto-resolved ESCALATEs logged for GATE H review).
66
+ H0.4 Attempt-budget hammer proposals (scopes that exhausted their T0 attempts during BUILD).
67
67
  H0.5 Classify every item: MUST-HAVE (the pitch's core problem is unsolved without it) vs
68
68
  NICE-TO-HAVE (`~`, improves but doesn't block the core promise). Default to NICE-TO-HAVE
69
69
  unless the item traces directly to a pitch boundary or a scope's business_goal — a
@@ -87,7 +87,7 @@ customer suffers through today), never to a perfect ideal.
87
87
 
88
88
  ```
89
89
  H1.1 Resolve the baseline: shapeup/<slug>/shaping/baseline.md if present (written at
90
- shaping time, design spec Blueprint F — first-class, not a pitch footnote). Absent →
90
+ shaping time — first-class, not a pitch footnote). Absent →
91
91
  degrade honestly: read the pitch's problem statement as the implicit baseline and flag
92
92
  "baseline not first-class — comparison is approximate" in the report. Do not invent one.
93
93
  H1.2 For each MUST-HAVE item from H0: "with this item cut/carried, is the shipped product
@@ -33,7 +33,7 @@ This skill lazily loads detail from resource files. **Read the relevant resource
33
33
  | `/shapeup breadboard-reflection` | `resources/breadboard-reflection.md` |
34
34
  | `/shapeup full` | Read `resources/shaping.md` first, then `resources/breadboarding.md` before B-phases, and `resources/context-compaction.md` for the run digest |
35
35
 
36
- > Resource paths are relative to this skill's directory. In Claude Code: `.claude/skills/shapeup/resources/`.
36
+ > Resource paths are relative to this skill's directory (`resources/` beside this SKILL.md, wherever the plugin is installed).
37
37
  > Multi-gate runs maintain a derived **decision digest** so each gate reads a compact slice instead of full prose — see `resources/context-compaction.md` and the "Run Workspace & Digest" section below.
38
38
 
39
39
  ---
@@ -374,8 +374,8 @@ Two roots, separated by artifact **nature** (full design →
374
374
  - **Shared** root `shapeup/[slug]/` = what the team contributes to
375
375
  (source + deliverable). **Local** root `.shapeup/[slug]/` = per-run scratch,
376
376
  hidden and fully gitignorable. Add **one** line to `.gitignore`: `.shapeup/`.
377
- No carve-out needed — the one committed report surface, the harvest feed
378
- `shapeup/metrics.jsonl`, lives in the shared root.
377
+ No carve-out needed — everything machine-written at run time, including the
378
+ harvest shards `.shapeup/metrics/*.jsonl`, lives under the local root.
379
379
  - `digest.md` is the run's derived decision context (the 4-field, two-zone read
380
380
  model the gates consume). It is **never** the source of truth and never crosses
381
381
  a skill boundary — `ba-pitch-analyzer` reads `pitch.md`/`shaping.md`, never the
@@ -182,9 +182,10 @@ Everything keys off the feature `<slug>`. Two roots, split by **who needs it**:
182
182
  - The **local** root `.shapeup/[slug]/` is per-run scratch + reports — hidden,
183
183
  fully gitignorable, dies with the run. It is derived from the same `<slug>`.
184
184
  - `.gitignore`: one line — `.shapeup/`. The whole local root is ignored; no
185
- carve-out is needed because the one committed report surface, the harvested
186
- signal feed `shapeup/metrics.jsonl` (written by the tech-lead at SHIP,
187
- fact-only; see the tech-lead skill's SHIP step), lives in the **shared** root.
185
+ carve-out is needed because everything machine-written at run time lives there,
186
+ including the harvested signal shards `.shapeup/metrics/*.jsonl` (written by
187
+ the tech-lead at SHIP, fact-only; see the tech-lead skill's SHIP step). The
188
+ committed report surface is the frozen `shapeup/[slug]/REPORT.md`.
188
189
  - All paths are **project-relative, resolved from the project root / cwd.** Never
189
190
  `/mnt/...` — that is the claude.ai authoring sandbox and dies in a user repo.
190
191
 
@@ -7,9 +7,9 @@ description: "Use this skill to design how each use-case's engine integrates int
7
7
 
8
8
  **Design the seam, or the engine ships orphaned.**
9
9
 
10
- The audit that motivated this skill found a 631-line asset pipeline with 26 passing tests and
11
- **zero call sites** in the app's composition root — built, green, and unreachable from the
12
- running application. Five more scopes had engines never wired to a player. This skill closes that
10
+ This skill exists because a sizeable engine module can be built, fully tested, and still have
11
+ **zero call sites** in the app's composition root — green, and unreachable from the
12
+ running application; observed across multiple scopes in one run, not theorized. It closes that
13
13
  hole at the front: before the scopes are sliced, it designs a committed **wiring map**
14
14
  (`shapeup/<slug>/wiring-map.md`) that names, for every use case, the chain from the
15
15
  engine module to a player-visible affordance — including **how** and **where** the engine attaches
@@ -29,7 +29,7 @@ Two payoffs, one artifact:
29
29
  proving.
30
30
  - **The slicer gets its integration seam up front.** Declaring each attachment *before*
31
31
  `scope-architect` runs supplies the missing input behind the round-1 substrate-expansion
32
- escalations — the four identical "declined by precedent" stalls. (This skill *front-loads* the
32
+ escalations — repeated identical "declined by precedent" stalls. (This skill *front-loads* the
33
33
  seam; it does not enforce the re-slice rule — that's a separate change.)
34
34
 
35
35
  You are the **sole writer** of the wiring map, written **directly** (the same authority
@@ -73,14 +73,14 @@ guessed `main.js` would make the later oracle certify nothing.
73
73
  3 WRITE shapeup/<slug>/wiring-map.md (WiringMap): {schema_version:1, feature,
74
74
  entry_point (echo of the profile), entries[]}. One entry per use case. A UC whose
75
75
  engine has no attachment path is exactly the gap this artifact exists to surface —
76
- write the entry with the seam you INTEND and raise it in deviations[]/escalates[], so
76
+ write the entry with the seam you INTEND and raise it in deviations[], so
77
77
  the build knows the wiring it must close. Your craft ends here: WRITE, then return the
78
78
  WorkResult. You do not run trace-lint — the orchestrator runs it advisory at L1b.
79
79
  ```
80
80
 
81
81
  **No use case is exempt.** If a UC's engine genuinely has no player-facing seam (a pure
82
82
  background job), say so in `wiring_seam` and name the boot/cron/init attachment that starts it — a
83
- cron registration, a boot hook. "It's internal" is how the asset pipeline stayed orphaned; there
83
+ cron registration, a boot hook. "It's internal" is how an engine stays orphaned; there
84
84
  is always an attachment to the entry point, or the code never runs.
85
85
 
86
86
  ## Anti-rationalization table
@@ -96,11 +96,18 @@ is always an attachment to the entry point, or the code never runs.
96
96
 
97
97
  ## Output contract — the WorkResult
98
98
 
99
+ **Escalation rule.** If you return `status: "escalated"`, the **first** entry in `deviations[]`
100
+ must be the blocker: one specific, answerable question plus the context needed to answer it.
101
+ Nothing else in the envelope carries it — there is no `escalates[]` field — so a vague entry, or
102
+ the question buried under other notes, reaches the human as "something went wrong" and costs a
103
+ round. Write it so someone without your context can answer it in one reply.
104
+
105
+
99
106
  `wiring-map.md` in your substrate, then `.shapeup/<slug>/results/<order-suffix>.json`:
100
- `status`, `artifacts[]` (the wiring map written), `escalates[]` (e.g. a missing profile, or a UC
101
- whose engine the spec never names the planner's territory), `assumptions[]` (engine paths
102
- inferred from the domain model where the spec was silent), `deviations[]` (any UC left with an
103
- uncertain seam, or an engine with no attachment path, and why). You never touch spec docs,
107
+ `status`, `artifacts[]` (the wiring map written), `assumptions[]` (engine paths
108
+ inferred from the domain model where the spec was silent), `deviations[]` (a missing profile, a
109
+ UC whose engine the spec never names the planner's territory a UC left with an uncertain
110
+ seam, or an engine with no attachment path, and why). You never touch spec docs,
104
111
  `scopes/*.md`, `project-profile.md`, task files, or run-state.
105
112
 
106
113
  ## Verification checklist
@@ -110,7 +117,7 @@ uncertain seam, or an engine with no attachment path, and why). You never touch
110
117
  - [ ] `entry_call_site` is a symbolic composition-root attachment resolved against the profile's `entry_point` — no invented line number, no guessed entry point
111
118
  - [ ] `entry_point` echoes the profile — no independently-chosen seam
112
119
  - [ ] The profile was READ, not guessed; a missing profile in orchestrated mode → ESCALATE, not an invented entry point
113
- - [ ] Any UC whose engine has no attachment path is raised in `deviations[]`/`escalates[]` (the wiring the build must close), never silently dropped
120
+ - [ ] Any UC whose engine has no attachment path is raised in `deviations[]` (the wiring the build must close), never silently dropped
114
121
  - [ ] The WorkResult validates against `work-result.schema.json`
115
122
 
116
123
  ## Invocation
@@ -116,6 +116,13 @@ Done-when statements; `_index.md` Non-Go list. Which UCs are in scope comes from
116
116
 
117
117
  ## Output contract — the WorkResult
118
118
 
119
+ **Escalation rule.** If you return `status: "escalated"`, the **first** entry in `deviations[]`
120
+ must be the blocker: one specific, answerable question plus the context needed to answer it.
121
+ Nothing else in the envelope carries it — there is no `escalates[]` field — so a vague entry, or
122
+ the question buried under other notes, reaches the human as "something went wrong" and costs a
123
+ round. Write it so someone without your context can answer it in one reply.
124
+
125
+
119
126
  1. Write the report `.shapeup/<slug>/evaluation/EVAL-FEATURE-<slug>.md` (or
120
127
  `EVAL-<task_id>.md` for a per-task run) per `references/report-schema.md`: verdict,
121
128
  per-dimension criteria table with confidence, stability block (flips), bug list (severity,
@@ -143,10 +150,10 @@ Done-when statements; `_index.md` Non-Go list. Which UCs are in scope comes from
143
150
  ```
144
151
 
145
152
  **Every FAIL criterion's `evidence` MUST carry a `file:line` locator** — schema-enforced, not
146
- advice: `validate-envelope` rejects the whole result before ingest sees it. A PASS may cite plain
147
- output. (Measured: a run returned a correct FAIL with `bugs: null` and no locator anywhere, which
148
- is unactionable without re-investigating. The rule used to be repeated five times in this prompt
149
- and enforced nowhere; it is now stated once and enforced by `domain.schema.json`.)
153
+ advice: the envelope is validated against `work-result.schema.json` at ingest and a locatorless
154
+ FAIL is rejected before any write. A PASS may cite plain output. (Observed, not theorized: a
155
+ correct FAIL verdict with no locator anywhere is unactionable without re-investigating the
156
+ rule used to live in prose and be enforced nowhere; it is now data in `domain.schema.json`.)
150
157
 
151
158
  The orchestrator's ingest appends the verdict ledger, un-ticks the `refuted` boxes, and sets
152
159
  `eval_verdict` frontmatter. You never touch a task file, a board, or run-state — and you
@@ -209,5 +216,5 @@ summary — standalone has no orchestrator to ingest for you.
209
216
  | Probe the RUNNING app, not the source alone | Apps that look right still break when used |
210
217
  | Re-probe every FAIL; flip ⇒ confidence low | A single snapshot lies; the ledger makes it visible |
211
218
  | Verdict-ledger lines are returned, appended by ingest, never rewritten | Verdict history is how a single-snapshot judge becomes measurable |
212
- | A verdict on a scoped spec without a T0 citation is structurally invalid | T0 is a machine fact the generator cannot fabricate (DD-7, PA4) |
219
+ | A verdict on a scoped spec without a T0 citation is structurally invalid | T0 is a machine fact the generator cannot fabricate |
213
220
  | UI assertions target affordances only (test_id/role/data-state) | Layer-3 styling is frozen; grading it resurrects the freeze through the judge |