Skip to content

Lay out each turn from its typed items: final answer by phase, live items, attempts, Jump to latest (CF-4) - #284

Merged
BunsDev merged 3 commits into
mainfrom
cf4-typed-turns
Oct 18, 2026
Merged

BunsDev merged 3 commits into
mainfrom
cf4-typed-turns

Conversation

@BunsDev

@BunsDev BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

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.json or the lockfile. Built on CF-1's contract (#282, merged as 7780093).

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 answer: the latest attempt's 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.
    • Live: every item still running, so two items can stream at once.
    • Folding: only work that succeeded folds (succeeded tools, finished reasoning, earlier replies, answered requests). Failed, denied, stopped and unknown work, pending requests and notices stay in view, and folded rows stay mounted.
    • Legacy: items saved before the contract keep the last-reply and newest-item heuristics, isolated in this one function.
  • components/transcript/TurnContent.tsx: moved out of ThreadView.tsx. It renders from layoutTurn, 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.
  • Assistant replies: commentary reads as progress. A reply that stopped says "Stopped before it finished", and one that never completed says "This reply may be incomplete".
  • Jump to latest: shown once the reader scrolls away from the bottom. New output never pulls them back down; the button returns to the bottom and resumes following. The 80 px follow threshold is unchanged.
  • Tool folding: toolFoldsAsWork no longer folds unknown tools; 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 diff before and after: 0 lines).

  • Red first.
    • 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.
    • The toolFoldsAsWork expectation change was not run red on its own.
  • Unit tests, full pnpm test on 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.
  • Full e2e, pnpm test:e2e, same tree: 321 passed, 3 skipped, 1 failed.
    • The failure was the new theme test. Test 2's switch to running reloads the thread from main and drops earlier injected items, so it couldn't find them.
    • It now sends its own turn. typed-turns.spec.ts then passed 5 of 5 on two consecutive runs.
  • Themes at 900×600: in Graphite, Jev and OpenCoven, a typed turn shows no sideways scroll, and the final answer, denied tool, Summary tag and attempt divider are all visible. Each run attaches a screenshot per theme; MODEX_SCREENSHOTS=<dir> keeps them.
  • Load average during the runs was about 190–250, from other sessions' builds.

Not covered here (#277 stays open)

  • The Markdown parser, code-block controls and unsafe-link cases: Render transcript Markdown with CommonMark and GitHub's extensions (CF-4 parser slice) #283.
  • Long-transcript and streaming performance measurement.
  • Selection and keyboard focus through sustained updates, and draft continuity across thread switches, have no new test here. The existing behaviour is unchanged.
  • Human VoiceOver acceptance and reduced-motion review. The new elements add no animation.

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
@vercel

vercel Bot commented Oct 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
modex Ready Ready Preview Oct 18, 2026 8:20am UTC

Request Review

@BunsDev BunsDev left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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") in test/conversation-events.test.ts with "succeeded".
  • Contract doc: the row | no status (web search), no error | unknown | becomes succeeded, 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.

@github-code-quality

Copy link
Copy Markdown

Code coverage overview

Languages: TypeScript

TypeScript / code-coverage-agent

main 7780093 cf4-typed-turns 4932767 +/-
62% 62% 0%
View coverage of the 5 most impacted files.
File main 7780093 cf4-typed-turns 4932767 +/-
apps/desktop/sr...asoningItem.tsx 0% 0% 0%
apps/desktop/sr...TurnContent.tsx 0% 0% 0%
apps/desktop/sr...ation-events.ts 99% 99% 0%
apps/desktop/sr...tool-summary.ts 100% 100% 0%
apps/desktop/sr.../turn-layout.ts 0% 100% +100%

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.

@BunsDev
BunsDev merged commit 8e26534 into main Oct 18, 2026
13 checks passed
@BunsDev
BunsDev deleted the cf4-typed-turns branch October 18, 2026 08:45
BunsDev added a commit that referenced this pull request Oct 18, 2026
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
BunsDev added a commit that referenced this pull request Oct 18, 2026
… 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
BunsDev added a commit that referenced this pull request Oct 18, 2026
Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
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
BunsDev added a commit that referenced this pull request Oct 19, 2026
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

This branch was successfully deployed

1 active deployment
Preview — 49327671 Deployed Oct 18, 2026 by vercel[bot]
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.

1 participant