Skip to content

refactor(views): remove the companion (VRM avatar) feature + dead steward wallet-policy UI - #10434

Merged
lalalune merged 3 commits into
developfrom
refactor/remove-companion-and-steward-policy-ui
Jun 30, 2026
Merged

lalalune merged 3 commits into
developfrom
refactor/remove-companion-and-steward-policy-ui

Conversation

@lalalune

Copy link
Copy Markdown
Member

What

Removes two dead UI surfaces — the companion (VRM avatar) feature and the
dead steward wallet-policy UI — re-derived on top of current develop. The
original work lived on chore/review-dirty-worktree-cleanup, but develop moved
~207 commits since it diverged, so the companion commit was applied via
git cherry-pick and every conflict + every new companion reference that
landed on develop in the interim was resolved/removed against develop's current
code (the branch does not cherry-pick cleanly on its own).

1. Remove the dead steward wallet-policy UI surface (b69f5e7)

PolicyControlsView was mounted nowhere (no route, no settings section, no
app-shell page), and the steward/injected slot wrappers always rendered null
because nothing assigns the boot-config steward slots. Only Storybook stories, a
stringly coverage entry, and a feature-surface showcase consumed it.

Removed: PolicyControlsView (+ story), the whole policy-controls/ module,
components/steward/, the AppBootConfig steward slots + Steward*Props /
AppBootSteward* types in boot-config-store, the barrel re-exports, the agent
external-modules.d.ts prop shims, the "settings/PolicyControlsView" coverage
entry, the feature-surfaces showcase, the orphaned template stub exports, and a
stale design-doc listing.

Kept (real wallet/auth plumbing): approveStewardTx +
/api/wallet/steward-approve-tx, client-types-steward
(StewardApprovalInfo), the shared StewardPolicyResult type, the
CloudWalletProvider "steward" value, and all cloud Settings sections.

2. Remove the companion (VRM avatar) feature entirely (6cc0193)

Deletes plugins/plugin-companion/ and every reference across the runtime, app,
UI, registry, build config, tests, and docs. The VRM avatar chat overlay, the
/companion view, the emote system (PLAY_EMOTE / /api/emote), the companion
ShellView state, and all wiring are gone.

  • Delete plugins/plugin-companion; drop its workspace dep + bundled-package /
    knip / docker-runtime / registry entries.
  • Strip companion from agent core-plugins, plugin-resolver,
    external-modules.d.ts, prompt-compaction, view-action-affinity,
    misc-routes, and every view-coverage / view-parity / tui-view /
    bundle-lifecycle test.
  • Remove the companion BuiltinTab + its TAB_PATHS / redirect wiring, the
    ShellView "companion" member, COMPANION_ENABLED, CompanionVrmPowerMode
    / CompanionHalfFramerateMode, and the CompleteFirstRunOptions /
    launchCompanionOverlay first-run plumbing (re-derived against develop's
    current useFirstRunCallbacks / first-run-finish / use-first-run-conductor
    — these were added after the branch diverged).
  • Remove the 15 dead lucide-react -> ../plugin-companion/node_modules tsconfig
    path mappings, the companion route from dev-route-catalog, the
    VITE_ENABLE_COMPANION_MODE gate on character-select (now unconditional),
    and the /api/emote PostEmoteRequestSchema contract + test.
  • Drop PLAY_EMOTE from the action-coverage ratchet, rewrite the companion-emote
    scenario to GENERATE_MEDIA-only (deterministic-media-actions.scenario.ts),
    and remove it from the prompts action spec + core action-docs.
  • Delete mobile-plugin-views.ts (its only registration was the companion
    app-shell page) + its main.tsx import; drop companion from the optional-app
    shim, the storybook glob/order, and the client-agent playEmote method.
  • xr-functional-parity / xr-bundle-coverage reconciled (17 → 16 plugins);
    view-interaction-coverage visualCases count reconciled to 56.
  • Retarget packages/app-core/scripts/ensure-avatars.mjs off the now-deleted
    plugins/plugin-companion/public / eliza/plugins/app-companion/public path
    to apps/app/public — the exact dir the surviving sibling process-vrms.mjs
    writes and the static server serves /vrms + /animations from. The surviving
    VRM content-pack / BundledVrmAsset avatar subsystem is unaffected.

Why it's safe — no dangling references

Final repo-wide grep over packages/ plugins/ scripts/ skills/ (source + config,
excluding dist/ and node_modules):

  • plugin-companion / @elizaos/plugin-companion → no source/config hits.
    Surviving matches are docs + generated artifacts only: historical analysis
    .md files (perf report, VIEW_E2E_FINDINGS, loadperf research,
    minimal-redesign catalog), checked-in e2e HTML snapshots
    (__e2e__/output/*.html), and generated *-report.json — none are live code.
  • PLAY_EMOTE / playEmote → no source/config hits except one generated
    retrieval-ranking entry, packages/shared/src/i18n/keywords/action-search.generated.keywords.json: "action.playEmote.request". Kept at develop's state on purpose: a full
    regen of that file produces a 49,508-line unrelated drift (locale
    reformatting + 200+ other actions' keyword drift over 207 commits); it is
    retrieval ranking metadata only (the header says it is not an availability
    check) and is regenerated by the legitimate generate:action-search-keywords
    pipeline. This matches the original removal commit's handling. (The downstream
    validation-keyword-data.{ts,js} files that also list playEmote are
    gitignored — regenerated at build, not in this PR; they regenerate clean.)
  • companion BuiltinTab in packages/ui/src/navigation + state → CLEAN.
  • PolicyControlsView / policy-controls / steward dead-slot props → no
    source hits (one stale storybook-README changelog line + generated report JSON
    remain — historical/generated, not code).

False positives deliberately KEPT (unrelated uses of "companion"): the
browser-bridge companion (plugin-browser / BrowserWorkspaceView), the
plugin-phone Phone Companion page (id: "phone-companion",
@elizaos/plugin-phone), mobile-companion / desktop-companion
remote-capability providers, the electrobun companion window, the CSRF companion
cookie, and the AOSP-local-inference "companion plugin" Android FFI help string.

Verification

Scoped tsgo typecheck of every affected package (missing type packages
symlinked from the parent monorepo's node_modules — gitignored, not in the
commit; this is the documented light-install worktree):

packages/agent     typecheck → 0 errors
packages/app-core  typecheck → 0 errors
packages/app       typecheck → 0 companion errors (1 pre-existing on develop:
                                steward-wallet-providers.tsx viem Config type)
packages/ui        typecheck → 0 companion errors (2 pre-existing on develop:
                                steward-wallet-providers.tsx; voice-control.test.ts
                                vitest mock — both byte-identical to origin/develop)

Unit tests (vitest):

packages/agent       view-agent-surface-coverage     68/68  ✓
packages/app-core    assert-required-bundled-packages 24/24  ✓
packages/app         view-interaction-coverage        10/10  ✓ (with the 56 reconcile)
packages/shared      contracts/misc-routes            27/27  ✓ (emote-schema removal clean)
plugins/plugin-xr    xr-functional-parity              8/8   ✓ (companion 17→16 reconcile)

scenario-runner/deterministic-action-coverage: the PLAY_EMOTE ratchet and
companion-emote → GENERATE_MEDIA scenario rewrite are correct (the test file has
zero companion refs; the rewritten scenario has 0 PLAY_EMOTE / 10 GENERATE_MEDIA),
but the suite can't run green in this worktree due to an environment install
gap — Cannot find package 'jimp' (transitively imported via plugin-vision;
absent from both worktree and parent node_modules). All 8 failures share that one
import error and are unrelated to companion; CI's full install runs it.

plugin-xr/xr-bundle-coverage: 2 size assertions fail because three unrelated
plugins' view bundle.js entries are 148-byte re-export shims in this worktree's
vite build (real content is in hashed chunks) — an env/build-config artifact, not
a companion regression. The companion-relevant xr-functional-parity passes.

audit:app status (the one remaining gate)

ATTEMPTED (during the paired PR A run on this same worktree). The visual-audit
pipeline builds every plugin view bundle, @elizaos/core + @elizaos/shared,
and transforms the app renderer (4632 modules), but the live-stack cannot boot
in this isolated worktree because its install:light provisioning is missing a
long tail of renderer/build deps (@elizaos/vitest-vite, lucide-react,
webxr-polyfill, @date-fns/tz, …). This is the documented "worktree shares
parent node_modules" constraint. The screenshot + manual-review loop is the one
remaining gate
and should run on a full-install checkout (CI) for the affected
surfaces — most relevant here: the launcher view-manager (which loses the
companion tile) and first-run / onboarding (companion-overlay launch removed).
No fabricated screenshots or verdicts are attached.

🤖 Generated with Claude Code

@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review. (436 files found, 100 file limit)

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a71217f2-bf57-45c1-8dfb-5f2dc36f17a0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/remove-companion-and-steward-policy-ui

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

❤️ Share

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

Shaw and others added 2 commits June 30, 2026 03:48
In the editable tree this surface was already dead — PolicyControlsView was
mounted nowhere (no route, no settings section, no app-shell page), and the
steward/injected slot wrappers always rendered null because nothing in the
real app assigns the boot-config steward slots. The only consumers were the
view's own Storybook stories, a stringly-typed agent coverage entry, and a
feature-surface showcase. The live steward components ship from the external
@elizaos/plugin-steward-app (not a declared dependency here).

Removed:
- PolicyControlsView.tsx (+ story) and the whole policy-controls/ module
  (AutoApprove/SpendingLimit/RateLimit/ApprovedAddresses/TimeWindow sections,
  PolicyToggle, helpers, constants, types, stories, smoke tests).
- components/steward/ (injected slot wrappers + stories + smoke test).
- AppBootConfig steward slots (stewardLogo/stewardApprovalQueue/
  stewardTransactionHistory) and the StewardLogoProps / StewardApprovalQueueProps
  / StewardTransactionHistoryProps / AppBootStewardTx* / AppBootStewardPending*
  / AppBootStewardApprovalActionResponse types in boot-config-store.
- The matching barrel re-exports (components/index, ui index), the agent
  external-modules.d.ts prop shims, the "settings/PolicyControlsView" coverage
  entry, the feature-surfaces story showcase, the orphaned template stub
  exports, and a stale design-doc listing.

Kept (real wallet/auth plumbing): client-wallet approveStewardTx +
/api/wallet/steward-approve-tx, client-types-steward (StewardApprovalInfo /
StewardApprovalActionResponse), state approveStewardTx, contracts
StewardApprovalInfo, the shared StewardPolicyResult type, and the
CloudWalletProvider "steward" value. The cloud dashboard + all cloud Settings
sections are untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deletes plugin-companion and every reference to it across the runtime,
app, UI, registry, build config, tests, and docs. The VRM avatar chat
overlay, the /companion view, the emote system (PLAY_EMOTE), the
companion ShellView state, and all wiring are gone; the shell state
machine is now {character, desktop}.

- Delete plugins/plugin-companion (199 files: plugin, views, actions,
  assets, tests) and drop its workspace dep (bun.lock, package.json)
- Strip companion from agent core-plugins, plugin-resolver,
  external-modules.d.ts, and every view-coverage / view-parity /
  tui-view / bundle-lifecycle test; remove the qa-checklist companion
  flow (live-e2e)
- scenario-runner: rewrite the companion-emote scenario to
  GENERATE_MEDIA-only (deterministic-media-actions), preserving media
  coverage, and drop PLAY_EMOTE from the action-coverage ratchet
- Regenerate the prompts action spec and core action-docs (PLAY_EMOTE
  removed); drop PLAY_EMOTE from the training context-catalog
- Remove the dead `lucide-react -> ../plugin-companion/node_modules`
  tsconfig path mapping from 15 plugins (lucide-react is root-hoisted)
- Remove companion stubs from the scaffold optional-app shim and the
  unused LockScreen.companionBar slot (+ its DEFERRED.md note)
- xr-functional-parity: drop facewear from the GUI===XR parity registry
  (its config moved to a Settings section, so it has no standalone GUI
  view); registry is now 14 plugins, counts reconciled
- Clean stale companion references from knip.json, README, the root
  CLAUDE.md/AGENTS.md, architecture.mdx, action-catalog.md, the
  spatial-convert workflow, real.config, and plugin vitest/comment refs

Drift fixups re-derived against current develop (the original commit had
drifted ~207 commits):
- Retarget ensure-avatars.mjs off the deleted
  eliza/plugins/app-companion/public dir to apps/app/public — the same
  surviving VRM target the sibling process-vrms.mjs writes and the
  static file server serves /vrms and /animations from
- Remove the now-dead playEmote client method (client-agent.ts) and the
  /api/emote PostEmoteRequestSchema contract + test (shared/contracts)
- Drop the companion storybook glob/sort, the design-review template
  companion ViewSpec, the EMOTE stub exports in the optional-app shim,
  and the dead VITE_ENABLE_COMPANION_MODE flag from app/.env.example
- Fix the view-interaction-coverage visualCases count (58 -> 56) after
  dropping the companion GUI_INTERACTION_OWNERS block

Note: packages/shared/src/i18n/keywords/action-search.generated.keywords.json
keeps its action.playEmote.request retrieval entry. It is generated
(retrieval-only ranking metadata, not an availability check) and a full
regen pulls in ~26k lines of unrelated keyword drift, so it is left at
develop's state, matching the original removal commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@lalalune
lalalune force-pushed the refactor/remove-companion-and-steward-policy-ui branch from 6cc0193 to 7abbac4 Compare June 30, 2026 07:50
The companion (VRM avatar) view (`viewId: "companion"`) is no longer
registered after the plugin-companion removal, so every reference that
routed to it — deterministic regexes, view-id token lists, command-matcher
keyword maps, evaluator candidate sets, and their test expectations — was a
dead route. Removed them:

- plugin-app-control: the `/my companion|the companion|my avatar/ -> companion`
  deterministic rule (views-show.ts), the `companion`/`avatar` view-name
  tokens (views.ts), the `companion` keyword block + view-id entry
  (view-command-matcher.ts), the `companion` evaluator candidate
  (view-context.ts), and the matching test expectations
  (view-command-matcher.test.ts, view-context.test.ts,
  view-command-shortcut.test.ts — including the companion-emote cases).
- plugin-app-control app/app-launch examples: swapped the removed
  `companion` example app for the still-existing `feed`.
- plugin-training: dropped `companion` from the view enums in
  gepa-view-switching / multishot-view-switching / verify-view-switching
  (and the verify direct-prompt case).
- plugin-facewear app-xr e2e route-server: dropped the `companion` view id.

Kept (legitimate non-view uses of "companion"): the desktop companion-BAR UI
variant (`"native" | "companion" | "titlebar"`), the electrobun companion
WINDOW mode, mobile/desktop-companion remote-capability providers,
phone-companion, browser-bridge companion, and the CSRF companion cookie.

Verify: plugin-app-control view-command-matcher/view-context/
view-command-shortcut tests 236/236 pass; plugin-app-control typecheck clean;
plugin-training scripts typecheck clean; grep shows no remaining
`viewId: "companion"` / avatar-view routing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@lalalune

Copy link
Copy Markdown
Member Author

Trivial residual to sweep during the audit:app pass (non-blocking, non-functional):

plugins/plugin-facewear/src/actions/view-actions.ts still carries one illustrative few-shot example referencing the now-removed companion view — "switch to companion in XR""Switching to companion view." in the XR_SWITCH_VIEW action's examples. The action resolves the target view id dynamically (extractViewId), so this is purely an example string, not a live route — but it should be repointed to a still-existing view (e.g. feed) so the few-shot doesn't teach a removed view id. Worth folding into the same PR when this gets the visual-audit pass.

All functional companion-view routing (plugin-app-control deterministic rules + matcher + evaluator, plugin-training view enums, facewear XR e2e route-server) is removed and verified clean.

@lalalune
lalalune merged commit 99cab28 into develop Jun 30, 2026
65 of 80 checks passed
@lalalune
lalalune deleted the refactor/remove-companion-and-steward-policy-ui branch June 30, 2026 08:56
@claude

claude Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error —— View job


I'll analyze this and get back to you.

@github-actions

Copy link
Copy Markdown
Contributor

LifeOps Benchmark — eliza

Run ID: lifeops-eliza-28429507661

LifeOps Benchmark

Model: gpt-oss-120b
Judge: claude-opus-4-7
Scenarios: 25
pass@1: 0.000
pass@k: 0.000
Total cost: $0.0000

Full artifacts: see the lifeops-run-eliza-28429507661 upload on this run.

@github-actions

Copy link
Copy Markdown
Contributor

LifeOps Multi-Tier Benchmark

Suite: — Tiers requested:

large — cancelled

frontier — cancelled

Artifacts: lifeops-multi-tier-large-28428931277, lifeops-multi-tier-frontier-28428931277

@lalalune

Copy link
Copy Markdown
Member Author

Post-merge review (adversarially verified): the core removal is clean and non-breaking — plugin-companion is fully gone, zero live-source references remain, and tsconfig/knip/registry/core-plugins/xr-parity-counts are all consistent. No over-deletion (the 2D avatar-preview path — getVrmPreviewUrl, content-packs, avatar API — is correctly retained).

There's a non-breaking dead-weight tail worth a follow-up cleanup (none of it breaks build/typecheck/tests, since every ref is inert dead config or tree-shaken dead code):

  • Orphaned 3D-renderer dep @pixiv/three-vrm still declared in packages/app/package.json, packages/ui/package.json (twice, at mismatched ^3.4.5/^3.5.3), and the project template — no live importer remains (the deleted VrmViewer was the sole consumer).
  • DynamicViewLoader.tsx:450-451 still maps @pixiv/three-vrm + /nodes shims for view bundles that no longer exist.
  • state/vrm.ts getVrmUrl() (the 3D .vrm.gz model URL) is exported-but-unused (only re-exported via barrels).
  • Dead story-gate refs (play-coverage-gate.mjs SCAN_ROOTS, a11y-baseline.json, ui-story-gate.yml path triggers), ~80 stale .gitignore lines, orphaned avatarselector.*/nav.companion i18n keys, and a stale play_emote keyword.

Filing as a follow-up rather than reopening, since none of it is a regression.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

LifeOps Benchmark — hermes

Run ID: lifeops-hermes-28429507661

LifeOps Benchmark

Model: gpt-oss-120b
Judge: claude-opus-4-7
Scenarios: 25
pass@1: 0.240
pass@k: 0.240
Total cost: $0.8623

Full artifacts: see the lifeops-run-hermes-28429507661 upload on this run.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

LifeOps Multi-Tier Benchmark

Suite: smoke — Tiers requested: large,frontier

large

LifeOps Multi-Tier Benchmark

Tier: large
Suite: smoke

frontier

LifeOps Multi-Tier Benchmark

Tier: frontier
Suite: smoke

Artifacts: lifeops-multi-tier-large-28429507801, lifeops-multi-tier-frontier-28429507801

lalalune pushed a commit that referenced this pull request Jul 1, 2026
…in set

#10434 (99cab28) removed the `companion` view from the builtin navigation
set (BuiltinTab / TAB_PATHS) — and `voice` is likewise no longer a builtin tab —
but the view-lifecycle matrix test still classified both, so
"covers exactly the builtin tab set" failed on develop tip (classified 36 vs
TAB_PATHS 34). TAB_PATHS/BuiltinTab is the authoritative builtin set and is
correct; the test was stale. Drop the two stale entries; assertion stays an
exact deep-equal against TAB_PATHS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lalalune added a commit that referenced this pull request Jul 1, 2026
…ity, dock upgrade trap, chat-first sidebar, credential-preserving saves (#10994)

* fix(ui): deep-QA batch — transcript data loss, notification reachability, dock upgrade trap, chat-first sidebar, credential-preserving saves

Confirmed by an adversarially-verified QA sweep of the chat/launcher/home
surfaces on develop (20 confirmed findings; this lands the highest-value,
lowest-risk nine):

- shell-state: keep attachment-only and secret-request-only turns in the
  transcript window — an image-only send (or a generated-image reply) used
  to silently vanish when the turn settled (CRITICAL: perceived data loss).
  New unit tests cover both roles, every phase, and the empty-array case.
- HomeScreen pull-zone: stop double-counting safe-area-top (the strip
  deadened ~70px of home content on notched iPhones — taps dead, scroll
  refused); the strip now spans only the residual tucked band + 30px. It is
  also a real button now: click/tap and Enter/Space open the notification
  center, so desktop fine-pointer and keyboard/AT users have a path to
  notifications (was pointer-drag-only, home-only).
- launcher-layout: re-seed the default dock for pre-#10800 payloads
  (favorites [] without intent marker) — those users were locked out of the
  dock forever with no in-product recovery. An explicitly committed empty
  dock now stamps dockCleared and stays respected. Corrupt payloads fall
  back to the seeded default. Migration + marker regression tests added.
- ConversationsSidebar: 'New chat' clears the active terminal session (the
  fresh conversation was created but never shown); the collapsed-rail '+'
  is now chat-first (it always spawned a TERMINAL via an unreachable-branch
  bug; New terminal stays in the expanded terminal section).
- conversation guards: stamp scope 'general' at creation and reject
  reserved legacy page-chat titles on rename — renaming a chat to
  'wallet'/'settings'/… made it vanish from every list with no recovery.
- plugin/connector config saves: handlePluginConfigSave now reports
  success; ConnectorsSection + PluginsView keep the user's typed draft when
  a save fails (pasted tokens/keys were silently wiped on network errors).
- e2e harness de-flake (verified root causes, not product bugs): chat-sheet
  keyboard block uses structural flicks instead of a knife's-edge velocity
  race; launcher fixture overflows the seeded dock (27 specs) with a loud
  bundle-time guard, restoring the Page-2/page-swipe assertions.

Focused suites green: shell-state (13), launcher-layout (35 incl.
property), HomeScreen (7), conversations (21), settings+plugins state
(202); run-launcher-e2e 15/15; run-chat-sheet-e2e PASSED (51 screenshots).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(evidence): post-fix captures for the deep UI/UX QA sweep (#10722)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): notification dismiss visibility, safe deep-link on home tile, reachable background redo

Three more adversarially-confirmed QA findings:

- NotificationCenter row dismiss (X) was opacity-0 with only a hover
  reveal — invisible on touch (the pull-down sheet is the primary mobile
  surface) yet still hit-testable, so near-edge taps silently deleted
  notifications; keyboard focus landed on an invisible button. Now dimmed
  visible at rest, full opacity on hover and focus-visible.
- The home Notifications tile fed producer/LLM-influenceable deepLink
  strings straight into the navigate:view rail, bypassing the
  scheme-checked navigateDeepLink single source of truth: https deep links
  were dead taps (cross-origin pushState throw swallowed), unvetted schemes
  were forwarded, and the tile never marked the notification read (surfaces
  diverged). Now mirrors NotificationRow.handleOpen exactly.
- Background redo (#10771) was unreachable by any user path: the UI channel
  handles op 'redo' but the BACKGROUND action's op union/resolver/schema
  had no redo and no UI control existed. Added the redo op end-to-end
  (regex intent, explicit op, handler broadcast, similes/examples/schema)
  and a Redo2 button next to Undo gated on canRedoBackground. 4 new tests;
  plugin suite 21/21, UI suites 15/15.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): overlays stacked above the chat win taps and Escape; palette opens visible

Two adversarially-confirmed z-order findings:

- The chat's document-level outside-tap swallower ate the FIRST tap on any
  surface painted above the chat glass (notification pull-down sheet at
  z-9501, tutorial card at Z_TUTORIAL, any Radix dialog) and collapsed the
  chat under it. The swallower now cedes taps whose target sits inside
  [data-above-shell-overlay] or [role=dialog]; the notification sheet,
  its backdrop, and the tutorial spotlight are tagged.
- /commands opened the CommandPalette dialog (Z_DIALOG=170) BEHIND the
  open chat sheet (Z_SHELL_OVERLAY=9000) — an invisible focus-trapped
  dialog; Escape then closed the palette AND collapsed the chat at once.
  The composer's palette route now collapses the sheet first so the
  palette opens visible over the pill (raising all dialogs above the shell
  overlay was rejected: tooltips at Z_TOOLTIP=300 inside dialogs would
  stack under them), and the chat's Escape handler defers to an open
  dialog (scoped to [data-state=open] dialogs + the notification sheet —
  broad role=dialog would match always-mounted shell surfaces and
  permanently disable Escape-collapse).

Regression tests: taps inside a role=dialog layer no longer collapse the
sheet; Escape with an open dialog leaves the chat open and collapses it
once the dialog is gone. 4/4 targeted + existing collapse tests green;
run-chatux-gesture-e2e ALL PASSED post-change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): one-shot terminal problem-focus + tap-vs-scroll discrimination on the chat action rail

Two adversarially-confirmed QA findings:

- A blocked/errored coding-agent session permanently hijacked the chat
  surface: ChatView auto-focused any error/blocked PTY session whenever
  activeTerminalSessionId was null, and 'blocked' is a routine long-lived
  waiting-for-input state — closing the panel or selecting a conversation
  bounced straight back to the terminal, locking the desktop chat window
  out of every conversation. Auto-focus now fires at most once per
  transition into a problem state (pickProblemSessionToAutoFocus + a
  handled-set ref; viewing counts as handled; recovery evicts so a new
  transition focuses again). 7 new tests reproduce the loop through
  React's real effect scheduling.

- Flick-scrolling the ChatView transcript on touch toggled the action rail
  (copy/edit/play/delete) on whichever message the finger started on:
  handleTapReveal had no move-slop or selection guard, unlike the shell
  ThreadLine. Touchstart position is now recorded and >10px travel (either
  axis) or a non-collapsed selection suppresses the toggle, mirroring
  ThreadLine's COPY_MOVE_CANCEL_PX pattern. 3 new tests (clean tap
  toggles; vertical/horizontal scrolls don't; selection suppresses).

Focused suites: 17 files / 85 tests green; packages/ui typecheck clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): reachable wallpaper picker (Background settings section) + credential form for mode-less connectors

Two adversarially-confirmed QA findings:

- BackgroundSettingsSection was orphaned dead code: the only surface with
  BackgroundSettingsControls is the 'background' view (viewKind: preview —
  hidden on every default build) and the section was never registered, so a
  default user could not change, undo, or preview the wallpaper from any
  visible UI (chat-only via the BACKGROUND action). Registered as a real
  system-group Settings section (Wallpaper icon, en label; other locales
  fall back to the default label).

- Settings → Connectors dead-ended every connector without a hardcoded
  mode list (farcaster, bluesky, matrix, nostr, msteams, mattermost,
  twitch, instagram, …) with the factually-wrong '{name} uses its own
  setup surface.' — no link, no form, even with declared parameters.
  Mirrors the canonical /connectors fallback: no declared modes + declared
  parameters → the credential form IS the setup surface
  (setupPluginId ?? plugin.id). local-setup/plugin-managed/cloud-managed
  modes keep their dedicated panels (regression suite still green).

Settings sweep: 18 files / 208 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(evidence): audit:app 349/349 post-fix captures (broken=0 needs-work=0)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): single-owner gesture arbitration for nested pagers + fine-pointer paging control at any width

Two adversarially-confirmed QA findings (one root cause):

- The home↔launcher rail and the launcher grid pager both attach pointer
  handlers on the same bubble path: one horizontal drag was tracked — and
  painted — by BOTH pagers (touch: ~1.35x finger motion, background gap at
  the screen edge, spring-back jerk on release), and for mouse/pen the
  outer handler's later setPointerCapture stole the pointer mid-drag,
  freezing the inner pager (dragRef never saw pointerup, ResizeObserver
  resync skipped) — mouse drag-paging on launcher page 0 could never reach
  the Developer page. Violated the shell-surface store invariant that a
  swipe claimed by two machines must be structurally impossible.

  useHorizontalPager now runs a shared pointer-claim registry: trackers
  register in bubble order (innermost first); the first pager that commits
  a horizontal axis AND canMove in the drag direction claims the pointer
  exclusively and synchronously EVICTS the rest (push, not poll — losers
  may never see another event once the winner holds capture). Eviction
  abandons the drag immediately and settles to the resting page. An
  unowned dead-end drag rubber-bands on the innermost pager only. finish()
  claims before advancing so two pagers can never both commit one
  pointerup. Also fixes a latent scheduleOffset bug where a synchronous
  rAF left the frame permanently pending, swallowing later drag paints.

- PagerEdgeButtons dropped its min-width:1024px gate (hover+fine-pointer
  kept): with page dots off in production, a fine-pointer window under
  1024px had NO control that reached launcher page 2.

Also updates the springboard-migration component tests to the #10800-QA
dock re-seed semantics (dock-less migrated layouts dock chat+settings;
manual page order still preserved for non-dock ids) — the old expectation
was invalidated by the launcher-layout upgrade-path fix in this PR.

Tests: 4 new nested-harness arbitration tests + 3 composed tests on the
real bubbling path (tile-origin drags) + real-browser regression in
run-home-screen-e2e (mouse drags page the inner grid both directions; the
rail never hijacks) — HOME-SCREEN and LAUNCHER e2e PASSED; focused suites
88/88; packages/ui typecheck clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(ui): drop stale companion+voice from view-lifecycle matrix builtin set

#10434 (99cab28) removed the `companion` view from the builtin navigation
set (BuiltinTab / TAB_PATHS) — and `voice` is likewise no longer a builtin tab —
but the view-lifecycle matrix test still classified both, so
"covers exactly the builtin tab set" failed on develop tip (classified 36 vs
TAB_PATHS 34). TAB_PATHS/BuiltinTab is the authoritative builtin set and is
correct; the test was stale. Drop the two stale entries; assertion stays an
exact deep-equal against TAB_PATHS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: moon <stupidlybadadvice@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Shaw <shawgotbags@gmail.com>
lalalune added a commit that referenced this pull request Jul 1, 2026
Six quality-gate tests were red on develop, protecting nothing while every
branch inherited the noise. Each fixed with an evidence-based intent decision:

