fix(council): let the Claude Code session id win over --session
A claim made with --session <name> stored the name, but the PreToolUse gate compares the hook payload's session_id, so the gate refused every edit of the claiming session and a later release without the flag exited 3. caller() now takes CLAUDE_CODE_SESSION_ID whenever it is set and falls back to --session only for other runtimes. test_council covers a claim and release with --session under a Claude Code session (negative control against the old caller fails). Claims made with --session before this fix go stale after an hour without a heartbeat and can then be taken over. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e2157998d7
commit
66e21dcd5c
2 files changed
+9
-3
No files matched your search
@@ -4,7 +4,8 @@
|
|||||||
Environment:
|
Environment:
|
||||||
AGENTS_COUNCIL_ROOT: council directory shared by every agent on the host;
|
AGENTS_COUNCIL_ROOT: council directory shared by every agent on the host;
|
||||||
/tmp/agents-council unless set, because all agents must agree on it.
|
/tmp/agents-council unless set, because all agents must agree on it.
|
||||||
CLAUDE_CODE_SESSION_ID: the caller's session; other runtimes pass --session.
|
CLAUDE_CODE_SESSION_ID: the caller's session, taken over --session; other
|
||||||
|
runtimes pass --session.
|
||||||
|
|
||||||
Exit codes:
|
Exit codes:
|
||||||
0 done, 2 usage error, 3 office held by a live session,
|
0 done, 2 usage error, 3 office held by a live session,
|
||||||
@@ -46,11 +47,11 @@ def live_sessions() -> dict[str, str | None]:
|
|||||||
|
|
||||||
|
|
||||||
def caller(session: str | None) -> tuple[str, str]:
|
def caller(session: str | None) -> tuple[str, str]:
|
||||||
if session:
|
|
||||||
return session, "other"
|
|
||||||
env = os.environ.get("CLAUDE_CODE_SESSION_ID")
|
env = os.environ.get("CLAUDE_CODE_SESSION_ID")
|
||||||
if env:
|
if env:
|
||||||
return env, "claude-code"
|
return env, "claude-code"
|
||||||
|
if session:
|
||||||
|
return session, "other"
|
||||||
print("council: no session id; pass --session", file=sys.stderr)
|
print("council: no session id; pass --session", file=sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ class TestCouncil(unittest.TestCase):
|
|||||||
self._run("s1", "claim", "work/x", "--work", "w")
|
self._run("s1", "claim", "work/x", "--work", "w")
|
||||||
self.assertEqual(self._run("s2", "takeover", "work/x", "--work", "w").returncode, 3)
|
self.assertEqual(self._run("s2", "takeover", "work/x", "--work", "w").returncode, 3)
|
||||||
|
|
||||||
|
def test_session_flag_cannot_hide_the_claude_code_session(self):
|
||||||
|
self._run("s1", "claim", "work/x", "--work", "w", "--session", "One")
|
||||||
|
self.assertIn('"session": "s1"', (self.root / "work/x/claim.json").read_text())
|
||||||
|
self.assertEqual(self._run("s1", "release", "work/x", "--session", "One").returncode, 0)
|
||||||
|
|
||||||
def test_only_the_holder_releases(self):
|
def test_only_the_holder_releases(self):
|
||||||
self._run("s1", "claim", "work/x", "--work", "w")
|
self._run("s1", "claim", "work/x", "--work", "w")
|
||||||
self.assertEqual(self._run("s2", "release", "work/x").returncode, 3)
|
self.assertEqual(self._run("s2", "release", "work/x").returncode, 3)
|
||||||
|
|||||||
Reference in new issue
Block a user