Skip to content

fix(agent-history): ignore sessions untouched before agent first seen - #3

Closed
dorokuma wants to merge 4 commits into
ryonakae:mainfrom
dorokuma:fix/discovery-stale-session-pollution
Closed

dorokuma wants to merge 4 commits into
ryonakae:mainfrom
dorokuma:fix/discovery-stale-session-pollution

Conversation

@dorokuma

@dorokuma dorokuma commented Aug 20, 2026 •

Copy link
Copy Markdown

What & why

When a pane agent is first observed, its authoritative session ref (agent_session) may not have landed yet, causing history resolution to fall back to discoverAgentHistory. Discovery scans candidate session roots and ranks them by cwd and mtime.

Previously, if an unrelated stale session shared the pane's cwd, discovery would pick it as the best candidate. That stale ref would then be persisted (discovered_file) and used for status events and wake summaries, misattributing output from another run.

Fix principle

Fail-closed: better to report no history than wrong or misattributed history.

  • Strict cwd matching: Only candidates with exact candidate.cwd === cwd are considered. If cwd is missing or does not match, discovery fails closed and returns null without falling back to other directories.
  • Strict recency enforcement (no grace window): Drop candidates whose mtime is older than firstSeenAtMs (candidate.mtimeMs < firstSeenAtMs). The previous 10-minute grace window (DISCOVERY_RECENCY_GRACE_MS) has been removed in favor of strict cut-off.
  • Accurate adoption semantics (isAdoption):
    • isAdoption is only set to true when Shepherd initially discovers an already-running agent without prior tracking in Shepherd DB, exempting only that initial take-over run from recency cutoffs.
    • Once a new run starts (e.g. done -> non-done status transition, pane revision decrement, agent change, or cwd change), first_seen_at is reset to the current timestamp, isAdoption transitions to false, and agent_session_hint_json is cleared to null.
    • Subsequent runs on the same terminal strictly enforce recency filtering.
  • New run detection & hint invalidation (AgentStore & AgentIndexService):
    • AgentStore.updateStatus and replaceForSession clear agent_session_hint_json and reset is_adoption = 0 on done -> non-done transitions.
    • sameIdentity includes firstSeenAt and isAdoption, ensuring status-driven new runs trigger identityChanged and force fresh discovery.
  • Unified preferred ref validation (isValidPreferredRef):
    • File refs (Codex, Claude, Pi, Gemini, OpenCode) must prove ownership of the target cwd (via readCandidateCwd or .project_root) and pass recency checks before reuse; unproven or mismatched refs fail closed and fall back to discovery.
    • Leftover kind: "agent_session" refs (when input.agentSession is null or does not match) are no longer unconditionally allowed and must pass cwd and recency validation.
    • Fresh authoritative session refs matching input.agentSession remain trusted.
  • OpenCode candidate integration: Refactored OpenCode discovery to participate as Candidate[] in the same candidate filtering (cwd, firstSeenAtMs) and mtime ranking.

Testing

  • Key test scenarios covered:
    1. Drops recently completed same-cwd sessions that predate firstSeenAtMs.
    2. Does not fall back to different-cwd candidates when cwd-matching candidate is missing or stale.
    3. Allows adoption of an already-running agent whose session predates firstSeenAtMs (isAdoption: true).
    4. Keeps candidates whose mtime is at or after firstSeenAtMs.
    5. Enforces recency and supports adoption for OpenCode discovery (opencode-sqlite).
  • Production wiring & integration coverage:
    • Production wiring verification across all three isAdoption lifecycle states (initial adoption, live continuation, and subsequent new run).
    • Status event new run (pane.agent_status_changed done -> working) clearing session hint and forcing discovery.
    • Rejection of preferred file refs with mismatched or unproven cwd.
    • Rejection of leftover stale agent_session refs when input.agentSession is null.
    • Reset of first_seen_at and session hint clearing on terminal reuse across new runs in AgentStore.
    • Rejection of stale persisted discovered_file refs via isValidPreferredRef with discovery fallback.
    • Preservation of authoritative agent_session refs matching input.agentSession.
  • Full test suite: 30 test files, 243 passing tests (pnpm typecheck and pnpm test clean).

When a pane agent is first observed, its authoritative session ref
(herdr detection or shepherd-pi registration) may not have landed
yet, so history resolution falls back to cwd-based discovery.
Discovery scans fixed roots (~/.pi/agent/sessions and peers) and
ranks by cwd match and mtime, which can select an unrelated stale
session that happens to share the cwd. That session's content is
then persisted into status events and delivered to the
orchestrator's wake messages, misattributing another run's output.

A session file untouched since before the agent's first_seen_at
cannot be that agent's live session, so discovery now drops
candidates older than first_seen_at (10 min grace) and reports no
history rather than a guessed one.

@ryonakae ryonakae left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for investigating the stale-session attribution issue. The problem is real, and filtering candidates by recency is a useful direction.

The current implementation still has correctness gaps that should be addressed before merging:

  1. A previous session from the same cwd remains eligible for ten minutes, so a recently completed run can still be attributed to a newly started agent. After stale cwd-matching candidates are removed, a newer candidate from a different cwd may also be selected.

  2. firstSeenAt is Shepherd's first observation time, not necessarily the agent process start time. When Shepherd first observes an already-running agent, valid history that has not been updated for ten minutes can be rejected. Conversely, first_seen_at can be retained when a stable terminal is reused, making the filter ineffective for a later run.

  3. OpenCode cwd discovery and previously persisted discovered_file refs bypass the new recency check.

Please define a discovery rule that fails closed when candidate ownership cannot be established, and add tests for recently completed same-cwd sessions, different-cwd candidates, adoption of an already-running agent, OpenCode, and persisted discovered refs.

I agree with the goal of the PR, but I don't think the current filter is strong enough to prevent the misattribution it is intended to fix. Happy to re-review after these cases are addressed.

@dorokuma

Copy link
Copy Markdown
Author

Closing as stale — this work has moved to a separate repository.

@dorokuma dorokuma closed this Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants