Skip to content

Batch: pipeline, Git lock, Markdown, themes, Host dashboard, Review button and terminal fixes (#340 to #348, #290, #292, #293) - #349

Merged
mubeda merged 595 commits into
mainfrom
claude/next-pr-planning-13d367
Nov 13, 2026
Merged

mubeda merged 595 commits into
mainfrom
claude/next-pr-planning-13d367

Conversation

@mubeda

@mubeda mubeda commented Nov 12, 2026 •

Copy link
Copy Markdown
Owner

What this changes

One batch of fixes and two features, planned in docs/plans/2026-11-09-issue-batch-340-345-290/ (spec, plan, phases, progress log).

Issue Change
#340 Pipeline views keep their header and selected job readable.
#341 A Git index lock left by a stopped command no longer blocks later Git work; long Git writes can be stopped.
#342 Checking a Markdown task box is one undo step; closing the window or quitting with unsaved Files edits asks first (Save all, Discard, Cancel). Mermaid diagrams render again in rendered Markdown.
#343 A refused push to a remote explains what to do.
#344 Worktree orphan warnings rest on durable creation records, so a worktree BiBCode created is not reported as an orphan.
#345 Rich Markdown editing in Files; Git status keeps arriving during network Git operations.
#290 Custom themes, and five levels in the left panel.
#292 A Host dashboard per environment: CPU, memory and volumes, live and since the server started; a host_metrics MCP tool; volumes the filter hides are listed with the reason and never read.
#293 A Review changes button in Source Control, with review prompts per project, one-off instructions, a shortcut, and terminal reviews on Windows.
#346, #347 The composer's model picker no longer shows stale values, and a chat bound to a provider that is unavailable says so and refuses Send.
#348 (not closed here, see below) Terminals shown in two windows, a view left above the bottom, and missing characters after returning to a Claude terminal.

Also: the Linux AppImage bundles its GStreamer plugins (audio in Files no longer kills the web process), the PDF preview loads (frame-src now allows blob:), and several tests that raced their own timeouts are fixed.

#348 in detail

Three separate causes were found on a real pair (the Linux desktop app as server, a second client):

  1. A window that does not size the terminal was cut off at the bottom. The window used last sizes the terminal; other windows show the whole grid scaled to fit (never below half size) with a notice. The server now answers a resize with what it holds, hands the size over when the sizing window leaves, and names one window to answer terminal queries while nobody owns the size.
  2. The view was left above the bottom. xterm 6 stops drawing a terminal whose area is 0 px wide (the right panel squeezing the center column). A row change from the other window then moved the scroll position, and the panel took that for the person scrolling up. The panel now records whether the view followed the output when drawing stops and restores it when drawing resumes. Automatic size requests wait until the terminal's area holds still.
  3. Characters were missing after returning to a Claude terminal. Every return rebuilds the screen from the server's raw byte history (1 MiB or 5,000 lines). The Claude CLI's fullscreen renderer writes only changed cells, so once its last full paint has left the history, cells, the screen mode and the input modes are lost. After such a lossy attach to a Claude terminal that BiBCode started, the server now sends SIGCONT to that terminal's own process group, and the CLI re-enters its screen and repaints.

Limits, stated plainly:

  • The repaint after a lossy attach is best-effort recovery for Claude terminals that BiBCode starts, on Linux and macOS servers. It is not terminal-state reconstruction. Other programs, Codex terminals and Windows servers behave as before. The lasting fix (the server keeps an emulated screen) is a follow-up.
  • A Claude session suspended in its own terminal resumes when a window re-attaches after a lossy history.
  • Pressing the scroll bar of a scaled-down terminal takes the size for that window; the mouse wheel does not.
  • The pair's client was headless Chromium and the Linux app's WebKitGTK window, not the macOS app. AI Terminal: rendering breaks intermittently until a panel is resized #348 stays open until it is confirmed there.

After the first review

  • Index lock ownership ([Bug]: Background Git polling makes the user's own git add/commit fail with "index.lock: File exists" #341). On Linux and macOS, a leftover index.lock is removed after a forced stop only if the killed Git process still held the lock open just before the kill, so a lock another Git process took meanwhile is kept even when it stays unchanged. Windows cannot check this: there a foreign lock that appeared while BiBCode's command held none and stayed unchanged for 2 seconds can still be removed. The Linux check has not run yet outside CI.
  • Images in rendered Markdown (Git: refreshing status hangs while a stacked action holds the read gate #345). A relative image loads from the workspace through the same asset and blob path as the Files preview; the saved file keeps the relative path; paths that leave the workspace are refused. Checked with the real editor in unit tests and a new packaged assertion that has not been run; not seen in a real browser. The read-only rendered view (shown while the editor loads, for files over 128 KiB, and for files the editor cannot change safely) still does not load relative images, as on main.

macOS app as client (run after CI went green)

The packaged macOS app (WKWebView) built from this branch was the client of the Linux desktop app running in the Docker lab (Ubuntu 22.04, WebKitGTK, Xvfb, no window manager), Profiler on in both. The server was a container, not a real desktop, and input on the Mac was synthetic.

Check Result
A window that does not size the terminal (DOM and WebGL renderer) 0 px cut off in 190 checks; scaled with the notice, down to the 0.5 floor
Right panel collapses the terminal to 0 px while the other window changes its rows at the bottom at once 15 of 15
Click, Shift-click, Fit, then typing on a scaled terminal size taken; the key arrived 20 of 20
Lossy re-attach of a Claude provider terminal (stand-in) one SIGCONT per lossy attach 30 of 30, none on complete histories; 27 of 30 screens identical, 3 differ only by live output read a moment apart
The Linux window reloads size handed to the Mac 3 of 5; in 2 of 5 the Linux window claimed it again at its own reload
Profiler no finding caused by this change

Found and not fixed here: when the PTY size changes while a window is away and that window returns to a complete history, the raw replay at the new size can leave rows that an attached window does not show (4 stale rows, 5 of 5 runs with the stand-in). A headless xterm.js probe shows the cause is the replay of bytes written for another size; a program that erases the whole screen when it is resized is not affected. Whether the real Claude CLI is affected is not known. The server-side emulated screen (follow-up) removes this class.

Not valid on this pair: whether an idle window takes the size back (every driver call counts as a user gesture, and the lab's window always reports focus). Not run: jumping between two terminals of two worktrees (the second worktree had no terminal).

Compatibility

  • Every changed wire shape is an optional field or sits behind a capability, and absent means the old behaviour. This is covered by contract tests and test doubles for both directions; the only mixed-version run on real builds was this client against an older server for the Host dashboard (it shows the "update the server" state). Terminal resize, attach and keybindings were not run against a real older server.
  • One known limit: a keybinding for Review changes (sourceControl.review) saved on a server makes a v0.15.3 client unable to load that server's keybindings, as chat.find already does for older clients. The keybindings guide says so.

Validation

The suites below ran on the tree of the last product commit (fix(terminal): ask a Claude terminal to repaint after a lossy attach). The commits after it change three test files and the plan log only; the changed tests were rerun on their own. Commit ids in the plan log predate a history rewrite that removed a local folder path from two early plan commits; the final tree is unchanged by it.

Where What Result
macOS (dev host) cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, vp check, vp run typecheck, vp run check:contracts, vp run check:markdown-links exit 0
macOS web unit suite 11,529 passed
macOS scripts suite; desktop e2e support tests 939 passed; 145 passed
macOS cargo test -p bibcode-server ; cargo test -p bibcode-desktop 7,340 passed, 1 failed (a stale CSP assertion, fixed in the next commit and rerun: 23 of 23 in its file); 532 passed
Linux (Docker lab, Ubuntu 22.04, WebKitGTK, xvfb) packaged default list; Host dashboard, Source Control review, PDF preview and footer specs 36 passed; all passed (one footer run timed out under load and passed on rerun; its time allowance was raised)
Linux cargo test -p bibcode-server terminal::, host_metrics; Clippy 408 passed; 66 passed; exit 0
Windows 11 arm64 (VM) fmt, Clippy, vp check, typecheck exit 0
Windows cargo test -p bibcode-desktop; cargo test -p bibcode-server 443 passed; 6,259 passed, 2 failed (the same stale CSP assertion; and removal_refuses_when_an_admission_does_not_drain_before_the_deadline, which fails about 1 run in 3 on main too)
Windows the ten review prompt cases, ConPTY resize, mapped network drives in the Host dashboard pass
Real pair see the table below

Real pair (Linux desktop app as server, headless Chromium clients, Profiler on):

Check Before the fixes Final build
Recorded divider-drag sequence, lost footer 18 of 19 replays 0 of 20
Right panel collapses the terminal to 0 px while the other window changes its rows view left above the bottom 15 of 15 at the bottom at once 15 of 15, also with no output after the panel closes
A view scrolled up on purpose (wheel, scroll-bar drag) n/a kept across the collapse; a wheel scroll after it is not pulled back
A program's scroll region with a fixed footer broken 59 of 61 checks intact 61 of 61
Lossy re-attach to a Claude provider terminal (fullscreen, cell-diff stand-in that handles SIGCONT) 0 of 5 correct 5 of 5: 0 differing rows, alternate screen and input modes restored; one signal per lossy attach; PTY size unchanged
Lossy re-attach of a plain shell with a half-typed command; a complete short history; a Codex terminal n/a no signal sent in any of them; the shell's line is intact
A window that does not size the terminal cut off by 85 to 257 px 0 px in every check, scaled with the notice
Click, Shift-click and Fit on a scaled terminal size taken without focus 15 of 15 size taken and the next key arrives
A focused but idle window (tab shown again, reload; 10 each) took the size on older builds 0 of 20

Not run: the owner's own Linux desktop as server with the macOS client; the packaged macOS suite (the host's console was locked, which hides the webview); the real Claude CLI on the pair (not signed in inside the test fixture; the owner confirmed by hand on their own server that kill -CONT repairs a terminal with missing characters); x64 Windows and Linux (CI); a focused WebKit window for the idle take-back check.

Profiler: on for every packaged and real-pair run. No finding caused by this change. Pre-existing: main-thread stalls up to 2.7 s and a few slow calls under software rendering, more rpc.slow occurrences for Git ref listing on the Windows VM.

Profiler coverage of the change: the unary server.getHostMetrics and the host metrics stream are registered through the session wrappers and are not long-running; the host collector stops in quiesce before the profiler's teardown and its disk threads are never joined; the resume signal adds no process, RPC or event and logs through tracing; no terminal content or mount point is logged.

React and UI.md review: the changed terminal panel, composer banner and Host dashboard components were reviewed against vercel-react-best-practices and UI.md (five small findings; three fixed: the unavailable-agent banner's guidance, the size notice as a status region, a 100% label for a scaled terminal; two left: the Fit button gives no feedback when no size request can be sent, and the Host dashboard sheet re-renders once a second while open).

Runbooks: docs/testing/cross-platform-validation.md, linux-desktop.md and windows-desktop.md were updated; the others were reviewed and remain accurate.

Known limits and follow-ups (ticketed separately)

  • A server-side emulated terminal screen for snapshots (the lasting fix for lossy re-attach; spike results and review requirements recorded).
  • On Windows, expanding a reasoning row scrolls the chat list to its end (chat-reasoning-rows, also on main).
  • removal_refuses_when_an_admission_does_not_drain_before_the_deadline is timing-sensitive under load (also on main).
  • A second BiBCode AppImage mount (another instance, or a stale mount) shows as a read-only volume in the Host dashboard.
  • After a web process crash the packaged e2e harness relaunches only once per worker.

Refs #348.
Closes #340, closes #341, closes #342, closes #343, closes #344, closes #345, closes #290, closes #292, closes #293, closes #346, closes #347.

🤖 Generated with Claude Code

mubeda and others added 30 commits November 11, 2026 20:11
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iew menu (#293)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…unavailable reviewer by its name (#293)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add as project in Files hardcoded Codex for the new Main, which a never-started main chat now keeps. Resolve the instance with the Default Agent fallback Add Project uses, through a shared resolveDefaultAgentInstanceId.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…347)

Render the real provider status banner and composer banners: a not-ready provider's banner explains it, while a disabled or removed provider, in a never-started or a started chat, shows nothing today and still lets Send submit the bound instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A chat bound to a disabled or removed provider instance showed no reason, an empty picker, and Send submitted the instance for the server to reject. The composer banners now name the provider and the recovery (Settings > Providers, or a chat with another agent from the + menu), and Send is refused with that reason through the composer's existing blocked-reason prop, for never-started and started chats. An instance configured in settings but not reported yet is not called unavailable; the exact-session-account test now configures its instance for that reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ider (#347)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and mirror cases (#348)

The fake grid now changes only when fit runs, so the test fails if the
scale handler stops refitting. A mirror must not claim on a scale change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#348)

When the last resize or owner claim fails and no further layout or input
event arrives, the PTY keeps its old size, for example 24 rows after the
mount shrank to 19, cutting off the bottom rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…348)

A failed resize or owner claim now retries after 250 ms, 1 s and 4 s
by re-measuring through the layout trigger, so a newer size replaces the
failed one and a renderer that no longer owns the size does not claim.
A success restores the budget; unmount and renderer replacement cancel
the timer. The red test advances timers asynchronously so each retry's
result can schedule the next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on return (#348)

Pane and document hiding each recreate the renderer; on return, with no
resize event, the smaller box is fitted and sent in the legacy path and
claimed by the owner. The fake fit addon can now apply its proposal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…retries (#348)

A zero-area mount still attaches with its sizeClaim; it supplies no fitted
dimensions and sends no layout-driven resize. Qualify the scale-change
entry and add the resize retry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…293)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…code reviews (#293)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Drives seeded layout actions around a Codex Terminal whose fixture TUI now
draws a footer on its last three rows, and after each step checks that the
PTY's rows, xterm's rows and the rows that fit the mount agree and that the
last row is inside every clipping ancestor. Not in the default spec list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The upstream AppRun hook builds its plugin, scanner and PTP helper paths from $APPDIR, which is unset when the extracted AppRun runs and names another AppDir when inherited from another AppImage. After the pinned plugin writes the hook, the wrapper checks its four expected lines, rewrites them to the AppDir the hook file lives in, and fails the build if the upstream hook drifts. A normal launch exports the same values as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mubeda and others added 6 commits November 12, 2026 17:12
… sync the follow repair calls (#348)

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… loaded host (#348)

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…, close the last tasks

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…#347, #348)

An enabled but unavailable agent's banner asks for its install and sign-in to be
checked instead of enabling it; the size notice is a status region and never
says 100% for a scaled terminal.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-12T22:33:32.240054Z 9c57548 PR opened
🔒 Security Review ✅ Completed 2026-11-12T22:35:49.506371Z 9c57548 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c57548ffa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/server/src/git/process.rs Resolved
mubeda and others added 7 commits November 12, 2026 19:56
…kspace (#345)

The rendered view draws an image's relative src against the page's
address, so it never loads. These tests expect the editor to keep the src
in the document, draw no img with it, and hand each image to the surface,
which reads it relative to the Markdown file through the workspace asset
route and refuses a path that leaves the workspace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ace (#345)

The rendered view's image node drew an <img> with the file's src, which
the page resolved against its own address, so a relative image never
loaded in the desktop app, the browser or a remote environment.

Images now get a node view the surface draws into, as Mermaid diagrams
do. A relative src is resolved against the Markdown file's folder; a path
that leaves the workspace, an absolute path or another scheme is refused
before any request, and the asset route still checks the path on the
server. The file is read through the workspace asset URL and shown from
a blob URL with the Files preview's own loader, revoked on unmount or a
new URL. While loading or on failure the image's text stands in, with
the reason on hover. The document and the saved file keep the src as
written. The packaged rich Markdown spec checks a relative placeholder
image loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…urvive a reissued address (#345)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e address (#345)

http: and https: pass through only with //, so http:/x.png is not read
against the page. A failed image is tied to its URL, so a reissued signed
URL tries again, and a refused asset address asks to check that the file
exists. The reissue test re-renders with a copied workspace, since the
React Compiler memo hides the mocked atom's change. The architecture
overview and the packaged validation runbook describe the image view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR review of #341: a write that takes index.lock can be force-stopped
while it holds none, and another Git process can take the lock meanwhile
and keep it unchanged past the 2 second wait. This test is red: the
cleanup deletes that live lock. A second test keeps R16 green: a lock
the forced write itself held open is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#341)

After a forced stop the cleanup removed any lock that appeared during
the command and stayed unchanged for 2 seconds, so it could delete a
live lock another Git process took while BiBCode's command held none.

The stop report now carries a check taken just before the forced kill,
while the root still runs: on Linux (/proc/<pid>/fd) and macOS
(proc_listpidspath limited to the root's process group) whether the
root has index.lock open. On those platforms a lock is removed only when
it did. Git removes its own lock on SIGTERM even while a hook runs with
the lock's descriptor closed, so a forced stop leaves one only when Git
could not act on the request, while it still holds it open. Windows has
no such check and keeps the previous rule; the docs say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s review and the follow-up issues

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@mubeda
mubeda merged commit a993819 into main Nov 13, 2026
25 checks passed
@mubeda
mubeda deleted the claude/next-pr-planning-13d367 branch November 14, 2026 10:46
mubeda added a commit that referenced this pull request Nov 14, 2026
Bump every version source to 0.16.0 and turn the Unreleased CHANGELOG
section (PRs #349, #359, #365 and #369) into the v0.16.0 entry. Raise
the Claude Code additive-hook ceiling to 2.1.323 after the live
PreToolUse guard proof passed on that build, and move the above-ceiling
test example to 2.1.324. Give the CI Test lookup contract test a
30-second limit: it runs bash and jq eleven times and timed out against the
5-second default under load.

Verified locally: cargo check for the workspace, vp check, vp run
typecheck, the full vp run -r --concurrency-limit 1 test graph in all
nine packages, release:smoke, the updater regression sets, cargo test
-p bibcode-desktop, and the macOS arm64 DMG signature and Finder icon
check. CI: the Seeded Packaged Desktop Upgrade workflow from v0.8.0
passed on all seven lanes for this branch (macOS x64 on a rerun after
its hosted runner lost its connection). Reviewed by Codex with no
findings.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment