Repository navigation
[Bug]: A headless claude -p started by another agent inherits the pane's Orca identity and fires notifications for that pane #26020
Description
Operating system
macOS 26.6.2 (25G83), Apple Silicon (arm64)
Orca version
1.4.221 (build e65bbc95cec14b365e5cb22e3fdfdff413c1f82b). Also seen on 1.4.218.
Agents
The pane's agent is OMP (oh-my-pi) 18.6.1. Claude Code 2.1.284 runs as its child.
Details
Short summary
Our coding agent (OMP) runs tools that start headless claude -p processes, such as review jobs and one-off probes. These child processes inherit the pane's ORCA_* environment. Orca's managed Claude hook therefore posts their lifecycle events as if they came from the pane's own agent.
As a result, Orca sends Agent Task Complete notifications for the parent pane while the parent agent is still working. The pane's status row also switches to the child Claude session.
What happened
Case 1, Orca 1.4.218. A local code-review harness started one claude -p reviewer per lens from an OMP pane. Each time a reviewer finished, Orca sent a completion notification for the OMP pane. OMP was still waiting for the whole review at that point.
Case 2, Orca 1.4.221. An OMP pane ran a subagent. That subagent used its bash tool to run three short headless Claude commands from /tmp, started between 17:57:02 and 17:57:26 local time:
claude -p "Reply with the single word ok" --model <model>
OMP was still working throughout.
- Orca minted three agent notifications for the OMP pane, one per child run. Their IDs in
mobile-notification-dismissals.jsoncarry turn start times of 17:57:05, 17:57:09 and 17:57:25. - Afterwards, the pane's persisted row in
agent-hooks/last-status.jsonwas a Claude row, not an OMP row:{ "source": "claude", "hookEventName": "StopFailure", "payload": { "state": "done", "prompt": "Reply with the single word ok", "agentType": "claude" }, "providerSession": { "transcriptPath": "~/.claude/projects/-private-tmp/<id>.jsonl" }, "providerSessionOnly": true }
How to reproduce
- Start a non-Claude agent in an Orca terminal. I used OMP.
- From that agent's shell tool, run
claude -p "Reply with the single word ok". - An Agent Task Complete notification arrives for the pane, and the pane's status row becomes the Claude child's row.
Expected
A child agent CLI started by the pane's agent should not change the pane's identity, settle the pane's turn, or fire Agent Task Complete. If you think it should notify, please make that a setting that is off by default.
What I found in the code (at e65bbc9)
- The managed hook acts on whatever
ORCA_PANE_KEYit inherits. It exits early only when there is no payload, when there is no pane key, or through a per-caller guard for Grok (grok-replay-guard.ts). - The hook already captures the agent's PID and start time (
ORCA_HOOK_AGENT_PID="${PPID:-}"inhook-process-capture.ts). shared/agent-status-identity.ts(resolveAgentStatusIdentity/shouldSuppressInheritedTerminalStatus) already tries to stop a nested child from taking over a pane. It suppresses the child'sdonewhile the parent row is fresh and not done.- In case 2, the pane still ended up with the child's Claude row and notifications. I have not traced why the fence did not apply. One possibility is that the child creates its own provider-session row.
Related: #13341 (a nested agent replaces the parent's icon), #25686 (a nested codex exec model leaks into the parent row), #20109 (a nested Grok fires Agent Task Complete), #10999 (a nested claude -p is captured as an automation reply).
Suggested fix
- Detect nesting from the process lineage. If the agent PID that the hook reports descends from a different recognised agent running in the same pane, treat its events as child work. Keep the pane's identity, do not settle the turn, and do not notify.
selectForegroundProcessCandidatealready applies this "outer agent wins" rule to foreground identity. - Add a documented opt-out that every managed hook checks first, for example
ORCA_AGENT_HOOKS=off. Tools could then mark the child agents they start. Today the only option is to strip everyORCA_*variable, which depends on Orca's internal variable names. - Optionally, add a setting for whether nested agent runs may notify, because teams differ on this.
Workaround we use
Our harness removes every ORCA_* variable from the environment of the claude -p children it starts. I verified this against a fake local hook receiver:
- An unsanitised child produced 1 POST.
- A sanitised child produced 0 POSTs and no spool entry.
This only covers launchers that go through our helper. Ad-hoc claude -p runs, like case 2, are still affected.
Activity
AmethystLiang commentedon Oct 7, 2026
Thanks for reporting. Looking into it
tianlrz commentedon Oct 16, 2026
Additional real-device report and local evidence from Orca 1.4.218 on macOS with a paired iOS client, 2026-10-16. This extends the inherited-pane problem to headless
codex execchildren of a Claude pane. Related: #25686 and the owner-isolation work in #26408.User-visible symptom
The phone repeatedly shows notifications for the same workspace containing raw child results such as:
{"checked":86,"errors":[]}Here
checkedis the number of numeric claims examined by a batch reviewer, anderrorsis that reviewer's finding list. It is application output from a child job, not an Orca diagnostic and not the parent agent's final reply.What was verified locally
subprocess.run, with captured stdout/stderr and no explicit replacement environment. The Codex path usescodex exec --skip-git-repo-check -s read-only ... -o <output> -; the Claude path usesclaude -p ... --no-session-persistence. These children inherit the launcher's environment.{"checked":86,"errors":[]}at 19:39:16.055 +08:00.mobile-notification-dismissals.jsoncontains a distinct notification ID for the parent Claude pane whose encoded state-start timestamp is 19:39:16.150, just 95 ms later.checked: 86, errors: []checked: 99, errors: []checked: 73, errors: []The parent's own transcript continued afterward and produced its actual summary at about 19:42:36. The individual reviewer results preceded that summary.
The saved notification registry contains 64 distinct records for this pane in the hour ending at 19:42:36, with a 17.87-second median interval between their encoded timestamps. Those are host-side notification records, not a measurement of 64 APNs deliveries. The repeated iOS banners are the user's observation; device-side push arrival logs were not collected.
Expected behavior / regression coverage
A batch child may return its machine-readable result to the parent, but must not settle the parent pane, replace its displayed response, or trigger a completion push for each item. Keep the genuine parent completion and input-needed notifications.
Please include both
Claude -> codex execand same-providerClaude -> claude -pbatch runs in owner-isolation regression coverage, and assert the downstream notification count as well as the status row. The same-provider case matters because an agent-type-only fence cannot distinguish two Claude sessions. I have not tested #26408 against this workload and am not claiming it already resolves the report.Inspection was read-only: no running task, hook configuration, notification preference, or child environment was changed. Raw transcripts, prompts and pane identifiers are omitted here.