devlyn-cli 3.1.3 → 3.2.1
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.
- package/README.md +21 -16
- package/bin/devlyn.js +36 -15
- package/bin/instruction-templates.json +782 -7
- package/bin/instructions.js +162 -28
- package/config/skills/_shared/archive_run.py +5 -1
- package/config/skills/_shared/codex-config.md +3 -14
- package/config/skills/_shared/engine-preflight.md +3 -3
- package/config/skills/_shared/role-config.py +14 -2
- package/config/skills/_shared/spec-verify-check.py +3 -4
- package/config/skills/_shared/state-phase-write.py +311 -18
- package/config/skills/_shared/task-complete.py +37 -4
- package/config/skills/_shared/terminal-claim-check.py +2 -2
- package/config/skills/devlyn:resolve/SKILL.md +25 -47
- package/config/skills/devlyn:resolve/references/phases/build-gate.md +5 -5
- package/config/skills/devlyn:resolve/references/phases/cleanup.md +5 -11
- package/config/skills/devlyn:resolve/references/phases/implement.md +1 -0
- package/config/skills/devlyn:resolve/references/phases/plan.md +3 -3
- package/config/skills/devlyn:resolve/references/state-schema.md +9 -8
- package/package.json +2 -1
- package/scripts/lint-skills.sh +12 -2
- package/scripts/test-owner-phases.py +238 -0
|
@@ -48,13 +48,13 @@ fi
|
|
|
48
48
|
</runtime_paths>
|
|
49
49
|
|
|
50
50
|
<engine_routing>
|
|
51
|
-
Each phase routes to an engine and prepends the per-engine adapter header from `_shared/adapters/<engine>.md` (e.g. `claude.md`, `codex.md`) to the canonical phase body. Adapter is the per-model delta (Anthropic's prompt-engineering guide for Claude, OpenAI's prompt guidance for Codex). Canonical body is engine-agnostic.
|
|
51
|
+
Each model-invoked phase routes to an engine and prepends the per-engine adapter header from `_shared/adapters/<engine>.md` (e.g. `claude.md`, `codex.md`) to the canonical phase body. Adapter is the per-model delta (Anthropic's prompt-engineering guide for Claude, OpenAI's prompt guidance for Codex). Canonical body is engine-agnostic.
|
|
52
52
|
|
|
53
|
-
-
|
|
54
|
-
- Claude Code phases
|
|
55
|
-
- Codex CLI phases: shell out via `bash "$CODEX_MONITORED_PATH"` with the same compounded prompt. Each `codex exec` child is a new session/fresh context. Write the compounded prompt to a file and set `DEVLYN_CODEX_PROMPT_FILE` with sole prompt argument `-`; the wrapper snapshots exact stdin bytes, seals transport evidence and emits a heartbeat. Without file transport stdin remains DEVNULL. No MCP. The wrapper call is foreground-blocking — never a background shell (`run_in_background`, `&`, `nohup`), never end your message while it runs: headless print-mode wind-down kills backgrounded children (0-byte delivery); the heartbeat is the observability channel.
|
|
56
|
-
- oh-my-pi phases: spawn the native `task` tool with a fresh `context` containing adapter-header + canonical-body + task-context. Capture the task result into `.devlyn/<phase>.stdout` and any tool error into `.devlyn/<phase>.stderr` before updating state. If the `task` tool is unavailable for an omp-routed phase, write the current phase verdict as `"BLOCKED"` and report `BLOCKED:fresh-context-unavailable`; do not fall back to same-context execution or a nested `omp -p` subprocess.
|
|
57
|
-
- Default engine: Claude when the orchestrator has Claude Code’s native `Agent`; otherwise its own fresh worker. PLAN is orchestrator-fixed and never inherits `--engine`, an executor pin, or `state.engine`. BUILD_GATE
|
|
53
|
+
- PLAN runs in the owner context; BUILD_GATE and CLEANUP run orchestrator commands with no separate model. The selected IMPLEMENT worker also owns code/doc cleanup before its final checkpoint. IMPLEMENT and VERIFY still require fresh workers. If the current CLI cannot spawn a required worker, write the current phase verdict as `"BLOCKED"` and report `BLOCKED:fresh-context-unavailable` with the failed spawn command; do not continue with ad-hoc same-context execution.
|
|
54
|
+
- Claude Code model-invoked phases: spawn `Agent` (`mode: "bypassPermissions"`); prompt = adapter-header + canonical-body + task-context.
|
|
55
|
+
- Codex CLI model-invoked phases: shell out via `bash "$CODEX_MONITORED_PATH"` with the same compounded prompt. Each `codex exec` child is a new session/fresh context. Write the compounded prompt to a file and set `DEVLYN_CODEX_PROMPT_FILE` with sole prompt argument `-`; the wrapper snapshots exact stdin bytes, seals transport evidence and emits a heartbeat. Without file transport stdin remains DEVNULL. No MCP. The wrapper call is foreground-blocking — never a background shell (`run_in_background`, `&`, `nohup`), never end your message while it runs: headless print-mode wind-down kills backgrounded children (0-byte delivery); the heartbeat is the observability channel.
|
|
56
|
+
- oh-my-pi model-invoked phases: spawn the native `task` tool with a fresh `context` containing adapter-header + canonical-body + task-context. Capture the task result into `.devlyn/<phase>.stdout` and any tool error into `.devlyn/<phase>.stderr` before updating state. If the `task` tool is unavailable for an omp-routed phase, write the current phase verdict as `"BLOCKED"` and report `BLOCKED:fresh-context-unavailable`; do not fall back to same-context execution or a nested `omp -p` subprocess.
|
|
57
|
+
- Default engine: Claude when the orchestrator has Claude Code’s native `Agent`; otherwise its own fresh worker. PLAN is orchestrator-fixed and never inherits `--engine`, an executor pin, or `state.engine`. BUILD_GATE runs orchestrator commands; probes retain their existing routes. `_shared/engine-preflight.md#role-resolution` defines the single resolver and explicit `--role-config` / project role precedence. Absent profiles preserve legacy `--engine` / executor / default behavior; VERIFY primary may be independently selected. Configured role/priority pins and flags fail closed on unavailable engine or failed authentication, including failures discovered at dispatch. Unconfigured automatic VERIFY selects an available OTHER engine.
|
|
58
58
|
- The `--engine` flag does not disable default pairing: the second judge uses the OTHER engine by default when available.
|
|
59
59
|
- Multi-LLM evolution: when a new model adapter ships in `_shared/adapters/`, that engine becomes selectable for the roles its adapter declares eligible without further skill changes (NORTH-STAR.md "Multi-LLM evolution direction").
|
|
60
60
|
</engine_routing>
|
|
@@ -72,7 +72,7 @@ After `state.implement_passed_sha` is set: SURFACE_CLOSE and CLEANUP are limited
|
|
|
72
72
|
</post_implement_invariant>
|
|
73
73
|
|
|
74
74
|
<transition_protocol>
|
|
75
|
-
For every direct complete→spawn handoff, call `state-phase-write.py ... --phase <current> transition` with the current phase's normal completion/attestation arguments plus caller-specified `--next-phase`, `--next-round`, `--next-triggered-by`, `--next-engine`, `--next-model`, and any next-phase metadata. The verb validates a legal edge and commits both lifecycle writes atomically; it returns JSON state facts only. It never selects a phase/engine, renders a prompt, or spawns an agent. Use standalone `spawn` only for the initial post-bootstrap dispatch; use standalone `complete` when no next phase opens (including a halt).
|
|
75
|
+
For every direct complete→spawn handoff, call `state-phase-write.py ... --phase <current> transition` with the current phase's normal completion/attestation arguments plus caller-specified `--next-phase`, `--next-round`, `--next-triggered-by`, `--next-engine`, `--next-model`, and any next-phase metadata. For owner PLAN/BUILD_GATE/CLEANUP, omit engine/model/prompt/session arguments. PLAN records `execution_kind: "orchestrator_context"`; BUILD_GATE/CLEANUP record `execution_kind: "orchestrator_commands"`, all with null worker identity. Existing metadata-bearing PLAN/CLEANUP and already-open absent-kind BUILD spans retain historical worker validation; never relabel their evidence. The verb validates a legal edge and commits both lifecycle writes atomically; it returns JSON state facts only. It never selects a phase/engine, renders a prompt, or spawns an agent. Use standalone `spawn` only for the initial post-bootstrap dispatch; use standalone `complete` when no next phase opens (including a halt).
|
|
76
76
|
</transition_protocol>
|
|
77
77
|
|
|
78
78
|
## PHASE 0: PARSE + CLASSIFY + ROUTE
|
|
@@ -97,7 +97,7 @@ Outer-owner boundary: before normal task writes, follow `references/task-complet
|
|
|
97
97
|
|
|
98
98
|
5. Announce one line: `resolve starting — run <run_id> — engine <engine> — mode <mode> — complexity <complexity-or-na> — pair <on|solo:auto_pair_other_engine_unavailable|disabled> — risk_probes <on|off>`.
|
|
99
99
|
|
|
100
|
-
6. Open the initial post-bootstrap span with standalone `state-phase-write.py ... spawn`: `plan` before
|
|
100
|
+
6. Open the initial post-bootstrap span with standalone `state-phase-write.py ... spawn`: `plan` before owner planning (normal run), or `verify` before MECHANICAL (verify-only). The bootstrap never chooses a phase, engine, branch, prompt, or agent.
|
|
101
101
|
|
|
102
102
|
### Explicit role dispatch
|
|
103
103
|
|
|
@@ -111,17 +111,13 @@ After the actual process exits, run `python3 "$DEVLYN_SHARED_DIR/judge-role-evid
|
|
|
111
111
|
|
|
112
112
|
## PHASE 1: PLAN
|
|
113
113
|
|
|
114
|
-
Skip in verify-only mode. The
|
|
114
|
+
Skip in verify-only mode. PLAN is orchestrator-fixed and never inherits `--engine`, an executor pin, or `state.engine`. The owner reads `references/phases/plan.md` and the original request/spec, then writes `.devlyn/plan.md` before product edits. Open with `state-phase-write.py ... --phase plan spawn --round 0`, without worker identity or a prompt dispatch. This is owner reasoning, not zero-cost mechanical work.
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
Subagent output (writes `.devlyn/plan.md`): file list to touch, risk list (out-of-scope expansions, ambiguous spec sections), acceptance restatement (what `## Verification` actually requires verbatim). For large work only, PLAN may add a fourth section `## Execution phases` under the conditions in `references/phases/plan.md`; a phase block missing a runnable `gate:` line disqualifies the whole section — treat the run as single-phase and log the disqualification.
|
|
119
|
-
|
|
120
|
-
State write: each PLAN receipt carries spawn-known `round`, `started_at`, `triggered_by`, `engine`, `model_requested`, and `prompt_sha256`; completion adds `completed_at`, `duration_ms`, `verdict`, nullable `model_effective`, and `output_sha256` under `references/state-schema.md`. Later mutations rehash bound output. Never edit PLAN to widen scope after completion; only the bounded PLAN re-spawn may replace its receipt.
|
|
116
|
+
Keep the file list/authorized surface, risks and verbatim verification requirements. Large work may include the existing execution-phase section only when every boundary has a runnable gate. Completing PLAN binds the exact output digest; all later mutations rehash it. Never widen it after implementation starts.
|
|
121
117
|
|
|
122
118
|
After return:
|
|
123
119
|
1. If `.devlyn/plan.md` lists zero files → halt with verdict `BLOCKED:plan-empty`.
|
|
124
|
-
2. If
|
|
120
|
+
2. If the plan exceeds the authorized scope, the sole correction is owner round 1 with `--triggered-by plan`, before implementation starts. Narrow to the original authorization, complete and bind the new output; a second failed plan halts. No worker dispatch or new permission is implied.
|
|
125
121
|
3. After any re-spawn above, if `state.risk_profile.risk_probes_enabled == true` and `state.risk_profile.risk_probes_explicit == false`, parse the `authorized_surface` array from the JSON block under `<!-- devlyn:authorized-surface -->`. For a well-formed string array, compute `probe_scale_small := len(authorized_surface) <= 2 AND no entry ends in "/**"`. If true, set `risk_probes_enabled = false`, leave `high_risk` unchanged, and append `auto-risk-probes demoted: plan surface small (<n> paths)` to `reasons` using the actual length. A missing or malformed block leaves probe state unchanged; BUILD_GATE owns its malformed-block failure.
|
|
126
122
|
|
|
127
123
|
## PHASE 1.5: RISK_PROBES
|
|
@@ -228,9 +224,9 @@ After return:
|
|
|
228
224
|
|
|
229
225
|
Skip in verify-only mode. Constrained design judgment within PLAN's invariants. Writes code, tests, and inline doc-comments. No standalone DOCS phase — what the spec licenses is updated here, what it does not is out of scope.
|
|
230
226
|
|
|
231
|
-
Engine/model/effort: frozen `role_resolution.roles.worker
|
|
227
|
+
Engine/model/effort: frozen `role_resolution.roles.worker`, including code/doc cleanup within this same invocation before its final checks; obtain validated argv additions using `role-config.py --state .devlyn/pipeline.state.json --role worker --resolved-model <existing-exact-phase-model>`. Prompt body: `references/phases/implement.md`.
|
|
232
228
|
|
|
233
|
-
For every Codex-routed IMPLEMENT
|
|
229
|
+
For every Codex-routed IMPLEMENT spawn, render the exact
|
|
234
230
|
prompt to `.devlyn/<phase>.prompt.<round>`, pass its SHA-256 to `state-phase-write.py
|
|
235
231
|
spawn --prompt-sha256`, and invoke only through `codex-monitored.sh` with these
|
|
236
232
|
seven variables set to the active state identity:
|
|
@@ -240,13 +236,14 @@ The session and receipt paths are `.devlyn/<phase>.worker-session.<round>.jsonl`
|
|
|
240
236
|
and `.devlyn/<phase>.invocation.<round>.json`. These three paths are round-scoped
|
|
241
237
|
so a retry cannot overwrite earlier prompt/session evidence. Every invocation
|
|
242
238
|
must include `--json -m <model_requested>` and `-c sandbox_workspace_write.network_access=<true|false>`: exactly
|
|
243
|
-
`
|
|
239
|
+
`false` for IMPLEMENT, so
|
|
244
240
|
user configuration cannot silently change the phase capability. Redirect wrapper stdout directly
|
|
245
241
|
to that session path. The wrapper rejects bypass/yolo flags and any sandbox other
|
|
246
242
|
than `workspace-write`. The receipt seals the requested model, sandbox, phase-scoped
|
|
247
243
|
network capability, prompt, terminal exit, and session digest; completion passes the
|
|
248
244
|
same canonical session via `--engine-session-log`. A missing/mismatched receipt
|
|
249
245
|
or same-round retry blocks. Respawn with a new round instead of replacing it.
|
|
246
|
+
Historical metadata-bearing PLAN/CLEANUP and absent-kind BUILD spans retain their original receipt-bound worker routes (network false for PLAN/CLEANUP, true for BUILD). New owner spans never create worker receipts.
|
|
250
247
|
|
|
251
248
|
State write: `phases.implement.{started_at, verdict, completed_at, duration_ms}`.
|
|
252
249
|
|
|
@@ -276,33 +273,22 @@ Run once iff `state.source.type == "generated"` and complexity is trivial/medium
|
|
|
276
273
|
|
|
277
274
|
Freeze `.devlyn/surface-close.input.patch`; assemble Claude adapter + `references/phases/surface-close.md` canonical body VERBATIM + data-only Goal/patch/hashes/surface/commands; write the exact bytes to `.devlyn/surface-close.prompt.<round>`, hash them and spawn with `--tools "Read,Grep,Glob,Edit,Write" --dangerously-skip-permissions --output-format json --strict-mcp-config --mcp-config '{"mcpServers":{}}'`, omitting `--model` at SPW spawn to inherit native Claude model settings. Record the effective model from the native JSON result. Use no positional prompt on the CLI route and retain its generated transport carrier. Bound at 600s (native or `run-bounded.py 600 --stdin-file .devlyn/surface-close.prompt.<round> --record-transport -- claude -p`), else block pre-spawn. Save raw stdout as `.devlyn/surface-close.output.json`, extract its `result` string to `.devlyn/surface-close.stdout`, retain `.devlyn/surface-close.worker-session.<round>.jsonl`, and complete with `.devlyn/surface-close.output.json` as `--engine-session-log`; non-JSON/missing result follows the existing failure path. Workers execute nothing; `surface-check` gates rows, citations, scope, and execution. Empty PASS completes; authorized delta is scoped-staged and committed. Only `BLOCKED:surface-close-adjudication-malformed` runs `surface-adjudication-recover --authorized-surface-json <array>`, whose successful rollback and transcript Edit/Write audit complete the nonterminal skip carrier and continue to BUILD_GATE. Timeout, input/prompt mismatch, attestation failure, execution-audit violation, and out-of-surface delta run `surface-rollback`, complete bare `BLOCKED`, and halt; rollback failure also halts. One shot; no `max_rounds`.
|
|
278
275
|
|
|
279
|
-
**Common post-fix checkpoint (BUILD_GATE and VERIFY):** After fix IMPLEMENT returns, increment `state.rounds.global`; scoped-stage the authorized surface and commit `chore(pipeline): implement fix round <n>`; run `python3 "$DEVLYN_SHARED_DIR/state-phase-write.py" --devlyn-dir .devlyn --phase implement durability-enforce --round <n> --origin-phase <build_gate|verify>`. Its receipt must PASS before re-entry, which enforces it again before VERIFY artifact clearing or phase spawn.
|
|
276
|
+
**Common post-fix checkpoint (BUILD_GATE and VERIFY):** CLEANUP findings use the same checkpoint with origin `cleanup`. After fix IMPLEMENT returns, increment `state.rounds.global`; scoped-stage the authorized surface and commit `chore(pipeline): implement fix round <n>`; run `python3 "$DEVLYN_SHARED_DIR/state-phase-write.py" --devlyn-dir .devlyn --phase implement durability-enforce --round <n> --origin-phase <build_gate|cleanup|verify>`. Its receipt must PASS before re-entry, which enforces it again before VERIFY artifact clearing or phase spawn.
|
|
280
277
|
|
|
281
278
|
## PHASE 3: BUILD_GATE
|
|
282
279
|
|
|
283
280
|
Skip in verify-only mode OR when `build-gate` in `state.bypasses`. Deterministic — same commands CI / Docker / production run.
|
|
284
281
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
must not add isolation, narrow the sandbox, or widen permissions inside the
|
|
289
|
-
worker. Invoke a Codex BUILD_GATE with
|
|
290
|
-
`-s workspace-write -c sandbox_workspace_write.network_access=true`; the
|
|
291
|
-
receipt rejects a missing, false, duplicate, or non-BUILD_GATE network override
|
|
292
|
-
before the worker starts. This retains the write sandbox while allowing the
|
|
293
|
-
loopback and network operations exercised by CI-equivalent gates. If an
|
|
294
|
-
authoritative route/tool response denies a required filesystem,
|
|
295
|
-
subprocess, loopback, PTY, or network operation, preserve that denial through
|
|
296
|
-
the BUILD_GATE process-evidence manifest and complete the phase as
|
|
297
|
-
`BLOCKED:build-env-underprovisioned`, not as a product finding or substitute
|
|
298
|
-
command. The worker:
|
|
282
|
+
The orchestrator reads `references/phases/build-gate.md` and runs the gates directly: no adapter header, rendered worker prompt or separate model. Open its span through the predecessor transition with no BUILD engine/model/prompt/session arguments. New spans record `execution_kind: "orchestrator_commands"`; historical already-open spans with that field absent retain their worker attestation contract.
|
|
283
|
+
|
|
284
|
+
Follow the BUILD body's capability contract on the parent's actual command route and log that basis; no child receipt attests it. Authoritative denial yields report-level `BLOCKED:build-env-underprovisioned` (bare `BLOCKED` in state). Keep spec/probe capture and its conditional post-spec denial carrier refresh; ordinary gates retain findings and raw logs. The orchestrator:
|
|
299
285
|
1. Detects language/framework via project files (`package.json`, `pyproject.toml`, etc.).
|
|
300
286
|
2. Runs language-specific gates (tsc / lint / test), deferring exact literal overlaps under `references/phases/build-gate.md`'s current-contract/current-round rule; task-context prompts must preserve that rule.
|
|
301
287
|
3. Always runs `python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py" --include-risk-probes` (verification_commands literal-match plus `.devlyn/risk-probes.jsonl` when present). If `state.risk_profile.risk_probes_enabled == true`, the script requires `.devlyn/risk-probes.jsonl`; a missing file is a CRITICAL mechanical blocker, not a silent solo run. The script routes each command through `process-evidence.py`; preserve `.devlyn/spec-verify.results.json` and its validated BUILD_GATE carrier for the state-bound archive flow.
|
|
302
288
|
4. If diff touches web-surface files: run the browser tier with the repo's available toolchain (for example Playwright or curl).
|
|
303
289
|
5. Emits `.devlyn/build_gate.findings.jsonl` + `.devlyn/build_gate.log.md`.
|
|
304
290
|
|
|
305
|
-
State write: `phases.build_gate.{started_at, verdict, completed_at, duration_ms, artifacts}
|
|
291
|
+
State write: `phases.build_gate.{execution_kind, started_at, verdict, completed_at, duration_ms, artifacts}`; engine/model fields are null, with no worker prompt or receipt. Completion keeps PLAN integrity and process-evidence floors.
|
|
306
292
|
|
|
307
293
|
Branch:
|
|
308
294
|
- `PASS` → PHASE 4.
|
|
@@ -311,21 +297,13 @@ Branch:
|
|
|
311
297
|
|
|
312
298
|
## PHASE 4: CLEANUP
|
|
313
299
|
|
|
314
|
-
Skip if `cleanup` in `state.bypasses`.
|
|
300
|
+
Skip if `cleanup` in `state.bypasses`. The owner reads `references/phases/cleanup.md`; no separate model invocation. Code/doc cleanup already belongs to the selected IMPLEMENT worker, with its original engine/model/effort and receipt.
|
|
315
301
|
|
|
316
|
-
|
|
317
|
-
- Tooling artifacts the spec did not list as deliverables (`test-results/`, `playwright-report/`, `.last-run.json`, coverage HTML).
|
|
318
|
-
- Dead code added by this diff (not pre-existing dead code).
|
|
319
|
-
- Doc references whose target this diff renamed or removed.
|
|
302
|
+
Open through the predecessor transition with `--next-pre-sha "$(git rev-parse HEAD)"`, omitting worker identity. Remove only observed generated, untracked/ignored artifacts owned by this run and not required deliverables; preserve source, user data, evidence and restart records. For PASS, tracked source and HEAD must equal `pre_sha`; the state writer checks them against `--post-sha` and rejects new untracked files relative to `.devlyn/untracked.baseline` (`.devlyn/` exempt). FAIL/BLOCKED closure preserves the observed delta and cannot enter VERIFY.
|
|
320
303
|
|
|
321
|
-
|
|
304
|
+
If a new code/doc cleanup issue appears after BUILD, emit a concrete finding and complete CLEANUP as FAIL into the existing selected IMPLEMENT repair route with `--next-triggered-by cleanup`, within the shared round budget. After repair, checkpoint and rerun BUILD_GATE before CLEANUP and fresh VERIFY; never reuse the earlier green checks. Keep the existing authorized surface, allowlist and durability checks. Do not broaden scope for cleanup.
|
|
322
305
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
After return:
|
|
326
|
-
1. Run `git diff --name-only <pre_sha>` and compare new untracked files against `.devlyn/untracked.baseline` (`.devlyn/` exempt) — any path outside the cleanup allowlist (including a cleanup-created untracked file) → revert to `pre_sha`, delete the unauthorized untracked file, and emit `invariant.cleanup-out-of-scope` into `.devlyn/cleanup.findings.jsonl`.
|
|
327
|
-
2. If allowlist honored and diff non-empty: `git add -u && git commit -m "chore(pipeline): cleanup"`.
|
|
328
|
-
3. Transition CLEANUP→VERIFY through `<transition_protocol>` with `--post-sha "$(git rev-parse HEAD)"` (also when the diff was empty), preserving completion/session arguments and the next-phase identity below.
|
|
306
|
+
On success transition CLEANUP→VERIFY with `--post-sha "$(git rev-parse HEAD)"` and the next judge identity. VERIFY MECHANICAL runs independently on the final source.
|
|
329
307
|
|
|
330
308
|
## PHASE 5: VERIFY (fresh subagent, findings-only)
|
|
331
309
|
|
|
@@ -378,7 +356,7 @@ Open the `final_report` span through the predecessor's `state-phase-write.py --d
|
|
|
378
356
|
|
|
379
357
|
3. **Terminal verdict** — derive from `state.phases.{plan, implement, surface_close, build_gate, cleanup, verify}.verdict` per the precedence rules in `references/state-schema.md#terminal-verdict`. Verify-only mode short-circuits to `state.phases.verify.verdict`.
|
|
380
358
|
|
|
381
|
-
4. **Render report to `.devlyn/final-report.md` before completion** — first line exactly `<!-- devlyn:final-report run_id=<current state.run_id> -->`, once only, followed by a nonempty report body. Sections: header (run_id, engine, mode, verdict, wall-time), per-phase summary (including SURFACE_CLOSE run or skip), pair/risk-probe status, findings table (verify + finish-gate findings), follow-up notes (the explicit line `pipeline continued to BUILD_GATE — surface_close_rolled_back_adjudication_malformed` when `continued_after_block` is set, any large-mode `## Assumptions` block, any pair-judge TIMEOUT (headline: solo verdict after pair TIMEOUT), any `--no-pair` / `--no-risk-probes` opt-out, any engine setup guidance after BLOCKED, `/devlyn:ideate` guidance after `BLOCKED:solo-headroom-hypothesis-required` that asks for the visible behavior `solo_claude` is expected to miss, and `/devlyn:ideate` guidance after `BLOCKED:solo-ceiling-avoidance-required` that asks for the concrete difference from rejected or solo-saturated controls such as `S2`-`S6`). User-facing text may follow archive; it cannot substitute for this file.
|
|
359
|
+
4. **Render report to `.devlyn/final-report.md` before completion** — first line exactly `<!-- devlyn:final-report run_id=<current state.run_id> -->`, once only, followed by a nonempty report body. Sections: header (run_id, engine, mode, verdict, wall-time), per-phase summary (including SURFACE_CLOSE run or skip, owner PLAN reasoning, and BUILD_GATE/CLEANUP as orchestrator commands with no separate model; code cleanup is included in IMPLEMENT cost), pair/risk-probe status, findings table (verify + finish-gate findings), follow-up notes (the explicit line `pipeline continued to BUILD_GATE — surface_close_rolled_back_adjudication_malformed` when `continued_after_block` is set, any large-mode `## Assumptions` block, any pair-judge TIMEOUT (headline: solo verdict after pair TIMEOUT), any `--no-pair` / `--no-risk-probes` opt-out, any engine setup guidance after BLOCKED, `/devlyn:ideate` guidance after `BLOCKED:solo-headroom-hypothesis-required` that asks for the visible behavior `solo_claude` is expected to miss, and `/devlyn:ideate` guidance after `BLOCKED:solo-ceiling-avoidance-required` that asks for the concrete difference from rejected or solo-saturated controls such as `S2`-`S6`). User-facing text may follow archive; it cannot substitute for this file.
|
|
382
360
|
|
|
383
361
|
5. Complete the span with `state-phase-write.py --devlyn-dir .devlyn --phase final_report complete --verdict <bare enum> --log-file .devlyn/final-report.md` — the enum class of the terminal verdict (`BLOCKED:<reason>` → `BLOCKED`; `NEEDS_WORK` / `PASS_WITH_ISSUES` / `PASS` unchanged) — BEFORE archive runs (archive prune skips runs whose `final_report.verdict` is null). The writer validates the canonical nonsymlink regular file, current run marker and nonempty body, then binds its exact bytes; validation failure leaves the phase open. Never hand-edit lifecycle fields in `pipeline.state.json` (`references/state-schema.md` § Write protocol).
|
|
384
362
|
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
# PHASE 3 — BUILD_GATE (canonical body)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The orchestrator reads this body directly, without an adapter header or worker prompt. BUILD_GATE runs the same commands CI / Docker / production run; gate selection and diagnosis remain orchestrator work.
|
|
4
4
|
|
|
5
5
|
<role>
|
|
6
6
|
Run language-specific gates and the spec literal-match verification. Emit findings; the orchestrator's fix loop consumes them. Git staging and commits belong to the parent IMPLEMENT checkpoint; verify any staging prerequisite with read-only Git inspection, never repeat `git add` or forward task staging instructions as worker actions.
|
|
7
7
|
</role>
|
|
8
8
|
|
|
9
9
|
<capability_contract>
|
|
10
|
-
|
|
11
|
-
filesystem, subprocess, loopback, PTY, and network capabilities are part of the
|
|
12
|
-
gate input, not permissions this worker may widen.
|
|
10
|
+
Before executing gates, inspect the orchestrator command route's effective filesystem, subprocess, loopback, PTY and network capabilities and record the actual basis in `.devlyn/build_gate.log.md`. Use an already authorized CI-equivalent route; do not infer capabilities from an engine name or unrelated command success, narrow the route, or widen permissions. No child receipt attests these parent capabilities.
|
|
13
11
|
|
|
14
12
|
If the parent route or an authoritative tool response explicitly denies a
|
|
15
13
|
required operation before the command can produce product output:
|
|
@@ -24,6 +22,8 @@ required operation before the command can produce product output:
|
|
|
24
22
|
3. Do not emit a product finding, run a substitute command, narrow the command,
|
|
25
23
|
retry it on another route, or treat the denial as test/lint output.
|
|
26
24
|
|
|
25
|
+
If a denial is appended after `.devlyn/spec-verify.results.json` was sealed, refresh only that existing object's `process_evidence` and `commands` before completion. Load `$DEVLYN_SHARED_DIR/process-evidence.py` with Python's `runpy.run_path`; using the current work root, `state.run_id`, `"build_gate"`, and its round, call `validate_manifest(work, manifest_relative_path(state, "build_gate"), run_id, "build_gate", round_, require_expectations=False)`, then `bound_carrier_summary_commands(work, carrier)` and `validate_summary_commands(work, commands, carrier)`. Replace only those two result fields with the returned carrier/commands. Preserve all other fields, genuine findings, failed expectations and raw streams; do not rewrite the manifest or replay commands. This is conditional on an actual append, not routine browser resealing. If no results file exists, retain the existing BLOCKED completion path.
|
|
26
|
+
|
|
27
27
|
The state writer derives the phase verdict floor from this sealed manifest. A
|
|
28
28
|
capability denial can complete only as `BLOCKED`; a failed product expectation
|
|
29
29
|
cannot complete as `PASS` or `PASS_WITH_ISSUES` even if a mutable results or
|
|
@@ -51,7 +51,7 @@ Resolve the concrete commands for steps 1–3; detection examples are not comman
|
|
|
51
51
|
1. **Type check** (TypeScript / mypy / etc.). Each error → one finding, severity `HIGH`, rule `correctness.type-check`.
|
|
52
52
|
2. **Lint**. Run a recognized configured language linter (for example, ESLint with a config, `ruff`, or `clippy`) and/or the exact `package.json` `scripts.lint` command. A declared specialized `lint:*` script (for example, `lint:json`) is not the general language lint gate on the strength of its declaration alone. If the spec's verification commands explicitly name such a specialized command, step 4 executes it and preserves its real failure severity. If no linter is applicable, log an explicit SKIP in `.devlyn/build_gate.log.md` and emit zero lint findings. Each error → finding, severity `MEDIUM`, rule `quality.lint`. Warnings stay LOW unless the spec elevates them.
|
|
53
53
|
3. **Test suite** (npm test / pytest / go test / cargo test). Each failing test → finding, severity `HIGH`, rule `correctness.test-failure`. Include the failing test's file:line and the assertion.
|
|
54
|
-
4. **Spec literal verification + risk probes**: `python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py" --include-risk-probes`. The script routes every literal command and risk probe through `process-evidence.py`, preserving exact command, outcome, raw stdout/stderr, and digests in the BUILD_GATE run/round manifest. It self-stages from sibling `spec.expected.json` next to `state.source.spec_path`, or the legacy inline carrier when the sibling is absent; benchmark-prestaged `.devlyn/spec-verify.json` still wins. It appends `.devlyn/risk-probes.jsonl` when present, and requires that file when `state.risk_profile.risk_probes_enabled == true`. Malformed `state.risk_profile` is also CRITICAL because it can hide enabled risk probes. When probes are enabled or `.devlyn/risk-probes.jsonl` exists, missing or mismatched top-level `state.risk_probes_digest` emits `correctness.risk-probe-integrity` CRITICAL; the digest binds `.devlyn/risk-probes.jsonl` plus referenced `.devlyn/probes/` script bytes. Command or risk-probe mismatch → CRITICAL finding. Missing required risk probes, missing/malformed generated carrier, or malformed sibling expected file → `correctness.spec-verify-malformed` CRITICAL. The same invocation also enforces PLAN's declared `authorized_surface` (the json block under `.devlyn/plan.md`'s `<!-- devlyn:authorized-surface -->` section, located by that sentinel regardless of the heading's language) against this run's diff and untracked delta: any changed path or created-during-run untracked path outside the declared surface → `scope.out-of-scope-file` CRITICAL (`.devlyn/` exempt) — the only sanctioned fix is removing the file, never widening the declared surface, since that would let the same worker that leaked scope self-authorize the leak; missing sentinel, malformed `plan.md`/surface block, or missing `.devlyn/untracked.baseline` → `scope.authorized-surface-malformed` CRITICAL. This scope check runs only here (BUILD_GATE), not when VERIFY MECHANICAL reuses this script post-CLEANUP,
|
|
54
|
+
4. **Spec literal verification + risk probes**: `python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py" --include-risk-probes`. The script routes every literal command and risk probe through `process-evidence.py`, preserving exact command, outcome, raw stdout/stderr, and digests in the BUILD_GATE run/round manifest. It self-stages from sibling `spec.expected.json` next to `state.source.spec_path`, or the legacy inline carrier when the sibling is absent; benchmark-prestaged `.devlyn/spec-verify.json` still wins. It appends `.devlyn/risk-probes.jsonl` when present, and requires that file when `state.risk_profile.risk_probes_enabled == true`. Malformed `state.risk_profile` is also CRITICAL because it can hide enabled risk probes. When probes are enabled or `.devlyn/risk-probes.jsonl` exists, missing or mismatched top-level `state.risk_probes_digest` emits `correctness.risk-probe-integrity` CRITICAL; the digest binds `.devlyn/risk-probes.jsonl` plus referenced `.devlyn/probes/` script bytes. Command or risk-probe mismatch → CRITICAL finding. Missing required risk probes, missing/malformed generated carrier, or malformed sibling expected file → `correctness.spec-verify-malformed` CRITICAL. The same invocation also enforces PLAN's declared `authorized_surface` (the json block under `.devlyn/plan.md`'s `<!-- devlyn:authorized-surface -->` section, located by that sentinel regardless of the heading's language) against this run's diff and untracked delta: any changed path or created-during-run untracked path outside the declared surface → `scope.out-of-scope-file` CRITICAL (`.devlyn/` exempt) — the only sanctioned fix is removing the file, never widening the declared surface, since that would let the same worker that leaked scope self-authorize the leak; missing sentinel, malformed `plan.md`/surface block, or missing `.devlyn/untracked.baseline` → `scope.authorized-surface-malformed` CRITICAL. This scope check runs only here (BUILD_GATE), not when VERIFY MECHANICAL reuses this script post-CLEANUP, while owner CLEANUP separately enforces unchanged tracked source and the final untracked baseline. Historical worker CLEANUP retains its existing allowlist.
|
|
55
55
|
5. **Browser** (only when diff touches `*.tsx`, `*.jsx`, `*.vue`, `*.svelte`, `page.*`, `layout.*`, `route.*`, `*.css`, `*.html`): start the dev server and run the repo's existing browser checks, or a minimal curl/HTML check when no browser test harness exists. Each failed check → finding, severity `HIGH`, rule `correctness.browser-flow-failed`.
|
|
56
56
|
|
|
57
57
|
Append all findings; do not stop on the first failure.
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
# PHASE 4 — CLEANUP (canonical body)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
<role>
|
|
6
|
-
Remove tooling artifacts, dead code added by this diff, and doc references invalidated by this diff. The cleanup is bounded by an allowlist enforced post-spawn.
|
|
7
|
-
</role>
|
|
3
|
+
The selected IMPLEMENT worker performs code/doc cleanup in its existing invocation before its final checks. The owner then uses the CLEANUP span for generated-artifact removal and invariant checks only; no new model call.
|
|
8
4
|
|
|
9
5
|
<input>
|
|
10
|
-
- Cumulative diff
|
|
11
|
-
-
|
|
12
|
-
- `state.phases.cleanup.pre_sha` (the orchestrator captured this before spawn — your post-cleanup diff against this SHA must stay within the allowlist).
|
|
6
|
+
- Cumulative diff, original spec and immutable PLAN authorized surface.
|
|
7
|
+
- CLEANUP `pre_sha`: tracked source and HEAD must remain unchanged during owner checks.
|
|
13
8
|
</input>
|
|
14
9
|
|
|
15
10
|
<allowlist>
|
|
16
|
-
|
|
11
|
+
Within IMPLEMENT and PLAN authorization, you may modify or delete:
|
|
17
12
|
|
|
18
13
|
1. **Tooling artifacts** the spec did not list as deliverables: `test-results/`, `playwright-report/`, `.last-run.json`, coverage HTML output, build artifacts, runtime caches (`__pycache__/`, `*.pyc`, `.cache/`).
|
|
19
14
|
2. **Dead code added by this diff** — symbols (functions, classes, types, exports) introduced by this diff that no other code added by this diff references AND that are not part of the spec's required surface. Pre-existing dead code is out of scope.
|
|
@@ -24,8 +19,7 @@ Files outside this allowlist must not change. Pre-existing tooling leaks (alread
|
|
|
24
19
|
</allowlist>
|
|
25
20
|
|
|
26
21
|
<output>
|
|
27
|
-
-
|
|
28
|
-
- Report your verdict in this reply: `PASS` if changes within allowlist (or no changes needed); `FAIL` if you cannot complete within the allowlist (the orchestrator will revert). Do not edit `pipeline.state.json` yourself — the orchestrator records it via `state-phase-write.py`.
|
|
22
|
+
The owner removes only run-owned untracked/ignored generated artifacts from item 1, preserving deliverables, evidence and recovery inputs. It records PASS when the source checkpoint is unchanged and no cleanup finding remains. A code/doc finding returns through the selected IMPLEMENT repair route and reruns BUILD before fresh VERIFY. Never modify source inside owner CLEANUP or reuse stale checks. Record lifecycle via `state-phase-write.py`.
|
|
29
23
|
</output>
|
|
30
24
|
|
|
31
25
|
<quality_bar>
|
|
@@ -14,6 +14,7 @@ You execute the plan. Constrained design judgment within PLAN's invariants — w
|
|
|
14
14
|
|
|
15
15
|
<output>
|
|
16
16
|
- Code changes implementing every Requirement. Verify with `git diff`.
|
|
17
|
+
- In this same selected worker invocation, remove dead code/comments introduced by this diff and repair references it invalidated, within PLAN authorization and `references/phases/cleanup.md`. Finish before the final checks/checkpoint; no separate cleanup model is dispatched.
|
|
17
18
|
- Tests added or updated for changed behavior. Run the focused development tests needed to establish that behavior; BUILD_GATE and VERIFY own the post-implementation full suite.
|
|
18
19
|
- For every sibling `spec.expected.json.process_evidence[]` item whose `phase` is `implement`, run `python3 "$DEVLYN_SHARED_DIR/process-evidence.py" --devlyn-dir .devlyn run --phase implement --id '<id>'`. The runner must report `expectation_met: true`; cite its manifest path in the phase reply.
|
|
19
20
|
- For each criterion satisfied, set `state.criteria[i].status: "implemented"` with an `evidence` record `{"file": "...", "line": N, "note": "brief"}`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# PHASE 1 — PLAN (canonical body)
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The orchestrator reads this body in its existing context; no PLAN worker is dispatched.
|
|
4
4
|
|
|
5
5
|
<role>
|
|
6
6
|
You translate a spec or generated criteria into a concrete plan: the file list to touch, the risks the implementation must navigate, and a verbatim restatement of what acceptance requires. The plan is the contract IMPLEMENT executes against.
|
|
@@ -20,14 +20,14 @@ Write `.devlyn/plan.md` with three sections:
|
|
|
20
20
|
3. **Acceptance restatement** — verbatim copy of the spec's `## Verification` block (or generated criteria's equivalent). The plan is wrong if any verification command later fails because of a planning oversight.
|
|
21
21
|
4. **Execution phases** — conditional; large work only. Emit this section ONLY when ALL hold: (a) spec frontmatter `complexity: high` (legacy `large`) or `state.complexity == "large"`; (b) the work spans multiple subsystems or more than ~8 files; (c) every phase boundary has at least one runnable gate command. Otherwise emit exactly the three sections above — when in doubt, do not decompose. When emitted: 2-5 `### Phase <k> — <title>` blocks, each with task checkboxes (`- [ ]`), a `gate:` line (1-2 commands, exit-code truth, runnable at that boundary), and the files it owns (a subset of section 1). Definitions are written once here and are the contract; the orchestrator later updates checkboxes as a display mirror only — routing truth lives in `pipeline.state.json`.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Record via `state-phase-write.py`: `PASS` if plan is shippable; `BLOCKED` if spec is internally contradictory or cannot be planned without violating constraints. Never hand-edit lifecycle fields.
|
|
24
24
|
</output>
|
|
25
25
|
|
|
26
26
|
<quality_bar>
|
|
27
27
|
- Scope first, then implementation. Decide what files to touch before deciding how to implement. Files not in the list are off-limits to IMPLEMENT.
|
|
28
28
|
- Tooling artifacts and reporter output are not deliverables unless the spec lists them. Plan to configure tools to emit to gitignored paths.
|
|
29
29
|
- Existing tests are contract. Plan to extend them; do not plan to remove or weaken them.
|
|
30
|
-
- Spec frontmatter is read-only
|
|
30
|
+
- Spec frontmatter is read-only; cleanup cannot broaden the task contract.
|
|
31
31
|
- If a Requirement says "match the literal output X", restate the literal in the plan. Paraphrasing the contract here propagates into IMPLEMENT.
|
|
32
32
|
</quality_bar>
|
|
33
33
|
|
|
@@ -92,13 +92,14 @@ Each entry under `phases.<name>` (for `plan`, `probe_derive`, `implement`, `surf
|
|
|
92
92
|
}
|
|
93
93
|
```
|
|
94
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.
|
|
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
96
|
|
|
97
97
|
- `verdict` — `"PASS" | "PASS_WITH_ISSUES" | "FAIL" | "NEEDS_WORK" | "BLOCKED"`. PHASE 6 (FINAL_REPORT) writes its own verdict per the terminal-verdict precedence.
|
|
98
|
-
- `
|
|
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.
|
|
99
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`.
|
|
100
|
-
- `invocation_receipt` — state-bound `{path, sha256, sandbox, sandbox_network_access, argv_sha256, exit_code}` for schema-v3 Codex PLAN/
|
|
101
|
-
- `triggered_by` — null on first run; one of `"build_gate" | "verify"` when the phase is a fix-loop respawn.
|
|
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 run; one of `"build_gate" | "cleanup" | "verify"` when the phase is a fix-loop respawn.
|
|
102
103
|
- `pre_sha` — captured before SURFACE_CLOSE or CLEANUP. It bounds that phase's post-spawn diff and rollback.
|
|
103
104
|
- `post_sha` — captured when SURFACE_CLOSE or CLEANUP completes; finish-gate subtracts CLEANUP's `pre_sha..post_sha` window.
|
|
104
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).
|
|
@@ -111,9 +112,9 @@ Each entry under `phases.<name>` (for `plan`, `probe_derive`, `implement`, `surf
|
|
|
111
112
|
|
|
112
113
|
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.
|
|
113
114
|
|
|
114
|
-
1. **Spawn**: `state-phase-write.py --devlyn-dir .devlyn --phase <name> spawn --round <N> [--triggered-by build_gate|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. Schema-v3 Codex IMPLEMENT
|
|
115
|
+
1. **Spawn**: `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.
|
|
115
116
|
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.
|
|
116
|
-
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 PLAN/
|
|
117
|
+
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`.
|
|
117
118
|
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).
|
|
118
119
|
|
|
119
120
|
## Terminal verdict (PHASE 6)
|
|
@@ -130,7 +131,7 @@ Precedence:
|
|
|
130
131
|
|
|
131
132
|
Header: `run_id | engine | mode | complexity | verdict | wall_time_s`.
|
|
132
133
|
|
|
133
|
-
Per-phase summary table: `phase | verdict | duration_ms | round | triggered_by | findings_count`; include SURFACE_CLOSE when run or skipped.
|
|
134
|
+
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.
|
|
134
135
|
|
|
135
136
|
Findings table: each emitted finding's `severity | rule_id | file:line | message | confidence`.
|
|
136
137
|
|
|
@@ -144,6 +145,6 @@ PHASE 6 step 4 moves `PER_RUN_PATTERNS` plus every state-bound process manifest/
|
|
|
144
145
|
|
|
145
146
|
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.
|
|
146
147
|
|
|
147
|
-
`state.engine` remains the legacy executor
|
|
148
|
+
`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.
|
|
148
149
|
|
|
149
150
|
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.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devlyn-cli",
|
|
3
|
-
"version": "3.1
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "Loop & harness engineering for AI coding agents (Claude Code, Codex, and any AGENTS.md-driven agent) — take intent to shipped, engineer-quality software hands-free with phase-gated pipelines and agent orchestration",
|
|
5
5
|
"homepage": "https://github.com/fysoul17/devlyn-cli#readme",
|
|
6
6
|
"bin": {
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"scripts/lint-fixtures.sh",
|
|
77
77
|
"scripts/lint-shadow-fixtures.sh",
|
|
78
78
|
"scripts/lint-skills.sh",
|
|
79
|
+
"scripts/test-owner-phases.py",
|
|
79
80
|
"CLAUDE.md",
|
|
80
81
|
"AGENTS.md"
|
|
81
82
|
],
|
package/scripts/lint-skills.sh
CHANGED
|
@@ -569,6 +569,13 @@ if ! grep -Fq 'SAFE_RUN_ID_RE' config/skills/_shared/archive_run.py \
|
|
|
569
569
|
bad "archive_run.py must safely archive pair/risk-probe evidence and reject unsafe run ids"
|
|
570
570
|
fi
|
|
571
571
|
|
|
572
|
+
section "Check 6c1: Owner phase lifecycle and scope boundaries"
|
|
573
|
+
if python3 scripts/test-owner-phases.py; then
|
|
574
|
+
ok "owner phase CLI and Git regression tests passed"
|
|
575
|
+
else
|
|
576
|
+
bad "owner phase CLI and Git regression tests failed"
|
|
577
|
+
fi
|
|
578
|
+
|
|
572
579
|
section "Check 6c2: SURFACE_CLOSE v6 dispatch is complete and mirrored"
|
|
573
580
|
surface_close_missing=0
|
|
574
581
|
if python3 config/skills/_shared/state-phase-write.py --self-test >/dev/null 2>&1; then
|
|
@@ -632,7 +639,7 @@ for tree in config/skills .agents/skills; do
|
|
|
632
639
|
|| ! grep -Fq -- '--tools "Read,Grep,Glob,Edit,Write" --dangerously-skip-permissions --output-format json --strict-mcp-config --mcp-config '\''{"mcpServers":{}}'\''' "$skill" \
|
|
633
640
|
|| ! grep -Fq '.devlyn/surface-close.output.json' "$skill" \
|
|
634
641
|
|| ! grep -Fq '**Common post-fix checkpoint (BUILD_GATE and VERIFY):**' "$skill" \
|
|
635
|
-
|| ! grep -Fq 'durability-enforce --round <n> --origin-phase <build_gate|verify>' "$skill" \
|
|
642
|
+
|| ! grep -Fq 'durability-enforce --round <n> --origin-phase <build_gate|cleanup|verify>' "$skill" \
|
|
636
643
|
|| ! grep -Fq '`surface-rollback`' "$skill" \
|
|
637
644
|
|| grep -Fq 'Supplied digests' "$phase" \
|
|
638
645
|
|| grep -Fq 'Hash both artifacts first' "$phase" \
|
|
@@ -1346,7 +1353,10 @@ for file in \
|
|
|
1346
1353
|
config/skills/devlyn:resolve/SKILL.md \
|
|
1347
1354
|
.agents/skills/devlyn:resolve/SKILL.md
|
|
1348
1355
|
do
|
|
1349
|
-
if ! grep -Fq '
|
|
1356
|
+
if ! grep -Fq 'orchestrator commands with no separate model' "$file" \
|
|
1357
|
+
|| ! grep -Fq 'execution_kind: "orchestrator_commands"' "$file" \
|
|
1358
|
+
|| grep -Fq 'resolved BUILD_GATE engine through the fresh-worker route' "$file" \
|
|
1359
|
+
|| grep -Fq 'Codex-routed IMPLEMENT, BUILD_GATE, or CLEANUP spawn' "$file" \
|
|
1350
1360
|
|| ! grep -Fq 'BLOCKED:build-env-underprovisioned' "$file" \
|
|
1351
1361
|
|| ! grep -Fq '.devlyn/<primary-engine>-judge.stdout' "$file" \
|
|
1352
1362
|
|| ! grep -Fq '.devlyn/<other-engine>-judge.stdout' "$file" \
|