Skip to content

Ask the process, not the transcript, whether an agent is still running - #5564

Open
vivekchand wants to merge 11 commits into
mainfrom
verdict-liveness
Open

Ask the process, not the transcript, whether an agent is still running#5564
vivekchand wants to merge 11 commits into
mainfrom
verdict-liveness

Conversation

@vivekchand

Copy link
Copy Markdown
Owner

No-PRD: field-reported UI accuracy bug, found and reproduced on a live node.

The report

A user opened the Trail page for claude_code:9a3e3302-6600-4245-9334-84909cd9c3d9 and asked: are we really accurate in saying still running?

No.

Last event in the transcript 02:34:55
Outcome classified 02:35:34 — 39s later
Still reading "Still running" at 12:14
Transcript file 38 lines, mtime 02:34, never grew again
~/.claude/sessions/<pid>.json absent — no live pid claims it

Measured across that node's store at the time: 43 sessions were labelled ongoing and 3 of them were. 21 were finished sub-agents, 6 were dead terminals, 13 were parked at a prompt. It failed in the other direction too — 12 live claude processes carried a "Finished" badge, 10 with a non-null ended_at.

Why

The label was decided by recency. classify_session returned ongoing when no terminal marker existed and an event had landed within 5 minutes. Recency is not liveness: it cannot separate an agent mid-turn from a terminal closed a second after the last reply, and it reads a long tool call as an ending.

The label was stamped once and never revisited. ongoing is the only label whose truth decays, and it was the one label nothing re-ran — reclassify_session_outcome fires on a session.ended event, which the family runtimes never emit. The first label a Claude Code session received was also its last.

The fix

process_control.session_live_state() reads the per-pid record Claude Code already publishes and returns busy / idle / dead / unknown in 4.5µs, off the existing 2s memo. unknown stays distinct from dead — a runtime we cannot probe keeps the old heuristic rather than being retired.

idle gets its own verdict instead of being folded into "Still running". A process parked at its prompt is waiting for a person; saying "running" sends someone to check on an agent that is waiting on them.

probe verdict confidence
busy Still running 0.95
idle Waiting on you 0.95
dead classified from the transcript as before
unknown recent-activity heuristic 0.6

_is_stale_classification now re-resolves the two process-shaped labels on a 10s TTL, under the 20s read cache that bounds the cost. Settled labels still never re-run — only live sessions keep re-resolving, and they are few by definition.

Two traps found while verifying

  • An absent ~/.claude/sessions is "cannot see", not "nothing is running". claude_code_session_map returns {} for both. Caught by pointing the probe at an empty HOME and watching five busy sessions flip to Finished — which is what a container with no mount, or the hosted dashboard, would have done to every session.
  • The id prefix beats agent_type. The sessions table stamps agent_type "openclaw" on rows whose id is claude_code:<uuid> — 1849 of them on this node. Trusting the argument would have left every family row unprobeable and fixed nothing.

Verified

Booted from this branch against the real machine's live processes:

  • 9a3e3302 (dead, the reported session) → Finished
  • 3 busy sessions → Still running, 95%
  • 2 idle sessions → Waiting on you, 95%

Tests

tests/test_outcome_classifier.py ran in no CI job — which is how its seeds came to be hard-coded 2026-05-17, inside the endpoint's ?window=30d when written and outside it three months later, asserting 0 == 3 for reasons unrelated to outcomes. Seeds are anchored to now; that file plus test_outcome_trend, test_otel_export_sessions_shape and the new test_session_live_state are added to the CI file list.

23 new tests. Every one was checked red against the unfixed code and green after. Broad suite matches baseline exactly (31 pre-existing failures on origin/main, 31 here). Zero new lint findings.

Scope

Verdict accuracy only. No new way to affect an agent — the CLAUDE.md intervention list is unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_0154kqdA7JGLTbfsFSM3u4fB

@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Visual diff

Comparing 3179c6020954 (head) against the PR base branch.

39 of 70 comparison(s) flagged (>1% pixel diff).

View Before After Diff
desktop overview before after diff · 0.01%
desktop flow before after diff · 0.03%
desktop brain before after diff · 0.06%
desktop usage before after diff · 0.01%
desktop crons ⚠️ before after diff · 100.00%
desktop memory ⚠️ before after diff · 4.65%
desktop security ⚠️ before after diff · 100.00%
desktop subagents before after diff · 0.00%
desktop transcripts ⚠️ before after diff · 2.93%
desktop logs ⚠️ before after diff · 5.90%
desktop skills ⚠️ before after diff · 100.00%
desktop models ⚠️ before after diff · 100.00%
desktop approvals before after diff · 0.01%
desktop alerts ⚠️ before after diff · 100.00%
desktop notifications before after diff · 0.01%
desktop limits before after diff · 0.00%
desktop history before after diff · 0.27%
desktop channels ⚠️ before after diff · 100.00%
desktop harness ⚠️ before after diff · 100.00%
desktop inventory ⚠️ before after diff · 1.53%
desktop nemoclaw before after diff · 0.09%
desktop guard ⚠️ before after diff · 100.00%
desktop signals before after diff · 0.00%
desktop policy ⚠️ before after diff · 100.00%
desktop selfevolve before after diff · 0.02%
desktop swimlane ⚠️ before after diff · 2.26%
desktop tool-catalog before after diff · 0.36%
desktop tracing before after diff · 0.85%
desktop turn-anatomy ⚠️ before after diff · 2.60%
desktop version-impact ⚠️ before after diff · 100.00%
desktop context-economics ⚠️ before after diff · 2.71%
desktop agents before after diff · 0.00%
desktop evals before after diff · 0.01%
desktop bench ⚠️ before after diff · 100.00%
desktop trail ⚠️ before after diff · 1.11%
mobile overview before after diff · 0.00%
mobile flow ⚠️ before after diff · 5.90%
mobile brain ⚠️ before after diff · 100.00%
mobile usage ⚠️ before after diff · 100.00%
mobile crons ⚠️ before after diff · 3.14%
mobile memory ⚠️ before after diff · 4.64%
mobile security ⚠️ before after diff · 3.78%
mobile subagents before after diff · 0.02%
mobile transcripts ⚠️ before after diff · 100.00%
mobile logs before after diff · 0.02%
mobile skills before after diff · 0.56%
mobile models before after diff · 0.00%
mobile approvals ⚠️ before after diff · 100.00%
mobile alerts before after diff · 0.58%
mobile notifications ⚠️ before after diff · 100.00%
mobile limits before after diff · 0.02%
mobile history ⚠️ before after diff · 2.09%
mobile channels ⚠️ before after diff · 1.61%
mobile harness ⚠️ before after diff · 100.00%
mobile inventory before after diff · 0.02%
mobile nemoclaw ⚠️ before after diff · 100.00%
mobile guard ⚠️ before after diff · 1.67%
mobile signals ⚠️ before after diff · 100.00%
mobile policy before after diff · 0.02%
mobile selfevolve ⚠️ before after diff · 100.00%
mobile swimlane before after diff · 0.00%
mobile tool-catalog before after diff · 0.06%
mobile tracing ⚠️ before after diff · 3.33%
mobile turn-anatomy before after diff · 0.12%
mobile version-impact ⚠️ before after diff · 3.28%
mobile context-economics before after diff · 0.01%
mobile agents before after diff · 0.01%
mobile evals before after diff · 0.00%
mobile bench ⚠️ before after diff · 100.00%
mobile trail ⚠️ before after diff · 100.00%

Folder: 3179c6020954. Full PNGs also attached as a workflow artefact.

Generated by visual-diff bot. Pixel diffs >1% flagged; eyeball the table before merging. This check is non-blocking — fail = bot bug, not a code problem.

Copy link
Copy Markdown
Owner Author

🔧 auto-fixed: two MOAT Verifier failures

test_age_alone_never_claims_waiting_on_you — the waiting age-bucket count was rendered as "N waiting on you" in two places (app.js:2568 and the _cmOutcomeChip map). Neither has process-state evidence — they only know the last event was >2 minutes ago. Changed both to "gone quiet" / "Gone quiet" to match every other surface that renders the same bucket (cm-live-group, inv-doing-idle, hero sub-line).

test_reclassify_retires_a_dead_session_that_reads_as_ongoing — the test expected LIVE_DEAD when no per-pid record exists, but session_live_state returns None when ~/.claude/sessions is absent (intentional: "cannot see" ≠ "dead" in a container). Fixed by creating an empty sessions dir via CLAUDE_CONFIG_DIR monkeypatch so the probe can see the directory and correctly report absence as dead.


Generated by Claude Code

@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 3 potential drift finding(s)

1. Requirement: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:66

The code implements a new "waiting" outcome label (line 66) that represents sessions idle at prompt, but this outcome category and its semantics are not documented in the requirements. The requirement describes "ongoing" sessions but does not define "waiting" as a distinct outcome or explain when a process is considered "waiting" vs "busy".

2. Blueprint: Runtime and Session Observability

File: clawmetry/process_control.py:1800

The code implements a new session_live_state() function (line 1800+) that probes actual process state (busy/idle/dead) instead of inferring from transcript recency. This is a new mechanism for determining session liveness not described in the blueprint, which focuses on session timelines and trace context but does not specify how to determine whether a session is actively running.

3. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:617

The code introduces time-dependent outcome re-classification with a 10-second TTL for "ongoing" and "waiting" labels (implemented via _LIVE_LABEL_TTL_MS and _is_stale_classification), but this TTL-based staleness mechanism and the concept of "time-dependent outcomes" that decay with clock time are not documented in the blueprint.

OUTCOME_ESCALATED = "escalated"
OUTCOME_COGNITIVE_LOOP = "cognitive_loop"
OUTCOME_ONGOING = "ongoing"
OUTCOME_WAITING = "waiting"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Requirement: Runtime and Session Observability

The code implements a new "waiting" outcome label (line 66) that represents sessions idle at prompt, but this outcome category and its semantics are not documented in the requirements. The requirement describes "ongoing" sessions but does not define "waiting" as a distinct outcome or explain when a process is considered "waiting" vs "busy".

return out


# What a live process is actually doing, as the runtime itself reports it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new session_live_state() function (line 1800+) that probes actual process state (busy/idle/dead) instead of inferring from transcript recency. This is a new mechanism for determining session liveness not described in the blueprint, which focuses on session timelines and trace context but does not specify how to determine whether a session is actively running.

github-actions Bot pushed a commit that referenced this pull request Sep 5, 2026

Copy link
Copy Markdown
Owner Author

Blocked on required review — no code changes needed. All CI checks are green (MOAT Verifier 72 tests passing).


Generated by Claude Code

@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 3 potential drift finding(s)

1. Requirement: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:66

The code implements a new "waiting" outcome label (line 66) representing sessions idle at their prompt, but this outcome category and its semantics are not documented in the requirements. The requirement describes "ongoing" sessions but does not define "waiting" as a distinct outcome or explain when a process is considered "waiting" vs "busy".

2. Blueprint: Runtime and Session Observability

File: clawmetry/process_control.py:1852

The code implements a new session_live_state() function that probes actual process state (busy/idle/dead) instead of inferring from transcript recency, but this mechanism for determining session liveness is not described in the blueprint. The blueprint does not specify how to determine whether a session is actively running or waiting.

3. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:617

The code introduces time-dependent outcome re-classification with a 10-second TTL for "ongoing" and "waiting" labels (implemented via _LIVE_LABEL_TTL_MS and _is_stale_classification), but this TTL-based staleness mechanism and the concept of outcomes that decay with clock time are not documented in the blueprint.

OUTCOME_ESCALATED = "escalated"
OUTCOME_COGNITIVE_LOOP = "cognitive_loop"
OUTCOME_ONGOING = "ongoing"
OUTCOME_WAITING = "waiting"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Requirement: Runtime and Session Observability

The code implements a new "waiting" outcome label (line 66) representing sessions idle at their prompt, but this outcome category and its semantics are not documented in the requirements. The requirement describes "ongoing" sessions but does not define "waiting" as a distinct outcome or explain when a process is considered "waiting" vs "busy".

return out


# What a live process is actually doing, as the runtime itself reports it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new session_live_state() function that probes actual process state (busy/idle/dead) instead of inferring from transcript recency, but this mechanism for determining session liveness is not described in the blueprint. The blueprint does not specify how to determine whether a session is actively running or waiting.

``escalated``.
now: clock override for tests. Defaults to ``time.time()``.
live: what the caller's process probe says about this session right
now — ``"busy"``, ``"idle"``, ``"dead"``, or ``None`` for "this node

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code introduces time-dependent outcome re-classification with a 10-second TTL for "ongoing" and "waiting" labels (implemented via _LIVE_LABEL_TTL_MS and _is_stale_classification), but this TTL-based staleness mechanism and the concept of outcomes that decay with clock time are not documented in the blueprint.

github-actions Bot pushed a commit that referenced this pull request Sep 5, 2026

Copy link
Copy Markdown
Owner Author

blocked on author decision — skipping (auto-mergeability sweep): Drift Bot found 3 blueprint/requirement gaps (new "waiting" outcome, session_live_state() mechanism, TTL-based re-classification) that require updating factory.8090.ai records before the E2E Gate can pass.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Automated PR sweep — 2026-09-05

Investigated this PR as part of a scheduled mergeability sweep. All CI checks are green. The sole blocker is:

  • drift-bot: failure8090-software-factory[bot] found 3 drift findings (code not backed by a product blueprint):
    1. clawmetry/outcome_classifier.py:66 — "waiting" outcome label not in requirements
    2. clawmetry/process_control.py:1800session_live_state() not in blueprint
    3. clawmetry/outcome_classifier.py:617 — TTL-based staleness mechanism not in blueprint

Cannot be auto-fixed by code changes. Resolving requires either updating the product blueprints at factory.8090.ai to document these behaviors, or removing the undocumented behavior from the implementation. Widening the PR is out of scope for the automated sweep.

Status: ⚠️ Partial — blocked by Drift Bot, needs blueprint update.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Automated sweep — standing-down note

This PR is blocked solely by Drift Bot (8090-software-factory[bot], drift-bot commit-status context). The E2E Gate requires Drift Bot as a mandatory leg; all other CI legs are passing.

Drift Bot found 3 blueprint-documentation findings on the current head commit — meaning this PR's diff introduces or modifies behaviour that isn't yet documented in the corresponding 8090 Software Factory blueprints. This is a documentation gap, not a code defect; no change to the PR's source files can clear it.

What's needed to unblock:

  1. Open the 8090 Software Factory blueprint(s) that cover the components changed in this PR.
  2. Add / update documentation to match the new behaviour introduced here.
  3. Drift Bot will re-scan on the next poll (usually within ~10 minutes) and flip to success once the blueprint aligns with the diff.

No further automated action can be taken from this sweep — Factory access is required.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Automated maintenance sweep — human action required

This PR is blocked by the drift-bot commit status (failure), set by the 8090 Software Factory when code behavior is not yet documented in the product blueprint at factory.8090.ai.

To unblock:

  1. Open factory.8090.ai → find the blueprint covering process control / session monitoring
  2. Add documentation for session_live_state(): a 4.5µs lookup (using the existing 2s memo) that returns busy/idle/dead/unknown based on the pid record — used by Guard to determine whether a session can be controlled
  3. Push any new commit (or empty commit: git commit --allow-empty -m "re-trigger drift-bot") to re-trigger drift-bot
  4. Once drift-bot passes, E2E Gate passes and the PR is mergeable

Generated by Claude Code

Copy link
Copy Markdown
Owner Author

blocked on author decision — skipping (auto-mergeability sweep)

All 11 non-Drift-Bot legs of the E2E Gate pass. The sole blocker is drift-bot: failure (a commit-status check posted by the 8090 Software Factory). Resolving that requires access to the factory blueprint to address the reported drift.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

blocked on author decision — skipping (auto-mergeability sweep)

The drift-bot check is failing because code changes are not documented in product blueprints at factory.8090.ai. This requires an author decision to update the product record before the E2E Gate can pass.


Generated by Claude Code

@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 3 potential drift finding(s)

1. Requirement: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:66

The code implements a new "waiting" outcome label for sessions idle at their prompt, but the requirement does not document this outcome category, its semantics, or when a session should be classified as "waiting" versus "busy".

2. Blueprint: Runtime and Session Observability

File: clawmetry/process_control.py:1852

The code implements a new session_live_state() function that probes actual process state (busy/idle/dead/unknown) to determine session liveness, but the blueprint does not describe this probe-based mechanism or how it determines whether a session is actively running versus waiting or dead.

3. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:617

The code introduces TTL-based staleness checking for "ongoing" and "waiting" outcomes (10-second TTL via _LIVE_LABEL_TTL_MS and _is_stale_classification), but the blueprint does not document this mechanism for re-resolving time-dependent outcomes or explain that these labels decay with the clock.

OUTCOME_ESCALATED = "escalated"
OUTCOME_COGNITIVE_LOOP = "cognitive_loop"
OUTCOME_ONGOING = "ongoing"
OUTCOME_WAITING = "waiting"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Requirement: Runtime and Session Observability

The code implements a new "waiting" outcome label for sessions idle at their prompt, but the requirement does not document this outcome category, its semantics, or when a session should be classified as "waiting" versus "busy".

return out


# What a live process is actually doing, as the runtime itself reports it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new session_live_state() function that probes actual process state (busy/idle/dead/unknown) to determine session liveness, but the blueprint does not describe this probe-based mechanism or how it determines whether a session is actively running versus waiting or dead.

``escalated``.
now: clock override for tests. Defaults to ``time.time()``.
live: what the caller's process probe says about this session right
now — ``"busy"``, ``"idle"``, ``"dead"``, or ``None`` for "this node

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code introduces TTL-based staleness checking for "ongoing" and "waiting" outcomes (10-second TTL via _LIVE_LABEL_TTL_MS and _is_stale_classification), but the blueprint does not document this mechanism for re-resolving time-dependent outcomes or explain that these labels decay with the clock.

github-actions Bot pushed a commit that referenced this pull request Sep 6, 2026

Copy link
Copy Markdown
Owner Author

E2E Gate rerun triggered (timing failure — gate checked required checks before individual jobs finished; all individual checks passed). CI now running. If still not green in 10 min, may need manual attention.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

✨ auto-fixed: merged current main into the branch (was behind; fresh CI triggered)


Generated by Claude Code

@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 3 potential drift finding(s)

1. Requirement: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:66

The code implements a new "waiting" outcome label for sessions idle at their prompt, but this outcome category, its semantics, and when a session should be classified as "waiting" versus "busy" are not documented in the requirements.

2. Blueprint: Runtime and Session Observability

File: clawmetry/process_control.py:1852

The code implements a new session_live_state() function that probes actual process state (busy/idle/dead/unknown) to determine session liveness, but the blueprint does not describe this probe-based mechanism or how it determines whether a session is actively running versus waiting or dead.

3. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5128

The code introduces TTL-based staleness checking for "ongoing" and "waiting" outcomes (10-second TTL via _LIVE_LABEL_TTL_MS and _is_stale_classification), but the blueprint does not document this mechanism for re-resolving time-dependent outcomes or explain that these labels decay with the clock.

OUTCOME_ESCALATED = "escalated"
OUTCOME_COGNITIVE_LOOP = "cognitive_loop"
OUTCOME_ONGOING = "ongoing"
OUTCOME_WAITING = "waiting"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Requirement: Runtime and Session Observability

The code implements a new "waiting" outcome label for sessions idle at their prompt, but this outcome category, its semantics, and when a session should be classified as "waiting" versus "busy" are not documented in the requirements.

@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 8 potential drift finding(s)

1. Blueprint: Runtime and Session Observability

File: clawmetry/process_control.py:1852

The code implements a new session_live_state() function that probes Claude Code process state (busy/idle/dead/unknown) to determine session liveness, but the blueprint does not document this probe-based mechanism or explain how it determines whether a session is actively running versus waiting or dead.

2. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:66

The code implements a new OUTCOME_WAITING label for sessions idle at their prompt, but the blueprint does not document this outcome category, its semantics, or the conditions for classifying a session as "waiting" versus "busy" or "ongoing".

3. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:603

The classify_session() function now accepts a new "live" parameter that supplies the caller's process probe result (busy/idle/dead/None), fundamentally changing how "ongoing" and "waiting" outcomes are determined, but this new parameter and its role in decision-making are not documented in the blueprint.

4. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5127

The code introduces TTL-based staleness checking for "ongoing" and "waiting" outcomes (10-second TTL via _LIVE_LABEL_TTL_MS and _is_stale_classification), but the blueprint does not document this mechanism for re-resolving time-dependent outcomes or explain that these labels decay with the clock rather than being stamped once.

5. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:78

The code defines new constants IN_FLIGHT_OUTCOMES and TIME_DEPENDENT_OUTCOMES that categorize "ongoing" and "waiting" as distinct outcome classes with special handling, but the blueprint does not document these outcome categories or explain that only live-process labels are time-dependent and require re-resolution.

6. Blueprint: Runtime and Session Observability

File: clawmetry/static/js/trail.js:99

The UI adds a new "Waiting on you" outcome label (purple color) distinct from "Still running" to represent sessions idle at their prompt, but this UI label, its visual treatment, and the semantics of distinguishing "running" from "waiting" are not documented in the blueprint.

7. Blueprint: Runtime and Session Observability

File: clawmetry/insights.py:303

The code now excludes the new "waiting" outcome label from the problems dashboard query (alongside "ongoing"), but this exclusion rule and the reasoning that "waiting" should be treated like "ongoing" as an in-flight state not counted as a failure are not documented in the blueprint.

8. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5280

The code calls a new _probe_session_live() wrapper that invokes session_live_state() from process_control to determine if a session is actually running, and passes this probe result to classify_session(), implementing a dependency and data flow that are not documented in the blueprint.

return out


# What a live process is actually doing, as the runtime itself reports it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new session_live_state() function that probes Claude Code process state (busy/idle/dead/unknown) to determine session liveness, but the blueprint does not document this probe-based mechanism or explain how it determines whether a session is actively running versus waiting or dead.

OUTCOME_ESCALATED = "escalated"
OUTCOME_COGNITIVE_LOOP = "cognitive_loop"
OUTCOME_ONGOING = "ongoing"
OUTCOME_WAITING = "waiting"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new OUTCOME_WAITING label for sessions idle at their prompt, but the blueprint does not document this outcome category, its semantics, or the conditions for classifying a session as "waiting" versus "busy" or "ongoing".

*,
approvals: list[dict[str, Any]] | None = None,
now: float | None = None,
live: str | None = None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The classify_session() function now accepts a new "live" parameter that supplies the caller's process probe result (busy/idle/dead/None), fundamentally changing how "ongoing" and "waiting" outcomes are determined, but this new parameter and its role in decision-making are not documented in the blueprint.

@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 8 potential drift finding(s)

1. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:66

The code implements a new OUTCOME_WAITING label for sessions idle at their prompt (waiting on user input), but the blueprint does not document this outcome category, its semantics, or the conditions for classifying a session as waiting versus ongoing.

2. Blueprint: Runtime and Session Observability

File: clawmetry/process_control.py:1852

The code implements a new session_live_state() function that probes Claude Code process state (busy/idle/dead/unknown) to determine session liveness in real-time, but the blueprint does not document this probe-based mechanism or explain how session status is determined.

3. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:603

The classify_session() function now accepts a new live parameter that supplies the caller's process probe result (busy/idle/dead/None), fundamentally changing how ongoing and waiting outcomes are determined, but this parameter and its role in decision-making are not documented in the blueprint.

4. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5318

The code introduces TTL-based staleness checking for ongoing and waiting outcomes (10-second TTL via _LIVE_LABEL_TTL_MS and _is_stale_classification), but the blueprint does not document that these labels decay with time and require re-resolution rather than being stamped once.

5. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:78

The code defines IN_FLIGHT_OUTCOMES and TIME_DEPENDENT_OUTCOMES constants categorizing ongoing and waiting as outcome classes with special handling, but the blueprint does not document these outcome categories or explain that only live-process labels are time-dependent.

6. Blueprint: Runtime and Session Observability

File: clawmetry/static/js/trail.js:99

The UI adds a new "Waiting on you" outcome label (purple color, distinct from "Still running") to represent sessions idle at their prompt, but this UI label, its visual treatment, and the semantics of distinguishing running from waiting are not documented in the blueprint.

7. Blueprint: Runtime and Session Observability

File: clawmetry/insights.py:303

The code now excludes the new waiting outcome label from the problems dashboard query (alongside ongoing), but this exclusion rule and the reasoning that waiting should be treated like ongoing as an in-flight state are not documented in the blueprint.

8. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5280

The code calls _probe_session_live() to determine if a session is actually running via process_control.session_live_state(), and passes the probe result to classify_session(), implementing a data flow dependency not documented in the blueprint.

OUTCOME_ESCALATED = "escalated"
OUTCOME_COGNITIVE_LOOP = "cognitive_loop"
OUTCOME_ONGOING = "ongoing"
OUTCOME_WAITING = "waiting"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new OUTCOME_WAITING label for sessions idle at their prompt (waiting on user input), but the blueprint does not document this outcome category, its semantics, or the conditions for classifying a session as waiting versus ongoing.

return out


# What a live process is actually doing, as the runtime itself reports it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new session_live_state() function that probes Claude Code process state (busy/idle/dead/unknown) to determine session liveness in real-time, but the blueprint does not document this probe-based mechanism or explain how session status is determined.

*,
approvals: list[dict[str, Any]] | None = None,
now: float | None = None,
live: str | None = None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The classify_session() function now accepts a new live parameter that supplies the caller's process probe result (busy/idle/dead/None), fundamentally changing how ongoing and waiting outcomes are determined, but this parameter and its role in decision-making are not documented in the blueprint.

Copy link
Copy Markdown
Owner Author

✨ auto-fixed: merged latest main into branch (was BEHIND; no conflicts)


Generated by Claude Code

@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 8 potential drift finding(s)

1. Blueprint: Runtime and Session Observability

File: clawmetry/process_control.py:1852

The code implements a new session_live_state() function that directly probes Claude Code process state (busy/idle/dead/unknown) to determine session liveness in real-time, but the blueprint does not document this probe-based mechanism or explain how session status is determined from process state.

2. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:66

The code implements a new OUTCOME_WAITING label for sessions idle at their prompt (waiting on user input), but the blueprint does not document this outcome category, its semantics, or the conditions for classifying a session as waiting versus busy or ongoing.

3. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:603

The classify_session() function now accepts a new live parameter that supplies the caller's process probe result (busy/idle/dead/None), fundamentally changing how ongoing and waiting outcomes are determined, but this parameter and its role in decision-making are not documented in the blueprint.

4. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5318

The code introduces TTL-based staleness checking for ongoing and waiting outcomes (10-second TTL via _LIVE_LABEL_TTL_MS and _is_stale_classification), but the blueprint does not document that these labels decay with time and require re-resolution rather than being stamped once.

5. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:78

The code defines IN_FLIGHT_OUTCOMES and TIME_DEPENDENT_OUTCOMES constants categorizing ongoing and waiting as outcome classes with special handling, but the blueprint does not document these outcome categories or explain that only live-process labels are time-dependent.

6. Blueprint: Runtime and Session Observability

File: clawmetry/static/js/trail.js:99

The UI adds a new "Waiting on you" outcome label (purple color, distinct from "Still running") to represent sessions idle at their prompt, but this UI label, its visual treatment, and the semantics of distinguishing running from waiting are not documented in the blueprint.

7. Blueprint: Runtime and Session Observability

File: clawmetry/insights.py:303

The code now excludes the new waiting outcome label from the problems dashboard query (alongside ongoing), but this exclusion rule and the reasoning that waiting should be treated like ongoing as an in-flight state are not documented in the blueprint.

8. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5280

The code calls _probe_session_live() to determine if a session is actually running via process_control.session_live_state(), and passes the probe result to classify_session(), implementing a data flow dependency and calling convention not documented in the blueprint.

return out


# What a live process is actually doing, as the runtime itself reports it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new session_live_state() function that directly probes Claude Code process state (busy/idle/dead/unknown) to determine session liveness in real-time, but the blueprint does not document this probe-based mechanism or explain how session status is determined from process state.

OUTCOME_ESCALATED = "escalated"
OUTCOME_COGNITIVE_LOOP = "cognitive_loop"
OUTCOME_ONGOING = "ongoing"
OUTCOME_WAITING = "waiting"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new OUTCOME_WAITING label for sessions idle at their prompt (waiting on user input), but the blueprint does not document this outcome category, its semantics, or the conditions for classifying a session as waiting versus busy or ongoing.

*,
approvals: list[dict[str, Any]] | None = None,
now: float | None = None,
live: str | None = None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The classify_session() function now accepts a new live parameter that supplies the caller's process probe result (busy/idle/dead/None), fundamentally changing how ongoing and waiting outcomes are determined, but this parameter and its role in decision-making are not documented in the blueprint.

@vivekchand vivekchand left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Test plan & review notes

Repo: vivekchand/clawmetry

What changed

  • process_control.session_live_state() probes the per-pid record Claude Code publishes (~/.claude/sessions/<pid>.json) and returns busy / idle / dead / unknown in ~4.5µs (memoised 2s).
  • A new waiting outcome (idle process, parked at prompt) is added to OUTCOME_KEYS so "Waiting on you" shows instead of "Still running" for parked sessions.
  • _is_stale_classification re-resolves the two live-process labels on a 10s TTL under the 20s read cache.
  • Four test files (test_outcome_classifier.py, test_session_live_state.py, test_outcome_trend.py, test_otel_export_sessions_shape.py) are added to the CI file list in ci.yml — they were previously not wired to any job.

