Skip to content

feat(native-chat): teach chat agents to show inline visuals in their own folder - #26099

Merged
brennanb2025 merged 29 commits into
mainfrom
brennanb2025/inline-visuals-skill
Oct 7, 2026
Merged

brennanb2025 merged 29 commits into
mainfrom
brennanb2025/inline-visuals-skill

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 14 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1603 $\color{#cf222e}{\Huge{\mathbf{−}}}$​192 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1411
Prod 32 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1398 $\color{#cf222e}{\Huge{\mathbf{−}}}$​239 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1159

ELI5

The base PR (branch brennanb2025/inline-visuals-render) lets Orca's native chat show an HTML chart or diagram inside a reply when the reply contains a ::orca-visual{file="…"} line. But nothing told the agent it could do that, where to put the HTML file, or let it write there. This PR closes that gap. Every native chat now gets its own private folder for visuals. The agent may write into exactly that folder, it is told where the folder is, and it gets a short Orca skill explaining when and how to make a visual. The folder is cleaned up once its chat is gone.

What Changed

Before: a Claude or Codex native chat didn't know inline visuals existed. Even if it guessed the syntax, it had nowhere sanctioned to put the HTML file: writing into the user's repo would dirty git status, and writing anywhere else needed approval or failed.

After, what you see: ask a native-chat Claude or Codex for something easier to see than read (say "compare p95 latency by region") and it can write one self-contained HTML page and end its reply with a visual line. The base PR renders it in the reply and in the right sidebar. Nothing is written into your project. In plan or read-only mode, or when any piece below is unavailable, the agent answers with a Markdown table, a Mermaid block or prose instead, as the skill tells it to.

Mechanism:

  • Per-chat folder, owned by Orca, on the host that runs the chat. It lives at <Orca data>/native-chat-visuals/<hash of the chat id>/ (the base PR's resolver) and is created at launch, readable only by the user. If creating it fails, the chat still starts, without visuals.
  • Write access to exactly that folder:
    • Claude: the folder is appended to the session's additional directories, after any --add-dir the user configured.
    • Codex: the folder is appended to the thread's sandbox_workspace_write.writable_roots. Codex replaces that list instead of merging it, so Orca first reads the user's own effective roots (config/read for that working directory) and keeps them. If they can't be read, Orca overrides nothing; a write there then asks for approval like any other outside path. Under full access nothing is added, because everything is writable already.
    • The user's permission mode is unchanged. Claude in Manual mode still asks before each write, as it does for any file. If a write is refused, the skill tells the agent not to retry.
  • Where the folder is: each chat has its own agent process, so the path goes into that process's environment as ORCA_CHAT_VISUALS_DIR and never into the skill text. A value Orca itself inherited (say, Orca started from inside a chat) is stripped from both agents' environments, so a chat can never be pointed at another chat's folder.
  • The skill: an original ~60-line SKILL.md, shipped as an unpacked plugin folder (resources/native-chat-visuals/, Claude plugin layout). It ships in the desktop app's resources and in the headless server (orcad) build and install manifest. It is resolved on the host that starts the agent, and a packaged app never loads it from a checkout.
    • Claude: passed as an SDK plugins entry (--plugin-dir) when the installed CLI accepts that flag. The flag first appears in Claude Code 2.0.25; I read the published packages, and 2.0.24's option parser has no such option. The skill is marked user-invocable: false, so it stays out of the / command menu while the model still sees it.
    • Version check: the existing per-binary version probe used for --thinking-display now serves every version-gated flag (one probe per binary and folder). Only a confirmed version is remembered. A probe that times out or fails is forgotten, so the next launch asks again instead of latching "unsupported" at boot. The probe also starts when native chat starts: in the home folder, which loads the binary from a cold disk, and in the folders of open Claude chats pinned to one (capped at 4). The answer is kept per folder, because a version manager's shim can pick a different CLI per project. So a chat in a folder not yet asked still runs its own probe, but against a warm binary (tens of milliseconds). Readable thinking keeps its 1.5 s budget, and is asked again once the longer skill wait has learned the version, so a slow first probe no longer drops it.
    • Codex: skills/extraRoots/set with the skill root, sent after initialize and before the thread opens, on every start, resume or respawn. Each chat has its own app-server, so the process-wide list is exactly Orca's roots. Both Codex requests run in parallel under a 2 s budget instead of the 30 s request default. An unsupported method (older Codex answers "unknown variant", newer "method not found"), an error or a hang leaves the chat working without the skill.
  • How the folder dies. The host never deletes a chat record today: closing a chat only hides it, and reopening restores it. So there is no deletion event to hang cleanup on. Instead a host sweep derives each folder's lifetime from the records. It runs 2 minutes after startup and then every 6 hours:
    1. A folder that no chat record (readable or not) maps to is removed, so any future record deletion cleans up for free.
    2. A folder whose chat ran in a local workspace that is provably gone is removed. Chat records and visuals are shared by every Orca profile while each profile keeps its own workspace list, so "gone" means one of:
      • the project or folder workspace is in no profile's list;
      • for a worktree Orca no longer tracks inside a project that still exists: its directory is definitively missing, the project itself is present, and git's own worktree records no longer name it (absolute or, with git 2.48+, relative records). A worktree on an unmounted drive is still recorded by git, so it is kept.
      • "Every profile" means the running profile's live list plus every other profile's saved list. The running profile is identified by its own storage folder, not the profile index a profile switch rewrites first. A profile that was created but never written to counts as empty.
    3. Everything else is kept: another host, WSL, the floating workspace, any profile whose data can't be read, a read-only record store, any error.
    • Only names the host minted are touched; symlinks and a symlinked root are never followed. A run removes at most 200 folders, and every failure is logged and never blocks anything.

Why

  • Skill plus reply line instead of a host tool: the user's chosen design. The cost is no visuals in plan or read-only turns; the skill falls back to Markdown there.
  • An Orca-owned per-chat folder instead of the workspace: nothing lands in the user's repo, there are no .gitignore or git-exclude edits, and the folder can be cleaned up with the chat.
  • An env var instead of a prompt instruction: each chat has its own agent process, so the environment is already per chat, and the skill text stays identical for everyone.
  • Read-then-append for Codex writable roots: the only stable per-thread way to add a writable root that doesn't silently narrow the user's own access. Codex's experimental "runtime workspace roots" thread field has the same replace semantics and also changes what Codex treats as the workspace, so it wasn't used.
  • A long, bounded wait plus an early probe instead of "pass the flag and retry if refused": a retry would sit inside the Claude start lifecycle, and on an old CLI the first start would visibly fail. With the early probe the wait is normally zero.
  • A derived sweep instead of a delete hook: there is no chat deletion to hook, and a stored "cleanup owed" flag would strand. Re-deriving from the records on every run gives each folder a way to die whenever its record or workspace goes.

Worst-case delay before a chat starts

  • Normal case: none, or tens of milliseconds. The version check runs when native chat starts. Measured on this machine (load average ~23), Orca's probe takes 10–24 ms warm, and 109–173 ms on a Claude binary not loaded for weeks. In the cold-start QA run below, the first chat after a fresh start waited about 0.3 s, for its workspace folder's warm probe; the second chat waited for none.
  • Worst case: up to 10 s, the probe's own kill time. This applies only to a chat whose CLI version is still unknown (first chat in a folder after Orca starts) when claude --version takes that long or hangs. The chat then starts without the skill, nothing is remembered, and the next launch asks again. The user's prompt is never held longer than that, plus at most 0.25 s for the thinking re-check.

Differences from the common pattern

  • Per-chat app-owned storage, an env var naming it, and a write grant for the agent: matches the common pattern's mechanism. Difference: the grant covers the one chat's folder, not the whole storage root. Intended, because it is narrower access.
  • Skill delivered as a Claude plugin directory and as a Codex skill root set after initialize: matches. Differences: Orca gates the Claude flag on a version check and bounds the Codex call. Intended: Orca runs whatever CLI the user installed, so older CLIs must still start.
  • Storage cleanup: the common pattern deletes storage when a thread is deleted. Orca has no chat deletion, so a sweep derives it. Intended while chats are never deleted; if record deletion is added later, rule 1 already covers it.
  • Codex writable roots are unioned with the user's configured roots, where the common pattern replaces them. Intended: never narrow the user's access.
  • Visuals for chats over SSH or on a remote runtime: temporary. Structured sessions don't run over SSH yet, and delivery follows when they do. The skill and folder are already resolved on the process-owning host and ship in the headless build.
  • An older viewer, from before the base PR, shows the visual line as plain text. Intended: native chat has no real users yet.

Linked Issue

N/A (maintainer). Part of the inline chat visuals work, stacked on brennanb2025/inline-visuals-render.

Visual Proof

Cold-start run on the final head (ab3e2d3): a freshly started, isolated, hidden Orca built from this branch. Its Claude is a stand-in program, not a real agent, that writes one chart into the folder named by ORCA_CHAT_VISUALS_DIR and ends its reply with the visual line. The UI was driven by a separate QA agent through Chrome DevTools on the hidden window.

First chat after the cold start: the folder, the grant and --plugin-dir were all handed over, and the chart renders inline. No raw ::orca-visual text appears anywhere on the page, the sidebar included.

31-final-first-chat.png

A second chat in the same app:

32-final-second-chat.png

Open in sidebar, from an earlier run:

04-sidebar.png

Testing

Platforms actually run: macOS only. Windows/Linux are covered by the cross-platform code paths and unit tests, not run live.

  • Unit and integration tests: new tests for the folder, env and skill delivery, the shared Claude version check (no latching of failures, per-call budget, prewarm), the Codex setup (unsupported, failed, hung, full access, user roots kept), the sweep (orphans, cross-profile, unmounted drive via git records, symlinks, failures) and the other-profile catalog. 193 explicit test files that import the changed modules: 192 pass, 1 file skipped as on the base branch.

  • Real CLIs, opt-in: run with ORCA_REAL_CLAUDE_CLI_TEST=1 / ORCA_REAL_CODEX_CLI_TEST=1:

    • The required claude-structured-real-cli and -fold suites pass.
    • Real Claude 2.1.280 launched through Orca's own resolver gets the plugin, and the model names orca-chat-visuals from its skill catalog. The skill appears in neither the CLI's skill list nor its command list, so it never reaches the / menu.
    • Real Codex 0.159.0 lists the skill, and opens the thread with the user's writable root plus the chat folder.
    • Real ~/.codex and ~/.claude settings hashes were unchanged across the runs.
  • Live app, cold start: hidden isolated rig built from this branch, with a stand-in Claude and a separate QA agent driving the UI through Chrome DevTools.

    • The first chat after a fresh start received the folder, the grant, the env var and --plugin-dir, and its chart rendered. So did a second chat.
    • The sweep removed a planted orphan folder about 2 minutes after start, and kept an entry Orca didn't create plus both live chats' folders.
  • Not verified:

    • A real model turn that writes a visual end to end (the live run used a stand-in agent).
    • Codex applying the writable-roots override on thread/resume. A fresh thread has nothing to resume, so this would need a real model turn; it is covered by unit tests against the request shape.
    • Windows and Linux live runs.
  • I manually tested these changes locally

  • Automated tests added/updated

Review

See the review-summary comment.

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation. (The skill text is original.)

Notes

  • Security: the grant is one Orca-owned folder per chat. The sweep removes only names it minted, never follows symlinks, and keeps anything unproven or owned by another profile.
  • Cross-platform: paths go through node:path; path comparison uses the shared normalizer; the env-var strip handles Windows key casing; the folder is owner-only on POSIX.
  • SSH / remote: delivery and the sweep run on the host that owns the chat. Another host, WSL and loss of contact are never treated as removal.
  • Mobile: no change here. Rendering is the base PR and its mobile sibling.
  • Performance: one mkdir per launch. The Claude version check is shared with the existing one and prewarmed. Codex setup is two local requests in parallel (4 ms against Codex 0.159.0). The sweep is bounded and runs rarely.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

Author: @BrennanKB5

…s visuals folder

A shared grammar for the ::orca-visual{file="..." title="..."} reply line,
the per-chat visuals folder location on the owning host, and the
agentSession.readVisual runtime method that reads one visual with lexical and
canonical containment, a 512 KiB bounded read and UTF-8 refusal.
Native-chat assistant replies render a ::orca-visual{...} line as the chat's
HTML visual in an opaque, scripts-only sandboxed frame: CSP first, the host
frame navigation guard registered before content runs, live theme without a
reload, fitted height, links opened in the viewer's browser only from a real
gesture, lazy mount, and one muted line when the visual cannot be shown.
Open in sidebar shows the same frame in the right sidebar, widened while it
is open and restored after.
…own folder

Native-chat Claude and Codex sessions now get a per-chat visuals folder on
the host that runs them, write access to exactly that folder, its path in
ORCA_CHAT_VISUALS_DIR, and an Orca skill that teaches the ::orca-visual line.

- Skill ships as an unpacked plugin folder in desktop and headless builds.
- Claude: --plugin-dir via SDK plugins, behind the CLI version probe (now one
  shared probe for every version-gated flag); folder added to
  additionalDirectories beside the user's own.
- Codex: skills/extraRoots/set and the folder appended to the user's own
  writable roots, between initialize and the thread open, under a 2 s budget;
  unsupported, failed or hung setup opens the chat without visuals.
- A host sweep removes folders no chat record maps to, and folders whose
  local workspace is provably removed; anything unproven is kept.
…streaming hold

Registers agentSession.readVisual from the methods index so the structured
method file stays under its line budget, replaces reflective reads with checked
narrowing, moves the pure height governor to src/shared for mobile, and holds a
half-written directive tail while the turn works (structured text rows carry no
running state).
Re-checks after the open that the chat's visuals folder is still the real
directory at Orca's path, reports unexpected filesystem faults by code without
host paths, and lets one click in a visual open at most one page.
… review fixes

One shared helper drops visual lines (outside fenced code) from reply text where
it becomes plain text: the structured status summary that feeds the sidebar row,
dashboard, notifications, phone rows and handoffs, and AI Vault reply previews.
Review fixes: height also counts a pinned body's overflow, only the live
frontier row holds a half-written visual line, the runaway-height stop needs the
same step repeated, and any host refusal evicts the cached revision.
- Visuals sweep: a workspace counts as removed only when no profile's
  catalog holds it (chats and visuals are shared by every profile, catalogs
  are not); a worktree in a known project is removed only when git no
  longer records it; any unreadable profile decides nothing; one catalog
  snapshot per run; a symlinked visuals root is never walked.
- The other-profile catalog reader moves out of window/ and also returns
  project ids.
- A folder Orca itself inherited is stripped at the spawn layer for both
  agents, not only from the launch overlay.
- Claude version probe: a probe that gave no version is never remembered;
  the plugin check waits up to the probe's kill time so a slow first probe
  no longer costs a chat its skill.
- Skill: kept out of Claude's / menu, filename and theme guidance matched to
  the renderer, refused writes are not retried.
- Opt-in real Codex test for skill discovery and the writable root.
…arts

The first chat after Orca starts usually finds the version known, so the
plugin and thinking-display checks answer at once instead of probing a
cold binary while the chat waits.
…nal-paths helper

Main removed the per-chat journal paths and the journal database's state
directory; the visuals folder keeps the same sha256 layout on its own and the
read method uses the profile state directory the chat host is opened in.
…er' into brennanb2025/inline-visuals-skill

# Conflicts:
#	src/main/claude/claude-structured-launch-resolution.ts
#	src/main/native-chat/native-chat-visuals-folder.ts
#	src/main/runtime/agent-session-record-store-file.ts
#	src/main/runtime/agent-session-record-store.ts
#	src/main/runtime/structured-agent-runtime-registrations.ts
…al lines

Reply previews in Agent Session History drop visual lines per text part before
lines are folded; the frame adds a body's overflow only when the body really
overflows; fence tracking follows CommonMark closers and openers; the copy
button copies a reply without visual lines; a coded read fault keeps its cause.
- Read git worktree records written relative to their own folder (git 2.48+),
  so a worktree on an unmounted drive in such a repo is still kept.
- Skip the running profile by its own storage folder, not the profile index a
  switch rewrites first; a profile never written to counts as empty, so the
  workspace rule is not switched off by a profile that was never opened.
- Ask for readable thinking again once the plugin check has waited for the
  version, so a slow first probe no longer drops it for the chat's life.
- Launch flag decisions move to their own module; tests use a typed record
  fixture instead of casts.
- Resolve a relative git worktree record against its folder's real path, so a
  project added through a link still matches and its worktree is kept.
- A profile with only backups of its data file is a lost file, not a fresh
  profile: it still stops the workspace rule.
- The readable-thinking re-check reads what is known and never starts a
  second version probe.
@brennanb2025

brennanb2025 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Review summary

Review rounds (Claude reviewers, independent of the author)

Round 1, two reviewers in parallel (correctness/design; deletion and grant safety) on the feature commit:

  • P1, fixed: the visuals sweep could delete another profile's chats' visuals. Chat records and visuals live at the user-data root, shared by every Orca profile, but the workspace catalog belongs to one profile. So a project held only by another profile looked "removed". Now a workspace counts as removed only when no profile holds it, read through the existing other-profile reader (moved to orca-profiles/other-profile-workspace-catalog.ts). Any profile that can't be read decides nothing.
  • P2, fixed: an unmounted drive could look like a removed worktree. "Folder missing, parent present" proved nothing. Now a worktree in a known project is removed only when git's own .git/worktrees/*/gitdir records no longer name it.
  • P2, fixed: the env-var strip didn't survive the spawn layer. Both spawn paths merge process.env back in. Claude now strips the key from the inherited env, and Codex adds it to envToDelete. The tests now assert on the env the child actually receives.
  • P3, fixed:
    • The skill would have appeared in Claude's / menu; it's now user-invocable: false. Against the real CLI, the model still names it and neither of the CLI's lists contains it.
    • The skill's filename and theme guidance now match the grammar and the frame.
    • A symlinked visuals root is never walked.
    • Each sweep run judges against one catalog snapshot.
    • An opt-in real-Codex test was added.
  • Live QA finding, fixed: the first chat after a cold start missed --plugin-dir because the version probe exceeded its 1.5 s budget, and the failure was latched for 10 minutes. Now:
    • only a confirmed version is remembered;
    • the plugin check waits up to the probe's own kill time (10 s);
    • the probe is prewarmed when native chat starts.
  • Routed to the base PR: a raw ::orca-visual line in the sidebar's plain-text reply preview. The base PR fixed it.

Round 2, confirmation: all round-1 fixes confirmed. Four P3s fixed:

  • git 2.48+ relative worktree records;
  • skip the running profile rather than the index's active one, which a profile switch rewrites first;
  • a never-written profile counts as empty, so it can't switch cleanup off forever;
  • re-ask for readable thinking after the longer skill wait.

Round 3, confirmation: no P1/P2. Three P3s fixed:

  • resolve relative records against the record folder's real path (a project added through a symlink);
  • a profile with only .bak backups of its data file is a lost file, not a fresh profile;
  • the thinking re-check never starts a second probe.

Live QA

Isolated hidden rig built from the branch, with a stand-in Claude by absolute path (never a real agent) and hooks off. The UI was driven by a separate QA agent (Codex) through Chrome DevTools; no desktop input was used.

  • Cold start: the first chat after a fresh start got the folder, the write grant, ORCA_CHAT_VISUALS_DIR and --plugin-dir, and the chart rendered inline. A second chat did too. Screenshots are in the PR body.

  • Sweep: a planted orphan folder was removed about 2 minutes after start. An entry Orca didn't create, and both live chats' folders, were kept.

  • Open in sidebar: works.

  • Version-check timing on this machine (load ~23): 10–24 ms warm; 109–173 ms on a binary not loaded for weeks. In the rig, the first chat waited ~0.3 s for its folder's warm probe.

  • Final run on the pushed code (ab3e2d3, a fresh cold rig):

    • Both chats got everything above, and both charts rendered (screenshots in the PR body).
    • document.body.innerText.includes("::orca-visual") was false, so no raw visual line shows anywhere, the sidebar included.
    • The planted orphan was swept.
  • QA tooling note: main's feat(native-chat): suggest files on @ and name the triggers #26017 turned the composer into a rich-text editor, so the QA driver's "send" helper was updated to type into it. This is not a product change.

  • An earlier attempt was cut short when another lane's QA driver killed my rig by port; it was relaunched and rerun.

Real CLIs (opt-in, real login for Claude, isolated home for Codex)

  • claude-structured-real-cli and -fold: pass.
  • Visuals discovery, Claude 2.1.280: the plugin loads through Orca's resolver, and the model lists orca-chat-visuals.
  • Visuals discovery, Codex 0.159.0: skills/list includes the skill, and the thread sandbox has the user's root plus the chat folder.

The real ~/.codex and ~/.claude settings hashes didn't change across my runs. Separately, the real ~/.codex/config.toml gained a trust entry for another lane's worktree (…/workspaces/orca/qa-r7-pi-laptop) at 10-06 23:44, and one for …/workspaces/orca/sta-8902-typed-permission-mode at 10-07 00:26. Neither path is one of mine: my rigs use isolated homes, and the QA driver used Orca's managed Codex home. I didn't restore either.

Local gate (on 01b0441; the later merges only brought base-PR fixes)

  • tc:node, tc:cli and tc:web: clean.
  • Changed-code quality gate against the base PR's head: 0 findings.
  • 193 explicit test files that import the changed modules: 192 pass, 1 skipped as on the base.

CI (classified against main)

After #26103 was squash-merged, this branch still carried its original commits, so the diff against main included the rendering work. The PR now targets main and contains only its own 46 changed files: the skill, folder delivery, grants, and cleanup.

Coordinator-approved final main merge: merged 7b054e649463b47b90852fde6c6baf82ef59a244 and pushed c6ab3a8, without rebasing or force pushing. Resolved four attachment/visuals conflicts by keeping main’s attachment store, stable session-id function, and setup/cleanup alongside this PR’s visuals delivery, version checks, and cleanup. Claude receives the user’s configured folders, the attachment store, and this chat’s visual folder together. Two added test cases cover prepared and unavailable visuals.

Final CI: 35 passed, 17 skipped, 0 failed, 0 pending. All ten unit-test shards, their verification and selection checks, static analysis/typechecks, relay integration, cross-version compatibility, both packages, all SSH host checks, and native smoke checks passed. GitHub reports the PR as mergeable and clean. Completed checks.

Issues from this head, classified against merged main:

  • Windows x64 preview SSH: unrelated terminal-probe timeout; passed on one rerun. The relay deployed and passed its self-test, but PowerShell showed only a prompt and did not echo the test marker within 90 seconds. The failing test/harness, SSH and relay sources, provisioning scripts, and workflow are identical to merged main. This looked like a startup/input timing flake, so the brief’s single-job rerun allowance was used; it passed on the same head with no code changes. Passing retry.
  • Coordinator-approved Linux cancellation/retry: stalled CI package setup before project checks. The first Linux smoke job spent 39m6s in Ubuntu package update/install and never reached Node dependencies, tests, or builds. Its workflow is identical to merged main. The coordinator approved cancelling that workflow and rerunning only the Linux job once. Cancellation settled and GitHub accepted one Linux-only retry; it passed in 1m37s on the same head. The earlier log shows stalled Ubuntu mirror downloads, not a project-check failure. No setup fix is carried in this PR. Linux retry.

Local validation on this pushed head passed:

  • Fresh node, web, and CLI typechecks, run sequentially through the machine-wide queue after deleting build-info caches.
  • orca-ci-checks --no-typecheck: 22/22 static checks passed, reusing those just-run typechecks.
  • 198 explicit targeted test files: 197 passed, 1 skipped; 1874 tests passed, 4 skipped. This includes main’s attachment grant/store installation tests and the combined-folder grant cases.
  • Working tree clean; no package-manager lockfile bookkeeping was committed.

The PR remains ready and unmerged, with auto-merge absent. No app, rig, or agent CLI was launched for this update; the earlier live QA and real-CLI results above are historical evidence.

Earlier reds, by owner:

  • Changed-code casting gate (first push): this PR's. Type casts in tests and in moved code. Fixed and verified locally against CI's base.
  • tc:web error in native-chat-composer-field-resume.test.tsx: main's own breakage. Fixed on main by fix(native-chat): repair main's web typecheck after the @-mention prop rename #26097 and brought in through the base PR.
  • ACP restore test (legacyDirectoryFor), local tc:node only: main's own breakage. Fixed on main and brought in through the base PR.
  • Cross-version "surface has 35 methods, expected 34": owned by the base PR. It added agentSession.readVisual without the manifest entry, and fixed it in 7a1977c.
  • React Doctor preflight on NativeChatVisualFrame.tsx: fixed in the base PR (7b9f21e) before it could show on this PR's runs; it never appeared here.

e2e: skipped by the workflow for this head; not a passing rendered-UI test.

Not verified

  • A real model turn that writes and shows a visual end to end. The live run used a stand-in agent; real-agent discovery is proven separately above.
  • Codex applying the writable-roots override on thread/resume. A fresh thread has nothing to resume without a real model turn, so this is covered only by request-shape unit tests.
  • Windows and Linux live runs. macOS only.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 7, 2026 08:26
@brennanb2025

Copy link
Copy Markdown
Contributor Author

CI after marking ready (same head, d392323): all 26 checks that ran passed.

  • Same as before: tests node 24 1/5 through 5/5, relay integration, unit-selection evidence, native smokes, mac-native-owner-smoke, persistence, glibc/glibc217/musl slots, repository guards, verify.
  • Newly run on ready: SSH hosts on Windows x64/ARM (inbox and preview OpenSSH) and macOS x64/arm64.

e2e was skipped again, by design: the E2E routing (config/scripts/pr-e2e-source-routing.mjs) maps this PR's changed files to no E2E spec ([]), so there is nothing to run. The live QA in the PR body stands in for E2E here.

Removing visual lines now closes only the gap each removal leaves, instead of
collapsing blank lines across the whole reply and trimming its indentation; the
visuals folder is checked parent first again so a broken path answers the same
way every time.
@brennanb2025
brennanb2025 changed the base branch from brennanb2025/inline-visuals-render to main October 7, 2026 18:13
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 43512fdf-e12b-4c80-b896-761a696e7453
📥 Commits

Reviewing files that changed from the base of the PR and between 748e650 and c6ab3a8.

📒 Files selected for processing (6)
  • src/main/claude/claude-structured-launch-resolution.test.ts
  • src/main/claude/claude-structured-launch-resolution.ts
  • src/main/claude/claude-structured-launch-visuals.test.ts
  • src/main/runtime/structured-agent-runtime-registrations.ts
  • src/main/runtime/structured-agent-session-runtime.ts
  • src/main/runtime/structured-claude-runtime-adapter.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The change adds a packaged native chat visuals skill and delivers private, session-specific visual folders to Claude and Codex launches. Claude now uses shared version-gated CLI flag detection for thinking display and plugin loading. Codex receives skill-root and writable-root thread configuration. The runtime schedules cleanup of unused visual folders using held sessions, profile catalogs, filesystem checks, and Git worktree records. Packaging and integration tests cover the new behavior.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to c6ab3

The change is mergeable with awareness that running the affected tests on Windows may require symlink permissions. The inspected PR Windows checks do not run those tests.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.58% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 95 functions across 51 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary change: enabling native chat agents to create inline visuals in a private chat folder.
Description check ✅ Passed The description follows the repository template and provides detailed user impact, implementation details, rationale, linked-issue handling, visual proof, testing, scope limitations, security notes, a…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8613dcde-3a9d-49eb-8d73-f511f03508f5
📥 Commits

Reviewing files that changed from the base of the PR and between 7f43d9b and c12edd7.

⛔ Files ignored due to path filters (1)
  • src/shared/rpc-contract/rpc-params-catalog.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing (94)
  • config/electron-builder.config.cjs
  • config/scripts/build-orcad.mjs
  • resources/native-chat-visuals/.claude-plugin/plugin.json
  • resources/native-chat-visuals/skills/orca-chat-visuals/SKILL.md
  • src/main/ai-vault/session-scanner-accumulator.ts
  • src/main/ai-vault/session-scanner-text-normalization.ts
  • src/main/ai-vault/session-scanner-values.test.ts
  • src/main/claude/claude-child-process-environment.ts
  • src/main/claude/claude-cli-flag-prewarm.test.ts
  • src/main/claude/claude-cli-flag-prewarm.ts
  • src/main/claude/claude-cli-flag-support.test.ts
  • src/main/claude/claude-cli-flag-support.ts
  • src/main/claude/claude-structured-launch-flags.ts
  • src/main/claude/claude-structured-launch-resolution.test.ts
  • src/main/claude/claude-structured-launch-resolution.ts
  • src/main/claude/claude-structured-launch-visuals.test.ts
  • src/main/claude/claude-structured-real-cli-visuals.test.ts
  • src/main/claude/claude-thinking-display-support.test.ts
  • src/main/claude/claude-thinking-display-support.ts
  • src/main/codex/codex-structured-child-environment.ts
  • src/main/codex/codex-structured-launch-resolution.ts
  • src/main/codex/codex-structured-real-cli-visuals.test.ts
  • src/main/codex/codex-structured-session-acquire.ts
  • src/main/codex/codex-structured-session-state.ts
  • src/main/codex/codex-structured-thread-open.ts
  • src/main/codex/codex-structured-visuals.test.ts
  • src/main/codex/codex-structured-visuals.ts
  • src/main/native-chat/agent-session-record-test-fixture.ts
  • src/main/native-chat/native-chat-visual-file-read.test.ts
  • src/main/native-chat/native-chat-visual-file-read.ts
  • src/main/native-chat/native-chat-visuals-delivery.test.ts
  • src/main/native-chat/native-chat-visuals-delivery.ts
  • src/main/native-chat/native-chat-visuals-folder.ts
  • src/main/native-chat/native-chat-visuals-skill-location.ts
  • src/main/native-chat/native-chat-visuals-sweep.test.ts
  • src/main/native-chat/native-chat-visuals-sweep.ts
  • src/main/orca-profiles/other-profile-workspace-catalog.test.ts
  • src/main/orca-profiles/other-profile-workspace-catalog.ts
  • src/main/runtime/agent-session-record-store.ts
  • src/main/runtime/agent-session-store-state.ts
  • src/main/runtime/claude-structured-session-integration.test.ts
  • src/main/runtime/native-chat-visuals-workspace-verdict.test.ts
  • src/main/runtime/native-chat-visuals-workspace-verdict.ts
  • src/main/runtime/orca-runtime-get-worktree-ps.ts
  • src/main/runtime/rpc/methods/index.ts
  • src/main/runtime/rpc/methods/structured-agent-session-rpc.test-fixture.ts
  • src/main/runtime/rpc/methods/structured-agent-session-visual.test.ts
  • src/main/runtime/rpc/methods/structured-agent-session-visual.ts
  • src/main/runtime/structured-agent-runtime-registrations.ts
  • src/main/runtime/structured-agent-session-runtime-teardown.ts
  • src/main/runtime/structured-agent-session-runtime.ts
  • src/main/runtime/structured-claude-runtime-adapter.ts
  • src/main/runtime/structured-claude-thinking-display-refusal.test.ts
  • src/main/window/history-gc-worktree-ids.ts
  • src/main/window/host-frame-navigation-guard.test.ts
  • src/main/window/host-frame-navigation-guard.ts
  • src/main/window/main-window-webview-security.test.ts
  • src/main/window/main-window-webview-security.ts
  • src/renderer/src/components/native-chat/NativeChatInlineVisual.tsx
  • src/renderer/src/components/native-chat/NativeChatMarkdown.tsx
  • src/renderer/src/components/native-chat/NativeChatMarkdown.visual.test.tsx
  • src/renderer/src/components/native-chat/NativeChatMessageRow.test.tsx
  • src/renderer/src/components/native-chat/NativeChatMessageRow.tsx
  • src/renderer/src/components/native-chat/NativeChatTranscriptChrome.tsx
  • src/renderer/src/components/native-chat/NativeChatView.tsx
  • src/renderer/src/components/native-chat/NativeChatVisualFrame.test.tsx
  • src/renderer/src/components/native-chat/NativeChatVisualFrame.tsx
  • src/renderer/src/components/native-chat/NativeChatVisualPanel.tsx
  • src/renderer/src/components/native-chat/native-chat-visual-markdown-extension.tsx
  • src/renderer/src/components/native-chat/native-chat-visual-markdown-syntax.test.tsx
  • src/renderer/src/components/native-chat/native-chat-visual-markdown-syntax.ts
  • src/renderer/src/components/native-chat/native-chat-visual-owner.tsx
  • src/renderer/src/components/native-chat/native-chat-visual-read-client.test.ts
  • src/renderer/src/components/native-chat/native-chat-visual-read-client.ts
  • src/renderer/src/components/native-chat/use-native-chat-visual-document.ts
  • src/renderer/src/components/native-chat/use-native-chat-visual-theme.ts
  • src/renderer/src/components/right-sidebar/index.tsx
  • src/renderer/src/components/right-sidebar/right-sidebar-panel-content.tsx
  • src/renderer/src/components/right-sidebar/right-sidebar-width.ts
  • src/renderer/src/components/sidebar/CommentMarkdown.tsx
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/store/slices/editor/actions/right-sidebar-state.ts
  • src/renderer/src/store/slices/editor/actions/right-sidebar-visual-state.test.ts
  • src/shared/native-chat-visual-directive.test.ts
  • src/shared/native-chat-visual-directive.ts
  • src/shared/native-chat-visual-height-governor.test.ts
  • src/shared/native-chat-visual-height-governor.ts
  • src/shared/native-chat-visual-shell.test.ts
  • src/shared/native-chat-visual-shell.ts
  • src/shared/orcad-artifacts.ts
  • src/shared/rpc-contract/agent-session-visual-params.ts
  • src/shared/structured-agent-session-latest-request.test.ts
  • src/shared/structured-agent-session-latest-request.ts
  • tests/e2e/cross-version-wire/structured-agent-session-surface-manifest.ts
💤 Files with no reviewable changes (2)
  • src/main/claude/claude-thinking-display-support.test.ts
  • src/main/claude/claude-thinking-display-support.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

Comment on lines +113 to +127
it('never touches entries it did not mint, symlinks included', async () => {
const state = stateDirectory()
const root = nativeChatVisualsRootFor(state)
mkdirSync(join(root, 'notes'), { recursive: true })
writeFileSync(join(root, 'f'.repeat(32)), 'a file, not a folder')
const target = mkdtempSync(join(tmpdir(), 'orca-visuals-target-'))
scratch.push(target)
writeFileSync(join(target, 'keep.txt'), 'keep')
symlinkSync(target, join(root, 'e'.repeat(32)))
await sweepNativeChatVisualsFolders(deps(state, []))
expect(existsSync(join(root, 'notes'))).toBe(true)
expect(existsSync(join(root, 'f'.repeat(32)))).toBe(true)
expect(existsSync(join(root, 'e'.repeat(32)))).toBe(true)
expect(existsSync(join(target, 'keep.txt'))).toBe(true)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Skip the symlink tests on Windows. Three new tests call symlinkSync. On Windows CI runners without symlink permission, that call fails with EPERM.

  • src/main/native-chat/native-chat-visuals-sweep.test.ts#L113-L127: change it( to it.skipIf(process.platform === 'win32')(.
  • src/main/native-chat/native-chat-visuals-sweep.test.ts#L171-L179: change it( to it.skipIf(process.platform === 'win32')(.
  • src/main/runtime/native-chat-visuals-workspace-verdict.test.ts#L212-L230: change it( to it.skipIf(process.platform === 'win32')(.

Based on learnings: tests that create filesystem symlinks "should be guarded with it.skipIf(process.platform === 'win32')".

📍 Affects 2 files
  • src/main/native-chat/native-chat-visuals-sweep.test.ts#L113-L127 (this comment)
  • src/main/native-chat/native-chat-visuals-sweep.test.ts#L171-L179
  • src/main/runtime/native-chat-visuals-workspace-verdict.test.ts#L212-L230

Source: Learnings

@brennanb2025
brennanb2025 merged commit c785c98 into main Oct 7, 2026
59 of 61 checks passed
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