Skip to content

Chat with the Claude Code session from Roughdraft comments - #154

Closed
PavelKaloshin wants to merge 99 commits into
Lex-Inc:mainfrom
PavelKaloshin:feat/claude-comment-chat
Closed

PavelKaloshin wants to merge 99 commits into
Lex-Inc:mainfrom
PavelKaloshin:feat/claude-comment-chat

Conversation

@PavelKaloshin

Copy link
Copy Markdown

Summary

This lets you talk to the Claude Code session that wrote a document through Roughdraft comment threads. It works like Claude Desktop artifacts, but locally.

Comment types

  • A comment has one of three types: Comment (the default), Ask or Change.
    • Ask: Claude answers in the thread, in Markdown, and does not edit the file.
    • Change: Claude edits the file, and you resolve the thread.
  • Keyboard flow: the first Ctrl+Enter confirms the text, digits or arrows pick the type (M picks the model for an Ask), and the second Ctrl+Enter sends.
  • Ask model: Fast (haiku, the default), Balanced (sonnet) or Session (the session's own model). It is stored in the endmatter only when it is not Fast.
  • Thread status with a timer: queued → passed to a subagent or forked Claude → working → answered/applied.
  • A dialog in the center of the page for reading long answers.
  • RFM: intent and model fields on root comments. CriticMarkup inside code fences is allowed.

Sessions listen automatically

  • A Claude Code plugin (packages/claude-plugin, roughdraft setup claude) adds:
    • a monitor that is always on;
    • SessionStart and PostToolUse(Write|Edit) hooks;
    • a skill.
  • Document ownership: each file is listened to by exactly one session. The Connect session menu in the header shows the owner and lets you reassign it (ADR 0007).
  • In plugin mode an Ask never wakes the main session: a headless fork (claude -p --resume <id> --fork-session) answers it right away. If that fork fails on a model other than the session's, it is retried once on the session model before the Ask goes to the session. A Change is made by the main session.
  • An idle session costs nothing:
    • listen runs in bounded rounds and restarts silently;
    • while forks run past a round's end, listen keeps printing Changes;
    • within one monitor process, each line reaches the session only once (--monitor-id).

Live reload without flicker

  • One WebSocket per tab (/api/live) replaces three EventSources, which had hit the browser's 6-connection limit.
  • Edits on disk are applied incrementally, keeping the cursor and selection.
  • Untouched blocks keep their original Markdown, tracked by source fingerprints. An open tab never writes back a document the user did not edit.

Fixes along the way

  • The watch crashed at undici's 300 s headersTimeout.
  • A generated id could clash with a reply id from the endmatter, and the reply was lost on save.
  • The inline composer was white in dark mode.
  • Fixed flaky tests.
  • Startup permission warnings in .claude/settings.json.

Docs: ADR 0006 (agent conversation in threads), ADR 0007 (ownership and session listening), the RFM spec, docs/spec/ui-state-screenshot-guide.md, and the skill, setup and prompt docs.

Test plan

  • pnpm check (lint, selectors, typecheck, unit tests, build)
  • pnpm test:smoke: 44/44
  • Full e2e, including the new comment-agent, reader, session-ownership and dark-mode composer specs
  • Real runs:
    • a headless fork with a real claude;
    • ownership routing with real processes;
    • the plugin in a real interactive session: an Ask answered without waking the main session, and nothing printed during 40 minutes idle;
    • expiry and redelivery with the real wrapper and a fake claude.
  • The latest wrapper (--monitor-id) inside a real Claude Code plugin monitor

🤖 Generated with Claude Code

PavelKaloshin and others added 30 commits June 3, 2026 21:50
Add a directory review mode: `rd open <dir>` opens a browsable, recursive
tree of the directory's Markdown files in a persistent sidebar (JupyterLab
style). Selecting a file opens it in the existing single-document workspace;
browser back/forward moves between previously opened files. Single-file mode
is unchanged.

- CLI: resolveTargetPath accepts a directory and buildTargetUrl emits ?dir=;
  directory open forces no-watch and rejects --watch.
- Server: listProjectTree skips node_modules and hidden/dot directories.
- App: ?dir navigation (getWorkspaceState, buildLocationForDirectorySelection),
  StorageBackend.listFileTree, DirectorySidebar tree + empty state, App routing
  branch with popstate handling.
- Docs: ADR 0005 (refines, does not override 0001) + screenshot guide states.
- Tests: CLI, server file-tree ignore-list, navigation, tree builder, and a
  @smoke spec covering list/open/switch/back.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Poll listFileTree every 2s in directory mode so files a coding agent (or any
process) creates, deletes, or renames on disk appear in the sidebar without a
reload. The open file already live-reloads via the existing disk watcher; add
smoke coverage for both in directory mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Clicking a file in the tree silently did nothing when the current file had
unsaved edits or had changed on disk: the switch was gated behind
window.confirm, which a Chrome app window / headless context auto-dismisses.
Roughdraft autosaves and never overwrites the target file without an explicit
action, and the beforeunload guard still covers tab close, so just load the
selected file. Adds a regression smoke test that switches with unsaved edits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Directory windows registered their open-request channel under the selected
file path (or nothing), so `rd open <dir>` never matched an existing window and
spawned a new one each time, stacking stale windows. Register the channel under
the directory path in directory mode, and keep the currently open file when the
same directory is reopened. Adds a reuse smoke test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a TipTap React NodeView on the code-block node that renders ```mermaid
fences as diagrams in the rich-text editor; other languages keep the normal
editable code block. mermaid is lazy-loaded so it stays out of the base bundle
and the markdown parse path. Serialization still uses the schema, so the
```mermaid source round-trips unchanged (ADR 0003) and stays editable in code
mode. Adds a round-trip unit test and @smoke e2e coverage (real SVG render +
source preserved).

Also add a Makefile of explicit dev targets (test/check/build/smoke/dev CLI)
as a reviewable command surface.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
/api/files served files via res.sendFile, whose default dotfiles: "ignore"
404s any path that traverses a dot-directory. Projects opened under a dot-path
(e.g. ~/.claude/skills/...) therefore failed to load every non-markdown file,
while markdown still worked because /api/markdown-file reads via fs.readFileSync.

Pass { dotfiles: "allow" }; ensureProjectPath already confines the path to the
project boundary. Adds a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The directory tree now lists every file, not only .md. Markdown still opens in
the full review/comment/edit workspace; all other files open read-only:
- code with on-demand syntax highlighting (@codemirror/language-data),
- plain text as monospaced text,
- images as a preview,
- other binaries as a "preview unavailable" stub.

Non-markdown files reuse the existing /api/files endpoint (no new server
surface) and cannot be commented on, edited, or saved. Updates ADR 0005 and the
UI screenshot guide; adds dev-task Makefile targets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an always-available "Reload from disk" control in the document header so a
reviewer can force-refresh the open file regardless of the disk-change watcher,
and switches the markdown/file reads to fetch with cache: "no-store" so manual
and watcher-triggered reloads always read current disk content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tables with a row holding two or more code-span links (e.g. two
`[\`ID\`](path)` references) vanished from the rich-text view. The pipe-span
guard used a `/\`[^\`\n]*\|[^\`\n]*\`/` regex that paired the closing backtick
of one code span with the opening backtick of the next and read the cell
separator `|` between them as living inside a code span. That false positive
made protectPipeSensitiveTables wrap the whole table as a raw block, so it (and
the markdown after it) stopped rendering.

Match real code spans instead and check each for a pipe. Adds regression tests
covering the two-link case, the adjacent-code-span case, and a genuine
pipe-in-code-span table that must stay protected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	docs/spec/ui-state-screenshot-guide.md
#	packages/app/src/App.tsx
#	packages/app/src/DocumentWorkspace.tsx
Commit a .claude/settings.json that allowlists read-only search/inspect and read-only/standard git plus the Makefile-driven flows, and denies footguns (awk, sed -i) and writes to the trusted files. New Makefile targets (rd-restart, build-app, api, verify-ui) and scripts/dev/verify-ui.mjs wrap previously-prompting flows; the Makefile and that script are write-denied so the surface can't be silently widened.

biome no longer lints .claude/; .gitignore keeps settings.json tracked while settings.local.json stays ignored. AGENTS.md documents which trusted path to use instead of each raw command, and that git must be run bare (not via 'git -C') to match the allowlist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Links in the rich-text view are now clickable: sibling .md links navigate in-app and keep directory mode (the sidebar and the dir path-boundary are preserved), external links open in a new tab, and in-page anchors scroll. buildLocationForLinkedMarkdownDocument now keeps the ?dir= query param so following a link from a folder stays in that folder.

Adds a persistent 'Full width' toggle in the document header and the read-only file viewer that expands content to the full width (minus the sidebar); the choice is stored in localStorage so it survives reloads.

Tests: view-preferences round-trip, app-navigation dir preservation, and e2e coverage for link navigation (including a link inside a table cell) and full-width persistence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… doc

Inline-code links like [`AC-E`](doc.md#AC-E) rendered as a bare <code> with no anchor because the Code mark excluded the link mark, so they were never clickable. Drop 'link' from the Code mark's excludes; code + link now coexist and the link stays clickable.

The full-width toggle only restyled the header row: the PageCard body keeps its own review-layout grid, so the document stayed capped at 46.5rem. Drive full width from a data-full-width ancestor attribute whose CSS lifts the max-width and grid-template cap on every review-layout-main/grid underneath it (header and body alike).

e2e now covers code-span links (incl. anchors) and asserts the document body actually widens, not just the toggle state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Empty in-document anchors like <a id="REQ-11"></a> were dropped during rendering, so #REQ-11 links had no target to scroll to. Add an anchorTarget inline node that preserves hrefless id anchors in the editor DOM, and a turndown rule that round-trips them back to <a id="..."></a> instead of an empty [](./) link.

After following a cross-document link with a #hash, PageCard now scrolls to the matching anchor once the new document renders; same-document anchor clicks still scroll directly.

Tests: anchor preservation + round-trip unit tests, and an e2e that follows a #anchor link into another document and asserts it scrolls into view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`open` now prints the document URL and waits as before, but no longer opens a browser unless you pass --open (the URL is yours to open). --print-url still prints-and-exits; ROUGHDRAFT_NO_OPEN=1 force-suppresses even with --open.

Flip the parser default to noOpen, add an --open flag, refresh the help/examples, and update the CLI tests (existing open-behaviour tests opt in with --open; a new test asserts the no-browser default). AGENTS.md notes the new default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Roughdraft had no math support, so TeX written as code spans rendered as
literal monospace text. Add KaTeX rendering across the full pipeline:

- marked extensions tokenize `$…$` / `$$…$$` and render via KaTeX, keeping
  the raw LaTeX in a data attribute for round-tripping. Registered on both
  the global `marked` (toHtml) and the editor's `createCriticMarked` parser.
- MathInline/MathBlock Tiptap atom nodes + MathView NodeView re-render the
  formula in the editor from the stored LaTeX.
- Turndown rules serialize the nodes back to `$…$` / `$$…$$`. The nodes emit
  their LaTeX as text content so Turndown does not treat the empty atom
  element as blank and drop it before the math rule runs.
- Import KaTeX CSS; add .math-block/.math-inline styles.

Covered by packages/app/test/math.test.ts (render, lone-`$` ignored, inline
and block round-trip) and a new Math fixture row in the UI screenshot guide.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`pnpm test:selectors` flagged two locators in links-and-layout.spec.ts that
target rendered-markdown DOM with no data-testid by design: a `<table>` from a
markdown table and the `#REQ-11` in-document anchor (the id is the behavior
under test). Mark both with the script's sanctioned `selector-check-ignore`
exception so the check passes without weakening the convention globally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the browser update-notice banner and its client-side version check:
delete UpdateNotice.tsx and the app-side update-status.ts, remove the
`updateStatus` state/effect and the `Homepage` `updateStatus` prop, and update
the homepage test accordingly. The server `/api/update-status` endpoint and the
CLI's update notice are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a heading outline so a reviewer can jump between sections instead of
scrolling. Directory mode gets a collapsible heading list under each .md row
in the tree, in a tinted accent-bordered box so entries never read as files;
single-file mode has no tree, so it gets its own collapsible outline rail.

Outlines stay honest while a file changes underneath them. The open document's
outline is derived from the content the existing watcher already refreshes, so
it cannot disagree with what is rendered; other expanded files are re-read on
the cadence the tree already polls at, with equal outlines suppressing renders.

Headings are read with the same split-then-lex pipeline the editor uses, so
parsed positions map 1:1 onto rendered headings. Rich text jumps scroll that
element; code mode scrolls to the heading's source line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The review watch was a single unbounded long-poll, so Node's built-in fetch
(undici, headersTimeout 300s) crashed `roughdraft open` with
UND_ERR_HEADERS_TIMEOUT whenever a review took longer than five minutes.

Poll in bounded 60s rounds instead, anchoring the event cursor with an
immediate first round and resuming each round from the last nextSequence so
a review completed between rounds is not missed. Undici timeouts are retried
as a safety net. Without --timeout the CLI still waits indefinitely.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Edit(path) deny rules already cover every file-editing tool, so the
Write(...) and MultiEdit(...) duplicates only produced startup warnings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An open tab re-saved agent-written documents after every external change,
and every save re-serialized the whole file, so untouched documents were
rewritten on disk.

- Ignore editor updates whose root transaction did not change the doc.
  TrailingNode appended an empty paragraph to meta-only transactions
  (highlight refreshes) for docs ending in a list or fence, which marked
  the document dirty and autosaved it.
- Keep the author's heading spacing: detect whether headings are followed
  by a blank line and only apply the compact normalization when they are not.
- Stop tight lists from gaining whitespace-only separator lines.
- Serialize fenced code verbatim instead of through Turndown, which
  collapsed newlines in fences that contain review markers.
- Write bare `{>>comment<<}` and dangling `{#id}` comments back as they
  were while untouched, instead of inventing id/by/at metadata.

Whether review markers inside fences should be literal (spec) or live
comments (Lex-Inc#18) is left open; that test is skipped pending a decision.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Root comments may carry intent: comment|ask|change in YAML endmatter or
inline attributes. extractRoughdraftReviewIndex exposes it on root comments;
unknown values and intent on replies/suggestions are warnings and ignored, so
newer documents stay valid. Writers already preserve unknown keys, so intent
survives appending replies and resolving. Multiline reply bodies round-trip
as YAML block scalars.

Also fix appendRoughdraftReply for endmatter-only parents (document-level
comments): the reply now lands in endmatter instead of being spliced inline
before the --- delimiter.

Spec, schema and a conformance fixture document the key.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…egistry

POST /api/review-events now accepts {type, commentId} for comment.ask,
comment.change and comment.followup. The server builds the event from the
saved file (anchor, body, author, short context, replies, thread root,
intent), 404/400s on missing or misplaced targets, and dedupes on
(type, commentId, latest reply id). review.completed is unchanged.

The watch long-poll gains a types filter (default stays review.completed,
so open/watch/MCP behave as before), skipHandled, listener identity, abort
on client disconnect, and returns instanceId/latestSequence so listeners can
detect restarts. The event buffer keeps 1000 events and never trims ask/change
events still awaiting an agent.

An in-memory AgentRegistry (ADR 0004 style) tracks listener presence (open
long-poll plus grace period) and per-thread status pending -> working ->
answered/applied, exposed via GET /api/agent/status and the SSE stream
GET /api/agent/events. New write endpoints: POST /api/agent/ack,
POST /api/comments/reply (rfm appendRoughdraftReply with optimistic version
check and retry) and POST /api/comments/resolve.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
roughdraft listen <file.md> registers as the document's listener and prints
exactly one self-contained line per ask/change/follow-up event (newlines
escaped, long text truncated with a hint), ending in the exact command to
answer. It polls in bounded 50s rounds with a sequence cursor (no undici
300s headers timeout), retries through server restarts, resets its cursor
when the server instance changes, and exits before Monitor's 30-minute
deadline (--max-seconds, default 1740) with a re-arm line carrying --since.
Without --since it replays retained events whose thread is still unanswered.

reply/ack/resolve go through the server (never write the file directly):
reply appends an AI reply (--stdin or --body, multiline Markdown) and sets
the thread status to answered or applied; ack sets working; resolve marks
the thread root resolved.

The watch endpoint now answers immediately when a cursor belongs to an
earlier server process, so a listener does not sit out a full round
waiting on sequences that will never come.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A listening agent no longer needs a separate blocking `open` to learn that
the user finished: `listen` prints a `[roughdraft] done …` line with the
review summary. Completions from before the listener started are skipped,
unlike unanswered comment events, which are still replayed.

Also widen the server-restart listen test's window: reconnect backoff could
outlast the old 3s budget on a loaded machine.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Record the decision to carry agent conversations as ordinary CriticMarkup
threads with an optional `intent`, with listener presence and thread status
kept in server memory (refines ADR 0002 and ADR 0004). Add the Claude Code
skill and setup/prompt guidance for `open --print-url` + Monitor on
`roughdraft listen`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PavelKaloshin and others added 28 commits October 18, 2026 17:13
…iming

Only add threadDetails when the server sends it, so the live-socket
status channel stays exactly what an older server pushed. Point the
four-tab smoke test at the status label, which no longer includes the
elapsed timer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A bare {>>...<<} has no id in the file, so every parse generates one past
the ids it has seen. Adding a comment or reply elsewhere takes the next id
in the editor, so re-parsing the saved splice numbered the bare comment
differently, the guard saw a different document and wrote the full
serialization: an untouched list item with the comment on its own
continuation line was rewritten with the comment joined onto the item.

The guard now compares bare comments by position instead of by their
generated id, in both the round-trip and the composition check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A document's Ask/Change/follow-up/Done events now go only to the session
that owns it. Sessions claim documents automatically (never from a live
owner) or by force (reassign, which tells the previous owner). New routes:
POST /api/sessions/{claim,release,listen}, GET /api/sessions. The agent
status snapshot gains additive owner and sessions fields.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
listen --session <id> follows every file the session owns, across
projects (lines carry file=), prints claimed/released lines, re-sends
the plugin's claims file on every (re)connect and keeps it in sync.
--quiet prints only events; --no-start never starts a server and waits
for one. claim [--force] makes a session the owner; sessions lists live
sessions; setup claude prints or runs the plugin install commands.
open claims the file for the calling Claude Code session.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Long agent answers are hard to read in the ~280px rail. Long comment
bodies (>400 chars or >6 lines) now clamp with a fade behind Read more,
and every comment can open a thread reader: a shadcn Dialog (720px,
80vh, scrolls inside) with the anchor quote, intent badge, agent status,
the whole thread as full-width Markdown, Reply / Resolve / Reopen, and
prev/next navigation in document order (arrows or J/K). It renders from
the same comment state as the rail, so live replies appear while open.

Shortcuts: O on a focused thread, Cmd+Option+O / Ctrl+Alt+O for the
selected thread. Closing returns focus to the rail thread; Reply hands
off to the rail composer. Near full-screen below 640px.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
roughdraft answer-fork <file> <id> runs claude -p --resume <session>
--fork-session, allowed only Read and roughdraft ack/reply (Edit, Write and
NotebookEdit removed, dontAsk). It claims the thread as delegated to a
fork-process first and puts it back in the queue if the fork fails or
times out. listen --fork-asks-after <s> runs it for an Ask the session
left queued, once per user message, at most two at a time, never for a
Change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
packages/claude-plugin is a plugin plus a local marketplace, installed
once at user scope (roughdraft setup claude). Its always-on monitor keeps
`roughdraft listen --session <id> --quiet --no-start` running with its
own restart loop and prints nothing unless there is an event. A
PostToolUse hook (Write|Edit, async) claims written .md files; a
SessionStart hook records the session id/name for the monitor and adds
one line of context. The bundled skill drops the arm-Monitor step.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The tab's live file channel compares content versions, not mtime and
size, and re-checks shortly after subscribing. So the external edit was
loaded into the editor before the test typed, the local append landed on
top of it and the save correctly succeeded: the conflict path under test
was never reached. Block file events like the sibling conflict tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The mobile storyboard test queried .homepage-workflow-scene-copy and
.homepage-workflow-panel-header, classes removed when the app UI moved
to Tailwind (Lex-Inc#75). The selector check missed them because the selector
string sits on the line after querySelector(. Give the scene copy a
data-testid and use the existing popup header test id.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
At a 390px viewport the expanded 15rem rail sat in the layout and left
the document a sliver (the code editor was 9px wide, so every character
wrapped). Below 768px the rail now starts as a strip, opens over the
document instead of beside it, closes when a heading is picked, and does
not touch the saved wide-screen preference.

The collapsed strip's toggle also sat under the fixed corner save status,
which swallowed clicks, so a collapsed rail could not be reopened with
the mouse. The toggle and the Outline label now start below or beside it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The save status moved to a fixed top-left corner (Lex-Inc#124), after which the
right-hand status stack only holds the review handoff button and is empty
(so hidden) without a watcher; the layout test still expected the save
status in that stack and failed on visibility. Rewritten to check both
real neighbours of the banner with a watcher connected, it showed a real
overlap: at phone width the full-width banner ran under the corner save
status. The banner now leaves 3rem on each side (still 52rem on desktop).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The check walked .claude/worktrees (other checkouts of this repo, used by
agents), so an unrelated in-progress branch could fail `make check` here.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The header names the session that receives the file's comments
("Claude · <name> · <folder>" / "Claude · No session") and opens a
shadcn dropdown, Connect session, listing live sessions (same folder
first) to reassign the file; with none it shows roughdraft setup claude.
Adds the dropdown-menu primitive (base-ui) and parses the additive owner
and sessions status fields; older servers keep the old label.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… listening

Integrates --fork-asks-after with session mode: listen --session takes
--fork-asks-after/--claude and forks a still-queued Ask per owned
document (SessionForkObserver); the plugin monitor passes
--fork-asks-after 15 (ROUGHDRAFT_PLUGIN_FORK_ASKS_AFTER=off disables).
listen <file> --session <id> stays a per-file listener and now reports
that session (so it claims the file).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Records one owner per document, the session-scoped listener and the
Claude Code plugin; updates ADR 0006 consequences, setup.md/prompt.md
(plugin install, no manual Monitor when installed), the standalone skill,
and the plugin skill's fork lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With no state file the CLI probes its default port 7373, so the two
--no-start tests found the developer's running Roughdraft and failed only
on machines where one was up. Pin the default port to the test server.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
listen --session --quiet --fork-asks-after 0 (the plugin default now) hands
every Ask to a headless fork at once and keeps its line from the session;
the line is printed only when no fork answers it (failure, no claude, an
unreadable status). --quiet also drops the 'forked cN' line.

Ask roots get an optional RFM 'model' (fast by default, balanced, session),
carried on events and event lines when not the default; the fork passes
--model haiku/sonnet and retries once with the session model on failure.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When Ask is selected on a new root comment, an 'Answered by' radio group
(Fast preselected) sits under the modes. M cycles the model in the keyboard
step, Tab moves into the picker where arrows move the model, and the step
hint names the key. A non-default choice is written as endmatter 'model'.
The status line says 'A forked Claude is working…' once a fork acks.

Docs: RFM spec Ask Model, ADR 0006 addendum (fork immediately, fast by
default, the cache/context tradeoff), screenshot guide, and both skills
(Asks rarely arrive in plugin mode; never retell a subagent's answer).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Below 1100px comments render inline (banner variant). The selected or
hovered comment body painted bg-white with no dark variant, so inside the
dark card the composer showed a white panel with invisible light labels
(Me, Comment, Change, Balanced, Session). Use the card surface token in
dark mode; light mode is unchanged.

Adds a dark-mode e2e for the side rail and inline layouts that checks the
composer shares the card's surface and its labels reach 4.5:1 contrast.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The grace-period test relied on a ~50 ms wall-clock window and failed
under load. createApp now accepts agentClock (passed to the agent
registry and session ownership), and the test waits for the long-poll
to register instead of sleeping, then steps the clock across the exact
grace boundary.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
At --max-seconds, listen --session waited (up to the 5-minute fork
timeout) for running headless forks without polling, so the plugin
wrapper could not restart it and a Change arriving meanwhile sat
unprinted. Now listen drains: it starts no new forks, keeps polling and
printing Changes and notices, and exits as soon as the last fork ends.
Asks arriving while draining stay queued on the server and are forked
by the next round (tested, together with Asks dropped while waiting for
a fork slot).

The "claude CLI not found" notice was printed once per document; it
is now printed once per listen process, and the claims file records it
so later wrapper rounds do not wake the session again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every round of the plugin's listen loop starts from cursor 0, so a Change
the session had not answered yet (queued or working), a claimed notice or
an Ask a fork gave back was printed again each round and woke the session
again. The wrapper now passes one --monitor-id per process; listen reports
the lines it printed (POST /api/sessions/printed) and the server skips them
for later rounds of the same session and monitor. Fork-taken or dropped
Asks are not recorded, so they are still forked next round; a new process,
a different monitor or a server restart gets everything again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 22, 2026

Copy link
Copy Markdown

@PavelKaloshin is attempting to deploy a commit to the Lex Team on Vercel.

A member of the Team first needs to authorize it.

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