fix(driver-owner): add OPC_DISABLE_OWNERSHIP=1 kill switch for non-Claude call sites#31
Open
WdBlink wants to merge 1 commit into
Open
fix(driver-owner): add OPC_DISABLE_OWNERSHIP=1 kill switch for non-Claude call sites#31WdBlink wants to merge 1 commit into
WdBlink wants to merge 1 commit into
Conversation
c30480 walks the parent-process chain via `ps` and treats any ancestor whose argv matches `/\bclaude\b/i` as the owning Claude session. On hosts that run multiple Claude Code CLI instances in parallel, or when opc-harness is invoked through a bash wrapper (`for` loop, `bash -c`, function call), `ps` may briefly expose a short-lived child of an unrelated Claude process in the parent chain. The loop is then stamped with the wrong claude_pid, and every subsequent complete-tick / next-tick fails closed with "not the loop owner". Add an opt-out env var that restores the pre-c30480 legacy behavior for non-Claude call sites (CI, batch harness, manual admin). Real `/opc <task>` use from Claude Code is unaffected — the kill switch is only consulted when the env var is set. Verified: bash test/run-all.sh goes from 85 pass / 40 fail (unpatched) to 104 pass / 21 fail with the kill switch. The 21 remaining failures are pre-existing on the v0.10.5 tag and are not regressions introduced by c30480 or by this patch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(driver-owner): add
OPC_DISABLE_OWNERSHIP=1kill switch for non-Claude call sitesWhat
Adds an opt-out env var (
OPC_DISABLE_OWNERSHIP=1) that restores thepre-c304800 legacy behavior of
checkOwnership()andresolveCallerIdentity().When set, both functions return immediately with a non-stamped / allow result
without invoking
findClaudeAncestorPid()or any fail-closed logic.Why
c304800 ("Bind OPC loop to its owning Claude session", 2026-07-07) walks the
parent-process chain via
ps -o ppid=,args=and treats any ancestor whoseargv matches
/\bclaude\b/ias the owning Claude session. The fail-closeddesign is correct for the case it targets: preventing a second Claude session
from driving a loop concurrently (the compaction double-drive bug).
It misfires on hosts where:
Multiple Claude Code CLI instances run in parallel.
psmay walk intoa sibling process tree (or briefly expose a short-lived child of an
unrelated Claude process during fork/exec) and stamp the loop with a
claude_pidthat is genuinely alive but not the caller's session.Every subsequent
complete-tickthen refuses with:The caller is not running under Claude Code at all (CI, batch harness,
bash -c,forloop, manualopc-harnessinvocation from a shell thatdidn't go through Claude's fork).
caller.claude_pidisnull. Thecurrent logic then fail-closes if the recorded owner happens to still be
alive — which is almost always the case in practice, because some other
Claude session IS alive on the box. The pre-c304800 behavior was to
allow in this situation (correctly: we cannot prove the caller is NOT
the owner).
This is reproducible on a stock macOS dev box running 3+ Claude CLI
instances (a Yoda + a ttys015 + a ttys017 session in my case). Run
bash test/run-all.shfrom a for-loop wrapper and watch 40 of 125 testfiles flip from pass to fail with the same
not the loop ownererror.Run the same test directly with
bash test/<file>.shand it passes.The 40 failures are the second category; the first category is what makes
the test suite (and any other batch-style invocation) unusable in practice.
The fix
OPC_DISABLE_OWNERSHIP=1opt-out. The kill switch is the minimum changethat:
(Claude Code invoking
/opc <task>),The kill switch is the user's explicit opt-in. Without the env var, the
c304800 logic is unchanged.
I considered alternatives and rejected them:
findClaudeAncestorPid()to requireclaude_pidto be thedirect parent of the harness's process. Doesn't fix the fork/exec
race during which a transient child of an unrelated Claude process
appears in the chain.
psancestry.Breaks the compaction-double-drive invariant the original commit was
added to fix.
cwd/dirargument. Doesn't match the model: thecwd argument is about which loop to drive, not which session is
allowed to drive it.
Verification
bash test/run-all.sh(125 test files, single host, macOS arm64):OPC_DISABLE_OWNERSHIP=1The 21 remaining failures on patched main are pre-existing on the
v0.10.5 tag (same count, same files, same
❌lines when run with thebackup
bin/opc-harness.mjs). They are not regressions introduced byc304800 or by this patch; they are independent test bugs in opc's
own test suite. I verified by re-running each failing file with
HARNESS="node /path/to/backup/bin/opc-harness.mjs"— 21/21 reproduceidentically.
Without the env var, normal
/opc <task>usage is unaffected (thecaller's
claude_pidis resolved normally, the kill switch is notconsulted). Verified via a fresh
init-loop → next-tick → complete-ticksequence with the same plan format the opc test suite uses — passes
with
verdict: FAILas expected.Patch
How to test this PR
I considered documenting
OPC_DISABLE_OWNERSHIPin the README's"Environment variables" section but left that out — happy to add it
in a follow-up if you'd like.