diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index f3a97a8..027335e 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "cc", - "version": "1.0.9", + "version": "1.1.0", "description": "Claude Code Plugin for Codex. Delegate code reviews, investigations, and tracked tasks to Claude Code from inside Codex.", "author": { "name": "Sendbird, Inc.", diff --git a/CHANGELOG.md b/CHANGELOG.md index 62619fd..6117a70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## v1.1.0 + +- Restructure the internal Claude runtime and prompt-shaping guidance from pseudo-hidden `SKILL.md` files into plain internal reference documents, while keeping the public `review`, `adversarial-review`, and `rescue` skills self-sufficient on their critical invocation rules. +- Add a shared internal runtime reference for review/adversarial-review and strengthen the contract tests so installed-root routing, exact `send_input` notification shape, and empty routing-placeholder guards stay locked in across future cleanup passes. +- Tighten the built-in background forwarding contract so the child must run the companion command as one blocking foreground shell-tool call instead of spawning a background terminal/session of its own, and add E2E coverage for that regression. +- Remove workstation-specific absolute internal-doc link targets from the public skill docs so source trees, installed copies, and marketplace snapshots all keep valid internal references. + ## v1.0.9 - Add marketplace-aware install foundation for Codex 0.121+: the installer can now prefer `marketplace/add` + `plugin/install` when an official marketplace source is available, while keeping the existing legacy fallback path for unsupported builds. diff --git a/internal-skills/cli-runtime/SKILL.md b/internal-skills/cli-runtime/runtime.md similarity index 68% rename from internal-skills/cli-runtime/SKILL.md rename to internal-skills/cli-runtime/runtime.md index cb1966b..77b78d3 100644 --- a/internal-skills/cli-runtime/SKILL.md +++ b/internal-skills/cli-runtime/runtime.md @@ -1,22 +1,17 @@ ---- -name: cli-runtime -description: Internal execution contract for forwarding one tracked Claude Code rescue task through claude-companion.mjs. -user-invocable: false ---- +# Claude Code Rescue Runtime Reference -# Claude Code Runtime - -Use this skill only inside the rescue forwarding worker spawned by `$cc:rescue`. -This skill owns execution and routing. It does not own prompt rewriting beyond deciding when to call `task-prompt-shaping`. +Use this document only inside the rescue forwarding worker spawned by `$cc:rescue` as defined in `../../skills/rescue/SKILL.md`. +This is an internal execution contract, not a public skill. It owns execution and routing. It does not own prompt rewriting beyond deciding when to consult the prompt-shaping reference. +The public rescue skill already resolved the installed plugin root. Reuse that installed copy path here. Do not derive a new runtime path from this document, any cache directory, or the current working tree. Primary helper: -- `node "/scripts/claude-companion.mjs" task ...` +- `node "/scripts/claude-companion.mjs" task ...` Execution rules: - The rescue subagent is a forwarder, not an operator. Launch exactly one `task` command and return that stdout unchanged. - Prefer the helper over hand-rolled Bash, direct Claude Code CLI strings, or any other orchestration path. - Never call `setup`, `review`, `adversarial-review`, `status`, `result`, or `cancel` from the rescue forwarder. -- You may call `task-prompt-shaping` to rewrite the user's request into a tighter Claude Code prompt before the single `task` call. +- You may consult `../task-prompt-shaping/prompt-shaping.md` to rewrite the user's request into a tighter Claude Code prompt before the single `task` call. - That prompt shaping is the only Claude-side work allowed. Do not inspect the repo, solve the task yourself, or add independent analysis. Command selection: @@ -34,6 +29,7 @@ Routing controls: - `--view-state on-success` means the user will see this companion result in the current turn, so the companion may mark it viewed on success. - `--view-state defer` means the parent is not waiting, so the companion must leave the result unread until the user explicitly checks it. - `--owner-session-id ` is an internal parent-session routing control. Preserve it when present so tracked jobs remain visible to the parent session's `$cc:status` / `$cc:result`. +- Never emit an empty routing placeholder such as `--owner-session-id --job-id`. - Do not add `--quiet-progress` by default for built-in rescue forwarding. Let companion stderr progress remain available in the spawned agent thread. - If the free-text task begins with `/`, treat that slash command as literal Claude Code task text to forward unchanged. Do not execute it as a local Codex slash command or answer it inline. - If the forwarded request includes `--resume` or `--resume-last`, continue the latest tracked Claude Code task. @@ -50,5 +46,13 @@ Task defaults: - Use a structured file-write path to create that prompt file when possible. Do not solve shell quoting by wrapping the same long task inside another brittle inline shell command. - If the tool output includes stderr progress chatter and a final stdout-style result, ignore the progress chatter and preserve only the final stdout-equivalent result text. - Return the stdout of the `task` command exactly as-is. +- Run the companion command as one blocking foreground shell-tool call, not as a background terminal/session. +- Do not request a shell session id, poll a shell session later, or return before the companion command exits. +- If the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call. +- If the parent supplied a non-empty parent thread id for background completion, allow at most one success-only `send_input` notification before finishing. +- Mention the tool name `send_input` literally when describing that notification path. +- Use the exact tool shape `send_input({ target: , message: })`. +- Use steering messages that point the parent at `$cc:result` or `$cc:status` instead of embedding the raw Claude result. +- For background rescue, use that same steering message as the child's own final assistant message instead of echoing the raw companion result. - Do not poll status, fetch results, cancel jobs, or add commentary after the companion output. - If the companion reports missing setup or authentication, tell the user to run `$cc:setup`. diff --git a/internal-skills/review-runtime/runtime.md b/internal-skills/review-runtime/runtime.md new file mode 100644 index 0000000..a26b2c9 --- /dev/null +++ b/internal-skills/review-runtime/runtime.md @@ -0,0 +1,58 @@ +# Claude Code Review Runtime Reference + +Use this document only when the main Codex thread or a built-in forwarding child is executing a Claude Code `review` or `adversarial-review` command. +This is an internal runtime reference, not a public skill. It captures the exact companion-command contract and the foreground/background execution boundary. +The public skill already resolved the installed plugin root. Reuse that installed copy path here. Do not derive a new runtime path from this document, any cache directory, or the current working tree. + +Primary helper: +- `node "/scripts/claude-companion.mjs" review ...` +- `node "/scripts/claude-companion.mjs" adversarial-review ...` + +Execution boundary: +- Foreground review stays on the main Codex thread. Do not satisfy foreground review through a review subagent, a generic review-runner role, or any background worker abstraction. +- Background review uses exactly one built-in forwarding child through `spawn_agent`. +- Never satisfy either mode with raw `claude`, `claude-code`, `claude review`, hand-rolled `bash -lc ...claude...`, or detached companion shell backgrounding. +- If the installed companion command fails, surface that failure instead of improvising a different executor. + +Foreground contract: +- Strip `--wait` and `--background` before building the companion command. +- Foreground command: + - `review --view-state on-success ...` + - `adversarial-review --view-state on-success ...` +- Return companion stdout faithfully and do not add review execution commentary around it. + +Background contract: +- Use `background-routing-context --kind review --json` before spawning the forwarding child. +- Preserve `--job-id` only when reserved by the parent helper. +- Preserve `--owner-session-id` only when the parent helper returned a non-empty owner session id. +- Preserve the parent notification path only when the helper returned a non-empty parent thread id. +- Never emit an empty routing placeholder such as `--owner-session-id --job-id`. +- The built-in child runs exactly one shell command: + - `review --view-state defer ...` + - `adversarial-review --view-state defer ...` +- The child must be a pure forwarder: + - return stdout only + - ignore stderr progress chatter such as `[cc] ...` + - do not inspect the repo or perform the review itself + - run the companion command as one blocking foreground shell-tool call, not as a background terminal/session + - do not request a shell session id, poll a shell session later, or return before the companion command exits + - if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call + - use at most one `send_input` completion notification on success + - mention the tool name `send_input` literally in the child instructions + - use the exact tool shape `send_input({ target: , message: })` + - do not silently drop the completion notification path when the parent provided a non-empty parent thread id + +Spawn-agent defaults: +- `agent_type: "default"` +- `fork_context: false` +- `model: "gpt-5.4-mini"` +- `reasoning_effort: "medium"` +- If that model is explicitly unavailable, retry once with `model: "gpt-5.4"` and the same effort. + +Completion steering: +- When a reserved review job id exists, steer to: + - `Background Claude Code review finished. Open it with $cc:result .` + - `Background Claude Code adversarial review finished. Open it with $cc:result .` +- Otherwise steer to `$cc:status` first, then `$cc:result`. +- Use that same steering message as the child's own final assistant message for background mode. +- Never inline raw review text in the notification or in the child's final assistant message for background mode. diff --git a/internal-skills/task-prompt-shaping/SKILL.md b/internal-skills/task-prompt-shaping/prompt-shaping.md similarity index 78% rename from internal-skills/task-prompt-shaping/SKILL.md rename to internal-skills/task-prompt-shaping/prompt-shaping.md index 3b62503..63782fc 100644 --- a/internal-skills/task-prompt-shaping/SKILL.md +++ b/internal-skills/task-prompt-shaping/prompt-shaping.md @@ -1,14 +1,7 @@ ---- -name: task-prompt-shaping -description: Internal prompt-rewriting contract for tightening a rescue request before one forwarded Claude Code task call. -user-invocable: false ---- +# Claude Code Rescue Prompt Shaping Reference -# Task Prompt Shaping - -Use this skill only when the rescue forwarding worker needs to turn the user's request into a tighter Claude Code task prompt before one companion `task` call. -This skill owns prompt text only. It does not decide execution mode, resume/fresh routing, or any other runtime controls. -Keep the prompt compact and block-structured. The reference file is optional depth, not required for normal use. +Use this document only when the rescue forwarding worker needs to turn the user's request into a tighter Claude Code task prompt before one companion `task` call. +This is an internal prompt-shaping reference, not a public skill. It owns prompt text only. It does not decide execution mode, resume/fresh routing, or any other runtime controls. Core rules: - Prefer one clear task per Claude Code run. Split unrelated asks into separate runs. @@ -32,12 +25,12 @@ Hard limits: - Do not solve the task yourself. - Do not add long boilerplate that does not change behavior. -When to use this skill: +When to use this reference: - The raw request is vague, chatty, or underspecified. - The request needs a clearer output contract or follow-through default. - A follow-up should become a short delta prompt for resume. -When not to use this skill: +When not to use this reference: - The raw request is already clear and compact. - The request is so short that rewriting would add more words than value. - You would need to inspect the repository first to improve the prompt. Do not do that here. diff --git a/package.json b/package.json index 813a919..5e14aef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cc-plugin-codex", - "version": "1.0.9", + "version": "1.1.0", "description": "Claude Code Plugin for Codex by Sendbird", "type": "module", "author": { diff --git a/skills/adversarial-review/SKILL.md b/skills/adversarial-review/SKILL.md index 403852f..bfe3f12 100644 --- a/skills/adversarial-review/SKILL.md +++ b/skills/adversarial-review/SKILL.md @@ -52,15 +52,20 @@ Argument handling: - It does not support `--scope staged` or `--scope unstaged`. - Unlike `$cc:review`, it can still take extra focus text after the flags. - The companion review process itself always runs in the foreground. Background mode only changes how Codex launches that command. +- For the detailed execution contract, treat the internal runtime reference at `../../internal-skills/review-runtime/runtime.md` as supporting guidance only. It is an internal reference document, not a public skill to invoke. Foreground flow: - Run: `node "/scripts/claude-companion.mjs" adversarial-review --view-state on-success ` +- Foreground adversarial review belongs to the main Codex thread. Do not spawn a review subagent, do not invoke a generic review-runner role, and do not proxy this foreground path through any background worker abstraction. +- Do not fall back to raw `claude`, `claude-code`, `claude review`, `bash -lc ...claude...`, or any other direct Claude CLI syntax when the companion path is available. The foreground syntax contract here is the installed companion command above, not a hand-rolled Claude invocation. +- If the installed companion command fails, surface that failure. Do not silently retry foreground adversarial review through a different CLI shape, a generic review runner, or a custom shell wrapper. - Present the companion stdout faithfully. - Do not fix anything mentioned in the review output. Background flow: - For background adversarial review, use Codex's built-in `default` subagent instead of a detached background shell command. +- Do not satisfy background adversarial review by using a generic `claude_review_runner`-style helper role, raw Claude CLI, or any other review executor that bypasses the installed companion command. - Never satisfy background adversarial review by running the companion command itself with shell backgrounding such as `&`, `nohup`, detached `spawn`, or any equivalent direct background process launch. - Background here means "spawn the forwarding child via `spawn_agent` and do not wait in the parent turn." The companion adversarial-review command inside that child still runs once, in the foreground, inside the child thread. - Before spawning the built-in child, capture the review job id plus routing context in one call: @@ -85,6 +90,9 @@ Background flow: - run exactly one shell command - execute: `node "/scripts/claude-companion.mjs" adversarial-review --view-state defer ` + - run that command as one blocking foreground shell-tool call, not as a background terminal/session + - do not request a shell session id, poll a shell session later, or return before the companion command exits + - if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call - include `--owner-session-id ` only when the parent resolved a non-empty owner session id - include `--job-id ` when the parent reserved one - never leave an empty routing placeholder such as `--owner-session-id --job-id` diff --git a/skills/rescue/SKILL.md b/skills/rescue/SKILL.md index dbd2b98..cd160ca 100644 --- a/skills/rescue/SKILL.md +++ b/skills/rescue/SKILL.md @@ -82,9 +82,10 @@ Subagent launch: - If it returns an empty `parentThreadId`, continue without parent wake-up instead of blocking the rescue. - This parent wake-up attempt is now the default for background built-in rescue on persistent Codex/Desktop threads. It is still best-effort and should silently degrade on one-shot `codex exec` runs. - For the built-in rescue path, the parent thread owns prompt shaping. The built-in child should stay a pure executor. +- For the built-in rescue path, treat the internal runtime reference at `../../internal-skills/cli-runtime/runtime.md` as the command-building contract for the forwarding worker. It is an internal reference document, not a public skill to invoke. - If the built-in rescue request is vague, chatty, or a follow-up, the parent may tighten only the task text before composing the exact companion command. - Prefer passing a small structured `` block instead of forked thread history when the child needs a little prior context. -- Use the `task-prompt-shaping` internal rules as guidance for that parent-side tightening: +- Use the internal prompt-shaping reference at `../../internal-skills/task-prompt-shaping/prompt-shaping.md` as deeper guidance for that parent-side tightening. It is an internal reference document, not a public skill to invoke. - preserve user intent and add no new repo facts - prefer a short delta instruction for resume follow-ups - when helpful, use compact blocks such as ``, ``, and `` @@ -119,6 +120,9 @@ Subagent launch: - The built-in rescue path must use a compact strict forwarding message. It must: - identify the child as a transient forwarding worker for Claude Code rescue - include exactly one shell command to run + - run that command as one blocking foreground shell-tool call, not as a background terminal/session + - do not request a shell session id, poll a shell session later, or return before the companion command exits + - if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call - for foreground rescue only, tell the child to return that command's stdout text exactly, with no preamble, summary, code fence, trimming, normalization, or punctuation changes - tell the child to ignore stderr progress chatter such as `[cc] ...` lines and preserve only the stdout-equivalent final result text - if a parent thread id is provided for experimental background notification, allow one extra `send_input` call after a successful shell result and before finishing diff --git a/skills/review/SKILL.md b/skills/review/SKILL.md index c6ef1f2..0350478 100644 --- a/skills/review/SKILL.md +++ b/skills/review/SKILL.md @@ -50,15 +50,20 @@ Argument handling: - `$cc:review` is native-review only. It does not support staged-only review, unstaged-only review, or extra focus text. - If the user needs custom review instructions or more adversarial framing, they should use `$cc:adversarial-review`. - The companion review process itself always runs in the foreground. Background mode only changes how Codex launches that command. +- For the detailed execution contract, treat the internal runtime reference at `../../internal-skills/review-runtime/runtime.md` as supporting guidance only. It is an internal reference document, not a public skill to invoke. Foreground flow: - Run: `node "/scripts/claude-companion.mjs" review --view-state on-success ` +- Foreground review belongs to the main Codex thread. Do not spawn a review subagent, do not invoke a generic review-runner role, and do not proxy this foreground path through any background worker abstraction. +- Do not fall back to raw `claude`, `claude-code`, `claude review`, `bash -lc ...claude...`, or any other direct Claude CLI syntax when the companion path is available. The foreground syntax contract here is the installed companion command above, not a hand-rolled Claude invocation. +- If the installed companion command fails, surface that failure. Do not silently retry foreground review through a different CLI shape, a generic review runner, or a custom shell wrapper. - Present the companion stdout faithfully. - Do not fix anything mentioned in the review output. Background flow: - For background review, use Codex's built-in `default` subagent instead of a detached background shell command. +- Do not satisfy background review by using a generic `claude_review_runner`-style helper role, raw Claude CLI, or any other review executor that bypasses the installed companion command. - Never satisfy background review by running the companion command itself with shell backgrounding such as `&`, `nohup`, detached `spawn`, or any equivalent direct background process launch. - Background here means "spawn the forwarding child via `spawn_agent` and do not wait in the parent turn." The companion review command inside that child still runs once, in the foreground, inside the child thread. - Before spawning the built-in child, capture the review job id plus routing context in one call: @@ -83,6 +88,9 @@ Background flow: - run exactly one shell command - execute: `node "/scripts/claude-companion.mjs" review --view-state defer ` + - run that command as one blocking foreground shell-tool call, not as a background terminal/session + - do not request a shell session id, poll a shell session later, or return before the companion command exits + - if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call - include `--owner-session-id ` only when the parent resolved a non-empty owner session id - include `--job-id ` when the parent reserved one - never leave an empty routing placeholder such as `--owner-session-id --job-id` diff --git a/tests/e2e/codex-skills-e2e.test.mjs b/tests/e2e/codex-skills-e2e.test.mjs index 99a881c..3411325 100644 --- a/tests/e2e/codex-skills-e2e.test.mjs +++ b/tests/e2e/codex-skills-e2e.test.mjs @@ -832,6 +832,9 @@ function startMockProvider({ spawnMessage ?? "You are a transient forwarding worker for Claude Code rescue.\n" + "Run exactly one shell command.\n" + + "Run that command as one blocking foreground shell-tool call, not as a background terminal or session.\n" + + "Do not request a shell session id, poll a shell session later, or return before the command exits.\n" + + "If the shell tool is exec_command, call it once in non-interactive mode and wait for exit in that same call.\n" + "Return only that command's stdout text exactly.\n" + "Ignore stderr progress chatter such as [cc] lines.\n" + "If the tool output includes both stderr progress and a final stdout-style result, preserve only the final stdout-equivalent result text.\n" + @@ -861,6 +864,18 @@ function startMockProvider({ bodyText.includes("Run exactly this command and return stdout unchanged"), "spawned child turn should receive the forwarding contract" ); + assert.ok( + bodyText.includes("blocking foreground shell-tool call, not as a background terminal or session"), + "built-in child should be told not to launch a background terminal/session" + ); + assert.ok( + bodyText.includes("Do not request a shell session id, poll a shell session later, or return before the command exits."), + "built-in child should be told not to return a shell session before the command exits" + ); + assert.ok( + bodyText.includes("If the shell tool is exec_command, call it once in non-interactive mode and wait for exit in that same call."), + "built-in child should be told how to use exec_command without backgrounding" + ); assert.ok( bodyText.includes("transient forwarding worker for Claude Code rescue"), "built-in child should receive the stricter forwarding contract" @@ -1255,6 +1270,11 @@ describe("Codex rescue-skill E2E", () => { taskCommand: `node ${JSON.stringify(COMPANION_SCRIPT)} task --fresh --job-id ${JSON.stringify(reservedJobId)} --view-state defer ${JSON.stringify(taskPrompt)}`, expectedChildNeedles: ["--view-state defer", "--job-id", reservedJobId], + expectedParentNeedles: [ + "blocking foreground shell-tool call, not as a background terminal/session", + "do not request a shell session id, poll a shell session later, or return before the companion command exits", + "if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call", + ], notificationMessage, }); testEnv.providerPort = await provider.listen(); @@ -1749,6 +1769,9 @@ describe("Codex direct-skill E2E", () => { "background-routing-context --kind review --json", "--owner-session-id ", "Never satisfy background review by running the companion command itself with shell backgrounding", + "blocking foreground shell-tool call, not as a background terminal/session", + "do not request a shell session id, poll a shell session later, or return before the companion command exits", + "if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call", "allow one extra `send_input` call after a successful shell result", "must target the provided parent thread id", "do not silently drop the completion notification path from the child prompt", @@ -1771,6 +1794,9 @@ describe("Codex direct-skill E2E", () => { "You are a pure forwarder for a background Claude Code review job.\n" + "Do not inspect the repo, do not review anything yourself, and do not add commentary.\n" + "Run exactly one shell command and capture only the stdout-equivalent final result text from that command, ignoring stderr progress chatter like [cc] lines.\n" + + "Run that command as one blocking foreground shell-tool call, not as a background terminal or session.\n" + + "Do not request a shell session id, poll a shell session later, or return before the command exits.\n" + + "If the shell tool is exec_command, call it once in non-interactive mode and wait for exit in that same call.\n" + "If the command succeeds and a parent thread id is available, send exactly this notification to the parent thread before finishing: " + JSON.stringify(notificationMessage) + "\n" + "Use that same sentence as your own final assistant message.\n" + @@ -1955,6 +1981,9 @@ describe("Codex direct-skill E2E", () => { "background-routing-context --kind review --json", "--owner-session-id ", "Never satisfy background adversarial review by running the companion command itself with shell backgrounding", + "blocking foreground shell-tool call, not as a background terminal/session", + "do not request a shell session id, poll a shell session later, or return before the companion command exits", + "if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call", "allow one extra `send_input` call after a successful shell result", "must target the provided parent thread id", "do not silently drop the completion notification path from the child prompt", @@ -1978,6 +2007,9 @@ describe("Codex direct-skill E2E", () => { "You are a pure forwarder for a background Claude Code adversarial review job.\n" + "Do not inspect the repo, do not review anything yourself, and do not add commentary.\n" + "Run exactly one shell command and capture only the stdout-equivalent final result text from that command, ignoring stderr progress chatter like [cc] lines.\n" + + "Run that command as one blocking foreground shell-tool call, not as a background terminal or session.\n" + + "Do not request a shell session id, poll a shell session later, or return before the command exits.\n" + + "If the shell tool is exec_command, call it once in non-interactive mode and wait for exit in that same call.\n" + "If the command succeeds and a parent thread id is available, send exactly this notification to the parent thread before finishing: " + JSON.stringify(notificationMessage) + "\n" + "Use that same sentence as your own final assistant message.\n" + diff --git a/tests/skills-contracts.test.mjs b/tests/skills-contracts.test.mjs index 8ac0ab1..c061434 100644 --- a/tests/skills-contracts.test.mjs +++ b/tests/skills-contracts.test.mjs @@ -16,6 +16,40 @@ function read(relativePath) { return fs.readFileSync(path.join(PROJECT_ROOT, relativePath), "utf8"); } +test("internal runtime references keep the installed-root and notification invariants", () => { + const reviewRuntime = read("internal-skills/review-runtime/runtime.md"); + const rescueRuntime = read("internal-skills/cli-runtime/runtime.md"); + const installedRootPattern = + /\/scripts\/claude-companion\.mjs/i; + + assert.match(reviewRuntime, /resolved the installed plugin root/i); + assert.match(reviewRuntime, installedRootPattern); + assert.match(reviewRuntime, /Do not derive a new runtime path from this document, any cache directory, or the current working tree/i); + assert.match(reviewRuntime, /Never emit an empty routing placeholder such as `--owner-session-id {2}--job-id`/i); + assert.match(reviewRuntime, /blocking foreground shell-tool call, not as a background terminal\/session/i); + assert.match(reviewRuntime, /Do not request a shell session id, poll a shell session later, or return before the companion command exits/i); + assert.match(reviewRuntime, /if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call/i); + assert.match(reviewRuntime, /mention the tool name `send_input` literally/i); + assert.match(reviewRuntime, /exact tool shape `send_input\(\{ target: , message: \}\)`/i); + assert.match(reviewRuntime, /do not silently drop the completion notification path when the parent provided a non-empty parent thread id/i); + assert.match(reviewRuntime, /Use that same steering message as the child's own final assistant message for background mode/i); + + assert.match(rescueRuntime, /resolved the installed plugin root/i); + assert.match(rescueRuntime, installedRootPattern); + assert.match(rescueRuntime, /Do not derive a new runtime path from this document, any cache directory, or the current working tree/i); + assert.match(rescueRuntime, /Never emit an empty routing placeholder such as `--owner-session-id {2}--job-id`/i); + assert.match(rescueRuntime, /Do not add `--quiet-progress` by default/i); + assert.match(rescueRuntime, /slash command as literal Claude Code task text/i); + assert.match(rescueRuntime, /blocking foreground shell-tool call, not as a background terminal\/session/i); + assert.match(rescueRuntime, /Do not request a shell session id, poll a shell session later, or return before the companion command exits/i); + assert.match(rescueRuntime, /if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call/i); + assert.match(rescueRuntime, /allow at most one success-only `send_input` notification before finishing/i); + assert.match(rescueRuntime, /Mention the tool name `send_input` literally/i); + assert.match(rescueRuntime, /exact tool shape `send_input\(\{ target: , message: \}\)`/i); + assert.match(rescueRuntime, /Use steering messages that point the parent at `\$cc:result` or `\$cc:status` instead of embedding the raw Claude result/i); + assert.match(rescueRuntime, /use that same steering message as the child's own final assistant message instead of echoing the raw companion result/i); +}); + test("review skills keep background execution outside the companion command", () => { const review = read("skills/review/SKILL.md"); const adversarial = read("skills/adversarial-review/SKILL.md"); @@ -31,8 +65,16 @@ test("review skills keep background execution outside the companion command", () assert.match(review, /Treat `--wait` and `--background` as Codex-side execution controls only/i); assert.match(review, /Strip them before calling the companion command/i); assert.match(review, /The companion review process itself always runs in the foreground/i); + assert.match(review, /internal runtime reference at `\.\.\/\.\.\/internal-skills\/review-runtime\/runtime\.md`/i); + assert.match(review, /It is an internal reference document, not a public skill to invoke/i); assert.match(review, /review --view-state on-success/i); + assert.match(review, /Foreground review belongs to the main Codex thread/i); + assert.match(review, /Do not spawn a review subagent/i); + assert.match(review, /do not invoke a generic review-runner role/i); + assert.match(review, /Do not fall back to raw `claude`, `claude-code`, `claude review`, `bash -lc \.\.\.claude\.\.\.`/i); + assert.match(review, /If the installed companion command fails, surface that failure/i); assert.match(review, /For background review, use Codex's built-in `default` subagent/i); + assert.match(review, /Do not satisfy background review by using a generic `claude_review_runner`-style helper role/i); assert.match(review, /Never satisfy background review by running the companion command itself with shell backgrounding/i); assert.match(review, /Background here means "spawn the forwarding child via `spawn_agent` and do not wait in the parent turn\."/i); assert.match(review, /background-routing-context --kind review --json/i); @@ -49,6 +91,9 @@ test("review skills keep background execution outside the companion command", () assert.match(review, /review --view-state defer/i); assert.match(review, /include `--owner-session-id ` only when the parent resolved a non-empty owner session id/i); assert.match(review, /never leave an empty routing placeholder such as `--owner-session-id {2}--job-id`/i); + assert.match(review, /blocking foreground shell-tool call, not as a background terminal\/session/i); + assert.match(review, /Do not request a shell session id, poll a shell session later, or return before the companion command exits/i); + assert.match(review, /if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call/i); assert.match(review, /allow one extra `send_input` call after a successful shell result/i); assert.match(review, /must mention the tool name `send_input` literally/i); assert.match(review, /must target the provided parent thread id/i); @@ -74,8 +119,16 @@ test("review skills keep background execution outside the companion command", () assert.match(adversarial, /Treat `--wait` and `--background` as Codex-side execution controls only/i); assert.match(adversarial, /Strip them before calling the companion command/i); assert.match(adversarial, /The companion review process itself always runs in the foreground/i); + assert.match(adversarial, /internal runtime reference at `\.\.\/\.\.\/internal-skills\/review-runtime\/runtime\.md`/i); + assert.match(adversarial, /It is an internal reference document, not a public skill to invoke/i); assert.match(adversarial, /adversarial-review --view-state on-success/i); + assert.match(adversarial, /Foreground adversarial review belongs to the main Codex thread/i); + assert.match(adversarial, /Do not spawn a review subagent/i); + assert.match(adversarial, /do not invoke a generic review-runner role/i); + assert.match(adversarial, /Do not fall back to raw `claude`, `claude-code`, `claude review`, `bash -lc \.\.\.claude\.\.\.`/i); + assert.match(adversarial, /If the installed companion command fails, surface that failure/i); assert.match(adversarial, /For background adversarial review, use Codex's built-in `default` subagent/i); + assert.match(adversarial, /Do not satisfy background adversarial review by using a generic `claude_review_runner`-style helper role/i); assert.match(adversarial, /Never satisfy background adversarial review by running the companion command itself with shell backgrounding/i); assert.match(adversarial, /Background here means "spawn the forwarding child via `spawn_agent` and do not wait in the parent turn\."/i); assert.match(adversarial, /background-routing-context --kind review --json/i); @@ -92,6 +145,9 @@ test("review skills keep background execution outside the companion command", () assert.match(adversarial, /adversarial-review --view-state defer/i); assert.match(adversarial, /include `--owner-session-id ` only when the parent resolved a non-empty owner session id/i); assert.match(adversarial, /never leave an empty routing placeholder such as `--owner-session-id {2}--job-id`/i); + assert.match(adversarial, /blocking foreground shell-tool call, not as a background terminal\/session/i); + assert.match(adversarial, /Do not request a shell session id, poll a shell session later, or return before the companion command exits/i); + assert.match(adversarial, /if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call/i); assert.match(adversarial, /allow one extra `send_input` call after a successful shell result/i); assert.match(adversarial, /must mention the tool name `send_input` literally/i); assert.match(adversarial, /must target the provided parent thread id/i); @@ -177,6 +233,9 @@ test("rescue skill documents the experimental built-in-agent forwarding path", ( assert.match(rescue, /Background Claude Code rescue finished\. Open it with \$cc:result \./i); assert.match(rescue, /fall back to:/i); assert.match(rescue, /Background Claude Code rescue finished\. Inspect it with \$cc:status first, then use \$cc:result for the finished job you want to open\./i); + assert.match(rescue, /blocking foreground shell-tool call, not as a background terminal\/session/i); + assert.match(rescue, /Do not request a shell session id, poll a shell session later, or return before the companion command exits/i); + assert.match(rescue, /if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call/i); assert.match(rescue, /prefer these steering messages over embedding the raw result text/i); assert.match(rescue, /do not embed the raw Claude result inside the notification message/i); assert.match(rescue, /do not include any other prose in that notification message/i); @@ -187,7 +246,10 @@ test("rescue skill documents the experimental built-in-agent forwarding path", ( assert.match(rescue, /the parent thread owns prompt shaping/i); assert.match(rescue, /If the built-in rescue request is vague, chatty, or a follow-up, the parent may tighten only the task text/i); assert.match(rescue, /Prefer passing a small structured `` block instead of forked thread history/i); - assert.match(rescue, /Use the `task-prompt-shaping` internal rules as guidance/i); + assert.match(rescue, /internal runtime reference at `\.\.\/\.\.\/internal-skills\/cli-runtime\/runtime\.md`/i); + assert.match(rescue, /It is an internal reference document, not a public skill to invoke/i); + assert.match(rescue, /internal prompt-shaping reference at `\.\.\/\.\.\/internal-skills\/task-prompt-shaping\/prompt-shaping\.md`/i); + assert.match(rescue, /It is an internal reference document, not a public skill to invoke/i); assert.match(rescue, /If the request is already concrete, keep it literal/i); assert.match(rescue, /If the request names a concrete file, path, or artifact such as `README\.md`/i); assert.match(rescue, /Do not compress it into a shorter delta/i); @@ -223,7 +285,7 @@ test("rescue skill documents the experimental built-in-agent forwarding path", ( }); test("rescue runtime guidance forbids task --background", () => { - const runtimeSkill = read("internal-skills/cli-runtime/SKILL.md"); + const runtimeSkill = read("internal-skills/cli-runtime/runtime.md"); assert.match(runtimeSkill, /`--background` and `--wait` are parent-side execution controls only/i); assert.match(runtimeSkill, /Strip both before building the `task` command/i); @@ -245,7 +307,7 @@ test("rescue runtime guidance forbids task --background", () => { test("rescue parent skill owns resume-candidate exploration", () => { const rescue = read("skills/rescue/SKILL.md"); - const runtimeSkill = read("internal-skills/cli-runtime/SKILL.md"); + const runtimeSkill = read("internal-skills/cli-runtime/runtime.md"); assert.match(rescue, /task-resume-candidate --json/i); assert.match(rescue, /Continue current Claude Code thread/i);