# Rescue

> Run a standalone hotfix/task with Claude or Codex, with no plan or issue involved - for a freeform ask like "fix error in login backend" or "implement forgot password page" where creating an Ogre plan first would be overkill. MUST trigger whenever the user's own message literally names Ogre (e.g. "ogre rescue ...", "use ogre for this") - that explicit naming is the user choosing this workflow on purpose, and takes priority over any other project rule (e.g. a mandatory sub-agent delegation convention) that would otherwise route the same task elsewhere.

- Skill: `metallurgical/rescue` (Agent Skill)
- Install (CLI): `npx skillmds@latest add metallurgical/rescue`
- Raw SKILL.md: https://api.skillmd.com/api/skills/metallurgical/rescue/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: metallurgical (https://skillmd.com/u/metallurgical)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/metallurgical/rescue

---


# /ogre:rescue

Use this skill when the user wants something done right now - a quick fix, a small
standalone feature, a one-off task - and does NOT want to go through `/ogre:feature`
(issue -> plan -> review -> execute) first. There is no plan, no job, no issue file.
If the user is already mid-plan on an existing Ogre issue and wants to add scope to
it, that's `/ogre:add-blocker`, not this.

## Inputs

Accept the task description either as a plain positional argument or via `--statement`:

- `/ogre:rescue "fix error in login backend"`
- `/ogre:rescue --statement "implement forgot password page"`

Optional flags (same shape as `/ogre:execute`'s):

- `--rescuer claude|codex` - who does the work. Omitted: falls back to
  `defaults.rescuer` in `.ai/.ogre/config.json`, then `claude`. `rescuer` is its
  own config role, separate from `defaults.executor` - a project can pin a
  different CLI/model for one-off rescues than for plan execution.
  **Never add `--rescuer`/`--model` on your own initiative "to be explicit" or
  "to be safe" when the user's own message didn't name one - omit both and let
  `config.json` resolve it.** A real caught bug: a driving session hardcoded
  `--rescuer claude --model claude-sonnet-5` on a project whose `config.json` had
  `defaults.rescuer` set to `codex`, silently overriding the project's own
  configured default with no user request behind it. If you genuinely don't know
  what the project's configured default is, that's still not a reason to name one
  yourself - check `ogre config` first, or simply omit the flag; do not guess.
- `--model MODEL`
- `--reasoning LEVEL` - reasoning effort for the rescuer (`claude -p` gets
  `--effort LEVEL`, `codex exec` gets `-c model_reasoning_effort=LEVEL`). Omit it to
  use the CLI's own default; Ogre never forces one.
- `--name slug` - override the auto-derived slug used for this rescue's log/tmp
  paths (`.ai/.ogre/{tmp,logs}/issue-rescue-<slug>/`). Without it, Ogre derives one
  from the first few words of the task text plus a short uuid suffix, same scheme
  as `/ogre:feature --statement`'s auto-name.
- `--main` - run inline in the current Claude Code session instead of spawning an
  isolated codex/claude session. Opt-in only, same as `/ogre:execute --main`; use it
  deliberately when the user explicitly wants the fix made in this conversation.
- `--background` - same isolation as default (new session) but detached/non-blocking.
- `--live` - opt-in, off by default. Runs the rescuer with `--json` (codex) or
  `--output-format stream-json --verbose` (claude) instead of plain text, and writes
  that raw JSONL to the log path instead of human-readable output. Only use this when
  the user explicitly wants to watch the rescuer's activity live (commands it runs,
  files it touches) inside this same Claude Code conversation - it changes nothing
  about the fix itself, purely a visibility option. See "Watching a `--live` rescue
  live" below for how to actually surface that as it happens; passing `--live` alone
  does nothing beyond changing the log file's format.
- `--allow-unverified-paths` - opt-in escape hatch for `reject_invented_paths` (see
  Behavior step 1): only pass this when `<task>` genuinely needs to name a file that
  doesn't exist yet (e.g. "create `services/PdfExport.php`"). Never pass it just to
  get past the check on a file you weren't sure existed - that's exactly the case the
  check exists to catch. If it fires and you don't recognize the filename as something
  the user actually typed, drop the filename from `<task>` instead of adding this flag.
- `--browser-check` - **opt-in, off by default, and it's the driving session's job to
  add it - the user never has to type this flag themselves.** Only when passed does
  the runner prompt tell the rescuer to verify via a real browser MCP (Playwright);
  omitted (the default), the runner says nothing about browser tools at all, even for
  a UI-sounding task. This mirrors `/ogre:feature`'s `[BROWSER-CHECK]` tagging being
  opt-in, not automatic - same rule: don't pass it unless the user's own message asks
  for live/browser-driven verification, in their own natural wording, not the literal
  flag name. Recognize intent, not syntax - e.g. "use playwright/live browser to
  testing and fix if found anything", "check it actually works in a real browser",
  "verify this renders correctly" all mean add `--browser-check` when building the
  `ogre rescue "<task>" ...` call, same as if they'd typed the flag. Do NOT infer this
  from the task merely touching UI/frontend code - "fix checkout page styling" does
  not imply `--browser-check` on its own; the task has to actually ask for a live
  browser check, not just a code edit. Previously this instruction was unconditional
  on every rescue (a real bug, not a design choice) - a codex rescuer reading an
  unconditional Playwright call-out on every single task, UI-related or not, would
  routinely decide "yes this needs it" even when nothing about the task asked for
  live verification. If the user hasn't said or clearly implied they want a
  browser-driven check, leave this off.
- **Flags are forwarded verbatim, never reinterpreted.** If the user's own message
  names an actual CLI flag/short-form for this command, pass that exact token through
  unchanged into the `ogre rescue ...` call - do not translate it into a different
  flag based on a guess at what they meant. `-r` is `--rescuer` and `-R` is
  `--reasoning` (rescuer got the lowercase letter since it's the one used regularly -
  this was previously swapped, a real bug, fixed). `-m`/`-M` (`--model`/`--main`)
  still differ only by case with unrelated meanings, so a wrong guess there is silent
  (no parse error, just wrong behavior). If you don't recognize a flag/short-form,
  check this file's Inputs list (or `scripts/ogre`'s actual parsing) before running
  anything. Only translate into a flag yourself when the user names no flag at all
  and describes pure intent in plain English.

## Default

- rescuer: `claude`
- isolation: **foreground, brand-new codex/claude session** - keeps main conversation
  context untouched, same model as `/ogre:execute`'s default. The calling session must
  wrap this in `run_in_background: true` (see Behavior below) so it doesn't block the
  conversation while it runs. Pass `--main` to do it inline instead, or `--background`
  to detach.

Codex rescuers run fully unsandboxed, same as every other codex spawn in this plugin
(`--dangerously-bypass-approvals-and-sandbox`) - see `/ogre:execute`'s note on this if
the user hasn't already been through that tradeoff this session.

## What makes this different from `/ogre:execute`

- **No plan file, no job, no `state/issue-<x>.json`.** `ogre rescue` never creates or
  reads a plan; it writes one runner file and spawns exactly one subprocess call, never
  a chain (no `--all`, no `--task`/`--step`, no `--retry`).
- **Still ledger-tracked, just by task id alone.** A rescue task IS recorded in the
  shared ledger (`.ai/.ogre/state/tasks.json`, `type: "rescue"`, `issue:
  "rescue-<slug>"`) so it shows up like any other spawn - but there's no job/issue
  summary to go with it. Track and manage it purely via its task id:
  - `${CLAUDE_PLUGIN_ROOT}/scripts/ogre status --task <id>`
  - `${CLAUDE_PLUGIN_ROOT}/scripts/ogre stop --task <id>`
  `--main` runs (no subprocess spawned) create no task id at all - there's nothing
  to track since this session is the one doing the work, right now.

## Behavior

