Skip to content

Release v1.0.0 — OpenCode V2, on desktop and mobile - #1028

Draft
pascalandr wants to merge 250 commits into
mainfrom
dev
Draft

pascalandr wants to merge 250 commits into
mainfrom
dev

Conversation

@pascalandr

@pascalandr pascalandr commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

CodeNomad 1.0.0 — OpenCode V2, on desktop and mobile

Draft covering changes since 0.20.0, including 0.20.1. Release validation remains in progress.

The best desktop and mobile client for OpenCode V2.

0.20.0 brought CodeNomad to OpenCode V2. 1.0.0 turns more of its native power into a complete workspace: coordinated agents, deeper history access and visual automation, with speed, efficiency and everyday stability at its core.

Highlights

  • Desktop and mobile: Pair another device through OpenTunnel-powered Remote Control, with an interface adapted to phones and tablets.
  • Very fast and efficient: Responsive streaming and navigation, controlled resource use and persistent workspace state support demanding, long-running agent workflows.
  • Missions: Combine OpenCode V2's native sessions, subagents and human questions into coordinated work toward an objective.
  • Beyond the public API: Native plugins and RPC unlock full-history queries, timeline navigation and selective cleanup.
  • Visual automation: Let agents inspect and operate CodeNomad and desktop browser previews to check what they build.
  • An extensible workspace: Files/Git, panel extensions, online help, customizable shortcuts and signed Tauri updates complete the experience.

Built for demanding workflows

  • A responsive workspace: Streaming, search and navigation render the content you need, while project restoration prioritizes your active conversation.
  • Controlled resource use: Bounded history queries, targeted rendering and lazy Files/Git refreshes support large conversations and busy repositories without loading everything into the interface.
  • Built for long sessions: Stable reading anchors and on-demand output expansion let you inspect earlier work while agents continue running.
  • Continuity across your work: Saved projects, drafts and window state carry your workspace across sessions; native request recovery keeps questions and permissions connected to their conversations.

Built around OpenCode V2

  • The shared OpenCode service keeps conversations available to terminal and other clients. New sessions respect native default agents, including plugin-provided defaults; collapsed families show subagent activity.
  • Attach skills through @; inspect Code Mode scripts, nested tool calls and web-search sources. Search providers support global defaults and project overrides.
  • Answer native questions and permissions above the composer, with source labels, preserved drafts and readable answer/decision receipts.
  • Manage native provider accounts and optionally rotate eligible subscription accounts when a quota is exhausted. Usage follows the selected account; Stats adds service-wide activity, token, recorded cost and per-model views.

Missions and native multi-agent coordination

  • An objective becomes a plan: Coordinators delegate to specialists through real OpenCode sessions, native subagents or independent conversations, using your chosen agents, models and reasoning settings.
  • Five approaches: Flexible, Debugging, Exploration, Product delivery and Compare two models. The new comparison scenario gives two models the same task, then produces a blind, scored verdict. Dependencies and results remain visible as plans evolve.
  • Human decisions stay human: Native questions and permissions bring you into the work; milestone briefings, summaries and reports explain progress without requiring you to follow every conversation.
  • One-time or daily work: Prepare and explicitly start a mission, use pause/stop/recovery controls, and save reusable briefs. Execution, background work and plugin infrastructure stay native to OpenCode—not a replacement engine.

From your desktop to your phone

  • Remote Control uses OpenTunnel, as does OpenCode's remote pairing: access your running workspace from a mobile browser or another desktop without opening an inbound port or configuring a VPN. Devices are revocable; enabled Remote Control resumes at launch.
  • The composer follows the on-screen keyboard; questions and permission actions stay reachable. Tablet panels remember portrait/landscape layouts, with compact headers and touch-friendly menus.
  • Mobile uses the responsive web interface, not a native iOS/Android app. Direct/LAN access remains a separate option.

History and visual automation

  • Native plugins run inside OpenCode and expose authenticated RPC over stored history, extending the public API with full-session/workspace search, timeline navigation and technical-content cleanup. Results identify their conversation and worktree and open the matching exchange.
  • Agents can inspect, click, type, select, scroll, drag, use keyboard shortcuts and wait for results in CodeNomad and desktop browser previews—verifying rendered work, not only editing source.
  • Each conversation retains its browser page and navigation history. Preview tabs share sign-ins within the desktop profile, separately from the trusted application.

Files, Git and everyday work

  • Workspace, Changes and Commits share one Files panel: edit text/Markdown, inspect images and diffs, stage/unstage, review history and initialize Git. Scoped filesystem updates refresh visible folders; large inventories stay responsive and refreshed diffs preserve selection and scroll.
  • Temporary projects, tab renaming, a compact launcher and Back/Forward conversation navigation simplify everyday work. An optional whitespace filter focuses reviews on meaningful changes.
  • Native worktrees, session-family navigation, device attachments and /btw build on 0.20.0. After sending, use the conversation for keyboard reading; typing resumes the composer.

Extensions, help and customization

  • Install panel extensions from the online catalogue without rebuilding CodeNomad. MCP Assets collects tool-returned images and attachments with explicit session-asset consent. Native OpenCode plugins retain Global/Project activation and package check/update actions.
  • Online help covers setup and workflows in ten languages, from Help and the command palette.
  • Customizable shortcuts, notification routing and unread badges join compact Settings, localized menus and more consistent light/dark surfaces. SideCars provide additional workspace panels.

Desktop updates and setup

  • Signed Tauri updates support Windows, macOS Intel/Apple Silicon and Linux AppImage. Stable/Preview feeds are separate from data profiles, preserving your tabs and drafts when changing feeds.
  • OpenCode setup adds clearer progress, interrupted-install recovery and explicit restart controls. Older installations on PATH no longer leave setup without a path forward.
  • Preferences restore the correct project context; incomplete request recovery preserves known questions and permissions and reports its cause.

Upgrading

  • OpenCode 2.1.0 or newer is required. This release adopts its new message, event and plugin contracts. CLI updates and shared-service restarts remain separate; Git enables the full repository/worktree experience.
  • Downloads: Windows Tauri .exe; Linux Tauri AppImage or Electron .deb; separate Intel/Apple Silicon macOS builds. The Electron .deb replaces the former Tauri .deb—choose AppImage to stay on Tauri.
  • Stable 0.20.0/0.20.1 users install from the release download or package manager; recent signed previews already have the updater. Update remote CodeNomad servers too. Multi-window state, restoration and named profiles remain supported.

Thanks for contributions

Thanks to creator @shantur, @pascalandr, @heunghingwan, and everyone who contributed ideas, reports and testing.

Changes since 0.20.0: v0.20.0...dev

## Summary
Follow-up to #836 and #838. An expanded interruption now takes priority
over an enlarged composer without changing its saved height or draft.
Collapse/settlement restores normal sizing. Keep the dock non-shrinking
and bound the stack to Android visualViewport changes so response fields
and actions remain reachable.

## Reproduction and scope
Real InstanceShell at 851x393 with maximum composer height reduced the
dock to 54px and its body to 11px, clipping the response and submit
controls. The regression uses native Form events, mobile web/remote
runtime and coarse-pointer Chromium emulation.
The user's separate screenshot showed pre-836 navigation buttons: the
installed bundle still contains those old labels. Merged PRs had not
replaced that installed build. This follow-up fixes the independently
reproduced current-code height issue; it does not claim deployment to
the user's phone.

## Validation
- 7 mobile shell tests: portrait, short viewport, landscape, immersive,
saved maximum size, keyboard resize/pan, long Forms, background/global
requests and permission actions.
- 44 header/composer/interruption browser regressions and 1 isolated
Electron zoom test pass.
- UI TypeScript, production UI build and diff check pass; rendered
captures inspected.

## Gatekeeper
Independent review will be published and corrected in a loop until zero
findings before the requested admin merge.

## Maintenance
Existing large components touched:
packages/ui/src/components/instance/instance-shell2.tsx (~1327 lines),
prompt-input.tsx (~1081), session/session-view.tsx (~711).
## Summary

I added a permanent **Restart OpenCode service** control under
**Settings → OpenCode → Troubleshooting**, shared with startup recovery.
It confirms interruption of active work across connected clients and
does not delete session history. The existing post-update shortcut
remains available.

I enabled authenticated same-version restarts through the existing
host/WSL lifecycle while retaining downgrade/unknown-version fences,
mutation serialization and no automatic retry. Configuration reload
remains a separate action that does not replace the daemon process. This
is a troubleshooting tool, not a fix for the memory behavior reported in
#833.

## Validation

- Final commit `84b4c743e447a7bb68b81e2a276389f94d4dcbaf`: 23
service/reconnect tests and 29 real Solid/Chromium setup tests passed.
- UI and server typechecks passed; production UI build passed.
- Browser captures inspected, including French settings at 390px.
- Native desktop packaging/live installed-application validation was not
performed; no shared daemon was restarted.

The ten touched `packages/ui/src/lib/i18n/messages/*/settings.ts`
translation files remain approximately 692–706 lines. No unrelated
refactor.

Independent gatekeeper reviews are published below: pass 1 identified
one P2 (superseded confirmation could lock the runtime controls); I
fixed it and added a browser regression. Pass 2 reviewed the complete
final diff and returned **zero actionable findings**.
## Summary
Follow up the three full-CI failures reported after #845. A delayed
session-activation callback could steal focus from an answer field and
insert the answer into the composer. Recheck input/dock/modal ownership
at execution and fence deactivation, disposal and superseded callbacks.

## Baseline evidence
The focus race reproduces at baseline 0465499 and #845 head;
deterministic new coverage fails against the old focus implementation.
The other two failures also reproduce on both snapshots: the transcript
fixture returned an empty body for permission-receipts, rendering error
panels that changed row geometry. Fulfill that route with valid empty
receipts; no assertion changes, added waits or forced clicks.

## Validation
- 7 deterministic activation/selection tests pass; competing inputs,
modal/dock ownership, stale callbacks, normal activation and phone
behavior covered.
- 46 transcript tests pass.
- Header/mobile/dock: 57/58 initially; one cold fixture navigation
timeout passed its isolated rerun.
- UI TypeScript and production UI build pass.
- Independent gatekeeper and full CI required before the requested admin
merge.

## Maintenance
Existing oversized file:
packages/ui/src/components/session/session-view.tsx (~720 lines).
…nts (#847)

## User-requested behavior
The user reported that seeing a question from an unrelated conversation
was problematic and explicitly selected **conversation and subagents**.
This change scopes the visible dock to the open conversation and
recursive descendants, excluding parents, siblings and unrelated roots.

## Implementation
- Shared hierarchy scope for dock and project badge; visible
counts/navigation/composer compaction use only that scope.
- Keep off-scope editors mounted, hidden and inert so answer and
permission drafts survive navigation; remember selection per
conversation.
- Explicit project-badge navigation opens the actual owner. Sessionless
global Forms use the no-session surface and remain reachable through the
badge.
- No unrelated fallback for stale focus intents; preserve native reply
identity.

## Validation
- 37 interruption dock/selection/mobile browser tests pass.
- Real-shell badge regression passes; 2 additional desktop/mobile
permission draft and owner-identity checks pass.
- UI TypeScript, production UI build and diff check pass.
- Independent gatekeeper in progress; full CI will be awaited before
merge.

## Scope
This implements the user's explicit conversation-scope preference. It
does not claim to resolve the separately observed Android form
disappearance after reconnect. No installed application was
rebuilt/replaced.

## Maintenance
Existing oversized component touched:
packages/ui/src/components/instance/instance-shell2.tsx (~1344 lines).
…848)

## Approved UX
The user found cross-conversation authorization useful after trying it,
but could not clearly identify the source. They explicitly approved
revising #847: keep in-place replies with compact external previews and
prominent persistent provenance.

## Behavior
- External conversations and sessionless project requests start compact;
deliberate expansion opens their form without changing conversations.
- Source name becomes the primary heading. Subagents show their parent
context; View conversation explicitly navigates to the source.
- One bounded external preview remains discoverable alongside a local
request. Arrival does not replace the current draft; settlement leaves
foreign fallbacks compact.
- The red badge expands the selected request in place, including from
the project Info view. Native reply identity, hidden drafts and
short-keyboard footer access remain preserved.
- Labels translated in all ten locales.

## Validation
- 71 affected browser checks pass; six targeted Info-badge checks pass
after reproducing/fixing the Info entry regression.
- UI TypeScript, production UI build and diff checks pass.
- Desktop/Android-touch compact, expanded, alongside-local and
short-keyboard captures inspected.
- Independent gatekeeper will be published and corrected until zero
findings; user requested admin merge without waiting for CI.

## Scope and maintenance
This implements the revised interaction preference, not a claimed fix
for the separate Android reconnect disappearance. Installation remains
separate. Existing oversized component touched:
packages/ui/src/components/instance/instance-shell2.tsx (~1335 lines).
## Summary
I stopped window resize events from overwriting the right panel's saved
width. Minimizing can briefly report a tiny viewport and previously
persisted the 200px minimum; restoring then retained that reduced width.

The existing drawer layout still constrains the displayed width when
space is limited. Explicit divider resizing still updates the saved
preference. This is a shared UI change for Tauri and Electron, with no
host-specific workaround.

## Validation
I added browser regressions using the real Solid shell for transient
0/160/320px viewport measurements, ordinary viewport shrink/restore,
overlay transitions, and subsequent divider resizing. Both new tests
fail before the fix.

I ran the UI typecheck, two drawer-layout unit tests, and 33 renderer
checks across runs. One cold-navigation timeout passed on an isolated
rerun. The existing Electron-only fixture was unavailable in this
checkout and excluded from the Tauri-only validation.

I built the Windows Tauri release executable (`tauri build
--no-bundle`), passed packaged-resource checks, and launched/closed it
with an isolated config. I did not replace the installed app, restart
OpenCode, or repeat the native minimize reproduction after the user's
confirmation. Native minimize/restore acceptance on the new binary is
therefore not claimed; the regressions cover the real renderer's resize
and persistence path.

Independent gatekeeper review: zero actionable findings on
`a0a02fc3398aaf1de35ac29e408336b909ce1650`, published in this PR. The
existing `instance-shell2.tsx` remains approximately 1,335 lines; no
unrelated refactor is included.

Closes #849
## Summary

I reduced two independently demonstrated sources of redundant work in
the UI shared by Electron and Tauri:

- I reuse the timeline projection when the outline snapshot, resident
marker identities and translated labels have not changed, preserving
reactive locale updates and structural marker changes.
- I discard superseded or disposed Markdown invocations before parsing
and before publishing results, including same-key language retries and
cache hits.

Related to #851, **not a confirmed fix for its Linux Electron freeze**.
I have not reproduced the reporter's environment, and this PR
intentionally does not close the issue. Native events, transcript
authority and OpenCode service lifecycle are unchanged.

## Evidence and validation

- Real native-dispatcher/SessionView fixture with 10k outline entries:
64 sub-bucket deltas trigger **64 full-history projections before / zero
after**; changed text buckets, locale and final text still update.
- Real Markdown/parser instrumentation: cold burst **33 parses before /
one after**, warm burst **32 before / one after**. Disposal, same-key
retries, cached results, theme/highlight, wrap and full-source copy are
covered.
- 100 browser regressions passed (70 renderer/parser/outline + 6
full-source-copy + 24 history-navigation/reasoning), plus 3 outline unit
tests; UI typecheck passed.
- Independent gatekeeper: **zero actionable findings**, with 60 browser
and 6 unit tests passed independently.
- Windows Tauri `build --no-bundle`, packaged-resource smoke and
isolated launch/capture/close passed. I did not replace the installed
app or restart OpenCode. No Electron desktop build/test was run;
Chromium fixture tests do not establish Linux Electron host behavior.
- The initial concurrent history/reasoning regression run hit a shared
Vite cache `EPERM` on Windows and timed out; all 24 affected checks
passed when rerun sequentially.

The broad synthetic stream profile still shows pauses and does **not**
demonstrate an overall responsiveness win. I recorded the measured work
reductions and remaining limits in
`dev-docs/STREAMING_RESPONSIVENESS.md`, with reproducible profiling
scripts.
## Résumé
Place les chevrons d’ouverture à gauche dans le panneau de
questions/permissions, ses aperçus externes et les messages **System**.

Pour une question provenant d’une autre conversation hors de la
hiérarchie active, remplace l’icône de question bleue par un **blason
rouge seul**, avec les couleurs et la forme du badge de statut partagé.

## Comportement
- En-tête d’une question externe : **chevron → blason rouge → titre**.
- Dock et aperçus : chevron vers le haut quand repliés ; dock vers le
bas quand ouvert.
- System : chevron vers la droite quand replié, vers le bas quand
ouvert, suivi de l’icône Info.
- Les demandes externes arrivent repliées et s’ouvrent par une action
explicite.
- Le blason dispose d’un libellé accessible et d’une infobulle
localisés.

## Validation
- Typecheck et build de production UI réussis.
- Tests navigateur ciblés : **47/48 réussis** ; un timeout au chargement
initial de la fixture avant les assertions.
- Assertions sur l’ordre des éléments, le sens des chevrons, la portée
du blason et la conservation des brouillons.
- [Revue indépendante : zéro constat
actionnable](#853 (review)).
Replay queued native events in bounded cooperative chunks so a delayed SDK window rotation cannot monopolize the renderer thread with repeated full-page hydration. Skip unchanged queued-ingress projection and publish one ordered dispatcher snapshot per chunk, preserving the existing pre-reduction compaction buffer.

Keep event-specific callbacks as ordering barriers, including inbox cancellation and revert, and revalidate transcript identity and generations after publications and macrotask yields. Disposal, reconnect, overflow recovery and later rotations cannot publish stale continuations. The shared UI path applies to both desktop hosts without host-specific changes.

Add real SDK/dispatcher regressions for queued hydration, unfinished replay at a timer boundary, FIFO arrivals, callback isolation boundaries and reset/overflow fencing. Update existing rotation tests to await final state. Targeted regressions and UI typechecking pass; the two full-suite failures also reproduce on the unchanged base. A Tauri guest reproduces the prior conditional stall; this does not claim attribution or resolution of every symptom in issue #851.
## Change
I made transcript rotation replay cooperative and publish once per
bounded chunk, instead of repeatedly hydrating the complete page for
every queued event. I also skip unchanged projection while events are
waiting. FIFO reduction, cancellation/revert barriers and generation
fences remain intact; the existing compaction buffer is unchanged.

## Validation
I ran the targeted SDK/dispatcher regressions and UI typecheck. The full
UI suite passes 1,186/1,188 tests; both failures also reproduce on the
unchanged base. Two new regressions fail before this change and pass
afterward.

I compared both versions in an isolated Tauri/WebView2 guest, with
observation hooks disabled: the longest release task fell from 3,195 ms
to 62 ms, and replay completion from 3,237 ms to 184 ms. Both retained
all 120 new characters. The fixture preloads 200 SDK messages, retains a
4 MiB tool result and deliberately delays one sync; it does not
establish incident attribution or physical keyboard latency.

I also completed the Tauri resource build and packaged-resource checks.
Independent review of `3b21c84566d2686f286139a98b11294f31389515` reports
no actionable findings, with 66/66 expanded tests and UI typecheck
passing. The installed app and shared daemon were not replaced or
restarted.

Related: #851
Replace transcript search DOM rewrites with document-scoped CSS Highlight registrations owned by each mounted message. Preserve selection, repaint committed asynchronous content, reveal active occurrences through nested scrollers and retain an overlap-safe Unicode mark fallback. Review findings were corrected and the integrated search/system regressions and typecheck pass. Production fixtures pass on Chromium, Electron and WebKit. Record reproducible raw measurements: reduced synchronous work, alongside Electron's worse paint-opportunity tail and three unrelated permission tests reproduced on the original base. Administrator merge explicitly requested by the maintainer.
…#856)

Removes the four benchmark JSON exports, two screenshots, evaluation
Markdown and AGENTS note introduced by #854: 31,419 text lines deleted.
Runtime code and runnable tests are retained. Measurement summaries stay
in #854's discussion. Validation: deletion-only diff, git diff --check,
and no remaining references to the removed paths.
## Summary

Cleanup following an inventory of 146 merged PRs from August 29 through
October 6, 2026, with targeted inspection of oversized diffs, generated
files, historical reports and current references.

- Remove the palette gallery, 21 PR screenshots, ten historical
reports/plans, the historical tail of the compatibility register, and
the unused February SDK tarball/metadata under `temp/`.
- Stop tracking five generated Tauri schemas; ignore their output
directory and root `temp/`. Keep capability/permission sources, build
logic and the vendored native fixes intact.
- Remove exactly 16 unused Plugins translation keys in each of ten
locales. Preserve the dynamically selected Global/Project labels and all
active labels.
- Repair links, index descriptions and the palette-test comment.
Preserve current runtime qualification/ownership contracts, palette
attribution and all executable regression tests.

58 files changed: **7 insertions, 11,723 deletions** (net **-11,716
lines**), including 22 binary removals. No new repository files or
committed audit artifacts.

## Validation

- UI typecheck: passed.
- Production UI build through Tauri `dev:prep`: passed.
- Palette quality: 40/40 passed.
- Real-component browser Plugins regressions: 8/8 passed.
- Clean Windows Tauri build with all five checked-in schemas absent:
188/188 tests passed, including actual generated ACL authority checks.
- Warm build, delete generated schemas again, incremental rebuild:
passed; generated JSON exists and remains ignored.
- Exact baseline comparison: only the intended 16 translation entries
removed per locale; retained compatibility body unchanged.
- Retained tracked text: no references to deleted reports or removed
translation keys. Synthetic Git-history fixture paths remain synthetic
and do not read the deleted gallery.
- Permission sources and runtime/build code unchanged; no added files;
staged whitespace check passed.

Validation was local on Windows, not a claim of completed Linux/macOS
CI. Existing locale `settings.ts` files remain approximately 677-691
lines; this cleanup removes entries without unrelated restructuring.
## Summary

Remove the hardcoded `base_version: 0.20.0` from the development release
workflow. The reusable workflow already defaults to the package version
at the exact dev SHA being built, currently `0.20.1`.

One-line deletion; the date/SHA suffix, development npm channel and
remote UI policy are unchanged. Future stable version bumps no longer
require updating this workflow.

## Validation

- Parsed both workflows with the existing YAML dependency and checked
the default/fallback contract and retained source SHA/channel inputs.
- Confirmed the source package version is `0.20.1`.
- `git diff --check` passes.

Existing prereleases are not renamed. No CI waiting or monitoring.
Resolve each @./ and @. mention directly in the shared prompt preparation helper. Previously every occurrence became a temporary root marker but only the first marker was restored, so later references could reach the model as ___ROOT___. Removing the sentinels also preserves literal marker text written by the user.

Keep parent-path matching, attached-path handling and verbatim pasted content unchanged. Add regression checks for repeated references, pasted display metadata, message history, shell submission and slash-command arguments. The three new tests fail on the unchanged helper and pass with this fix; all 21 targeted tests and UI typechecking pass.
I removed the temporary root markers from prompt preparation. Each `@./`
or `@.` reference now resolves directly, so repeated references no
longer send `___ROOT___` to the model.

I added regression tests for repeated references, literal marker text,
pasted content and display metadata, shell input and command arguments.
All three new tests fail before the fix; all 21 targeted tests and UI
typechecking pass afterward.

I also built the Tauri resources and passed the packaged-resource
checks. I did not replace the installed app or restart the shared
daemon.

Fixes #863
…2.0.24 (#865)

## Summary

- Qualify and pin the generated client/plugin at OpenCode **2.0.24**,
recommended independently of the unchanged **2.0.7** technical minimum.
- **On every runtime version**, automatic Form/permission recovery uses
only the authenticated loaded-only broker. Missing capacity stops
recovery non-authoritatively; it never falls back to ordinary lists
across historical/inactive worktrees.
- Retain known requests and editor drafts through unavailable/partial
recovery and rehydration, and show a localized recovery-incomplete
status even when no request is known. Only explicit settlement
reconciliation may use ordinary lists at the attempted request's
authorized location. Keep mutation fences and settlement tombstones.
- **Remove the private reader's runtime and Effect version allowlist.**
An untested/custom/prerelease/missing version label no longer disables
recovery. Registration checks the real native RPC capability; each read
validates graph, existing-only lease, queue, codec and identity
contracts. These checks fail safely without mass-loading fallback.
Tested versions remain evidence, not grounds for blocking other
versions.

## Contract audit

The authenticated isolated 2.0.24 daemon exposes 117 native OpenAPI
paths vs 116 on 2.0.22. The new `/api/vcs/init` is not exposed by
CodeNomad. Of 167 structural schema changes, most add declared
`LocationNotFoundError` failures to location-sensitive routes; other
changes add optional `ServerInfo.capabilities.persistentPty`,
WorktreeError's literal `_tag` and the VCS-init error. Published
declaration changes: 11/30 client files (+service-probe), 3/60 plugin,
8/36 protocol and 3/102 schema files (three retired IDE/question
declarations removed). No removed declaration is imported by CodeNomad;
no consumed dependency establishes a higher minimum.

The upstream source audit leaves LocationServiceMap, Form, Permission
and RPC contracts unchanged; the Location-services wrapper changes only
its TypeScript error parameter. Effect remains rc.112. The published
native implementation chunks are hash-checked against separate receipts
before each isolated fixture run.

Native SDK service contender/probe/PTY handoff internals change, but
CodeNomad retains its selected-CLI lifecycle adapter and bounded
authenticated registration fallback rather than using
`Service.ensure/stop`. **No shared daemon restart, storage mutation or
OpenCode PR is part of this change.**

The source audit also covers compaction-agent model/budget selection,
ChatGPT's temporary remote-model synchronization removal, local-provider
discovery/cache sharing, text-capable default selection, bounded MCP
connection/catalog retries and OAuth callback timing, Windows
instruction ancestry, skill frontmatter, subagent text boundaries and
PTY runtime storage/availability. These remain native-owned; no
CodeNomad adapter change is justified by the consumed contracts.
`dev-docs/OPENCODE_V2_COMPATIBILITY.md` records the consequences and
limits: synthetic account tests are not real OAuth/provider
certification, compaction isolation does not exhaust alternate
summarizer configurations, and no desktop terminal-survival test across
service restart is claimed.

## Native loaded-only evidence

### Follow-up: unjustified version lock removed

At the user's explicit direction, remove both
`PENDING_SNAPSHOT_NATIVE_VERSIONS` and the Effect-version equality
check, as well as all version checks in registration and reader
execution. The reader no longer accepts or consults an executing-version
argument. Regression coverage registers across 2.0.7–2.0.24,
newer/custom/prerelease/missing labels and a context whose `app` getter
throws if accessed. Actual malformed/missing contracts, unsafe data,
eviction/disposal races and timeouts remain non-authoritative. The rule
against blocking untested versions is recorded in `AGENTS.md`.

The source comparison across published tags 2.0.7–2.0.24 finds an
unchanged LocationServiceMap and existing-only lookup, RPC contracts and
queue-list interfaces. Changes in older releases affect Form
cancellation feedback/state and permission rejection feedback, not queue
listing. All these tags use Effect rc.112. This is source evidence, not
a claim that all eighteen releases have passed native fixtures.

After gate removal, **29/29 reader/broker/admission tests pass**, as do
server typecheck and pruning build. Fresh published native fixtures on
**2.0.23 and 2.0.24 pass**, recovering permissions/global Forms with 100
cold candidates (three actual Git worktrees), four native boots
before/after and zero cold constructions. The fresh bounded
**full-server run on the follow-up passes: 888 passed, 0 failed, 6
skipped (894 tests, 76 suites)**. Receipts: `no-gate-server.log`,
`no-gate-native-23/receipt.json`, `no-gate-native-24/receipt.json`,
`matrix-source-inventory.json`, `server-all-no-gate-final.log`.

The follow-up also records the distinction between pinned build
dependencies and an independently updated external service in
`AGENTS.md`; no README changes. Independent read-only gatekeeper review
of the **full diff**
`1abeba78043fc6ec1fac4eb57eb6c82a541cf66e..e159068`:
**APPROVE, 0 findings**, 59 independent targeted tests passed plus an
isolated reactive check. The review is published separately on this PR;
it is an agent review, not human approval or deployment acceptance. At
the pre-merge CI inspection, runtime contracts (minimum/latest),
OpenCode compatibility (Linux/Windows/macOS) and native pruning
(Linux/macOS) passed; general tests, native pruning on Windows and Tauri
tests were still in progress. Earlier cancelled/skipped runs are not
passes. The user explicitly requested an admin merge after zero
gatekeeper findings; no all-green CI claim is made.

The committed Bun fixture now uses the same managed `.ts` plugin
discovery shape as production, not configured-package directory
resolution. It uses an authenticated unmodified published HTTP router
in-process, with an in-memory database, private discovery paths, no
listener/service discovery and no external provider.

| Published runtime | Preloaded Locations | Locations after recovery |
Native boot logs before/after | Cold candidates | Cold constructed |
|---|---:|---:|---:|---:|---:|
| 2.0.22 | 4 | 4 | 4 / 4 | 100 (three actual Git worktrees) | 0 |
| 2.0.24 | 4 | 4 | 4 / 4 | 100 (three actual Git worktrees) | 0 |

Both runs recover three permissions, six Forms including three global
Forms. They exercise absent/present backend presence, two backends
retaining registration, expiry, return and final disposal, coexistence
with the existing Promise pruning RPC, authenticated 401 rejection,
schema limits and non-authoritative failures. The boot-log assertion
also detects briefly constructed Locations even if they later disappear
from the map.

Short-fixture RSS: 2.0.24 460,599,296 → 461,824,000 bytes; 2.0.22
463,929,344 → 461,701,120 bytes. These are receipts, **not a memory
profile or comparable deployment soak**, and do not attribute the
reported 10 GiB to this fallback.

## Validation

Environment: Windows, Node **25.2.1**, Bun **1.3.14**. All native checks
use owned processes, synthetic providers/configuration/data and explicit
isolated CLI paths; never the shared daemon or user database.

- `bun scripts/test-pending-snapshot-native.mjs <qualified-packages>
<fresh-output>`: passes separately on published 2.0.22 and 2.0.24.
- 2.0.24 native automation, pruning parent runner,
Location/worktree/proxy/event relay, environment, fork, aside, degraded
Git, prompt skills, provider usage/accounts, permission receipts and
blank-session cleanup: **12 suites pass**.
- Minimum 2.0.7 with the same 2.0.24 client/plugin dependencies: **nine
isolated suites pass** (automation, pruning,
Location/worktree/proxy/relay, environment, fork, aside, degraded Git,
prompt skills and blank-session cleanup). Operation-specific
usage/account/receipt checks do not establish a new global minimum.
- `test-compaction-isolation-native.mjs <2.0.24-cli> --context-mib 2`:
passes with native compaction and outline enabled; the second session
completes before releasing the first session's provider gate.
- Focused UI pending recovery, compaction, Forms and permission
lifecycle: **37/37 pass**. Coverage includes 100 cold worktrees on
unsupported capability, reconnection, request retention, per-kind
mutation fences, settlement reconciliation, late supported/unsupported
responses and compaction admission.
- Final browser dock/selection/mobile regressions: **44/44 pass**. The
incomplete status appears with no known request at 393 px, has no
horizontal overflow, preserves a typed answer on repeated unavailable
recovery and disappears only after full recovery. Real-shell provenance,
recursive interruptions, Android keyboards and per-conversation drafts
remain covered.
- Server/UI typechecks and shared server/UI/plugin builds: pass.
- Final full server unit run after dependency reinstall, `node --import
tsx --test --test-concurrency=2 'packages/server/src/**/*.test.ts'`:
**888 pass, 0 fail, 6 skipped** (894 tests, 76 suites). An earlier
unbounded parallel run exceeded the existing 30-second pending-broker
deadline; the bounded rerun passes without changing that deadline.
- Broad UI unit tests: **1189 pass, 2 fail**. Both failures are
reproduced from unmodified base `1abeba78` sources in an extracted tree:
`classic-dark-surfaces.test.ts` expects a removed accent-hover override;
`tool-content.test.ts` expects copy text where the renderer returns
none. These paths use OpenCode types only, no runtime client import.
This suite is **not green**; unrelated UI corrections are intentionally
not included.
- `git diff --check`: pass. Lockfile retains unrelated package
versions/integrities; npm peer metadata recalculation remains.
- Lockfile reproducibility: `npm ci --ignore-scripts --no-audit
--no-fund` passes after browser fixtures release the Windows esbuild
executable. An earlier attempt during those fixtures encountered
`EPERM`; a simultaneously launched server rerun was invalidated by
dependency removal and is not counted as product evidence.

Detailed local receipts/logs are retained under
`C:\Users\Admin\AppData\Local\Temp\opencode\q224`:
`pending-native-final/receipt.json`, `pending-native-22/receipt.json`,
`schema-audit.log`, `*-declarations.diff`,
`server-all-bounded-final.log`, `ui-pending-final.log`,
`ui-all-final.log`, `ui-unrelated-baseline.log`,
`browser-interruption-final.log`, native-suite logs and
`compaction-2mib-final.log`.

## Deployment acceptance and limits

Not deployed to the active user service. After authorized deployment,
compare passive-recovery Location boot/eviction counts and process
RSS/private memory over a comparable observation window, separating
explicit user actions. Accumulated pre-fix Locations are not
automatically evicted. Only an explicitly confirmed shared-service
restart may release them. The separate Usage-flicker work is not a
substitute for this fix.

Unknown requests cannot be exhaustively recovered on a daemon without
the capability; known requests and live events remain usable. Existing
bootstrap warming, bounded coverage, compaction admission and ownership
constraints remain documented in
`dev-docs/OPENCODE_V2_COMPATIBILITY.md`.

File-size signal: `packages/ui/src/stores/instances.ts` remains
approximately 2,300 lines; no threshold-only refactor is included.
#862)

## What changes

I add independently distributed right-panel UI extensions and an
official online catalogue in Customize right panel. Users browse/search
available addons, choose one and confirm installation directly from its
exact GitHub Release, without a manual ZIP download, rebuilding
CodeNomad or restarting OpenCode. Manual ZIP installation remains
available.

I publish the curated index, contribution rules and Session info demo at
https://github.com/NeuralNomadsAI/CodeNomad-Extensions. That repository
can host several official addons; the index can also reference
author-owned repositories. Separate means independent of the main
application repository, not a mandatory repository per addon.

I show the manifest and exact archive SHA-256 before trust confirmation.
Installation starts disabled; users enable it for all projects in the
server profile or for the exact opened folder. Replacement requires the
previous digest and revokes every activation grant. Removal is explicit.
Packages and grants stay outside project/OpenCode storage.

I keep author code out of the main renderer. API 1 exposes only session
ID, locale and appearance through a per-mount MessageChannel. It does
not expose filesystem, transcript, images, credentials, native commands
or the OpenCode proxy. Frames are sandboxed without same-origin access
and receive a restrictive CSP; obsolete responses and ports are fenced
on project/session changes, disconnects, revocation and replacement.

I document the package, permissions, API compatibility, scope,
release/checksum and maintainer rules in `dev-docs/PANEL_EXTENSIONS.md`,
with a standalone starter in `examples/panel-extension/`. GitHub's
source ZIP is not an installable package. There is no arbitrary URL
installer or automatic update.

I load catalogue metadata only during discovery. Inspection and
installation each revalidate a fresh index, exact ZIP digest and full
manifest; unsupported APIs/permissions cannot install. Downloads are
bounded, deadline-limited, credential-free HTTPS requests to GitHub's
exact release hosts, with constrained redirects. A withdrawn or changed
selection cannot become a different trusted package. Offline catalogue
failures leave installed addons and ZIP installation available.

## Scope and limits

I keep #801's assets gallery for a separate follow-up. It needs a
bounded, authorized image/history capability rather than access to
internal stores. This PR only establishes external distribution and a
minimal context API.

Windows Tauri may inject native bridge objects into subframes: object
presence is not a grant. My isolated WebView2 fixture proves parent
command execution while direct/raw child calls do not reach it, and
verifies context delivery plus DOM/network restrictions. A trusted
hostile extension can still stall its renderer or attempt
self-navigation; this is not process isolation or a universal offline
guarantee. The installer warns users to trust the author.

I have not run a native Electron/macOS/Linux qualification or changed
the shared daemon. The runtime implementation is shared between desktop
hosts; native GUI validation here is Tauri only.

## Validation

- UI and server typechecks.
- 14 focused server/registry/manifest tests.
- Real right-panel browser tests for ZIP consent, activation scopes,
replacement, revocation, context changes, stale reads and frame
isolation.
- Five catalogue/server checks and seven right-panel browser scenarios
cover metadata-only browse/search, online consent, withdrawal,
incompatibility, offline ZIP fallback, hashes/manifests, redirect
restrictions and size budgets.
- Live public GitHub index and Release download matched SHA-256
`cf80226a40dd077a6a8cc2a4b65bbb46bf33619d62007982bd7dcea34ae4545f`;
installation and activation used a disposable isolated store.
- `node scripts/test-panel-extension-native.mjs` on Windows, using the
application's locked Tauri dependencies and an isolated browser profile:
parent native call allowed; child native calls, parent DOM and network
APIs blocked; session context delivered.
- UI build, Windows Tauri release build (`--no-bundle`) and
packaged-resource smoke passed. Full browser suite results are recorded
in the follow-up validation comment.

Existing oversized files touched only for integration:
`packages/server/src/api-types.ts` (~735 lines),
`packages/server/src/index.ts` (~713),
`packages/server/src/server/http-server.ts` (~2,438).
I restore right-panel customization to one compact Extensions action and move catalogue, ZIP installation, activation and trust/removal confirmations into a separate non-modal utility window. Lists stay single-line, metadata is available on hover and keyboard focus, toolbar actions remain reachable at narrow translated widths, and disposal fences late results.\n\nI remove speculative per-folder activation from the UI, client contract and backend. Each installed addon has one general activation control per CodeNomad profile. Existing packages and general grants survive; legacy folder-only consent is never broadened, and obsolete grants are removed only during an explicit successful mutation. Trust, digest-bound replacement/removal and owned-instance executable-panel reads remain enforced.\n\nI publish independent gatekeeper iterations until zero findings on the exact final head. Both typechecks, nine server tests and thirteen real-component extension/Status tests pass, including migration and French/Hebrew/Japanese/Spanish narrow layouts. Windows Tauri/WebView2 command denial passes. I use the requested admin override with the documented CI caveat: four unrelated browser failures also occurred in the final pre-correction run. The installed desktop and shared OpenCode daemon are unchanged.
## Requested change
Restore the normal question/permission icon for requests from unrelated
conversations. Show the complete shared red status badge, including its
localized text, separately at the trailing end of the collapsed bar.
Clicking the badge expands the selected request.

## Layout
Retain leading disclosure chevrons and source titles. Crowded mobile
headers wrap trailing controls instead of squeezing away the
conversation name. Reuse existing badge geometry, colors and
translations.

## Validation
- Initial dock/mobile run: 36 passes, one cold fixture navigation
timeout.
- All six final targeted checks pass, including the previously timed-out
recovery case; fixture initial navigation now has the same 60-second
budget as the real-shell fixture. Assertions preserved.
- Final UI TypeScript, production UI build and diff checks pass.
- Desktop/mobile compact question and permission captures inspected.
- Independent gatekeeper reviewed final diff/captures: ZERO FINDINGS.
Review will be published before the requested admin merge without
waiting for CI.

Installation is separate; no running application or native daemon was
restarted.
I put Extensions below a separator in the customization popup, collapsed
by default. I combine installed addons and the catalogue in one compact
searchable list and remove the separate modes and floating window.

I make the existing addon checkbox the only profile-wide activation
control. Disabled installed addons stay listed; reset never grants
consent. Explicit trust, digest-bound replacement/removal, offline ZIP
installation and sandbox protections remain intact.

I pass the UI typecheck, 9 server tests and 14 real-component
extension/Status browser tests, including narrow translated/RTL layouts
and real ZIP gestures. I inspect the interactive preview validated by
the user. Windows Tauri release qualification and the final independent
gatekeeper review are in progress; I do not replace the installed app or
stop the shared daemon.
Project the existing bell/history unread count onto browser tab favicons and the native dock or taskbar in both Electron and Tauri. Reading, deleting or clearing notifications updates the badge without querying sessions or adding polling; the original favicon returns when no unread items remain.

Aggregate per-window counts in the native hosts, accept bounded count-only calls from owned local and remote primary renderers, and keep preferences windows and preview guests outside the capability. Navigation and closure remove only that window's contribution, with document-generation and main-frame checks fencing stale updates. Windows overlays are generated by the host; other platforms use available native count APIs.

Add real Chromium regressions for notification lifecycle, icon pixels and restoration, unavailable native support and late image completion, alongside focused Electron bridge/security and Tauri state/ACL tests. UI/Electron typechecks, 15 focused Electron Node tests, four Tauri tests and Windows Tauri resource prebuild/smoke checks pass; no installed app or shared daemon was restarted.
Serialize the shared renderer-to-host badge calls so asynchronous Tauri dispatch cannot publish an older unread count after a newer clear. Final cleanup stays in the same FIFO, while rejected native calls do not prevent subsequent updates.

Add a Chromium regression with deliberately held native promises to check send order through a rejected call, rapid add/clear, and disposal. All six browser badge tests and the UI typecheck pass; Tauri resource prebuild and packaged smoke checks also pass with the ordered adapter.
Make the history search window and command palette movable from their existing top bars with native mouse and touch pointers. Share placement, pointer cleanup and viewport bounds in DismissibleWindow while leaving inputs, selectors and close buttons interactive and retaining non-modal dismissal and session authority.

Portal the movable inline search without losing its initial transcript anchor. Align utility stacking with opening order, revalidate placement as content grows, and bound height to the visual viewport. Native overflow and a sticky existing toolbar keep the last result, pagination and drag/close controls reachable in landscape and at pinch zoom; preserve the palette's 60vh limit and horizontal edge access when zoomed.

Extend the real-component browser regressions with drag, touch, RTL, viewport shrink, content growth, asymmetric drawers, overlap, zoom/pan and pagination. All 62 targeted browser scenarios, UI typecheck and production build pass. Five independent Gatekeeper passes corrected eight findings and the final review reports zero actionable findings on bb13b96.

Merge with the requested administrator override. Native CI jobs pass; the full browser suite has 489 passes, three pre-existing permission-fallback-diff failures and two skips. Read-only base/head reproduction confirms those unchanged tests wait for Copy patch in an intentionally collapsed external request without expanding it. Do not change permission semantics or hide these unrelated failures.
Observe primary WebView2 renderer/browser exits and WebKitGTK web-process termination without polling, new dependencies or guest capabilities. Factory enrollment happens before native build, so first loads do not depend on registration timing. Physical-window identities fence retired page, failure and close callbacks; failure revokes count admission until a new page starts.

Native subscriptions capture only a weak host rooted outside managed application state, avoiding controller/handler/AppHandle ownership cycles. Windows ignores frame, GPU, utility and unresponsive-process events. Update the existing narrow remote-capability regression to include the badge-only command.

Document the macOS limitation: pinned Tauri does not forward Wry's Darwin builder termination hook, so a surviving-window crash may retain its count until reload/navigation/close. Do not replace the runtime-owned WKWebView delegate or invent process monitoring. All 195 Windows Rust tests pass serially; Linux API review is not presented as compile/runtime qualification.
## Summary

Mirror the existing bell/history unread notifications on the browser tab
favicon and native desktop dock/taskbar, as clarified in #868. This
includes all existing notification kinds, including update notices;
session idle/attention status does not introduce a separate counter.

- Use a small red dot on the tab favicon and restore the original icon
when the unread count reaches zero.
- Forward count-only updates through the shared native adapter for both
Electron and Tauri. Native hosts aggregate live primary-window
contributions so clearing one window does not clear another's unread
notifications.
- Keep native badge authorization restricted to owned local/remote
application renderers, not preferences windows or preview guests, and
remove contributions on reload/navigation and window closure. Electron
handles renderer crashes; Tauri uses native termination signals on
Windows/Linux, with physical-window fences and weak native handler
ownership.
- Reuse history add/read/read-all/delete/clear updates without scanning
sessions or polling. History remains per-window and in-memory, as
before.

Browser tabs are covered, not installed-PWA launcher icons. Native
dock/taskbar availability depends on the operating system and desktop
environment.

**Tauri/macOS limitation:** the pinned public Tauri API does not forward
Wry's Darwin web-content termination hook. A surviving-window renderer
crash may retain its last count until reload/navigation/close. This does
not replace or swizzle the runtime's WKWebView delegate or add process
polling; normal macOS badge updates remain supported. See
`packages/tauri-app/NOTIFICATION_BADGES.md` for the API evidence.

## Validation

- Six real Chromium regressions cover unread lifecycle, generated badge
pixels, icon restoration, the 50-item history cap, Electron/Tauri
adapter dispatch, unavailable native support, disposal before image load
and FIFO native updates through failures/cleanup.
- UI and Electron typechecks passed.
- Fifteen focused Electron Node tests and all 195 Windows Tauri Rust
tests passed without launching either application. The Rust suite runs
serially (`--test-threads=1`); an initial parallel run had three
native-service fixture failures that pass serially.
- Windows Tauri resource prebuild/smoke checks passed. No native
visual/runtime qualification or Linux/macOS validation is claimed.
- The Linux termination hook was reviewed against the installed API, but
was not compiled or runtime-tested on this Windows host.
- Broad Electron Node suite: 208 passed; two failures outside the badge
tests (missing local Electron installation and a Windows
process-termination assertion). The broad suite is not claimed green.

Fixes #868
Do not mount a permission-receipt request for the internal no-session draft sentinel. Reject malformed session IDs at the receipt API boundary with a 400 instead of forwarding them to the native client where they raise a 500.

Cover the draft-to-session transition and reconnect in the browser fixture, and exercise malformed IDs alongside valid pagination and ownership checks on the server. Targeted browser/server tests and both TypeScript typechecks pass.
Closes #876.

The no-session draft sentinel is only a local draft identity; it must
never enter the permission-receipt API. Skip the read while that draft
is selected and reject invalid session IDs as HTTP 400 at the API
boundary rather than forwarding them to OpenCode as HTTP 500. Valid
session pagination and ownership checks remain unchanged.

Validation: 12/12 server permission-receipt tests; 9/9 targeted browser
tests; UI and server TypeScript typechecks.
Treat idle historical session directories as optional recovery hints while keeping roots, registered worktrees, active sessions and known request placements required. Exclude only paths rejected by ownership, retaining real loaded-only RPC negotiation and non-authoritative failure behavior. Replace the recurring interruption banner with the existing once-per-episode notification.\n\nIndependent gatekeeper reviewed the complete change and the updated dev merge tree with zero findings; the verdict is published as agent review comments. Targeted broker, UI and browser suites, typechecks and server build passed; other CI jobs were still running at admin merge.
## Behavior
- Selected skills appear once in the existing composer attachment bar
alongside files. Removing a skill preserves the draft and other
attachments.
- Sent and historical user messages display their attached skill names,
including messages containing only a skill. Labels survive native
history reloads.
- Long skill names wrap within the message width instead of being
clipped on narrow desktop, touch and RTL transcripts.

## Implementation
The shared message content filter now admits normalized skill parts into
the existing badge renderer. The redundant composer skill row and its
styles are removed.

## Validation
- The browser fixture exercises native message loading, normalization,
the store and the full transcript rather than an isolated badge. It
covers mixed attachments, deduplication, removal, sending, skill-only
history and reloads.
- The history-label regression fails on the original content filter.
- All three narrow long-name regressions fail before the wrapping fix
and pass afterward, measuring message, badge and text bounds.
- Final head `b2a31f339c6879f1fd76d32c55db9375669547cf`: focused
browser/skill checks: 16 passed. Session-action regressions: 53 passed.
UI typecheck and build passed; narrow rendered captures inspected.

Existing large files touched:
`packages/ui/src/components/message-block.tsx` (~1,889 lines),
`packages/ui/src/components/prompt-input.tsx` (~1,078 lines).
… on quit (#1091)

## Problem

Quitting the Linux **Tauri AppImage** can show Ubuntu's "Sorry, Ubuntu
24.04 has experienced an internal error" dialog. apport records a
`WebKitWebProcess` crash with **SIGBUS**.

The AppImage runtime unmounts the bundle as soon as the main process
exits. WebKit's helper processes, started from that mount, can still be
running for a moment: their next page fault in the vanished mount raises
SIGBUS.

- Ubuntu 24.04 VM, `1.0.0-dev-20261022-1840c710` AppImage: 2 crashes in
3 quits.
- The same build extracted with `--appimage-extract` (no mount) never
crashed.
- Stable releases shipped Tauri Linux as a `.deb`, so the problem comes
with the AppImage distribution.

## Fix

On `RunEvent::Exit`, which covers ordinary quits and updater restarts,
an AppImage launch (`APPIMAGE` + `APPDIR` set) stops its direct child
processes whose executable lies under the mount, before the process
exits. The WebKit helpers are direct children of the main process. The
new module is `appimage_exit.rs`:

- each child is pinned with a **pidfd**, then checked again as a child
of this process running from the mount, so a process that reused a PID
is never signalled;
- `SIGTERM`, then pidfd polling up to 1.5 s, then `SIGKILL` and a final
0.5 s;
- children running from elsewhere (the shared OpenCode service) keep
running;
- `.deb`/Electron, Windows and macOS are unchanged; kernels without
`pidfd_open` keep the previous behaviour.

`dev-docs/TAURI_UPDATER.md` documents the exit contract and its
validation.

## Validation

- **Linux unit tests** (new): `/proc/<pid>/stat` parsing with command
names containing spaces and parentheses, and a real child started from a
temporary "mount" that is stopped while a child started elsewhere keeps
running. They pass on Ubuntu 24.04.
- **Real AppImages** on the Ubuntu 24.04 VM, repacked with appimagetool
around the released AppDir, once with the original binary and once with
the fixed one:

| Trial | Original binary | Fixed binary |
|---|---|---|
| Secondary `WebKitWebProcess` frozen (`SIGSTOP`) before closing the
window | survives the main process, i.e. outlives the mount | stopped
before the main process exits |
| Normal quit | 0.06 s, no leftover | 0.08 s, no leftover, no crash
report |

  The shared OpenCode service kept running throughout.
- `cargo check -p codenomad-tauri` passes on Windows.
- Running the whole crate suite on the VM, only the cross-host election
tests (they need the full checkout with `tsx`) and the zsh test (zsh not
installed) failed. Linux CI has both.
…lerators (#1092)

## Problem

On Linux the **Tauri** app showed a GTK menu bar ("File Edit View Window
Help", in English until the renderer reported its labels) above the
application's own title-bar menus, so every window had two menu rows.

Windows and Linux present menus in the title bar, and the host already
called `hide_menu()` on non-macOS platforms, but on Linux the hide never
stuck. Windows are created invisible, hidden, then shown, and Tao shows
a window with GTK's `show_all()`, which shows every child widget again,
including the menu bar.

## Fix

`menu_bar::hide` (new `menu_bar.rs`) replaces the four scattered
`hide_menu()` calls: local windows, Preferences, fullscreen restore and
fullscreen toggle.

- **Windows**: same `hide_menu()` as before.
- **macOS**: nothing, since its menu bar is global.
- **Linux**: on the main thread, where GTK widgets live, the GTK menu
bar is also marked **no-show-all**, so `show_all()` leaves it hidden.
- GTK only activates the accelerators of on-screen widgets. A merely
hidden bar would therefore have disabled native accelerators such as
`Mod+Shift+N` (New Window) that the visible bar provided, and that
`docs/help` documents. The bar answers `can-activate-accel` with `true`,
connected once per bar.

`gtk` becomes a direct Linux dependency, already locked at 0.18.2
through Tauri. `dev-docs/UI_SHELL.md` documents the hidden native bar;
the public help stays accurate.

## Validation

Ubuntu 24.04 VM, AppImages repacked around the released AppDir:

| Build | GTK menu bar | Ctrl+Shift+N |
|---|---|---|
| Original binary | shown above the title-bar menus | opens a new window
|
| Hide + no-show-all only (intermediate) | gone | falls through to the
renderer's New session ✗ |
| **This PR** | gone in the main, second and Preferences windows | opens
a new window ✓ |

Quitting leaves no helper process or crash report. `cargo check -p
codenomad-tauri` passes on Windows, and the Linux release build compiles
the GTK calls.
…lder command (#1093)

## Problem

On an Ubuntu 24.04 VM, OpenCode 1.17.18 was installed by curl in
`~/.opencode/bin` and also linked as `~/.local/bin/opencode`. With that
setup, the OpenCode panel offered **no installation**, only the generic
"Select a supported executable or install @opencode/cli on the execution
host" advice.

On macOS/Linux the user npm prefix is `~/.local`, so that link occupies
OpenCode's command name exactly where OpenCode 2 would be installed
beside the older version. npm refuses to link over it, and CodeNomad
never modifies a foreign installation. The plan therefore correctly had
no prefix, but nothing told the user why.

## Fix

`sharedInstallationPlan` reports the obstacle on the foreign
installation, forwarded as `foreignInstallation.blocked`:

| `reason` | `path` | When |
|---|---|---|
| `occupied` | the occupying command | a non-npm command holds
OpenCode's name in the user npm bin directory |
| `unwritable` | the user npm prefix | the prefix, or a recognized npm
installation in it (an `@opencode/cli` launcher, a Windows npm shim or
an `opencode-ai` 1.x installation), cannot be written |

The installation notice then says how to clear it: move or rename that
command, or fix the folder's permissions, then choose **Check status and
updates**. The usual curl layout, on PATH only through
`~/.opencode/bin`, still offers **Install OpenCode 2 for CodeNomad**.
This was verified on the same VM, where the installation then succeeded.

The English messages and their nine translations, the `opencode.md` help
page in all ten locales and `dev-docs/OPENCODE_RUNTIME_SETUP.md`
describe the obstacle.

## Tests

- `legacy-installation.test.ts`: builds the exact VM layout (link in the
user npm bin directory) and checks that the plan never offers a prefix
over the foreign command and reports `occupied`. The ordinary layout
must still offer the prefix, and a read-only prefix must report
`unwritable` (POSIX, non-root).
- `opencode-setup.test.ts`: renders the occupied notice with no install
action.
- A second POSIX regression puts a verified npm OpenCode 1.x
installation in a read-only user prefix and expects `unwritable`, never
`occupied`.
- Results:
- the `opencode-update` unit tests: 51/51 on Windows (1 POSIX-only
skipped); on the Ubuntu VM as uid 1000, 48 passed, 0 failed, 4
Windows-only skipped, including both POSIX cases;
  - the `opencode-setup` browser tests: 34/34;
  - the server and UI typechecks;
  - `npm run test:help`.
…oute (#1094)

## Problem

The **macOS quarantine** troubleshooting section said that macOS "can
report that the application is damaged". On a macOS 15.8 Intel VM, the
`1.0.0-dev-20261022` Tauri and Electron apps behave differently once
downloaded with a quarantine attribute:

- both are rejected at first launch with **"CodeNomad" Not Opened**:
Apple could not verify "CodeNomad" is free of malware… The only choices
are Done or Move to Trash;
- `spctl` rejects both bundles: Electron is only ad-hoc signed, Tauri is
unsigned, and neither is notarized.

The section also limited the **System Settings → Privacy & Security**
step to Intel Macs and did not name the actual action.

## Change

Ten locales, `docs/help/<locale>/troubleshooting.md`, with headings
unchanged:

- The introduction states that the macOS releases are not notarized. A
downloaded copy is therefore blocked at first launch with **"CodeNomad"
Not Opened**, or reported as damaged.
- Step 3: for the **Not Opened** message, **Open Anyway** for CodeNomad
in **System Settings → Privacy & Security**, confirmed with an
administrator password, is an alternative to `xattr`. The Intel-only
wording is gone.
- macOS labels stay in English in every locale, as in the existing
translations.

## Verification

- On the VM, after a blocked launch, Privacy & Security showed
**"CodeNomad" was blocked to protect your Mac.** with **Open Anyway**. I
did not click it, since it needs the user's password.
- The documented `xattr -dr com.apple.quarantine
/Applications/CodeNomad.app` let both apps start.
- `npm run test:help`: 9/9.
… composer (#1095)

Agent message headers showed raw identifiers (build • claude-code/claude-opus-5-5[1m]:fast • xhigh) while the composer footer names the same choice Build, Opus 5.5 Fast, xhigh. The header now looks up the agent and model names in the instance's agent and provider catalogs, as the composer selectors do, and keeps the identifier when an agent or model is missing from them. A usage-metrics browser test asserts the header text and fails on the previous code; usage-metrics and the UI typecheck pass. Independent review approved with no findings.
One automatic compaction stacked several identical "Session … was compacted" notifications. The notification path was not idempotent: every delivered session.compaction.ended created a bubble (and a history entry when routed there), and the same event reaches a window once per project tab open on its folder (the server publishes one envelope per owning workspace) and again when the event stream replays it.

stores/compaction-notice.ts claims each compaction end once per window by its durable position (aggregate and sequence, or the event ID without one), within a bounded memory of recent compactions; later deliveries still update the session's status in their tab but no longer notify. One bubble per session: a later compaction of the same session replaces the bubble still shown, each bubble is forgotten when it expires unless a newer one replaced it, and a notice routed away from the screen retains nothing. Each distinct compaction still reaches the history when routed there.

Validation: unit tests for the claims and the bubble lifecycle; a browser regression delivers one compaction to two tabs on the same folder plus a replay through the real event dispatcher and toaster and asserts one bubble and one history entry, then one replaced bubble and a second entry for the next compaction (three bubbles without the claim, two without the replacement). notification-icon-badge and compaction-responsiveness pass; UI own-tsconfig typecheck and root typecheck pass. Gatekeeper review (GPT-6.1 Sol): one P3 (retained bubble handles) fixed, then zero actionable findings at 585140a.

Fixes #986
## Problem

Since #1096, the UI unit job on `dev` fails in two
`instances-pending-compaction.test.ts` tests, with `TypeError:
toast.dismiss is not a function` (also in #1096's own PR run).

#1096 shows one bubble per session, so a later compaction end dismisses
the session's earlier bubble. These tests end several compactions in one
session. Under Node with tsx, `import toast from "solid-toast"` yields
the CommonJS module object, which has no `dismiss`. The fixture replaced
only `toast.custom`, so the dismissal threw.

The app is not affected: Vite resolves the real `toast` function, which
has `dismiss`.

## Fix

The fixture also replaces `toast.dismiss` with a no-op and restores it
after each test, as `session-lifecycle-signals.test.ts` already does.
Test-only change.

## Validation

- Reproduced on unmodified `dev` (`333d73452`): 2 of 7 tests in the file
fail.
- With the fix:
- `instances-pending-compaction`, `compaction-notice` and
`session-lifecycle-signals`: 18/18;
  - full UI unit suite: 1694/1694;
  - UI typecheck.
## Summary

- Qualify OpenCode **2.1.3** (latest stable, 22 October) with the
existing locked **2.1.0 client/plugin** dependencies, and **recommend**
it.
- Keep the technical **minimum at 2.1.2**: nothing CodeNomad consumes
changed, so the minimum has no new reason to move.
- A 2.1.2 daemon stays ready and gets an optional upgrade offer. 2.1.3
is the `tested` version. Newer and custom labels keep authenticated
contract recognition.
- The reviewed-contract fast path lists 2.1.3 beside the 2.1.2 minimum.
- The recommended version is updated in the help (all ten locales), both
READMEs, `dev-docs/OPENCODE_COMPATIBILITY.md` and the architecture
skill.

## Contract review (v2.1.2 → v2.1.3, 66 commits)

- **Authenticated OpenAPI:** 119 → **121 paths**, 144 → **146
operations**. Every consumed operation is unchanged. The only changes:
  - added `GET /api/experimental/voice/models`;
- added `POST /api/experimental/voice/session/{sessionID}/instructions`;
- an optional hosted `session.id` in the `POST
/api/experimental/voice/session` response.

  None of these is on the instance proxy allowlist, so they stay closed.
- **Published declarations:** the client adds only `voice.models` /
`voice.instructions`. The schema changes only typing (agent `color`
decoding, `Durable` as a tagged union). The plugin is unchanged.
- **Windows managed services** move their default port from **45278 to
28894** ([#57428](anomalyco/opencode#57428)).
CodeNomad discovers the daemon through the selected CLI's `service
status` and the service registration, and assumes no port.
- Live check: this machine's shared daemon was upgraded to 2.1.3 and
restarted on 28894, and the running CodeNomad desktop kept working
through it.
- **Core:** mostly refactors, plus behaviour fixes that the native
suites below exercise:
  - compaction summary output limit;
  - `keepID` validated as a message ID;
  - MCP media kept out of attachments;
  - only changed instruction entries resent;
  - relative-directory V1 migration;
  - legacy `apply_patch` permission mapped to `edit`.

## Isolated Windows qualification

Base: dev `333d73452`, rebased on `853b14f2f` (#1097) for the final UI
unit run; Node **24.20.0**; exact official CLI **2.1.3**; unchanged
locked client/plugin **2.1.0**.

- **Build and checks:** production server/UI/native plugin builds,
server and root typechecks, native bundle guard.
- **Native runtime (9/9):**
  - Code Mode job ordering, plugin runtime, automation;
  - complete pruning, history, ownership, worktree and event relay;
  - loaded-only pending snapshot;
- Missions contract, family control, native and Compare/Wayfinder
trajectory.
- **Sessions (15/15):** environment, fork, aside, interrupt, lifecycle,
blank cleanup, prompt skills, provider usage/accounts/selection, service
usage, web search, plugin packages, output limit, Promise-tool failure.
- **Recurring (5/5):** all **11 recurring Mission journeys**, subagent
depth, **2 MiB** compaction, permission receipts.
- **Migrations (9/9):**
- history from **2.0.3, beta-19271, 2.0.32, 2.1.0, 2.1.1 and 2.1.2** to
2.1.3;
  - Git degraded/init;
  - temporary workspaces.
- **Installation (3/3), with the 2.1.3 recommendation:**
  - fresh install of the 2.1.2 minimum;
- native **2.0.15 → 2.1.3** upgrade, after which the already-running
daemon keeps its PID and 2.0.15 version;
  - explicit OpenCode **1.18.10 → 2.1.3** replacement.
- **After the recommendation change:**
  - admission/transport/update/setup units **36/36**;
  - full server **2,463 passed, 8 skipped**, no failure or cancellation;
  - UI units **1,694/1,694** (with #1097);
  - focused browser **126/126**;
  - native pruning with the real rendered UI;
  - the exact CI invocation of the Code Mode regression (without tsx);
  - ten-locale help checks.

The UI unit run on `333d73452` itself had two failures, in
`instances-pending-compaction.test.ts` (`toast.dismiss is not a
function`). They come from #1096 and also fail on unmodified `dev`;
#1097 fixes that fixture.

This is Windows automation evidence. In CI, `latest` now resolves to
2.1.3: the compatibility job runs the native suites against the minimum
and latest on Windows, and the migration and Git-degraded certification
runs them on Ubuntu, Windows and macOS. WSL, packaged desktop,
installers/updater end-to-end and real mobile devices keep their
separate release gates.
@pascalandr
pascalandr deployed to github-pages October 22, 2026 14:18 — with GitHub Actions Active
@pascalandr
pascalandr deployed to github-pages October 22, 2026 14:20 — with GitHub Actions Active
## Summary

- Replace the CodeNomad logo with the new **bare CN mark**: a bold C and
N in saturated blue `#0088ad` on a transparent background, an orange
ring `#f7970e` at the centre of the C and a trace joining it to the N.
No tile.
- One source drives every placement: the Electron and Tauri app icons
(ICO, ICNS, PNG, Linux hicolor sizes), the favicon and PWA icons, the
home, empty-session, empty-state and loading screens, and the help site.
- No code or layout changes: every placement keeps its square image box.

## Assets

- `images/CodeNomad-Icon.svg` (new): vector source, the mark on a square
552-unit canvas. The trace overlaps the N stem so anti-aliasing leaves
no seam where the shapes meet.
- `images/CodeNomad-Icon.png` and
`packages/ui/src/images/CodeNomad-Icon.png`: 1024×1024 transparent
export of the SVG.
- `electron/resources/icon.{icns,ico,png}`: regenerated with
`packages/electron-app/scripts/generate-icons.js` and copied to
`packages/tauri-app/src-tauri/`. The ICO keeps its nine frames (16–256)
and the ICNS its twelve entries.
- `packages/tauri-app/src-tauri/icons/linux/*.png`: exported straight
from the SVG at 32–512 px.
- `BUILD.md` documents the source and the regeneration steps, including
the Tauri copies.

## Validation

- Rendered every ICO frame (16–256 px) on dark and light backgrounds:
legible from 24 px; at 16 px the trace fades and the ring reads as an
orange dot.
- Screenshots of the real `FolderSelectionView`, `BrandedEmptyState`,
`EmptyState` and loading screen components in dark and light themes.
- `node --import tsx --test
tests/browser/notification-icon-badge.test.ts` (favicon drawing from the
icon): 6/6 pass.
- `npm run test:help`: pass.
## Summary

- Remove `images/CodeNomad-Icon-original.png`, the source of the
previous tile logo. The new bare CN mark comes from
`images/CodeNomad-Icon.svg` (#1099).
- Nothing references the file: no build script, document or component.

## Validation

- `git grep Icon-original` finds no reference.
…1102)

## Summary

- The main window's custom title bar (Windows and Linux) shows the new
CN mark instead of the text "CodeNomad" before its menus. The
Preferences window keeps its text title.
- `NativeTitlebar` gains an optional `logo` prop: it renders
`packages/ui/src/images/CodeNomad-Mark.svg` (the mark from
`images/CodeNomad-Icon.svg` cropped to its bounds) 24 px high, centred
in the 36 px bar, with the title as `alt` so the accessible name stays
"CodeNomad".
- The image is `draggable={false}` so pressing it still moves the
window, and it hides below 640 px like the title did.
- `BUILD.md` lists the cropped asset beside its source.

## Validation

- `npm run typecheck --workspace @codenomad/ui`: pass.
- Screenshots of the real `NativeTitlebar` at 1x and 2x in dark and
light themes: mark 24×45.5 px at y=6 in the 36 px bar,
`alt="CodeNomad"`, Preferences title unchanged.
## Summary

- The title-bar mark from #1102 goes from 24 px to **20 px** high, at
the owner's request: 8 px of margin above and below it in the 36 px bar,
closer to the weight of the menu labels.
- Only `.native-titlebar-logo` height changes; the brand container still
centres it vertically.

## Validation

- Screenshot of the real `NativeTitlebar` at 20 px in dark and light
themes, 1x and 2x: mark 20×38 px at y=8 in the 36 px bar.
## Summary

- The title-bar mark goes from 20 px to **14 px** high, at the owner's
request after trying it live: it now matches the visual weight of the
menu labels, with 11 px of margin above and below it in the 36 px bar.
- Only `.native-titlebar-logo` height changes; the brand container still
centres it vertically.

## Validation

- Tried live in the owner's installed Tauri app with a 14 px override.
- Screenshots of the real `NativeTitlebar` at 14 px in dark and light
themes, 1x and 2x: mark 14×26.5 px at y=11 in the 36 px bar.
## Summary

Large logos follow their title instead of the window, on the plain empty
state's ratio (6rem logo above a `text-3xl` title, 3.2x):

- **Home page** and **branded empty session**: 6rem (the home page's
12rem rule from 640 x 721 px goes; the session drops `h-48`).
- **Narrow session column**: the title shrinks to 1.5rem, so the logo
shows 4.8rem.
- **Loading screen**: 8rem (128 px) beside its larger 2.8rem title,
instead of 180 px.
- `dev-docs/UI_CONVENTIONS.md` records the rule.

## Validation

- Real home page, branded and plain empty states, narrow session column
and loading screen in dark and light at 1100 x 760: logo/title 96/30,
96/30, 96/30, 76.8/24 and 128/44.8 px.
- Tried in the owner's installed app through a temporary override.
- `npm run typecheck --workspace @codenomad/ui`.
…bar (#1105)

## Summary

On macOS, CodeNomad showed **two menu bars**: the system menu bar and,
inside the window, the UI title bar with the same
File/Edit/View/Window/Help menus plus Windows-style buttons at the
right.

macOS windows now follow the platform convention (as in VS Code or
Slack):
- The menus live only in the **global menu bar**.
- The **native traffic lights** sit at the left of CodeNomad's title
bar, which keeps the theme colours and shows the CodeNomad mark
(Preferences: its title) after them.
- The main window's title bar now also holds **Settings, the
notification history bell and Remote Access**, so the project tab bar
gives that space to the tabs (and drops its separator).
- Windows, Linux and browsers are unchanged.

## Implementation

- **Tauri**: new `title_bar.rs` configures local application and
Preferences windows: overlay title bar with a hidden title on macOS, no
decorations elsewhere.
- **Electron**: `titleBarStyle: "hidden"` on macOS, `frame: false`
elsewhere, for the same two windows.
- Both keep the traffic lights at their **standard place**. Tauri loses
a custom `traffic_light_position` once the window is shown and resized
(it came back to the default on the VM), so on macOS the UI bar takes
the native **28 px** title bar height instead, which centres them, and
leaves their 60 px clear in every writing direction.
- `NativeTitlebar` drops the in-window menus and window buttons on
macOS. The actions move from `InstanceTabs` into a shared `AppActions`
component, rendered by the title bar on macOS and by the tab bar
elsewhere (`appActionsInTitlebar`).
- The native close button of Preferences goes through the existing
guarded close request, like Cmd+W.
- `dev-docs/UI_SHELL.md` describes the per-platform title bar; the
Settings help says where the gear and bell are on macOS, in all ten
languages.

## Validation

- **macOS 15 test VM**, Electron app and Tauri build patched with this
change: traffic lights and mark centred in the 28 px bar in both hosts
(measured from screenshots), no in-window menus or buttons, actions at
the right end opening Preferences, Remote Access and the notification
history below the bar, Preferences title after the lights, and its red
button closing the window.
- New `tests/browser/macos-titlebar.test.ts` renders the real title bar
and tab bar as macOS and as Windows.
- `tab-chrome`, `new-instance-launcher`, `opencode-setup` and
`notification-icon-badge` browser tests (58 pass, 1 skipped), `npm run
test:help`, `npm run typecheck` pass.
…ory (#1107)

## Summary

- The notification history's **Clear all** and **Settings** text buttons
become icon buttons (red trash can, gear), like the panel's close
button.
- Their labels stay as tooltips (`title`) and accessible names
(`aria-label`), with the existing translations. **Mark all as read**
keeps its text.
- Shared `window-icon-button` geometry; the existing toast-history
action classes keep hover, danger and disabled styling.

## Validation

- Screenshots of the real `ToastHistoryPanel` with two notifications,
dark and light themes, including the hover state.
- `npm run typecheck --workspace @codenomad/ui`.
## Summary

The app icon looked smaller than its neighbours in the Windows taskbar:
the square source SVG kept an empty ~4 % margin on each side, so the 24
px icon drew the CN in 22 x 12 px on a transparent background.

- `images/CodeNomad-Icon.svg` is cropped to a square centred on the
mark, touching the left and right edges (height stays about half: the CN
is twice as wide as tall).
- Master PNG, in-app copy, Electron/Tauri `.ico`/`.icns`/`.png` and
Linux sizes regenerated: 24 px now 24 x 14 px, 32 px now 32 x 18 px.
- In-app logos use the same PNG, so their heights shrink by the same
ratio to keep the approved on-screen sizes (5.55rem, 4.44rem narrow,
7.4rem loading); `dev-docs/UI_CONVENTIONS.md` updated.

## Validation

- Regenerated `icon.png` loses no opaque pixel of the master; the mark
spans columns 0-1023.
- Measured bounding boxes in the 16/24/32/48 px `.ico` entries before
and after.
- `npm run typecheck --workspace @codenomad/ui`.
…permission (#1109)

## Summary

System notifications on Windows: everything about them in one PR.

1. **Windows data profiles never showed system notifications**
(`fix(windows): show system notifications from every data profile`).
Tauri runs each data profile, such as **dev**, under its own
AppUserModelID (`…client.scope.s<hash>`), and its notifications carry
that ID; Windows shows a desktop app's notifications only under an ID it
knows, and the installer's Start menu shortcut carries the stable ID
alone. Windows recorded every notification (100 on the affected machine)
and displayed none. Every launch now registers the running ID under
`HKCU\Software\Classes\AppUserModelId\<identifier>` (`DisplayName`
CodeNomad, `IconUri` the bundled web app icon
`resources\server\public\pwa-192x192.png`, which the packaged-resource
smoke check requires; `windows_notification_identity.rs`). Electron
always uses the stable ID.
2. **The permission was lost at every restart on Windows.** The
notification plugin's page script reports *denied* there until the page
requests permission, though Windows never asks, so Settings asked again
after each restart. The check now asks the native plugin command, which
answers granted on desktop: granted once, kept.
3. **Notifications were held back whenever the window was visible**
(`fix(notifications): hold system notifications back only while
focused`). With **Notify when the app is focused** off (the default), an
open window stays visible behind other applications or on another
screen, so nothing was sent unless CodeNomad was minimized. Delivery is
now skipped only while the window is visible and focused
(`document.hasFocus()`). The setting's subtitle and the Settings help
say *focused* in all ten languages; `dev-docs/SETTINGS.md` records the
rule.

Out of scope by decision: several CodeNomad windows open on the same
project may each send the event's notification (as they already do when
the setting is on), and focus inside a browser preview counts as leaving
the page.

## Validation

- Affected machine: no profile notification showed before; after
registering its ID as above, a notification sent under it showed in the
notification centre under CodeNomad.
- `cargo test --bins -- windows_notification_identity` (writes the key,
reads back both values).
- `tests/browser/system-notification-focus.test.ts`: real routed
notification from a visible page; nothing while focused, one once focus
is lost, one while focused with the setting on; red on the previous
visibility check.
- `npm run typecheck --workspace @codenomad/ui`, `npm run test:help`.
heunghingwan and others added 2 commits October 23, 2026 11:48
…bound (#1106)

## Problem

Long model reasoning still appears to freeze roughly 30 seconds in, even
after #1088 kept the store streaming across shared-stream reconnects.
The visible thinking text stops growing while the panel keeps visibly
refreshing — the live duration keeps ticking, the transcript keeps
following, and the reasoning scroller stays pinned to its bottom. To a
user watching a long think, it is indistinguishable from the earlier
store freeze, which is why the previous fix looked like it had not
worked.

## Root cause

The record display projection caps every reasoning part's text at
`REASONING_RENDER_CHARACTER_LIMIT` (10 000 characters —
`extractReasoningTextForRender` inside `buildRecordDisplayData`,
introduced by #648's memory bounding). The cap applies while the part
streams, not only once it settles:

- A reasoning stream at typical model speed crosses 10 000 characters
around 30 seconds in — the same moment the connection-recheck freeze
used to surface, so the two bugs produced nearly identical symptoms.
- Past the bound, every delta still re-projects the message (the row
re-renders, the header duration ticks, the transcript keeps following —
"refreshing, just no content"), but the displayed text is byte-for-byte
the same 10 000-character prefix, so `ReasoningStreamOutput`'s `<pre>`
never changes again until the part completes.
- The store keeps the whole text the whole time and copy actions read it
whole; only the display copy is silently capped, with no reveal.

## Changes

- `stores/message-v2/record-display-cache.ts`: a reasoning part without
`time.completed` (still streaming) now projects its whole `text`. That
is a reference to the store's string, not a copy — per delta it is
cheaper than the previous sliced 10 000-character copy. Once the part
completes, the projection returns to the bounded 10 000-character
prefix, preserving #648's retained-copy bound for settled transcripts.
- `dev-docs/TEXT_DISPLAY_BOUNDS.md` describes the split: completed
reasoning keeps the bounded prefix; a still-streaming part renders
whole.

## Behavior compatibility

- Completed reasoning renders exactly as before (bounded prefix,
whole-text copy).
- Timeline markers, hover previews and search slice their own text from
the raw parts (`message-timeline.tsx` still bounds previews at 4096
characters) and are unchanged.
- A reasoning part that never settles (interrupted mid-think) keeps its
whole text by reference — it renders live and retains no extra copy.
- No help-page change: the bound was never documented in the user help
and no labels, controls or defaults changed.

## Validation

- Red→green unit regression in `instance-store.test.ts`: the streaming
projection keeps the whole text past the bound; the completed projection
keeps the bounded prefix. Pre-fix it fails at the first assertion (10
000 vs 10 020 characters).
- New browser regression `tests/browser/reasoning-stream.test.ts` (real
`SessionView`, native SSE dispatcher): three ~6.9K-character reasoning
deltas keep rendering whole past the bound, and
`session.reasoning.ended` returns the card to the 10 000-character
prefix. Pre-fix it fails exactly like the live bug: `AssertionError:
delta 2 renders whole (got 10000)`.
- The `session.tsx` fixture gains `startReasoning` / `reasoningDelta` /
`endReasoning` emitters plus an opt-in `revealThinking` preferences hook
(thinking rows are hidden by default).
- `tsc --noEmit` clean; `instance-store` 30/30; `empty-content` +
`message-part-grouping` + `render-text-window` 18/18.
- Shared-fixture `message-scroll` suite: 127/128 **identically on this
branch and on the clean base commit**; the single failure is the first
test's page-load navigation timeout, a pre-existing local Vite
cold-start issue (the same environment phenomenon class #1088
documented), not a regression.

---------

Co-authored-by: Pascal André <pascalandr@gmail.com>
)

## Summary

- New logo: a hexagon framing a C and an n, in equal bands (ring, gap,
strokes, counters share one width), readable down to 16 px.
- **App icon**: white ring and letters over black gaps, transparent
canvas, hexagon spanning top to bottom; one fixed icon for light and
dark taskbars, docks and tabs.
- **In the UI** (`components/brand-logo.tsx`): black hexagon with white
gaps on light palettes, white version on dark ones, picked in CSS from
`data-theme` (system appearance when unset), so it also works outside
ThemeProvider (title bar). Loading screen (always dark) uses the white
one. Sizes: 4rem beside a text-3xl title, 3.2rem narrow column, 5.3rem
loading screen, 16px title bar.
- **Icons rendered from the vector**: `scripts/generate-icons.mjs` draws
every `.ico` entry (16-256 px), `.icns` slot, Linux size and the 1024 px
master straight from `images/CodeNomad-Icon.svg` with Chromium, instead
of shrinking one large PNG; it writes the containers itself, so Electron
drops `png2icons`/`pngjs`. One command replaces BUILD.md's four manual
steps.

## Validation

- New `tests/browser/brand-logo.test.ts`: real title bar + home page;
which hexagon each logo shows on light, dark and system (both ways)
palettes, with heights; red without the logo stylesheet.
- UI + Electron typechecks, Electron tests (283), `macos-titlebar` and
`notification-icon-badge` browser tests, `npm run test:help`.
- Icon checks: ICO entries 16-256 (DIB, PNG at 256), ICNS slots
icp4-ic14; UI captures on porcelain, linen, mist and ember palettes.

This branch was successfully deployed

1 active deployment
github-pages — 42cab13a Deployed Oct 23, 2026 by pascalandr via deploy #184
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants