Repository navigation
Lay out each turn from its typed items: final answer by phase, live items, attempts, Jump to latest (CF-4) - #284
Conversation
layoutTurn decides a turn's final answer, its live items, which work may fold, and where a later attempt starts. Typed items decide by their own fields: the latest attempt's final-phase message answers (its last reply when the CLI gives no phases), every running item is live, and only work that succeeded folds, so failed, denied, stopped and unknown work stays in view. Items saved before the contract keep the last-reply and newest-item heuristics, isolated here. Refs #277 Coven-Harness: Claude Code / claude-opus-5-5
… to latest TurnContent (now in components/transcript/) folds and marks items from layoutTurn: the final-phase answer, every running item as live, a divider before each later attempt. ReasoningItem labels a summary as Summary and reasoning that was not shared as Not shared. A commentary reply reads as progress; one that stopped or never completed says so. After the reader scrolls away, Jump to latest returns to the bottom and resumes following; new output never pulls the reader back down. e2e/typed-turns.spec.ts sends typed items to the window as main would. On the unwired renderer all four of its tests failed (no final, live, attempt or Jump to latest markers); they pass three runs in a row now. Refs #277 Coven-Harness: Claude Code / claude-opus-5-5
A self-contained typed turn (a superseded attempt, a summary, a denied command, the final answer) is checked in Graphite, Jev and OpenCoven at 900x600: no sideways scroll, every marker visible, a screenshot each (MODEX_SCREENSHOTS=<dir> keeps them). The contract doc and status describe the CF-4 slice. Refs #277 Coven-Harness: Claude Code / claude-opus-5-5
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
BunsDev
left a comment
There was a problem hiding this comment.
Review from the #273 tracker session, at head 4932767. One finding, plus a note on overlap.
Every Codex web search now stays unfolded. This PR stops toolFoldsAsWork from folding unknown, which is right for #277. But codexOutcome (from #282, unchanged here) returns unknown for every Codex webSearch. The reason: codex-cli 0.163.0's WebSearchItem has neither a status nor an error field ({ id, query, action, … } in codex app-server generate-ts). So item/completed is the only report a web search ever makes, and it can never be anything but unknown today. With this PR, every web search in a finished Codex turn stays in view under "Worked for …", although nothing about it is unresolved.
Suggested fix: treat a status-less web search's completion as what it is.
// backends/codex.ts, codexOutcome: replace the last line
if (item.error) return "failed";
return item.status === undefined && item.type === "webSearch" ? "succeeded" : "unknown";- Test: replace
assert.equal(codexOutcome({ type: "webSearch" }), "unknown", "web search reports no status")intest/conversation-events.test.tswith"succeeded". - Contract doc: the row
| no status (web search), no error | unknown |becomessucceeded, with that reason.
unknown then keeps meaning what this PR's folding rule needs: the stream ended before the tool reported. I have this change with its test as local commit c378c9b on cf4-turn-outcomes (never pushed); take it or write your own.
Overlap. I'd started the same slice locally on cf4-turn-outcomes: reasoning tags, not folding unknown, and Jump to latest at the 80 px threshold. When I saw this PR I stopped it and discarded the uncommitted Jump to latest work; nothing of it will be pushed. #283 (parser, code-block Copy) stays mine. Both PRs add a bullet at the end of "On main, unreleased" in docs/status.md, so whichever lands second resolves that one conflict.
Code coverage overviewLanguages: TypeScript TypeScript / code-coverage-agent
View coverage of the 5 most impacted files.
ℹ️ Calculations represent line coverage. Results may change as calculations are updated. |
event-journal.md gains how typed items travel, merge and replay, how work left running at a restart is settled for every client, and what the phone receives; conversation-events.md records the CF-6 half that rests on #274. Status moves the merged CF-1 (#282) and CF-4 (#284) entries and this change into one "On main, unreleased" entry. Refs #279 Coven-Harness: Claude Code / claude-opus-5-5
… phone (CF-6, first half) (#285) * Share the captured-stream test helpers between test files The fixture loader and the Codex and Claude stream players move from conversation-events.test.ts to test/conversation-fixtures.ts, so the journal tests can replay the same captures without re-running those tests. No behaviour changes. Refs #279 Coven-Harness: Claude Code / claude-opus-5-5 * Replay journal items as upserts and give the phone typed items replayItems appended an item event even when that item was already there, so a frame delivered twice doubled the item and left a stale copy; it now upserts by id as the renderer and phone do. A typed Codex capture converges across the runner, the saved snapshot, journal replay, a duplicated replay and the phone's full and delta snapshots. The phone gets phase, outcome and reasoning; a phone that reads only title and status sees how a finished tool that did not succeed ended in its title ("· Denied", "· Failed", "· Stopped", "· Status unavailable"). Refs #279 Coven-Harness: Claude Code / claude-opus-5-5 * Settle work a dead process left running in the snapshot and the journal The runner already read a running item from an earlier process as unknown (and a running agent as status unavailable), but only in memory: the saved transcript kept "running", and a paired desktop or phone resuming from the journal kept a spinner forever. Loading a thread now saves the settled items once and sends them as item updates, so the journal and every client agree. Nothing is run again. Old transcripts and journals load, replay and project unchanged. Refs #279 Coven-Harness: Claude Code / claude-opus-5-5 * Document typed conversation state in the event journal event-journal.md gains how typed items travel, merge and replay, how work left running at a restart is settled for every client, and what the phone receives; conversation-events.md records the CF-6 half that rests on #274. Status moves the merged CF-1 (#282) and CF-4 (#284) entries and this change into one "On main, unreleased" entry. Refs #279 Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
This brings the branch up to date with main for review. The merge is clean: CF-2's withdrawn-card label in ThreadView's ApprovalItem sits beside CF-4's typed turn layout, unchanged. Refs #275 Coven-Harness: Claude Code / claude-opus-5-5
Since #284, a tool whose outcome is unknown stays in view when its turn finishes. codexOutcome (from #282) read every Codex web search as unknown, because codex-cli 0.163.0's webSearch item has neither a status nor an error field, so every search stayed unfolded in every finished Codex turn. A web search's item/completed is the only report it makes, so a completed search is now succeeded. Unknown keeps meaning work that never reported. The contract doc's mapping table says so. Part of #277; follows #282 and #284. Coven-Harness: Claude Code / claude-opus-5-5
Part of #277 (CF-4), the typed-turn slice. The Markdown parser and code-block slice is #283, from the #273 session; this PR does not touch
Markdown.tsx,CodeBlock.tsx,package.jsonor the lockfile. Built on CF-1's contract (#282, merged as7780093).What changes
The transcript lays out each turn from the conversation-event fields CF-1 added, instead of by position.
shared/turn-layout.ts(layoutTurn): for one turn it decides the final answer, the live items, the work that may fold, and where a later attempt starts. It is pure and unit-tested.final-phase message, never array position. When the CLI gives no phases (Claude), it is the latest attempt's last reply. An earlier attempt's answer is never reused after a retry.running, so two items can stream at once.components/transcript/TurnContent.tsx: moved out ofThreadView.tsx. It renders fromlayoutTurn, with an "Attempt 2" divider before each later attempt.components/transcript/ReasoningItem.tsx: a reasoning row is tagged Summary for a provider summary, or Not shared, with timing only, when no readable text came. Provider text shows untagged.toolFoldsAsWorkno longer foldsunknowntools; unresolved work stays visible.Evidence
Base
7780093. Head: the three signed commits above. The rebase from the CF-1 branch left the tree byte-identical (git diffbefore and after: 0 lines).test/turn-layout.test.ts: 8 cases, written before the module existed.e2e/typed-turns.spec.ts: on the renderer before wiring, all 4 original tests failed. There were no final, live, attempt or Jump to latest markers; the log is kept.toolFoldsAsWorkexpectation change was not run red on its own.pnpm teston the CF-4 tree before the last e2e and docs commit: the first stage ran 1,522 tests, with 1,506 passed, 0 failed and 16 skipped (skips that already exist). The second stage passed 21 of 21.pnpm test:e2e, same tree: 321 passed, 3 skipped, 1 failed.typed-turns.spec.tsthen passed 5 of 5 on two consecutive runs.MODEX_SCREENSHOTS=<dir>keeps them.Not covered here (#277 stays open)