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:
1 parent
ff8b381520
commit
feccfd5e03
19 files changed
+1128
-338
No files matched your search
@@ -676,6 +676,7 @@ class LoopRunner:
|
||||
if status == "completed":
|
||||
if self.verbose:
|
||||
log_success(f"Job '{job_id}' completed successfully.")
|
||||
self._autopin(job_id)
|
||||
return True
|
||||
elif status in ("error", "cancelled"):
|
||||
log_error(f"Job '{job_id}' finished with status '{status}'.")
|
||||
@@ -686,6 +687,23 @@ class LoopRunner:
|
||||
log_error(f"Job '{job_id}' timed out after {max_wait}s.")
|
||||
return False
|
||||
|
||||
def _autopin(self, job_id: str) -> None:
|
||||
"""Issue #40 recovery path: the handler the wrapper already ran for this
|
||||
job (one job/token/attempt is handled once), run like the wrapper from
|
||||
the control root's skills - lib_py.loop imports no sibling but
|
||||
delegation (G9). Never fails the wait; the handler reports on stderr."""
|
||||
skills = self.workspace_root / ".agents" / "skills"
|
||||
if not (skills / "lib_py" / "agents" / "autopin.py").exists():
|
||||
skills = Path(__file__).resolve().parents[2]
|
||||
yaml_path = os.environ.get("AGENT_SESSIONS_YAML") or str(self.mam_dir / "agent-sessions.yaml")
|
||||
env = dict(os.environ)
|
||||
env["PYTHONPATH"] = str(skills) + (os.pathsep + env["PYTHONPATH"] if env.get("PYTHONPATH") else "")
|
||||
try:
|
||||
subprocess.run([sys.executable, "-m", "lib_py.agents.autopin", "--registry-dir", str(self.jobs_dir),
|
||||
"--job", job_id, "--yaml", yaml_path], env=env, timeout=300)
|
||||
except Exception as exc:
|
||||
log_warn(f"autopin for job '{job_id}' failed: {exc}")
|
||||
|
||||
def _relay_while_submitting(self, label: str, since: str, stop: threading.Event) -> None:
|
||||
"""Issue #30 (review R2): the wrapper runs until its job ends and its
|
||||
output is captured, so a blocked agent would only reach the loop log
|
||||
|
||||
Reference in new issue
Block a user