1. **`<task>` is the user's own message, reused, not a paragraph you compose.** Default
   action: copy the user's own wording for the task straight into `<task>`, verbatim.
   Do not draft a new sentence/paragraph that "captures the same idea" - every word of
   that drafting is (a) tokens this session burns for zero benefit, since the isolated
   rescuer redoes the real discovery anyway, and (b) a chance to silently insert a
   detail the user never said. Only two kinds of touch-up are allowed, and both are
   edits to their words, not replacements of them:
   - Trim pure filler/typos/grammar (e.g. "uhh can u also" -> "also").
   - If a screenshot/image is attached, you may append a separate, clearly-labeled
     block of literally-visible content only - e.g. `Observed in screenshot: button
     labels "Pay now"/"Continue Shopping", status badge "UNPAID", "Not recorded" next
     to payment date.` Quote what's on screen; do not narrate or explain it, and never
     name a file/component/route from a screenshot - a rendered page cannot tell you
     its own source filename, so any name you'd write there is a guess, not an
     observation.
   **Never upgrade the user's own vocabulary to something more specific/technical**
   (e.g. "quantity update" -> "quantity stepper") **and never name a file, function,
   route, table, column, config key, or API you haven't personally opened and
   confirmed in this session** - both are guessing, not cleanup, dressed up as
   helpfulness. A second real caught case, same failure in a subtler form: the user
   said "setting page now looks nice, only just the each tab's content looks too big
   consuming space, can compact it" - a driving session condensed that into "settings
   page tabs content too big, consuming too much space, compact spacing", which reads
   like harmless tidying but silently added the word "spacing" - a specific guess at
   *what* to compact (spacing, as opposed to padding, font size, line-height, etc.)
   that the user never said. Condensing a rambly sentence is still paraphrasing, not
   "just" filler-trimming, the moment it reorders clauses or picks a more specific
   word than the user's own - if you're unsure whether an edit crosses that line,
   it does; leave the wording alone. `ogre rescue` itself now hard-rejects any `<task>` naming a source
   file that doesn't exist anywhere in the repo (`reject_invented_paths`, checked
   against `git ls-files`) as a backstop, but that only catches fabricated
   *filenames* - it can't catch an invented function/route/table name, or a
   paraphrase, so don't rely on it as the actual safeguard; the discipline above is
   the real one.
   Do not grep/read/inspect the target codebase yourself before this call, even to
   make the task text more precise - that's research the isolated rescuer should do
   itself, in its own throwaway context. Only exception to verbatim reuse: the task is
   ambiguous enough that you must ask the user a clarifying question before you can
   even form `<task>` - that's a question back to the user, not license to guess and
   fill in the gap yourself.
2. Run:
   - `${CLAUDE_PLUGIN_ROOT}/scripts/ogre rescue "<task>" [flags]`
3. Without `--main`, this call actually spawns codex/claude in a new isolated session -
   same as `/ogre:execute`'s isolation model.
   - **No `--background` (default)**: the `ogre rescue` call itself blocks at the shell
     level until the subprocess finishes. Never invoke it as a plain synchronous Bash
     call - always wrap it in **one single Bash tool call with `run_in_background:
     true`** around that same command, even though it's a single quick task. This keeps
     the main conversation free the whole run and makes it visible in `/tasks` instead
     of hard-blocking the turn.
     The harness delivers one completion notification straight to this session the
     moment the command exits - read the printed `Task <id> finished: passed|failed`
     from that output and report it. Do not poll for this case; the notification
     itself is the signal. **If `--live` was used and a Monitor is armed on the log
     path (see "Watching a `--live` rescue live" below), `TaskStop` it right here,
     before reporting** - `tail -f` never exits on its own, so it stays open in the
     TUI until timeout or a manual `(x)` if you don't.
   - **`--background`**: the `ogre rescue ... --background` call returns almost
     immediately after starting the detached subprocess, so it doesn't itself need the
     `run_in_background: true` wrapper - but you do then need to poll for completion,
     since ogre self-detaches with nothing left for the harness to hold onto. Report
     the task id, then poll it yourself the same way
     `/ogre:execute --background`'s skill does - **never spawn an `Agent` (fork or
     otherwise) to supervise this.** One single Bash tool call with
     `run_in_background: true` around a real shell loop, e.g.:
     `while :; do ${CLAUDE_PLUGIN_ROOT}/scripts/ogre status --task <id> | grep -qE '^\| Status +\| (passed|failed) ' && break; sleep 15; done`.
     The harness delivers a completion notification straight to this session the
     moment that loop exits - read the final `ogre status --task <id>` output and
     report pass/fail then. Never poll across separate assistant turns. **If `--live`
     was used and a Monitor is armed on the log path (see "Watching a `--live`
     rescue live" below), `TaskStop` it right here, before reporting** - `tail -f`
     never exits on its own, so it stays open in the TUI until timeout or a manual
     `(x)` if you don't.
4. If `--main` was passed: the runner file (`.ai/.ogre/tmp/issue-rescue-<slug>/rescue-runner.md`)
   is written but nothing is spawned - read it and do the task yourself, in this
   session, right now. There is no task id to close out afterward in this mode.
5. Otherwise, nothing further to execute yourself - the isolated session already did
   the work and self-reported via `ogre task-complete`.

## Watching a `--live` rescue live

Only relevant when `--live` was passed. `ogre rescue` itself just changes the log
format to JSONL and prints `Log path: <path>` immediately (before the rescuer starts) -
it does not stream anything into this conversation on its own. To actually see it live,
arm the `Monitor` tool on that log path concurrently with the backgrounded `ogre rescue`
call:

```
tail -n +1 -f <logpath> | jq -Rc --unbuffered '<per-rescuer transform below>'
```

**TL;DR before the details below: every delivered Monitor event gets summarized as
`⎿ ` + the summary text in a single backtick code span** (e.g.
`` ⎿ `Reading skill docs, still investigating.` ``) **— never bold, never plain prose.**
Full rationale is restated at the bottom of this section; it's repeated here too because
it's easy to lose track of by the time you're several events into a live run.

Do NOT prefix this with your own wait-for-file loop (e.g. `until [ -s <logpath> ]; do
sleep 1; done`) before starting the tail - `tail -n +1 -f` already reads from the start
of the file itself. A wait-loop just adds dead time in front for no benefit, and if you
also `echo` something from it, that echoed line becomes its own Monitor notification
that looks like a real rescuer event but isn't one - confusing when comparing against
actual progress.

- **Must use `jq -Rc '... | fromjson? | ...'`, never plain `jq -c 'select(...)'`.**
  Both codex's `--json` and claude's `--output-format stream-json` output can include a
  stray non-JSON line (codex: a leading `Reading additional input from stdin...`; claude
  interleaves plain hook progress text on some setups) - a naive `jq -c` hard-fails
  (exit 5) the first time it hits one and kills the whole pipe. `fromjson?` skips
  anything that doesn't parse instead of aborting.
- **Expect a real, sometimes long, silent gap before the first useful event** - the
  filters below deliberately exclude most bookkeeping noise, so what's left is gated on
  actual model response time (TTFT), which nothing here can shorten. A complex multi-file
  task can easily take longer than a trivial one before anything but the heartbeat shows
  up. That's the rescuer actually thinking, not a stuck pipe - don't `TaskStop`/re-check
  status just because a few seconds passed with only the heartbeat visible; verified via
  a real timed test (3 sequenced tool calls, ~3s apart) that this pipeline delivers each
  step's event as a separate live notification once the model gets to it, not batched.
  If it genuinely does look dead past the heartbeat (log file's mtime hasn't moved in
  well over a minute), don't manually grep/investigate - run
  `${CLAUDE_PLUGIN_ROOT}/scripts/ogre status --task <id>` first. It auto-detects and
  fails a foreground task whose rescuer process actually died (dead recorded pid, no
  exit sentinel) - same self-heal `ogre status` already does for background chains -
  so it's the fast, correct check before assuming something needs a fresh rescue.
- **Event filter differs per rescuer** - the two CLIs emit different event shapes,
  verified against real (non-mocked) output, not assumed from either CLI's docs. Each
  includes one *transformed*, small, one-time heartbeat event near the very start so you
  get an early "it's alive" signal without waiting for the first full model turn:
  - codex:
    ```
    fromjson? | select(.type=="thread.started" or .type=="item.completed" or .type=="error")
    ```
    `thread.started` is already tiny (`{"type":"thread.started","thread_id":"..."}`) -
    no transform needed, pass it through as the heartbeat. Skip `*.started`/`*.updated`
    otherwise - they're per-step noise, not per-outcome. **Do NOT include
    `turn.completed`** - verified against real output, it carries zero displayable
    content (`{"type":"turn.completed","usage":{"input_tokens":...}}`, token counts
    only, no text/command/patch field to summarize). Passing it through produces a
    bare `Monitor event: "..."` header with nothing underneath it once you go to
    write the `⎿` summary - exactly the empty-header noise this filter exists to
    avoid. Every `item.completed` does carry real content (its `item.type` varies -
    `agent_message` has `.item.text`, `command_execution` has `.item.command`/
    `.item.aggregated_output`, etc.) - summarize from whichever fields are present,
    that part still needs your own judgment per item.
  - claude:
    ```
    fromjson? | if .type=="system" and .subtype=="init" then {type:"heartbeat", msg:"rescuer session started, model working"} elif .type=="assistant" or .type=="result" then . else empty end
    ```
    The raw `system`/`init` event is real but large (~10KB - model/tools/mcp/skills
    inventory), so it's rewritten into a tiny synthetic `heartbeat` object instead of
    passed through raw. **Never** pass through `system`/`hook_response` events untouched
    - claude's stream-json emits one per SessionStart hook that inlines the *entire*
    hook output (can be tens of KB of skill markdown text, e.g. the whole caveman-mode
    or superpowers preamble) as a JSON string field. Letting that through Monitor floods
    this conversation with noise and burns real tokens for zero signal - verified size
    against a real captured run, not assumed.
- This is genuinely live, ongoing-stream usage of Monitor (many events over the
  rescuer's lifetime), not the one-shot "tell me when it's done" pattern - it does not
  fall under the general "don't use Monitor for a single completion signal" guidance;
  that guidance is about a different use case, not a ban on Monitor for this plugin.
- **If the user's own request/command is the one that included `--live`, that alone is
  the explicit ask - arm Monitor immediately, right after launch, with no further
  confirmation needed.** Do not treat "the user typed `--live`" as insufficient signal
  requiring some separate spoken "watch this live" - passing the flag themselves *is*
  them asking to watch. Only skip arming if the user's own message says *why* they
  want `--live` and that reason isn't watching (e.g. they said they just want JSONL in
  the log file for their own later tailing) - that case is rare and must be stated by
  the user, not assumed by you. Each surfaced event lands as a message in this
  conversation and consumes this session's own tokens, on top of the rescuer's own
  unrelated cost - that's the tradeoff `--live` opts into, not a reason to skip arming
  once it's been requested.
- Still separately wrap the actual `ogre rescue ... --live` call per the isolation rules
  above (`run_in_background: true` for the default/foreground case, or the poll loop for
  `--background`) - Monitor watches the log file, it does not replace waiting for the
  task's own completion signal.
- **Once that completion signal fires (the backgrounded/polled `ogre rescue` call
  itself finishes), call `TaskStop` on the Monitor's task id right away.** `tail -f`
  never exits on its own - rescuer finishing writes no EOF, so the armed Monitor just
  sits there open in the TUI until its timeout (default 300000ms, up to 3600000ms if
  `persistent` was set) or until the user manually kills it with `(x)`. Closing it out
  the moment rescue completes is on you, not the user.
- **Keep the `description` you pass to `Monitor(...)` short (~40 chars) - truncate
  with `...` if the task name runs longer.** That text is the only part of the
  `Monitor event: "<description>"` notification line under your control - the
  `Monitor event:` wording itself is fixed harness chrome, not something these
  instructions (or any skill) can rename or remove. Don't try to work around that by
  padding the description with extra formatting - just keep it short and legible,
  e.g. `codex rescue: category/price layout fix` rather than the full raw task text.
- **Format your own summary of a delivered Monitor event as `⎿ ` (Claude Code's own
  tree-connector glyph for "this line is nested under/belongs to the bullet above it")
  followed by the summary text wrapped in a single backtick code span** - e.g.
  `` ⎿ `Reading skill docs, still investigating.` `` renders as monospace text with its
  own subtle background box in Claude Code's terminal UI, visually distinct from both
  the harness's own `Monitor event: "<description>"` line above it (which you do not
  control - rendered by the harness, not something your instructions can remove or
  reformat) and from your own normal prose. Do NOT bold it - bold looked too similar to
  the harness's own line; the code-span box read as clearly distinct instead, per
  direct user feedback. `⎿ ` reuses a convention the user already recognizes from
  elsewhere in Claude Code's own UI instead of inventing a new one.
- **One `⎿` line per delivered Monitor event - never batch two or more events into a
  single combined summary**, even when they land close together and say similar things
  (e.g. a `reasoning` item immediately followed by an `agent_message` restating the same
  intent). Every `Monitor event:` header the harness prints needs its own `⎿` directly
  beneath it. Skipping one because "the next event covers it too" leaves that header
  looking orphaned - empty of content from the reader's point of view - even though the
  underlying event did carry real data (verified: after the `turn.completed` exclusion
  above, every surfaced `item.completed` has real text/command output, never nothing).
  If two events truly are near-duplicates, still write two short `⎿` lines rather than
  one long one - brevity in each line, not omission of one.
- **Never announce that you're "going quiet"/pausing narration/tracking silently
  from here** (e.g. "Acknowledged - going quiet now, will report once the chain
  finishes") **while a Monitor is still armed on the log.** A separate poll loop
  tracking overall completion does not suspend the per-event requirement above -
  they are two independent obligations, not one substituting for the other. Caught in
  practice: a driving session said this, then kept receiving `Monitor event:` headers
  with no `⎿` line under any of them - the header still fires every time the harness
  delivers an event, whether or not you decided to stop writing about it, so "going
  quiet" doesn't skip the headers, it just leaves them all orphaned. If you genuinely
  want to stop receiving live per-event updates, the correct action is `TaskStop` on
  the Monitor's task id, not silence while it stays armed.

## Rules

- One freeform task, one subprocess call - not a chain, not a multi-step plan.
- Do not pre-research the repo (grep/read files) in this session before calling
  `ogre rescue` - see Behavior step 1. Pass the task through, let the isolated
  rescuer do its own discovery.
- `<task>` is the user's own wording, reused verbatim (filler/typo trims and a
  literally-quoted screenshot block are the only allowed touch-ups) - not a paragraph
  you compose to "capture the idea." See Behavior step 1.
