devlyn-cli 2.10.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/AGENTS.md +4 -9
  2. package/CLAUDE.md +11 -15
  3. package/README.md +31 -7
  4. package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.json +25 -0
  5. package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.md +10 -0
  6. package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.json +18 -0
  7. package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.md +9 -0
  8. package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.json +18 -0
  9. package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.md +9 -0
  10. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F16-cli-quote-tax-rules/l2_risk_probes/result.json +28 -0
  11. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F23-cli-fulfillment-wave/l2_risk_probes/result.json +28 -0
  12. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F25-cli-cart-promotion-rules/l2_risk_probes/result.json +30 -0
  13. package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.json +28 -0
  14. package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.md +15 -0
  15. package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.json +28 -0
  16. package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.md +15 -0
  17. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/F21-cli-scheduler-priority/l2_risk_probes/result.json +32 -0
  18. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.json +44 -0
  19. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.md +16 -0
  20. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.json +18 -0
  21. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.md +9 -0
  22. package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.json +28 -0
  23. package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.md +15 -0
  24. package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.json +28 -0
  25. package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.md +15 -0
  26. package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.json +28 -0
  27. package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.md +15 -0
  28. package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.json +28 -0
  29. package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.md +15 -0
  30. package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.json +28 -0
  31. package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.md +15 -0
  32. package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.json +28 -0
  33. package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.md +15 -0
  34. package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.json +28 -0
  35. package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.md +15 -0
  36. package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.json +28 -0
  37. package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.md +15 -0
  38. package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.json +28 -0
  39. package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.md +15 -0
  40. package/bin/devlyn.js +6 -1
  41. package/config/skills/_shared/.ruff.toml +2 -0
  42. package/config/skills/_shared/adapters/claude.md +9 -1
  43. package/config/skills/_shared/adapters/codex.md +5 -0
  44. package/config/skills/_shared/adapters/grok.md +4 -2
  45. package/config/skills/_shared/archive_run.py +738 -56
  46. package/config/skills/_shared/codex-config.md +19 -4
  47. package/config/skills/_shared/codex-monitored.sh +81 -12
  48. package/config/skills/_shared/collect-codex-findings.py +311 -128
  49. package/config/skills/_shared/engine-preflight.md +8 -7
  50. package/config/skills/_shared/expected.schema.json +61 -0
  51. package/config/skills/_shared/finish-gate.py +12 -11
  52. package/config/skills/_shared/grok-anchor-guard.py +18 -1
  53. package/config/skills/_shared/invocation-receipt.py +1041 -0
  54. package/config/skills/_shared/judge-output-parser.py +200 -0
  55. package/config/skills/_shared/judge-role-evidence.py +273 -0
  56. package/config/skills/_shared/process-evidence.py +909 -0
  57. package/config/skills/_shared/resolve-bootstrap.py +776 -84
  58. package/config/skills/_shared/resolve-stop-hook.py +33 -1
  59. package/config/skills/_shared/role-config.py +552 -0
  60. package/config/skills/_shared/run-bounded.py +1 -1
  61. package/config/skills/_shared/spec-verify-check.py +749 -181
  62. package/config/skills/_shared/state-phase-write.py +1838 -58
  63. package/config/skills/_shared/terminal-claim-check.py +15 -0
  64. package/config/skills/_shared/verify-merge-findings.py +958 -51
  65. package/config/skills/devlyn:engines/SKILL.md +8 -16
  66. package/config/skills/devlyn:ideate/references/spec-template.md +1 -0
  67. package/config/skills/devlyn:queue/SKILL.md +7 -4
  68. package/config/skills/devlyn:resolve/SKILL.md +91 -33
  69. package/config/skills/devlyn:resolve/references/outer-loop.md +15 -0
  70. package/config/skills/devlyn:resolve/references/phases/build-gate.md +31 -2
  71. package/config/skills/devlyn:resolve/references/phases/implement.md +3 -2
  72. package/config/skills/devlyn:resolve/references/phases/verify.md +120 -84
  73. package/config/skills/devlyn:resolve/references/state-schema.md +22 -8
  74. package/package.json +5 -1
  75. package/scripts/lint-skills.sh +314 -285
@@ -38,6 +38,20 @@ CODEX_MONITORED_ISOLATED=1 bash "$CODEX_MONITORED_PATH" \
38
38
  bash "$CODEX_MONITORED_PATH" \
39
39
  -C <project-root> \
40
40
  -s workspace-write \
41
+ -c sandbox_workspace_write.network_access=false \
42
+ -c model_reasoning_effort=xhigh \
43
+ "<inlined-prompt>"
44
+ ```
45
+
46
+ **CI-equivalent BUILD_GATE** keeps the same write sandbox and enables general
47
+ outbound network access inside that sandbox, including the loopback servers and
48
+ network-backed test gates that CI may exercise:
49
+
50
+ ```bash
51
+ bash "$CODEX_MONITORED_PATH" \
52
+ -C <project-root> \
53
+ -s workspace-write \
54
+ -c sandbox_workspace_write.network_access=true \
41
55
  -c model_reasoning_effort=xhigh \
42
56
  "<inlined-prompt>"
43
57
  ```
@@ -45,10 +59,11 @@ bash "$CODEX_MONITORED_PATH" \
45
59
  Notes:
46
60
  - `-C` — project root so Codex's working directory matches.
47
61
  - `-s read-only` / `-s workspace-write` — sandbox policy. Use workspace-write for implementation/probe phases that write tracked files or `.devlyn` artifacts.
62
+ - `-c sandbox_workspace_write.network_access=<true|false>` — required and receipt-bound for mutation phases: `true` only for BUILD_GATE and, per call, PROBE-DERIVE when the probe's visible Verification command requires a localhost service; `false` for PLAN, IMPLEMENT, CLEANUP, and PROBE-DERIVE by default. This allows CI-equivalent loopback/network tests without widening to `danger-full-access` and prevents user configuration from silently changing other phases.
48
63
  - `-c model_reasoning_effort=xhigh` — config override for reasoning depth. Required for deep critique; skills may choose `high` or `medium` when thoroughness doesn't warrant xhigh.
49
- - **Omit `-m <model>`** — Codex CLI uses its configured flagship (currently `gpt-5.5`, automatically whatever ships next). This is the zero-touch mechanism. Only name `-m` when a role explicitly needs a different model (e.g., `gpt-5.3-codex` for SWE-bench-heavy coding tasks, `gpt-5.3-codex-spark` for speed).
64
+ - **For every receipt-bound Codex call, add `--json -m <model_requested>` to the recipes above**, matching the model recorded at spawn; keep the skill’s existing model-selection rules. Other routes keep their omission rules unless an explicit role profile supplies a model: normal workers may inherit user config, while unconfigured isolated VERIFY uses its CLI default. Explicit profiles use role-config.py options and the separate judge-role-evidence.py contract; they do not relax mutation receipts. Omission proves neither model identity nor role fitness. Re-certify seats after model/version changes before re-pinning them.
50
65
  - `CODEX_MONITORED_ISOLATED=1` — required for bounded read-only critique/probe/judge calls. The wrapper adds `--ignore-user-config --ignore-rules --ephemeral --disable codex_hooks --disable hooks` so user config, AGENTS.md, hooks, and project rules cannot add hidden context, tool calls, or transcript side effects. Do not set it for workspace-write implementation phases.
51
- - Wrapper calls are **foreground-blocking**. Never launch them via a backgrounded shell (`run_in_background`, `&`, `nohup`) and never end the orchestrator message while one runs: a headless print-mode session kills backgrounded children at wind-down (observed 2026-07-07: an FS1 A-arm IMPLEMENT codex call was killed at turn end → 0-byte delivery). The heartbeat stream is the observability channel; block on the call.
66
+ - Wrapper calls are **foreground-blocking**. Never launch them via a backgrounded shell (`run_in_background`, `&`, `nohup`) and never end the orchestrator message while one runs: a headless print-mode session kills backgrounded children at wind-down (observed 2026-07-07: an FS1 A-arm IMPLEMENT codex call was killed at turn end → 0-byte delivery). The heartbeat stream is the observability channel; block on the call. Interactive Claude Code caps a foreground Bash call at `BASH_MAX_TIMEOUT_MS` (the installer sets at least 3600000 ms, IMPLEMENT's effective outer ceiling), so every foreground Codex wrapper call must pass the Bash `timeout` explicitly at that ceiling or the phase's own budget (for example, 600s judges) — never rely on the 120 s default.
52
67
  - Raw `codex exec ...` invocations are **forbidden** in skill prompts. The benchmark variant arm runs a PATH shim (`scripts/codex-shim/codex`) that transparently re-routes any raw `codex exec` to the wrapper as a safety net, but skills should always emit the wrapper form directly so the orchestrator's first-attempt has the right shape. Two prior iterations (iter-0006 universal foreground ban, iter-0008 prompt-level kill-shape contract) failed because the orchestrator picked starvation-prone shapes (`codex exec ... 2>&1 | tail -200`) from its own pattern prior — the wrapper plus the shim is the runtime binding layer those iters lacked. See `autoresearch/iterations/0009-wrapper-and-hook.md`.
53
68
 
54
69
  ## Availability check
@@ -63,10 +78,10 @@ If the check fails while Codex is explicitly selected or conditionally required
63
78
 
64
79
  ## Why CLI over other paths
65
80
 
66
- The local Codex CLI (fronted by `codex-monitored.sh`) is the primary (and only) integration. It beats alternatives on three dimensions: the model is inherited from the CLI's own default so no skill edits are needed when OpenAI ships a new flagship; flags compose on the command line and the skill docs stay grep-friendly; the invocation has one failure mode (the binary is on PATH or it isn't), which the shared availability check reports explicitly.
81
+ The local Codex CLI, fronted by `codex-monitored.sh`, is the integration boundary. CLI flags compose with the existing phase routes and keep invocation evidence inspectable. The shared preflight checks availability; a present binary does not establish model identity or role fitness.
67
82
 
68
83
  ## Invocation from inside a skill prompt
69
84
 
70
85
  Skills write the invocation as a Bash command the runtime executes. Example shape from `/devlyn:resolve` PHASE 2 IMPLEMENT when routed to Codex:
71
86
 
72
- > Run `bash "$CODEX_MONITORED_PATH" -C <state.base_ref.repo_root> -s workspace-write -c model_reasoning_effort=xhigh "<IMPLEMENT prompt>"`. Omit `-m` so the CLI flagship is auto-selected. Capture stdout as the IMPLEMENT reply; non-zero exit → treat as subagent failure. The wrapper emits `[codex-monitored]` heartbeat and lifecycle lines on **stderr** — stdout stays clean for Codex output, so the orchestrator can parse the reply without filtering. Heartbeat-on-stderr keeps the orchestrator's combined-output stream non-silent (defeats the iter-0008 byte-watchdog kill) without polluting the codex-reply view of stdout. Do not pipe the wrapper; direct capture or file redirection preserves streaming and avoids the pipe-refusal exit.
87
+ > Run `bash "$CODEX_MONITORED_PATH" --json -C <state.base_ref.repo_root> -s workspace-write -m <model_requested> -c sandbox_workspace_write.network_access=false -c model_reasoning_effort=xhigh "<IMPLEMENT prompt>"`. Capture stdout as the IMPLEMENT reply; non-zero exit → treat as subagent failure. The wrapper emits `[codex-monitored]` heartbeat and lifecycle lines on **stderr** — stdout stays clean for Codex output, so the orchestrator can parse the reply without filtering. Heartbeat-on-stderr keeps the orchestrator's combined-output stream non-silent (defeats the iter-0008 byte-watchdog kill) without polluting the codex-reply view of stdout. Do not pipe the wrapper; direct capture or file redirection preserves streaming and avoids the pipe-refusal exit.
@@ -54,6 +54,10 @@
54
54
  # CODEX_MONITORED_ALLOW_PIPED — set non-empty to skip the pipe-stdout
55
55
  # refusal. Reserved for tests; don't use
56
56
  # in skill prompts.
57
+ # DEVLYN_INVOCATION_* — run/phase/round/workdir/prompt/session/
58
+ # receipt identity. When any is set, all
59
+ # are required and the wrapper seals a
60
+ # canonical invocation receipt.
57
61
 
58
62
  set -uo pipefail
59
63
 
@@ -76,6 +80,19 @@ START=$(date +%s)
76
80
  TIMEOUT_FLAG=""
77
81
  CODEX_ARGS=("$@")
78
82
 
83
+ for codex_arg in "${CODEX_ARGS[@]}"; do
84
+ case "$codex_arg" in
85
+ --dangerously-bypass-approvals-and-sandbox|--yolo)
86
+ printf '[codex-monitored] error: forbidden Codex bypass flag: %s\n' "$codex_arg" >&2
87
+ exit 64
88
+ ;;
89
+ danger-full-access|-s=danger-full-access|--sandbox=danger-full-access)
90
+ printf '[codex-monitored] error: forbidden Codex sandbox: danger-full-access\n' >&2
91
+ exit 64
92
+ ;;
93
+ esac
94
+ done
95
+
79
96
  require_nonnegative_int() {
80
97
  local name="$1"
81
98
  local value="$2"
@@ -102,6 +119,34 @@ require_positive_int() {
102
119
  require_positive_int CODEX_MONITORED_HEARTBEAT "$HEARTBEAT_SEC"
103
120
  require_nonnegative_int CODEX_MONITORED_TIMEOUT_SEC "$TIMEOUT_SEC"
104
121
 
122
+ RECEIPT_ENABLED=""
123
+ for receipt_value in \
124
+ "${DEVLYN_INVOCATION_RUN_ID:-}" \
125
+ "${DEVLYN_INVOCATION_PHASE:-}" \
126
+ "${DEVLYN_INVOCATION_ROUND:-}" \
127
+ "${DEVLYN_INVOCATION_WORKDIR:-}" \
128
+ "${DEVLYN_INVOCATION_PROMPT_FILE:-}" \
129
+ "${DEVLYN_INVOCATION_SESSION_FILE:-}" \
130
+ "${DEVLYN_INVOCATION_RECEIPT:-}"; do
131
+ if [ -n "$receipt_value" ]; then
132
+ RECEIPT_ENABLED=1
133
+ fi
134
+ done
135
+ if [ -n "$RECEIPT_ENABLED" ]; then
136
+ for receipt_name in \
137
+ DEVLYN_INVOCATION_RUN_ID DEVLYN_INVOCATION_PHASE DEVLYN_INVOCATION_ROUND \
138
+ DEVLYN_INVOCATION_WORKDIR DEVLYN_INVOCATION_PROMPT_FILE \
139
+ DEVLYN_INVOCATION_SESSION_FILE DEVLYN_INVOCATION_RECEIPT; do
140
+ eval "receipt_value=\${$receipt_name:-}"
141
+ if [ -z "$receipt_value" ]; then
142
+ printf '[codex-monitored] error: %s is required for invocation receipt\n' \
143
+ "$receipt_name" >&2
144
+ exit 64
145
+ fi
146
+ done
147
+ require_nonnegative_int DEVLYN_INVOCATION_ROUND "$DEVLYN_INVOCATION_ROUND"
148
+ fi
149
+
105
150
  if [ -n "${CODEX_MONITORED_ISOLATED:-}" ]; then
106
151
  CODEX_ARGS=(
107
152
  --ignore-user-config
@@ -187,16 +232,25 @@ terminate_process_group() {
187
232
  kill -KILL -- "-$pgid" 2>/dev/null || true
188
233
  }
189
234
 
235
+ terminate_monitor_group() {
236
+ local pid="$1"
237
+ kill -TERM -- "-$pid" 2>/dev/null || kill -TERM "$pid" 2>/dev/null || true
238
+ kill -KILL -- "-$pid" 2>/dev/null || kill -KILL "$pid" 2>/dev/null || true
239
+ wait "$pid" 2>/dev/null || true
240
+ }
241
+
190
242
  forward_signal() {
191
243
  local sig="$1"
192
244
  if [ -n "${CODEX_PID:-}" ] && kill -0 "$CODEX_PID" 2>/dev/null; then
193
245
  kill -"$sig" -- "-$CODEX_PID" 2>/dev/null || kill -"$sig" "$CODEX_PID" 2>/dev/null || true
194
246
  fi
195
- if [ -n "${HB_PID:-}" ] && kill -0 "$HB_PID" 2>/dev/null; then
196
- kill -TERM "$HB_PID" 2>/dev/null || true
247
+ if [ -n "${HB_PID:-}" ]; then
248
+ terminate_monitor_group "$HB_PID"
249
+ HB_PID=""
197
250
  fi
198
- if [ -n "${WATCHDOG_PID:-}" ] && kill -0 "$WATCHDOG_PID" 2>/dev/null; then
199
- kill -TERM "$WATCHDOG_PID" 2>/dev/null || true
251
+ if [ -n "${WATCHDOG_PID:-}" ]; then
252
+ terminate_monitor_group "$WATCHDOG_PID"
253
+ WATCHDOG_PID=""
200
254
  fi
201
255
  }
202
256
 
@@ -215,11 +269,23 @@ if [ -n "${CODEX_MONITORED_ISOLATED:-}" ]; then
215
269
  printf '[codex-monitored] isolated=1\n' >&2
216
270
  fi
217
271
 
272
+ if [ -n "$RECEIPT_ENABLED" ]; then
273
+ RECEIPT_HELPER="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/invocation-receipt.py"
274
+ python3 "$RECEIPT_HELPER" start \
275
+ --workdir "$DEVLYN_INVOCATION_WORKDIR" \
276
+ --receipt "$DEVLYN_INVOCATION_RECEIPT" \
277
+ --run-id "$DEVLYN_INVOCATION_RUN_ID" \
278
+ --phase "$DEVLYN_INVOCATION_PHASE" \
279
+ --round "$DEVLYN_INVOCATION_ROUND" \
280
+ --prompt-file "$DEVLYN_INVOCATION_PROMPT_FILE" \
281
+ --session-file "$DEVLYN_INVOCATION_SESSION_FILE" \
282
+ -- "${CODEX_ARGS[@]}" || exit 64
283
+ fi
284
+
218
285
  # Launch codex with stdin closed; output streams directly to OUR stdout/stderr.
219
286
  set -m
220
287
  "$CODEX_BIN" exec "${CODEX_ARGS[@]}" < /dev/null &
221
288
  CODEX_PID=$!
222
- set +m
223
289
  printf '[codex-monitored] codex pid=%d\n' "$CODEX_PID" >&2
224
290
 
225
291
  heartbeat_loop "$CODEX_PID" &
@@ -231,21 +297,24 @@ if [ "$TIMEOUT_SEC" -gt 0 ]; then
231
297
  timeout_loop "$CODEX_PID" "$TIMEOUT_SEC" "$TIMEOUT_FLAG" &
232
298
  WATCHDOG_PID=$!
233
299
  fi
300
+ set +m
234
301
 
235
302
  wait "$CODEX_PID"
236
303
  EXIT=$?
237
304
  terminate_process_group "$CODEX_PID" "post-exit-descendants"
238
-
239
- kill -TERM "$HB_PID" 2>/dev/null || true
240
- wait "$HB_PID" 2>/dev/null || true
241
- if [ -n "${WATCHDOG_PID:-}" ]; then
242
- kill -TERM "$WATCHDOG_PID" 2>/dev/null || true
243
- wait "$WATCHDOG_PID" 2>/dev/null || true
244
- fi
305
+ CODEX_PID=""
306
+ forward_signal TERM
245
307
  if [ -n "$TIMEOUT_FLAG" ] && [ -f "$TIMEOUT_FLAG" ]; then
246
308
  EXIT=124
247
309
  fi
248
310
 
311
+ if [ -n "$RECEIPT_ENABLED" ]; then
312
+ python3 "$RECEIPT_HELPER" finish \
313
+ --workdir "$DEVLYN_INVOCATION_WORKDIR" \
314
+ --receipt "$DEVLYN_INVOCATION_RECEIPT" \
315
+ --exit-code "$EXIT" || exit 64
316
+ fi
317
+
249
318
  printf '[codex-monitored] codex exited: code=%d elapsed=%ds\n' \
250
319
  "$EXIT" $(( $(date +%s) - START )) >&2
251
320
  exit "$EXIT"