devlyn-cli 4.0.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/AGENTS.md +11 -95
  2. package/CLAUDE.md +7 -160
  3. package/LICENSE +21 -0
  4. package/README.md +83 -114
  5. package/bin/devlyn.js +330 -472
  6. package/bin/instruction-templates.json +120 -2
  7. package/bin/instructions.js +35 -8
  8. package/config/skills/_shared/adapters/claude.md +8 -85
  9. package/config/skills/_shared/adapters/codex.md +14 -34
  10. package/config/skills/_shared/adapters/omp.md +1 -11
  11. package/config/skills/_shared/codex-monitored.sh +4 -54
  12. package/config/skills/_shared/engine-doctor.sh +18 -58
  13. package/config/skills/_shared/engine-preflight.md +15 -47
  14. package/config/skills/_shared/expected-contract.py +242 -0
  15. package/config/skills/_shared/expected.schema.json +10 -95
  16. package/config/skills/_shared/invocation-receipt.py +26 -757
  17. package/config/skills/_shared/platform-support.py +37 -28
  18. package/config/skills/_shared/role-config.py +100 -421
  19. package/config/skills/_shared/runtime-principles.md +17 -93
  20. package/config/skills/_shared/task-complete.py +490 -285
  21. package/config/skills/_shared/task-completion.md +235 -0
  22. package/config/skills/devlyn-engines/SKILL.md +9 -11
  23. package/config/skills/devlyn-ideate/SKILL.md +57 -107
  24. package/config/skills/devlyn-ideate/references/elicitation.md +18 -99
  25. package/config/skills/devlyn-ideate/references/loop.md +110 -0
  26. package/config/skills/devlyn-ideate/references/package-format.md +128 -0
  27. package/config/skills/devlyn-ideate/scripts/acceptance.py +795 -0
  28. package/config/skills/devlyn-ideate/scripts/queue.py +1780 -0
  29. package/package.json +2 -3
  30. package/config/commit-conventions.md +0 -28
  31. package/config/skills/_shared/adapters/README.md +0 -74
  32. package/config/skills/_shared/adapters/grok.md +0 -100
  33. package/config/skills/_shared/archive_run.py +0 -969
  34. package/config/skills/_shared/codex-config.md +0 -81
  35. package/config/skills/_shared/collect-codex-findings.py +0 -505
  36. package/config/skills/_shared/finish-gate.py +0 -521
  37. package/config/skills/_shared/grok-anchor-guard.py +0 -78
  38. package/config/skills/_shared/judge-output-parser.py +0 -200
  39. package/config/skills/_shared/judge-role-evidence.py +0 -320
  40. package/config/skills/_shared/phase-prompt-render.py +0 -183
  41. package/config/skills/_shared/process-evidence.py +0 -926
  42. package/config/skills/_shared/resolve-bootstrap.py +0 -1410
  43. package/config/skills/_shared/resolve-stop-hook.py +0 -359
  44. package/config/skills/_shared/run-bounded.py +0 -58
  45. package/config/skills/_shared/spec-verify-check.py +0 -5083
  46. package/config/skills/_shared/state-phase-write.py +0 -5989
  47. package/config/skills/_shared/terminal-claim-check.py +0 -777
  48. package/config/skills/_shared/verify-merge-findings.py +0 -3528
  49. package/config/skills/devlyn-design-ui/SKILL.md +0 -650
  50. package/config/skills/devlyn-ideate/references/from-spec-mode.md +0 -61
  51. package/config/skills/devlyn-ideate/references/project-mode.md +0 -80
  52. package/config/skills/devlyn-ideate/references/spec-template.md +0 -121
  53. package/config/skills/devlyn-ideate/references/templates/decision.md +0 -48
  54. package/config/skills/devlyn-ideate/references/templates/roadmap.md +0 -50
  55. package/config/skills/devlyn-ideate/references/templates/vision.md +0 -44
  56. package/config/skills/devlyn-queue/SKILL.md +0 -66
  57. package/config/skills/devlyn-queue/scripts/append.py +0 -94
  58. package/config/skills/devlyn-resolve/SKILL.md +0 -379
  59. package/config/skills/devlyn-resolve/references/free-form-mode.md +0 -71
  60. package/config/skills/devlyn-resolve/references/outer-loop.md +0 -23
  61. package/config/skills/devlyn-resolve/references/phases/build-gate.md +0 -75
  62. package/config/skills/devlyn-resolve/references/phases/cleanup.md +0 -33
  63. package/config/skills/devlyn-resolve/references/phases/implement.md +0 -44
  64. package/config/skills/devlyn-resolve/references/phases/plan.md +0 -39
  65. package/config/skills/devlyn-resolve/references/phases/probe-derive.md +0 -261
  66. package/config/skills/devlyn-resolve/references/phases/surface-close.md +0 -34
  67. package/config/skills/devlyn-resolve/references/phases/verify.md +0 -310
  68. package/config/skills/devlyn-resolve/references/state-schema.md +0 -152
  69. package/config/skills/devlyn-resolve/references/task-completion.md +0 -160
  70. package/config/templates/prompt-templates.md +0 -71
  71. package/config/templates/template-feature.spec.md +0 -255
  72. package/config/templates/template-product-spec.md +0 -680
@@ -1,92 +1,15 @@
1
1
  # Claude adapter
2
2
 
3
- > Source: <https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices>
4
-
5
3
  ## Invocation
6
4
 
7
- Orchestrator-side contract for spawning a Claude judge from a non-Claude
8
- orchestrator (Codex CLI, oh-my-pi) — the reverse-direction counterpart of
9
- `_shared/codex-config.md`. Source: <https://code.claude.com/docs/en/headless>
10
- and <https://code.claude.com/docs/en/cli-reference>.
5
+ A Claude executor starts as
6
+ `claude -p "<prompt>" --dangerously-skip-permissions --add-dir "<common git dir>"`,
7
+ where `<common git dir>` is `git rev-parse --path-format=absolute --git-common-dir`:
8
+ a linked task worktree commits there.
11
9
 
12
10
  **Availability probe**: `command -v claude >/dev/null 2>&1`; record
13
11
  `claude --version` as evidence. The probe is necessary, not sufficient:
14
- `claude -p` needs network access to the Anthropic API, so a network-denying
15
- sandbox (e.g. Codex CLI's default `workspace-write`) fails the spawn even
16
- though the binary resolves. A failed spawn is the same fail-closed class as a
17
- failed probe (`_shared/engine-preflight.md`): explicit route →
18
- `BLOCKED:claude-unavailable`; automatic escalation → solo + reported skip.
19
-
20
- **Read-only pair-JUDGE call** (bounded by the VERIFY pair contract — at most
21
- two targeted probes; the invoking phase sets `--effort`, pair-JUDGE uses
22
- `medium`):
23
-
24
- ```bash
25
- python3 "$DEVLYN_SHARED_DIR/run-bounded.py" 600 --stdin-file "<judge-prompt-file>" --record-transport -- claude -p \
26
- --permission-mode dontAsk \
27
- --tools "Read,Grep,Glob" --allowedTools "Read,Grep,Glob" \
28
- --setting-sources project --strict-mcp-config --mcp-config '{"mcpServers":{}}' \
29
- --effort medium \
30
- > .devlyn/claude-judge.stdout 2> .devlyn/claude-judge.stderr
31
- ```
32
-
33
- - Write the complete prompt as exact UTF-8 file bytes; no positional prompt or shell substitution. With `--record-transport`, the runner snapshots stdin and exclusively creates `<judge-prompt-file>.transport.json` to bind actual native argv and delivered bytes; an existing carrier rejects dispatch. Preserve it for explicit judge-role authentication. Ordinary `--stdin-file` reads require no output or writable input directory; without it, stdin remains DEVNULL.
34
- - Omit `--model` — the CLI's configured default is used (zero-touch, same
35
- rule as codex-config.md's "omit `-m`").
36
- - `dontAsk` denies anything not allowlisted (official guide: "denies
37
- anything not in your permissions.allow rules or the read-only command
38
- set — useful for locked-down CI runs"); in `-p` mode a denied tool call
39
- fails without prompting, so the child can never hang on a permission ask.
40
- - The `--setting-sources project --strict-mcp-config --mcp-config
41
- '{"mcpServers":{}}'` trio is the hermetic-child pattern: no user-global
42
- settings, hooks, or MCP servers. `--bare` is NOT used — it skips
43
- OAuth/keychain reads and requires `ANTHROPIC_API_KEY`, which
44
- subscription-auth machines do not have.
45
- - Findings come back as JSONL on stdout (same emission shape as the Codex
46
- direction); the orchestrator writes the canonical
47
- `.devlyn/verify.pair.findings.jsonl`. Raw stdout stays diagnostic at
48
- `.devlyn/claude-judge.stdout`; `verify-merge-findings.py` blocks the run
49
- if stdout contains findings the canonical file lacks.
50
- - Exit 124 is a wall-budget abort (terminates the native child process tree; POSIX process group) → the orchestrator
51
- writes `.devlyn/verify.pair.timeout.json`; budget abort ≠ availability failure
52
- and the fail-closed availability rules above are unchanged.
53
- - Do not pipe stdout (`| tail`, `| grep`); capture to file. Non-zero exit other
54
- than 124 or empty stdout → spawn failure, fail closed per the probe rule above.
55
-
56
- ## Identity
57
-
58
- You are Claude by Anthropic. Anthropic's prompt-engineering guide for this model governs your behavior on top of the canonical phase prompt below.
59
-
60
- ## Output discipline
61
-
62
- You calibrate response length to task complexity automatically — keep simple lookups short, scale up only when the task warrants it. Do NOT pad with context the user didn't ask for. When the canonical body sets a structural format (XML, JSON, sections), follow it literally; do not silently restructure.
63
-
64
- ## Examples and structure
65
-
66
- When prompt maintenance adds examples for Claude, prefer concise positive examples over lists of negative prohibitions. Wrap examples in `<example>` tags (or `<examples>` for several) so examples stay distinct from instructions and variable inputs.
67
-
68
- ## Tool-use posture
69
-
70
- When the canonical body lists tools, use them when their result would change your answer. Make independent tool calls in parallel; chain only when one depends on another's output. Do not narrate "I'll now call X" preambles unless the canonical body requests progress updates.
71
-
72
- ## Effort and autonomy
73
-
74
- For long-horizon coding, review, and agentic runs, assume the harness selected `high` or `xhigh` effort unless told otherwise. Spend that depth on upfront task/constraint understanding and end-state verification, not on verbose narration. If the user or orchestrator gives a complete task in one turn, proceed autonomously instead of requiring progressive clarification.
75
-
76
- ## Anti-patterns
77
-
78
- You interpret instructions literally and explicitly. The official guide is explicit about three failure modes:
79
-
80
- 1. **Review-prompt self-filtering**: when the canonical body asks for findings, report every issue you find — including low-severity and low-confidence ones; do not filter for importance or confidence. The harness has a separate filter step.
81
- 2. **Subagent spawning**: do NOT spawn a subagent for work you can complete in a single response. Spawn only when the canonical body explicitly requests it OR when fanning out across independent items.
82
- 3. **Overengineering**: do NOT add files, abstractions, error handling, validation, or "future flexibility" beyond what the spec asks. A bug fix doesn't need surrounding cleanup. The right complexity is the minimum needed for the current task.
83
-
84
- You do NOT need stronger imperatives ("CRITICAL!", "YOU MUST!") to follow rules. Normal phrasing is sufficient.
85
-
86
- ## Explicit role capability
87
-
88
- `role-config.py` validates explicit CLI judge effort against this bounded native capability declaration. Source: Claude Code2.1.263 built-in Fable5.1 metadata (native SHA ef5d2909c8af49f31ab6d5487e90316777bc2fac170adfe8160716caa8aaf4f9, byte157907500; effort/max/xhigh gates159068038/159068443/159068839). This is option support, not model fitness. Unknown version/model effort support fails with actionable unsupported-role-option. Model-only requests pass the exact ID to the native CLI without this effort table and still require matching native result evidence; engine-only defaults remain available. Native Agent workers have no validated exact-model/effort transport here, so explicit worker fields are rejected instead of switching channels.
89
-
90
- <!-- devlyn-effort 2.1.263 claude-fable-5-1 low,medium,high,xhigh,max -->
91
-
92
- For an explicit CLI judge profile, use the resolved `--model`/`--effort` in the bounded read-only invocation and add `--output-format json`. Follow the canonical resolve `JUDGE_STEM` round-scoped argv/prompt/raw capture and `judge-role-evidence.py` derivation contract. Only its exact derived result goes to the findings collector; never feed the JSON wrapper directly. Omitted options keep that role’s existing defaults.
12
+ `claude -p` needs network access to the Anthropic API, so in a network-denying
13
+ sandbox (e.g. Codex CLI's default `workspace-write`) it fails even though the
14
+ binary resolves. A drain blocks when the executor cannot start, and a task
15
+ whose executor exits without a submission waits for the next drain.
@@ -1,34 +1,14 @@
1
- # Codex (OpenAI) adapter
2
-
3
- > Source: <https://developers.openai.com/api/docs/guides/prompt-guidance?model=gpt-5.5>
4
-
5
- ## Identity
6
-
7
- You are OpenAI's Codex model. OpenAI's prompt-guidance for this model governs your behavior on top of the canonical phase prompt below.
8
-
9
- ## Output discipline
10
-
11
- Your default is efficient, direct, task-oriented. The canonical body specifies the outcome and constraints; you choose the efficient path. Do not over-specify process steps when an outcome is clearly stated. Use Markdown only where it carries structure (`inline code`, code fences, short lists/tables); otherwise favor short paragraphs and natural transitions. When `text.verbosity` is `low`, prefer even shorter responses.
12
-
13
- ## Tool-use posture
14
-
15
- Resolve the request in the fewest useful tool loops without sacrificing correctness. For retrieval tasks: start with one broad search using short discriminative keywords; make another retrieval call only when the top results don't answer the core question or a required fact / parameter / source is missing. For tool-heavy tasks, start with a brief preamble: a one-line acknowledgment of the request and the first step you'll take.
16
-
17
- For a constrained-read VERIFY packet, judge the supplied material without tools; missing or unbound inputs require a verdict-binding BLOCKED finding. Keep the read-only sandbox and bounded review. Otherwise, for VERIFY JUDGE retrieval, batch related reads. After sealed parity proves a
18
- canonical file and its `.agents` mirror are byte-identical, inspect the
19
- canonical file only. Open self-test bodies or MECHANICAL raw streams only for a
20
- named clause that remains unresolved after the broad pass.
21
-
22
- ## Anti-patterns
23
-
24
- The official guide warns explicitly about carrying over instructions from older prompt stacks — earlier models needed more help, and process-heavy directives now narrow GPT-5.5's search space.
25
-
26
- 1. **Avoid absolute imperatives for judgment calls.** ALWAYS / NEVER / must / only are reserved for true safety invariants and required output fields. For judgment calls, use decision rules with conditions ("when X, do Y"). The canonical body uses this style; do not promote softer guidance to absolute rules.
27
- 2. **Don't over-specify process when the destination is clear.** If the canonical body names the outcome, choose the path; do not narrate every step.
28
- 3. **Stop rules are explicit.** When the canonical body or the harness asks you to stop / abstain / ask, follow the stop rule rather than retrying loops indefinitely. Loop-minimization does not outrank correctness or required citation.
29
-
30
- ## Prompt-maintenance cue
31
-
32
- When asked to improve a failed prompt, act as a metaprompter for itself: name the observed failure, then propose the smallest instruction to add, remove, or relocate. Prefer subtractive changes before adding new rules; keep the canonical body model-neutral and put only GPT-specific tactics in this adapter.
33
-
34
- Do not narrate internal deliberation. State results and decisions directly.
1
+ # Codex adapter
2
+
3
+ ## Invocation
4
+
5
+ A Codex executor runs through `_shared/codex-monitored.sh`, never a raw or piped
6
+ `codex exec`: `bash "$DEVLYN_SHARED_DIR/codex-monitored.sh" <arguments for codex exec> "<prompt>"`.
7
+ The `workspace-write` sandbox keeps `.git` read-only, so an executor that commits adds as
8
+ writable roots the common Git directory (`git rev-parse --path-format=absolute --git-common-dir`)
9
+ plus, in a linked worktree, its own Git directory (`git rev-parse --path-format=absolute --git-dir`),
10
+ listed exactly because the sandbox protects it even under a listed parent. It also
11
+ denies network, which installing a new dependency needs:
12
+ `-s workspace-write -c 'sandbox_workspace_write.writable_roots=["<git dir>","<worktree git dir>"]' -c 'sandbox_workspace_write.network_access=true'`.
13
+ Deliver a multiline prompt as exact file bytes: set `DEVLYN_CODEX_PROMPT_FILE=<file>`
14
+ and pass the sole prompt argument `-`.
@@ -1,13 +1,3 @@
1
1
  # oh-my-pi adapter
2
2
 
3
- ## Identity
4
-
5
- You are the model running behind the `omp` CLI. Follow the canonical phase prompt exactly; this adapter exists so oh-my-pi can be selected as a first-class native task worker by `/devlyn-resolve`.
6
-
7
- ## Output Discipline
8
-
9
- Keep phase output machine-consumable and concise. Write the artifacts the canonical body names, update `.devlyn/pipeline.state.json` only where that phase owns the state write, and avoid narrative outside the required phase result.
10
-
11
- ## Fresh Context
12
-
13
- Each native `task` tool invocation with a fresh `context` is a fresh phase worker. Do not call `/devlyn-resolve` recursively from inside a phase prompt; execute only the phase body you received.
3
+ Engine `omp`, the oh-my-pi CLI.
@@ -22,9 +22,9 @@
22
22
  # (Round 2 finding #1 fix: shim alone does not defeat `| tail`; the
23
23
  # wrapper must reject the pipe shape directly.)
24
24
  # 2. Closes stdin unless DEVLYN_CODEX_PROMPT_FILE supplies the sole prompt.
25
- # 3. Streams codex stdout to OUR stdout line-by-line — the orchestrator reads
26
- # stdout as the subagent reply (per `_shared/codex-config.md`) so we MUST
27
- # NOT swallow it (e.g. `tail -n 200`). codex stderr forwards to OUR stderr.
25
+ # 3. Streams codex stdout to OUR stdout line-by-line — the caller reads
26
+ # stdout as the Codex reply, so we MUST NOT swallow it (e.g.
27
+ # `tail -n 200`). codex stderr forwards to OUR stderr.
28
28
  # 4. Emits a `[codex-monitored] heartbeat` line every CODEX_MONITORED_HEARTBEAT
29
29
  # seconds (default 30s) on STDERR while codex is alive. Heartbeat-on-stderr
30
30
  # keeps the orchestrator's combined-output stream non-silent without
@@ -47,18 +47,10 @@
47
47
  # CODEX_REAL_BIN when set, else `codex`.
48
48
  # Set this when the shim has put us first
49
49
  # on PATH.
50
- # CODEX_MONITORED_ISOLATED — set non-empty for bounded read-only
51
- # probe/judge calls that must ignore
52
- # user config, project rules, session
53
- # persistence, and hook side effects.
54
50
  # CODEX_MONITORED_ALLOW_PIPED — set non-empty to skip the pipe-stdout
55
51
  # refusal. Reserved for tests; don't use
56
52
  # in skill prompts.
57
53
  # DEVLYN_CODEX_PROMPT_FILE — exact binary stdin prompt; requires sole `-`.
58
- # DEVLYN_INVOCATION_* — run/phase/round/workdir/prompt/session/
59
- # receipt identity. When any is set, all
60
- # are required and the wrapper seals a
61
- # canonical invocation receipt.
62
54
 
63
55
  set -uo pipefail
64
56
 
@@ -120,45 +112,6 @@ require_positive_int() {
120
112
  require_positive_int CODEX_MONITORED_HEARTBEAT "$HEARTBEAT_SEC"
121
113
  require_nonnegative_int CODEX_MONITORED_TIMEOUT_SEC "$TIMEOUT_SEC"
122
114
 
123
- RECEIPT_ENABLED=""
124
- for receipt_value in \
125
- "${DEVLYN_INVOCATION_RUN_ID:-}" \
126
- "${DEVLYN_INVOCATION_PHASE:-}" \
127
- "${DEVLYN_INVOCATION_ROUND:-}" \
128
- "${DEVLYN_INVOCATION_WORKDIR:-}" \
129
- "${DEVLYN_INVOCATION_PROMPT_FILE:-}" \
130
- "${DEVLYN_INVOCATION_SESSION_FILE:-}" \
131
- "${DEVLYN_INVOCATION_RECEIPT:-}"; do
132
- if [ -n "$receipt_value" ]; then
133
- RECEIPT_ENABLED=1
134
- fi
135
- done
136
- if [ -n "$RECEIPT_ENABLED" ]; then
137
- for receipt_name in \
138
- DEVLYN_INVOCATION_RUN_ID DEVLYN_INVOCATION_PHASE DEVLYN_INVOCATION_ROUND \
139
- DEVLYN_INVOCATION_WORKDIR DEVLYN_INVOCATION_PROMPT_FILE \
140
- DEVLYN_INVOCATION_SESSION_FILE DEVLYN_INVOCATION_RECEIPT; do
141
- eval "receipt_value=\${$receipt_name:-}"
142
- if [ -z "$receipt_value" ]; then
143
- printf '[codex-monitored] error: %s is required for invocation receipt\n' \
144
- "$receipt_name" >&2
145
- exit 64
146
- fi
147
- done
148
- require_nonnegative_int DEVLYN_INVOCATION_ROUND "$DEVLYN_INVOCATION_ROUND"
149
- fi
150
-
151
- if [ -n "${CODEX_MONITORED_ISOLATED:-}" ]; then
152
- CODEX_ARGS=(
153
- --ignore-user-config
154
- --ignore-rules
155
- --ephemeral
156
- --disable codex_hooks
157
- --disable hooks
158
- "${CODEX_ARGS[@]}"
159
- )
160
- fi
161
-
162
115
  # --- Pipe-stdout refusal (iter-0009 R2 finding #1) -------------------------
163
116
  # `[ -p /dev/stdout ]` is the POSIX test for "is fd 1 a FIFO/pipe". Verified
164
117
  # correct on macOS via lsof: distinguishes piped (`| cat`) from redirected
@@ -266,9 +219,6 @@ trap cleanup EXIT
266
219
 
267
220
  printf '[codex-monitored] start: ts=%s heartbeat=%ds timeout=%ss bin=%s\n' \
268
221
  "$(date -u +%FT%TZ)" "$HEARTBEAT_SEC" "$TIMEOUT_SEC" "$CODEX_BIN" >&2
269
- if [ -n "${CODEX_MONITORED_ISOLATED:-}" ]; then
270
- printf '[codex-monitored] isolated=1\n' >&2
271
- fi
272
222
 
273
223
  RECEIPT_HELPER="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/invocation-receipt.py"
274
224
  # Native Python owns Windows PIDs and the timeout tree; MSYS PIDs are different.
@@ -304,7 +254,7 @@ if [ -n "$TIMEOUT_FLAG" ] && [ -f "$TIMEOUT_FLAG" ]; then
304
254
  EXIT=124
305
255
  fi
306
256
 
307
- if [ -n "$RECEIPT_ENABLED" ] || [ -n "${DEVLYN_CODEX_PROMPT_FILE:-}" ]; then
257
+ if [ -n "${DEVLYN_CODEX_PROMPT_FILE:-}" ]; then
308
258
  python3 "$RECEIPT_HELPER" complete-dispatch --exit-code "$EXIT" || exit 64
309
259
  fi
310
260
 
@@ -5,25 +5,18 @@
5
5
  # just what's pinned. This script never writes .devlyn/engines.json, never
6
6
  # installs anything, never changes pin validation — it only reports.
7
7
  #
8
- # CATALOG NOTE: `pi` (bin/devlyn.js's Pi/earendil-works install target) has
9
- # no verified CLI binary name anywhere in this repo — its own installer
10
- # detect() only checks project marker files, never shells out to a binary.
11
- # Fabricating a `command -v pi` check would be a guess this repo's evidence
12
- # rule forbids, so its binary column reports "unknown" rather than "no".
13
- #
14
8
  # macOS-safe bash 3.2: no associative arrays, no mapfile/readarray.
15
9
  set -uo pipefail
16
10
 
17
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
18
12
  ADAPTERS_DIR="$SCRIPT_DIR/adapters"
19
13
 
20
- TARGETS=(claude codex grok omp pi)
21
- KINDS=(cli-engine cli-engine cli-engine cli-engine orchestrator-only)
22
- BINARIES=(claude codex grok omp "")
14
+ TARGETS=(claude codex omp pi)
15
+ KINDS=(cli-engine cli-engine cli-engine orchestrator-only)
16
+ BINARIES=(claude codex omp "")
23
17
  INSTALL_HINTS=(
24
18
  "see https://docs.anthropic.com/en/docs/claude-code"
25
19
  "npm install -g @openai/codex"
26
- "see https://docs.x.ai/build/overview (install + browser auth)"
27
20
  "brew install can1357/tap/omp"
28
21
  ""
29
22
  )
@@ -39,72 +32,39 @@ check_adapter() {
39
32
  if [ -f "$ADAPTERS_DIR/$1.md" ]; then printf 'yes'; else printf 'no'; fi
40
33
  }
41
34
 
42
- check_role() {
43
- # $1 = adapter file path. Reads the optional `## Role eligibility` fixed
44
- # ASCII fields (_shared/adapters/README.md); absent file or section = both
45
- # roles eligible by default (every adapter shipped before iter-0051).
35
+ check_executor() {
36
+ # $1 = adapter file path. An adapter may declare the fixed ASCII field
37
+ # `executor: no` under `## Role eligibility`; otherwise it can execute.
46
38
  [ -f "$1" ] || { printf 'n/a'; return; }
47
- local exec_ok='yes' judge_ok='yes'
48
- grep -q '^executor: no' "$1" 2>/dev/null && exec_ok='no'
49
- grep -q '^pair_judge: no' "$1" 2>/dev/null && judge_ok='no'
50
- if [ "$exec_ok" = 'yes' ] && [ "$judge_ok" = 'yes' ]; then printf 'executor+judge'
51
- elif [ "$judge_ok" = 'yes' ]; then printf 'judge-only'
52
- elif [ "$exec_ok" = 'yes' ]; then printf 'executor-only'
53
- else printf 'none'
54
- fi
39
+ if grep -q '^executor: no' "$1" 2>/dev/null; then printf 'no'; else printf 'yes'; fi
55
40
  }
56
41
 
57
- printf '%-8s %-17s %-8s %-8s %-14s %-12s %s\n' \
58
- 'target' 'kind' 'binary' 'adapter' 'role' 'pin_eligible' 'note'
59
-
60
- pin_eligible_count=0
61
- missing_hints=()
42
+ printf '%-8s %-17s %-8s %-8s %-9s %s\n' \
43
+ 'target' 'kind' 'binary' 'adapter' 'executor' 'note'
62
44
 
63
45
  for i in "${!TARGETS[@]}"; do
64
46
  target="${TARGETS[$i]}"
65
47
  kind="${KINDS[$i]}"
66
48
  binary="$(check_binary "${BINARIES[$i]}")"
67
49
  adapter="$(check_adapter "$target")"
68
- role="$(check_role "$ADAPTERS_DIR/$target.md")"
69
-
70
- pin_eligible='no'
71
- case "$kind" in
72
- cli-engine) [ "$binary" = 'yes' ] && [ "$adapter" = 'yes' ] && pin_eligible='yes' ;;
73
- esac
50
+ executor="$(check_executor "$ADAPTERS_DIR/$target.md")"
74
51
 
75
52
  note='-'
76
53
  case "$kind" in
77
54
  cli-engine)
78
- if [ "$pin_eligible" = 'yes' ]; then
79
- pin_eligible_count=$((pin_eligible_count + 1))
80
- elif [ "$binary" = 'no' ]; then
55
+ if [ "$binary" = 'no' ]; then
81
56
  note="not installed; ${INSTALL_HINTS[$i]}"
82
- missing_hints+=("$target: ${INSTALL_HINTS[$i]}")
83
- else
84
- note="binary present, no adapter — ship _shared/adapters/$target.md"
57
+ elif [ "$adapter" = 'no' ]; then
58
+ note="binary present, no adapter — reinstall devlyn-cli, whose releases ship the adapters"
59
+ elif [ "$executor" = 'no' ]; then
60
+ note='adapter declares executor: no'
85
61
  fi
86
62
  ;;
87
63
  orchestrator-only)
88
- note='informational only; not a routable role engine — no verified CLI binary or adapter'
64
+ note='informational only; not a routable engine — no verified CLI binary or adapter'
89
65
  ;;
90
66
  esac
91
67
 
92
- [ "$target" = 'grok' ] && note='not emission-certified; BLOCKED pair source'
93
-
94
- printf '%-8s %-17s %-8s %-8s %-14s %-12s %s\n' \
95
- "$target" "$kind" "$binary" "$adapter" "$role" "$pin_eligible" "$note"
68
+ printf '%-8s %-17s %-8s %-8s %-9s %s\n' \
69
+ "$target" "$kind" "$binary" "$adapter" "$executor" "$note"
96
70
  done
97
-
98
- printf '\n'
99
- if [ "$pin_eligible_count" -lt 2 ]; then
100
- printf 'Recommendation: only %d adapter-valid engine(s) available. VERIFY pair-judge and\n' "$pin_eligible_count"
101
- printf 'risk-probe escalation need a second, genuinely different model to check against —\n'
102
- printf 'iter-0045 found different model tiers hit different failure-mode blind spots (not\n'
103
- printf 'that any arbitrary second model helps); with fewer than 2, those routes can never\n'
104
- printf 'fire and every run stays solo. Add one of:\n'
105
- for hint in "${missing_hints[@]}"; do
106
- printf ' - %s\n' "$hint"
107
- done
108
- else
109
- printf 'Pair-judge diversity: %d adapter-valid engines available.\n' "$pin_eligible_count"
110
- fi
@@ -1,61 +1,29 @@
1
- # Shared — Engine Pre-flight
1
+ # Shared — Executor Pre-flight
2
2
 
3
- Used by `/devlyn-resolve` and `/devlyn-ideate`. One shared availability rule so every skill routes identically.
3
+ Used by `/devlyn-ideate` and `/devlyn-engines`: one rule for which engine executes implementation work and what happens when it is missing.
4
4
 
5
- ## Rule
5
+ ## Selection
6
6
 
7
- Each skill resolves the effective engine from its own SKILL.md default plus any explicit `--engine` flag passed by the user. `/devlyn-resolve` also computes the default VERIFY-pair route and conditional risk-probe requirements before the phase that needs the OTHER engine.
7
+ The executor is the `executor` pin in `.devlyn/engines.json` at the checkout root (set with `/devlyn-engines executor <name>`), else the CLI the user opened. A linked worktree or subdirectory has no pin unless one is set there. Pi and Grok have no executor adapter, so a drain from them needs a claude, codex or omp pin. Keys earlier releases wrote for retired pipeline roles (`pair_judge_priority`, `roles`) stay as written and select nothing: a worker profile never becomes the executor.
8
8
 
9
- Engine requirements have two classes:
9
+ Before dispatching to the executor, run:
10
10
 
11
- - **Explicit routes** — `--engine`, `--risk-probes`, `--pair-verify`, configured executor/role/priority pins. These are promises. If the required engine is unavailable, fail closed with `BLOCKED:<engine>-unavailable` and never downgrade to solo.
12
- - **Automatic routes** — high-risk risk-probes remain conditional; VERIFY pair-JUDGE is selected by default unless `--no-pair` was passed. Both require an available OTHER engine. If it is absent, proceed solo and report the skipped route and reason. This is route selection, not a fallback.
13
-
14
- ## Role resolution
15
-
16
- The orchestrator is the CLI the user opened. PLAN remains orchestrator-fixed. Resolve the legacy executor as explicit `--engine` > `cwd/.devlyn/engines.json` `executor` > the invoking skill's supported default. BUILD_GATE runs commands in the orchestrator without a separate model; risk-probe derivation keeps its existing route.
17
-
18
- The role profiles and `--role-config` below apply only to `/devlyn-resolve`; `/devlyn-ideate` keeps its existing `--engine` / legacy executor selection and never inherits worker/judge profiles. `role-config.py` is resolve’s deterministic resolver. Optional project `roles.worker`, `roles.primary_judge`, and `roles.pair_judge` each require `engine` and accept exact `model`/`effort`; `/devlyn-resolve --role-config <path>` supplies the same `{"roles": {...}}` object for one run. Each entry replaces the entire lower entry. Worker/primary precedence is run role > `--engine` engine-only > project role > legacy executor. Pair precedence is run role > project role > existing `pair_judge_priority`/binary complement. An absent primary profile follows the legacy executor, not the worker profile. No parent/global devlyn config lookup.
19
-
20
- Worker controls IMPLEMENT, its included code/doc cleanup, and their repair rounds only; owner CLEANUP performs artifact/invariant checks without a worker dispatch; primary/pair control VERIFY only. OTHER must differ from the resolved primary engine. Explicit same-engine pair is invalid; a legacy priority list skips the primary. `--no-pair` does not dispatch or check availability of an unused pair. Configured pins remain explicit promises, while an unconfigured unavailable automatic OTHER records its solo reason.
21
-
22
- Before the first phase, `/devlyn-resolve` freezes resolution with `state-phase-write.py --freeze-roles`; subsequent status/dispatch reads that state instead of mutable config. `state.engine` stays the legacy executor. The resolver rejects statically unsupported selected channel fields before PLAN, and validates adapter names and the existing ASCII `executor: yes|no` / `pair_judge: yes|no` eligibility markers; absent eligibility section means both. Malformed configuration fails with `BLOCKED:invalid-engine-config`.
23
-
24
- Before each affected worker/judge, read `role-config.py --state .devlyn/pipeline.state.json --role <role> [--resolved-model <inherited-exact-model>]`. It returns source/channel, requested values and argv additions. Explicit Codex options require current version-matching native model metadata; refresh the native CLI's cache when missing/stale. Explicit Claude judge effort requires the adapter's version/exact-model capability declaration; model-only selection passes the exact ID without that effort declaration and validates the native result. No model/effort fields means unchanged phase defaults. Unknown support fails with `BLOCKED:unsupported-role-option`; no clamp, fallback or invented model identity. Native Agent workers reject unsupported explicit Claude fields without switching to a CLI worker. Show inherited/unknown values honestly; availability is not role fitness.
25
-
26
- Engine-only adapter routes remain supported according to existing eligibility. New adapters do not automatically gain explicit model/effort mappings. Status reports unavailable pins and unresolved legacy channels without weakening run freeze/dispatch. CLI presence is not authentication. Status never launches a model, changes user configuration or promotes a default.
27
-
28
- When a run or phase requires engine `<name>` (claude, codex, omp, or any adapter-backed engine), before spawning that phase:
11
+ ```sh
12
+ python3 "$DEVLYN_SHARED_DIR/role-config.py" --workdir <project> --default-engine <this CLI> --select
13
+ ```
29
14
 
30
- 1. Run the adapter's availability probe — default `command -v <name> >/dev/null 2>&1`; an adapter's `## Invocation` section may declare a stricter contract (e.g. `adapters/claude.md`: the probe is necessary but not sufficient under a network-denying sandbox, so a failed spawn is handled identically to a failed probe).
31
- 2. On failure -> set the current phase/run verdict to `BLOCKED:<name>-unavailable`, preserve the failed check evidence, and show the adapter's setup guidance: install/configure that CLI, run its auth/login flow, verify `<name> --version`, then rerun. If the user intentionally wants solo VERIFY, they may rerun with `--no-pair`.
32
- 3. On success -> proceed with the original engine value.
15
+ A pin is a promise. When the pinned engine's CLI is unavailable, the command exits 1 with `BLOCKED:<engine>-unavailable`: stop, preserve the evidence, and show the setup guidance — install and authenticate that CLI, verify `<engine> --version`, rerun. Never substitute another engine. A malformed file, or a name without an executor-eligible adapter, is `BLOCKED:invalid-engine-config`. CLI presence is not authentication, and status never launches a model.
33
16
 
34
- Never prompt the user mid-pipeline. Missing engines for explicit routes are BLOCKED states, not silent fallbacks. Missing OTHER engines for automatic escalations are reported solo-skips, not fallbacks — the auto route was never selected.
17
+ ## Engines
35
18
 
36
- Per-skill defaults: `/devlyn-resolve` uses Claude for IMPLEMENT when the orchestrator has Claude Code's native Agent primitive; Codex CLI and oh-my-pi orchestrators use their own fresh worker as the default route (Codex child process, omp native `task`) so implementation and independent verification retain structural isolation. PLAN uses the owner context; BUILD_GATE/CLEANUP use owner commands. VERIFY invokes the OTHER engine by default when available unless `--no-pair` was passed. `/devlyn-ideate` defaults to Claude; `--engine` selects the elicitation/normalization adapter, not an automatic cross-model challenge phase. Any future ideate read-only critique must follow `_shared/codex-config.md` isolation rules. Each SKILL.md flag block is source of truth for that skill's default.
19
+ An engine is pinnable when `_shared/adapters/<name>.md` exists and does not declare `executor: no` under `## Role eligibility`. Adapters ship with devlyn-cli releases, and a reinstall replaces `_shared`. An adapter's `## Invocation` says how to start it. On native Windows, use native Node/npm and Python (`python3` on PATH), with Git for Windows Bash for the shipped shell wrapper.
37
20
 
38
- ## What a skill must report after a BLOCKED engine check
21
+ ## Reporting a blocked engine
39
22
 
40
- When an engine required by the selected route or default VERIFY pair route is absent, the final user-facing report/summary shows the requested route, the missing engine, and setup steps:
23
+ The final report names the selected engine, the verdict and the setup steps; it never reports a downgraded success:
41
24
 
42
25
  ```
43
- Engine: claude + codex pair required
26
+ Engine: codex (pinned executor)
44
27
  Verdict: BLOCKED:codex-unavailable
45
- Setup: install/configure Codex CLI; run the current Codex auth/login flow; verify `codex --version`; rerun. Use `--no-pair` only for an intentional solo VERIFY run.
28
+ Setup: install/configure Codex CLI; run the current Codex auth/login flow; verify `codex --version`; rerun.
46
29
  ```
47
-
48
- Do not report a downgraded successful run when a required engine is missing.
49
-
50
- ## Canonical cross-engine invocations
51
-
52
- For native Windows, use native Node/npm and Python (`python3` on PATH), with Git for Windows Bash for the shipped shell wrapper. Supported npm engine shims resolve to native argv; unsupported/malformed shims fail visibly. Deliver multiline prompts from files: Codex uses `DEVLYN_CODEX_PROMPT_FILE` with explicit `-`; Claude uses `run-bounded.py 600 --stdin-file <prompt-file> --record-transport -- claude -p`. Retain generated transport evidence. A policy-denied Codex judge read uses the complete inline no-tools packet in `_shared/codex-config.md#constrained-windows-judge-reads`, keeping read-only isolation and the selected role.
53
-
54
- After the availability check passes, Codex keeps the monitored `codex-monitored.sh`
55
- path; every other resolved OTHER engine follows `_shared/adapters/<name>.md`
56
- `## Invocation`. Capture `.devlyn/<name>-judge.stdout`, then normalize it with
57
- `collect-codex-findings.py --devlyn-dir "<abs repo>/.devlyn" --stdout-file
58
- <name>-judge.stdout`. A non-zero collector exit writes no canonical findings:
59
- the pair source is `BLOCKED` for `verify.pair.emission-contract`, and unparsed
60
- stdout remains diagnostic-only rather than being merged. On exit 124, the
61
- timeout marker's `engine` field is the resolved `<name>`.