devlyn-cli 3.0.1 → 3.1.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/AGENTS.md +1 -1
- package/CLAUDE.md +3 -3
- package/README.md +20 -3
- package/bin/devlyn.js +69 -23
- package/config/skills/_shared/adapters/claude.md +3 -2
- package/config/skills/_shared/adapters/codex.md +1 -1
- package/config/skills/_shared/archive_run.py +5 -3
- package/config/skills/_shared/codex-config.md +13 -8
- package/config/skills/_shared/codex-monitored.sh +12 -19
- package/config/skills/_shared/collect-codex-findings.py +1 -0
- package/config/skills/_shared/engine-preflight.md +2 -0
- package/config/skills/_shared/finish-gate.py +4 -1
- package/config/skills/_shared/invocation-receipt.py +194 -5
- package/config/skills/_shared/judge-role-evidence.py +33 -6
- package/config/skills/_shared/phase-prompt-render.py +2 -0
- package/config/skills/_shared/platform-support.py +370 -0
- package/config/skills/_shared/process-evidence.py +4 -2
- package/config/skills/_shared/resolve-bootstrap.py +102 -59
- package/config/skills/_shared/resolve-stop-hook.py +3 -1
- package/config/skills/_shared/role-config.py +7 -5
- package/config/skills/_shared/run-bounded.py +35 -21
- package/config/skills/_shared/spec-verify-check.py +274 -157
- package/config/skills/_shared/state-phase-write.py +26 -9
- package/config/skills/_shared/task-complete.py +1173 -0
- package/config/skills/_shared/verify-merge-findings.py +9 -8
- package/config/skills/devlyn:queue/SKILL.md +3 -3
- package/config/skills/devlyn:resolve/SKILL.md +14 -7
- package/config/skills/devlyn:resolve/references/outer-loop.md +10 -2
- package/config/skills/devlyn:resolve/references/phases/verify.md +5 -3
- package/config/skills/devlyn:resolve/references/task-completion.md +117 -0
- package/package.json +1 -1
- package/scripts/lint-skills.sh +10 -6
|
@@ -474,7 +474,7 @@ def read_findings(devlyn: pathlib.Path) -> tuple[list[dict[str, Any]], dict[str,
|
|
|
474
474
|
)
|
|
475
475
|
if (devlyn / "pipeline.state.json").is_file():
|
|
476
476
|
try:
|
|
477
|
-
state = loads_strict_json((devlyn / "pipeline.state.json").read_text())
|
|
477
|
+
state = loads_strict_json((devlyn / "pipeline.state.json").read_text(encoding="utf-8"))
|
|
478
478
|
required = JUDGE_ROLE_EVIDENCE["required_roles"](state)
|
|
479
479
|
if rank(source_verdicts.get("mechanical")) >= 2:
|
|
480
480
|
required = []
|
|
@@ -1663,21 +1663,21 @@ def self_test() -> int:
|
|
|
1663
1663
|
for malformed in (None, {}, {"roles": {}}):
|
|
1664
1664
|
bad_state = loads_strict_json(original_state)
|
|
1665
1665
|
bad_state["role_resolution"] = malformed
|
|
1666
|
-
(devlyn / "pipeline.state.json").write_text(json.dumps(bad_state))
|
|
1666
|
+
(devlyn / "pipeline.state.json").write_text(json.dumps(bad_state), encoding="utf-8")
|
|
1667
1667
|
result = subprocess.run([sys.executable, str(pathlib.Path(__file__).resolve()),
|
|
1668
|
-
"--devlyn-dir", str(devlyn), "--write-state"], capture_output=True, text=True)
|
|
1668
|
+
"--devlyn-dir", str(devlyn), "--write-state"], capture_output=True, text=True, encoding="utf-8")
|
|
1669
1669
|
assert result.returncode == 0 and "Traceback" not in result.stderr, result.stderr
|
|
1670
|
-
persisted = loads_strict_json((devlyn / "pipeline.state.json").read_text())
|
|
1670
|
+
persisted = loads_strict_json((devlyn / "pipeline.state.json").read_text(encoding="utf-8"))
|
|
1671
1671
|
assert persisted["phases"]["verify"]["verdict"] == "BLOCKED"
|
|
1672
|
-
assert "verify-role-resolution-invalid" in (devlyn / "verify-merged.findings.jsonl").read_text()
|
|
1672
|
+
assert "verify-role-resolution-invalid" in (devlyn / "verify-merged.findings.jsonl").read_text(encoding="utf-8")
|
|
1673
1673
|
(devlyn / "pipeline.state.json").write_bytes(original_state)
|
|
1674
1674
|
different_primary = {"engine": "codex", "phases": {"verify": {"engine": "claude"}}}
|
|
1675
|
-
(devlyn / "pipeline.state.json").write_text(json.dumps(different_primary))
|
|
1676
|
-
(devlyn / "verify.primary.timeout.json").write_text(json.dumps({"engine": "claude", "budget_seconds": 600}))
|
|
1675
|
+
(devlyn / "pipeline.state.json").write_text(json.dumps(different_primary), encoding="utf-8")
|
|
1676
|
+
(devlyn / "verify.primary.timeout.json").write_text(json.dumps({"engine": "claude", "budget_seconds": 600}), encoding="utf-8")
|
|
1677
1677
|
marker, violation = read_primary_timeout_marker(devlyn)
|
|
1678
1678
|
assert marker is not None and violation is None
|
|
1679
1679
|
different_primary["phases"]["verify"]["engine"] = None
|
|
1680
|
-
(devlyn / "pipeline.state.json").write_text(json.dumps(different_primary))
|
|
1680
|
+
(devlyn / "pipeline.state.json").write_text(json.dumps(different_primary), encoding="utf-8")
|
|
1681
1681
|
assert read_primary_timeout_marker(devlyn)[1] is not None
|
|
1682
1682
|
(devlyn / "verify.primary.timeout.json").unlink()
|
|
1683
1683
|
(devlyn / "pipeline.state.json").write_bytes(original_state)
|
|
@@ -3433,4 +3433,5 @@ def main() -> int:
|
|
|
3433
3433
|
|
|
3434
3434
|
|
|
3435
3435
|
if __name__ == "__main__":
|
|
3436
|
+
runpy.run_path(str(pathlib.Path(__file__).with_name("platform-support.py")))["configure_utf8"]()
|
|
3436
3437
|
raise SystemExit(main())
|
|
@@ -31,11 +31,11 @@ Read `docs/specs/queue.md` (absent → report "queue empty — nothing staged" a
|
|
|
31
31
|
|
|
32
32
|
- `add <intent text>` — append `- [ ] <intent>` to `docs/specs/queue.md` (create the file with its header if missing). If the intent came out of a conversation that already produced a spec, link it: `- [ ] (spec: docs/specs/<id>/spec.md) <intent>`.
|
|
33
33
|
- `drain` — serial drain per the project-instructions contract. For each pending item, in order:
|
|
34
|
-
1.
|
|
34
|
+
1. Allocate the owner's absent task branch per `../devlyn:resolve/references/task-completion.md` (linked worktree optional), then spec it if unspecced (the queue entry is the user's go-ahead). Unattended assumptions may only take scope-narrowing, reversible, non-user-visible defaults; material ambiguity (user-visible behavior, data/state semantics, new files/scripts/flags, implementation surface) → mark `[F] needs-review: <question>`, commit that queue transition, and continue.
|
|
35
35
|
2. Commit the current queue-item delta and linked spec bundle as the scoped owner baseline, then run `/devlyn:resolve --spec <path>` hands-free.
|
|
36
36
|
After every resolve invocation, run `python3 "$DEVLYN_SHARED_DIR/terminal-claim-check.py" .`; exit 79 marks `[F] FAILED-INCOMPLETE` from the predicate, never from the session self-report.
|
|
37
37
|
3. Outer loop on the terminal verdict: PASS → mark `[x]`. Findings-backed verdicts (NEEDS_WORK, verify/build-gate exhaustion) → amend the spec, commit that scoped amendment, then re-run — at most 3 outer iterations. Infrastructure / invalid-input / engine-availability / implement-empty BLOCKED verdicts are not spec-amendable → mark `[F] <verdict>` immediately.
|
|
38
|
-
4. Commit each terminal `[x]` / `[F]` queue transition before advancing. A blocked item never halts the queue.
|
|
39
|
-
5. When the queue is drained (or the session must stop), emit the drain report: per-item verdict, every logged assumption, and the commit range produced.
|
|
38
|
+
4. Commit each terminal `[x]` / `[F]` queue transition before advancing. For successfully archived normal runs, invoke owner completion once after that declared queue-file-only commit; bind it separately from the verified source. Honor local-only/no-push. Failed products never publish. Retain PR/pending workspaces and use a separate owned branch from base for the next item; unavailable safe placement stops the drain with remaining items pending. A product-blocked item otherwise never halts the queue.
|
|
39
|
+
5. When the queue is drained (or the session must stop), emit the drain report: per-item product verdict and separate delivery status/URL/resume, every logged assumption, and the commit range produced.
|
|
40
40
|
|
|
41
41
|
Strictly SERIAL — one `/devlyn:resolve` run at a time, never parallel. Never invent queue items, never reorder them, and never delete an item — only mark `[x]` / `[F]`.
|
|
@@ -52,7 +52,7 @@ Each phase routes to an engine and prepends the per-engine adapter header from `
|
|
|
52
52
|
|
|
53
53
|
- Phase spawning is mandatory for every orchestrator. Same-context PLAN / IMPLEMENT / BUILD_GATE / CLEANUP / VERIFY is a contract violation. If the current CLI cannot spawn a fresh 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
54
|
- Claude Code phases other than PLAN: spawn `Agent` (`mode: "bypassPermissions"`); prompt = adapter-header + canonical-body + task-context. PLAN uses the native foreground call shape in PHASE 1.
|
|
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.
|
|
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
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
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 and 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.
|
|
@@ -77,6 +77,8 @@ For every direct complete→spawn handoff, call `state-phase-write.py ... --phas
|
|
|
77
77
|
|
|
78
78
|
## PHASE 0: PARSE + CLASSIFY + ROUTE
|
|
79
79
|
|
|
80
|
+
Outer-owner boundary: before normal task writes, follow `references/task-completion.md` for prospective task-branch ownership (linked worktree optional) and `references/outer-loop.md` for owner-input commits. Existing branches cannot be retroactively adopted. Verify-only does not allocate or publish; phase workers never own delivery.
|
|
81
|
+
|
|
80
82
|
1. Run the bootstrap once with the exact tokenized `<pipeline_config>` and this orchestrator's default engine from `<engine_routing>`:
|
|
81
83
|
|
|
82
84
|
```bash
|
|
@@ -103,7 +105,7 @@ Use the frozen role entry and `role-config.py --state .devlyn/pipeline.state.jso
|
|
|
103
105
|
|
|
104
106
|
For a Codex worker with explicit effort, retain the exact wrapper arguments (excluding `bash` and the wrapper path) as `.devlyn/<phase>.argv.<round>.json` before launch. The wrapper's existing invocation receipt binds that array's canonical JSON hash; completion checks the requested effort against it. This adds no mutation receipt relaxation or new launcher.
|
|
105
107
|
|
|
106
|
-
For a judge with explicit model/effort, let `JUDGE_STEM=.devlyn/<engine>-judge.r<VERIFY-round>`. Retain the exact
|
|
108
|
+
For a judge with explicit model/effort, let `JUDGE_STEM=.devlyn/<engine>-judge.r<VERIFY-round>`. Retain the exact UTF-8 prompt as `$JUDGE_STEM.prompt` and complete dispatched argv array (including launcher) as `$JUDGE_STEM.argv.json`. Codex sets `DEVLYN_CODEX_PROMPT_FILE="$JUDGE_STEM.prompt"` and passes the sole prompt `-`; Claude uses `run-bounded.py 600 --stdin-file "$JUDGE_STEM.prompt" --record-transport -- claude -p` with no positional prompt. Preserve the generated `$JUDGE_STEM.prompt.transport.json`; it binds the snapshotted bytes and actual native argv, including wrapper-added isolation. Missing/mismatched transport evidence BLOCKs authentication; never fabricate the carrier. Codex uses the isolated read-only monitored route with timeout600 and redirects stdout/stderr to `$JUDGE_STEM.stdout`/`$JUDGE_STEM.stderr`. Claude uses the existing 600s `run-bounded.py` CLI route, `--tools Read,Grep,Glob --allowedTools Read,Grep,Glob --permission-mode dontAsk --setting-sources project --strict-mcp-config --mcp-config '{"mcpServers":{}}' --output-format json`; redirect raw JSON to `$JUDGE_STEM.output.json` and stderr to `$JUDGE_STEM.stderr`. Never substitute a Claude CLI worker for native Agent.
|
|
107
109
|
|
|
108
110
|
After the actual process exits, run `python3 "$DEVLYN_SHARED_DIR/judge-role-evidence.py" --devlyn-dir .devlyn --role <primary_judge|pair_judge> --exit-code <actual-exit>`. On failure preserve raw artifacts and BLOCK the source; never create successful evidence manually. On success it retains round-scoped evidence and produces canonical `.devlyn/<engine>-judge.stdout` (exact Claude result extraction, exact Codex stdout copy) for the unchanged findings collector. Merge revalidates/binds evidence and archives all bound originals, including prior rounds. Missing explicit judge evidence cannot become PASS. Unconfigured judge routes retain their existing capture format.
|
|
109
111
|
|
|
@@ -111,7 +113,7 @@ After the actual process exits, run `python3 "$DEVLYN_SHARED_DIR/judge-role-evid
|
|
|
111
113
|
|
|
112
114
|
Skip in verify-only mode. The heaviest phase by design — spec/criteria define non-negotiable invariants; plan formalizes how the implementation hits them.
|
|
113
115
|
|
|
114
|
-
Engine: PLAN is orchestrator-fixed and never inherits `--engine`, an executor pin, or `state.engine`; Claude Code uses a native Claude `Agent` worker, while Codex CLI / oh-my-pi use their own fresh worker per `_shared/engine-preflight.md` (PLAN-pair is **research-only** — iter-0033d/f/g; unblock conditions in PHASE 1 / iter-0033g §H; iter-0020 falsified Codex-BUILD/IMPLEMENT, NOT PLAN-pair). Before rendering, assign the current orchestrator's literal adapter name (`PLAN_ENGINE=claude`, `PLAN_ENGINE=codex`, or `PLAN_ENGINE=omp`) and assign `PLAN_MODEL=<exact model id passed to that fresh worker>`; replace the metavalue and fail closed if either assignment is empty. Write the data-only task context to `.devlyn/plan.task-context` with `PLAN_WORKING_DIR="$(pwd -P)"`: its first two lines are exactly `Working directory: $PLAN_WORKING_DIR` and `Plan output: $PLAN_WORKING_DIR/.devlyn/plan.md`, followed by the task data. For round `PLAN_ROUND`, render adapter + `references/phases/plan.md` + task context without byte normalization to `PLAN_PROMPT_FILE=.devlyn/plan.prompt.$PLAN_ROUND`, and set `PLAN_PROMPT_SHA256="$(python3 "$DEVLYN_SHARED_DIR/phase-prompt-render.py" --adapter "$DEVLYN_SHARED_DIR/adapters/$PLAN_ENGINE.md" --canonical-body "$DEVLYN_SKILL_DIR/references/phases/plan.md" --task-context .devlyn/plan.task-context --output "$PLAN_PROMPT_FILE")"`. The renderer fails closed before writing the round-scoped prompt if either fixed header field is missing, non-absolute, or mismatches the active repository paths. Open round 0 with `state-phase-write.py ... --phase plan spawn --round 0 --engine "$PLAN_ENGINE" --model "$PLAN_MODEL" --prompt-sha256 "$PLAN_PROMPT_SHA256"`. In Claude Code, invoke the native `Agent` with its `prompt` field set to the exact `PLAN_PROMPT_FILE` bytes without appending a terminal LF (the renderer emits none), explicitly set `run_in_background: false`, and omit `mode`; this is a data field, so a path, `$(cat ...)`, or any variable-reference substitution is not the rendered prompt. If native input validation rejects this call, surface the failure without retrying a different `mode` or background shape and fail closed through the existing `BLOCKED:fresh-context-unavailable` path. The PLAN worker inherits the parent's permission context: the hands-free parent already uses native `bypassPermissions`, while a non-bypass parent's permission prompt remains the user's native boundary and is never silently escalated. A Codex CLI PLAN must pass explicit `-m "$PLAN_MODEL"` and the seven `DEVLYN_INVOCATION_*` values around `codex-monitored.sh`, using phase `plan`, `PLAN_ROUND`, `PLAN_WORKING_DIR`, `PLAN_PROMPT_FILE`, `PLAN_SESSION_FILE=.devlyn/plan.worker-session.$PLAN_ROUND.jsonl`, and `PLAN_RECEIPT_FILE=.devlyn/plan.invocation.$PLAN_ROUND.json`; invoke the wrapper as `bash "$CODEX_MONITORED_PATH" --json -C "$PLAN_WORKING_DIR" -s workspace-write -m "$PLAN_MODEL" -c sandbox_workspace_write.network_access=false <other options>
|
|
116
|
+
Engine: PLAN is orchestrator-fixed and never inherits `--engine`, an executor pin, or `state.engine`; Claude Code uses a native Claude `Agent` worker, while Codex CLI / oh-my-pi use their own fresh worker per `_shared/engine-preflight.md` (PLAN-pair is **research-only** — iter-0033d/f/g; unblock conditions in PHASE 1 / iter-0033g §H; iter-0020 falsified Codex-BUILD/IMPLEMENT, NOT PLAN-pair). Before rendering, assign the current orchestrator's literal adapter name (`PLAN_ENGINE=claude`, `PLAN_ENGINE=codex`, or `PLAN_ENGINE=omp`) and assign `PLAN_MODEL=<exact model id passed to that fresh worker>`; replace the metavalue and fail closed if either assignment is empty. Write the data-only task context to `.devlyn/plan.task-context` with `PLAN_WORKING_DIR="$(pwd -P)"`: its first two lines are exactly `Working directory: $PLAN_WORKING_DIR` and `Plan output: $PLAN_WORKING_DIR/.devlyn/plan.md`, followed by the task data. For round `PLAN_ROUND`, render adapter + `references/phases/plan.md` + task context without byte normalization to `PLAN_PROMPT_FILE=.devlyn/plan.prompt.$PLAN_ROUND`, and set `PLAN_PROMPT_SHA256="$(python3 "$DEVLYN_SHARED_DIR/phase-prompt-render.py" --adapter "$DEVLYN_SHARED_DIR/adapters/$PLAN_ENGINE.md" --canonical-body "$DEVLYN_SKILL_DIR/references/phases/plan.md" --task-context .devlyn/plan.task-context --output "$PLAN_PROMPT_FILE")"`. The renderer fails closed before writing the round-scoped prompt if either fixed header field is missing, non-absolute, or mismatches the active repository paths. Open round 0 with `state-phase-write.py ... --phase plan spawn --round 0 --engine "$PLAN_ENGINE" --model "$PLAN_MODEL" --prompt-sha256 "$PLAN_PROMPT_SHA256"`. In Claude Code, invoke the native `Agent` with its `prompt` field set to the exact `PLAN_PROMPT_FILE` bytes without appending a terminal LF (the renderer emits none), explicitly set `run_in_background: false`, and omit `mode`; this is a data field, so a path, `$(cat ...)`, or any variable-reference substitution is not the rendered prompt. If native input validation rejects this call, surface the failure without retrying a different `mode` or background shape and fail closed through the existing `BLOCKED:fresh-context-unavailable` path. The PLAN worker inherits the parent's permission context: the hands-free parent already uses native `bypassPermissions`, while a non-bypass parent's permission prompt remains the user's native boundary and is never silently escalated. A Codex CLI PLAN must pass explicit `-m "$PLAN_MODEL"` and the seven `DEVLYN_INVOCATION_*` values around `codex-monitored.sh`, using phase `plan`, `PLAN_ROUND`, `PLAN_WORKING_DIR`, `PLAN_PROMPT_FILE`, `PLAN_SESSION_FILE=.devlyn/plan.worker-session.$PLAN_ROUND.jsonl`, and `PLAN_RECEIPT_FILE=.devlyn/plan.invocation.$PLAN_ROUND.json`; invoke the wrapper as `DEVLYN_CODEX_PROMPT_FILE="$PLAN_PROMPT_FILE" bash "$CODEX_MONITORED_PATH" --json -C "$PLAN_WORKING_DIR" -s workspace-write -m "$PLAN_MODEL" -c sandbox_workspace_write.network_access=false <other options> - >"$PLAN_SESSION_FILE" 2>.devlyn/plan.stderr`, then pass that same session as `state-phase-write.py ... --engine-session-log "$PLAN_SESSION_FILE"` on completion/transition. The wrapper must write and finish the receipt around that direct capture; this state-bound receipt proves requested-model dispatch and prompt/session binding; effective model remains unknown, and a native model-reroute event blocks completion. oh-my-pi delivers the same prompt through its unchanged route. Product code records rendered intent; it never parses a transcript or intercepts, rewrites, wraps, or forces native delivery.
|
|
115
117
|
|
|
116
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.
|
|
117
119
|
|
|
@@ -191,7 +193,7 @@ Invocation contract when OTHER engine is Codex:
|
|
|
191
193
|
|
|
192
194
|
- Invoke Codex only through the monitored wrapper path in `CODEX_MONITORED_PATH`
|
|
193
195
|
resolved from `DEVLYN_SHARED_DIR`:
|
|
194
|
-
`CODEX_MONITORED_ISOLATED=1 bash "$CODEX_MONITORED_PATH" -C "$PWD" -s workspace-write -c sandbox_workspace_write.network_access=false -c model_reasoning_effort=high
|
|
196
|
+
`DEVLYN_CODEX_PROMPT_FILE="<probe-prompt-file>" CODEX_MONITORED_ISOLATED=1 bash "$CODEX_MONITORED_PATH" -C "$PWD" -s workspace-write -c sandbox_workspace_write.network_access=false -c model_reasoning_effort=high -`.
|
|
195
197
|
Append `-c sandbox_workspace_write.network_access=true` only when a probe's visible Verification command requires a localhost service (for example, a DB test harness); never as a default.
|
|
196
198
|
Isolation keeps user config, AGENTS.md, hooks, and project rules
|
|
197
199
|
from adding hidden context, tool calls, or transcript side effects.
|
|
@@ -233,10 +235,11 @@ prompt to `.devlyn/<phase>.prompt.<round>`, pass its SHA-256 to `state-phase-wri
|
|
|
233
235
|
spawn --prompt-sha256`, and invoke only through `codex-monitored.sh` with these
|
|
234
236
|
seven variables set to the active state identity:
|
|
235
237
|
`DEVLYN_INVOCATION_{RUN_ID,PHASE,ROUND,WORKDIR,PROMPT_FILE,SESSION_FILE,RECEIPT}`.
|
|
238
|
+
Set `DEVLYN_CODEX_PROMPT_FILE` to that same prompt file and pass sole prompt `-`; retain its generated `.transport.json` carrier.
|
|
236
239
|
The session and receipt paths are `.devlyn/<phase>.worker-session.<round>.jsonl`
|
|
237
240
|
and `.devlyn/<phase>.invocation.<round>.json`. These three paths are round-scoped
|
|
238
241
|
so a retry cannot overwrite earlier prompt/session evidence. Every invocation
|
|
239
|
-
must include `--json
|
|
242
|
+
must include `--json -m <model_requested>` and `-c sandbox_workspace_write.network_access=<true|false>`: exactly
|
|
240
243
|
`true` for BUILD_GATE and exactly `false` for PLAN, IMPLEMENT, and CLEANUP, so
|
|
241
244
|
user configuration cannot silently change the phase capability. Redirect wrapper stdout directly
|
|
242
245
|
to that session path. The wrapper rejects bypass/yolo flags and any sandbox other
|
|
@@ -271,7 +274,7 @@ After the final phase's gate PASS: `git diff <base_ref.sha>...HEAD --stat` — e
|
|
|
271
274
|
|
|
272
275
|
Run once iff `state.source.type == "generated"` and complexity is trivial/medium. Engine is Claude always, pair-judge-routed; executor flag/pin ignored. Per `_shared/engine-preflight.md`, unavailability skips via `surface-skip`, reports `auto_surface_close_claude_unavailable`, never BLOCKs/reroutes.
|
|
273
276
|
|
|
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;
|
|
277
|
+
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`.
|
|
275
278
|
|
|
276
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.
|
|
277
280
|
|
|
@@ -356,7 +359,9 @@ check, and prints setup guidance: install/configure the missing CLI, complete
|
|
|
356
359
|
its auth/login flow, verify `<engine> --version`, then rerun. `--no-pair` is only
|
|
357
360
|
for an intentional solo run.
|
|
358
361
|
|
|
359
|
-
|
|
362
|
+
For policy-denied Windows Codex judge reads, prepare the complete inline evidence packet from `_shared/codex-config.md#constrained-windows-judge-reads` for each fresh judge, explicitly instructing no tools. Missing inputs produce a verdict-binding BLOCKED finding; sandbox, isolation, model/effort and 600s bounds remain unchanged.
|
|
363
|
+
|
|
364
|
+
Findings are written to `.devlyn/verify.findings.jsonl`. **Except on the supplied no-tools route, VERIFY agents may inspect authorized source, diff and sealed evidence using native read/search tools or non-mutating shell commands. They must not mutate files or execute verification, lint, test, build, probe or invented-scenario commands.** Capture the primary reply as `.devlyn/<primary-engine>-judge.stdout` and its stderr sibling; never use the generic `.devlyn/verify-judge.stdout`. A Codex primary-JUDGE uses the distinct route `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`: omit `-m` only without an explicit model; an explicit profile supplies the validated model/effort additions below. Omit bypass flags and capture both streams directly without pipes. On primary exit 124, write `.devlyn/verify.primary.timeout.json` with exactly `{"engine": "<resolved-primary-engine>", "budget_seconds": 600}` before merge. A malformed marker BLOCKs; a valid marker preserves canonical primary findings and floors `judge` at `BLOCKED` even when output is empty, never `PASS`, a solo verdict, or pair-style `TIMEOUT`. Without the marker, existing required-primary failure behavior is unchanged. For Codex pair-JUDGE, set `DEVLYN_CODEX_PROMPT_FILE="<pair-prompt-file>"` with sole prompt `-` and keep the read-only `codex-monitored.sh` route with `CODEX_MONITORED_ISOLATED=1 CODEX_MONITORED_TIMEOUT_SEC=600` and `-c model_reasoning_effort=medium`, no `tail`/`head`/`grep` pipes, and direct stdout/stderr capture; every other resolved OTHER engine follows `_shared/adapters/<name>.md` `## Invocation`. Capture the pair reply as `.devlyn/<other-engine>-judge.stdout`, then run `python3 "$DEVLYN_SHARED_DIR/collect-codex-findings.py" --devlyn-dir "<abs repo>/.devlyn" --stdout-file <other-engine>-judge.stdout` before merge. Raw stdout is diagnostic-only. A non-zero collector exit must write no canonical `.devlyn/verify.pair.findings.jsonl`; set the pair source to `BLOCKED` for `verify.pair.emission-contract` and never merge as though unparsed stdout were only a diagnostic. VERIFY prompts remain bounded, read-only, and must not ask the judge to edit `.devlyn`. On pair-JUDGE subprocess exit 124, write `.devlyn/verify.pair.timeout.json` with `{"engine": "<resolved name>", "budget_seconds": 600}` before merge. With that marker, a capture the parser rejects solely because its message stream was truncated before the terminal result stays `TIMEOUT`; every other parser rejection stays `BLOCKED`. The orchestrator records `phases.verify.judge_durations_ms: {judge: <int>, pair_judge: <int|null>}` as it collects each judge result; durations never nest inside `sub_verdicts`. After primary and pair findings are written, run `python3 "$DEVLYN_SHARED_DIR/verify-merge-findings.py" --write-state`; it rehashes and binds the VERIFY process-evidence carrier before merging. Branch only on the merged `state.phases.verify.verdict`; a HIGH/CRITICAL finding from either judge must mechanically become `NEEDS_WORK`. Never write `.devlyn/verify-merged.findings.jsonl` or `.devlyn/verify-merge.summary.json` by hand; `verify-merge-findings.py` is their only writer. State write: `phases.verify.{started_at, verdict, completed_at, duration_ms, judge_durations_ms: {judge, pair_judge}, sub_verdicts: {mechanical, judge, pair_judge?}, artifacts}`.
|
|
360
365
|
|
|
361
366
|
Branch:
|
|
362
367
|
- `PASS` → PHASE 6.
|
|
@@ -379,6 +384,8 @@ Open the `final_report` span through the predecessor's `state-phase-write.py --d
|
|
|
379
384
|
|
|
380
385
|
6. **Archive** — invoke the deterministic script: `python3 "$DEVLYN_SHARED_DIR/archive_run.py"`. The script reads `run_id` from `.devlyn/pipeline.state.json`, moves the static per-run artifact set (`PER_RUN_PATTERNS` remains the single ownership list) plus every state-bound process-evidence manifest/raw stream into `.devlyn/runs/<run_id>/`, preserves evidence-relative layout, and rehashes bound bytes before any move. An unsafe/missing/altered evidence path or destination collision reports archive failure without changing the already-derived product verdict. It then best-effort prunes to the last 10 completed runs. Archive must run; running this step as deterministic-script-not-prose ensures the move actually happens (iter-0033a Smoke 3 caught a case where the agent claimed archive ran without moving the files).
|
|
381
386
|
|
|
387
|
+
After successful normal-run archive, return to the outer owner for `references/task-completion.md`. A queue owner first commits its terminal queue transition, then completes once. Honor local-only/no-push; report delivery pending/failure separately from the archived product verdict. This is outside the phase graph.
|
|
388
|
+
|
|
382
389
|
## State management
|
|
383
390
|
|
|
384
391
|
`.devlyn/pipeline.state.json` is the single authoritative verdict source. Branch on `state.phases.<name>.verdict` directly; never parse `.devlyn/*.findings.jsonl` for routing decisions. Schema and write protocol: `references/state-schema.md`. Every "State write: `phases.X.{...}`" note above describes the resulting shape only — the orchestrator writes it via initial `spawn`, atomic `transition` for direct handoffs, or terminal `complete`; re-entry preserves the prior lifecycle in `history[]` (`references/state-schema.md#write-protocol`). Phase workers never edit `pipeline.state.json` directly.
|
|
@@ -3,13 +3,21 @@
|
|
|
3
3
|
A full `/devlyn:resolve` run starts only from committed owner inputs. Keep each
|
|
4
4
|
commit scoped: no unrelated paths or queue-item changes.
|
|
5
5
|
|
|
6
|
-
1. Before
|
|
6
|
+
1. Before task writes, the outer owner allocates an absent task branch per
|
|
7
|
+
[task completion](task-completion.md); linked worktree allocation is optional.
|
|
8
|
+
Never adopt a pre-existing task branch. Before an item's first full run, commit the current queue-item delta, when
|
|
7
9
|
applicable, together with its linked spec bundle. For conversational work,
|
|
8
10
|
commit the agreed spec bundle before the full run.
|
|
9
11
|
2. When findings require an accepted verification amendment, commit only that
|
|
10
12
|
linked spec-bundle amendment before rerunning the item.
|
|
11
13
|
3. After a terminal result, commit the current item's `[x]` or `[F]` queue
|
|
12
14
|
transition before starting the next pending item.
|
|
15
|
+
4. After mandatory successful normal-run archive and any terminal queue commit,
|
|
16
|
+
the outer owner completes delivery once per [task completion](task-completion.md).
|
|
17
|
+
Bind the verified source and declared queue-only commit separately. Failed or
|
|
18
|
+
verify-only runs do not publish; local-only/no-push wins. Pending delivery
|
|
19
|
+
retains its workspace and never changes the product verdict.
|
|
13
20
|
|
|
14
21
|
Drain strictly serially. Bootstrap and resolve never make these owner commits;
|
|
15
|
-
the outer-loop driver owns them.
|
|
22
|
+
the outer-loop driver owns them. Return queue custody before completion; never
|
|
23
|
+
advance onto a retained pending task branch.
|
|
@@ -36,6 +36,8 @@ Emit findings to `.devlyn/verify-mechanical.findings.jsonl`. Each match = one fi
|
|
|
36
36
|
|
|
37
37
|
### JUDGE (fresh-context grading)
|
|
38
38
|
|
|
39
|
+
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.
|
|
40
|
+
|
|
39
41
|
Grade the diff against the spec on rubric axes:
|
|
40
42
|
|
|
41
43
|
- **Spec compliance** — did every Requirement get an `evidence` record pointing at code that satisfies it?
|
|
@@ -219,10 +221,10 @@ generic `.devlyn/verify-judge.stdout`. When the primary engine is Codex, invoke
|
|
|
219
221
|
it only through this distinct monitored route:
|
|
220
222
|
|
|
221
223
|
```bash
|
|
222
|
-
CODEX_MONITORED_ISOLATED=1 CODEX_MONITORED_TIMEOUT_SEC=600 bash "$CODEX_MONITORED_PATH" -C "$PWD" -s read-only -c model_reasoning_effort=high
|
|
224
|
+
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
|
|
223
225
|
```
|
|
224
226
|
|
|
225
|
-
This command supplies unconfigured defaults. Explicit judge profiles use
|
|
227
|
+
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
|
|
226
228
|
`SKILL.md#explicit-role-dispatch` for validated model/effort options and
|
|
227
229
|
round-scoped evidence. Omit bypass flags; do not pipe either stream. On primary exit
|
|
228
230
|
124, write `.devlyn/verify.primary.timeout.json` with exactly
|
|
@@ -233,7 +235,7 @@ floors `judge` at `BLOCKED`, including when findings/stdout are empty. It never
|
|
|
233
235
|
produces `PASS`, a solo verdict, or pair-style `TIMEOUT`; without the marker,
|
|
234
236
|
existing missing/invalid primary-output behavior remains fail-closed.
|
|
235
237
|
|
|
236
|
-
Codex pair-JUDGE keeps the monitored
|
|
238
|
+
Codex pair-JUDGE sets `DEVLYN_CODEX_PROMPT_FILE="<pair-prompt-file>"` with sole prompt `-` and keeps the monitored
|
|
237
239
|
`codex-monitored.sh` route with
|
|
238
240
|
`CODEX_MONITORED_ISOLATED=1 CODEX_MONITORED_TIMEOUT_SEC=600` and
|
|
239
241
|
unconfigured default `-c model_reasoning_effort=medium`; isolation blocks user config, AGENTS.md,
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Outer-owner task completion
|
|
2
|
+
|
|
3
|
+
Completion belongs to the outer task owner after source acceptance. It does not
|
|
4
|
+
add a phase or change product verdicts, finish-gate/archive authority or worker
|
|
5
|
+
isolation. Explicit local-only/no-push instructions win. Otherwise delivery is
|
|
6
|
+
authorized by the task scope; no additional approval ceremony is required.
|
|
7
|
+
|
|
8
|
+
## Allocate before work
|
|
9
|
+
|
|
10
|
+
Use the installed `_shared/task-complete.py` from the retained base checkout,
|
|
11
|
+
before committing owner inputs or starting direct/full work:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
python3 "$DEVLYN_SHARED_DIR/task-complete.py" allocate --repo . \
|
|
15
|
+
--task '<task identity>' --branch '<absent task branch>' \
|
|
16
|
+
--repository '<owner/repo>' --remote origin --base main
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This creates and switches to a native task branch. Add `--worktree <absent path>`
|
|
20
|
+
only when the owner explicitly chooses a linked checkout; allocation is optional
|
|
21
|
+
for worktrees. Save the returned receipt path, outside the task checkout under
|
|
22
|
+
the common Gitdir. Existing branches/trees cannot be adopted, even when their
|
|
23
|
+
names look generated. An interrupted allocation stays blocked for inspection;
|
|
24
|
+
do not delete its receipt and enroll the resulting branch. Pre-existing tasks
|
|
25
|
+
remain owner-managed and cannot use this helper retroactively.
|
|
26
|
+
|
|
27
|
+
## Accept a scoped commit
|
|
28
|
+
|
|
29
|
+
Direct work: finish actual decisive checks and diff review, stage only the
|
|
30
|
+
accepted paths and commit them. The helper never stages product files. Write a
|
|
31
|
+
root acceptance file in the checkout (usually ignored `.devlyn/acceptance.json`):
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{"kind":"direct","task":"<task identity>","source_sha":"<full commit>","checks":[{"command":"<actual check>","evidence":".devlyn/checks.log"}]}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The owner accepts these checks; the helper preserves evidence bytes and does not
|
|
38
|
+
claim to have independently proved the assertions. No synthetic pipeline state
|
|
39
|
+
or full resolve run is needed to deliver a direct task.
|
|
40
|
+
|
|
41
|
+
For full resolve, use only its intended successfully archived normal run:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{"kind":"pipeline","task":"<task identity>","source_sha":"<cleanup.post_sha>","run_id":"<exact archived run_id>"}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Terminal CLEAN alone is insufficient. Successful VERIFY and terminal precedence,
|
|
48
|
+
the run-bound report/digest, clean finish summary and required evidence must
|
|
49
|
+
agree. Failed, incomplete and verify-only runs are ineligible. Acceptance binds
|
|
50
|
+
exact bytes and source before push; changed acceptance/evidence or subsequent
|
|
51
|
+
product commits require a new accepted task, never implicit descendant approval.
|
|
52
|
+
|
|
53
|
+
For a queue drain, resolve returns the acceptance/archive to the queue owner.
|
|
54
|
+
Commit the terminal queue transition first, then add
|
|
55
|
+
`"queue":{"commit":"<full terminal commit>","file":"docs/specs/queue.md"}` to
|
|
56
|
+
acceptance. This commit must have only the verified source as parent and change
|
|
57
|
+
exactly the declared queue file. The owner reviews the transition; the helper
|
|
58
|
+
does not infer that arbitrary edits to that file are valid queue metadata.
|
|
59
|
+
Complete once per item after that commit. Failed items are never published.
|
|
60
|
+
|
|
61
|
+
## Deliver and resume
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
python3 "$DEVLYN_SHARED_DIR/task-complete.py" complete \
|
|
65
|
+
--receipt '<returned receipt.json>' --acceptance .devlyn/acceptance.json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Project policy is `git config --local devlyn.completionMode auto|pr`; absent means
|
|
69
|
+
`auto`. Invalid local values fail before external effects, even with an override.
|
|
70
|
+
Use `complete --mode auto|pr` for one task, or `--local-only`/`--no-push` to honor
|
|
71
|
+
the user's delivery restriction. These per-task choices persist in the receipt.
|
|
72
|
+
|
|
73
|
+
`pr` pushes the exact accepted task ref and creates/reuses its exact repository,
|
|
74
|
+
head and base PR, then reports `PR` with its URL and retains the checkout. `auto`
|
|
75
|
+
also requests `gh pr merge --auto --merge --match-head-commit <accepted SHA>`.
|
|
76
|
+
Repository checks/reviews and merge policy remain authoritative: no admin bypass,
|
|
77
|
+
strategy fallback or repository setting changes. A repository that disallows
|
|
78
|
+
merge commits fails visibly; disabled auto merge may also reject a pending
|
|
79
|
+
request. Command success alone never proves merge.
|
|
80
|
+
|
|
81
|
+
`PENDING` retains the workspace and reports a receipt-based resume command.
|
|
82
|
+
`BLOCKED` includes the cause and the same retry path; repair the reported condition
|
|
83
|
+
before retrying. Resume reobserves Git/PR state under a per-receipt lock, reuses
|
|
84
|
+
the original acceptance, and performs only missing eligible effects. Delivery results remain separate from the
|
|
85
|
+
immutable product result; do not rewrite an archived PASS for a delivery error.
|
|
86
|
+
|
|
87
|
+
## Yield and retain recoverability
|
|
88
|
+
|
|
89
|
+
After actual matching MERGED evidence, wait for the owner's actual children,
|
|
90
|
+
stop/yield known task writers, and invoke completion with `--writers-stopped`.
|
|
91
|
+
For a removable linked checkout, run from outside it using the installed helper
|
|
92
|
+
outside that checkout. An OS process observation supplements the owner assertion;
|
|
93
|
+
neither the receipt nor a scan guarantees exclusion of future writers. Unknown
|
|
94
|
+
writers, inaccessible process state, caller cwd inside the tree, dirty/untracked
|
|
95
|
+
files, changed refs/Gitdir/registration or locks retain the affected resources.
|
|
96
|
+
On native Windows, locking and file durability are supported, but the helper cannot prove writer cessation and reports `writer observation unsupported on this platform; retain workspace`. Even `--writers-stopped` cannot authorize deletion without that observation: retain the workspace, refs and external receipt/custody, report delivery separately, and preserve the receipt-based resume command. Do not kill unknown processes or force worktree removal to make completion pass.
|
|
97
|
+
|
|
98
|
+
The receipt directory holds byte-verified `custody/`, `manifest.json` and a guarded
|
|
99
|
+
`refs/devlyn/completed/<id>` recovery ref. Archive/check evidence is copied there
|
|
100
|
+
before publication; removal rechecks it and source reachability through the merge.
|
|
101
|
+
Ignored files in removable trees must be covered by that exact owned evidence
|
|
102
|
+
manifest. Unknown ignored data requires explicit owner custody outside the helper;
|
|
103
|
+
it never silently archives or deletes arbitrary user content. A partial/corrupt
|
|
104
|
+
custody copy blocks cleanup and retains the original for repair.
|
|
105
|
+
|
|
106
|
+
In-place completion retains the checkout and its ignored user data, switches
|
|
107
|
+
safely to updated base, then compare-deletes only its exact owned task refs.
|
|
108
|
+
Linked completion uses native non-force removal. Local ref deletion is guarded
|
|
109
|
+
by the expected SHA; remote deletion uses an exact force-with-lease solely as
|
|
110
|
+
compare-and-delete, never for product publication. A changed remote branch is
|
|
111
|
+
retained. Retry after removal uses the external receipt and evidence, so the
|
|
112
|
+
removed checkout is not needed. Recovery refs/evidence have no automatic expiry.
|
|
113
|
+
|
|
114
|
+
Queue drains remain serial. Record source verdict and delivery status separately;
|
|
115
|
+
retain pending/PR workspaces. Before advancing, use a separate owned branch from
|
|
116
|
+
base; do not put the next item's commits on a retained pending branch. If safe
|
|
117
|
+
placement is unavailable, report remaining items pending and stop the drain.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devlyn-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.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": {
|
package/scripts/lint-skills.sh
CHANGED
|
@@ -58,6 +58,7 @@ fi
|
|
|
58
58
|
critical_path_files=$(cat <<'EOF'
|
|
59
59
|
_shared/.ruff.toml
|
|
60
60
|
_shared/process-evidence.py
|
|
61
|
+
_shared/platform-support.py
|
|
61
62
|
_shared/invocation-receipt.py
|
|
62
63
|
_shared/role-config.py
|
|
63
64
|
_shared/judge-role-evidence.py
|
|
@@ -66,6 +67,7 @@ _shared/judge-output-parser.py
|
|
|
66
67
|
_shared/collect-codex-findings.py
|
|
67
68
|
_shared/verify-merge-findings.py
|
|
68
69
|
_shared/archive_run.py
|
|
70
|
+
_shared/task-complete.py
|
|
69
71
|
_shared/state-phase-write.py
|
|
70
72
|
_shared/terminal-claim-check.py
|
|
71
73
|
_shared/resolve-stop-hook.py
|
|
@@ -79,6 +81,8 @@ devlyn:resolve/SKILL.md
|
|
|
79
81
|
devlyn:queue/SKILL.md
|
|
80
82
|
devlyn:engines/SKILL.md
|
|
81
83
|
devlyn:resolve/references/state-schema.md
|
|
84
|
+
devlyn:resolve/references/task-completion.md
|
|
85
|
+
devlyn:resolve/references/outer-loop.md
|
|
82
86
|
devlyn:resolve/references/free-form-mode.md
|
|
83
87
|
devlyn:resolve/references/phases/plan.md
|
|
84
88
|
devlyn:resolve/references/phases/probe-derive.md
|
|
@@ -401,8 +405,8 @@ check_skill_mirror_parity \
|
|
|
401
405
|
|
|
402
406
|
# ---------------------------------------------------------------------------
|
|
403
407
|
# 6b. VERIFY merge verdict binding self-test.
|
|
404
|
-
for helper in role-config judge-role-evidence; do
|
|
405
|
-
if python3 "config/skills/_shared/$helper.py" --self-test
|
|
408
|
+
for helper in role-config judge-role-evidence task-complete; do
|
|
409
|
+
if python3 "config/skills/_shared/$helper.py" --self-test; then
|
|
406
410
|
ok "$helper.py self-test passed"
|
|
407
411
|
else
|
|
408
412
|
bad "$helper.py self-test failed"
|
|
@@ -511,7 +515,7 @@ if ! grep -Fq 'PHASES = {"implement", "build_gate", "verify"}' config/skills/_sh
|
|
|
511
515
|
fi
|
|
512
516
|
|
|
513
517
|
section "Check 6b2: Codex invocation receipts are phase-owned"
|
|
514
|
-
if python3 config/skills/_shared/invocation-receipt.py --self-test
|
|
518
|
+
if python3 config/skills/_shared/invocation-receipt.py --self-test; then
|
|
515
519
|
ok "invocation-receipt.py self-test passed"
|
|
516
520
|
else
|
|
517
521
|
bad "invocation-receipt.py self-test failed"
|
|
@@ -624,7 +628,7 @@ for tree in config/skills .agents/skills; do
|
|
|
624
628
|
|| ! grep -Fq 'executor flag/pin' "$skill" \
|
|
625
629
|
|| ! grep -Fq 'auto_surface_close_claude_unavailable' "$skill" \
|
|
626
630
|
|| ! grep -Fq 'canonical body VERBATIM' "$skill" \
|
|
627
|
-
|| ! grep -Fq 'run-bounded.py 600 -- claude -p' "$skill" \
|
|
631
|
+
|| ! grep -Fq 'run-bounded.py 600 --stdin-file .devlyn/surface-close.prompt.<round> --record-transport -- claude -p' "$skill" \
|
|
628
632
|
|| ! grep -Fq -- '--tools "Read,Grep,Glob,Edit,Write" --dangerously-skip-permissions --output-format json --strict-mcp-config --mcp-config '\''{"mcpServers":{}}'\''' "$skill" \
|
|
629
633
|
|| ! grep -Fq '.devlyn/surface-close.output.json' "$skill" \
|
|
630
634
|
|| ! grep -Fq '**Common post-fix checkpoint (BUILD_GATE and VERIFY):**' "$skill" \
|
|
@@ -821,7 +825,7 @@ if ! grep -Fq 'spec.expected.json top-level array produced a traceback' config/s
|
|
|
821
825
|
|| ! grep -Fq 'NaN risk-probes JSONL did not report invalid numeric constant' config/skills/_shared/spec-verify-check.py \
|
|
822
826
|
|| ! grep -Fq 'def reject_json_constant' config/skills/_shared/spec-verify-check.py \
|
|
823
827
|
|| ! grep -Fq 'loads_strict_json(line)' config/skills/_shared/spec-verify-check.py \
|
|
824
|
-
|| ! grep -Fq 'loads_strict_json(expected_path.read_text())' config/skills/_shared/spec-verify-check.py \
|
|
828
|
+
|| ! grep -Fq 'loads_strict_json(expected_path.read_text(encoding="utf-8"))' config/skills/_shared/spec-verify-check.py \
|
|
825
829
|
|| ! grep -Fq 'top-level must be a JSON object' config/skills/_shared/spec-verify-check.py \
|
|
826
830
|
|| ! grep -Fq 'has invalid JSON' config/skills/_shared/spec-verify-check.py; then
|
|
827
831
|
bad "spec-verify-check.py self-test must fail malformed spec.expected.json cleanly without traceback"
|
|
@@ -1561,7 +1565,7 @@ fi
|
|
|
1561
1565
|
# ---------------------------------------------------------------------------
|
|
1562
1566
|
section "Check 10a0: Codex primary VERIFY budget is bounded and mirrored"
|
|
1563
1567
|
primary_verify_missing=0
|
|
1564
|
-
primary_verify_route='CODEX_MONITORED_ISOLATED=1 CODEX_MONITORED_TIMEOUT_SEC=600 bash "$CODEX_MONITORED_PATH" -C "$PWD" -s read-only -c model_reasoning_effort=high
|
|
1568
|
+
primary_verify_route='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'
|
|
1565
1569
|
for file in \
|
|
1566
1570
|
config/skills/devlyn:resolve/SKILL.md \
|
|
1567
1571
|
.agents/skills/devlyn:resolve/SKILL.md \
|