fix(agents): pin post-discovered session ids on a verified completed; agy never takes another workspace's or session's conversation (Issue #40)

- BaseAgentAdapter: id_lifecycle (pre_assigned: claude, grok / post_discovered)
  and capture_active_session_id - exactly one verified candidate newer than the
  session that no other row records, else a reason (no-candidate, ambiguous, ...).
- agy: verify_artifact (discover) rejects an id last_conversations.json maps to
  another workspace and always excludes ids other rows record (a cache hit no
  longer bypasses it). capture uses the cache only as a proposal: the
  conversation's presence lock or DB must be held by the row's pane process or
  a descendant (lsof/ps); anything unprovable is not pinned.
- lib_py/agents/autopin.py: handle_completed, run by the side that verified the
  job's completed - the delegate-job wrapper (direct), create --onboard and the
  loop's wait_for_job (recovery, as a subprocess: G9). Local herdr jobs only;
  one (job, dispatch token, attempt) is handled once (job-scoped flock + marker
  in the job dir); writes go through atomic_dump_yaml; a pin records
  session_id_source=discovered / session_id_verified=true; a failure leaves the
  reason on the row and never changes the job's result.
- reconcile.sh drift C for agy, hermes, opencode and codex calls the same
  capture (the agy *.db glob is gone); _pin_and_verify_resume uses pin_row.
- Tests: tests/test_issue40_auto_pin.py; issue64 :407 now expects False; the
  static reconcile-source checks follow the move to the adapters; the two tier2
  drift-C component tests create with --no-onboard (onboarding now pins).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-10-20 23:40:03 +09:00
1 parent ff8b381520
commit feccfd5e03
19 files changed
+1128 -338

No files matched your search

+11 -10
View File
@@ -230,10 +230,11 @@ def test_status_and_reconcile_static_ro_connect_usage():
assert status_text.count("_ro_connect(db)") == 2
assert "sqlite3.connect" not in status_text
# Issue #40: drift C reads the agents' databases only through the adapters
# (lib_py.agents.autopin.capture), which use _ro_connect.
reconcile_text = (SKILLS / "multi-agent-mux-monitor" / "scripts" / "reconcile.sh").read_text()
assert reconcile_text.count("_ro_connect(hdb)") == 1
assert reconcile_text.count("_ro_connect(odb)") == 1
assert reconcile_text.count("_ro_connect(state_db)") == 1
assert "autopin.capture(s, agent, d, home_dir=home)" in reconcile_text
assert "_ro_connect" not in reconcile_text
assert "sqlite3.connect" not in reconcile_text
@@ -370,12 +371,11 @@ d['herdr_sessions'] = [
# 4. Cross-workspace agy DB sidecar regression test (Task 1 root-cause)
# ---------------------------------------------------------------------------
def test_unpinned_agy_reconcile_leaves_other_workspace_clean_db_without_sidecars(tmp_path):
"""When workspace A has an unpinned running agy session, reconcile.sh's
drift-C scan iterates every `*.db` in `~/.gemini/antigravity-cli/conversations/`
and calls `verify_session_uuid(ws_a, 'agy', uuid, ..., mode='discover')`.
Even when `last_conversations.json` maps `uuid_b` to another workspace `ws_b`,
`verify_artifact` opens `uuid_b.db` to query `SELECT count(*) FROM steps`;
with `_ro_connect`, no `-wal` or `-shm` sidecar is created beside `uuid_b.db`."""
"""`verify_session_uuid(ws_a, 'agy', uuid_b, ..., mode='discover')` for an
unpinned running agy session in workspace A leaves no `-wal` or `-shm`
sidecar beside another workspace's `uuid_b.db` (reconcile.sh's drift C
used to call it for every `*.db` in the conversations folder). Since Issue
#40 an id `last_conversations.json` maps to another workspace is rejected."""
home = tmp_path / "home"
ws_a = tmp_path / "ws_a"
ws_b = tmp_path / "ws_b"
@@ -404,5 +404,6 @@ def test_unpinned_agy_reconcile_leaves_other_workspace_clean_db_without_sidecars
"pane": {"cwd": str(ws_a), "cmd": "agy"},
"_sibling_claimed_uuids": [],
}
assert verify_session_uuid(str(ws_a), "agy", uuid_b, row=row_a, home_dir=str(home), mode="discover") is True
# Issue #40: an id the cache maps to another workspace is rejected
assert verify_session_uuid(str(ws_a), "agy", uuid_b, row=row_a, home_dir=str(home), mode="discover") is False
assert sorted(os.listdir(db_b.parent)) == [f"{uuid_b}.db"]