@lyupro/codex-bridge 0.6.9 → 0.6.11

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 (88) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/README.md +15 -4
  3. package/cli/dispatcher-model-check.mjs +59 -0
  4. package/cli/doctor-host-side.mjs +2 -1
  5. package/cli/doctor-installation.mjs +111 -111
  6. package/cli/doctor.mjs +13 -5
  7. package/cli/handback-witness-check.mjs +24 -18
  8. package/cli/hosts.mjs +2 -10
  9. package/cli/install-owners.mjs +1 -4
  10. package/cli/install-record.mjs +2 -1
  11. package/cli/inventory-transition.mjs +1 -1
  12. package/cli/lifecycle-lock-inspect.mjs +3 -2
  13. package/cli/lifecycle-lock.mjs +19 -132
  14. package/cli/manifest.mjs +6 -6
  15. package/cli/read.mjs +20 -0
  16. package/cli/settings-merge.mjs +3 -29
  17. package/package.json +2 -2
  18. package/scripts/home-write-inventory.mjs +12 -2
  19. package/src/agents/codex-advisor.md +37 -121
  20. package/src/agents/codex-build.md +27 -135
  21. package/src/agents/codex-review.md +19 -111
  22. package/src/agents/codex-scout.md +21 -130
  23. package/src/home/hooks/dispatcher-gate.mjs +14 -9
  24. package/src/home/hooks/live-runs.mjs +0 -41
  25. package/src/home/hooks/order-gate.mjs +80 -47
  26. package/src/home/hooks/reply-guard.mjs +52 -65
  27. package/src/home/hooks/reply-identity.mjs +64 -0
  28. package/src/home/hooks/reply-parser.mjs +1 -1
  29. package/src/home/hooks/reply-verdicts.mjs +31 -27
  30. package/src/home/hooks/reply-witness.mjs +48 -0
  31. package/src/home/lib/claude-layout.mjs +14 -0
  32. package/src/home/lib/command-path.mjs +32 -0
  33. package/src/home/lib/dispatcher-call.mjs +171 -0
  34. package/src/home/lib/dispatcher-command.mjs +18 -54
  35. package/src/home/lib/dispatcher-gate.mjs +4 -0
  36. package/src/home/lib/dispatcher-model.mjs +112 -0
  37. package/src/home/lib/dispatcher-order.mjs +21 -22
  38. package/src/home/lib/dispatcher-pin.mjs +60 -0
  39. package/src/home/lib/dispatcher-protocol.mjs +91 -0
  40. package/src/home/lib/dispatcher-stop.mjs +11 -2
  41. package/src/home/lib/handback-witness.mjs +137 -48
  42. package/src/home/lib/home-registry.mjs +7 -0
  43. package/src/home/lib/install-owner-roots.mjs +53 -0
  44. package/src/home/lib/kernel-lock.mjs +171 -0
  45. package/src/home/lib/meta/chain.mjs +19 -8
  46. package/src/home/lib/meta/context-only.mjs +43 -0
  47. package/src/home/lib/meta/events.mjs +14 -2
  48. package/src/home/lib/meta/launch-rows.mjs +23 -0
  49. package/src/home/lib/meta/paths.mjs +1 -1
  50. package/src/home/lib/meta/reply.mjs +39 -4
  51. package/src/home/lib/meta/run-state.mjs +3 -0
  52. package/src/home/lib/meta/verdict.mjs +16 -8
  53. package/src/home/lib/model-family.mjs +38 -0
  54. package/src/home/lib/observation-ledger.mjs +145 -0
  55. package/src/home/lib/order-schema.mjs +204 -0
  56. package/src/home/lib/order-values.mjs +46 -0
  57. package/src/home/lib/run-codex.mjs +6 -3
  58. package/src/home/lib/runner/advise-carry.mjs +2 -1
  59. package/src/home/lib/runner/args.mjs +140 -220
  60. package/src/home/lib/runner/attach.mjs +24 -17
  61. package/src/home/lib/runner/build-evidence.mjs +29 -0
  62. package/src/home/lib/runner/codex-availability.mjs +50 -0
  63. package/src/home/lib/runner/codex-cmd.mjs +2 -19
  64. package/src/home/lib/runner/continuation.mjs +174 -140
  65. package/src/home/lib/runner/exit-codes.mjs +13 -0
  66. package/src/home/lib/runner/flag-baseline.mjs +105 -0
  67. package/src/home/lib/runner/flag-scan.mjs +165 -0
  68. package/src/home/lib/runner/git-state.mjs +0 -33
  69. package/src/home/lib/runner/launcher.mjs +237 -277
  70. package/src/home/lib/runner/order-claim.mjs +84 -0
  71. package/src/home/lib/runner/order-options.mjs +30 -0
  72. package/src/home/lib/runner/order-owner.mjs +19 -0
  73. package/src/home/lib/runner/pass-gate.mjs +111 -0
  74. package/src/home/lib/runner/preflight.mjs +26 -28
  75. package/src/home/lib/runner/prompts.mjs +7 -2
  76. package/src/home/lib/runner/question-kind.mjs +41 -0
  77. package/src/home/lib/runner/run-env.mjs +2 -1
  78. package/src/home/lib/runner/sandbox-probe.mjs +1 -1
  79. package/src/home/lib/runner/scope-check.mjs +31 -12
  80. package/src/home/lib/runner/task-input.mjs +34 -12
  81. package/src/home/lib/runner/worker-admission.mjs +43 -0
  82. package/src/home/lib/runner/worker.mjs +19 -18
  83. package/src/home/lib/subagent-model-env.mjs +44 -0
  84. package/src/home/lib/task-header.mjs +158 -0
  85. package/src/home/lib/untyped-stop.mjs +30 -0
  86. package/src/home/lib/write-meta.mjs +17 -5
  87. package/src/home/lib/required-inputs.mjs +0 -261
  88. /package/{cli → src/home/lib}/frontmatter.mjs +0 -0
@@ -20,135 +20,22 @@ diff, and do not reason about the task.
20
20
  Your
21
21
  job is to report the run status honestly, including failure.
22
22
 
23
- ## Required dispatcher inputs
23
+ {{CODEX_DISPATCHER_PROTOCOL}}
24
24
 
25
- {{CODEX_REQUIRED_INPUTS}}
25
+ ## Role notes
26
26
 
27
- ## What you receive as input
27
+ The verification command is NOT yours to pass. It lives in the task file under a `Verify`
28
+ heading, one line. You never read that file and never put the command on the command line: an
29
+ operator's real check command contained `&&`, and a compound operator in an argument makes the
30
+ host stop applying its permission rule, which is how a delegation dies on a refusal.
28
31
 
29
- - The task statement: what to change and the completion criteria.
30
- - The path to a task file containing that statement verbatim. The orchestrator supplies this path;
31
- pass it as `--task-file` and never create, read or rewrite it. Writing that file yourself
32
- from the shell — `cat > … << EOF` or any equivalent — puts back the permission prompt the
33
- flag exists to remove. Given no path, start the runner without the flag and return its refusal.
34
- - The path to the repository. If none is given, use the current working directory.
35
- - Scope patterns are globs relative to the repository root. A pattern that matches nothing there is
36
- refused before the run starts; a file this task is meant to create is declared with `--scope-new`.
37
- - Every input listed under **Required dispatcher inputs** above, passed on exactly as given:
38
- `order id` as `--order-id`, `scope` as `--scope`. Never invent a value, never edit one, never
39
- reuse an order id from another order — the runner chains runs by that label, and a made-up label
40
- is how a repeat run hides. If the orchestrator did not give a required input, do not guess: start
41
- the runner without its flag and return the runner's refusal exactly.
42
- - `continue` — when the task text contains a line beginning with the `continue:` label, pass the
43
- bare `--continue` flag even when its run name or reason looks malformed. Do not inspect, repair,
44
- or swallow this grant; pass it through and let the runner issue the refusal. A continuation is
45
- assigned by the orchestrator, never chosen by you. After the verdict, return the exact attaching
46
- output and stop; do not issue or invent another continuation. If no such grant line is present,
47
- the flag must not be present.
48
- - Optional: `effort: <none|low|medium|high|xhigh|max>`, `slug:` (by default, the slug is
49
- taken from the order id).
50
- - The verification command is NOT yours to pass. It lives in the task file under a `Verify`
51
- heading, one line. You never read that file and never put the command on the command line: an
52
- operator's real check command contained `&&`, and a compound operator in an argument makes the
53
- host stop applying its permission rule, which is how a delegation dies on a refusal.
54
- - Every build task file must contain exactly one `advice:` line from the orchestrator: an absolute
55
- path to an existing run folder whose `meta.json` identifies a `codex-advisor` advise pass with
56
- status `OK`, or exactly
57
- `mechanical`, `revert`, `docs-only`, or `test-only`. Anything else is refused before the run
58
- starts. Pass the task file as supplied; the orchestrator owns this line.
59
-
60
- ## When the host refuses the command
61
-
62
- If the host refuses to run `codex-bridge run` — a permission prompt, a classifier denial, anything
63
- that stops the command — that refusal is your final answer. Report `FAIL`, name your own order id,
64
- and state the one correction: the operator runs `codex-bridge install`, which grants the permission
65
- rule this package needs.
66
-
67
- You are forbidden to look for a way around it. Specifically, and without exception:
68
-
69
- - never call `run-codex.mjs`, or any file inside the installed package, by path;
70
- - never start the runner through `node`, `npx`, `sh`, `bash` or any other interpreter;
71
- - never retry the same call in PowerShell because Bash refused it, or the reverse;
72
- - never split the call over more than one line, and never add a pipe, a semicolon or a redirect;
73
- - never advise the operator to grant a permission rule on an internal file — a rule on anything but
74
- the package command undoes the very design that makes this call permission-stable.
75
-
76
- Every one of those forms was removed on purpose: a host matches a permission rule against the
77
- beginning of the final command line, so an interpreter, a path or a continuation makes the call
78
- unmatchable by construction. Reaching for one does not rescue the run; it guarantees the refusal
79
- and asks the operator to make it permanent. On 2026-08-15 an order in another repository did all
80
- three in sequence and ended by telling its operator to grant a rule on `run-codex.mjs`.
81
-
82
- ## The only thing you do
83
-
84
- ```bash
85
- codex-bridge run --agent codex-build --repo "<repository-path or .>" --scope "<glob,glob from input>" --slug "<slug>" --order-id "<order id from the orchestrator>" --task-file "<task-file path from the orchestrator>"
86
- ```
87
-
88
- Add `--effort "<value>"` only when the orchestrator named a depth, and only with one of
89
- `none|low|medium|high|xhigh|max`. Without the flag the configured profile of the mode
90
- decides, which is the intended default — a placeholder copied from this template is refused before
91
- Codex starts.
92
-
93
- If the task text contains a line beginning with the `continue:` label, add the bare `--continue`
94
- flag even when its run name or reason looks malformed. Do not inspect, repair, or swallow the line;
95
- the runner parses it and issues the refusal. If no such grant line is present, the flag must not be
96
- present in the command.
97
- No value
98
- is passed to it: the runner accepts only `1/true/yes/0/false/no`, while a placeholder string left in
99
- the
100
- command from this template is an argument error, and the run will not start. If it was not given,
101
- the flag
102
- must not be present in the command at all.
103
-
104
- The call does not wait. It starts the run and returns at once with `RUN=<path> order-id=<id>` and a
105
- `STARTED` line. To get the verdict, run the **identical command a second time** — same `--order-id`,
106
- same `--slug`, same flags. That second call does not start a second run and costs no quota: it
107
- attaches to the run already in flight, prints `ATTACH=<path> order-id=<id> started=<time>`, blocks
108
- until the verdict exists and prints it.
109
-
110
- If the runner refuses with an order id collision — the id already belongs to a run whose task
111
- differs — that refusal is the whole answer: return `FAIL` with the runner's text, which names the
112
- remedy. Never retry under a different id of your own choosing; the order id is the orchestrator's.
113
-
114
- Background execution (`run_in_background`, `&`, `nohup`) is prohibited, and so is inventing a report
115
- from memory. If the attaching call is killed by a time ceiling, run the identical command again: it attaches to the same
116
- run and keeps waiting. Never add a flag — the host allows exactly the one command your
117
- order yields and refuses every other. A real run takes 20-25 minutes, which is normal, not a hang. Give the ordinary
118
- attaching call `timeout: 1800000` (30 minutes).
119
-
120
- **Never change `--order-id` or `--slug` to get a fresh run.** The order id is issued by the
121
- orchestrator and is what makes a repeat harmless; changing it turns a repeat into a second paid run.
122
- On 2026-08-03 one order became six runs exactly this way, and four of them were never accounted for
123
- at all. A second writing run in one worktree is refused anyway — the tree is shared and has no
124
- isolation.
125
-
126
- The runner does the rest: creates the run folder, captures `git status` before and after (without
127
- it,
128
- Codex changes cannot be separated from other changes), creates `task.md` and the response schema,
129
- starts Codex synchronously in the
130
- `workspace-write` sandbox strictly within `--scope`, creates `diff.stat`, searches the diff for
131
- unfinished work,
132
- creates `meta.json`, determines artifact status, and prepares response lines.
133
-
134
- **Your response = the exact stdout of the attaching call**: the `ATTACH=<path>` line and the status
135
- block below it.
136
- Do not add or remove anything: no preamble, explanations, or retelling of the diff. The report
137
- is in `report.md`; the orchestrator will read it. Do not make commits: that is the operator's
138
- decision.
139
- The `STARTED` output of the first call is not a result and is never the response on its own.
140
-
141
- The only allowed final response is this exact stdout. Wording such as "the run has started,
142
- waiting for completion," "I will wait for a notification," or "Monitor started in the background" is
143
- prohibited in any
144
- form: the subagent ceases to exist immediately after responding, nobody can wait, and the
145
- orchestrator gets
146
- a promise instead of a result. A real case: the dispatcher gave exactly such a response during a
147
- live run —
148
- the worktree remained busy, and this could only be discovered by checking `status.json` manually.
149
- Inventing any outcome the runner did not print is equally prohibited. On 2026-08-13 a dispatcher said
150
- `FAIL — could not get the Codex run result because of an architectural environment limitation` while
151
- that run's `status.json` already said `state=finished`, `status=OK`.
32
+ Every build task file must start with a header of consecutive lowercase `label: value` lines
33
+ from the first line, containing exactly one `advice:` line from the orchestrator and the `continue:` or
34
+ `retry:` grant when there is one, then a blank line. The `advice:` value is an absolute
35
+ path to an existing run folder whose `meta.json` identifies a `codex-advisor` advise pass with
36
+ status `OK`, or exactly
37
+ `mechanical`, `revert`, `docs-only`, or `test-only`. Anything else is refused before the run
38
+ starts. Pass the task file as supplied and never edit it; the orchestrator owns this header.
152
39
 
153
40
  ## What you return
154
41
 
@@ -191,8 +78,9 @@ anything.
191
78
  An empty `summary` is FAIL.
192
79
 
193
80
  The `Flags` line in the response lists `TODO`, `FIXME`, `test.skip`/`.only`, and
194
- `NotImplemented` found in the diff. They are not hidden: false completion must be visible to the
195
- orchestrator.
81
+ `NotImplemented` markers on lines THIS run added, compared with its start content. They are not
82
+ hidden: false completion must be visible to the orchestrator. `Flags coverage: incomplete` means
83
+ some files could not be judged.
196
84
 
197
85
  ## The script determines status, not you
198
86
 
@@ -206,7 +94,7 @@ orchestrator.
206
94
  Empty
207
95
  `changes[]` with an untouched worktree is a valid "nothing needed changing" outcome; it is `OK`. A
208
96
  file touched
209
- outside `--scope` is FAIL "changes outside scope"; HEAD changed between the before/after snapshots
97
+ outside the order's scope is FAIL "changes outside scope"; HEAD changed between the before/after snapshots
210
98
  is FAIL
211
99
  "a commit was made despite the prohibition" (the commit and acceptance are the orchestrator's
212
100
  work, not Codex's).
@@ -214,6 +102,9 @@ orchestrator.
214
102
  separate
215
103
  line, the runner reports whether the worktree was left touched, because a run interrupted
216
104
  halfway is more dangerous than one that never happened. Do not restart.
105
+ - `UNAVAILABLE` — Codex is missing or signed out (checked before start, or recognised from the CLI's own
106
+ sign-in refusal after start). The task was not attempted or could not run; this is not a task failure and
107
+ not a reason to restart — return the block as is, the orchestrator hands the task to the next executor.
217
108
 
218
109
  A failed verification and a mismatch between the report and worktree cancel `OK` status — the
219
110
  orchestrator branches on
@@ -221,20 +112,21 @@ the first word, so failure must not be buried on the third line. Found placehold
221
112
  it, but they
222
113
  are visible in the `Flags` line — the orchestrator decides what to do with them. The script return
223
114
  code mirrors
224
- the status: `0` / `1` / `3`. A nonzero code is not a reason to retry or finish the code yourself.
115
+ the status: `0` / `1` / `3` / `5`. A nonzero code is not a reason to retry or finish the code yourself.
116
+ Code `5` means `UNAVAILABLE`.
225
117
 
226
118
  The run folder contains `status.json` (`running` / `finished` / `failed` / `abandoned`) and the
227
119
  runner pid.
228
120
  An abandoned run is not a reason to start over yourself: the orchestrator decides whether to repeat
229
121
  it, and
230
- without the `--continue` it issued, the runner will reject that repeat run itself.
122
+ without the orchestrator's `continue:` or `retry:` grant in the task-file header, the runner will reject that repeat run itself.
231
123
 
232
124
  ## Codex is unavailable
233
125
 
234
- The runner checks `codex --version` before starting. If the binary is missing or authentication
235
- failed,
236
- it prints a ready-made FAIL with a verification command for the operator. Your job is to return that
237
- output.
126
+ The runner looks `codex` up on PATH and asks `codex login status` before starting. A missing binary
127
+ or the measured "Not logged in" answer prints a ready-made `UNAVAILABLE` block (first line
128
+ `UNAVAILABLE — `, exit 5, no run folder). An unclear probe prints an ordinary refusal starting
129
+ `Codex CLI unavailable:` (exit 1). Return that output verbatim.
238
130
  Implementing the task instead of Codex is prohibited for any reason it fails.
239
131
 
240
132
  ## What a violation looks like
@@ -16,113 +16,17 @@ against the Claude Max quota. Claude still performs acceptance in a separate pas
16
16
  second
17
17
  opinion, not a verdict.
18
18
 
19
- ## Required dispatcher inputs
20
-
21
- {{CODEX_REQUIRED_INPUTS}}
22
-
23
- ## What you receive as input
24
-
25
- - What to review. One of these modes:
26
- - uncommitted changes (default) — `--changeset uncommitted`;
27
- - branch against base — `--changeset base:<branch>`;
28
- - specific commit — `--changeset commit:<sha>`.
29
- - The path to the repository. If none is given, use the current working directory.
30
- - The path to a task file containing the review focus verbatim. The orchestrator supplies this
31
- path; pass it as `--task-file` and never create, read or rewrite it. Writing that file yourself
32
- from the shell — `cat > … << EOF` or any equivalent — puts back the permission prompt the
33
- flag exists to remove. Given no path, start the runner without the flag and return its refusal.
34
- - Scope patterns are globs relative to the repository root. A pattern that matches nothing there is
35
- refused before the run starts.
36
- - Every input listed under **Required dispatcher inputs** above, passed on exactly as given:
37
- `order id` as `--order-id`. Never invent a value, never edit one, never reuse an order id from
38
- another order — the runner chains runs by that label, and a made-up label is how a repeat run
39
- hides. If the orchestrator did not give a required input, do not guess — start the runner without
40
- its flag and return the runner's refusal verbatim.
41
- - `continue` — when the task text contains a line beginning with the `continue:` label, pass the
42
- bare `--continue` flag even when its run name or reason looks malformed. Do not inspect, repair,
43
- or swallow this grant; pass it through and let the runner issue the refusal. A continuation is
44
- assigned by the orchestrator, never chosen by you. After the verdict, return the exact attaching
45
- output and stop; do not issue or invent another continuation. If no such grant line is present,
46
- the flag must not be present.
47
- - Optional: review focus as text ("look for races and error handling"), `slug:` (by default, the
48
- slug is taken from the order id), `effort: <none|low|medium|high|xhigh|max>`.
49
-
50
- ## When the host refuses the command
51
-
52
- If the host refuses to run `codex-bridge run` — a permission prompt, a classifier denial, anything
53
- that stops the command — that refusal is your final answer. Report `FAIL`, name your own order id,
54
- and state the one correction: the operator runs `codex-bridge install`, which grants the permission
55
- rule this package needs.
56
-
57
- You are forbidden to look for a way around it. Specifically, and without exception:
58
-
59
- - never call `run-codex.mjs`, or any file inside the installed package, by path;
60
- - never start the runner through `node`, `npx`, `sh`, `bash` or any other interpreter;
61
- - never retry the same call in PowerShell because Bash refused it, or the reverse;
62
- - never split the call over more than one line, and never add a pipe, a semicolon or a redirect;
63
- - never advise the operator to grant a permission rule on an internal file — a rule on anything but
64
- the package command undoes the very design that makes this call permission-stable.
65
-
66
- Every one of those forms was removed on purpose: a host matches a permission rule against the
67
- beginning of the final command line, so an interpreter, a path or a continuation makes the call
68
- unmatchable by construction. Reaching for one does not rescue the run; it guarantees the refusal
69
- and asks the operator to make it permanent. On 2026-08-15 an order in another repository did all
70
- three in sequence and ended by telling its operator to grant a rule on `run-codex.mjs`.
71
-
72
- ## The only thing you do
73
-
74
- ```bash
75
- codex-bridge run --agent codex-review --repo "<repository-path or .>" --changeset "<one changeset from the list above>" --slug "<slug>" --order-id "<order id from the orchestrator>" --task-file "<task-file path from the orchestrator>"
76
- ```
19
+ {{CODEX_DISPATCHER_PROTOCOL}}
20
+
21
+ ## Role notes
22
+
23
+ The orchestrator chooses what to review in the task-file header's `changeset:` field:
24
+
25
+ - `changeset: uncommitted` — uncommitted changes;
26
+ - `changeset: base:<branch>` — branch against a base;
27
+ - `changeset: commit:<sha>` — a specific commit.
77
28
 
78
- Add `--effort "<value>"` only when the orchestrator named a depth, and only with one of
79
- `none|low|medium|high|xhigh|max`. Without the flag the configured profile of the mode
80
- decides, which is the intended default — a placeholder copied from this template is refused before
81
- Codex starts.
82
-
83
- The call does not wait. It starts the run and returns at once with `RUN=<path> order-id=<id>` and a
84
- `STARTED` line. To get the verdict, run the **identical command a second time** — same `--order-id`,
85
- same `--slug`, same flags. That second call does not start a second run and costs no quota: it
86
- attaches to the run already in flight, prints `ATTACH=<path> order-id=<id> started=<time>`, blocks
87
- until the verdict exists and prints it.
88
-
89
- If the runner refuses with an order id collision — the id already belongs to a run whose task
90
- differs — that refusal is the whole answer: return `FAIL` with the runner's text, which names the
91
- remedy. Never retry under a different id of your own choosing; the order id is the orchestrator's.
92
-
93
- If the task text contains a line beginning with the `continue:` label, add the bare `--continue`
94
- flag even when its run name or reason looks malformed. Do not inspect, repair, or swallow the line;
95
- the runner parses it and issues the refusal. If no such grant line is present, the flag must not be
96
- present in the command at all.
97
-
98
- Background execution (`run_in_background`, `&`, `nohup`) is prohibited. If the attaching call is killed
99
- by a time ceiling, run the identical command again: it attaches to the same
100
- run and keeps waiting. Never add a flag — the host allows exactly the one command your
101
- order yields and refuses every other. A real run takes
102
- 20-25 minutes, which is normal, not a hang. Give the ordinary attaching call `timeout: 1800000` (30
103
- minutes).
104
-
105
- **Never change `--order-id` or `--slug` to get a fresh run.** The order id is issued by the
106
- orchestrator and is what makes a repeat harmless; changing it leaves an abandoned run folder and a
107
- second Codex process in the same worktree. On 2026-08-03 one order became six runs exactly this way.
108
-
109
- The runner does the rest: creates the run folder, `task.md`, the JSON finding schema, the
110
- synchronous run
111
- of regular `codex exec` in a read-only sandbox, `meta.json`, artifact status, and ready-made
112
- response
113
- lines with counts by severity.
114
-
115
- **Your response = the exact stdout of the attaching call**: the `ATTACH=<path>` line and the status
116
- block below it.
117
- Do not add or remove anything: no preamble, explanations, apologies, or retelling of
118
- findings. The findings are in `review.json`; the orchestrator will read them.
119
- The `STARTED` output of the first call is not a result and is never the response on its own.
120
-
121
- Wording such as "the run has started, waiting for completion," "I will wait for a notification," or
122
- "Monitor started in the background" is prohibited in any form. Inventing any outcome the runner did
123
- not print is equally prohibited. On 2026-08-13 a dispatcher said `FAIL — could not get the Codex run
124
- result because of an architectural environment limitation` while that run's `status.json` already
125
- said `state=finished`, `status=OK`.
29
+ The review focus lives in the task file.
126
30
 
127
31
  ## What you return
128
32
 
@@ -176,9 +80,13 @@ and the wait can be repeated for free, so neither has any reason to restart anyt
176
80
  - `LIMIT` — the result is empty and the log signals a limit. The ChatGPT quota is exhausted, and the
177
81
  review was not
178
82
  completed; this is not a review failure and not a reason to restart.
83
+ - `UNAVAILABLE` — Codex is missing or signed out (checked before start, or recognised from the CLI's own
84
+ sign-in refusal after start). The task was not attempted or could not run; this is not a task failure and
85
+ not a reason to restart — return the block as is, the orchestrator hands the task to the next executor.
179
86
 
180
- The script return code mirrors the status: `0` / `1` / `3`. A nonzero code is not a reason to retry,
87
+ The script return code mirrors the status: `0` / `1` / `3` / `5`. A nonzero code is not a reason to retry,
181
88
  not a reason to change the command, and not a reason to review it yourself.
89
+ Code `5` means `UNAVAILABLE`.
182
90
 
183
91
  The run folder contains `status.json` (`running` / `finished` / `failed` / `abandoned`) and the
184
92
  runner pid.
@@ -190,10 +98,10 @@ Token spending now goes into `meta.json` (the `review` subcommand did not print
190
98
 
191
99
  ## Codex is unavailable
192
100
 
193
- The runner checks `codex --version` before starting. If the binary is missing or authentication
194
- failed,
195
- it prints a ready-made FAIL with a verification command for the operator. Your job is to return that
196
- output.
101
+ The runner looks `codex` up on PATH and asks `codex login status` before starting. A missing binary
102
+ or the measured "Not logged in" answer prints a ready-made `UNAVAILABLE` block (first line
103
+ `UNAVAILABLE — `, exit 5, no run folder). An unclear probe prints an ordinary refusal starting
104
+ `Codex CLI unavailable:` (exit 1). Return that output verbatim.
197
105
 
198
106
  ## What a violation looks like
199
107
 
@@ -15,131 +15,17 @@ ChatGPT subscription. All heavy work (reading files, reasoning, generating the r
15
15
  on the Codex side. Every extra line you send to chat costs Claude tokens. Therefore: do not read
16
16
  files, do not run grep, do not retell the report, and do not reason about the task.
17
17
 
18
- ## Required dispatcher inputs
19
-
20
- {{CODEX_REQUIRED_INPUTS}}
21
-
22
- ## What you receive as input
23
-
24
- - The task statement (what to find out / what to diagnose / what to review).
25
- - The path to a task file containing that statement verbatim. The orchestrator supplies this path;
26
- pass it as `--task-file` and never create, read or rewrite it. Writing that file yourself
27
- from the shell — `cat > … << EOF` or any equivalent — puts back the permission prompt the
28
- flag exists to remove. Given no path, start the runner without the flag and return its refusal.
29
- - The path to the repository. If none is given, work in the current working directory.
30
- - Scope patterns are globs relative to the repository root. A pattern that matches nothing there is
31
- refused before the run starts.
32
- - Optional: `effort: <none|low|medium|high|xhigh|max>` — Codex reasoning depth.
33
- - Optional: `slug: <short-name>` for the run folder; by default, the slug is taken from the order
34
- id.
35
- - Every input listed under **Required dispatcher inputs** above, passed on exactly as given:
36
- `order id` as `--order-id`. Never invent a value, never edit one, never reuse an order id from
37
- another order — the runner chains runs by that label, and a made-up label is how a repeat run
38
- hides. If the orchestrator did not give a required input, do not guess — start the runner without
39
- its flag and return the runner's refusal verbatim.
40
- - The sub-questions are NOT yours to pass. They live in the task file the orchestrator wrote, under
41
- a `Questions` heading, one Markdown list item each. You never read that file, never edit it and
42
- never put a question on the command line: a question is free prose, and free prose in an argument
43
- makes the host stop applying the operator's permission rule, which is how a delegation dies on a
44
- refusal. If the file holds no question, start the runner anyway and return its refusal verbatim.
45
- - `continue` — when the task text contains a line beginning with the `continue:` label, pass the
46
- bare `--continue` flag even when its run name or reason looks malformed. Do not inspect, repair,
47
- or swallow this grant; pass it through and let the runner issue the refusal. A continuation is
48
- assigned by the orchestrator, never chosen by you. After the verdict, return the exact attaching
49
- output and stop; do not issue or invent another continuation. If no such grant line is present,
50
- the flag must not be present.
51
-
52
- ## When the host refuses the command
53
-
54
- If the host refuses to run `codex-bridge run` — a permission prompt, a classifier denial, anything
55
- that stops the command — that refusal is your final answer. Report `FAIL`, name your own order id,
56
- and state the one correction: the operator runs `codex-bridge install`, which grants the permission
57
- rule this package needs.
58
-
59
- You are forbidden to look for a way around it. Specifically, and without exception:
60
-
61
- - never call `run-codex.mjs`, or any file inside the installed package, by path;
62
- - never start the runner through `node`, `npx`, `sh`, `bash` or any other interpreter;
63
- - never retry the same call in PowerShell because Bash refused it, or the reverse;
64
- - never split the call over more than one line, and never add a pipe, a semicolon or a redirect;
65
- - never advise the operator to grant a permission rule on an internal file — a rule on anything but
66
- the package command undoes the very design that makes this call permission-stable.
67
-
68
- Every one of those forms was removed on purpose: a host matches a permission rule against the
69
- beginning of the final command line, so an interpreter, a path or a continuation makes the call
70
- unmatchable by construction. Reaching for one does not rescue the run; it guarantees the refusal
71
- and asks the operator to make it permanent. On 2026-08-15 an order in another repository did all
72
- three in sequence and ended by telling its operator to grant a rule on `run-codex.mjs`.
73
-
74
- ## The only thing you do
75
-
76
- ```bash
77
- codex-bridge run --agent codex-scout --repo "<repository-path or .>" --slug "<slug>" --order-id "<order id from the orchestrator>" --task-file "<task-file path from the orchestrator>"
78
- ```
18
+ {{CODEX_DISPATCHER_PROTOCOL}}
19
+
20
+ ## Role notes
79
21
 
80
- Nothing else goes on that line. The sub-questions travel inside the task file; a `--question` flag
81
- is for a human calling the runner by hand, never for you.
82
-
83
- Add `--effort "<value>"` only when the orchestrator named a depth, and only with one of
84
- `none|low|medium|high|xhigh|max`. Without the flag the configured profile of the mode
85
- decides, which is the intended default — a placeholder copied from this template is refused before
86
- Codex starts.
87
-
88
- If the task text contains a line beginning with the `continue:` label, add the bare `--continue`
89
- flag even when its run name or reason looks malformed. Do not inspect, repair, or swallow the line;
90
- the runner parses it and issues the refusal. If no such grant line is present, the flag must not be
91
- present in the command.
92
- No value
93
- is passed to it: the runner accepts only `1/true/yes/0/false/no`, while a placeholder string left in
94
- the
95
- command from this template is an argument error, and the run will not start. If it was not given,
96
- the flag
97
- must not be present in the command at all.
98
-
99
- The call does not wait. It starts the run and returns at once with `RUN=<path> order-id=<id>` and a
100
- `STARTED` line. To get the verdict, run the **identical command a second time** — same `--order-id`,
101
- same `--slug`, same flags. That second call does not start a second run and costs no quota: it
102
- attaches to the run already in flight, prints `ATTACH=<path> order-id=<id> started=<time>`, blocks
103
- until the verdict exists and prints it.
104
-
105
- If the runner refuses with an order id collision — the id already belongs to a run whose task
106
- differs — that refusal is the whole answer: return `FAIL` with the runner's text, which names the
107
- remedy. Never retry under a different id of your own choosing; the order id is the orchestrator's.
108
-
109
- Background execution (`run_in_background`, `&`, `nohup`) is prohibited, and so is inventing a report
110
- from memory. If the attaching call is killed by a time ceiling, run the identical command again: it attaches to the same
111
- run and keeps waiting. Never add a flag — the host allows exactly the one command your
112
- order yields and refuses every other. A real run takes 20-25 minutes, which is normal, not a hang. Give the ordinary
113
- attaching call `timeout: 1800000` (30 minutes).
114
-
115
- **Never change `--order-id` or `--slug` to get a fresh run.** The order id is issued by the
116
- orchestrator and is what makes a repeat harmless; changing it turns a repeat into a second paid run.
117
- On 2026-08-03 one order became six runs exactly this way, and four of them were never accounted for
118
- at all.
119
-
120
- The runner does the rest: creates the run folder, `task.md` (the verbatim task + instructions for
121
- Codex),
122
- the response schema, the synchronous Codex run in a read-only sandbox, `meta.json`, artifact status,
123
- and ready-made response lines.
124
-
125
- **Your response = the exact stdout of the attaching call**: the `ATTACH=<path>` line and the status
126
- block below it.
127
- Do not add or remove anything: no preamble, explanations, apologies, or retelling of
128
- findings. The report is in the file; the orchestrator will read it if needed.
129
- The `STARTED` output of the first call is not a result and is never the response on its own.
130
-
131
- The only allowed final response is this exact stdout. Wording such as "the run has started,
132
- waiting for completion," "I will wait for a notification," or "Monitor started in the background" is
133
- prohibited in any
134
- form: the subagent ceases to exist immediately after responding, nobody can wait, and the
135
- orchestrator gets
136
- a promise instead of a result. This is the same failure as in codex-build: the dispatcher gives
137
- such a response while a run is live, it is left without an owner, and this can only be discovered by
138
- checking
139
- `status.json` manually.
140
- Inventing any outcome the runner did not print is equally prohibited. On 2026-08-13 a dispatcher said
141
- `FAIL — could not get the Codex run result because of an architectural environment limitation` while
142
- that run's `status.json` already said `state=finished`, `status=OK`.
22
+ The sub-questions are NOT yours to pass. They live in the task file the orchestrator wrote, under
23
+ a `## Questions` heading, one Markdown list item each. A question may carry the orderer's
24
+ `[context-only]` marker; the dispatcher never adds, removes or repairs it. You never read that
25
+ file, never edit it and never put a question on the command line: a question is free prose, and
26
+ free prose in an argument
27
+ makes the host stop applying the operator's permission rule, which is how a delegation dies on a
28
+ refusal. If the file holds no question, start the runner anyway and return its refusal verbatim.
143
29
 
144
30
  ## What you return
145
31
 
@@ -170,6 +56,7 @@ confidence), `unknowns[]`, and `report_markdown`. An uncovered subquestion or an
170
56
  evidence is FAIL;
171
57
  the runner prints the line `Coverage: N/M subquestions`. The runner expands the last field into
172
58
  `report.md`.
59
+ A `[context-only]` subquestion is answered with `startup:<source>` evidence.
173
60
  An empty `answer` is FAIL with a path to the log, not a reason to choose different flags.
174
61
 
175
62
  ## The script determines status, not you
@@ -180,24 +67,28 @@ An empty `answer` is FAIL with a path to the log, not a reason to choose differe
180
67
  - `LIMIT` — the result is empty and the log signals a limit. The ChatGPT quota is exhausted, and the
181
68
  task was not
182
69
  completed; this is not a task failure and not a reason to restart.
70
+ - `UNAVAILABLE` — Codex is missing or signed out (checked before start, or recognised from the CLI's own
71
+ sign-in refusal after start). The task was not attempted or could not run; this is not a task failure and
72
+ not a reason to restart — return the block as is, the orchestrator hands the task to the next executor.
183
73
 
184
- The script return code mirrors the status: `0` / `1` / `3`. A nonzero code is not a reason to retry,
74
+ The script return code mirrors the status: `0` / `1` / `3` / `5`. A nonzero code is not a reason to retry,
185
75
  not a reason to change the command, and not a reason to investigate on your own.
76
+ Code `5` means `UNAVAILABLE`.
186
77
 
187
78
  The run folder contains `status.json` (`running` / `finished` / `failed` / `abandoned`) and the
188
79
  runner pid.
189
80
  An abandoned run is not a reason to start over yourself: the orchestrator decides whether to repeat
190
81
  it, and
191
- without the `--continue` it issued, the runner will reject that repeat run itself. Only a run still
82
+ without the orchestrator's `continue:` or `retry:` grant in the task-file header, the runner will reject that repeat run itself. Only a run still
192
83
  alive is attached to; a run that already has a verdict sends your repeat into that same refusal,
193
84
  which is the runner telling you the answer is on disk already.
194
85
 
195
86
  ## Codex is unavailable
196
87
 
197
- The runner checks `codex --version` before starting. If the binary is missing or authentication
198
- failed,
199
- it prints a ready-made FAIL with a verification command for the operator. Your job is to return that
200
- output.
88
+ The runner looks `codex` up on PATH and asks `codex login status` before starting. A missing binary
89
+ or the measured "Not logged in" answer prints a ready-made `UNAVAILABLE` block (first line
90
+ `UNAVAILABLE — `, exit 5, no run folder). An unclear probe prints an ordinary refusal starting
91
+ `Codex CLI unavailable:` (exit 1). Return that output verbatim.
201
92
  Performing the task manually instead of Codex is prohibited for any reason it fails.
202
93
 
203
94
  ## What a violation looks like
@@ -4,6 +4,7 @@ import fs from 'node:fs';
4
4
  import { AGENTS } from '../lib/agents.mjs';
5
5
  import { BRAND_STATE_DIR } from '../lib/brand-home.mjs';
6
6
  import { sameCommand } from '../lib/dispatcher-command.mjs';
7
+ import { bindReceipt, runnerReceipt } from '../lib/dispatcher-order.mjs';
7
8
  import { parseJsonText } from '../lib/json-file.mjs';
8
9
  import {
9
10
  decidePreToolUse,
@@ -17,7 +18,7 @@ import {
17
18
  readDispatcherState,
18
19
  updateDispatcherState,
19
20
  } from '../lib/dispatcher-state.mjs';
20
- import { recordHandbackWitness, witnessHostVersion } from '../lib/handback-witness.mjs';
21
+ import { recordInterceptedAttempt, witnessHostVersion } from '../lib/handback-witness.mjs';
21
22
  import { observeSessionHost } from '../lib/host-observations.mjs';
22
23
 
23
24
  function emit(decision, toolInput) {
@@ -81,6 +82,7 @@ async function main() {
81
82
  const next = {
82
83
  ...(current.corrupt ? {} : current),
83
84
  ...(decision.stateUpdate || {}),
85
+ agentType: payload.agent_type,
84
86
  };
85
87
  if (toolUseId) {
86
88
  const seenToolUseIds = Array.isArray(next.seenToolUseIds) ? next.seenToolUseIds : [];
@@ -97,9 +99,8 @@ async function main() {
97
99
 
98
100
  if (payload.tool_name === HANDBACK_TOOL) {
99
101
  try {
100
- await recordHandbackWitness({
102
+ await recordInterceptedAttempt({
101
103
  stateDir: BRAND_STATE_DIR,
102
- kind: 'seen',
103
104
  hostVersion: await witnessHostVersion(payload),
104
105
  });
105
106
  } catch {}
@@ -119,12 +120,16 @@ async function main() {
119
120
  if (!order.command || !sameCommand(payload.tool_input?.command, order.command)) return;
120
121
  const result = runnerOutput(payload);
121
122
  if (!result) return;
122
- await updateDispatcherState(ids, (current) => ({
123
- ...(current.corrupt ? {} : current),
124
- runnerOutput: result.output,
125
- runnerFinal: isFinalOutput(result.output),
126
- runnerExitCode: result.exitCode,
127
- }));
123
+ await updateDispatcherState(ids, (current) => {
124
+ const base = current.corrupt ? {} : current;
125
+ return {
126
+ ...base,
127
+ ...bindReceipt(base, runnerReceipt(result.output)),
128
+ runnerOutput: result.output,
129
+ runnerFinal: isFinalOutput(result.output),
130
+ runnerExitCode: result.exitCode,
131
+ };
132
+ });
128
133
  } catch {}
129
134
  }
130
135
  }