> For the complete documentation index, see [llms.txt](https://faction-os.gitbook.io/faction-os-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://faction-os.gitbook.io/faction-os-docs/apps/web/readme_web.md).

# Web App

Vite, React, TypeScript, Tailwind, and Zustand frontend for the FactionOS cockpit.

## Local Commands

```bash
npm --workspace apps/web run dev
npm --workspace apps/web run build
```

## Key Areas

* `src/App.tsx` wires the main cockpit surfaces together.
* `src/components/` contains overlays, drawers, roster, mission, settings, and asset-driven battlefield UI.
* `src/lib/` contains pure analytics, formatting, replay, notification, local erasure, game projection, and UI helper logic.
* `src/store/` contains client state stores, including event folding into the derived `GameProjection` aggregate.
* `public/` contains browser-served static assets for the app shell (including `public/music/` background-score tracks).
* `tests/` contains Vitest and happy-dom coverage for UI behavior and pure helpers.

## Cockpit Shell Contract

* The TitleBar is the primary command strip. It exposes local-server status, command palette, settings, shortcut help, heatmap, standings, leaderboard, tool usage, mission complexity, trophy room, and background-music controls.
* Background music (`src/store/useBackgroundMusic.ts` + `MusicToggleButton`) is an explicit opt-in loop over `public/music/` tracks. It is independent of the voice-bark `audioEnabled` setting (which governs synthetic Web Audio cues), never autoplays, and persists its resume point (track + elapsed seconds) to `localStorage` across reloads.
* `src/lib/gameProjection.ts` owns the versioned derived game projection, parser guards, persistence helpers, replay/mock write guard, and selectors consumed from `src/store/useGameStore.ts`. The browser key is `factionos-game-v1`; it stores bounded aggregate state only and is cleared by reset/browser-local erasure, not by hosted or trusted unified erasure.
* Local-server status is local-only. `idle`, `connecting`, `connected`, `disconnected`, and `no server` states must not imply hosted availability, hosted storage, public replay hosting, or remote collaboration.
* The command palette and keyboard shortcuts should match visible shell controls. Settings opens through transient Zustand state, not a persisted preference.
* Mission, notice, roster, scroll, quest, and War Room regions are wrapped with shell surface metadata so loading, empty, offline, and disconnected states remain visible without blank panels.
* The bottom rail owns the compact Quest Board, Orchestration, and War Room cards. `BottomRailExpansionHost` promotes each card into a focused dialog with the same mounted panel implementation, stable `bottom-rail-*` test ids, focus return, Escape close, scroll containment, and one expanded surface at a time. Compact and focused modes must not fork state or duplicate a second workbench instance.
* The focused Orchestration surface is the primary Command Center working path for Campaigns, Queue, File/Git, terminal runtime controls, Executors, Federation, Metrics, handoff, and adjacent-surface links. Retained `Summary`, detail, run-history, terminal, and state popups are secondary inspectors or scoped detail openers; they are not the primary Campaigns or Command Center work surface.
* Focused Quest Board and War Room surfaces keep the same action guards as compact mode: request buttons are disabled while matching requests are in flight, stale state is reset or revalidated on re-entry, and optional Worker or local-server boundaries stay visible as product copy.
* The War Room web panel can use the optional Cloudflare Worker backend in `apps/warroom` for room lifecycle, leader approval, presence, retry, bounded catch-up UX, and redacted cockpit federation.

## Orchestration Diagnostics Contract

The Orchestration panel includes a Diagnostics tab backed by `GET /diagnostics/provider-readiness`. `src/lib/orchestrationApi.ts` validates the provider diagnostics response with the shared protocol parser before it reaches UI state. `src/lib/commandCenterUi.ts` then normalizes provider, setup, runtime, and recovery data into product-facing labels and rejects blocked raw provider diagnostics fields.

The Diagnostics tab renders `DiagnosticsRecoveryPanel` and `ProviderCapabilityMatrix`. It shows provider readiness, disabled reasons, hook/model status, setup command/env-name labels, runtime health counts, safe recovery actions, command-only repair guidance, loading, empty, error, offline, and recovery-success states. It uses the existing command-center in-flight mutation keys for duplicate prevention: `provider-recovery:<action>` and `provider-setup-task:provider_setup`.

The panel may call `POST /diagnostics/orchestration/recover` only for the two server-supported safe cleanup actions: stale listener PID cleanup and malformed spool JSON cleanup. The setup task button creates a local `source: "system"` queue entry referencing `provider-readiness`; it does not run provider setup or call cloud providers. Visible diagnostics must not include raw env values, provider payloads, prompts, command output, logs, tokens, absolute paths, transcripts, raw spool payloads, or local state file contents.

## Orchestration Channel And Scope Contract

The Orchestration panel includes a scope filter bar backed by command-center query parameters. `src/store/useGameStore.ts` stores project, worktree, source, session roster, and scope-kind filters; changing filters clears selected command-center detail so filtered-out records are not shown as active. The shell maps those filters to command-center snapshot requests and keeps task queue, guarded-action, and command-center hydration local.

The Federation tab renders `ChannelCommandPanel` beside collaboration and handoff state. `src/lib/orchestrationApi.ts` validates channel health, pairing, direct channel command listing, and generic/GitHub webhook intake responses. `src/lib/commandCenterUi.ts` converts channel command rows into safe labels for channel, source, delivery, validation, replay, and scope metadata.

Channel UI is review-first. It can show provider posture, paired/unpaired status, trusted-execution labels, inbound command state, replay counts, and scope labels, but it must not render raw request headers, cookies, auth payloads, webhook secrets, raw bodies, raw payloads, signatures, terminal output, prompts, file contents, diffs, logs, absolute paths, or Worker frames. Trusted execution remains unavailable/proposal-only until a later scoped implementation adds explicit consent, authorization, audit, rollback, and tests.

## Command Center Queue And File/Git Contract

The Command Center Queue tab can create local tracking, terminal executable, and Git executable queue entries. Git queue entries are limited to status, stage, unstage, and policy-blocked push planning. The File/Git workbench can copy a reviewed Git draft or structured file mutation draft into a queue task through the shared queue create handler. Queue dispatch runs only the bounded terminal, Git, and file operations allowed by the server coordinator.

Visible queue and File/Git surfaces show product-facing labels such as `Dispatch Git`, `Dispatch file`, compact dirty/staged/conflict counts, file operation labels, path labels, changed-byte counts, rollback state, backup availability, policy-blocked state, retry availability, and recovery guidance. Queue rows may show a rollback button only when a file execution reports an available backup id. They must not render raw Git stdout, stderr, diffs, patch bodies, file contents, search/replacement text, commit messages, command bodies, tokens, secrets, broad absolute paths, terminal drawer links for Git or file runs, or implementation diagnostics.

Campaign workbench dispatch uses the same safe queue and execution evidence contract. The action copy is `Dispatch executable tasks`; planning-only tasks stay visible as planning-only rows, while executable terminal and Git tasks can show readiness, result status, compact summaries, dependency state, queue links, and execution run links. Campaign graph rows sort by `dependencyTaskIds`, show blocked/waiting/retryable context with product-facing labels, file metadata, and keep queue/run links as scoped detail openers. Toasts summarize executed, planning-only, unavailable, failed, blocked, waiting, retryable, and partial-failure counts. Retry controls are shown only for retryable failed or unavailable executable work; dependent-blocked and planning-only rows stay visible without implying execution. Campaign rows and broad workbench responses must not render command text, terminal output, raw stdout/stderr, Git diffs, broad file paths, private executable payloads, or implementation diagnostics.

## Orchestration Metrics And Notifications Contract

The Orchestration panel Metrics tab renders `MetricsAndNotificationsPanel`. It consumes command-center usage metrics, notification readiness records, notification preferences, WebSocket usage/readiness updates, and task queue age data from `src/store/useGameStore.ts`. `src/lib/commandCenterMetrics.ts` builds rollup cards, cost/token/line labels, attention severity buckets, age buckets, mobile metric filters, and notification readiness views. Labels must remain product-facing and safe; broad raw prompts, transcripts, terminal output, provider payloads, file contents, diffs, secrets, and absolute paths must not enter these cards.

The tab can call `refreshCommandCenterUsageMetrics`, `refreshCommandCenterNotificationReadiness`, `updateCommandCenterNotificationPreferences`, and `unsubscribeCommandCenterNotifications` from `src/lib/orchestrationApi.ts`. Refresh and preference actions use command-center in-flight mutation keys and show compact toast feedback. Local notification enablement may request browser permission after a user action and then stores command-center notification preferences on the local server. Web Push consent is readiness state only; the browser does not create a subscription in this build, and the subscribe entry point surfaces an unavailable guardrail instead of claiming delivery.

`src/lib/commandCenterNotifications.ts` routes newly visible command-center approval, question, blocked-task, failed-task, and review-gate items through the existing local notification helper when local preferences allow the category and browser permission is granted. Disabled categories, muted categories, denied/revoked permission, unsupported browsers, throttling, and duplicate in-flight notifications fall back to in-app copy without replaying seeded snapshots. Click targets stay same-origin relative and payload text is bounded.

## Federation/Handoff Workbench Contract

The Orchestration panel Federation tab renders the collaboration and handoff workbench. `src/lib/orchestrationApi.ts` validates `GET /command-center/collaboration-posture` and the collaboration mutation routes before data reaches the store. `src/store/useGameStore.ts` keeps a normalized `collaborationPosture` collection and merges compact `command_center_collaboration_posture_update` plus safe expanded `command_center_handoff_update` metadata from WebSocket frames.

`src/components/orchestration/CollaborationHandoffPanel.tsx` shows handoff visibility, readiness checks, resume-source state, command-center Notice linkbacks, lineage links, relay posture, no-claim remote summary, loading, empty, error, offline, and duplicate-in-flight states. It can post bounded review request, blocker, or completion Notices, scaffold handoffs, validate readiness, set a resume source for ready handoffs, and reset stale state on re-entry. Each action uses stable in-flight keys, idempotency keys where the server accepts them, bounded form values, refresh-on-success, and compact toast feedback.

The drawer can display collaboration posture and handoff detail, but broad Command Center rows, replay, exports, notifications, and diagnostics remain metadata-only. The UI must not render raw prompts, command bodies, terminal output, transcripts, diffs, file contents, raw Worker frames, tunnel tokens, authority values, logs, replay buffers, export payloads, or broad paths. Remote access copy remains a summary label such as `no_claim` or `unavailable`; the browser does not open tunnels, control Worker commands, or imply hosted collaboration safety.

## Notice Board Cockpit Contract

The bottom-right Notice Board is a real active-room coordination surface, not a static demo feed. It renders canonical Notice Board messages from the local server and preserves compatibility with older payload aliases during the Phase 17 migration.

`src/store/useGameStore.ts` handles `notice_board_hydrate`, `notice_board_message`, and `notice_resolved`. Hydrate accepts canonical `messages` and compatibility `notices`; message and resolve events accept canonical `notice` and compatibility `message` aliases. `src/lib/noticeBoard.ts` normalizes the payloads, dedupes by id, sorts newest-first, caps retained history at 50, keeps the compact cockpit list to 6 rows by default, and derives severity from notice type and priority when needed.

Target visibility is enforced in the web store. Untargeted notices are visible to everyone in the active room. Targeted notices are visible when a selected or active hero, mission, or session identity matches the target. If no identity context is available, the web store fails open so the cockpit does not hide potentially important coordination from a user who has not selected a hero yet. Denied, restricted, or revoked target labels are treated as hidden.

`src/components/NoticeBoard.tsx` renders a compact active-room list with type, priority, severity tint, relative age, author, target summary, up to two related-file labels plus a hidden count, content, and resolved state. Its header opens a full local detail popup that can show the complete canonical Notice payload, including ids, timestamps, room, tags, targets, related files, resolution, content/body text, and additional local fields present on the notice.

War Room room notices converge through the same canonical board. The War Room store converts safe `warroom_room_notice` input into a local `POST /notice-board` `announcement` request, blocks unsafe notice text before send, guards duplicate sends while in flight, clears the input after success, and merges Worker `notice_board_message`, `notice_resolved`, and `notice_board_catchup` frames into game-store notices. Remote relay payloads are still optional external transfer and must stay limited to safe coordination messages.

The Notice Board is local cockpit state, like mission and hero detail surfaces. It should not impose a stricter local display boundary than those surfaces: when the local Notice payload includes prompts, coordination text, command context, transcripts, or other first-class local detail fields, the full popup may display them clearly. Optional War Room Worker federation remains a separate external-transfer boundary; remote relay payloads should still be bounded before they enter the local board. Related files are displayed as supplied by the server or protocol helpers.

## Quest Board Cockpit Contract

The bottom Quest Board renders manager-owned local suggestion state from the canonical `suggestion_update` WebSocket snapshot. `src/lib/questBoard.ts` normalizes idle suggestions, codebase issues, session follow-ups, analysis results, project scan items, scan status, and summary counts into typed cards. The web store keeps the string-only `idle_suggestion` event as compatibility input only until a canonical snapshot arrives.

Quest Board cards expose source, title/message, category or severity, priority/effort, relative file path and line when safe, suggested prompt, and bounded agent labels. The component renders loading, empty, disconnected, offline, malformed-snapshot, scan-running, success, warning, and error states without blank panels or raw absolute-path leakage. Summary counts come from the snapshot when available and are recomputed locally only for compatibility cards.

Idle suggestion cards can be accepted, assigned to a selected idle hero, or dismissed. Codebase issue cards can be dismissed. Accept and assign call the local server's suggestion accept route and surface `send_prompt` or `prompt_sent` results as bounded feedback; they do not launch a real executor, Docker task, remote terminal, or codebase issue accept-as-quest flow. Dismiss actions call suggestion or issue dismiss routes and reconcile optimistic removals against the next snapshot.

The codebase scan, analysis, and project scan controls call the shipped local `/api/suggestions/scan`, `/api/suggestions/analyze`, and `/api/suggestions/project-scan` routes. Codebase scan requires the Settings scan root and sends bounded Quest ignore patterns. Trigger buttons and card actions are disabled while their matching request is in flight, reject duplicate local triggers, rollback optimistic removals on failure, and show compact offline, conflict, timeout, malformed-response, and validation feedback.

Scanner camp focus is shipped for the battlefield-to-Quest Board loop. Current codebase issue snapshots from the canonical `suggestion_update` event reconcile into aggregate scanner camps in the browser-local `GameProjection`; activating a battlefield camp opens the existing Quest Board surface filtered to the real issue ids that created that camp. The focused banner can clear back to the full board, and dry Banish uses the existing issue-dismiss action for current issue cards. Banish is deliberately loot-less and progress-inert: it does not grant XP, rewards, achievements, camp kills, camp damage, or Dawn Report behavior.

The battlefield scanner state is also shipped. Manual scans can show Dispatch Scouts while running, completed clean canonical snapshots show Golden Age, and not-yet-scanned or failed scans stay visually distinct from Golden Age. Failed scans preserve existing camps instead of pretending the board is clean. Mock mode may show drill camps in memory, but it is labeled as drill state and does not persist durable camp progress. Reduced-motion mode keeps static camp, scout, Golden Age, failed-scan, and drill labels visible while removing motion-only treatment.

Phase 24 implementation evidence is recorded for the live tier and combat playback slice. Quest Board accept and assign successes can create projection-owned pending links, `mission_start` binds them to camp links, and the battlefield renders linked mission strikes, counterattacks, combo stages, display-only HP drain, verified burn-downs, live skirmishers, rams, gremlins, wraith stacks, siege pressure, scrying, and a single alert focus. The slice is local-first and evidence-backed: replay, reconnect, and mock traffic cannot write durable live-tier progress, persisted projection data remains aggregate-only under `localStorage["factionos-game-v1"]`, and Phase 24 does not add rewards, XP, loot, banners, new protocol events, server routes, hosted dependencies, public-demo synthetic fronts, or trusted-erasure claims.

Keyboard shortcuts match the visible Quest Board controls: `1`, `2`, and `3` accept the top three actionable idle cards, `Alt+X` bulk-dismisses actionable idle suggestions, and `Alt+R` focuses the Quest Board reply target. Shortcut handling ignores editable fields and browser-reserved combinations, and the shortcut help modal must list these bindings with the rest of the cockpit commands.

## Phase 05 War Room Boundary

* Current web runtime: optional Worker room lifecycle and participation controls with local-only fallback when no Worker URL is configured.
* Browser-safe Worker URL defaults can be supplied with `VITE_FACTIONOS_WARROOM_WORKER_URL`. The value is bundled into the Vite app, so it must be a public Worker origin only. Browser localStorage hints can override or clear the default; a stored empty URL keeps local-only mode.
* Shipped Phase 05 Session 04 states: local-only, unavailable, create, join, pending, approved, rejected, connected, reconnecting, caught-up, disconnected, left, invalid input, stale decision, non-leader decision, room not found, room full, rate-limited, and failed socket-open feedback.
* The War Room panel includes Worker diagnostics for disabled, invalid URL, unchecked, checking, healthy, connected, offline, timed-out, rate-limited, bad-response, and generic error states. Diagnostics show source labels, service metadata, and app-level rate-limit copy without exposing account ids, request bodies, prompt content, command output, broad paths, tokens, replay buffers, exports, scans, media drafts, or local diagnostics.
* Create, join, approve, reject, connect, retry, disconnect, leave, and reset controls guard duplicate actions while requests are in flight. Connect and join decisions revalidate the room snapshot before mutating visible state.
* Browser persistence stores only bounded hints: Worker URL, participant id, and participant display name. It does not store room events, socket frames, prompts, local server auth, replay buffers, scan payloads, exports, media drafts, transcripts, command bodies, terminal output, diagnostics, or broad paths.
* Participant names, colors, roles, online flags, pending join state, and local participant markers are display metadata, not hosted identity or account proof.
* The roster and approval queue expose leader, member, observer, pending, online, offline, and local-participant state as text and accessible labels, not color-only indicators.
* Reconnect retry is bounded and leaves the cockpit usable when the optional Worker is unavailable. Catch-up display shows only counts, timestamps, and safe event families; it does not render raw Worker event payloads.
* Leave and reset clear only this browser's local room context. They do not claim trusted erasure of Worker storage, browser hints, local logs, archives, exports, replay buffers, or future hosted data.
* Worker federation is optional external transfer. Session 05 ships allowlisted redacted presence, focus, cursor, hero, mission, and room notice context. The web client must not send prompts, file contents, command bodies, terminal output, transcripts, secret values, broad paths, exports, replay buffers, media drafts, scan payloads, or local diagnostics through the Worker.
* Remote context stays in War Room state and must not enter local hero truth, mission truth, replay persistence, exports, notifications, adapters, settings, or localStorage identity hints.
* Keyboard, pointer, screen-reader, reduced-motion, and mobile behavior must remain visible and bounded inside the bottom-rail panel.
* Phase 05 Session 07 recorded desktop/mobile browser evidence, final security closeout, and documentation closeout for the full War Room phase. The evidence covers disabled, diagnostics, join, pending, create, approved, connected, reconnecting, unavailable, and redacted remote-context states.

## Phase 07 Browser Hosted Config Boundary

* `src/lib/hostedConfig.ts` owns browser-visible hosted config parsing. It consumes the shared protocol vocabulary and accepts only documented public-client `VITE_` values.
* `VITE_FACTIONOS_WARROOM_WORKER_URL` is the only active hosted-adjacent browser default. It must be a credential-free http(s) Worker URL with no query string or fragment. Blank, absent, malformed, reserved, blocked, or unsafe values keep local-only War Room mode.
* Reserved `VITE_SUPABASE_URL` and `VITE_SUPABASE_PUBLISHABLE_KEY` remain placeholders only. They do not enable hosted auth, hosted storage, analytics, public replay, push, remote access, or account-backed behavior.
* Secret-like `VITE_` mirrors of server-only or deploy-only values are blocked. Do not expose service-role keys, API tokens, Cloudflare account ids, zone ids, tunnel tokens, provider secrets, webhook secrets, OAuth secrets, or raw authority tokens through browser config.

## Phase 07 Browser Hosted Identity Boundary

* `src/lib/hostedIdentity.ts` owns browser copy for hosted identity planned or unavailable posture. It consumes protocol vocabulary from `packages/protocol/src/hostedIdentity.ts`.
* The helper exposes local-only fallback, no account UI, no OAuth redirect, no persisted account state, docs paths, unsupported claim labels, and non-overclaim labels. It does not create sign-in controls, account pages, Supabase clients, OAuth redirects, localStorage account state, or hosted account onboarding.
* Room-local Worker authority remains separate from hosted account identity. It can prove access to one Worker room only; it is not SSO, organization membership, public collaboration safety, analytics consent, production audit proof, or trusted erasure.
* Browser UI that displays hosted identity posture must use planned or unavailable language until a later session adds provider flow design, consent, revocation, authorization, audit, tests, docs, and local-only fallback for active account behavior.

## Phase 07 Browser Hosted Persistence Boundary

* `src/lib/hostedPersistence.ts` owns browser copy for hosted persistence planned or unavailable posture, disabled public replay hosting, and local replay fragment fallback. It consumes protocol vocabulary from `packages/protocol/src/hostedPersistence.ts`.
* The helper exposes local-only fallback, no hosted storage, no upload endpoint, no storage client, no account UI implication, no public replay hosting, replay caps, docs paths, blocked payload labels, and unsupported claim labels. It does not create Supabase clients, storage clients, public replay routes, service workers, upload flows, account-backed replay records, or persisted hosted identifiers.
* Current replay sharing remains redacted local URL-fragment sharing using `#replay=`. It caps event count and per-event size, strips consumed or malformed hashes on boot, and does not upload replay data or create hosted public replay storage.
* Future hosted persistence or public replay UI must remain unavailable or disabled until a later session adds consent, payload eligibility, redaction, authorization, retention, export, deletion, expiration, takedown, no-index posture, abuse controls, failure states, tests, docs, and local-only fallback.

## Phase 07 Browser Hosted Analytics Boundary

* `src/lib/hostedAnalytics.ts` owns browser copy for optional analytics readiness, consent, opt-out, settings copy, payload scrubbing, and the optional Umami `/api/send` runtime sender. It consumes protocol vocabulary from `packages/protocol/src/hostedAnalytics.ts`.
* Runtime analytics is off unless public browser config and consent are ready. Missing, blank, false, malformed, or incomplete analytics config keeps the cockpit local-only. Consent alone, provider config alone, a website id alone, or an API key alone does not make analytics ready.
* When ready, the sender posts approved minimized events to Umami `/api/send` with a safe route id. This build still has no tracking script, beacon, SDK import, dashboard, recorder, heatmap, server ingestion, or account-backed consent store.
* Current wired browser events are `cockpit_view`, `settings_status_view`, `hosted_guardrail_status`, `export_local_action`, `replay_local_action`, `notification_local_status`, and `war_room_local_status`. Keep additions behind `packages/protocol/src/hostedAnalytics.ts`, payload allowlist tests, no-send tests, docs, and local-only fallback.
* The Settings drawer displays passive disabled, unavailable, consent-required, opted-out, or ready guardrail copy and revalidates the status when the drawer opens. It does not provide a send-event control or analytics dashboard link.
* Analytics payload helpers are allowlist based. They drop unknown fields and block prompts, provider prompts, transcripts, file/code content, commands, terminal output, secrets, broad paths, exports, replay buffers, scans, diagnostics, logs, backups, account ids, zone ids, tunnel tokens, media drafts, War Room payloads, and quarantined historical content.
* Analytics UX must keep opt-out dominant over config and consent, keep local-only fallback visible, and preserve source, tests, docs, consent, payload minimization, and failure states before new events or providers are added.
* Local self-hosted Umami setup, browser smoke checks, the headless user-agent caveat, and dashboard verification are documented in `../../docs/runbooks/local-umami-analytics.md`.

## Phase 07 Browser Hosted Operations Boundary

* `src/lib/hostedOperations.ts` owns browser copy for push, Web Push, VAPID, remote access, Cloudflare Tunnel, and operator diagnostics posture. It consumes protocol vocabulary from `packages/protocol/src/hostedOperations.ts`.
* Browser notifications remain local OS notifications. The notification helper can request browser permission after user action, show local active-tab or service-worker notifications, and fall back to in-app toasts. It does not create push subscriptions, call a push backend, read VAPID keys, or imply Web Push delivery.
* Command-center notification readiness can record local preferences, browser permission, service-worker availability, Web Push consent, and safe subscription summaries received from the local server. Readiness and consent copy must continue to say hosted delivery is inactive unless a later scoped implementation wires a real push delivery path with tests and docs.
* Notification payload text and click targets are bounded locally. Click targets must stay same-origin relative URLs, and the service worker backstop must not forward prompts, paths, tokens, room payloads, logs, backups, replay buffers, exports, or arbitrary external URLs.
* Remote access and Cloudflare Tunnel remain unavailable in the browser. No browser UI should expose tunnel tokens, Cloudflare account ids, zone ids, API tokens, remote executor controls, hosted diagnostic agents, production-hosted validation, or trusted erasure claims.
* Future push or remote-access UX must remain disabled or unavailable until a later session adds explicit consent or opt-in, VAPID exposure rules, local auth or hosted authorization, token expiration and revocation, rate limits, Origin/CORS, audit, abuse controls, payload minimization, tests, docs, and local-only fallback.

## Phase 08 Production-Hosted App-Shell Boundary

* `npm run release:hosted-smoke` can validate an optional deployed app shell only when `FACTIONOS_PRODUCTION_APP_URL` or `FACTIONOS_HOSTED_APP_URL` is set.
* No production app host is configured by default, so `app-shell` is an unavailable no-claim result in default and no-network release checks.
* Local Vite, local browser, mocked Worker, same-origin, and CI evidence are not production-hosted app evidence.
* A loaded app shell does not prove hosted identity, hosted storage, analytics capture, public replay hosting, push delivery, remote access, Cloudflare Tunnel, production account behavior, certification, or trusted erasure.
* App-shell smoke output must remain sanitized to target id, safe hostname, status, booleans, timing bucket, docs path, and issue codes.

## Hero, Mission, Permission, And Plan Contract

* Hero and mission workflows must keep `selectedHeroId` and `selectedMissionId` synchronized across roster cards, battlefield tokens, hero detail, mission log rows, analytics drilldowns, and mission detail.
* Mission filters must expose state, search, tag, and hero scope as visible, reversible context. Empty states must explain whether no data exists or filters hid the visible rows.
* Mission detail must show prompt, hero attribution, state, timing, tools, token totals, plan context, and bounded text fallbacks without requiring a connected local server.
* Permission and plan decision surfaces must show requester, requested action, rationale, pending or expired state, and result feedback. Duplicate approve, deny, accept, or reject triggers must be guarded locally and should align with the server duplicate-response guard documented in `docs/api/README_api.md`.
* The Command Center Attention tab renders paired attention and permission state from local REST snapshots and compact WebSocket frames. Approve and reject controls must capture an optional bounded reason, send expected revisions and idempotency keys, disable duplicate clicks while a decision is in flight, refresh on success, and show offline, loading, empty, expired, stale, timeout, and malformed-response states without debug copy.
* The Command Center Executors tab renders registry capability posture and bounded execution runs from local REST snapshots and compact WebSocket frames. Family rows show ready, unavailable, approved-not-executing, observe-only, and proposal-only status without implying broad execution. Refresh controls call executor probe helpers with idempotency keys and disable duplicate probes while in flight. The run-history drawer shows queued, executing, executed, failed, unavailable, and not-executed metadata with focus management and Escape close support; it must never render raw command bodies, terminal output, provider payloads, secrets, broad paths, or unbounded result text.
* The Executors tab also renders Session 12 local terminal/container controls. The isolation capability panel shows PTY, Docker, Apple Containers, image, browser dependency, auth mount, worktree mount, network, provider runtime, cleanup, fallback, probe, preview, and run states with product-facing copy only. The terminal runtime drawer is the only browser surface allowed to render the bounded terminal output window for a selected terminal session; broad executor rows, run history, replay, exports, notifications, and diagnostics remain metadata-only. Terminal and container controls must show offline, loading, empty, unavailable, running, exited, killed, restarted, failed, and duplicate-in-flight states without debug copy.
* The Command Center File/Git tab renders active file intents, conflict warnings, dirty state, reviewed file task draft controls, Git workbench previews, and compact execution results from local REST snapshots and WebSocket frames. File conflict checks, clear, and override controls record intent metadata only; file task creation queues a structured manager-backed mutation request for local dispatch and rollback. Git preview and Git execute controls must send idempotency keys, disable matching duplicate triggers while in flight, show loading, empty, error, offline, conflict, dirty, and executed states, and keep broad UI copy limited to repo-relative labels, safe path labels, rollback metadata, and bounded summaries.
* The Mission Artifacts panel renders workpads, evidence metadata, verification attempts, and review gates from local REST snapshots plus compact WebSocket frames. It must show loading, empty, error, offline, disconnected, linked-record, progress-chip, blocked-label, and drawer-detail states without exposing raw evidence contents, prompts, diffs, terminal output, file contents, transcripts, tokens, logs, or broad absolute paths.
* Session 16 command-center shortcuts use the `O` sequence family: `O D` opens details, `O A` focuses Attention, `O Y` and `O N` decide the selected open Attention row, `O B` and `O F` cycle blocked and failed-equivalent queue rows, `O S` and `O 0` focus or reset scope filters, `O T` opens the terminal drawer, `O I` focuses isolated spawn controls, and `O H` focuses handoff controls. Mutating shortcuts must resolve a visible selected or focused row, reuse local duplicate in-flight guards, and show product-facing unavailable toasts when a target is stale.
* The Command Center overview includes the adjacent-surface panel for Mission Control, Plan Workpad, Permission Modal, Notice Board, Quest Board, War Room, Settings, Git/Codex, terminal, handoff, export, replay, achievements, mission history, channels, remote access, and usage. Rows either open existing local UI entry points, select command-center tabs, or render explicit unavailable/no-claim copy. Remote Access remains a no-claim row and must not imply tunnels, hosted diagnostics, Worker commands, or remote executors.
* These surfaces are local runtime controls only. Channel/webhook intake is proposal-first and bounded to local command-center review state. The surfaces must not imply hosted identity, hosted storage, trusted remote command execution, autonomous orchestration, public replay hosting, or web-to-Worker command federation.

## Phase 10 Codex Cockpit Display Boundary

* Shared CLI display labels come from `packages/protocol/src/heroes.ts`; web surfaces should use `src/lib/heroCli.ts` instead of rendering raw CLI ids as fallback copy.
* `codex-cli` is displayed as `Codex CLI` with provider label `OpenAI`. OpenAI model ids are external display data, not a provider-specific model taxonomy.
* Codex heroes participate in the same hero cards, hero detail, mission log search, leaderboard, standings, efficiency, replay, and export-adjacent surfaces as other CLIs. No hosted analytics, public replay hosting, external storage, or Codex settings mutation is implied by the browser UI.
* Mission search may match Codex hero names, `Codex CLI`, `codex-cli`, `OpenAI`, external model labels or ids, and tool names such as `apply_patch`, `Bash`, and `mcp__*` while preserving state, tag, and hero-scope filters.
* Browser-visible tool labels must use bounded display names and categories. `apply_patch` is a file edit, `Bash` is terminal work, and `mcp__*` is MCP/custom work; summaries must not expose raw arguments.
* Replay links and export failure copy remain local-only browser data. Codex-shaped prompts, command previews, broad paths, transcript references, terminal output, patch text, token-like values, URLs, and MCP argument bodies must be redacted before encoding, copying, downloading, or displaying.

## Completed Workflow Behavior

* Roster cards, hero detail, mission rows, mission detail, and leaderboard drilldowns use shared store actions for hero and mission opening. Opening a mission revalidates the mission id and selects the matching hero when the hero is present in the local snapshot.
* Hero scope is visible in the roster and mission log. Users can clear only the hero scope or clear all mission filters from the mission log summary row.
* Mission filtering combines state, search, inferred tags, and hero scope. Empty states distinguish no local mission data from filters that hide matching rows.
* Mission detail includes local snapshot context, offline copy, missing hero or plan snapshot copy, mission status copy, bounded assistant text, and plan context when the open workpad plan matches the mission.
* Permission and plan decisions are local runtime decisions. The UI disables duplicate clicks while a local decision is being accepted, and the game store rejects repeated resolve calls after the first accepted decision.
* Timeline history includes text captions and accessible labels for mission counts, active hours, and tool-call counts so the graph is not color-only.

## Battlefield Contract

* The app battlefield is an asset-driven 2D DOM surface. Do not add Three.js, WebGL, canvas physics, Phaser, PixiJS, or new media dependencies to this path.
* Hero tokens are native buttons with stable accessible names, `aria-pressed`, active mission context, static state badges, and shared-store selection through `selectedHeroId`.
* Empty terrain uses a native clear-selection button below the token layer. Token activation must not propagate into empty-board clearing.
* Selected hero state revalidates when heroes change, so removed heroes do not leave stale selected labels or drawer context.
* Coordinates are clamped through `battlefield2d.ts`; label placement is helper-owned so top, center, bottom, missing, non-finite, outlying, and malformed-limit cases stay testable.
* Background and hero art expose loading, loaded, and fallback states. Empty and local snapshot states must remain visible without a connected local server.
* Reduced motion disables nonessential loop and burst animation while preserving labels, rings, badges, selected treatment, and achievement meaning.
* Achievement bursts are capped, deterministic, nonblocking, and timer-cleaned on expiration, cap removal, and component unmount.
* Phase 24 layers stay DOM/CSS and product-facing: `LiveLegionLayer`, `CombatPlaybackLayer`, and `ScryingAndAlertFocusLayer` expose live tier, combat playback, scrying, alert focus, typed asset/fallback posture, and reduced-motion static twins without direct runtime loading from `assets/generated/game-design/`.

## Settings, Replay, Notification, Export, And Scan Contract

* Settings persist only the bounded browser preference snapshot: theme, explicit audio opt-in, audio volume, reduced motion, severity filter, mock reconnect preference, scan root hint, Quest ignore patterns, roster sort, and notification opt-in. Malformed, stale, partial, invalid enum, denied/restricted/revoked audio, malformed Quest ignore values, or storage-unavailable snapshots fall back to safe defaults.
* Browser audio is optional and off for new users. Most cues are synthetic Web Audio; recorded faction click voice lines (see Faction Click Voice Lines) are the one file-backed exception. Enabling audio attempts a local browser unlock and reports muted, unsupported, locked, unlocked, playing, blocked, failed, or unavailable status through Settings, toasts, and hero Speak copy.
* Audio cues are nonessential. Hero state, captions, mission rows, notices, badges, and settings status remain the source of truth when audio is muted, blocked, failed, unavailable, or unsupported.
* Reset settings restores browser preferences only. It is not a trusted erasure workflow for replay buffers, archives, memory findings, PID files, or workspace files.
* Local erasure controls live in Settings and separate browser-local cleanup from local-server filesystem cleanup. Browser cleanup requires `ERASE BROWSER FACTIONOS STATE` and clears settings, selected faction, derived game projection storage, replay storage, replay URL fragments, War Room hints, local auth hints, and in-memory cockpit context. Local server cleanup requires `ERASE LOCAL FACTIONOS STATE` and uses the loopback server routes.
* Browser notifications are opt-in and local-only. Unsupported, default, denied, granted, throttled, constructor-failure, and local-off states surface as in-app feedback; no push backend is implied.
* Replay share links encode redacted local URL fragments, cap event count and per-event size, strip consumed or malformed hashes on boot, and do not upload replay state or create hosted/public replay storage.
* CSV and JSON exports use the local server, include local auth headers when configured, surface schema and privacy response headers, and map server-offline or validation failures to bounded local copy.
* Codebase scans post the configured root and bounded Quest ignore pattern list to the local server's `/suggestions/scan?full=true` route. The Settings field stores one relative pattern per line and the server merges it with `.factionos-questignore` under the approved root. Scan payloads stay local-only and are never sent to hosted storage, analytics, Worker federation, or provider services. The UI explains the explicit root requirement, approved-root allowlist, `FACTIONOS_SCAN_ROOTS`, filesystem-root refusal, symlink exclusion, file caps, scan status, issue counts, and offline state without broad raw absolute-path leakage.

## Phase 08 Browser Local Erasure Boundary

* `src/lib/localErasure.ts`, `src/store/useLocalErasureStore.ts`, and `src/components/LocalErasurePanel.tsx` own browser-local erasure controls.
* The panel previews browser and local-server boundaries on drawer entry, resets transient confirmation state on close, and disables duplicate confirm actions while requests are in flight.
* Browser erasure removes only this browser's local settings, selected faction hint, derived game projection storage, replay buffer, replay URL fragment, War Room hints, local auth hint, and in-memory cockpit state. It does not delete local server archives, memory, logs, backups, exports, valid spool entries, malformed spool entries, Worker Durable Object state, hosted account/storage/analytics state, public replay, push, remote-access state, or workspace files.
* Local server erasure is exposed as a loopback route client inside the panel. Unavailable, malformed, rejected, completed, and partial-failure states stay visible without raw paths, stored values, confirmation values, replay payloads, room payloads, tokens, logs, backups, exports, diagnostics, or spool payloads.
* Full trusted unified erasure remains unavailable as a broad claim after Session 08 closeout. Browser and local-server cleanup evidence is scoped to the listed boundaries; Worker room-state erasure, hosted data, public replay, analytics, push, remote, backups/logs/archives, and workspace files remain separately scoped or no-claim.

## Phase 02 Browser Validation

* Session 07 browser closeout validates app desktop and mobile Playwright projects with cockpit screenshots, battlefield board screenshots, fallback-board screenshots, nonzero battlefield dimensions, browser guards, loaded image checks, no-canvas assertions, and desktop overlay screenshots.
* Forced app battlefield art failures render visible fallback terrain and hero initials instead of a blank board.
* App e2e evidence is local-browser evidence. It does not claim hosted auth, hosted replay, web-to-Worker federation, or production deployment validation.

## Phase 03 Browser Validation

* Session 07 browser closeout validates desktop and mobile app smoke coverage for the local orchestration panel with browser guards and panel screenshots.
* The browser evidence covers local REST snapshot hydration for queue entries, source-owned templates, empty lineage state, pending guarded-action controls, unavailable guarded-action state, and local-only boundary copy.
* Desktop evidence confirms the constrained cockpit panel, local-only boundary, queue controls, and attached deeper orchestration sections. Mobile evidence verifies the full scrolled control set visibly.

## Phase 04 Media Boundary

* Current app runtime media is limited to app-owned static files such as `/battlefield/*.webp`, `/portraits/*.jpg`, `/favicon.svg`, `/favicon.ico`, `/manifest.webmanifest`, and `/robots.txt`, plus synthetic local Web Audio cues controlled by explicit opt-in settings.
* App media fixture records live in `src/data/mediaCatalog.ts`, with pure lookup and filtering helpers in `src/lib/mediaCatalog.ts`. These fixtures use shared `@factionos/protocol` catalog contracts and validators.
* The Phase 04 media matrix at `.spec_system/archive/phases/phase_04/media_gap_matrix.md` owns provenance, risk, owner-session routing, and release blockers for expanded media work.
* New app runtime image, audio, music, SFX, HUD, achievement, or video references must start with a catalog record before component code, docs, or demo mirror paths depend on the file. Approved records need source, rights, attribution state, optimized output metadata, byte size, dimensions or duration, browser support, fallback, accessibility, privacy, and no release blockers.
* Public demo mirror paths can be represented in app-facing catalog fixtures for parity review, but the standalone `public-demo/` artifact must not import workspace TypeScript or app helper modules.
* Full app file-backed voice lines, SFX, alerts, celebration audio, music, future HUD art, achievement icons, and optional video are planned only after catalog and promotion gates exist. Session 05 does not promote any audio files; it only gates the local synthetic runtime and visible fallback copy.
* Media UI must remain understandable when images fail, audio is disabled, browser playback is blocked, reduced motion is enabled, or optional media is unavailable.
* Phase 24 generated Phase 3 sheets are app implementation references only: `src/lib/generatedPhase3Assets.ts` pins ids, source paths, fallback labels, and reduced-motion pairings, while `src/data/mediaCatalog.ts` keeps the records blocked from browser runtime use until app-owned serving, slicing, optimization, metadata cleanup, fallback, accessibility, privacy, and media gates exist.

## Phase 04 Media Validation

* Session 08 closeout validated the app desktop/mobile Playwright projects for battlefield rendering, failed app art fallbacks, reduced-motion battlefield state, local orchestration panel visibility, browser guards, and retained screenshots under `test-results/e2e-app/`.
* The app media boundary is also covered by `npm run media:gates:check`, `npm run media:visual:check`, `npm run media:drafts:check`, and `npm run battlefield:check`.
* Only `media.battlefield.background` and `media.battlefield.heroStandees` are release-ready app media records. Portraits and favicon/brand-adjacent media remain conditional until source, rights, attribution, metadata, fallback, accessibility, privacy, and budget evidence closes.
* Full app audio is synthetic Web Audio except for the recorded faction click voice lines (see Faction Click Voice Lines). File-backed SFX, alerts, celebration audio, and music remain deferred until later catalog, promotion, accessibility, privacy, and performance review.

## Phase 08 Media Decommission Boundary

* Session 07 keeps media release readiness limited to approved battlefield runtime records. Conditional portraits, brand, showcase, public-demo speech, public-demo music, generated drafts, and quarantined historical media remain non-release until their blockers close.
* The app must not import, transform, optimize, cache, or generate runtime assets directly from ignored `EXAMPLES/` media or raw provider staging.
* Public-demo cache decisions are owned by `public-demo/sw.js` and public-demo validation docs. Session 07 does not change the current `factionos-demo-v9` cache version because no precached content changed.
* Stable web docs may preserve media conclusions and blocker labels only. They must not copy raw prompts, provider payloads, sensitive local paths, copied historical code, or quarantined media excerpts.

## Phase 05 War Room Validation

* Session 07 closeout validates the War Room panel in app desktop/mobile Playwright projects with a same-origin mocked Worker and fake browser socket.
* The retained evidence lives under `test-results/e2e/` and covers disabled, diagnostics, join, pending, create, approved, connected, reconnecting, unavailable, and redacted remote-context states.
* This is local browser evidence. It does not claim hosted identity, mobile certification, public replay hosting, analytics, remote execution, production app hosting, or trusted erasure.

## Phase 06 Collaboration And Mobile Baseline

* Phase 06 Session 01 records collaboration, isolation, and mobile requirements in `.spec_system/archive/phases/phase_06/collaboration_isolation_safety_baseline.md`.
* Mobile and accessibility acceptance lives in `.spec_system/archive/phases/phase_06/mobile_accessibility_acceptance_checklist.md`.
* Later web work must keep optional Worker transfer separate from local-only cockpit state. Remote context must not enter local hero truth, mission truth, queue state, guarded-action state, replay persistence, exports, notifications, settings, adapters, scans, archives, memory, media records, or localStorage identity hints.
* Create, join, approve, reject, connect, retry, disconnect, leave, reset, and federation-send controls must keep duplicate-action guards, snapshot revalidation, visible failure paths, accessible labels, focus behavior, reduced-motion behavior, text fit, and no-overlap behavior aligned with the Phase 06 checklist.
* Leave and reset remain local browser context cleanup only. They do not erase Worker storage, browser hints, local logs, archives, exports, replay buffers, backups, memory, settings, valid spool entries, or future hosted data.

## Phase 06 War Room Authority State

* Phase 06 Session 02 keeps Worker-issued War Room authority separate from browser identity hints. The browser may persist only Worker URL, participant id, and display name hints; room authority stays in Zustand memory and must not be written to localStorage, replay state, exports, settings, logs, or diagnostics.
* Create and join responses store the Worker authority envelope in memory and visible UI state reports only `None`, `Issued`, `Expired`, `Mismatch`, or `Stale`. The UI must never display raw authority tokens or describe authority as hosted account proof.
* Snapshot, connect, socket, approve, and reject flows require current authority. Leader decisions send leader authority, the expected room revision, and an idempotency key so duplicate retries are deterministic and stale decisions fail visibly.
* Hydration, Worker URL changes, participant id changes, leave, reset, rejected joins, expired authority, invalid authority, missing authority, and authority mismatch clear browser authority. Stale revision or replayed authority failures mark the visible authority state stale until the user rejoins or creates a room again.
* Local-only fallback still works when no Worker URL, Cloudflare credentials, hosted identity, hosted storage, analytics, or public replay hosting are configured. Leave, reset, browser refresh, and local hint cleanup are not trusted erasure for Worker storage, browser hints, local logs, archives, exports, replay buffers, backups, memory, settings, or future hosted data.

## Phase 06 Collaboration Event Contracts

* Browser outbound federation uses protocol-validated presence, focus, cursor, hero state, mission state, and room notice frames only.
* Outbound frames are built from safe ids and bounded safe labels. Prompts, assistant text, file contents, command bodies, terminal output, transcripts, URLs, broad paths, exports, replay buffers, scan payloads, diagnostics, logs, media drafts, settings, adapter payloads, and localStorage hints stay local.
* Inbound `remote_event` and `warroom_pending_catchup` socket frames are parsed through shared protocol validators before the Zustand store receives them.
* Remote context remains dedicated War Room overlay state. It can update remote presence, cursor, focus, hero, mission, notice, catch-up, and summary views, but it must not mutate local hero truth, mission truth, queue state, guarded-action state, replay persistence, exports, notifications, settings, adapters, scans, archives, memory, media records, or local identity hints.
* Catch-up and panel summaries expose counts, timestamps, and safe family labels only. They do not render raw target ids, labels from blocked payloads, request bodies, prompts, paths, commands, terminal output, diagnostics, exports, replay buffers, logs, media drafts, or settings values.
* Hosted identity, hosted storage, analytics, public replay hosting, remote execution, production-hosted validation, mobile certification, and trusted erasure remain no-claim or later scoped release work unless a release record proves the specific claim.

## Phase 06 Collaborative Cockpit UX

* Session 04 makes lifecycle, role, authority, reconnect, diagnostics, roster, approval, remote-context, and room-notice state visible in the War Room panel. The visible state summary reports only bounded labels such as local only, unavailable, pending, approved, rejected, connected, reconnecting, caught up, disconnected, left, issued, expired, mismatch, stale, leader, member, observer, and pending.
* Active-room controls keep existing duplicate-action guards for create, join, connect, retry, disconnect, leave, reset, approval decisions, federation sends, and room notices. In-flight controls render disabled or busy states and leave local cockpit workflows usable when the Worker is absent or unreachable.
* Approval and roster rows render leader, member, observer, pending, online, offline, and local participant state as text and accessible labels. Member, observer, pending, missing-authority, stale, and non-leader decision failures remain visible without exposing request bodies, authority tokens, paths, or Worker internals.
* Remote context is shown as redacted Worker context and deterministic remote rows. It may display safe participant counts, presence, focus, cursor, hero, mission, and room notice hints, but it must not mutate local hero selection, mission selection, queue state, guarded actions, replay, exports, notifications, settings, adapters, scans, archives, memory, media records, diagnostics, logs, backups, or localStorage identity hints.
* Room notices use the safe collaboration frame contract. URLs, broad paths, command-like text, prompts, and credential-shaped values are blocked before send; blocked notice text surfaces as visible local error copy.
* Browser evidence and tests use local or mocked Worker paths only. Session 04 does not claim hosted identity, hosted storage, analytics, public replay hosting, remote execution, production-hosted validation, mobile certification, or trusted erasure.

## Phase 06 Mobile Accessibility Hardening

* Session 06 hardens the local cockpit shell for supported desktop and mobile browser widths. The grid uses bounded region scroll, wrapped labels, stable minimum touch targets, and named landmarks for the battlefield, mission and notice rail, hero and scroll rail, and bottom quest, orchestration, and War Room rail.
* Battlefield, mission, roster, drawer, settings, replay, command palette, orchestration, War Room, notice, quest, and toast surfaces expose visible empty, loading, offline, unavailable, disabled, in-flight, and error copy where those states apply. Long names, prompts, notices, remote labels, and status badges wrap instead of hiding primary controls.
* Reduced-motion evidence keeps meaning in static labels, state badges, selection rings, and status text. Browser evidence seeds the app's reduced-motion setting where component state is preference-backed, and also verifies the browser media preference.
* Optional Worker federation remains bounded to allowlisted and redacted room context. Long remote labels in evidence are safe display labels only; remote context still does not mutate local hero truth, mission truth, orchestration state, replay persistence, exports, notifications, settings, adapters, scans, archives, memory, media records, diagnostics, logs, backups, or localStorage identity hints.
* Local orchestration remains a browser view over local REST/WebSocket state. Guarded-action decisions and command-center controls request bounded loopback-server actions only; the web UI does not directly execute terminal, git, Docker, file, remote, hosted, or Worker commands.
* Session 06 component and Playwright evidence is local browser evidence with local server and mocked Worker paths. It is not WCAG certification, mobile device certification, production-hosted validation, hosted identity proof, hosted storage proof, analytics validation, remote execution validation, public replay hosting, or trusted erasure.

## Phase 06 Closeout

* Session 07 closes the web-owned Phase 06 subset for collaboration UX, redacted remote context, local-only fallback, authority failure states, reconnect/leave/reset behavior, mobile layout reachability, text wrapping, focus behavior, pointer behavior, screen-reader labels, and reduced-motion evidence.
* The closeout includes a Plan Workpad mobile overflow fix found by local app-mobile Playwright validation. Footer controls and decision copy wrap on narrow screens while the desktop right-rail layout is preserved.
* Retained evidence is local Playwright coverage with local server and mocked or same-origin Worker paths. It does not claim hosted identity, production-hosted validation, mobile or WCAG certification, hosted storage, analytics, public replay hosting, remote execution, or trusted erasure.

## Phase 07 Hosted UX And Config Boundary

* Phase 07 Session 01 creates hosted-service guardrails only. The web app does not ship hosted account UX, hosted storage, analytics dashboards, public replay hosting, push delivery, remote access, hosted diagnostics, production-hosted validation, or trusted erasure.
* Browser-visible config is limited to documented `VITE_` values. A browser-visible value must be publishable, revocable, non-secret, optional, and backed by local-only fallback. Service-role keys, API tokens, provider keys, webhook secrets, Cloudflare account ids, zone ids, tunnel tokens, VAPID private keys, raw OAuth secrets, and raw authority tokens must never be bundled into the app.
* Future analytics UX must not ship runtime capture until consent, opt-out, taxonomy, payload scrubbing, tests, and docs exist. Future push UX must remain local-notification or unavailable until VAPID, subscription, unsubscribe, retention, and payload-minimization rules are implemented.
* Remote access, tunnels, and hosted diagnostic agents remain unavailable until explicit opt-in, authorization, redaction, audit, tests, and docs exist.
* Public replay remains local URL-fragment sharing. Any hosted replay UX needs consent, redaction, payload caps, expiration, takedown, no-index posture, authorization, failure states, tests, and docs before activation.
* Phase 07 Session 07 closeout validated the shipped web posture: hosted config helper, planned-state hosted identity/persistence/operations copy, consent-gated analytics Settings copy, optional minimized Umami sender, and local-only notification posture. Web tests and the full suite (2186 passed / 1 skipped) confirm the app starts, hydrates, and renders without hosted accounts, analytics, push, or remote access, and stores no raw tokens, account ids, or blocked payloads in browser state. Phase 08 Session 08 adds local release-candidate browser evidence, while production-hosted validation and full trusted erasure remain no-claim.

## Phase 08 Release UX Boundary

* Session 01 release artifacts live under `.spec_system/archive/phases/phase_08/` and route web-owned release evidence to later sessions. They are planning evidence only and do not activate hosted UX.
* Local erasure UX ships as scoped local/browser controls with inventory, preview, confirmation, progress, audit, verification, partial-failure, unsupported, and redacted output behavior. Reset settings, replay clear, room leave/reset, browser refresh, and local hint cleanup are not full trusted unified erasure.
* Session 04 owns Worker room-state erasure copy and hosted identity no-claim copy. The War Room panel can preview and confirm Worker room-record erasure only for the current room leader, using the exact Worker confirmation phrase and a fresh room revision. This deletes one Durable Object room record only; browser state, local files, hosted identity, production-hosted validation, and full trusted unified erasure remain separate gates. The web app must keep Worker authority, participant names, local auth tokens, analytics consent, and public replay consent separate.
* Session 05 owns production-hosted web validation. Local, mocked, same-origin, and Playwright evidence must not be presented as deployed app validation.
* Session 06 owns release mobile/accessibility evidence and certification wording. Automated or local browser checks are not formal WCAG or device certification unless matching evidence is recorded.
* Session 06 release evidence is recorded in `.spec_system/archive/phases/phase_08/mobile_accessibility_certification_evidence.md`. The web-owned evidence uses `npx vitest run apps/web/tests/MobileAccessibility.test.tsx`, focused Biome checks, `npm run test:e2e -- --project=app-desktop --project=app-mobile tests/e2e/app.e2e.ts`, and the public-demo desktop/mobile Playwright projects. It covers local cockpit regions, title-bar controls, settings, replay, export discovery, scan copy, notifications, local erasure, War Room, command palette, shortcuts, heatmap, standings, public-demo mobile layout, visible audio fallbacks, reduced motion, offline shell, text fit, overflow, focus, accessible names, and privacy-safe visible text.
* The evidence remains local browser and component evidence. It does not prove formal WCAG certification, physical-device mobile certification, production-hosted validation, hosted identity, hosted storage, analytics capture, public replay hosting, Web Push delivery, remote access, or trusted unified erasure.
* Session 08 records local app desktop/mobile Playwright as passing with one recovered mobile flaky retry. The optional production app shell remains unavailable in hosted smoke, so do not claim deployed web app validation.

## Faction Click Voice Lines

* Clicking a hero (roster `HeroCard` or battlefield `HeroToken`) or a faction icon in `FactionPicker` plays a recorded faction voice line - the first file-backed audio the web app ships. Clips live in `apps/web/public/speech/` (20 MP3s; all five factions get four lines each).
* Playback is owned by `apps/web/src/store/useFactionVoiceLine.ts`, a shared singleton `Audio` player wired through the catalog in `apps/web/src/lib/factionVoiceLines.ts`. It honors the existing audio opt-in: muted or zero-volume clicks stay silent, a new click cuts off the still-playing line instead of overlapping, and the same line is not repeated back-to-back.
* These recorded click lines are separate from the synthetic Web Audio barks in `voiceSynth.ts`, which still fire on hero state transitions. When the browser cannot play a line or blocks playback, a visible-equivalent toast is posted; hero labels and state badges remain authoritative.
* These clips were wired directly through the audio opt-in rather than the Phase 04 media catalog/promotion gate. Catalog records, byte-size and accessibility metadata, and service-worker/demo-parity reconciliation for the speech set remain outstanding follow-ups.

## Deferred Boundaries

* Remote agent orchestration commands, trusted inbound chat or webhook command execution, hosted or shared queues, real handoff/fork/resume/stop executors, and broad terminal/git/file execution are deferred to later approved scopes. The shipped Command Center includes only local proposal-first channel intake, bounded File/Git workbench routes, and terminal/container controls behind explicit readiness gates.
* Web-to-Worker War Room federation remains optional and redacted. The web panel can create, join, approve, reject, connect, retry, disconnect, leave, reset, send allowlisted context, receive remote overlays, and summarize remote room context when a Worker URL is configured, while local-only cockpit use remains the default fallback.
* Hosted accounts, hosted storage, public replay hosting, hosted analytics dashboards, and collaboration identity are not part of this local web workflow.
* Unified erasure across archives, memory, settings, replay buffers, PID/workspace files, browser state, Worker evidence, hosted placeholders, and release wording remains a Phase 08 closeout gate even though Session 04 adds a narrow Worker room-state deletion control.
* Public-demo media parity, service worker cache updates, and app/demo asset-copy validation are covered by Phase 04 gates. Future app-owned runtime media changes must keep those gates in the same change.

For product requirements, read `.spec_system/PRD/PRD.md` and `.spec_system/PRD/PRD_UX.md`.

## Phase 03 Orchestration Boundary

Phase 03 Session 05 ships local cockpit controls for task queues, agent templates, subagent lineage, mission graph context, and guarded-action decisions. Session 06 adds executor registry posture and execution run history to the same local panel. Session 12 adds local terminal and container runtime surfaces inside the Executors tab: a scoped terminal runtime drawer for selected bounded session output and an isolation capability panel for PTY, Docker, Apple Containers, image, mount, network, provider runtime, cleanup, fallback, preview, and run readiness. Session 16 adds the command-center shortcut and adjacent-surface ergonomics documented above. The panel consumes compact WebSocket hydration frames and local REST snapshots from the loopback server. It shows pending, accepted, rejected, expired, unavailable, approved-not-executing, queued, executing, executed, failed, empty, loading, offline, and disconnected states without implying hosted queues, War Room command federation, trusted inbound chat command execution, analytics, public replay hosting, broad remote execution, Worker command bridges, or tunnels. App desktop/mobile Playwright evidence for the shortcut and adjacent-surface flow lives in `tests/e2e/orchestration-command-center.e2e.ts`.

Guarded-action and command-center attention approve or reject controls send local REST decisions with expected revisions and idempotency keys. The browser disables duplicate decisions while one is in flight and revalidates action state from WebSocket or REST updates. Metadata-only queue rows keep truthful start-tracking behavior. Queue creation can also create bounded terminal, Git, file, or container executable rows; the container mode exposes only safe image, runtime, run-label, and hero-label controls. Campaign task refinement can add the same compact container executable metadata, and campaign dispatch creates queue entries only for campaign tasks that carry a validated terminal, Git, file, or container executable payload. Campaign dispatch follows the persisted `dependencyTaskIds` DAG: prerequisites must complete before dependent executable tasks are queued, and failed, unavailable, cancelled, unknown, cyclic, or planning-only prerequisites show safe blocked/waiting copy instead of running dependents. Planning-only campaign tasks are not queued or executed. Queue rows with a terminal executable payload may dispatch through the local terminal runtime, show safe execution summaries, expose retry when failed or unavailable, and link to the scoped terminal drawer without rendering command text or terminal output in broad rows. Campaign rows can link to the created queue entry and bounded execution run while rendering only safe readiness, `failureCode`/`unavailableReason` labels, file rollback metadata, and result summaries.

Observed Hero/Lineage controls record lifecycle intent and do not claim process control. Managed Hero/Lineage controls appear only when a lifecycle record has a managed session id, ready/running readiness, and supported `stop`, `restart`, or `message` actions. Stop and restart submit scoped local managed-session commands; message uses a bounded request-scoped input and stores only the compact server result summary. Stale, stopped, unsupported, offline, and duplicate in-flight states must keep controls disabled or absent. The Hero/Lineage surface must never render raw commands, terminal output, transcripts, provider payloads, broad paths, tokens, or external/hosted control claims. Provider mutation intents such as model, permission, and isolation changes remain unavailable audit records unless a later scoped executor proves safe control. File/Git workbench controls use the same in-flight guard pattern for conflict checks, file-intent resolutions, Git preview, Git execute requests, file queue creation, and file queue rollback. File-intent actions remain metadata-only; file mutation happens only through structured local file executor requests with compact result metadata. Terminal and container controls use the same in-flight guard pattern for terminal run, input, kill, restart, container probe, isolated spawn preview, isolated spawn run, container queue create, container queue dispatch, and container campaign refinement requests. Container queue and campaign rows show product-facing `Dispatch container` or `Retry container` labels plus runtime, cleanup posture, container run id, unavailable reason, retryability, and recovery guidance only. They must never render Docker commands, raw container output, env, mounts, volumes, image-inspect payloads, workspace paths, or arbitrary hosted/remote execution claims. Queue, lineage, guarded-action, attention, permission, file-intent, Git workbench, terminal session, container readiness, mission artifact, and execution references use detail openers or bounded summaries, so cockpit selection remains synchronized. Artifact WebSocket reducers merge workpad, evidence, verification, and linked review-gate records by id and close stale drawer selections when a selected artifact disappears; terminal snapshot refreshes and queue-targeted execution events must also clear or revalidate stale selected terminal sessions and duplicate in-flight keys. Attention decisions record local audit state and paired command-center updates only; they do not run executors, tools, terminal commands, file edits, Worker actions, hosted jobs, or remote operations. Guarded-action approvals are registry-gated: Git workbench adapters, structured file adapters, and Session 12 terminal/container adapters are the shipped bounded executor-ready subset when their server probes pass. File guarded-action entries execute only through validated local file mutation requests with compact result and rollback metadata, while other default entries remain unavailable or approved-not-executing unless a later scoped implementation registers them.

Broad orchestration UI copy is helper-owned and redacted. WebSocket orchestration frames are allowlist-sanitized before they enter the store and replay buffer. Sensitive prompts, command bodies, terminal output, file contents, tokens, transcript paths, broad absolute paths, raw container probe output, and raw proposal rationale must not appear in broad panel rows, run-history drawers, replay-share paths, notification copy, export-adjacent state, diagnostics, or settings persistence. Bounded terminal output may appear only in the selected terminal runtime drawer after the terminal session detail is loaded.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://faction-os.gitbook.io/faction-os-docs/apps/web/readme_web.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
