Repository navigation
v0.9.2: remote latency, PR panel, watcher loop, delivery and update fixes - #92
Merged
Merged
Conversation
Relay-connected clients now open server-loopback dev servers through the preview gateway at their own https origin, and the Connect tunnel comes back after a server restart. Refs #68. Also proposes the SOCKS-routed preview profiles of #69 (design only, with the Linux WebKitGTK spike). - relay: provision 4 preview hostnames per managed endpoint (<env>-p1..p4: proxied CNAMEs plus ingress rules before the catch-all), found by name so re-linking adds missing ones and deprovisioning removes them before the tunnel; the runtime config carries hostname and previewHostnames. - server: the tunnel runtime publishes its hostnames; a gatewayOpen caller on loopback under the endpoint hostname takes a free preview slot (a live target never loses its slot) and gets origin https://<preview hostname>; new reasons relay-not-provisioned and relay-busy. An outermost main-port layer serves preview hostnames through the slot's target handler, HTTPS-facing (Secure cookie, https Origin rule and Location), never BiBCode's routes, CORS, or admission gate. Preview responses are Cloudflare-CDN-Cache-Control and Cache-Control no-store, service worker scripts are refused, and relay hostnames over plain HTTP redirect to HTTPS. The gateway drain starts when shutdown is signaled and is shared by every caller. - server: restore the Connect tunnel from the stored link at startup, unless the server now listens on another port than the tunnel was provisioned for; link, unlink, and restore share one lock; connect.linkState reports managedEndpoint, previewHostnames, managedEndpointStale, and linkGeneration, and relayConfig refuses a background re-link whose requireLinkGeneration no longer matches. - web: relay resolution (and any server-returned origin) bootstraps on the returned origin and maps it back to localhost per tab; browser mode never frames these previews; the primary client, once authenticated, re-links once per session, silently and fenced, when its tunnel lacks preview hostnames or points at an old port. - docs: remote architecture, user guide, relay README, a relay step in the cross-platform runbook, the #68 spec (with review addenda) and plan, and the #69 proposal. Native relay validation (desktop and browser-mode client over a deployed relay stage) is recorded in the runbook and was not run here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After the request was created or found, the dialog kept a Close button next to Done. It now shows only Done, and the form's lock comes from the request state itself, so a status re-read after creation cannot unlock the fields or bring back the create action. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
The connector now also starts at boot (tunnel restore), so its own death must reach the Profiler like any other child BiBCode runs (AGENTS.md: Profiler, Crash, and Trace Coverage). - A watcher checks the active connector every 5 s without reaping it. An exit nobody asked for withdraws the relay preview hostnames, logs a warning, reports subprocess.exit and subprocess.crash (Watch::Failures, labelled by the connector's executable), and ends the rest of its process group. It holds the runtime weakly, so a runtime dropped without shutdown still drops its connector. - apply and shutdown report a connector that died on its own since the last check before replacing or stopping it; the Windows liveness probe no longer consumes the process handle. - Relay hostname matching on every main-port request no longer clones or allocates. - Docs: Profiler section, profiler skill events and findings, remote architecture, and a connector-crash step in the relay runbook. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ms (#90) Approved design. Every GitLab read costs a 2.3 s glab start, half of it a synchronous telemetry request, and the panel waits on getContext before any stored copy can paint. The design serves the last context and stored copies first, refreshes them in the background, extends stored copies to GitHub, turns glab telemetry off, and runs Approve's pre-check reads in one wave. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
glab 1.122 sends a telemetry event to the GitLab host before each command returns. On a VPN link that is a second HTTPS connection per call: 2.3 s per `glab api` read instead of 1.2 s. One helper, provider_cli_environment, now sets GLAB_SEND_TELEMETRY=0 for the pull request runner, merge request lookup and creation, and Settings discovery. The user's own glab configuration is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
connect.linkProof compared the proof's loopback origin with the request URI's authority, but an HTTP/1.1 request carries an origin-form target with no authority, so every BiBCode Connect link and background re-link from a real client failed with "Invalid managed endpoint origin." The check now falls back to the Host header, as the auth layer does; tests had passed absolute URIs and never hit the real shape. Also pins the preview-hostname label boundary (a 60-character label keeps its hash whole, a 61-character one shortens its stage prefix), tests that gatewayOpen and relayConfig payloads decode with and without the new optional fields, and corrects the contracts comment on `origin`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Approve and every other GitLab action re-read the merge request, approvals, reviewers, GraphQL metadata and project one after another before acting: five glab starts in a row, about 6 s with telemetry off and 11.5 s before. They are independent, so they now run together, as the detail read already does. A failed action now logs its error code with tracing::warn!, never its message, which can carry checkout paths and host names. The one recorded Approve failure left no code behind. The action test fixture answers concurrent reads by role; roles now queue, so a later group's read of the same argv takes the later response. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
A preview hostname is public, and every request to it refreshed its target's idle clock before any session check, so anyone polling the hostname (or a stale tab's reload client) kept the target, and its relay slot, from ever closing; four such targets left every open failing relay-busy. Now only a bootstrap or a request carrying a gateway session counts as activity. That request holds the target open through its principal check, so the idle sweep cannot close the target under it before its lease takes over. Addresses the Codex review on #91. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The panel showed a skeleton until getContext answered, and that read runs the CLI's authentication probe plus three host reads: 0.65-3.3 s on a VPN link once the 30 s server cache expired. Each live available context is now stored with the other pull request display copies. The panel's first read sends `allowStale`; the server answers from the stored row (`stale: true`) with no host call, and the panel reads live at once and replaces it. Older clients never send the flag and keep reading live. - Stored rows are fenced by read order: unusable answers leave a tombstone, and a logout or a Rescan that rejects the host raises a per-host fence on the database worker, so an earlier read never writes back. - A self-hosted host is recorded again from its stored context after a restart, so neither getContext nor readSnapshot needs CLI discovery. - The stored lookup and the live fallback share getContext's 30 s deadline and one scope resolution. - Each stored answer gets one live read; a remounted panel joins the read in flight, and a failed live read keeps the stored context on screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
Stored display copies were GitLab-only, so every GitHub list and pull request waited on `gh` (about 2 s for the list). Successful live reads now store them for both hosts, `readSnapshot` serves both, and a successful action deletes that number's rows for both. Only GitLab routes subscribe and poll; GitHub stays on demand. A GitHub list ignores stored copies once its live first page answers: with no poller to correct it, a late stored page judged newer by a skewed server clock would otherwise stay on screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
) On the Linux desktop the Git watcher's inotify thread ran at 99% CPU for hours while the process grew to 3.3 GB. An overflow asks for a full re-registration of the worktree's watches, and on Linux that walk queues an open event per watched directory. A tree larger than the kernel's 16,384-event queue (235,371 directories here, mostly node_modules of nested agent worktrees) therefore overflowed again on every re-registration: about 80 million watches were added in a loop. A per-watch gate now spaces full re-registrations: the first runs at once, later overflows defer the next one 30 s, doubling to 30 minutes, reset after a quiet stretch; a single timer retries the deferred one. Status keeps its sticky fallback reads meanwhile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
A remote client over Tailscale waited 7-10 s per terminal keystroke while the server answered in 0 ms: every event on an open agent thread re-read and re-sent the whole thread (40,170 activities, 6-10 MB) and every event anywhere re-sent the whole shell. The link carried 2.2 GB in a few hours and small replies queued behind it. - A thread subscriber that sends `patches: true` now gets `patch` items against the snapshot it holds: changed fields, removed fields, and per array its new length plus only the entries that differ, by index. The client decodes just those values with the thread's field schemas (`applyThreadPatch`). Older clients keep whole snapshots; older servers ignore the flag. - Thread updates coalesce 100 ms after the first event of a burst, the shell 500 ms, so streamed text no longer reloads the thread per delta. - `fixtures/thread-stream/lifecycle.json`, written by a server test, pins that the patches rebuild the server's snapshot exactly. Sending raw events instead was rejected: the client reducer diverges from the server projection (session stop, turn timing, activity sequence, message order). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
"Keep hidden" sends the catalog generation the client saw, and the server demanded an exact match with its current authoritative snapshot. Every completed refresh advances the generation, even when nothing changed (window focus, the 2 s poller, Git Manager revalidation), so a remote client that sees each generation seconds late failed every time: "Catalog generation 9 is no longer authoritative; current generation is 10". The service now records the oldest generation since which the adoptable worktree paths are unchanged and accepts any generation in that range, hiding exactly what the client was shown; a set that changed since is still refused, now with a tracing warning. Also: a subscriber joining a VCS summary producer that had already published waited up to 30 s for its first value, because the watch receiver treated the current value as seen. It now gets the current summary at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
…pression design The new summary test committed without a configured identity, which works on a developer machine but exits 128 on CI runners. It now sets user.name and user.email like its neighbours. Adds the approved design for compressing large server messages before E2EE encryption (#89), with its length side-channel safeguards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
) Over a slow remote link, first thread loads and reconnects still send multi-megabyte JSON snapshots that hold every other reply for seconds. Ciphertext does not compress, so the WebSocket's permessage-deflate cannot help an E2EE connection; compression has to happen before encryption. A client that can inflate raw deflate offers `deflate-v1` in `e2ee_auth`. When the server confirms it, a server message of at least 16 KiB leaves as a 4-byte big-endian uncompressed length plus raw deflate (level 3, on a blocking thread), every record flagged 0x10, whenever that is smaller. Client messages and control records stay uncompressed. The 64 MiB limit applies to the uncompressed size on both sides; the client refuses a larger declared length or output that differs from it. The client keeps arrival order while a message inflates: later messages wait until its handler's effect has started, effects still run concurrently so a backpressured stream holds up no other reply, and a message dropped before its handler ran (bad data, or a close first) stops delivery so nothing overtakes it. A closed socket cancels its inflation, and the server's compression job shares the message bytes and stops between chunks once its write is dropped. The design's length side-channel trade-off was approved with safeguards: server-to-client only, per message, 16 KiB threshold, negotiated, and `BIBCODE_REMOTE_COMPRESSION=0` stops the server confirming it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
…r ends its turn (#93) After an idle-suspended Claude thread resumed, Claude sometimes reported one or two `result` frames before it replayed the message BiBCode had just written (3 of the last 19 deliveries on the affected thread; one more was earlier work finishing). The runtime had already started the new turn, so that result completed it. Acceptance then arrived with the terminal revision moved, `publish_running_session` skipped `running`, and the thread read `ready` while Claude worked for minutes. The next message was therefore written as a start instead of queued; Claude, busy in tool calls, did not replay it within 60 s, and it showed "Delivery uncertain". A turn now begins when Claude replays its input. Between a normal delivery's write and the replay of that same write (matched by its acknowledgement registration, so another write's replay does not count), a successful result belongs to work Claude was still finishing and is ignored (logged). An error result still ends the turn, since Claude can fail before reading, and once the write's acknowledgement wait ends without its replay, results end turns as before. The skipped running publication is logged too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
…red (#93) When Claude reads a message only after its 60 s acknowledgement wait, the delivery stays "Delivery uncertain" although Claude got it, and Retry would send it a second time. The expired write already keeps its place in the acknowledgement order; its registration now carries the outbox delivery key, and the replay it consumes emits an internal `delivery.confirmed-late` event. The event pump moves that thread's matching uncertain row to delivered; a row retried or dismissed meanwhile is left alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
The replay can arrive between the acknowledgement timeout and its `sending -> uncertain` write. The confirmation now runs off the event pump and waits up to a second for a matching row that is still sending, instead of dropping the only confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
`server.getProviderCapabilities` started the provider CLI on every call; for Claude that is 1.2 to 1.5 s on the server (profiler findings 6f4b4ab71016 and 9e6264fb967c, 170+ slow calls in a day), paid each time the composer opened and again on remote clients. Successful catalogs are now cached per instance and directory for the current provider-settings generation. Entries younger than 60 s are answered as is; older ones are answered at once while a single background discovery refreshes them. A settings change drops every entry, and failures are never cached. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
To see which methods fill a slow remote link, the session now counts each method's replies and stream items where the connection already encodes them before queueing (E2EE and record-framed sockets), so no reply is serialized twice. Every 5 minutes the latency detector emits one `rpc.bytes` event per method with message count, total and largest encoded size. Counting runs only while the profiler is on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
A catalog that reports discovery issues (for example Claude failing to reload skills) asks the user to retry, so it is no longer cached. The cache keeps at most 64 catalogs, evicting the oldest, so removed worktrees do not stay for the server's lifetime, and a failed background refresh is logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
…t and report missed crashes (#94) On the v0.9.0 -> v0.9.1 AppImage update the relaunched app and both crash monitors died with SIGBUS seconds after the restart, and the Profiler raised nothing. The desktop ran as the main process of a `systemd-run --user` service (KillMode=control-group, ExitType=main): when the old process exited, systemd stopped the unit and killed its cgroup, the relaunched AppImage's FUSE mount included. - Desktop: after an installed update, a host that sits directly in a `.service` unit relaunches the new AppImage through `systemd-run --user --scope` at the last exit callback, so the new app lives in its own scope. If systemd-run fails, Tauri's relaunch runs as before. - Profiler: every start now asks systemd-coredump for crashes of this same executable since the last session began (whatever that session's state) and raises a `crash.native` finding for each one no session reported, grouped by signal and linked by `coredump-<pid>-<time>` so it is raised once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
A response guard picks the reply it sends after admission, so only an upper bound is known where bytes were counted; counting it skewed totals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
#95) Windows users on v0.7.2 reported that "update and restart" closed BiBCode without the new version starting. The seeded-upgrade lanes could not catch it: they always seeded from the latest stable release, and the Windows cleanup passed when nothing was running. - `desktop-upgrade-smoke.yml` takes an optional `previous_tag` input (validated as a stable `vX.Y.Z`, passed through the environment) that seeds every packaged lane, and the release runbook runs it from v0.7.2. - After the candidate is installed, the Windows lanes wait for the installed executable to be running with a window (`tasklist /V`), so a missing relaunch fails the lane. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
…watch (#88) notify's recursive inotify watch adds a watch for every directory below the worktree, so a checkout with nested agent worktrees and build output held ~235k watches; re-registering them overflowed the kernel queue, and each overflow asked for another full re-registration (99% CPU, 2.5 GB). On Linux the native backend now walks the worktree itself and watches each directory non-recursively, skipping the directories Git ignores (one `git ls-files --others --ignored --directory` per registration, confirmed by `git check-ignore` so a directory whose current contents merely happen to be ignored stays watched) and nested repositories or worktrees, which have their own watches. A worker thread watches directories created later the same way, skipping ignored directory names. The affected main checkout goes from about 312,000 watched directories to about 2,400. macOS and Windows keep their single native recursive handle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
…sh queries - The update relaunch exits only after the new process has left the service's cgroup (it is in its systemd scope); if systemd-run exits or does not move within 5 s, it is stopped and Tauri's relaunch runs. - The unrecorded-crash query advances a `crash-query-cursor` only after coredumpctl answered, so a start where it failed or timed out retries the same window next time instead of skipping it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
…ch check A busy runner can make `tasklist /V` hit its bound; that sample now counts as not yet relaunched and polling continues within the restart budget, while a listing that cannot run at all still fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
The E2EE compression change added `flate2` (raw deflate, zlib-rs backend already in the graph through `zip`). It performs local compression only and adds no network or telemetry path; the zero-telemetry contract's server dependency list and the dependency-upgrade ledger now account for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
Review follow-ups to the pruned worktree watch: - Directories created later are checked with `git check-ignore` by path, one call per new level, so an anchored rule such as `/build/` no longer skips `src/build/`; names are used only when Git cannot answer. - A removed and recreated directory is watched again. - A `.gitignore` or `.git/info/exclude` change re-reads the rules and watches what they now keep. - Submodules (a `.git` file pointing into `.git/modules/`) stay watched, since the parent's status shows their changes; linked worktrees and other repositories are still left to their own watches. - A watch the worker cannot add reaches the watcher callback as an error. - Git runs through the supervised process runner. The unrecorded-crash query now tells coredumpctl's "No coredumps found" from a failed listing, so the cursor never advances past records it did not read. Also fixes a clippy lint in the watcher. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
…ubmodules - Only writes to `.gitignore` or `.git/info/exclude` re-read the ignore rules. Opening the file also produced an event, and Git opens it to answer, so one read started an endless loop of Git runs and walks. - Submodules (their Git directory in this worktree's `modules/`, resolved rather than matched by substring) are walked with their own ignore rules. - When a later `check-ignore` fails, new directories named `node_modules` or `target` are skipped instead of treating the failure as "nothing ignored". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
…0.9.2 work PR #91 lets the preview gateway be reached over BiBCode Connect, accepts Connect link proofs requested over HTTP/1.1, counts only session traffic as relay target activity, and keeps the tunnel connector covered by the Profiler. Its own CI passed (28 checks). Merged here so the v0.9.2 candidate carries both PRs and is verified together. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
Pruning inside submodules needed each submodule's own ignore rules, exclude files, Git queries and fallback, and broke when a submodule was initialized after the watch began. A submodule (resolved from the superproject's `modules/` each time, so a late one is recognized) now keeps notify's recursive watch, as before the pruned watch; the superproject around it stays pruned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
…k failed ones - A directory created inside a recursively watched submodule is no longer sent through the pruned additions, which re-registered it non-recursively and dropped notify's recursion beneath it. - A recursive watch that fails partway is unwatched before the error is reported, so no unrecorded watches stay behind. - The profiler maintenance test no longer asserts that a stale events file survives until maintenance: in a full test run another test's event can open an events file whose retention removes it first. Findings compaction, which only maintenance does, still shows the hourly run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
The filter that leaves a submodule's contents to its recursive watch also matched the submodule root, so a submodule directory removed and created again never got its recursive watch back. Only strict descendants are left to it now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
Bump the application manifests and Cargo.lock to 0.9.2 and add the curated v0.9.2 changelog entry covering #88, #89, #90, #93, #94, #95, the PR #91 preview gateway over BiBCode Connect (#68), the skill catalog cache, and the Create PR/MR dialog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1
This was referenced Oct 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What Changed
The v0.9.2 release candidate: work for #88, #89, #90, #93, #94 and #95 from the 2026-10-13 profiling session, PR #91 (preview gateway over BiBCode Connect, closes #68) merged in, and the version bump with the curated changelog.
patchitems (changed fields; per array the new length and changed entries) instead of the whole thread per event; thread updates coalesce 100 ms, shell snapshots 500 ms. Server messages of 16 KiB or more are raw-deflate compressed before E2EE encryption when the client offersdeflate-v1(approved with safeguards;BIBCODE_REMOTE_COMPRESSION=0turns it off). The Profiler emitsrpc.bytesper method every 5 minutes.glabruns without its telemetry request; Approve's permission reads run in one wave and failures log their code; the panel paints from stored copies for GitHub and GitLab, then reads live.git ls-files --ignored --directoryconfirmed bygit check-ignore); new directories are added by a worker (path-scopedcheck-ignore),.gitignorewrites re-read the rules, submodules keep notify's recursive watch. One checkout: ~312,000 → ~2,400 watched directories.resultbefore the new message's replay no longer ends the new turn, so the thread stays running and the next message is queued; a replay after the 60 s wait marks the delivery delivered.systemd-run --user --scopeand exits only once it has left the unit; the Profiler raisescrash.nativefor coredumps of BiBCode processes no session covered (cursor advances only after a successfulcoredumpctlquery).desktop-upgrade-smoke.ymltakesprevious_tag(release runbook: run from v0.7.2); Windows lanes require the updated app to relaunch with a window.server.getProviderCapabilitiesanswers from a cache (per instance, directory and settings generation; 60 s fresh, background refresh; partial catalogs uncached; 64 entries).Validation
cargo fmt --check,cargo clippy -p bibcode-server -p bibcode-desktop --all-targets -D warnings, server lib 3,252 (one profiler test made robust to parallel-suite events, see159b3fe2),rpc_wire15,provider_claude47, desktop lib 516.vp check,vp run typecheck,vp run check:contracts,vp test run(12,352 passed),vp run release:smoke,cargo check --workspace --all-targets --locked.Not in this PR
6b0bedc9a176, ~50 MB/h on v0.9.1): needs a heap snapshot first.Profiler coverage
Compression runs on a blocking thread inside the writer's timing. New events:
rpc.bytes; new finding source: unrecorded coredumps (crash.native). Watcher Git calls run through the supervisedProcessRunner. The relaunch helper is the app itself (logged on failure). New logs: late-delivery confirmation, ignored early Claude result, skipped running publication, background capability refresh failures,systemd-runfallback.🤖 Generated with Claude Code
https://claude.ai/code/session_01RsdQGFUPxjNZ8GQocThxT1