Current status
mergeable_state: blocked — needs approval. 65 comments; from prior bot sweeps this appears to be a Drift Bot / E2E Gate issue, not a code failure. The PR body states 23 new tests, all red-before-green, and no new lint findings.

Smoke commands

# Fast: just the new test files
python3 -m pytest tests/test_outcome_classifier.py \
                  tests/test_session_live_state.py \
                  tests/test_outcome_trend.py \
                  tests/test_otel_export_sessions_shape.py -v

# Check the liveness probe against live processes
python3 - <<'EOF'
from clawmetry.process_control import session_live_state
import os, glob
# Point at real sessions if available
for f in glob.glob(os.path.expanduser("~/.claude/sessions/*.json"))[:3]:
    sid = "claude_code:" + os.path.basename(f).replace(".json","")
    print(sid, "->", session_live_state(sid))
EOF

# API spot-check: sessions list should show "Waiting on you" label for idle ones
curl -sS http://localhost:8900/api/sessions | python3 -m json.tool | grep -A2 '"outcome"'

What to look at visually

  • Sessions list (http://localhost:8900/api/sessions) — idle Claude Code processes should now show waiting outcome, not ongoing.
  • Trail page for a recently-finished session — should show Finished, not Still running.
  • Guard tab — session ranking by spend_at_risk should still sort correctly with waiting added to non-finished set.

Likely failure modes from the diff

  • "Cannot see" vs "nothing running": the PR body flags that an absent ~/.claude/sessions dir returns {} for both mounted and unmounted cases — in a hosted/containerised environment every Claude Code session could wrongly flip to dead. Check the guard in claude_code_session_map handles an unmounted home gracefully.
  • agent_type vs id-prefix: the id prefix (claude_code:<uuid>) must win over the agent_type column when dispatching the probe. Worth a grep: grep -n "agent_type" clawmetry/process_control.py.
  • waiting excluded from finished: cohort_compare.py, harness_bench.py, insights.py all updated — check that the cost-outcomes endpoint (/api/usage/outcomes) still reports correctly when some sessions are waiting.
  • Pre-existing 31 failures on main: the PR body notes they exist on origin/main too, so a clean run against main is the baseline, not zero.

Issue link

  • No issue number in the PR body (No-PRD: field-reported UI accuracy bug). If there's an issue for the "43 sessions labelled ongoing, 3 actually were" report, linking it (Closes #N) would auto-close on merge.

Generated by Claude Code

github-actions Bot pushed a commit that referenced this pull request Sep 9, 2026
@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 8 potential drift finding(s)

1. Blueprint: Runtime and Session Observability

File: clawmetry/process_control.py:1876

The code implements a new session_live_state() function that directly probes Claude Code process state (busy/idle/dead/unknown) to determine session liveness in real-time, but the blueprint does not document this probe-based mechanism, the three process states it returns, or how session status is determined from live process state rather than transcript activity.

2. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:66

The code implements a new OUTCOME_WAITING label for sessions idle at their prompt (waiting on user input), but the blueprint does not document this outcome category, its semantics, the conditions for classifying a session as waiting versus busy, or that waiting is displayed as "Waiting on you" in the UI.

3. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:603

The classify_session() function now accepts a new live parameter that supplies the caller's process probe result (busy/idle/dead/None), fundamentally changing how ongoing and waiting outcomes are determined and their confidence scores, but this parameter, its role in decision-making, and the confidence thresholds (0.95 for probed states vs 0.6 for time-based) are not documented in the blueprint.

4. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:78

The code defines IN_FLIGHT_OUTCOMES and TIME_DEPENDENT_OUTCOMES constants categorizing ongoing and waiting as outcome classes with special handling (stale within 10 seconds), but the blueprint does not document these outcome categories, the 10-second TTL mechanism, or that only live-process labels are time-dependent and require re-resolution.

5. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5315

The code introduces TTL-based staleness checking for ongoing and waiting outcomes via _LIVE_LABEL_TTL_MS (10 seconds) and _is_stale_classification(), which re-resolves these labels on every read instead of stamping them once, but the blueprint does not document this mechanism or that outcome labels decay with time and require continuous re-resolution.

6. Blueprint: Runtime and Session Observability

File: clawmetry/static/js/trail.js:104

The UI adds a new "Waiting on you" outcome label (purple color, distinct from "Still running") to represent sessions idle at their prompt, but the blueprint does not document this UI label, its visual treatment, the color scheme, or the semantics of distinguishing waiting from running in the outcome vocabulary.

7. Blueprint: Runtime and Session Observability

File: clawmetry/insights.py:303

The code now excludes the new waiting outcome label from the problems dashboard query (alongside ongoing), treating both as in-flight states that should not be counted as failures, but the blueprint does not document this exclusion rule or explain why waiting should be treated like ongoing in aggregation logic.

8. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5410

The code calls _probe_session_live() to determine if a session is actually running via process_control.session_live_state(), and passes the probe result to classify_session(), implementing a data flow dependency and calling convention not documented in the blueprint, including the logic that None means "cannot tell" (not dead) and that this probe must be called on every classification.

return out


# What a live process is actually doing, as the runtime itself reports it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new session_live_state() function that directly probes Claude Code process state (busy/idle/dead/unknown) to determine session liveness in real-time, but the blueprint does not document this probe-based mechanism, the three process states it returns, or how session status is determined from live process state rather than transcript activity.

OUTCOME_ESCALATED = "escalated"
OUTCOME_COGNITIVE_LOOP = "cognitive_loop"
OUTCOME_ONGOING = "ongoing"
OUTCOME_WAITING = "waiting"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new OUTCOME_WAITING label for sessions idle at their prompt (waiting on user input), but the blueprint does not document this outcome category, its semantics, the conditions for classifying a session as waiting versus busy, or that waiting is displayed as "Waiting on you" in the UI.

github-actions Bot pushed a commit that referenced this pull request Sep 10, 2026
@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 8 potential drift finding(s)

1. Blueprint: Runtime and Session Observability

File: clawmetry/process_control.py:1876

The code implements a new session_live_state() function that directly probes Claude Code process state (busy/idle/dead/unknown) to determine session liveness in real-time, but the blueprint does not document this probe-based mechanism, the three process states it returns, or how session status is determined from live process state rather than transcript activity.

2. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:66

The code implements a new OUTCOME_WAITING label for sessions idle at their prompt (waiting on user input), but the blueprint does not document this outcome category, its semantics, the conditions for classifying a session as waiting versus busy, or that waiting is displayed as "Waiting on you" in the UI.

3. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:603

The classify_session() function now accepts a new live parameter that supplies the caller's process probe result (busy/idle/dead/None), fundamentally changing how ongoing and waiting outcomes are determined and their confidence scores, but this parameter, its role in decision-making, and the confidence thresholds (0.95 for probed states vs 0.6 for time-based) are not documented in the blueprint.

4. Blueprint: Runtime and Session Observability

File: clawmetry/outcome_classifier.py:78

The code defines IN_FLIGHT_OUTCOMES and TIME_DEPENDENT_OUTCOMES constants categorizing ongoing and waiting as outcome classes with special handling (stale within 10 seconds), but the blueprint does not document these outcome categories, the 10-second TTL mechanism, or that only live-process labels are time-dependent and require re-resolution.

5. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5424

The code introduces TTL-based staleness checking for ongoing and waiting outcomes via _LIVE_LABEL_TTL_MS (10 seconds) and _is_stale_classification(), which re-resolves these labels on every read instead of stamping them once, but the blueprint does not document this mechanism or that outcome labels decay with time and require continuous re-resolution.

6. Blueprint: Runtime and Session Observability

File: clawmetry/static/js/trail.js:104

The UI adds a new "Waiting on you" outcome label (purple color, distinct from "Still running") to represent sessions idle at their prompt, but the blueprint does not document this UI label, its visual treatment, the color scheme, or the semantics of distinguishing waiting from running in the outcome vocabulary.

7. Blueprint: Runtime and Session Observability

File: clawmetry/insights.py:303

The code now excludes the new waiting outcome label from the problems dashboard query (alongside ongoing), treating both as in-flight states that should not be counted as failures, but the blueprint does not document this exclusion rule or explain why waiting should be treated like ongoing in aggregation logic.

8. Blueprint: Runtime and Session Observability

File: clawmetry/local_store.py:5414

The code calls _probe_session_live() to determine if a session is actually running via process_control.session_live_state(), and passes the probe result to classify_session(), implementing a data flow dependency and calling convention not documented in the blueprint, including the logic that None means "cannot tell" (not dead) and that this probe must be called on every classification.

return out


# What a live process is actually doing, as the runtime itself reports it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new session_live_state() function that directly probes Claude Code process state (busy/idle/dead/unknown) to determine session liveness in real-time, but the blueprint does not document this probe-based mechanism, the three process states it returns, or how session status is determined from live process state rather than transcript activity.

OUTCOME_ESCALATED = "escalated"
OUTCOME_COGNITIVE_LOOP = "cognitive_loop"
OUTCOME_ONGOING = "ongoing"
OUTCOME_WAITING = "waiting"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The code implements a new OUTCOME_WAITING label for sessions idle at their prompt (waiting on user input), but the blueprint does not document this outcome category, its semantics, the conditions for classifying a session as waiting versus busy, or that waiting is displayed as "Waiting on you" in the UI.

*,
approvals: list[dict[str, Any]] | None = None,
now: float | None = None,
live: str | None = None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Drift Bot (ClawMetry) — Blueprint: Runtime and Session Observability

The classify_session() function now accepts a new live parameter that supplies the caller's process probe result (busy/idle/dead/None), fundamentally changing how ongoing and waiting outcomes are determined and their confidence scores, but this parameter, its role in decision-making, and the confidence thresholds (0.95 for probed states vs 0.6 for time-based) are not documented in the blueprint.

github-actions Bot pushed a commit that referenced this pull request Sep 10, 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