Skip to content

Render transcript Markdown with CommonMark and GitHub's extensions (CF-4 parser slice) - #283

Merged
BunsDev merged 24 commits into
mainfrom
cf4-markdown-parser
Oct 18, 2026
Merged

BunsDev merged 24 commits into
mainfrom
cf4-markdown-parser

Conversation

@BunsDev

@BunsDev BunsDev commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Part of #277 (CF-4), tracker #273. This is the parser slice that the issue says can land before #274. Semantic turn rendering waits for #274's landed contract (PR #282) and is not in this PR: final answer by phase, outcomes, reasoning labels, and Jump to latest.

What changes

Markdown.tsx replaces its hand-written subset with micromark plus its GFM extension. The parsed tree renders straight to React elements, with no HTML string anywhere.

  • Renders: tables (with column alignment), task lists, block quotes, nested lists, strikethrough, footnotes, reference links, horizontal rules, ####+ headings, and fences that keep their language (<pre class="code" data-lang="ts"><code>).
  • Code blocks (components/transcript/CodeBlock.tsx): a language label (text when there is none) and an accessible Copy. Copy goes through main's clipboard:write and nothing else; a status line announces success or failure.
  • Safe by construction:
    • Raw HTML and MDX-looking text render as text.
    • Only http(s) links become links, through the existing safeHref. javascript:, file:, data:, relative and autolinked non-web targets stay as the text that was written, as before.
    • An image becomes a link named Image: <alt>, so reading never fetches a remote image.
    • Task-list boxes are disabled.
  • Streaming:
    • An unclosed fence runs to the end of the text, so a streaming fence shows its code so far.
    • While text keeps changing, it is split at blank lines outside fences (streamingChunks), and only the last piece is parsed again. Each piece is a memoized component.
    • Once the text rests for 300 ms it is parsed whole once, so the settled rendering is always the exact one.
  • Settled renders sit in a 400-entry cache, so reopening a thread does not parse it again.
  • Styles for the new elements use the existing border, surface and text tokens, so all three themes follow.

Dependency choice

Pinned exact, as renderer-only devDependencies bundled by Vite (like React and xterm): mdast-util-from-markdown@2.0.3 (Feb 2026), micromark-extension-gfm@3.0.0, mdast-util-gfm@3.1.0, @types/mdast@4.0.4.

micromark is the unified collective's CommonMark reference implementation, with spec-tested GFM extensions. Using it at the mdast level means Modex owns every element it outputs: there's no rehype, no dangerouslySetInnerHTML, and no component loading.

@create-markdown/core (already used by Notes) is a block-editor format, not a CommonMark/GFM parser, so it was not reused. react-markdown would pull in the whole remark/rehype pipeline for the same parse.

Measurements

Apple M3 Max, Node 24.18.1, macOS, renderer code loaded through Vite SSR. The machine was shared with other sessions (load average 21–50), so treat these as relative.

Case Before (89c98f2) After
7.9 KB reply streamed in 331 deltas, mean per delta 0.8 ms 0.15–0.18 ms (streaming pieces)
Same, worst delta 4.5–5.0 ms 0.7–1.0 ms
One full parse of that reply (what every delta would cost without the streaming pieces) — 4.1–4.5 ms mean
First open of a 200-message, 239 KB thread 32–41 ms 135–178 ms
Reopening that thread 32–41 ms 7 ms
Renderer main chunk 436.6 kB (129.0 kB gzip) 518.0 kB (151.8 kB gzip)

The first-open cost is a real regression, and it is in this PR's evidence for review. A later CF-4 slice can defer parsing of folded work items if it matters in practice. The bundle now crosses Vite's 500 kB advisory warning; nothing gates on it.

Tests

  • test/markdown-render.test.ts, 11 tests:
    • GFM elements, heading levels, fence language and indentation.
    • Mid-stream fences, every prefix of a streamed reply (no lost or doubled words), Unicode.
    • Inert raw HTML, link schemes, bare URL punctuation, reference links, images never fetched.
    • The streaming splitter (lossless, never inside a fence), and pieces rendering the same text as the whole.
  • On 89c98f2 five of them fail for missing behavior. Four pass and stay as safety guards (prefixes, Unicode, raw HTML, link schemes). The two streaming tests failed with streamingChunks is not a function before it existed.
  • test/markdown-render.test.ts also covers the bounded settled cache, and the code-block label and Copy markup. Both were seen failing before their code existed.
  • e2e/conversation-rendering.spec.ts, 3 tests, through the real app and mock backend. The two Markdown tests fail with the 89c98f2 Markdown.tsx swapped in. The Copy test fails with the 505b488 one: no code-block header. All three pass at head. Copy puts exactly the block's code on the clipboard, and no tool, approval or new turn starts.
  • Existing transcript specs sources, app, titles and agents passed locally: 28/28.

Checks on 68b89c8

Base 89c98f280292b13fd9b1c7af1bf2fe65cd2fa0d1, head 68b89c8dac5436012e498958bf6f4f6b89e88f61. Run locally on macOS (Apple Silicon), Node 24.18.1, pnpm 10.34.5, on a machine shared with other sessions:

Command Result
pnpm install --frozen-lockfile passed
pnpm build passed
pnpm typecheck passed
pnpm test passed: worktree scripts 109/109, core 16/16, desktop 1,484 passed + 16 skipped of 1,500, desktop native/update 21/21, site 10/10, Store desktop 16/16
pnpm test:e2e passed: desktop 320 passed + 3 skipped (21.5 min), site 7/7

The 3 e2e skips are the real-Open-Knowledge cases in knowledge.spec.ts, which skip without that local install. The 16 unit skips are the suite's existing platform and native skips. This PR adds none.

After the local run

  • 135069f changes only a test helper. CodeQL flagged visible() in markdown-render.test.ts as "incomplete multi-character sanitization" (high): it stripped tags with a regex before decoding entities. It now skips tags character by character and decodes each entity once. On 135069f, markdown-render.test.ts passed 13/13 and the typecheck is clean, and CodeQL passes.
  • No product code changed after 68b89c8, so the full local run above still describes what ships.
  • CI on 68b89c8 (run 39108256492): unit + e2e (macOS) and iPhone companion passed.

After CF-1 (#282) merged

acba096 merges main (7780093) into this branch with no conflicts. A fresh full local run on acba096 (acba09642fe805a451714ace122e7198e6942f92) passed:

Command Result
pnpm install --frozen-lockfile passed
pnpm build passed
pnpm typecheck passed
pnpm test desktop 1,511 passed and 16 skipped of 1,527; worktree scripts 109/109, core 16/16, desktop native/update 21/21, site 10/10, Store desktop 16/16
pnpm test:e2e desktop 320 passed, 3 skipped (the real-Open-Knowledge cases), 20.0 min; site 7/7

CI on 135069f passed every check, CodeQL included.

Latest head: 046df0e

main kept moving (#284, then #285). ab8474a and 046df0e merge them in, both without conflicts.

  • Full local run, 09ae3e5 (main at 8e26534 merged in): install, build, typecheck and pnpm test all passed. pnpm test:e2e ran 324 passed, 3 skipped, 1 failed. The failure, sidebar-details.spec.ts:40, was its setup's git init hitting No space left on device while the machine's disk was full. Run alone afterwards, that spec passed 3/3.
  • 09ae3e5 also makes the Copy e2e click again until Copied shows. CI run 39112253838 had missed once there, likely because a late follow-up suggestion moved the transcript under the pointer. 9/9 local repeats passed.
  • 046df0e (main at fb76aac merged in): typecheck, build, markdown-render 13/13, and conversation-rendering, typed-turns, sources and sidebar-details e2e 15/15. CI on 046df0e (run 39116117568) passed every check, including the required unit + e2e (macOS). Merge state: CLEAN.
  • 74833f7 (main at d3711c6, Show the command that ran, its exit code, bounded output and honest source labels (CF-5, first part) #287 merged in, no conflicts): typecheck, build, markdown-render 13/13, and conversation-rendering, typed-turns, sources and conversation-evidence e2e 16/16. CI on 74833f7 passed every check. Merge state: CLEAN.

Reshaped at 29a74e8: the settle fix lives in #295

After review I found two more defects in this PR:

  • a reply that finished streaming remounted its Markdown, dropping a selection or focus;
  • tables collapsed at 900×600.

I fixed both here (083b8fb, 291b08b, verified on fc16df4). Then #295 appeared, another session's CF-4 completion, stacked on this branch at 74833f7, with its own fix for the same remount, its own continuity tests and its own theme captures. Keeping both fixes would conflict, so 29a74e8 hands that defect to #295:

  • Markdown.tsx, markdown-render.test.ts and conversation-rendering.spec.ts return to 74833f7.
  • transcript-continuity.spec.ts is dropped.
  • The status clause is reverted.

Until #295 merges, this PR alone still has the remount-at-settle defect. #295 depends on this PR, so they land in order.

Kept here, because #295 does not touch styles.css:

  • The narrow-table fix. At 900×600 with Review open, the transcript is 265 px wide. A table with an unbreakable path squeezed its Detail column to 58 px and its row to 1,009 px. Cells now keep at least 10em and wrap long tokens, so the table scrolls in its own box.
  • Its theme check, in its own file: e2e/markdown-narrow.spec.ts, run in Graphite, Jev and OpenCoven at 900×600, checks:
    • no sideways page scroll;
    • the table and the code scroll in their boxes;
    • every cell is at least about 10 characters wide;
    • Copy stays visible.

On f1bbb28 (main at 04b7ec7 merged in): typecheck, build, markdown-render 13/13, and conversation-rendering, markdown-narrow, typed-turns and sources e2e 13/13. The full suites are left to CI. The last full local run was on fc16df4, all passed.

Ready: 44b21bc

44b21bc merges main at d3dfe76 (#294). Before pushing, typecheck, build and the conversation-fidelity, conversation-rendering and markdown-narrow e2e specs (8/8) passed. CI then passed every check. Merge state: CLEAN, current with main.

Not done here (left open on #277)

Markdown.tsx's hand-written subset becomes micromark with its GFM
extension (mdast-util-from-markdown 2.0.3, micromark-extension-gfm
3.0.0, mdast-util-gfm 3.1.0, pinned and bundled into the renderer).
The tree renders straight to React elements: tables, task lists (inert
boxes), block quotes, nested lists, strikethrough, footnotes, reference
links and language-tagged fences. Raw HTML stays text, only http(s)
links are links, other targets stay as written, and an image becomes a
link to it instead of being fetched.

Parsing every delta again would cost a long reply about 4 ms a render
here, five times the old subset. While text keeps changing, only its
last block (split at blank lines outside fences) is parsed again, about
0.2 ms a delta; the whole text is parsed once it rests for 300 ms.
Settled renders are cached, so reopening a thread does not parse it
again. First open of a 200-message, 239 KB thread: about 160 ms, against
40 ms before.

Part of #277 (parser slice); semantic turn rendering waits for #274.

Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
The transcript's Markdown also renders fetched pages, which can be
large. RenderCache keeps the 400 most recent settled renders of text up
to 64,000 characters; longer text renders each time and is never kept.

Coven-Harness: Claude Code / claude-opus-5-5
CodeBlock (components/transcript/) heads a fenced block with its
language ("text" when it has none) and a Copy button. Copy sends the
code through main's clipboard:write and does nothing else: no tool,
approval or turn starts. A status line tells screen readers it copied,
or that copying failed. The e2e copies a block and checks the clipboard
holds exactly its code while the thread stays idle.

Part of #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 9:03pm UTC

Request Review

Comment thread apps/desktop/test/markdown-render.test.ts Fixed
@github-code-quality

github-code-quality Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Code coverage overview

Languages: TypeScript

TypeScript / code-coverage-agent

main 7ec5f14 cf4-markdown-parser 5f96c1e +/-
63% 63% 0%
View coverage of the 2 most impacted files.
File main 7ec5f14 cf4-markdown-parser 5f96c1e +/-
apps/desktop/sr...t/CodeBlock.tsx 0% 81% +81%
apps/desktop/sr...ts/Markdown.tsx 0% 89% +89%

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


Updated October 18, 2026 21:29 UTC

CodeQL flagged the Markdown test's visible() helper as incomplete
multi-character sanitization: it removed tags with a regex and then
decoded entities. It only reads React's static markup in a test, but
the helper now skips tags character by character and decodes each
entity once, which is also what a reader sees.

Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
CI run 39112253838 failed the code-block Copy e2e once: Copied never
showed after a 42 ms click, while local runs and two other CI runs
passed. The likely cause: the follow-up suggestion arrives after the
turn ends, grows the composer, and the transcript moves under the
pointer. The test now clicks until Copied shows. A missed click copies
nothing, so this cannot hide a wrong copy, and the clipboard and
nothing-started assertions are unchanged. 9 of 9 local repeats passed.

Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
@BunsDev

BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Holding this PR (back to draft): I found a defect in it. While a reply streams, Markdown renders it as memoized pieces (Blocks). 300 ms after the text rests, it switches to plain elements from one full parse. The children's component type changes, so React unmounts every piece and mounts new DOM. A selection, keyboard focus, or a code block's Copied state inside a reply is lost the moment the reply finishes streaming.

#277 requires selection and focus to survive sustained updates. I'm adding a failing e2e test for it, then a fix: the settled parse keeps the same piece structure, so React updates in place instead of remounting. I'll mark this ready again with fresh CI.

@BunsDev
BunsDev marked this pull request as draft October 18, 2026 11:01
While a reply streamed, Markdown rendered it as memoized pieces; once
the text rested it switched to plain elements from one full parse. The
children's component type changed, so React unmounted every piece and
mounted new DOM: text the reader had selected in the reply, keyboard
focus on a control in it, and a code block's Copied state were lost the
moment it finished (e2e/transcript-continuity.spec.ts, red on 74833f7:
the selection read "" and Copy lost focus).

Both ways now render one Piece per streaming piece. The settled render
parses the whole text and groups its top-level blocks back into those
pieces by source offset, so settling updates each piece where it stands.
Streaming pieces are kept per component instance by text, so an
unchanged piece is still not parsed again.

Part of #277.

Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
At 900x600 with the Review panel open, the transcript is 265 px wide. A
table with an unbreakable path took 413 px for that column and squeezed
the others to a word: Detail measured 58 px and its row 1,009 px tall.
Cells now keep at least 10em and let a long path or URL wrap inside, so
the table scrolls sideways in its own box (the row is 344 px; nothing is
cut to one word per line).

conversation-rendering.spec.ts checks, in Graphite, Jev and OpenCoven at
900x600: no sideways page scroll, a wide table and a long code line
scroll in their own boxes, every cell is at least about ten characters
wide, and Copy stays visible. It attaches a screenshot per theme
(MODEX_SCREENSHOTS keeps them).

Part of #277.

Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
Coven-Harness: Claude Code / claude-opus-5-5
@BunsDev
BunsDev merged commit a4039b2 into main Oct 18, 2026
13 checks passed
@BunsDev
BunsDev deleted the cf4-markdown-parser branch October 18, 2026 21:42
BunsDev added a commit that referenced this pull request Oct 18, 2026
Both entries kept in docs/status.md.

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
Both entries kept in docs/status.md.

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
Both entries kept in docs/status.md.

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
#283 merged as a4039b2, but the CF-1 block's Still open line still listed "the Markdown parser
(#283)". #293 rewrote that line before #283 landed and kept the item, so it stayed on main
after #293 merged (57e7607). Name what CF-4 still has open instead: #277's checkpoint lists
reference-viewport screenshots and a keyboard and reduced-motion review of the Markdown
elements; human VoiceOver is already in the list.

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
… from Still open (#306)

* Record #185's downgrades in the approval rules spec

The spec's Known limits said commands are clipped before judgment "without a truncation
downgrade", and its rule section listed only the destructive and escalation downgrades. Since
#185 (2026-10-12), an allow also asks when the full command line chains, redirects or
substitutes commands, or when the digest clipped text Jev needed (approvals/gate.ts:155-158,
digest.ts:103-111).

Add a dated progress note under the enablement list: #185 closed the clipped-command false
allow, the quoted-command one is open and the corpus has not been run live since, so item 1 is
unmet. The note names the next step. The enablement criteria themselves are unchanged.

Coven-Harness: Claude Code / claude-opus-5-5

* Drop the merged Markdown parser from status.md's Still open list

#283 merged as a4039b2, but the CF-1 block's Still open line still listed "the Markdown parser
(#283)". #293 rewrote that line before #283 landed and kept the item, so it stayed on main
after #293 merged (57e7607). Name what CF-4 still has open instead: #277's checkpoint lists
reference-viewport screenshots and a keyboard and reduced-motion review of the Markdown
elements; human VoiceOver is already in the list.

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 19, 2026
#307)

Since #283 the transcript parses Markdown with micromark and imports only safeHref from
shared/inline. Two comments still described the old arrangement:

- engine/canvas.ts said Canvas renders "the same small Markdown subset as transcript
  previews". The transcript now renders full GFM; the comment now names Canvas's own subset,
  which docs/canvas.md already lists.
- shared/inline.ts said the renderer's Markdown is a thin mapper over tokenizeInline. Canvas
  is now its only caller outside the tests.

Comments only; no behaviour changes.

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 19, 2026
CF-2 (#286), CF-3 (#293), CF-4's Markdown slice (#283), CF-6's request half (#308) and CF-7's
clarifications (#312) are on main, with their merge commits. The candidate note says the fixture
rows now run on main, and no candidate is pinned while the live-provider and human gates stay open.

Refs #281

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 19, 2026
…nts at once, reconnect, a clarifying question (CF-8) (#313)

* Add the request variants to the fidelity path

Four cases in e2e/conversation-fidelity.spec.ts, through the real app:

- A native withdrawal: the Claude fixture takes its own request back (control_cancel_request).
  The card closes as withdrawn, no answer reaches the CLI, a late answer is refused, and the file
  is unchanged.
- The desktop and the phone answer at once, over the bridge and the companion's HTTPS. Exactly one
  is accepted, the CLI reads one decision, the card names who gave it, and the file matches the
  winner.
- With the journal on, a phone that loaded the open card reconnects after the desktop's decision.
  Its delta shows the card answered by the desktop and sent, its late answer is a 409, and every
  count stays at one.
- A clarifying question inside the path, from a codex-cli 0.163.0-shaped fixture: answered on its
  card, heard once, followed by a real edit and check, saved to Notes, and inert after a restart
  that re-runs nothing.

The fixture now counts every decision the CLI reads, so the earlier cases assert that too. The
golden path is marked slow: since #301 it also takes 18 reference captures and relaunches, and it
ran past 90 s on a Mac at load average 100.

These cases found the companion's 404 for threads started on the Mac, fixed in the CF-6 branch.

Refs #281

Coven-Harness: Claude Code / claude-opus-5-5

* Record the request variants in the acceptance receipt

The matrix rows for a clarifying question, a native withdrawal, simultaneous answers and a
reconnect after a decision move from open to verified with a fixture, with what each case asserts.
The defects list adds the companion's 404 for threads started on the Mac. The fidelity summary gains
rows for questions and approvals.

Refs #281

Coven-Harness: Claude Code / claude-opus-5-5

* Record CF-8's request variants in status

Refs #281

Coven-Harness: Claude Code / claude-opus-5-5

* Record the landed children in the acceptance receipt

CF-2 (#286), CF-3 (#293), CF-4's Markdown slice (#283), CF-6's request half (#308) and CF-7's
clarifications (#312) are on main, with their merge commits. The candidate note says the fixture
rows now run on main, and no candidate is pinned while the live-provider and human gates stay open.

Refs #281

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 19, 2026
… and the routines spec (#310)

v0.2.0 was published on 2026-10-18 (23:14Z) from the signed tag on 01b1b8a. This
follows Val's status layout (#290):

- Shipped gains a v0.2.0 row linking its release and its review.
- The 13 "On main, unreleased" items move unchanged to a new "Status summary"
  at the end of the v0.2.0 review, along with the old In flight entry. Each one
  was introduced by a commit in the tag (#248, #253, #254, #257 to #261, #263,
  #264, #266, #267, #269). The CF slices and #283's Markdown parser came after
  the tag, so they stay.
- In flight keeps a short follow-ups entry for the gates no receipt records yet:
  the independent download checks, the banner upgrade on the release Mac, and
  the Companion 0.2.0 (7) TestFlight upload.
- The routines spec and the README now say Phase 1, and most of Phase 0, ship
  from v0.2.0.

Unchanged: the website's static fallback (apps/site/release.json) and the
release evidence, which stay with the release owner. Claimed on #290.

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 20, 2026
…, lands #295) (#333)

* Preserve typed conversation fidelity and streaming reader state (CF-4)

Lands #295, which another session wrote, onto current main. It was
stacked on #283's pre-squash history and had sat for a day with
conflicts. This applies #295's own net change, 74833f7..dad15cd (its
commits cce0a40, 00576be, c974d0f and dad15cd), and resolves the
conflicts against what has landed since.

From #295: typed records take their final answer only from the CLI's
own final phase, never by position; failed, denied, stopped and unknown
messages and reasoning stay visible, including after a retry; reasoning
summaries keep their label while streaming; a streaming reply keeps its
code controls, focus and selection; and returning to a thread restores
its expansion and scroll position for the session. Its verification
record is in docs/reviews/2026-10-18-cf4-completion/.

Resolutions:
- TurnContent keeps main's footer and #295's remembered expansion.
- ThreadView keeps both import additions.
- turn-layout keeps both new tests.
- typed-turns.spec keeps main's version (setTheme, and the
  expanded-turn check from #301). #295's selectTheme duplicated
  setTheme, so conversation-rendering.spec now uses setTheme and the
  duplicate is gone.
- status.md takes #295's corrected streaming sentence and lists the
  change under On main, unreleased, not In flight.

Supersedes #295. Part of #277, tracker #273.

Coven-Harness: Claude Code / claude-opus-5-5

* Keep Claude's last reply as its answer; only a phase-typed turn needs an explicit final

#295 made every typed turn answer only with an explicit final message.
That fixed a real case: a Codex turn of partial answers
(codex-cli 0.163.0's partial_answer, stored as unspecified) could make
its last partial the final answer by position. It also removed CF-1's
documented fallback for a CLI that reports no message phase at all.
Claude Code then never showed a final answer, and CF-8's golden path
failed waiting for one.

finalAnswerIndex now treats a turn as typed when any message carries a
final or commentary phase, or any native phase the CLI sent. Such a
turn answers only with an explicit final. A turn whose CLI reports no
phase (Claude, old history, older Codex models) keeps the last reply,
and layoutTurn uses this rule again for the latest attempt.

Tests: a Claude turn answers with its last reply (red against #295's
rule: final -1, expected 2), and a Codex partial-answer turn never
invents one. turn-layout and conversation-events pass 39/39. The golden
path, typed-turns, conversation-rendering and conversation-continuity
specs pass 22/22.

Coven-Harness: Claude Code / claude-opus-5-5

* Restore main's fold expectations for the phase-less offline mock

#295 rewrote app, approvals, layout and routines specs for its
explicit-final-only rule. For example: "The scripted adapter reports an
unspecified phase. Do not invent a final answer by position: its
successful work remains visible." The offline mock reports no message
phase, so with CF-1's fallback restored (0532b8b) its last reply is the
answer again and its finished work folds, as on main. CI failed nine of
these tests on 0532b8b ("Expected: 0, Received: 1" final answers).

These four specs are main's versions again. Locally, app, approvals,
layout, routines, conversation-fidelity and typed-turns pass 50/50.

Coven-Harness: Claude Code / claude-opus-5-5

This branch was successfully deployed

1 active deployment
Preview — 5f96c1e1 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.

2 participants