Restore the cross-platform Control UI startup gzip budget by keeping render-only sensitive-path helpers outside the startup config-draft graph. Preserve existing behavior and caller APIs while moving generic schema, search, and path helpers to their existing startup owner.
* fix(control-ui): stop config form save from corrupting 64-bit id strings
Saving the schema-driven config form coerced every numeric-looking string
to a JS number before submission. For union-typed fields such as
tools.elevated.allowFrom.* (anyOf: string | number), string entries
holding 64-bit ids (Discord/Telegram snowflakes) were rewritten through
Number(), which rounds past 2^53:
"1048113311314608148" -> 1048113311314608100. The corruption also hit
untouched fields, because serialization coerces the whole form, so merely
saving an unrelated setting silently broke elevated-approval allowlists
(fail-closed: the real user id no longer matched).
Two guards fix this:
- coerceFormValues keeps a string that already satisfies a string variant
of an anyOf/oneOf union instead of parsing it into another variant's
number.
- coerceConfigFormNumberString refuses lossy integer parses: plain
integer text beyond Number.MAX_SAFE_INTEGER that does not round-trip
through BigInt stays a string, so pure number/integer fields fail
validation loudly instead of storing a corrupted id.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(control-ui): harden 64-bit config id preservation
* fix(control-ui): validate mixed-union scalar branches
* test(control-ui): prove real gateway id preservation
* test(control-ui): use communications route for config proof
* test(control-ui): grant config proof admin scope
* test(control-ui): reopen raw config for proof
* fix(control-ui): preserve explicit union input types
* test(control-ui): exercise union collection draft
* ci: retry flaky control ui e2e
* fix(control-ui): preserve mixed scalar branch types
* ci: retry service worker e2e
* fix(control-ui): preserve typeless string union branches
* fix(control-ui): reject lossy decimal coercion
* fix(control-ui): reject lossy pure numeric input
* fix(control-ui): preserve exact numeric branch semantics
* ci: retry checkout rate limit
* ci(control-ui): capture real gateway proof
* test(control-ui): frame config proof values
* ci: retry checkout download
* test(control-ui): prove Gateway-served production bundle
* fix(control-ui): preserve exact incremental union edits
* refactor(control-ui): isolate scalar edit session state
* fix(control-ui): keep scalar edit branch type internal
* fix(control-ui): avoid detached focus selector
* fix(control-ui): round-trip exact numeric branches
* refactor(control-ui): share exact scalar formatting
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
With message preview off, rows collapse to one line and the second-row icon
logic (spinner, unread dot, badges, facepile, trail) rides beside the title.
Two defects broke that:
- Long titles crushed the icon endcap: the single-line title used flex-basis
auto, so overflow shrink was distributed onto the endcap (24px -> ~7px),
clipping the status icons. Basis 0 now mirrors the two-line subtitle
contract: text absorbs all shrink before icons do.
- Native catalog session rows always rendered the two-line skeleton with an
empty details line (46px vs 30px) and never collapsed, in both preview
modes. Catalog rows never carry preview text, so they are now always
single-line.
Regression coverage: long-title endcap geometry in
chat-flow.sidebar-presentation.e2e.test.ts (fails pre-fix), and
codex-sessions.e2e.test.ts density assertions updated to the new
all-single-line contract.
* refactor(ui): unify sidebar alerts with custodian
* refactor(ui): move custodian alert state
* fix(ui): use the defined mono token in the custodian alert card
* fix(ui): break the update-watcher import cycle
Type the watcher against a structural leaf contract instead of
Pick<ApplicationContext,...>; context.ts reaches this module through
overlays-types.ts, so naming the context type closed a madge cycle.
* fix(ui): re-arm the alert explanation when an incident recurs
Failed-automation and model-auth alerts keep one incident id across
recover-then-fail-again, so an id-keyed dedupe showed the renewed alert
and never explained it. Scope ask-once to the presentation instead.
* refactor(ui): share Control UI resource route contract
Gateway and Control UI now share browser-safe resource route grammar, while authorization and domain validation remain handler-owned.
* perf(ui): keep resource routes out of startup
Narrow browser-safe contract modules and the lazy authenticated avatar loader keep route/cache code out of the startup chunk while the stable Gateway barrel remains compatible.
* feat(ui): unify focused presentation routes
/focus/<target> replaces unshipped standalone query links across dashboard, terminal, desktop, and native apps.
Gateway-served index assets are anchored so nested documents resolve their bundles from the Control UI base path.
* test(gateway): narrow emitted asset URLs
Fixes check:test-types TS18048/TS2322 by dropping unmatched optional captures before comparing emitted asset URLs.
* test(docs): follow centralized cloud secret guidance
Fixes the stale current-main docs test after #126132 centralized GCP and Hetzner setup in docker-vm-runtime.
* test(ui): retry missing locator reads
The 500ms locator text read can time out while the menu label is still rendering, causing expect.poll to reject instead of using its owning 10s retry window. Treat only Playwright TimeoutError as a missing value so the outer poll retries while page-closure and arbitrary failures still surface.
* test(android): capture TLS probe coroutine
The TLS probe test inferred its coroutine from mutable scope children, racing unrelated child startup and teardown in CI. Capture the exact Job from inside the probe coroutine and join that owner before asserting the stale-attempt guard.
* fix(gateway): preserve plugin focus routes
Keep approval handling ahead of plugin dispatch, but treat focus documents as an unclaimed Control UI fallback after plugin authentication and routing. Exact and prefix plugin routes therefore retain ownership, while unclaimed reads serve the focus document and other methods return 404.
* fix(ui): migrate released terminal links
Preserve stable v2026.7.1 terminal query compatibility by rewriting the root/base ?view=terminal URL once to the canonical /focus/terminal path with history.replace. Keep URL parsing path-only, and leave the removed desktop and dashboard query forms as a hard cut.
* test(codex): assign run-attempt tools shard
Cached filtered configs caused duplicate ownership, and the test lacked a canonical full-suite owner.
* test(ui): keep cloud recovery proof state-owned
The recovery test should assert owner state and reload identity, while dedicated tests own transient alert visibility.
* test(qa): wait for outbound bus state
* fix(qa): reserve gateway ports through staging
* refactor(qa): keep socket creation in gateway owner
Pinned sessions inherited the nav zone's muted colour on the session *title*, so a
pinned row's preview line outshone its own name and the same session read dimmer
pinned than unpinned. A title is content in every zone; only glyphs, meta and the
hover fill follow the zone. The subtitle drops to plain --muted, already proven AA
against every surface by theme-contrast.test.ts.
Rows with no preview text reserved a fixed 18px second line anyway, leaving a dead
band or a lone spinner hanging below-right of the title. They now collapse to one
line via a --single-line class plus a CSS variant, so the endcap rides beside the
title. Rows also gained a little vertical air and reclaimed the right-hand gutter
inside the row, leaving the deliberate scrollbar clearance from #124879 intact.
Adds a localStorage-backed "Show message preview" toggle to the session sort menu
(default on). Operator-actionable state is exempt: attention, the queued
concurrency-slot explanation, and critical observer headlines (stuck /
waiting-on-user) always show, because hiding them behind a display preference is
the silent-failure class.
Also registers extensions/codex/src/app-server/run-attempt-tools.test.ts, which
arrived in #126189 with no shard claim and left main red.
Scope embedded Desktop refresh and connection ownership to the active presented sidebar slot. Hidden retained panels now invalidate pending work and disconnect RFB, while reactivation returns to a freshly loaded picker without silently reconnecting.
* improve(ui): make online sidebar section collapsible
* improve(ui): show online avatars in compact state
* improve(ui): align online section heading
* fix(ui): satisfy sidebar CI gates
* fix(ui): keep online expanded on first visit