Repository navigation
fix(conversations): an unreadable conversation file is said, not 'no prompts' (#1306) - #1434
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…prompts' (#1306) Every source (Claude, Codex, OpenCode, Pi, Grok) raises a typed ConversationPromptsUnreadable when a transcript or store is there but cannot be read or listed; only genuine absence answers []. promptFolder rethrows non-ENOENT errors; View Prompts says it in fixed words; search degrades that one row. Steering q116: discovery (stat/readdir walks) follows the same rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
290621a to
5cee073
Compare
|
Steering q116 addressed; the branch was rebuilt before any reviewer started (none had).
🤖 Generated with Claude Code |
… review a, b) existsSync read EACCES as absent (Codex known/indexed rollouts, the OpenCode database); the Codex walk skipped locked day directories; an unopenable Codex index with no rollout found; a Pi session named for the id with a damaged header; a Grok transcript without its summary; and a Claude/Codex transcript whose every line is garbage (TranscriptUnparseable) all answered []. Each is now ConversationPromptsUnreadable. isPresent / assertTreeListable tell absent from unknown; an OpenCode query failure after open is typed; a Grok read racing its writer retries twice before it is said. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1434 review c) A fresh Pi home with no .pi answers [] (the probe's missing half survived a mutation), a store path through a plain file (ENOTDIR) is absence, and the service's cause log is asserted. Plan records round 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round-1 disposition. a, b and c were all FIX-BEFORE-MERGE. Every valid finding is fixed fail-first, and each new test is red on the previous head (5cee073). a and b (major): unknown still read as "no prompts" one level down. Fixed in 0974c69.
c (major, test gap): the absent half of the Pi rule survived a mutation. It is pinned on a fresh home with no Not changed (residuals):
Declined: b4. An inaccessible unrelated Claude project aborting the fallback walk is the stated contract (unknown is never "no prompts"), and c independently called it a design stance, not a defect. Checks: 🤖 Generated with Claude Code |
…row are unreadable (#1434 verification a) The Codex locator skips a file it cannot open, so a listable tree holding the conversation's own unreadable rollout read as absent; assertTreeListable now fails on a file named for the conversation. A Pi file named for the session whose first row is not its session header is damaged, not 'another cwd'. The OpenCode typed query error and the prompt folder's cache-hit parse check are pinned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Verification (a) disposition. Verifier a was FIX-BEFORE-MERGE: two major findings and two test gaps. All four are fixed fail-first in e1e67e8, and each mutation is red.
🤖 Generated with Claude Code |
|
Verification b and c disposition at 38c1644.
B6: the verification cap is spent. Reviewer c's latest report is MERGE-READY; please manager-verify a (e1e67e8) and b (38c1644). 🤖 Generated with Claude Code |
…pts-unreadable # Conflicts: # src/main/conversations/service.system.test.ts
d58e1d0
into
integration/batch-2026-09-27-p
Real conflict: batch Q's #1434 and this PR both changed codex.ts's imports (#1434 adds ConversationPromptsUnreadable and friends from ./types.js, this PR adds LruMap). Both kept. Semantic conflict: #1434's promptsUnreadable.system.test.ts reads the private rolloutPaths as a Map (.has/.get), which this PR bounded as an LruMap. The two Codex tests now read it with peek(), which does not touch recency, so the lookup cannot change what the source evicts next. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #1306.
Problem
View Prompts (and search) on a conversation whose transcript or store is there but unreadable said "no prompts". As the issue suspected, the pattern was in every source:
catch { return [] }, and the package's resolution skips an unreadable header, so the session reads as "not found".catch { return [] }.extractPromptsFromFileturned every stat and read failure into an empty answer.[].Change
ConversationPromptsUnreadable(sources/types.ts): a typed error with a fixed message and the cause.[], because a fresh session has no transcript and View Prompts shows the live feed. A file or store that is there but unreadable throws.promptFolderis empty only for ENOENT/ENOTDIR.opencode.dbexists.statand projects walk, Codex's rollout walk and Pi's listing treat only ENOENT/ENOTDIR as absence. The Codex and Pi packages swallow their ownreaddirerrors, so each source probes its root directories first.ConversationService.promptslogs the cause in main and rethrows. Search already catches per conversation (label-only for that row).ViewPromptsModalshows "Couldn't read this conversation's prompts."Plan:
docs/plans/2026-09-27-conversation-prompts-unreadable.md.Rules added in review (round 1 and the capped verification pass)
isPresent(provider, path)treats only ENOENT/ENOTDIR as absence; EACCES and every other stat failure throw. Used for Codex's known and indexed rollout paths and for OpenCode'sopencode.db, including one under an inaccessible parent.assertTreeListablethrows on any readdir failure other than absence. It also throws on a file named for THIS conversation that the locator skipped, i.e. an unreadable own rollout in a listable tree.[].TranscriptUnparseable). This is checked on both the fresh fold and the cache hit. A file with some good lines still answers its good prompts.sessionheader for this id may mean "another cwd". A header that doesn't parse, has another type or id, orid: nullis damage.summary.json(or the reverse) is unreadable. A write still in progress is retried twice at 150 ms before it is called unreadable.ConversationPromptsUnreadable.Tests (all red on main; real files from the recorded conversation corpus, made unreadable with chmod)
promptsUnreadable.system.test.ts: Claude, Codex, OpenCode (a non-database store) and Grok throw the typed error, and a missing file stays[].pi.system.test.ts: the same for Pi.prompts(), each failing then recovering: an unlistable Claude project directory; an unlistable Codex sessions tree (a missing one stays[]); an unlistable Pi session directory.service.system.test.ts: View Prompts rejects, and search still finds another conversation.ViewPromptsModal.renderer.test.tsx: fixed words, no IPC text.promptsUnreadable.system.test.tsunless noted):ff781eb7-…).opencodeQueryError.test.ts(a query that fails after the store opened).id: null.service.system.test.tsasserts the warning in main.Verification
rm -rf .tsc-out; npx tsc -b: clean.NODE_ENV=test npx vitest run src/main/conversations: 127/127 at ba9c793 (origin/main merged in: a real conflict inservice.system.test.ts's imports, resolved by keeping both). The modal test passes.87692683) is the plan alone; the implementation is5cee073e.🤖 Generated with Claude Code