1-3. no-focus-ring-gate — the design system owns focus affordances globally:
  - MessageSearchPanel: drop focus:bg-muted/60 + focus:outline-none (hover keeps
    the pointer affordance).
  - agent-orchestrator-room-view: the status-dot cutout ring-1 ring-bg becomes
    border border-bg (same 1px bg-colored cutout, no ring utility).
  - ContinuousChatOverlay edit-textarea (#10713, my surface): drop
    focus:border-white/40; the base border-white/20 stays.
4. will-change-gate — xr-scene declared willChange on LAYOUT properties
   (left/top/width/height), the exact anti-pattern the gate's doctrine calls
   out (only transform/opacity benefit from compositor promotion; layout props
   still reflow). Removed the ineffective hint.
5. view-lifecycle-matrix — the MATRIX carried stale rows: companion (feature
   removed by #10434) and voice are no longer in BUILTIN_VIEW_IDS. Removed the
   stale classifications; the per-view policy loop (74 cases) stays green.
6. widget-coverage — ratcheted the app-manifest floor 28 → 26 with receipts:
   plugin-companion removed by #10434, plugin-elizamaker by c394453 — both
   intentional deletions that shrank the set without the floor moving.
7. overlay-app-registry ×2 — the expected arrays listed feed before contacts
   while the assertion sorts the received list; the 4-member set is identical,
   only the expectation ordering was wrong. Alphabetized.

All 5 gate files green (93 tests); packages/ui typecheck exit 0; touched-
component suites (ContinuousChatOverlay + message-search, 109 tests) green.

Co-authored-by: lalalune <shaw.nicola.walters@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
lalalune pushed a commit that referenced this pull request Jul 2, 2026
…load routes (#10434 follow-up)

The 3D companion (VRM avatar + its scene background) was removed in #10434,
but the plumbing that fed it was left behind: the POST/GET
`/api/avatar/vrm` + `/api/avatar/background` routes, the client's
uploadCustomVrm/hasCustomVrm/uploadCustomBackground/hasCustomBackground
methods (upload* had no callers at all), and the startup existence probes.
Nothing renders an uploaded model or scene background anymore, so those
HEAD probes only produced 404 noise on every first-run/startup.

- avatar-routes.ts: drop the four vrm/background handlers (keep the live
  Discord avatar cache route); drop the now-unused readRequestBodyBuffer +
  resolveStateDir imports and the unused req/json ctx destructure.
- client-agent.ts: delete the four dead ElizaClient methods + their type
  declarations.
- startup-phase-hydrate.ts: drop the hasCustomVrm/hasCustomBackground
  probes (and the resolveApiUrl import + two Date.now() render-time
  nondeterminism sources they carried), keeping the live
  `resolvedIdx === 0 -> setSelectedVrmIndex(1)` default-avatar fallback.

Live built-in avatar selection (selectedVrmIndex / avatarIndex /
getVrmPreviewUrl) and the content-pack asset path are untouched. Agent +
UI typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lalalune pushed a commit that referenced this pull request Jul 2, 2026
…y probe (#9950, #10712)

- #9950: the all-views aesthetic audit caught a crashed density probe with a
  zero-density placeholder ({borderDividerCount:0, whitespaceRatio:1}), which
  SILENTLY satisfied both the minimalism budget and the ratchet — a probe crash
  read as "perfectly minimal → pass". Now the failure is recorded as a
  densityProbeFailures finding (surfaced in the summary log + per-view markdown,
  same posture as hoverFailures) and the budget/ratchet are skipped when the
  probe failed, so a crash can never manufacture a pass.
- #10712: force-add the four gitignored live-run streaming logs the evidence
  README cites (git add -f) so a fresh clone keeps the real-Cerebras run proof.
- Strict-config typecheck fixes for the #10694/#10434 work: widen
  makeGlslConfig's uniforms param to the unknown-valued partial it already
  normalizes, and drop the two now-removed avatar setters (setCustomVrmUrl/
  setCustomBackgroundUrl) from the useStartupCoordinator deps literal in
  AppContext (they left HydratingDeps with the dead-route removal).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lalalune added a commit that referenced this pull request Jul 2, 2026
…ckground + interaction/QA de-larp (#9952, #10694, #10722, #10434) (#11083)

* feat(app-control/ui): give background redo its real triggers — agent op + settings button (#10694)

The #10771 slice landed redo in the store and the background:apply
receiver, but nothing could emit it: the BACKGROUND action op union
stopped at set|undo|reset and the settings controls only rendered Undo.

- BACKGROUND action: add "redo" to the op union/param enum, REDO plan
  inference (checked before UNDO so "restore the change I undid" reads
  as redo), handler branch broadcasting {op:"redo"}, REDO_BACKGROUND
  simile, examples + docs.
- Settings: Redo2 button paired with Undo, disabled without redo
  history, registered as agent-surface element background-redo.
- Tests: 5 new action tests (21/21 green; plugin suite 1262 green),
  new BackgroundSettingsControls.test.tsx, BackgroundView redo case,
  fuzz/store seeds carry the redo fields.
- background e2e: fixture mirrors useDisplayPreferences redo semantics;
  runner gains set→set→undo→redo round-trip (agent redo + UI redo) with
  refreshed screenshots/video evidence (11/11 assertions green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): de-larp gesture e2e to real CDP touch — and fix the two real bugs it exposed (#10722)

CI gated the synthetic-event gesture runner while the real-touch runner
was excluded — the gated test was the larp. Converting to real input
immediately surfaced two real production bugs the synthetic events
masked:

- TopicGroup: pointerup→onTap toggles, React swaps header↔pill, and the
  browser's synthesized click re-hit-tests onto the replacement element
  and toggles BACK — a tap/click on the header/pill was a visible no-op
  on phones and desktop. Fixed with the suppressClickRef +
  onClickCapture idiom (onClick kept for keyboard activation).
- HomeScreen: the home scroller (overflow-y-auto ⇒ touch-action:auto)
  consumed horizontal touch pans itself, so home→launcher swipe could
  never fire from the widget area on real touch. Added touch-pan-y.

Test/CI changes:
- run-chatux-gesture-e2e: synthetic PointerEvent dispatch → shared
  touchTap/touchSwipe (real CDP touch) + real mouse flicks for the
  fine-pointer profile; assertions now match real behavior (vertical
  touch flick on a pan-y header scrolls, it does not collapse).
- run-home-screen-e2e: the 3 mobile outer-rail swipes converted to real
  touch (+touchSwipeRight); desktop rail paging deliberately stays
  mouse (fine-pointer affordance, documented).
- real-touch-gestures: settleMainThread (rAF round-trip) before every
  gesture — root-causes and fixes a pre-existing develop red where CDP
  touch raced a busy renderer main thread in conversation-swipe;
  deleted the zero-consumer touchPinch export (touchTap now consumed).
- ui-e2e-gate.yml: gate test:home-screen-e2e (the honest runner is now
  in CI), fix the mislabeled chatux step, add output-home artifacts +
  src/testing path filters.

Verified locally: chatux 7/7, home-screen full pass (~70 asserts),
conversation-swipe ALL PASSED (5 failed on develop baseline before the
helper fix), chat-sheet pass, 40/40 unit tests, Biome clean, workflow
YAML parsed. Evidence re-captured in output-home/ and output/.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(agent/app-core): de-larp #10712 — honest SSE contract + live streaming/thinking assertions with real Cerebras evidence

The "local-inference vs cloud-resolved provider path" parity matrix was
a labeled mock: the providerPath string only round-tripped through the
test's own fixtures, and no code under test reads a provider branch
(route → generateChatResponse → messageService → useModel is
provider-agnostic; selection lives in core's model registry, which the
contract test mocks by design). Collapsed to one honestly-named
conversation-stream-sse-contract.test.ts pinning the full frame
contract: thinking→streaming status ordering, ordered cumulative
tokens, terminal done with fullText+thought, done-is-terminal,
thought-never-in-visible-text.

The real provider path is now covered where it belongs — the live e2e:
- streaming-visible-text.live.e2e.test.ts records every SSE payload and
  adds a live frame-contract test (status ordering, monotonic fullText,
  done schema, conditional-but-contractual thought that must never leak
  into visible frames).
- Fixed a real warm-boot race it exposed: /api/health flips ready
  before the deferred provider plugin registers — gate on
  /api/status.canRespond instead.
- Hardened the lane: persistent models cache, 300s ready timeout,
  hang-proof teardown, ELIZA_STREAM_FRAME_DUMP evidence hook.

Live evidence (hand-read, key-free logs) in
.github/issue-evidence/10712-live-streaming-thinking/: 3 consecutive
green runs against Cerebras gpt-oss-120b, 19 token frames growing
43→909 chars, real usage (2737 prompt / 229-234 completion tokens).
Honest findings recorded: live reply arrives via running_action (REPLY
callback), and thought was absent on this path in both dumped runs —
the thought-carrying branch stays pinned deterministically.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(app): give the Her-minimal aesthetic gate teeth — ratchet with committed baseline (#9950)

The minimalism metrics were computed but could never block: computeVerdict
mapped every breach to needs-eyeball, and the signal was actually DEAD in
real runs — the audit spec never passed viewportArea, so
exceedsMinimalismBudget always returned false. A cramped view shipped
green.

Ratchet design (same idiom as ui-determinism-baseline):
- aesthetic-minimalism-baseline.json records breaching views' metrics
  (83 entries / 33 slugs from a real full audit:app run on this tree).
- Breaching view NOT in baseline → needs-work (BLOCKING).
- Baselined view → blocks only when a metric regresses past baseline
  ±5% relative tolerance (live-DOM jitter absorber; real regressions
  move density far past 5% — recorded values cluster at 89-304 vs the
  45 ceiling). Within tolerance stays soft.
- Spec afterAll now throws unconditionally on ratchet failures (verified
  live: the empty-baseline run correctly failed on all 83). Corrupt
  baseline fails loudly via parseMinimalismBaseline.
- Refresh: bun run --cwd packages/app audit:app:minimalism:update
  (regenerates from the latest report.json; prunes healed views so a
  re-breach counts as new).

Also: collectHoverViolations no longer swallows hover failures with
.catch(()=>{}) — failed probes are recorded as hoverFailures findings.
First real run surfaced 8 previously-invisible probe failures.

Verified: full audit:app run (348 passed, gate replay against committed
baseline → ratchet-blocking=0, good=136/needs-eyeball=212); 55/55 audit
unit tests incl. 12 new ratchet cases; Biome clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(ui): lock the floating chat to onboarding until an option is chosen, auto-collapse on completion (#9952 follow-up)

During in-chat first-run the overlay was fully interactive: free text
went to the real server before a runtime existed, and Escape/outside
tap/pull-down could dismiss the onboarding sheet entirely (the e2e
helpers relied on it). Now, while first-run is pending:

- Composer locked: textarea disabled with "Choose an option to
  continue"; attach/mic/send (and push-to-talk) disabled; submitText
  hard-guarded against the prefill/dictation/slash side doors. The
  transcript choice widgets and OAuth/secret blocks stay interactive.
- Send seam hard-blocked: sendActionMessage drops any
  non-__first_run__: value while firstRunComplete is false — nothing
  reaches rawSendActionMessage.
- Collapse locked at one choke point plus drag gates: document/thread/
  composer Escape, grabber tap + keyboard close, outside-tap detector,
  pull-down/drag/settle-free, header clear + launcher buttons, and
  conversation swipe are all no-ops; the sheet stays pinned at full.
- Auto-collapse exactly once on the completion falling edge
  (wasFirstRunOpenRef), revealing home; ordinary sessions never
  collapse. Desktop ?shellMode=chat-overlay verified unaffected — that
  shell never mounts the overlay/conductor.
- Dead code: finishRemote + normalizeRemoteTarget deleted;
  runFirstRunFinish narrowed to FirstRunFinishDraft (cloud|local); dead
  switchAgentProfile port unwired (live remote path is
  adopt-remote-first-run, untouched).

Tests: new ContinuousChatOverlay.firstrun.test.tsx (8 — lock, no-collapse
paths, choice interactivity, one-shot auto-collapse + unlock);
conductor state-machine tests now drive the real handleFirstRunAction
seam (local end-to-end + POST-once + re-tap idempotency, BYO-keys
banner, cloud OAuth→agent-pick→bind→tutorial, cloud error→local retry,
unmount unregistration); e2e asserts the lock + negative Escape mid-
onboarding and auto-collapse + unlock in all five completion flows
(14 passed incl. Pixel-7 touch lane); cloud-live spec picks
tutorial:skip before its real turn since completion now gates the
composer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(agent,ui): remove the dead custom-VRM / custom-background upload routes (#10434 follow-up)

The 3D companion (VRM avatar + its scene background) was removed in #10434,
but the plumbing that fed it was left behind: the POST/GET
`/api/avatar/vrm` + `/api/avatar/background` routes, the client's
uploadCustomVrm/hasCustomVrm/uploadCustomBackground/hasCustomBackground
methods (upload* had no callers at all), and the startup existence probes.
Nothing renders an uploaded model or scene background anymore, so those
HEAD probes only produced 404 noise on every first-run/startup.

- avatar-routes.ts: drop the four vrm/background handlers (keep the live
  Discord avatar cache route); drop the now-unused readRequestBodyBuffer +
  resolveStateDir imports and the unused req/json ctx destructure.
- client-agent.ts: delete the four dead ElizaClient methods + their type
  declarations.
- startup-phase-hydrate.ts: drop the hasCustomVrm/hasCustomBackground
  probes (and the resolveApiUrl import + two Date.now() render-time
  nondeterminism sources they carried), keeping the live
  `resolvedIdx === 0 -> setSelectedVrmIndex(1)` default-avatar fallback.

Live built-in avatar selection (selectedVrmIndex / avatarIndex /
getVrmPreviewUrl) and the content-pack asset path are untouched. Agent +
UI typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ui,app-control): arbitrary programmable GLSL shader background (#10694)

develop already shipped the versioned undo/redo half of #10694 (#10771 +
#10960). This adds the headline feature it was missing: a real, arbitrary
GLSL fragment-shader background mode.

Renderer (@elizaos/ui/backgrounds):
- shader-schema.ts — typed, clamped uniform contract (u_speed/scale/intensity/
  seed), hexToRgb, and a static fragment-source safety gate (size bound + reject
  unbounded while/do loops). three.js-free so it unit-tests without WebGL.
- shader-presets.ts — 5 self-contained GLSL ES 1.00 presets (aurora, lava,
  plasma, waves, nebula), bounded loops only.
- ProgrammableShaderBackground.tsx — renders the fragment via raw three.js
  (already a dep; no new deps) with the full safety sandbox required for
  untrusted GPU code: compile-validate BEFORE showing, uniform clamping, a
  frame-time watchdog that falls back on a GPU stall, WebGL context-loss
  recovery, and a single static frame under prefers-reduced-motion. Any failure
  → onFallback and the color field paints instead — a bad shader can never
  white-screen or hang the app.
- BackgroundConfig gains a `glsl` mode (source + presetId + uniforms);
  normalizeBackgroundConfig validates/clamps it and collapses a
  missing/hostile/oversized source to the color field on load.
- AppBackground switches to the shader (keyed by source for a fresh attempt on
  replacement) with the color-field fallback; useBackgroundApplyChannel handles
  glsl payloads: preset set, explicit source, and live-shader uniform tweaks.

Agent (plugin-app-control BACKGROUND action):
- NL → shader plan: named presets (+ synonyms: molten→lava, ocean→waves,
  cosmic→nebula), a generic "animated shader" → default preset, and relative
  tweaks (slower/faster/brighter/dimmer/bigger/more-detailed) → uniform patch.
  Precedence: named preset > concrete color > tweak > generic shader, so
  "red brighter" stays red and "molten" beats a bare color. The action names a
  preset id + uniforms only; GLSL source stays in the renderer.

Tests (all green): shader-schema (clamp/NaN/hostile/while-loop/hex), preset
library integrity, ProgrammableShaderBackground fallback + no-crash-on-garbage,
persistence glsl round-trip + hostile-source collapse, AppBackground glsl render
+ fallback, a 400-step set/undo/redo storm fuzz across shader/image/glsl
asserting clamped-finite uniforms + history-cap + flag invariants, and BACKGROUND
action preset/tweak inference + emit. Stories added for story-gate. ui +
plugin-app-control typecheck + biome clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ui): gate the last onboarding-lock collapse seam + document the contract (#9952)

Cluster-A audit follow-up. Two gaps on the onboarding lock:

- The TUTORIAL_CHAT_CONTROL_EVENT handler (rest/reset → collapse, prefill →
  un-pill) was the one collapse seam outside the gated funnel. It is unreachable
  in the real flow (the tour starts only after completeFirstRun), but a stray or
  adversarial window event during onboarding could have collapsed the pinned
  sheet. Now the handler no-ops while firstRunOpen. New firstrun test drives
  rest/reset/pill/prefill during onboarding and asserts the sheet stays pinned
  FULL and the composer stays locked.
- The lock contract had zero documentation. Documented the full contract (pinned
  FULL, composer + side-door lock, every gated collapse path, one-shot
  auto-collapse) in first-run/IN_CHAT_ONBOARDING_DESIGN.md + a README pointer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ui,app): strict-typecheck cleanups + de-larp the aesthetic density probe (#9950, #10712)

- #9950: the all-views aesthetic audit caught a crashed density probe with a
  zero-density placeholder ({borderDividerCount:0, whitespaceRatio:1}), which
  SILENTLY satisfied both the minimalism budget and the ratchet — a probe crash
  read as "perfectly minimal → pass". Now the failure is recorded as a
  densityProbeFailures finding (surfaced in the summary log + per-view markdown,
  same posture as hoverFailures) and the budget/ratchet are skipped when the
  probe failed, so a crash can never manufacture a pass.
- #10712: force-add the four gitignored live-run streaming logs the evidence
  README cites (git add -f) so a fresh clone keeps the real-Cerebras run proof.
- Strict-config typecheck fixes for the #10694/#10434 work: widen
  makeGlslConfig's uniforms param to the unknown-valued partial it already
  normalizes, and drop the two now-removed avatar setters (setCustomVrmUrl/
  setCustomBackgroundUrl) from the useStartupCoordinator deps literal in
  AppContext (they left HydratingDeps with the dead-route removal).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(spatial): de-larp the WEBXR "validated end-to-end" overclaim (#10722)

Cluster-E audit: WEBXR_PLATFORMS.md claimed the production enterImmersiveScene()
was "validated end-to-end against the IWER emulator … read back two textured
quads from the session framebuffer", but no committed test opens an immersive
session or reads pixels — the only test exercises the mocked navigator.xr
availability contract — and enterImmersiveScene/enterImmersiveFromSpecs have no
production caller. Removed the unbacked claim and added an explicit "NOT yet
validated" section stating the immersive render path + hand-pose interaction are
uncovered and the entry points are unused, so the doc no longer advertises a
validation that doesn't exist.

(The sibling tui-pty "vacuous skip" audit finding was a false negative: the
real-PTY suite IS wired into CI via test.yml's "Real-PTY TUI smoke" step —
`test:tui-pty` sets RUN_TUI_PTY=1 internally — and passes locally; left as-is.)

Also bundles the four force-added #10712 live-run streaming logs the evidence
README cites (were gitignored).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ui): drop the duplicate background redo button left by the rebase (#10694)

The rebase onto develop merged both develop's original standalone redo button
(#10960) and this branch's paired undo+redo layout into
BackgroundSettingsControls — two "Redo background change" buttons sharing the
same background-redo agent-element ref. Kept the paired-disabled layout (undo +
redo shown together, redo disabled without redo history) and removed the
superseded standalone. BackgroundSettingsControls.test.tsx 4/4 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Shaw <shawgotbags@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant