Let the owner find a session, and say so when one cannot be read - #83
Merged
Conversation
Two complaints about the same screen, and they turned out to be one thing: the page was a scree GUI, not Modore's judgment. It opened with "도구별 세션 저장소 — Codex 2,886개". That number is coverage, not a finding; nobody arrives at this screen asking how many sessions a store holds, and the answer prompts "so which of the 2,886 is the problem?". The order is inverted now — protected worktrees, then what is about to expire, then lineage — and the store table is a footer disclosure reading "Codex · Claude · Gemini · VS Code · Kiro — 5개 저장소 · 3302개 기록 확인". Verdict, then evidence, then inventory. And Modore could not show a conversation it already had on disk. Three commands read transcript bodies -- `bind --deep` to prove a session touched a workspace, `title` to name it, `preserve` to export it -- and none let the owner simply look. `scree.py inspect` is the fourth: one named session, masked, per-turn capped, recent window plus the opening request, subagents not opened. It appears in the app as 대화 보기 on a session row, beside 보존 for the full masked export. The rule that made the earlier restraint sound was never "metadata-only product" -- it was **metadata-only judgment**, and that is unchanged: no judgment path calls inspect, pinned by a test asserting the report and bind outputs carry none of its keys. Reading real sessions through it found a decoder bug immediately. Codex records one reply twice, as `response_item/message` and again as `event_msg/agent_message`, so every agent turn rendered doubled -- 16 of 41 turns on a live rollout were a repeat of the line before. Consecutive identical turns collapse now, which cannot merge a genuine repeat because those are separated by a reply. `developer` and `system` turns are dropped for a different reason: they are the harness talking to the agent, they are the longest thing in a Codex rollout, and they pushed the actual exchange off the screen. Verified in the running app: the reordered page, the footer coverage line, and a real conversation rendering inline with speakers separated and text selectable. The dedupe is verified against live rollouts (41 turns to 21) and pinned by regression tests; it was not re-checked on screen after the fix.
The viewer landed on the retirement screen, which is the one place someone is not browsing -- they are deciding about a repo. The screen the complaint came from still had no way to find a conversation, so this puts the browser where the question is asked: search across workspace, tool and path, most recent first, click to read. The listing is metadata-only on purpose. It runs over every store on the machine, and describing thousands of transcripts by opening them would be a content read of the whole disk on every refresh. Bodies are still read one at a time, by name, through `inspect`. Editor workspace state is listed beside agent transcripts -- Modore's boundary is durable local state, not conversations specifically -- but only entries whose store actually keeps a transcript offer to open one, which also retires the "대화 보기" that VS Code bindings were getting on the retirement screen and that opened to an empty panel. Four failures were all rendering as "there was nothing here", which is the worst possible sentence to put in front of someone about to delete something: - a transcript that vanished, one that cannot be read, and one this build cannot decode now each carry their own status and their own sentence, instead of collapsing into an empty conversation; - a fetch that fails leaves a failed state and a retry, instead of spinning on "대화를 읽는 중…" forever; - turns are identified by ordinal, not by role plus text, which collided on exactly the case the dedupe rule preserves -- the same person saying the same thing twice with a reply between; - the conversation cache is keyed by the transcript's bytes, so a session an agent is still appending to is a miss rather than a silently stale panel. That last one exposed the same bug one level down: PresentationCacheKey read resource values through the caller's URL, and URL memoises them per instance. A screen holds its bindings for its whole lifetime, so the key whose entire purpose is noticing changed bytes was answering with the size and mtime the file had when it was first looked at. The title cache had been quietly inheriting that too. Also removes a stray `status` reference that would have raised NameError the first time a file-access binding was built, and corrects README and the module docstring, which both said three commands read inside a conversation while listing four. Python 624 · Modore 238 · MothballCore 120. Verified end to end against this machine's real stores through the app's own invocation path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to
52e42d8, closing six review findings plus two bugs found while fixing them.The browser moved to where the question is asked
inspect's only consumer was the repo-retirement screen — the one place someone is not browsing. The AI 세션 screen, where the original complaint came from, still had no way to find a conversation. It now has one: search across workspace, tool and path, newest first, click to read.Backed by a new
scree.py sessions— metadata-only on purpose. The listing runs over every store on the machine (3,200 entries here), and describing that many transcripts by opening them would be a content read of the whole disk on every refresh. Bodies are still read one at a time, by name, throughinspect.Editor workspace state is listed beside agent transcripts — Modore's boundary is durable local state, not conversations specifically — but only entries whose store actually keeps a transcript offer to open one. That also retires the
대화 보기VS Code bindings were getting on the retirement screen, which opened to an empty panel.Four failures were all rendering as "there was nothing here"
The worst possible sentence to show someone about to delete something.
[]대화를 읽는 중…foreverConversationLoadStatewith a failure message and a retryTurn.idwasrole + text.hashValuePresentationCacheKeyTwo bugs found along the way
NameErrorin_file_access_binding— a stray"status": statushad been pasted into a function with no such variable in scope. It would have raised the first time a file-access binding was built.PresentationCacheKeywas reading through the caller'sURL, andURLmemoises resource values per instance. A screen holds its bindings for its whole lifetime, so the key whose entire purpose is noticing changed bytes answered with the size and mtime the file had when it was first looked at. The title cache had been quietly inheriting this. Verified directly: same instance reports 8 bytes after the file grew to 16; a fresh instance reports 16.Docs corrected too — README and the module docstring both said three commands read inside a conversation while listing four.
Verification
swift test -Xswiftc -warnings-as-errors -Xswiftc -strict-concurrency=complete).52e42d8shipped Python-only tests, which is why the ordinal and load-state bugs passed CI untouched.release_smoke.pyclean; no local user paths in packaged sources.-cwrapper invocation path: index → pick a Codex session →inspectreturnsstatus: ok, ordinals[0,1,2],masked: true; a missing path returnsstatus: missing.