Skip to content

Commit b7035a1

Browse files
committed
docs: align recovery and hook discovery contracts
1 parent a88d322 commit b7035a1

3 files changed

Lines changed: 36 additions & 29 deletions

File tree

docs/adr/0010-use-thread-bound-direct-companion.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ an installed-plugin E2E demonstrates trusted per-call identity metadata.
4444

4545
## Consequences
4646

47-
The plugin manifest must explicitly declare its native hooks. Integration tests
48-
must install the marketplace snapshot, discover the Skill through real Codex
49-
app-server, execute the installed hook, and call the installed companion through
50-
ordinary stdio. Tests cover absent and mismatched thread identity, two sessions
51-
in one workspace, pending-choice replay, shell metacharacters in tasks, and
52-
production-owned background launch. An opt-in authenticated `codex exec` E2E is
53-
the final model-driven proof; environments without authentication, credits, or a
47+
Codex auto-discovers native hooks at the default `hooks/hooks.json` path, so the
48+
optional plugin-manifest hook override is omitted. Integration tests must install
49+
the marketplace snapshot, prove discovery through real Codex app-server, execute
50+
the installed hook, and call the installed companion through ordinary stdio.
51+
Tests cover absent and mismatched thread identity, two sessions in one workspace,
52+
pending-choice replay, shell metacharacters in tasks, and production-owned
53+
background launch. An opt-in authenticated `codex exec` E2E is the final
54+
model-driven proof; environments without authentication, credits, or a
5455
configured model report unqualified rather than passing.

docs/superpowers/plans/2026-08-06-runtime-correctness-remediation.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
- `scripts/lib/background-worker.mjs`: spawn the private worker with bounded protected stdio and verify/reap startup.
1818
- `scripts/zcode-companion.mjs`: constant `invoke`/`invoke-choice` entrypoints, ordinary output, reconciliation entry, and orchestration.
1919
- `hooks/user-prompt-hook.mjs`: record original prompt and authorization facts without emitting a secret.
20-
- `.codex-plugin/plugin.json` and `skills/*/SKILL.md`: explicitly load hooks and use only constant direct commands.
20+
- `hooks/hooks.json`, `.codex-plugin/plugin.json`, and `skills/*/SKILL.md`: use Codex's default hook discovery without a redundant manifest override and use only constant direct commands.
2121
- `scripts/lib/prompts.mjs`: separate trusted Rescue objective from untrusted Git evidence.
2222
- `scripts/lib/recovery.mjs`: reconcile owned nonterminal jobs from persisted ZCode turn boundaries.
2323
- `scripts/lib/state.mjs` and `scripts/lib/job-control.mjs`: persist recovery fields and command-specific default selection.
@@ -55,7 +55,7 @@ Run:
5555
node --test tests/identity.test.mjs tests/integration/skills.test.mjs tests/integration/two-session-hooks.test.mjs tests/plugin-contracts.test.mjs tests/skills-contracts.test.mjs
5656
```
5757

58-
Expected: failures show there is no active-turn lookup, public invocation still requires FD3/FD4, hooks are undeclared, and pending-choice storage is absent.
58+
Expected: failures show there is no active-turn lookup, public invocation still requires FD3/FD4, default hooks are not discovered, and pending-choice storage is absent.
5959

6060
- [ ] **Step 3: Implement the exact active-turn and prompt parser boundary**
6161

@@ -77,7 +77,7 @@ Make `invoke <command>` and `invoke-choice <command> <enum>` read `CODEX_THREAD_
7777

7878
- [ ] **Step 5: Update installed plugin contracts**
7979

80-
Declare `"hooks": "./hooks/hooks.json"`. Replace each public Skill's FD instructions with a constant command such as:
80+
Place registrations at the default `hooks/hooks.json` path and omit the optional manifest hook-path override. Replace each public Skill's FD instructions with a constant command such as:
8181

8282
```text
8383
node "<plugin-root>/scripts/zcode-companion.mjs" invoke rescue
@@ -154,7 +154,7 @@ Assert that accepted sends persist `inputId`, `startRevision`, and `beforeMessag
154154

155155
- [ ] **Step 2: Write the failing real-worker-crash test**
156156

157-
Start a production background worker against the persistent fake ZCode peer, wait until the job is `running` with a persisted boundary, kill the worker process, change the peer to completed/stopped/missing variants, then invoke status/result/start in a new companion process. Assert completed becomes succeeded with only post-boundary output, stopped becomes cancelled, and missing/ambiguous becomes terminal failed; no case remains running.
157+
Start a production background worker against the persistent fake ZCode peer, wait until the job is `running` with a persisted boundary, kill the worker process, change the peer to completed/stopped/missing/ambiguous variants, then invoke status/result/start in a new companion process. Assert completed becomes succeeded with only post-boundary output, stopped becomes cancelled, and missing becomes failed. Ambiguous or known-active work must retain its guard unless `session/stop` is acknowledged; a failed stop remains retryable.
158158

159159
- [ ] **Step 3: Verify RED**
160160

@@ -166,11 +166,11 @@ Expected: recovery fields are rejected or absent, selection chooses the latest a
166166

167167
- [ ] **Step 4: Implement recovery under the cancellation lock**
168168

169-
`reconcileOwnedJobs({ store, dataRoot, workspace, ownerSessionId, createClient })` lists only owned nonterminal records. For each record, hold `withJobCancellationLock`, restore broker ownership, use `session/list` to establish existence and `session/read` for state/messages, compare the persisted input/revision/before-message boundary, and transition exactly once. Coordinate `cancelling` with the same lock. On unsafe ambiguity, persist a bounded recovery error and terminal `failed` status.
169+
`reconcileOwnedJobs({ store, dataRoot, workspace, ownerSessionId, createClient })` lists only owned nonterminal records. For each record, hold `withJobCancellationLock`, prove the exact worker lease is orphaned, restore broker ownership, use `session/list` to establish existence and `session/read` for state/messages, compare the persisted input/revision/before-message boundary, and transition exactly once. Coordinate `cancelling` with the same lock. On unsafe ambiguity, attempt bounded `session/stop`; without acknowledgement, persist a bounded retry error and retain the nonterminal guard.
170170

171171
- [ ] **Step 5: Call reconciliation at every required entry**
172172

173-
Run it before starting new work and before status/result/cancel selection. Do not reconcile sibling-owned jobs. Ensure connection/release/close happens in `finally` and a recovery failure for one job cannot skip terminalizing that job or leak the broker client.
173+
Run it before starting new work and before status/result/cancel selection. Do not reconcile sibling-owned jobs. Ensure connection/release/close happens in `finally`; a recovery failure for one job must not skip siblings, release an unsafe guard, or leak the broker client.
174174

175175
- [ ] **Step 6: Verify GREEN and commit**
176176

@@ -248,7 +248,7 @@ git commit -m "feat: persist workspace model configuration"
248248

249249
- [ ] **Step 1: Write failing release-contract tests**
250250

251-
Assert package/check scripts include the installed bridge E2E contract, no Skill mentions FD3/FD4/caller secrets, hooks are declared, background launch is production-owned, real ZCode release E2E requires non-empty `ZCODE_REAL_E2E_MODEL`, and the complete companion invocation uses that model.
251+
Assert package/check scripts include the installed bridge E2E contract, no Skill mentions FD3/FD4/caller secrets, default `hooks/hooks.json` is valid and auto-discovered without a manifest override, background launch is production-owned, real ZCode release E2E requires non-empty `ZCODE_REAL_E2E_MODEL`, and the complete companion invocation uses that model.
252252

253253
- [ ] **Step 2: Add the opt-in authenticated Codex Skill E2E**
254254

@@ -280,4 +280,3 @@ Expected: all deterministic tests pass, only explicitly unqualified authenticate
280280
git add package.json README.md README.zh-CN.md tests
281281
git commit -m "test: qualify the installed Codex ZCode bridge"
282282
```
283-

docs/superpowers/specs/2026-08-03-zcode-plugin-codex-design.md

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Companion command interface
5454

5555
### Plugin package
5656

57-
The distributable contains `.codex-plugin/plugin.json`, eight skill directories, Codex hooks, prompts, schemas, a Node.js companion runtime, and local marketplace metadata. Skills are intentionally thin: they resolve the plugin root, validate user intent where interaction is necessary, invoke the companion, and faithfully present its output.
57+
The distributable contains `.codex-plugin/plugin.json`, eight skill directories, Codex hooks, prompts, schemas, a Node.js companion runtime, and local marketplace metadata. Codex auto-discovers the default `hooks/hooks.json`; the optional manifest hook-path override is omitted. Skills are intentionally thin: they resolve the plugin root, validate user intent where interaction is necessary, invoke the companion, and faithfully present its output.
5858

5959
### Companion runtime
6060

@@ -299,24 +299,31 @@ TrackedJob
299299

300300
State changes are atomic and validated. Terminal jobs never return to a running
301301
state. `cancelling` is nonterminal; a failed stop may transition it back to
302-
`running` while recording `lastCancelError`. Foreground work also receives a job
303-
record so interruption does not erase its outcome. Multiple read-only reviews may
304-
coexist in one workspace; only one writable rescue may run there by default to
305-
prevent concurrent edits.
302+
`running` while recording `lastCancelError`. Every foreground or background
303+
Review, Adversarial Review, Rescue, and Transfer worker acquires and durably
304+
claims an exact worker lease before discovery, history reads, Git inspection, or
305+
ZCode session work. Foreground work therefore also receives a job record so
306+
interruption does not erase its outcome. Multiple read-only reviews may coexist
307+
in one workspace; only one writable rescue may run there by default to prevent
308+
concurrent edits.
306309

307310
For every accepted ZCode turn, the job persists the minimum recovery boundary:
308311
the `inputId`, accepted state revision, and the set of assistant message IDs
309312
visible before send. On the next start, status, result, or cancel invocation,
310313
the companion locks each owned nonterminal job, reconnects the broker, restores
311314
ownership, and reconciles through `session/read` and, when needed,
312-
`session/list`. A remotely completed turn extracts only assistant output beyond
313-
the persisted boundary, writes the result artifact, and transitions to
314-
`succeeded`. A stopped/cancelled turn becomes `cancelled`; a missing session,
315-
ambiguous boundary, incompatible state, or other condition that cannot be
316-
safely resumed becomes an explicit terminal `failed` recovery outcome. A
317-
persisted `cancelling` job coordinates with the cancellation lock and either
318-
finishes cancellation or records a terminal recovery failure. No crash may
319-
leave an owned job permanently `running`.
315+
`session/list`. A remotely completed turn with a complete boundary extracts only
316+
assistant output beyond that boundary, writes the result artifact, and becomes
317+
`succeeded` even if local status was `cancelling`. A remotely paused/stopped turn
318+
becomes `cancelled`; a proven missing or terminal-error session becomes
319+
`failed`. Known active or protocol-ambiguous sessions receive a best-effort
320+
`session/stop`; only an acknowledged stop permits terminalization. If stop is
321+
not acknowledged, recovery retains the nonterminal job and writable guard with
322+
a bounded error so a later cancellation can retry. A live exact lease is never
323+
reconciled away; an orphan claimed queued job fails safely, while a legacy
324+
lease-less queued record receives a conservative bounded stale grace period.
325+
Orphan Transfer jobs stop a known imported session before failing; without a
326+
known session they may fail safely because no mutating turn was sent.
320327

321328
## Model and Thought-Level Selection
322329

@@ -411,7 +418,7 @@ The CLI discovery order is:
411418

412419
The runtime requires ZCode CLI 0.16.1 or newer. Discovery results may be cached with their version and observation time, but every launch verifies that the resolved target still exists. A JavaScript entrypoint is launched through the current Node executable; a native executable is launched directly.
413420

414-
The broker starts lazily for the first active session and is reused while healthy. Codex lifecycle hooks release session ownership and stop an idle broker. Cleanup must not stop sessions or jobs owned by sibling Codex sessions. After an abnormal exit, the next start, status, result, or cancel invocation reconciles owned nonterminal jobs, including jobs left in `cancelling`, from persisted turn boundaries and ZCode session state while holding the job's recovery/cancellation lock.
421+
The broker starts lazily for the first active session and is reused while healthy. Codex lifecycle hooks release session ownership and stop an idle broker. Cleanup must not stop sessions or jobs owned by sibling Codex sessions. After an abnormal exit, the next start, status, result, or cancel invocation reconciles owned nonterminal jobs, including jobs left in `cancelling`, from persisted worker leases, turn boundaries, and ZCode session state while holding the job's recovery/cancellation lock. Unsafe ambiguity retains the job guard until remote terminal/missing proof or acknowledged stop.
415422

416423
## Local Storage
417424

0 commit comments

Comments
 (0)