Repository navigation
fix(session-flow): classify automated, Desktop and orphaned sessions in audit-sessions #6076
Description
Part of #6074
Paths below are relative to plugins/session-flow/skills/audit-sessions/ unless they start with plugins/. Line numbers are on origin/main 9671ece.
Problem
Four counting and population defects:
E4. Automated and sandbox sessions sit in the baseline and the medians (medium). scripts/collect.py:779 ingests every project (for main in sorted(root.glob("*/*.jsonl")):). scripts/sweep.py:119-120 takes medians over every record, and drift runs before the scope and window filters (sweep.py:332-341). Neither script reads the transcript's entrypoint field.
E6. tools.interrupts flags any single occurrence (low). reference/sweep-rules.json:77-86 sets the threshold to 0 and scripts/sweep.py:131 flags v > threshold, so one interrupt is a finding. plugins/session-flow/scripts/transcript_reader.py:69 matches both [Request interrupted by user] and ... for tool use]; collect.py:364-374 counts it, and one user rejection can also land in tools.denials and tools.user_rejected.
E7. Claude Desktop prompts typed by the person are not counted as typed turns (low). transcript_reader.py:188-190 returns False when promptSource is set and not in {"typed", "queued"} (:70), even after origin.kind is human (:185-187). Desktop records carry promptSource: sdk. The injected-prefix list (:58, which includes <system-reminder>) then also rejects a Desktop prompt that begins with a reminder block.
I6. A set-aside orphaned transcript would be ingested as a phantom session (low, latent). collect.py:779 globs */*.jsonl and :638 uses main.stem as the session id, so <id>.orphaned-<timestamp>-<suffix>.jsonl becomes a second session.
Evidence
Verified this pass: the lines above at 9671ece.
Measured by the audit on one Windows 11 machine, 2026-10-03 (not re-measured here):
- E4: 245 of 764 sessions are automated (120 in temp sandboxes, 125 headless
entrypoint: sdk-clioutside temp); none has a typed turn. Pooled medians:tokens.main23,820,tokens.sub0,time.active_s0,human.turns1. Interactive-only medians: 79,380, 16,740, 1,174 and 2.promptSourcecannot classify sessions, because Desktop typed prompts carrysdk(E7). The monitoring-usage docs listapp.entrypoint("How the session was launched, such as cli, sdk-cli, ..."); the transcript key itself is undocumented. - E6: 81 sessions flagged; 57 of them have exactly one interrupt. The built-in
/insightscounts the same markers (agreed in 197 of 200 joined sessions), so the operator chose to keep the definition and change only the threshold. The store's own p90 is 1; threshold 1 cuts 81 flagged sessions to 24. - E7: 7 user records in 4 Desktop transcripts carry
origin.kind: humanwithpromptSource: sdk(example text: "retry, keep using sonnet"). Without stripping a leading<system-reminder>block only 5 of the 7 are recovered. No CLI typed or queued record starts with a reminder (0 of 2,337), so CLI counts do not change. Desktop "runs the same underlying engine" per the desktop docs. - I6: reproduced on a scratch sandbox with the installed scripts:
<id>.jsonlplus<id>.orphaned-<timestamp>-<suffix>.jsonlgave 2 records for 1 real session. https://code.claude.com/docs/en/claude-directory.md describes the file: "A previous transcript for the session that Claude Code set aside instead of overwriting or deleting it. It doesn't appear in the session picker". None existed on that machine.
Proposed approach
E4:
- Store every
entrypointvalue seen on each record (ascc_versionsis stored, collect.py:643); a record whose transcript is gone is classed unknown. Add a census canary for the undocumented key. A cheaper first step needing no re-collect: classify temp-sandbox sessions from the storedcwd. - Compute medians and findings over interactive sessions only and print how many automated sessions were set aside. Do not add an
--include-automatedflag. Optional: report the automated population as its own table.
- Known limit, not solved: real work run headless is classed
sdk-cli.
E6: change the tools.interrupts threshold from 0 to 1 and update its basis. The maintainer confirms the value. Not recommended (operator decision): counting only the bare interrupt form, which would diverge from /insights.
E7: in is_typed_turn, when origin.kind is human, accept the record regardless of promptSource, and strip one leading <system-reminder>...</system-reminder> block before the injected-prefix test. /session-flow:retro shares this reader and benefits.
I6: at collect.py:779, skip a transcript whose stem contains .orphaned-. Do not test for a UUID-shaped id (fixtures use ids such as gold-0001). Optional: a skip count in the collect summary.
Files: scripts/collect.py, scripts/sweep.py, scripts/census.py, reference/canaries.json, reference/sweep-rules.json, plugins/session-flow/scripts/transcript_reader.py, tests under scripts/tests/ (test_collect.py, test_sweep.py, test_transcript_reader.py), CHANGELOG and plugin.json.
Acceptance criteria
- Store records carry the transcript's
entrypointvalue(s), or an explicit unknown. - A fixture store with one
clisession and onesdk-clisession yields medians from theclisession only and reports 1 session set aside. - No flag outside SKILL.md's accepted argument set (SKILL.md:25-27) is added.
- A session with exactly one interrupt produces no
tools.interruptsfinding; a session with two produces one. - A Desktop fixture user record with
origin.kind: humanandpromptSource: sdkcounts as a typed turn, including when it starts with a<system-reminder>block. - The existing test_transcript_reader.py cases for CLI records pass unchanged.
- A fixture project holding
<id>.jsonland<id>.orphaned-<ts>-<suffix>.jsonlyields exactly one store record.
Constraints and gotchas
transcript_reader.pyis shared with/session-flow:retro, and feat(session-flow): teach the transcript reader eight current record types #5931 (eight new record types) edits the same file; coordinate or rebase.- New store fields go within v1 (store-layout.md:58); do not redefine existing keys.
- The baseline child computes its recheck-trigger p90 over the interactive population defined here, so land E4 first.
- Version bump and CHANGELOG line per PR (AGENTS.md:20-22).
Context
Source: local handoff item 20261003-070338-session-flow-audit-sessions-accuracy-and-coverage.md (findings E4, E6, E7, I6). Related: #5931, #5818 (retro parser counting).
Activity
fix(session-flow): purge stored records of set-aside transcripts on c…