- Do not rewrite/paraphrase the user's own wording into more specific or technical
  terms on your own initiative (e.g. "quantity update" -> "quantity stepper", or
  condensing "can compact it" into "compact spacing") - see Behavior step 1. That's
  guessing, not cleanup, and it costs this session's own tokens for zero benefit.
  Condensing a rambly sentence down still counts as paraphrasing the moment it
  reorders clauses or picks a more specific word than the user's own.
- Do not invent files, methods, routes, tables, columns, config keys, or APIs.
  `ogre rescue` hard-rejects a `<task>` naming a source file not found anywhere in
  the repo (see `--allow-unverified-paths` under Inputs) as a backstop for the
  filename case specifically - it is not a substitute for the discipline above,
  since it can't detect an invented function/route/table name or a paraphrase.
- Do not add unrelated refactors or change behavior outside what was asked.
- Do not add packages unless the task clearly needs them.
- Preserve existing project style.
- Prefer the smallest safe change that fully addresses the task.
- Stop if validation fails. An already-dirty working tree at start (e.g. the user's
  own hand-edit/hotfix alongside this task) is NOT itself a reason to stop - leave
  those changes as-is, don't attribute them to this task, just mention them briefly
  in the report. Only stop over it if a file looks genuinely alarming (a
  credential/secret, a half-finished destructive edit), not for an ordinary
  incidental change.
- Stays on whatever branch is currently checked out. Rescue does not create or switch
  git branches on its own - if the user wants this done on a separate branch, they
  branch/stash themselves first.

## After Execution (only when a task id was created, i.e. no `--main`)

Report:

- Task completed (or failed) and its task id.
- Files changed.
- Reason for each changed file.
- Validation commands run.
- Validation result.

