Skip to content

Arch violation: reconcileAndEnrich() infers stopped status from adapter absence instead of probing truth #113

@c-h-

Description

@c-h-

What state is duplicated

SessionTracker.reconcileAndEnrich() in session-tracker.ts implements a complex merge algorithm that combines adapter discover() results with daemon-tracked sessions. When a daemon-launched session is NOT returned by adapter.discover(), and the adapter succeeded, and the grace period has passed — the daemon marks the session as stopped in its own state.

// Session disappeared from adapter results — mark stopped
this.state.setSession(id, {
  ...record,
  status: 'stopped',
  stoppedAt: new Date().toISOString(),
});

Where is the ground truth?

Process liveness (kill(pid, 0)). A session is running if and only if its process is alive. Absence from discover() results could mean:

  • The adapter's scan had a race condition (session file not yet written)
  • The adapter's file scanning missed it (index not updated)
  • The process genuinely died

How does it desync?

  1. Discovery race conditions: A session can be alive but not yet indexed by the adapter. discover() scans files — if the session JSONL/JSON hasn't been flushed to disk yet, it won't appear.
  2. Adapter-specific quirks: Claude Code's history.jsonl may not be updated instantly. OpenCode's session JSON files are written asynchronously.
  3. False negative cascade: Once marked stopped in daemon state, the session stays stopped even if the next discover() finds it — because reconcileAndEnrich only checks daemon-tracked sessions with status running/idle/pending.

User-visible symptom

Sessions briefly (or permanently) reported as "stopped" during adapter indexing gaps, despite the actual process being alive.

Proposed fix

Replace the "absence = stopped" logic with explicit PID liveness checks:

  1. If a daemon-tracked session isn't in discover() results, check kill(record.pid, 0) before marking stopped
  2. Only mark stopped if the PID is confirmed dead
  3. If PID is alive but adapter doesn't return it, keep reporting it as running (trust PID over discover scan)

This makes PID the single source of truth for "is it running?" rather than adapter file scanning.

Related: #110, #111

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions