devlyn-cli 4.1.0 → 4.2.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 (72) hide show
  1. package/AGENTS.md +10 -94
  2. package/CLAUDE.md +7 -160
  3. package/LICENSE +21 -0
  4. package/README.md +65 -110
  5. package/bin/devlyn.js +95 -116
  6. package/bin/instruction-templates.json +43 -2
  7. package/bin/instructions.js +29 -8
  8. package/config/skills/_shared/adapters/claude.md +8 -85
  9. package/config/skills/_shared/adapters/codex.md +14 -34
  10. package/config/skills/_shared/adapters/omp.md +1 -11
  11. package/config/skills/_shared/codex-monitored.sh +4 -54
  12. package/config/skills/_shared/engine-doctor.sh +18 -58
  13. package/config/skills/_shared/engine-preflight.md +15 -47
  14. package/config/skills/_shared/expected-contract.py +242 -0
  15. package/config/skills/_shared/expected.schema.json +10 -95
  16. package/config/skills/_shared/invocation-receipt.py +26 -757
  17. package/config/skills/_shared/platform-support.py +37 -28
  18. package/config/skills/_shared/role-config.py +100 -421
  19. package/config/skills/_shared/runtime-principles.md +17 -93
  20. package/config/skills/_shared/task-complete.py +490 -285
  21. package/config/skills/_shared/task-completion.md +235 -0
  22. package/config/skills/devlyn-engines/SKILL.md +9 -11
  23. package/config/skills/devlyn-ideate/SKILL.md +57 -107
  24. package/config/skills/devlyn-ideate/references/elicitation.md +18 -99
  25. package/config/skills/devlyn-ideate/references/loop.md +110 -0
  26. package/config/skills/devlyn-ideate/references/package-format.md +128 -0
  27. package/config/skills/devlyn-ideate/scripts/acceptance.py +795 -0
  28. package/config/skills/devlyn-ideate/scripts/queue.py +1780 -0
  29. package/package.json +2 -3
  30. package/config/commit-conventions.md +0 -28
  31. package/config/skills/_shared/adapters/README.md +0 -74
  32. package/config/skills/_shared/adapters/grok.md +0 -100
  33. package/config/skills/_shared/archive_run.py +0 -969
  34. package/config/skills/_shared/codex-config.md +0 -81
  35. package/config/skills/_shared/collect-codex-findings.py +0 -505
  36. package/config/skills/_shared/finish-gate.py +0 -521
  37. package/config/skills/_shared/grok-anchor-guard.py +0 -78
  38. package/config/skills/_shared/judge-output-parser.py +0 -200
  39. package/config/skills/_shared/judge-role-evidence.py +0 -320
  40. package/config/skills/_shared/phase-prompt-render.py +0 -183
  41. package/config/skills/_shared/process-evidence.py +0 -926
  42. package/config/skills/_shared/resolve-bootstrap.py +0 -1410
  43. package/config/skills/_shared/resolve-stop-hook.py +0 -359
  44. package/config/skills/_shared/run-bounded.py +0 -58
  45. package/config/skills/_shared/spec-verify-check.py +0 -5083
  46. package/config/skills/_shared/state-phase-write.py +0 -5989
  47. package/config/skills/_shared/terminal-claim-check.py +0 -777
  48. package/config/skills/_shared/verify-merge-findings.py +0 -3528
  49. package/config/skills/devlyn-design-ui/SKILL.md +0 -650
  50. package/config/skills/devlyn-ideate/references/from-spec-mode.md +0 -61
  51. package/config/skills/devlyn-ideate/references/project-mode.md +0 -80
  52. package/config/skills/devlyn-ideate/references/spec-template.md +0 -121
  53. package/config/skills/devlyn-ideate/references/templates/decision.md +0 -48
  54. package/config/skills/devlyn-ideate/references/templates/roadmap.md +0 -50
  55. package/config/skills/devlyn-ideate/references/templates/vision.md +0 -44
  56. package/config/skills/devlyn-queue/SKILL.md +0 -66
  57. package/config/skills/devlyn-queue/scripts/append.py +0 -94
  58. package/config/skills/devlyn-resolve/SKILL.md +0 -379
  59. package/config/skills/devlyn-resolve/references/free-form-mode.md +0 -71
  60. package/config/skills/devlyn-resolve/references/outer-loop.md +0 -23
  61. package/config/skills/devlyn-resolve/references/phases/build-gate.md +0 -75
  62. package/config/skills/devlyn-resolve/references/phases/cleanup.md +0 -33
  63. package/config/skills/devlyn-resolve/references/phases/implement.md +0 -44
  64. package/config/skills/devlyn-resolve/references/phases/plan.md +0 -39
  65. package/config/skills/devlyn-resolve/references/phases/probe-derive.md +0 -261
  66. package/config/skills/devlyn-resolve/references/phases/surface-close.md +0 -34
  67. package/config/skills/devlyn-resolve/references/phases/verify.md +0 -310
  68. package/config/skills/devlyn-resolve/references/state-schema.md +0 -152
  69. package/config/skills/devlyn-resolve/references/task-completion.md +0 -160
  70. package/config/templates/prompt-templates.md +0 -71
  71. package/config/templates/template-feature.spec.md +0 -255
  72. package/config/templates/template-product-spec.md +0 -680
@@ -1,310 +0,0 @@
1
- # PHASE 5 — VERIFY (canonical body, fresh worker context)
2
-
3
- Per-engine adapter header is prepended at runtime. **You are spawned with empty conversation context.** No carry-over from PLAN / IMPLEMENT / BUILD_GATE / CLEANUP. This is the structural guarantee of independence — the prompt body reinforces it but the spawn is what makes it real. If the orchestrator cannot provide a fresh worker, VERIFY must be `BLOCKED:fresh-context-unavailable`; same-context review is forbidden.
4
-
5
- <role>
6
- Independent quality layer. You answer one question: did the diff deliver what the spec said it would, with no scope creep, no quality regression, and no constraint violation? You produce findings only — you have no code-mutation tools.
7
- </role>
8
-
9
- <input>
10
- - `spec.md` (or `.devlyn/criteria.generated.md` for free-form mode) — the contract.
11
- - `spec.expected.json` — the mechanical acceptance contract per `_shared/expected.schema.json`.
12
- - The cumulative diff against `state.base_ref.sha`.
13
- - The source hash (`state.source.spec_sha256` for spec mode, `state.source.criteria_sha256` for generated free-form mode) — re-read the source contract from disk and confirm the hash matches; if it does not, emit a verdict-binding finding with reason `source_sha256_mismatch` and terminal verdict `BLOCKED`, then stop reviewing without editing state.
14
- - `.devlyn/spec-verify.results.json` plus any required validated VERIFY
15
- process-evidence manifest and raw stdout/stderr streams named by its
16
- `process_evidence` carrier. These are immutable MECHANICAL results; rehash
17
- every named byte before JUDGE spawn and again during merge. Valid pure-design
18
- contracts with no executable obligations have `commands: []` and
19
- `process_evidence: null`.
20
-
21
- You do NOT receive: PLAN, IMPLEMENT's reasoning, BUILD_GATE's findings, CLEANUP's allowlist negotiations. Reading those would compromise independence. Inspect authorized source, diff and sealed evidence using native read/search tools or non-mutating shell commands; executable verification belongs exclusively to MECHANICAL.
22
- </input>
23
-
24
- <sub_phases>
25
-
26
- ### MECHANICAL (deterministic)
27
-
28
- The orchestrator completes these checks before spawning either JUDGE,
29
- independent of BUILD_GATE's earlier run:
30
-
31
- 1. `SPEC_VERIFY_PHASE=verify_mechanical SPEC_VERIFY_FINDINGS_FILE=verify-mechanical.findings.jsonl SPEC_VERIFY_FINDING_PREFIX=VERIFY-MECH python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py" --include-risk-probes` against the post-CLEANUP code. In spec mode, sibling `spec.expected.json` wins; a malformed sibling is CRITICAL, not a fallback. When `state.risk_profile.risk_probes_enabled == true`, missing `.devlyn/risk-probes.jsonl` is also CRITICAL. The script also checks `forbidden_patterns`, `required_files`, `forbidden_files`, and `max_deps_added`, and writes `.devlyn/spec-verify.results.json` with a VERIFY process-evidence carrier.
32
-
33
- 2. Validate the carrier and every named raw stream. A missing, altered,
34
- escaping, duplicate, or expectation-mismatched record is a CRITICAL mechanical
35
- blocker. Freeze the validated results, manifest, and streams as JUDGE inputs.
36
-
37
- Emit findings to `.devlyn/verify-mechanical.findings.jsonl`. Each match = one finding. Severity from the pattern's `severity` field (disqualifier → CRITICAL, warning → MEDIUM). A verdict-binding MECHANICAL result skips both JUDGEs.
38
-
39
- ### JUDGE (fresh-context grading)
40
-
41
- On the constrained Windows read route, the orchestrator supplies the complete spec/expected contract, accepted source SHA/diff, relevant source/tests with file:line locators, and validated sealed MECHANICAL results/manifests/required raw streams inline. Judge that supplied evidence without running tools. Missing, truncated or unbound inputs produce a verdict-binding BLOCKED finding. Retain fresh context, read-only sandbox, isolation, model/effort and bounded output; never widen permissions. This route addresses observed policy-denied reads, not a universal Windows sandbox limitation.
42
-
43
- Grade the diff against the spec on rubric axes:
44
-
45
- - **Spec compliance** — does cited evidence show how every applicable Requirement and Constraint is satisfied?
46
- - **Scope** — does the diff touch only files PLAN listed (or the cleanup allowlist)? Out-of-scope file = HIGH finding `scope.out-of-scope-violation`.
47
- - **Quality** — does the implementation follow the framework's idiomatic patterns, or are there hand-rolled helpers replacing standard primitives? `design.unidiomatic-pattern` MEDIUM if so.
48
- - **Consistency** — internal style (naming, error shape, module boundaries) consistent with the surrounding code.
49
-
50
- **Bounded primary review**: the primary JUDGE makes one broad pass over the
51
- source contract, sealed MECHANICAL carrier, and cumulative diff, covering all
52
- four rubric axes and every binding Requirement and Constraint clause. It then makes one
53
- targeted interaction pass over the clauses the broad pass left unresolved.
54
- Before any third pass, emit the required terminal result. If R1–R8 or another
55
- spec axis remains uncovered, emit a verdict-binding BLOCKED coverage finding
56
- instead of continuing or assuming PASS.
57
-
58
- For each finding, write file:line evidence. Do not paraphrase code; quote it.
59
-
60
- **Clause-level check**: split each Requirement and Constraint into its binding clauses before
61
- you pass it. Words like `before`, `after`, `once`, `always`, `never`,
62
- `regardless`, `irrelevant`, `permanent`, `idempotent`, `duplicate`, `raw`, and
63
- `signature` usually encode a separate invariant. A passing verification command
64
- proves only the case it actually exercises; it does not prove neighboring
65
- clauses. For stateful, auth, parsing, idempotency, rollback, and error-priority
66
- flows, construct at least one counterexample in your head and trace the code
67
- order, including failed-operation rollback and the next entity's state. If the
68
- code order can return the wrong status/body/output for a binding clause, emit a
69
- HIGH spec-compliance finding even when the provided verifier passes.
70
-
71
- Respect each clause's scope qualifiers. Do not widen an invariant beyond the
72
- words in the spec: `inside a warehouse`, `per resource`, `for this line`,
73
- `after validation`, and similar qualifiers are binding. When two ordering rules
74
- coexist, compose them in the stated order instead of inventing a stronger global
75
- ordering. A finding based on a widened invariant is a false positive and must
76
- not drive the fix loop.
77
-
78
- **Targeted interaction pass**: trace interactions among the clauses the broad
79
- pass left unresolved. For high-complexity specs, one-axis examples are not
80
- enough: construct at least one adversarial scenario that combines two or more
81
- explicit verification bullets. Prioritize combinations such as
82
- ordering/priority + blocked interval/failure, ordering/priority +
83
- all-or-nothing rollback + later entity state, validation/error-priority +
84
- stdout/stderr contract, or auth/idempotency + duplicate/replay ordering. If the
85
- implementation only passes isolated examples but fails the combined scenario,
86
- emit a HIGH finding tied to all relevant spec clauses.
87
-
88
- JUDGE does not execute literal verification, lint, test, build, risk-probe, or
89
- newly invented interaction commands. For high-complexity behavior, executable
90
- coverage must already be declared in sibling `spec.expected.json` or derived
91
- risk probes and present in the sealed MECHANICAL evidence. Missing coverage is
92
- a verdict-binding finding; review the implementation's clause and code order
93
- without inventing a replacement command.
94
-
95
- **Coverage check**: account for every applicable Requirement and Constraint using sealed MECHANICAL evidence for required executable checks and cited source/design evidence for pure-design clauses or explicitly retained source-review obligations. Inspect applicable unchanged code and documentation; absence of a code change alone is not missing coverage. Source review never substitutes for required executable coverage. For any unsupported clause, emit a verdict-binding coverage finding identifying the missing evidence. JUDGE does not run checks or edit state; the orchestrator records `state.verify.coverage_failed: true` from coverage findings before merge.
96
-
97
- **Verdict-binding check**: a demonstrated violation of an applicable mandatory
98
- task, public, or existing test contract is binding, including unmet new
99
- requirements and incorrect customer documentation. Quote the exact applicable
100
- clause and concrete file:line evidence. Small impact, rare inputs, or a
101
- pre-existing defect do not excuse that violation. A changed line or requirement
102
- reference alone does not establish applicability.
103
-
104
- Emit HIGH/CRITICAL as appropriate, or MEDIUM with literal `verdict_binding: true`.
105
- Do not label a binding finding LOW/INFO: the merge honors HIGH/CRITICAL and
106
- MEDIUM/true; confidence is reported but adds no merge threshold. Preferences,
107
- style, stronger inferred invariants, genuinely ambiguous clauses, and unrelated
108
- pre-existing issues are not demonstrated mandatory violations. Name the actual
109
- ambiguity or evidence limitation; do not substitute an impact argument.
110
- Advisory MEDIUM findings remain non-binding and produce `PASS_WITH_ISSUES`.
111
-
112
- **Anti-self-filter rule**: report every finding you observe, including ones you consider low-severity or low-confidence. Tag each with `confidence: high|medium|low` and let the harness's downstream filter rank them. Filtering at this stage suppresses recall.
113
-
114
- ### Pair-mode (default when OTHER engine is available)
115
-
116
- MECHANICAL runs first. A verdict-binding blocker routes to the fix loop without
117
- either judge and records `pair_judge: null`. After it passes, honor `--no-pair`;
118
- otherwise resolve the OTHER-engine route. An explicit `--pair-verify` is an
119
- availability promise: an unavailable OTHER engine BLOCKs. An automatic route
120
- with no OTHER engine records `auto_pair_other_engine_unavailable` and continues
121
- solo.
122
-
123
- When the OTHER engine is available, write `pair_trigger` at judge-spawn time
124
- with `pair.default` plus every applicable outcome-independent canonical reason,
125
- then dispatch the primary and pair JUDGEs concurrently against the same frozen
126
- diff via foreground parallel dispatch, never background shells. A primary blocker does not cancel the pair-JUDGE; both finding sets join the same VERIFY invocation and merge by worst source verdict without vote counting. An orchestrator
127
- without foreground parallel dispatch runs the same two required judges
128
- sequentially, with no flag or extra state marker. This is a dispatch-shape
129
- fallback, not outcome-dependent escalation; the primary cannot skip the pair.
130
- Both JUDGEs inspect the same immutable evidence; neither runs probes or mutates
131
- shared state.
132
-
133
- Keep these reasons as telemetry; they no longer gate the second spawn:
134
- `mode.pair-verify`, `mode.verify-only`, `complexity.high`,
135
- `complexity.large`, `spec.complexity.high`, `spec.complexity.large`,
136
- `spec.solo_headroom_hypothesis`, `risk.high`, `risk_probes.enabled`,
137
- `risk_probes.present`, `coverage.failed`, `mechanical.warning`, and
138
- `judge.warning`.
139
-
140
- Malformed `state.risk_profile` is a VERIFY contract violation: it must be an
141
- object, `high_risk` / `risk_probes_enabled` / `pair_default_enabled` must be
142
- JSON booleans when present, and `reasons` must be a string array. The merge
143
- blocks malformed state because it can hide a required route.
144
-
145
- If `--no-pair` was set, do not spawn the OTHER-engine judge. Record
146
- `pair_trigger: { eligible: false, reasons: [], skipped_reason: "user_no_pair" }`
147
- and continue with solo VERIFY. This is an explicit user opt-out, not an engine
148
- availability fallback. `--pair-verify` and `--no-pair` are mutually exclusive;
149
- if both are present, stop with `BLOCKED:invalid-flags`.
150
-
151
- When the OTHER engine is available, persist this before either judge starts:
152
-
153
- ```json
154
- "pair_trigger": {
155
- "eligible": true,
156
- "reasons": ["pair.default", "complexity.high"],
157
- "skipped_reason": null
158
- }
159
- ```
160
-
161
- After both judges return, append every applicable outcome-dependent reason
162
- (`coverage.failed`, `mechanical.warning`, `judge.warning`) before merge. Eligible schema-v3 state must contain `pair.default` and every other applicable canonical
163
- reason. The OTHER-engine judge is mandatory; missing output is a contract
164
- violation.
165
-
166
- `pair_trigger` remains strict: eligible state has canonical reasons and no skip;
167
- ineligible state has empty reasons and one of `user_no_pair`,
168
- `mechanical_blocker`, `auto_pair_other_engine_unavailable`, or null.
169
- `primary_judge_blocker` is parser-recognized only for archived v2.0 replay and
170
- retains the existing rejection when a pre-known reason applied. New runs never
171
- write it. The `--engine` flag does not disable default pairing.
172
-
173
- Run the shared availability pre-flight before spawn. Explicit `--pair-verify`
174
- unavailability stays `BLOCKED:<engine>-unavailable` with setup guidance.
175
- Automatic unavailability records `auto_pair_other_engine_unavailable` and runs
176
- solo. Never synthesize pair findings.
177
-
178
- When eligible and the orchestrator spawns a second VERIFY agent with the OTHER engine's adapter, both judgments are merged:
179
- - Any HIGH/CRITICAL finding either model surfaces is verdict-binding.
180
- - Any MEDIUM finding with literal `verdict_binding: true` is also binding.
181
- Apply the same mandatory-clause, applicability and evidence check above;
182
- neither impact nor pre-existing origin makes an applicable violation advisory.
183
- - Other lower-severity disagreements are logged but do not change the verdict.
184
- - The orchestrator handles merge; you only emit your own findings.
185
- - The second judge's job is adversarial complement, not a duplicate summary:
186
- review the two highest-risk explicit `## Verification` bullets that cross
187
- state mutation, all-or-nothing rollback, ordering, idempotency, auth, or
188
- error-priority clauses. The primary judge owns broad coverage; the pair judge
189
- is a bounded adversarial complement. Do not read `.claude/skills`,
190
- `.codex/skills`, `.agents/skills`, `CLAUDE.md`, `AGENTS.md`, or other harness docs unless the
191
- orchestrator pasted a specific excerpt into the prompt. Use only the spec,
192
- diff, implementation files, tests, and sealed MECHANICAL evidence. Complete at
193
- most two targeted reviews before first output; inspection remains read-only.
194
- Pair-JUDGE output: emit JSONL findings then a bare terminal verdict line, or
195
- emit only `PASS` when clean. `_shared/judge-output-parser.py` is the single
196
- acceptance rule for pair output: JSONL findings, then a `# SUMMARY {json}`
197
- line or a bare verdict line (`PASS` alone when clean); it ignores bare
198
- code-fence lines and unwraps the registered Codex JSON envelope including its
199
- narrated-preamble recovery (iter-0082), and binds a whole-message NDJSON
200
- capture only through its uniquely attested terminal `end_turn` assistant
201
- message (iter-0106); every other non-empty line blocks the pair source.
202
- When the spec names exact keys, row shapes, JSON
203
- object shape, or an exact error body, compare the sealed parsed key sets/deep
204
- equality so aliased keys, missing keys, and extra keys are verdict-binding
205
- failures. For priority/stateful specs, trace implementation code order for an
206
- earlier input entity that would succeed under input-order processing, a later
207
- higher-priority entity that consumes or blocks the critical resource, and a
208
- failure/blocked/rollback edge that determines a later entity's state. Scope
209
- qualifiers are binding for the pair judge too: do not reinterpret `inside a
210
- warehouse`, `per resource`, or line-scoped rules as global rules. When both
211
- priority ordering and rollback/blocked-interval behavior appear, perform this
212
- dominance-loss code-order review first and confirm the sealed evidence covers
213
- complete accepted/scheduled and rejected output ordering.
214
-
215
- Both resolved JUDGEs are read-only. Capture the primary reply as
216
- `.devlyn/<primary-engine>-judge.stdout` and its stderr sibling; never write the
217
- generic `.devlyn/verify-judge.stdout`. When the primary engine is Codex, invoke
218
- it only through this distinct monitored route:
219
-
220
- ```bash
221
- DEVLYN_CODEX_PROMPT_FILE="<primary-prompt-file>" CODEX_MONITORED_ISOLATED=1 CODEX_MONITORED_TIMEOUT_SEC=600 bash "$CODEX_MONITORED_PATH" -C "$PWD" -s read-only -c model_reasoning_effort=high - >.devlyn/codex-judge.stdout 2>.devlyn/codex-judge.stderr
222
- ```
223
-
224
- Write the complete prompt to the named file as exact UTF-8 bytes before launch and retain its generated `.transport.json` carrier. This command supplies unconfigured defaults. Explicit judge profiles use
225
- `SKILL.md#explicit-role-dispatch` for validated model/effort options and
226
- round-scoped evidence. Omit bypass flags; do not pipe either stream. On primary exit
227
- 124, write `.devlyn/verify.primary.timeout.json` with exactly
228
- `{"engine": "<resolved-primary-engine>", "budget_seconds": 600}` before merge. The
229
- marker is primary-only authority: malformed, wrong-engine, or wrong-budget
230
- content BLOCKs; a valid marker preserves all canonical primary findings and
231
- floors `judge` at `BLOCKED`, including when findings/stdout are empty. It never
232
- produces `PASS`, a solo verdict, or pair-style `TIMEOUT`; without the marker,
233
- existing missing/invalid primary-output behavior remains fail-closed.
234
-
235
- Codex pair-JUDGE sets `DEVLYN_CODEX_PROMPT_FILE="<pair-prompt-file>"` with sole prompt `-` and keeps the monitored
236
- `codex-monitored.sh` route with
237
- `CODEX_MONITORED_ISOLATED=1 CODEX_MONITORED_TIMEOUT_SEC=600` and
238
- unconfigured default `-c model_reasoning_effort=medium`; isolation blocks user config, AGENTS.md,
239
- hooks, and project rules from hidden context/tool side effects. Do not pipe it
240
- to `tail`, `head`, `grep`, `sed`, or `awk`; capture stdout/stderr directly.
241
- Every other resolved OTHER engine follows `_shared/adapters/<name>.md`
242
- `## Invocation`. Capture the pair reply as `.devlyn/<other-engine>-judge.stdout`, then run
243
- `python3 "$DEVLYN_SHARED_DIR/collect-codex-findings.py" --devlyn-dir
244
- "<abs repo>/.devlyn" --stdout-file <other-engine>-judge.stdout` before merge.
245
- The orchestrator writes the canonical `.devlyn/verify.pair.findings.jsonl`.
246
- The pair prompt must include a bounded-output contract: no harness-doc reads,
247
- read-only inspection, maximum two targeted reviews before first output, stop on
248
- the first verdict-binding finding, and emit PASS immediately after the bounded
249
- reviews pass.
250
- Raw stdout is diagnostic-only. A non-zero collector exit must write no
251
- canonical findings file: set the pair source to `BLOCKED` for
252
- `verify.pair.emission-contract`, and do not merge as though unparsed stdout were
253
- only a diagnostic. Do not ask the judge to edit `.devlyn`.
254
-
255
- Both pair-judge directions are wall-budgeted at 600s. The orchestrator records
256
- `state.phases.verify.judge_durations_ms: {"judge": <int>, "pair_judge": <int|null>}`
257
- as it collects each judge result; these wall durations are siblings of, never
258
- nested inside, normalized string `sub_verdicts`. A judge subprocess exit
259
- 124 is a budget abort: before merge, the orchestrator writes
260
- `.devlyn/verify.pair.timeout.json` with `{"engine": "<resolved name>",
261
- "budget_seconds": 600}`. Three cases are binding: marker plus no canonical pair
262
- findings and no stdout findings — including a capture the parser rejects solely
263
- because its message stream was truncated before the terminal result — records
264
- `pair_judge: "TIMEOUT"`, computes the merged verdict from mechanical plus
265
- primary judge, and surfaces `solo verdict after pair TIMEOUT` in the report
266
- header; marker plus canonical findings or parseable stdout findings binds those
267
- findings exactly as today, and every other parser rejection stays `BLOCKED`
268
- under the stdout emission contract; no marker preserves the existing `BLOCKED`
269
- contract for missing, empty, or uncaptured pair output. A budget
270
- abort is not an availability fallback; explicit-route availability still fails
271
- closed.
272
-
273
- After all VERIFY findings files are written, run:
274
-
275
- ```bash
276
- python3 "$DEVLYN_SHARED_DIR/verify-merge-findings.py" --write-state
277
- ```
278
-
279
- This deterministic merge is the routing source of truth for VERIFY. It writes
280
- `.devlyn/verify-merged.findings.jsonl`, `.devlyn/verify-merge.summary.json`, and
281
- updates `state.phases.verify.{verdict,sub_verdicts,merged}`. Branch on the
282
- merged state verdict, not on either model's prose verdict. Absent a BLOCKED source, any HIGH/CRITICAL
283
- finding from either judge is `NEEDS_WORK`; a MEDIUM must set literal
284
- `verdict_binding: true` to become `NEEDS_WORK`, regardless of confidence.
285
-
286
- Do not create, edit, truncate, or summarize `.devlyn/verify-merged.findings.jsonl`
287
- or `.devlyn/verify-merge.summary.json` by hand. Those files have exactly one
288
- writer: `verify-merge-findings.py`. If that command fails, preserve stderr and set
289
- VERIFY to `BLOCKED`; do not synthesize merge artifacts in prose.
290
-
291
- </sub_phases>
292
-
293
- <output>
294
- - `.devlyn/verify-mechanical.findings.jsonl` — MECHANICAL findings.
295
- - `.devlyn/spec-verify.results.json` plus its sealed VERIFY process-evidence manifest/raw streams — immutable JUDGE inputs.
296
- - `.devlyn/verify.findings.jsonl` — JUDGE findings.
297
- - `.devlyn/verify-merged.findings.jsonl` and `.devlyn/verify-merge.summary.json` — deterministic merge artifacts.
298
- - `phases.verify.{verdict, sub_verdicts, merged}` are written by `verify-merge-findings.py --write-state`, never by you (VERIFY agents have no code-mutation tools). `completed_at`/`duration_ms`/`artifacts` are recorded by the orchestrator via `state-phase-write.py` after this phase returns. `PASS` requires zero CRITICAL/HIGH findings, zero verdict-binding MEDIUM findings, and coverage met.
299
- </output>
300
-
301
- <quality_bar>
302
- - Independence is structural (fresh context) and behavioral (no code mutation). Both must hold.
303
- - MECHANICAL is the sole verification executor; JUDGE performs sealed-evidence and clause/code-order review only.
304
- - Quote, do not paraphrase. Findings without quoted file:line evidence are excluded.
305
- - Coverage > confidence. Missing-evidence findings outrank a confident "looks fine."
306
- </quality_bar>
307
-
308
- <runtime_principles>
309
- VERIFY's discipline is "the spec is the contract, the diff is the evidence, the verdict is the comparison."
310
- </runtime_principles>
@@ -1,152 +0,0 @@
1
- # pipeline.state.json schema
2
-
3
- Single authoritative verdict source for `/devlyn-resolve`. The orchestrator branches on `state.phases.<name>.verdict` directly — never parses `.devlyn/*.findings.jsonl` for routing. Living document; bump `version` on a breaking change.
4
-
5
- ## Top-level shape
6
-
7
- ```json
8
- {
9
- "version": "3.0",
10
- "run_id": "rs-<UTC-timestamp>-<12-hex>",
11
- "started_at": "2026-04-30T12:00:00Z",
12
- "engine": "claude",
13
- "mode": "spec",
14
- "pair_verify": false,
15
- "complexity": null,
16
- "risk_profile": { "high_risk": false, "reasons": [], "risk_probes_enabled": false, "risk_probes_explicit": false, "pair_default_enabled": true },
17
- "risk_probes_digest": null,
18
- "process_evidence": null,
19
- "base_ref": { "branch": "main", "sha": "abc123..." },
20
- "rounds": { "max_rounds": 4, "global": 0 },
21
- "bypasses": [],
22
- "implement_passed_sha": null,
23
- "source": {
24
- "type": "spec",
25
- "spec_path": "docs/roadmap/phase-1/X.md",
26
- "spec_sha256": "...",
27
- "criteria_path": null,
28
- "criteria_sha256": null
29
- },
30
- "criteria": [
31
- { "id": "C1", "ref": "spec://requirements/0", "status": "pending", "evidence": [], "failed_by_finding_ids": [] }
32
- ],
33
- "phases": {
34
- "plan": null,
35
- "probe_derive": null,
36
- "implement": null,
37
- "surface_close": null,
38
- "build_gate": null,
39
- "cleanup": null,
40
- "verify": null,
41
- "final_report": null
42
- },
43
- "verify": { "coverage_failed": false, "pair_trigger": null }
44
- }
45
- ```
46
-
47
- ## Field rules
48
-
49
- - **version** — string. Bump major on a breaking schema change.
50
- - **session_id** — optional string or null stamped from `CLAUDE_CODE_SESSION_ID`; a missing/null value leaves Stop-hook pressure inert.
51
- - **mode** — `"free-form" | "spec" | "verify-only"`.
52
- - **pair_verify** — boolean. True only for `--pair-verify`. Pairing is already default-when-available; this flag makes OTHER-engine availability an explicit fail-closed promise and adds `mode.pair-verify` telemetry. It is mutually exclusive with `risk_profile.pair_default_enabled == false` from `--no-pair`.
53
- - **complexity** — `null | "trivial" | "medium" | "large"`. Free-form mode populates this; spec/verify-only mode leaves it null.
54
- - **engine** — any engine name with a shipped adapter (`_shared/adapters/<name>.md`); `"claude"` and `"codex"` today. A required unavailable engine stops the run with `BLOCKED:<engine>-unavailable`.
55
- - **engine_source** — `"flag" | "engines.json" | "default"` — provenance of the resolved executor engine (`_shared/engine-preflight.md#role-resolution`). Optional on archived pre-iter-0038 runs; absent means `default`.
56
- - **source** — downstream contract provenance. Spec/verify-only set `type: "spec"`, `spec_path`, and `spec_sha256`. Free-form sets `type: "generated"`, `goal_path: ".devlyn/goal.raw.txt"` + exact-byte `goal_sha256`, and `criteria_path: ".devlyn/criteria.generated.md"` + raw-byte `criteria_sha256`. VERIFY re-checks its contract hash.
57
- - **`.devlyn/external-diff.patch` artifact** — verify-only Git patch with `a/` and `b/` path prefixes (`git diff --binary --src-prefix=a/ --dst-prefix=b/`). A zero-byte file means no changes; file-path checks reject malformed or other-prefix input. File exclusions cover both names in rename/copy headers. The verifier fails closed if the artifact exists outside `"verify-only"` mode.
58
- - **risk_profile** — PHASE 0 routing state. `high_risk` controls automatic risk probes; `risk_probes_enabled` / `risk_probes_explicit` preserve their current semantics; `pair_default_enabled` is false only for explicit `--no-pair`. `risk_profile` must remain an object with boolean `high_risk`, `risk_probes_enabled`, `risk_probes_explicit`, and `pair_default_enabled` fields when present, plus `reasons` as a string list. Malformed state blocks VERIFY because it can hide required routes.
59
- - **risk_probes_digest** — top-level sha256 written by PHASE 1.5 after probe validation; BUILD_GATE/VERIFY replay semantics live in `phases/build-gate.md` step 4.
60
- - **process_evidence** — absent or JSON null for legacy runs and new runs with no captured obligation; otherwise an append-only array of `{phase, round, manifest: {path, sha256}, streams: [{id, stdout: {path, sha256}, stderr: {path, sha256}}]}` carriers. Paths are worktree-relative and live under `.devlyn/process-evidence/<run_id>/<phase>/round-<round>/`. A successful IMPLEMENT completion/transition validates every sibling `spec.expected.json.process_evidence[]` item declared for `phase: "implement"`, rehashes both raw streams (including zero-byte files), and appends the carrier before accepting the checkpoint. Missing, altered, escaping, duplicate-id, or expectation-mismatched evidence blocks without a state mutation. Later phases reuse the same carrier shape; no schema-major bump is required because readers must accept absent/null legacy state.
61
- - **base_ref.branch** — checked-out branch name, or `null` for a detached HEAD; `base_ref.sha` remains the exact immutable base in both states.
62
- - **rounds.global** — repair IMPLEMENT admissions committed by `state-phase-write.py`, independent of invocation `round`; `max_rounds` defaults to 4. Normal phased advancement uses a fresh IMPLEMENT invocation round without a repair charge. Downstream phases carry the current IMPLEMENT round, or zero before IMPLEMENT.
63
- - **phases.implement.exec** — only on phase-gated large runs (plan.md has `## Execution phases` with >1 phase): `{ "total": N, "current": k, "statuses": ["PASS"|"FAIL"|null, ...], "commits": ["<sha>", ...] }`. Persist the current status as FAIL on every failed gate and retain current until PASS. Phase definitions live in plan.md (immutable contract); progress lives here (routing truth — never route on plan.md checkboxes). Absent on single-phase runs. `cumulative.patch` for such runs is `git diff <base_ref.sha>...HEAD`.
64
- - **phases.probe_derive** — optional PHASE 1.5 entry when `--risk-probes` is enabled. Artifacts include `.devlyn/risk-probes.jsonl`. Probe failures later surface through BUILD_GATE/VERIFY as `correctness.risk-probe-failed`, or as `correctness.verification-timeout` when the probe exceeds its verification budget.
65
- - **phases.surface_close** — generated trivial/medium one-shot: `pre_sha`, `input_patch_sha256`, assembled-byte `prompt_sha256`, `untracked_before`; spawn requires Claude plus a requested model, and `surface-check` gates PASS. `.devlyn/surface-close.output.json` retains the Claude JSON wrapper used for completion attestation while the round JSONL remains the execution-audit backstop. Automatic Claude unavailability alone records null verdict plus `skipped_reason: "auto_surface_close_claude_unavailable"`. After an adjudication-malformed reply only, successful rollback plus transcript write-audit records the two nonterminal fields `skipped_reason: "surface_close_rolled_back_adjudication_malformed"` and `continued_after_block: true`; neither creates a terminal floor. Other failures store bare `"BLOCKED"`. `durability[]` is append-only: `{round, origin_phase, receipt_path, receipt_sha256, restore_commit_sha}` per `.devlyn/closure-durability.round-<n>.json`.
66
- - **bypasses** — array of phase names from `--bypass`. Valid: `"build-gate" | "cleanup"`. PLAN, IMPLEMENT, VERIFY are non-bypassable (orchestrator rejects at parse time).
67
- - **implement_passed_sha** — captured at end of PHASE 2; null until then. Activates the post-implement invariant for SURFACE_CLOSE, CLEANUP, and VERIFY.
68
- - **criteria** — generated from spec's `## Requirements` checklist (one per `- [ ]`). `status: pending → implemented` is the legal transition. `failed_by_finding_ids` populates when VERIFY surfaces a finding tied to a criterion.
69
- - **`.devlyn/untracked.baseline` artifact** — written at PHASE 0 (`spec-verify-check.py --write-untracked-baseline`); BUILD_GATE flags created-during-run unauthorized untracked files against it (`.devlyn/` exempt; semantics in `phases/build-gate.md` step 4).
70
- - **verify.coverage_failed** — outcome-dependent telemetry set by the orchestrator when JUDGE reports missing clause evidence. It no longer gates pair dispatch: after MECHANICAL passes, `pair.default` dispatches both judges concurrently whenever `pair_default_enabled != false` and the OTHER engine is available. An orchestrator without foreground parallel dispatch runs the same two required judges sequentially; a primary blocker never skips the pair. A verdict-binding MECHANICAL blocker still skips both. Legacy complexity values remain accepted only for archived compatibility.
71
- - **verify.pair_trigger** — strict decision state: `{ "eligible": boolean, "reasons": string[], "skipped_reason": string|null }`. Schema v3.0 eligible state requires `pair.default` plus every applicable telemetry reason and `skipped_reason: null`; outcome-dependent reasons are appended after both judges return. Canonical reasons are `pair.default`, `mode.verify-only`, `mode.pair-verify`, `complexity.high`, `complexity.large`, `spec.complexity.high`, `spec.complexity.large`, `spec.solo_headroom_hypothesis`, `risk.high`, `risk_probes.enabled`, `risk_probes.present`, `coverage.failed`, `mechanical.warning`, and `judge.warning`. Ineligible new-run state has empty reasons and only `user_no_pair`, `mechanical_blocker`, `auto_pair_other_engine_unavailable`, or null; `primary_judge_blocker` remains parser-recognized only for archived v2.0 replay and retains its existing pre-known-reason rejection. Explicit `mode.pair-verify` cannot use the unavailable-engine skip. Missing, contradictory, incomplete, or unknown trigger state BLOCKs VERIFY.
72
-
73
- ## Per-phase shape
74
-
75
- Each entry under `phases.<name>` (for `plan`, `probe_derive`, `implement`, `surface_close`, `build_gate`, `cleanup`, `verify`, `final_report`):
76
-
77
- ```json
78
- {
79
- "started_at": "2026-04-30T12:00:01Z",
80
- "completed_at": "2026-04-30T12:00:30Z",
81
- "duration_ms": 29000,
82
- "round": 0,
83
- "triggered_by": null,
84
- "verdict": "PASS",
85
- "engine": "claude",
86
- "model_requested": "<orchestrator-selected model id or null>",
87
- "model_effective": "<session-attested model id or null>",
88
- "pre_sha": null,
89
- "post_sha": null,
90
- "artifacts": { "findings_file": null, "log_file": null },
91
- "sub_verdicts": null
92
- }
93
- ```
94
-
95
- `history` is absent until a phase is re-entered. Immediately before re-entry overwrites the live lifecycle, `state-phase-write.py` appends those prior values as one object to `history[]`, creating the append-only array on first re-entry. Codex worker history also retains its state-bound `invocation_receipt`; PLAN history retains the complete prompt/output receipt. Owner phase history preserves output/checkpoint digests, its round and present execution kind/worker provenance; historical absence of the kind remains absent.
96
-
97
- - `verdict` — ordinary phases use `"PASS" | "PASS_WITH_ISSUES" | "FAIL" | "NEEDS_WORK" | "BLOCKED"`; FINAL_REPORT persists the full terminal verdict, including `BLOCKED:<reason>`.
98
- - `execution_kind` — owner PLAN uses `"orchestrator_context"`; owner BUILD_GATE/CLEANUP use `"orchestrator_commands"`, null engine/model fields and no worker prompt, receipt or role identity. Worker claims and current-round worker artifacts are rejected. Already-open absent-kind spans retain historical receipt validation; unknown or null kinds fail closed.
99
- - `model_requested` / `model_effective` — `model_requested` is supplied by the orchestrator through `--model`; schema-v3 completion cannot replace the spawn engine or requested model. Schema-v3 Codex IMPLEMENT and historical worker PLAN/CLEANUP/BUILD_GATE phases validate requested dispatch using the canonical `.devlyn/<phase>.invocation.<round>.json`; state rehashes the receipt, round-scoped prompt and worker session and cross-checks model, sandbox, prompt, run, phase, round and exit. A structured native model-reroute event or malformed JSONL blocks completion, even at exit0. The receipt proves argv delivery, so `model_effective` remains null; absence of a reroute is not positive model attestation. Arbitrary paths and plaintext `model:` headers are not evidence. Claude JSON output accepts a singleton `modelUsage` key or, for multiple entries, the sole key whose token counters exactly match top-level usage. Missing/malformed evidence and requested/effective mismatch fail closed: the phase verdict becomes `BLOCKED` and completion fails.
100
- - `output_sha256` — on schema-v3 PLAN completion, SHA-256 of exact `.devlyn/plan.md` bytes. Only caller-verdict BLOCKED with a lexically absent, never-bound plan records null; receipt/session attestation still applies. This terminal null permits only FINAL_REPORT lifecycle operations while the path remains lexically absent. Bound bytes are rehashed on later spawn/completion/transition, including BLOCKED; legal PLAN re-entry replaces the digest only at the new PLAN completion. FINAL_REPORT completion binds exact `.devlyn/final-report.md` bytes here and its canonical path in `artifacts.log_file`.
101
- - `invocation_receipt` — state-bound `{path, sha256, sandbox, sandbox_network_access, argv_sha256, exit_code}` for schema-v3 Codex IMPLEMENT and historical worker PLAN/CLEANUP/BUILD_GATE. Receipt schema 2.0 makes `sandbox` exactly `workspace-write`; `sandbox_network_access` is exactly `true` for BUILD_GATE and `false` for the other mutation phases. A missing CI capability or wider phase authority blocks before launch. Pre-2.0 receipts are intentionally rejected rather than migrated because they did not attest this capability; do not rewrite in-flight evidence. Continue through the owning session or start in a distinct worktree; restarting via explicit manual archive requires the operator to establish that prior writers stopped, never automatic archival to defeat admission refusal. A completed final report does not prove all interactive writers exited. The receipt is retained in history and rehashed before archive.
102
- - `triggered_by` — null on first IMPLEMENT, phased advancement and phase-gate repair; an IMPLEMENT re-entry already carrying `"cleanup"` retains it when either path supplies null. Downstream repair records `"build_gate" | "cleanup" | "verify"`.
103
- - `pre_sha` — captured before SURFACE_CLOSE or CLEANUP. It bounds that phase's post-spawn diff and rollback.
104
- - `post_sha` — captured when SURFACE_CLOSE or CLEANUP completes; finish-gate subtracts CLEANUP's `pre_sha..post_sha` window.
105
- - `sub_verdicts` — only populated for VERIFY: `{ "mechanical": "PASS|FAIL", "judge": "PASS|...", "pair_judge": "PASS|..." | "TIMEOUT" | null }`. Values are normalized by `verify-merge-findings.py`; model prose verdicts cannot upgrade or downgrade the deterministic findings-derived verdict. `sub_verdicts.pair_judge` is `"TIMEOUT"` when a pair judge exceeded its wall budget and a valid `.devlyn/verify.pair.timeout.json` marker was read — semantics in `references/phases/verify.md` (pair budget section).
106
- - `judge_durations_ms` — only populated for VERIFY: `{ "judge": <non-negative int>, "pair_judge": <non-negative int|null> }`. The orchestrator writes each wall duration when it collects that judge's result; values remain a sibling of `sub_verdicts`, whose values stay normalized strings. VERIFY spawn resets this key to null so re-entry cannot retain prior-round timings.
107
- - `merged` — only populated for VERIFY after `verify-merge-findings.py --write-state`: `{ "verdict": "...", "findings_file": ".devlyn/verify-merged.findings.jsonl", "summary_file": ".devlyn/verify-merge.summary.json" }`.
108
- - `pair_trigger` — only populated for VERIFY; same shape as top-level `verify.pair_trigger` when the phase stores it locally.
109
- - `correctness.risk-probe-failed` — emitted by `spec-verify-check.py --include-risk-probes` when an executable probe derived from the visible `## Verification` section fails.
110
-
111
- ## Write protocol
112
-
113
- Every non-self-test writer command, including role freezing, holds `.devlyn/pipeline.state.lock` before state read through final write or artifact clearing. `verify-merge-findings.py --write-state` holds the same lock across findings read, merge and state write. Use one Python command to acquire `platform-support.py.file_lock(..., blocking=True)`, reread state, update `exec`, atomically replace state, and release before invoking the CLI. No nested lock or unlocked fallback.
114
-
115
- Phase lifecycle (`started_at`/`completed_at`/`duration_ms`/`round`/`triggered_by`/`verdict`) is written by a deterministic script, never hand-edited JSON — a prior hand-edited fix-loop respawn left `started_at` stale, corrupting cross-phase ordering because `completed_at`/`round`/`triggered_by` advanced to the new round while `started_at` didn't. Phase workers report their verdict and artifact paths in their reply; they never edit `pipeline.state.json` themselves.
116
-
117
- 1. **Spawn**: IMPLEMENT admission selects the greatest started `(round, phase-order)` among IMPLEMENT, BUILD_GATE, CLEANUP, VERIFY. A complete BUILD_GATE/CLEANUP FAIL, merged VERIFY NEEDS_WORK, or current IMPLEMENT gate FAIL admits one charged repair with the exact trigger; a completed phased IMPLEMENT with all preceding statuses PASS and current status null admits uncharged advancement. Both paths require the next invocation round. Counter validation and increment happen under the state lock, and refusal preserves original state bytes. `state-phase-write.py --devlyn-dir .devlyn --phase <name> spawn --round <N> [--triggered-by build_gate|cleanup|verify] [--pre-sha <sha>] [--engine <e>] [--model <requested-model>] [--prompt-sha256 <hex>]`. Re-entry preserves lifecycle/receipts in `history[]`, resets owned fields, and leaves fields such as `phases.implement.exec`; VERIFY also clears prior-round files. Owner PLAN/BUILD_GATE/CLEANUP spans omit engine/model/prompt arguments. Metadata-bearing PLAN/CLEANUP calls retain the legacy worker API; owner spans cannot be relabeled as workers. Successful owner CLEANUP requires pre/post SHA equality, unchanged HEAD/tracked source and no new untracked files versus the baseline; failure closure preserves the observed delta; code/doc cleanup belongs to the selected IMPLEMENT invocation. Schema-v3 Codex IMPLEMENT and historical worker CLEANUP requires the requested model and prompt digest; an inherited top-level Codex engine is persisted into the phase rather than becoming an unattested null route. SURFACE_CLOSE inherits native Claude model settings when `--model` is omitted and rejects explicit blank models; it additionally requires `--input-patch-sha256 <hex> --prompt-sha256 <hex> --untracked-before-json <array>`; use `surface-check --authorized-surface-json <array>`, `surface-adjudication-recover --authorized-surface-json <array>` only after its adjudication-malformed result, `surface-rollback` for other failures, or `surface-skip` only for automatic Claude unavailability.
118
- 2. **Transition** (normal direct handoff): `state-phase-write.py ... --phase <current> transition` takes the same completion/attestation arguments plus caller-specified `--next-phase`, `--next-round`, `--next-triggered-by`, `--next-engine`, `--next-model`, and next-phase metadata. It validates a fixed legal edge, applies complete plus spawn to a detached candidate, and replaces state once; either both lifecycle writes land or neither. A passing IMPLEMENT edge first validates and binds all declared process evidence through `_shared/process-evidence.py`. The verb returns JSON paths/digests/state facts only and never chooses a phase, engine, branch, prompt, or agent.
119
- 3. **Complete** (after the orchestrator reads the phase's reply and determines the verdict, when no next phase opens): `python3 "$DEVLYN_SHARED_DIR/state-phase-write.py" --devlyn-dir .devlyn --phase <name> complete --verdict <V> [--post-sha <sha>] [--findings-file <path>] [--log-file <path>] [--engine-session-log <path>]`. Pass only the phase/round canonical worker session. SURFACE_CLOSE requires `.devlyn/surface-close.output.json` even for inherited model selection (`model_requested=null`); native JSON supplies `model_effective`, and missing/malformed evidence or an explicit model mismatch blocks completion; schema-v3 Codex IMPLEMENT and historical worker PLAN/BUILD_GATE/CLEANUP pass the wrapper-captured `.devlyn/<phase>.worker-session.<round>.jsonl` and requires its sibling invocation receipt plus `.devlyn/<phase>.prompt.<round>`. Never scan an engine-global session directory or synthesize a model header. `duration_ms` is derived from the phase's own `started_at`. `--verdict` is required except VERIFY, whose verdict remains owned by `verify-merge-findings.py --write-state`.
120
- 4. **Before archive** (PHASE 6 step 5): write `.devlyn/final-report.md` with exactly one first-line `<!-- devlyn:final-report run_id=<current state.run_id> -->` and a non-whitespace body, then complete FINAL_REPORT with `--log-file .devlyn/final-report.md`. Missing, unsafe, empty or mismatched report input fails before completion; the marker proves run identity, not semantic completeness. This also applies to BLOCKED missing-PLAN-output closure. Archive rehashes a present FINAL_REPORT `output_sha256` binding before any move; missing/altered files or malformed/null bindings fail without moving artifacts. Historical states with that field absent retain existing archive behavior. Archive prune skips runs whose final_report verdict is null (treated as in-flight).
121
-
122
- ## Terminal verdict (PHASE 6)
123
-
124
- Precedence:
125
-
126
- 1. finish-gate exit 1 or 2 → `BLOCKED:finish-gate-unclean`; `phases.<any>.verdict == "BLOCKED"` → terminal `BLOCKED:<reason>`.
127
- 2. A refused shared repair admission with exhausted counters and current failing predecessor: VERIFY `NEEDS_WORK` → terminal `NEEDS_WORK`; BUILD_GATE/CLEANUP/phase-gate FAIL → `BLOCKED:repair-budget-exhausted`. Close a still-open predecessor after refused transition (`FAIL` for pre-VERIFY, no `--verdict` for VERIFY), preserve findings, then open FINAL_REPORT. Evaluate current-round exhaustion before any older VERIFY result.
128
- 3. `phases.verify.verdict == "PASS_WITH_ISSUES"` → terminal `PASS_WITH_ISSUES`; any finish-gate offender → `BLOCKED:finish-gate-unclean` under precedence 1.
129
- 4. `phases.verify.verdict == "PASS"` → terminal `PASS`.
130
- 5. Verify-only mode: terminal = `phases.verify.verdict` directly (PHASE 1-4 are skipped).
131
-
132
- ## Final-report shape
133
-
134
- Header: `run_id | engine | mode | complexity | verdict | wall_time_s`.
135
-
136
- Per-phase summary table: `phase | verdict | duration_ms | round | triggered_by | findings_count`; include SURFACE_CLOSE when run or skipped and identify new BUILD_GATE as orchestrator commands with no separate model.
137
-
138
- Findings table: each emitted finding's `severity | rule_id | file:line | message | confidence`.
139
-
140
- Follow-up notes: when `phases.surface_close.continued_after_block` is true, exactly one explicit `pipeline continued to BUILD_GATE — surface_close_rolled_back_adjudication_malformed` line (never a terminal floor); any large-mode assumptions; any pair-judge TIMEOUT surfaced as `solo verdict after pair TIMEOUT`; pair/risk-probe opt-out state; engine setup guidance for `BLOCKED:<engine>-unavailable`; and any `state.verify.coverage_failed` axes.
141
-
142
- ## Archive contract
143
-
144
- PHASE 6 step 4 moves `PER_RUN_PATTERNS` plus every state-bound process manifest/raw stream and invocation receipt into `.devlyn/runs/<run_id>/`, preserving relative layout and rehashing each bound digest first. It includes every phase/round prompt and worker session, including BUILD_GATE, and revalidates the receipt's internal prompt/session digests; it never scans engine-global sessions. Unsafe paths, symlinks, missing files, digest mismatches, and destination collisions block archive visibly without changing the already-derived product verdict. Machine config stays; the last 10 completed runs remain.
145
-
146
- ## Explicit role configuration
147
-
148
- PHASE0 `state-phase-write.py --freeze-roles --default-engine <orchestrator-default>` freezes `role_resolution` (legacy engine/source, three role entries, input paths/digests, snapshot digest). `role_config_input` holds only the validated per-run roles object and provenance; `role_no_pair` records that flag. No config reread after freezing. Missing legacy snapshots are compatible; present null/malformed snapshots fail. The helper does not launch models.
149
-
150
- `state.engine` remains the legacy executor; PLAN stays orchestrator-fixed, BUILD runs orchestrator commands, and probe routing is unchanged. IMPLEMENT (including code/doc cleanup) and historical worker CLEANUP enforce frozen worker engine/model. Owner CLEANUP does not claim that identity. VERIFY spawn explicitly records the primary in `phases.verify.engine`; merge, timeout and OTHER exclusion use it, falling back only when absent. Requested settings are distinct from native-observed fields. Explicit Codex worker effort binds `role_argv` against the existing invocation argv digest; effort remains dispatch evidence, not provider-internal attestation.
151
-
152
- Successful explicit judges retain `<engine>-judge.r<round>.*` raw/prompt/argv/derived/evidence files; `verify.role_evidence` binds their hashes, observed model/nullable effort and named evidence basis. VERIFY history preserves those bindings. Merge alone binds successful evidence; archive rehashes every bound artifact. Canonical `<engine>-judge.stdout` is the current collector input and is never substituted for the immutable round raw. Failed raw remains diagnostic and cannot become a successful binding.