From 0d752aabdbeeb1d01410a6cf5be28598720d5ab3 Mon Sep 17 00:00:00 2001 From: WdBlink Date: Sat, 11 Jul 2026 22:22:31 +0800 Subject: [PATCH] fix(driver-owner): add OPC_DISABLE_OWNERSHIP=1 kill switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ` 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. --- bin/lib/driver-owner.mjs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bin/lib/driver-owner.mjs b/bin/lib/driver-owner.mjs index d9c39b0..47e55b5 100644 --- a/bin/lib/driver-owner.mjs +++ b/bin/lib/driver-owner.mjs @@ -109,6 +109,13 @@ export function findClaudeAncestorPid() { // ── Caller identity ───────────────────────────────────────────── export function resolveCallerIdentity() { + // Kill switch: OPC_DISABLE_OWNERSHIP=1 → pretend caller has no Claude ancestor + // (legacy behavior). Use for test harnesses, batch scripts, and hosts where + // `ps` walks the parent chain to an unrelated Claude process (race / multi- + // session hosts). checkOwnership() also short-circuits on the same env var. + if (process.env.OPC_DISABLE_OWNERSHIP === "1") { + return { claude_pid: null, claude_started_at: null, host: hostname() }; + } const claude_pid = findClaudeAncestorPid(); return { claude_pid, @@ -165,6 +172,13 @@ export function makeOwner(caller, token = generateOwnerToken()) { export function checkOwnership(state, caller, opts = {}) { const owner = state && state._owner; + // Kill switch: OPC_DISABLE_OWNERSHIP=1 → legacy behavior (allow all). Use for + // test harnesses, batch scripts, and environments where `ps` walks the parent + // chain to a different, unrelated Claude process (race / multi-session hosts). + if (process.env.OPC_DISABLE_OWNERSHIP === "1") { + return { decision: "OWNER", reason: "ownership check disabled via OPC_DISABLE_OWNERSHIP=1" }; + } + // Legacy loop (pre-ownership) — no stamp to enforce. if (!owner || owner.claude_pid == null) { return { decision: "OWNER", reason: "no ownership stamp — legacy loop" };