{"id":"@lssm-tech/app.web-application-monolith","slug":"app.web-application-monolith","name":"@lssm-tech/app.web-application-monolith","version":"9.1.1","path":"packages/apps/web-application-monolith","manifestPath":"packages/apps/web-application-monolith/package.json","manifestBlobSha":"c8a3c389db15ea4e19ac8dc61250ca3e5f984be9","layer":"apps","physicalArea":null,"primaryArea":"app-composition","classification":{"privateDeclared":true,"catalogFamily":null,"catalogKind":null,"catalogStatus":null,"adoptionMode":null,"stability":null},"description":null,"descriptionOrigin":"package.json","catalog":{"visualTesting":{"status":"deferred","owner":"@contractspec-core","targetWave":"wave-2-core-ui","rationale":"Foundational Storybook adoption establishes shared catalogs and guardrails before this package migrates."}},"canonicalImports":[],"scripts":{"clean":"rm -rf dist .turbo","build":"NODE_ENV=production bun --bun next build","dev":"bun --bun next dev","start":"NODE_ENV=production bun --bun next start","lint":"bun run lint:fix","lint:fix":"node ../../../scripts/biome.cjs check --write --unsafe --only=nursery/useSortedClasses . && node ../../../scripts/biome.cjs check --write .","lint:check":"git ls-files -co -z --exclude-standard -- . ':!next-env.d.ts' | xargs -0 node ../../../scripts/biome.cjs check","lint:privacy":"bun run scripts/lint-privacy-signals.ts","lint:emitters":"bun scripts/lint-page-emitters.ts","lint:no-debt":"bun scripts/lint-no-debt.ts","lint:dev-toolbox-boundary":"depcruise --config .dependency-cruiser.cjs src/","test":"bun test --isolate && bun test ./tests-e2e/support","test:e2e":"playwright test","e2e:local":"bun tests-e2e/support/local-stack/run-local-stack.ts","test:e2e:runtime-topology":"playwright test --config playwright.runtime-topology.config.ts","test:journeys":"playwright test -c tests-e2e/journeys/playwright.config.ts","typecheck":"bun --bun x tsgo --project tsconfig.build.json --noEmit","typecheck:e2e":"bun --bun x tsgo --project tsconfig.e2e.json --noEmit","typecheck:e2e-local":"bun --bun x tsgo --project tsconfig.e2e-local.json --noEmit","test:e2e-local-support":"bun test ./tests-e2e/support","release:check-ux":"bun run scripts/check-ux-copy.ts","prebuild":"bun --bun x tsgo --project tsconfig.build.json --noEmit","companyos:builder:e2e":"bun test src/next-app.test.tsx","experience:coverage":"bun run scripts/generate-experience-coverage.ts"},"scriptStatus":"complete pinned manifest","dependencies":{"@fontsource/ibm-plex-mono":"5.3.0","@fontsource/ibm-plex-sans":"5.3.0","@fontsource/newsreader":"5.3.0","@lssm-tech/bundle.managed-companyos":"workspace:*","@lssm-tech/bundle.marketing":"workspace:*","@lssm-tech/integration.authos-better-auth":"workspace:*","@lssm-tech/integration.communication-os-host-auth":"workspace:*","@lssm-tech/integration.communication-os-client":"workspace:*","@lssm-tech/integration.integration-topology-evidence-postgres":"workspace:*","@lssm-tech/integration.provider-database":"workspace:*","@lssm-tech/integration.providers-impls":"workspace:*","@lssm-tech/integration.runtime-managed":"workspace:*","@lssm-tech/lib.authos-runtime":"workspace:*","@lssm-tech/lib.authos-spec":"workspace:*","@lssm-tech/lib.builder-spec":"workspace:*","@lssm-tech/lib.communication-spec":"workspace:*","@lssm-tech/lib.content-gen":"workspace:*","@lssm-tech/lib.contracts-integrations":"workspace:*","@lssm-tech/lib.contracts-runtime-client-react":"workspace:*","@lssm-tech/lib.contracts-runtime-core":"workspace:*","@lssm-tech/lib.contracts-spec":"workspace:*","@lssm-tech/lib.design-system":"workspace:*","@lssm-tech/lib.design-catalog":"workspace:*","@lssm-tech/lib.data-exchange-core":"workspace:*","@lssm-tech/module.design-studio":"workspace:*","@lssm-tech/lib.observability":"workspace:*","@lssm-tech/lib.organization-planning-spec":"workspace:*","@lssm-tech/opa-booking-react":"workspace:*","@lssm-tech/lib.ui-kit-web":"workspace:*","@lssm-tech/lib.ui-kit-core":"workspace:*","@lssm-tech/lib.personalization":"workspace:*","@lssm-tech/module.auth-os":"workspace:*","@lssm-tech/lib.presentation-runtime-core":"workspace:*","@lssm-tech/lib.presentation-runtime-next":"workspace:*","@lssm-tech/lib.presentation-runtime-react":"workspace:*","@lssm-tech/lib.surface-runtime":"workspace:*","@lssm-tech/lib.translation-runtime":"workspace:*","@lssm-tech/module.agent-fleet":"workspace:*","@lssm-tech/module.app-submission-readiness":"workspace:*","@lssm-tech/module.builder-workbench":"workspace:*","@lssm-tech/module.communication-os":"workspace:*","@lssm-tech/module.customer-support":"workspace:*","@lssm-tech/module.governance-suite":"workspace:*","@lssm-tech/module.review-queue":"workspace:*","@opentelemetry/api":"^1.9.1","@opentelemetry/sdk-trace-base":"^2.10.0","@posthog/react":"^1.11.1","@vercel/analytics":"2.0.1","autoprefixer":"^10.6.1","better-auth":"1.7.5","lucide-react":"catalog:ui","next":"catalog:","next-themes":"^0.4.6","posthog-js":"^1.434.0","posthog-node":"^5.51.7","react":"catalog:","react-dom":"catalog:","tailwind-merge":"catalog:ui","tailwindcss-animate":"catalog:ui","@lssm-tech/lib.crypto-utils":"workspace:*","@lssm-tech/lib.founder-intelligence-spec":"workspace:*","zod":"catalog:","@lssm-tech/bundle.marketing-operations":"workspace:*"},"devDependencies":{"@axe-core/playwright":"^4.13.0","@lssm-tech/tool.typescript":"workspace:*","@playwright/test":"^1.63.0","@tailwindcss/postcss":"4.3.3","@types/node":"catalog:types","@types/react":"catalog:types","@types/react-dom":"catalog:types","axe-core":"^4.13.0","dependency-cruiser":"^18.3.1","happy-dom":"catalog:testing","postcss":"catalog:build","postcss-load-config":"^6.0.1","tailwindcss":"catalog:build","tw-animate-css":"1.4.0","typescript":"catalog:"},"peerDependencies":{},"optionalDependencies":{},"peerDependenciesMeta":{},"bundledDependencies":null,"dependencyStatus":"complete pinned manifest","exports":{".":"./src/index.ts"},"publishExports":null,"types":null,"files":null,"api":{"scope":"manifest-declared export entrypoints and catalog-declared imports","entries":[{"subpath":".","importPath":"@lssm-tech/app.web-application-monolith","target":"./src/index.ts","targets":[{"conditions":[],"target":"./src/index.ts","sourcePath":"packages/apps/web-application-monolith/src/index.ts","sourceUrl":"https://github.com/lssm-tech/contractspec/blob/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/web-application-monolith/src/index.ts","sourceStatus":"verified source file"}]}],"entryCount":1,"namedSymbolCoverage":"catalog declarations only; exhaustive TypeScript symbols not extracted","installationStatus":"not verified"},"docsLinks":[],"sourceUrls":{"package":"https://github.com/lssm-tech/contractspec/tree/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/web-application-monolith","manifest":"https://github.com/lssm-tech/contractspec/blob/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/web-application-monolith/package.json","readme":"https://github.com/lssm-tech/contractspec/blob/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/web-application-monolith/README.md","guide":"https://github.com/lssm-tech/contractspec/blob/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/web-landing/public/llms-packages/app.web-application-monolith.txt"},"readme":{"status":"available","body":"# Managed CompanyOS Web Application Monolith\n\nReal Next.js App Router web entrypoint for Managed CompanyOS, the invite-led AI operating system for companies built on the ContractSpec substrate. It renders the public `/companyos` managed landing plus an authenticated cockpit whose organization and workspace scope comes from verified session authority: onboarding, admin, AuthOS, integration hub, operating graph, composite workflow, replay, evidence, lifecycle, knowledge, team, analytics, finance, special ops, and degraded-state navigation.\n\nThe public login route discovers enabled AuthOS methods from the API and renders\nemail/password, email OTP, email magic link, Google, Microsoft, LinkedIn, TikTok,\nFacebook, and GitHub without exposing provider credentials. The authenticated\nAuthOS account page supports explicit social account linking and unlinking.\nAuthentication-audit listing and bounded CSV export stay same-origin in the\nbrowser. The web BFF forwards only the HttpOnly session cookie and allowlisted\nfilters to the API; tenant and actor authority remain server-derived.\n\n`/companyos/auth/mfa` is both the session-bound second-factor challenge and the\nauthenticated MFA management surface. Credential accounts confirm their current\npassword before setup or disablement. Setup returns an authenticator URI, verifies\nthe first six-digit code, and reveals recovery codes only after success. Sign-in\ncan use either TOTP or a single-use recovery code without client-supplied tenant\nor challenge authority. The security page reads the server-owned\n`twoFactorEnabled` state rather than a fixture flag.\n\nThe app stays thin by composing `@lssm-tech/bundle.managed-companyos` and design-system primitives. Provider decisions remain approval-gated. Credential values are never displayed — only redacted references (`'********'`) appear, enforced by `assertRedactedSecretDisplay` in the bundle.\n\n## Embedded CommunicationOS\n\nThe embedded CommunicationOS pages clear deterministic preview data before\nrendering live mode. Their scoped same-origin typed client loads the selected\nroom plus persisted contacts, governed work, and journal evidence. Authorized\naction, handoff, and identity decisions use canonical idempotent APIs and then\nreconcile the live projection; denied or failed mutations never update a\nfixture-backed success state.\n\nThe legacy `/companyos/inbox`, `/threads`, `/actions`, `/handoffs`, `/contacts`,\nand `/compose` entry points are compatibility redirects into that authenticated\nroute family. Thread-detail redirects preserve the opaque room identifier. No\nlegacy collaboration URL renders static demo-tenant data.\n\n`/companyos/communication-os/agents` is the persisted managed-agent registry.\nAuthorized organization managers propose, activate, reject, and revoke agent\nrecords through the same tenant-scoped governed-work journal. Non-manager\ncreation and lifecycle writes are denied by the API, and the UI remains\nread-only. Legacy `/companyos/agents/**` pages redirect to the corresponding\nlive agent, action, governance, handoff, audit, or compose surface.\n\n## Public booking browser posture\n\nThe public booking route keeps availability custody same-origin and renders\nexplicit unavailable/degraded states. Time choices expose DST-disambiguating\nlabels, and operator repair controls retain a 44px minimum touch target. Browser\ntests exercise these repository-owned contracts without live provider writes.\nThe dynamic Server Component reads the declared adapter posture at request\ntime. Unset, invalid, or sandbox-only posture is projected as degraded\navailability with no selectable slots; only `adapter_ready` enables the\ninteractive lifecycle client.\nCurrent screenshot baselines, deployed-domain journeys, and tenant activation\nremain external proof gates; local or detached-worktree screenshots are not\nproduction-readiness evidence.\n\nThe protected OPA Booking hub proxies per-link embed-origin changes only after\nsame-origin validation and current-session resolution. Owners can replace an\nactive link's full exact-HTTPS allowlist under optimistic revision control;\nsaving an empty list disables widget code without changing the hosted URL.\n\nThe protected `/companyos/admin/google-calendar` journey is enterprise-admin\nonly. It loads selectable calendar identifiers from the authenticated API,\nnever offers a free-text provider identifier, and persists explicit\nEurope/Paris working hours, buffers, notice, horizon, and approval mode through\nthe same-origin server action. It does not run OAuth or provider writes.\n\nThe protected public-profile editor previews the exact persisted revision\nthrough `GET /api/v2/opa/profile/preview?revision=<n>`. The no-store BFF\nderives tenant authority from the current session and strips tenant, profile,\nlink, event-type, scheduling-resource, and connection refs before the browser\nrenders desktop and mobile candidates. The editor page uses the same\nserver-authoritative Better Auth subject as protected mutations. It derives an\nopaque authority fingerprint, keys the client subtree with that value, and\nrequires every initialize/save/publish/revoke command to echo it. A stale tab\nor pre-switch draft receives `409 profile_context_changed` with `no-store`\nbefore the destination profile is read or written; no raw authority value is\nrendered.\n\nThe protected `/companyos/opa-os` page reads request-time planning-store and\nactive-workspace Google Calendar readiness through a no-store BFF. It renders\nruntime failures as unavailable/error rather than as ordinary setup gaps.\nDaily-operation and briefing-preference BFF mutations validate the browser's\nexact configured web origin before resolving a session or synthesizing the\ntrusted upstream origin. Missing, sibling, suffix-confused, and hostile origins\nreceive a no-store `403` and never reach the API host.\nAuthorized enterprise admins,\nfounder approvers, and operations managers can submit authority-free meeting\nintent and receive a durable agenda, availability-aware proposed times, and a\nredacted pending-approval receipt. The responsive en/fr/es UI labels cached or\nunavailable calendar data and states explicitly that preparation created no\ncalendar event or Google Meet link. A validated route-selected `meetingRef`\nbinds the written-first surface to live OPA state; the API re-proves the current\nsession and tenant instead of treating route state as authority. Enterprise\nadmins and founder approvers can\nthen approve, deny, cancel, or reschedule through same-origin CSRF-protected BFF\nroutes. Operations managers can prepare and manually refresh status but do not\nsee lifecycle mutation controls. Pending provider work is refreshed only by an\nexplicit user action; the page does not poll automatically. A confirmed Google\nMeet link is rendered only from authenticated canonical booking status. See\n[`docs/runbooks/opa-smart-meeting.md`](../../../docs/runbooks/opa-smart-meeting.md).\n\nProduction uses persisted membership for the live `lssm` and `companyos`\norganizations plus the owner's personal context. The `ndconsulting` and\n`hirly` organizations were decommissioned on 2026-10-20\n([evidence](../../../docs/evidence/authos/decommission-hirly-ndconsulting-2026-10-20.md)), so the context switcher offers only Personal,\nLSSM, and CompanyOS. NDconsulting's Special Ops history and physical\ntenant/workspace identity stay reserved for local/reference fixtures; sibling\norganizations cannot enumerate it. This README does not claim that domains, data authority, support\nownership, or a release has been activated.\n\nThe authenticated shell maps the server-resolved physical tenant to the\ncontract-owned LSSM or CompanyOS brand (the ND Consulting brand mapping is\nretained for local/reference fixtures only). That mapping changes\nvisible labels, breadcrumbs, command headings, graph workspace labels, and the\nshell title without changing fixture ids, navigation evidence, or provider\nauthority. Personal, future, and unknown tenants render the neutral\n`CompanyOS workspace` identity rather than a fictive ContractSpec company.\nBehavior signals use the authenticated tenant subject and a fresh correlation\nid; they no longer attribute production entry events to a shared demo tenant.\n\n## Special Ops production cutover\n\nUse [`docs/runbooks/special-ops-monolith-production.md`](../../../docs/runbooks/special-ops-monolith-production.md) for the database-first Special Ops, AI Ops Sprint, and all-in-one work-graph promotion gates. The web app remains a thin shell: render production-owned bundle/API projections, preserve redaction and AI-dispatch blocks, and do not import `examples/*` at runtime.\n\n## Managed CompanyOS production path\n\nUse [`docs/runbooks/managed-companyos.md`](../../../docs/runbooks/managed-companyos.md) as the cross-lane production-path evidence ledger for DB lifecycle, auth/session runtime, route conversion, UX richness, Special Ops, bundle boundary, and docs/contract gates. This README documents the current web surface; it is not a release gate for any DB-backed route.\n\nCurrent route status:\n\n- Most deterministic reference pages remain `force-static`; integration, onboarding, root, evidence, and other session/persona-sensitive route sections document `force-dynamic` where implemented.\n- DB-backed route promotion requires local/test migrate + seed + verify output, not static workspace builders alone.\n- Auth/session promotion requires unauthenticated denial, authenticated access, role-gate, session revoke, auditor read-only, and tenant-mismatch evidence.\n- Special Ops promotion requires database-first capability graph/workspace/seed-run receipt evidence or an explicit known-gap note in the evidence packet.\n- Replay and evidence routes remain read-only; adding mutation affordances there is a breaking contract change.\n\nDo not hide mock/reference data behind production wording. If a page still reads deterministic bundle projections, call it deterministic/reference in release notes and attach the follow-up lane rather than claiming runtime persistence.\n\n## Personal, company, team, and workspace switching\n\nThe authenticated shell consumes `GET /api/companyos/contexts`, a fresh\nserver-owned projection of the user's personal context and current company,\nteam, and workspace memberships. The browser receives only opaque references\nand a signed five-minute one-use authority token. It never receives or submits a\nphysical tenant identifier.\n\n- An empty account can call the idempotent personal-context ensure route and\n  enter a private, non-shareable workspace without joining a company. The\n  authenticated layout renders this narrow bootstrap before tenant authority\n  exists and does not render protected CompanyOS children until the server\n  confirms the new context.\n- Joining LSSM, CompanyOS, or another company adds a context; it\n  does not move or merge personal data.\n- Company, team, and workspace changes use one atomic\n  `POST /api/companyos/contexts/set-active` command. The selector disables\n  duplicate input, awaits the committed projection, invalidates previous/next\n  cache namespaces, and renders only the confirmed destination.\n- First authenticated load selects a deterministic authorized context and its\n  default workspace when the session has no active selection. Organization and\n  team changes carry their default workspace in the same authority mutation, so\n  protected routes never render against a half-selected session.\n- Team scope can only narrow authority. The next protected API request freshly\n  intersects organization, team, and workspace capabilities and fails closed if\n  membership or a canonical grant was revoked.\n- The grouped picker includes an explicit company-scope choice. Clearing team\n  scope is an authoritative transition and never relies on a local-only UI\n  reset; the compact trigger omits redundant `Company-wide` copy.\n- Browser transport uses `credentials: include` and `cache: no-store`; hostile\n  Origins, malformed projections, inconsistent active refs, expired tokens, and\n  raw provider error details are rejected.\n- One shell-owned context trigger opens the grouped organization, team, and\n  workspace picker. Search appears above eight destinations. Its account-wide\n  `topbar` (default) or `sidebar-footer` placement is hydrated on the server;\n  the footer remains an icon trigger when collapsed and is repeated in the\n  mobile navigation dialog. All forms retain 44px targets and accessible names.\n- The account menu owns explicit `View as`, language, and preference radio\n  submenus. Persona preview is server-confirmed, session-scoped, disclosed by a\n  banner, and intersects the preview projection with the real subject so it can\n  narrow presentation/actions but never widen authorization.\n- Existing organization list/set-active and workspace-list routes remain\n  compatibility surfaces. Retained ND design-partner route names remain isolated\n  under `ndconsulting`.\n\nThe browser transport and cache lifecycle have repository-local tests, but this\nREADME does not claim that a live production deployment, migration, visual\nbaseline approval, or organization\ncutover is clear. Follow\n[`docs/runbooks/real-organizations-rollout.md`](../../../docs/runbooks/real-organizations-rollout.md)\nfor the separately gated migration and operator evidence.\n\nG006 real-route regression evidence is configured in\n`playwright.g006.config.ts`. It starts the actual Next routes with a\ndeterministic synthetic API seam, writes only to `.playwright-output/g006`, and\ncompares committed 320/375/768/1280px en/fr/es light/dark public and admin\nbaselines without snapshot\nupdates. The three-test run also checks axe serious/critical findings,\nresponsive overflow, 44px target height, arbitrary public handles,\nnon-enumerating absence, and browser-visible private-reference exclusion. The\nolder component harness is supplemental evidence only and is not separately\nconfigured as a Playwright project.\n\n\n## CompanyOS marketing contract boundary\n\nThe `(marketing)` route group is wrapped with the shared marketing contract boundary and backed by the full CompanyOS route contract registry from `@lssm-tech/bundle.marketing/content`. The seven PRD CompanyOS pages remain adapter-rendered; bespoke marketing pages stay copy-owned by their existing components while being contract-addressable through the inert boundary. Public legal/trust pages (`/mentions-legales`, `/privacy`, `/terms`, `/cookies`, `/security`) are part of this unauthenticated marketing surface and must stay aligned with the blueprint, sitemap, robots/LLMO artifacts, footer links, and static `public/llms.txt` fallback.\n\nSEO discovery is always on. `/sitemap.xml` is an XML index,\n`/sitemaps/static.xml` contains the exact 24 indexable blueprint routes, and\nprofile shards live below `/sitemaps/profiles/<generation>/<cursor>.xml`.\nUnavailable or stale profile discovery deliberately returns a valid static-only\nindex with `X-Sitemap-Status: degraded`, `no-store`, and `Retry-After: 60`;\ndirectly unavailable profile shards return `503`. Configure only server-side\n`API_ORIGIN` in the web deployment. See the\n[SEO discovery runbook](../../../docs/runbooks/companyos-seo-discovery.md).\n\n## Route inventory\n\n### Persona home pages (Phase 2 boil-lake)\n\n| Route | File | Persona | Purpose |\n|---|---|---|---|\n| `/companyos/founder` | `src/app/companyos/founder/page.tsx` | `founder_approver` | Mission intelligence, pending approvals, evidence readiness, governance alerts |\n| `/companyos/ops` | `src/app/companyos/ops/page.tsx` | `operations_manager` | Operational dashboards, workflow status, team assignments, performance metrics |\n| `/companyos/frontline` | `src/app/companyos/frontline/page.tsx` | `frontline_staff` | Task queue, shift information, training resources, quick-access tools |\n| `/companyos/auditor` | `src/app/companyos/auditor/page.tsx` | `auditor` | Audit findings, compliance status, risk assessments, evidence trails (read-only) |\n| `/companyos/admin` | `src/app/companyos/admin/page.tsx` | `enterprise_admin` | System configuration, user management, integration hub, workspace settings |\n\n### Core feature routes\n\n| Route | File | Purpose |\n|---|---|---|\n| `/` | `src/app/page.tsx` | Cockpit landing; persona dispatcher; bundle proof + governance report |\n| `/companyos` | `src/app/companyos/page.tsx` | Public managed landing; renders bundle-owned Managed CompanyOS view-model/template with deterministic reference posture |\n| `/companyos/onboarding` | `src/app/companyos/onboarding/page.tsx` | Authenticated 3-step role, working-style, and display-preference setup; completion waits for durable tenant-scoped persistence |\n| `/companyos/admin/public-comms` | `src/app/companyos/admin/public-comms/page.tsx` | Force-dynamic persisted profile editor with same-origin authenticated mutations and optimistic revision recovery |\n| `/u/[handle]` | `src/app/u/[handle]/page.tsx` | Force-dynamic arbitrary published profile; unknown, draft, and revoked handles share the same non-enumerating 404 |\n| `/companyos/integrations` | `src/app/companyos/integrations/page.tsx` | Force-dynamic Integration Hub catalog using bundle view-models/templates and per-request role morph resolution |\n| `/companyos/integrations/connections` | `src/app/companyos/integrations/connections/page.tsx` | Force-dynamic global connections view with redacted credential references |\n| `/companyos/integrations/syncs` | `src/app/companyos/integrations/syncs/page.tsx` | Force-dynamic sync health/config view with redacted credential references |\n| `/companyos/integrations/runtime` | `src/app/companyos/integrations/runtime/page.tsx` | Force-dynamic deployment topology passport; separates semantic validation from provider receipt evidence |\n| `/companyos/integrations/[integrationId]` | `src/app/companyos/integrations/[integrationId]/page.tsx` | Force-dynamic integration detail overview with role-gated actions |\n| `/companyos/integrations/[integrationId]/credentials` | `src/app/companyos/integrations/[integrationId]/credentials/page.tsx` | Force-dynamic security-class credentials tab; never statically render |\n| `/companyos/integrations/[integrationId]/secrets` | `src/app/companyos/integrations/[integrationId]/secrets/page.tsx` | Force-dynamic security-class secrets tab; never statically render |\n| `/companyos/integrations/[integrationId]/config` | `src/app/companyos/integrations/[integrationId]/config/page.tsx` | Force-dynamic integration configuration tab |\n| `/companyos/integrations/[integrationId]/audit` | `src/app/companyos/integrations/[integrationId]/audit/page.tsx` | Force-dynamic integration audit tab |\n| `/companyos/opa-os` | `src/app/companyos/opa-os/page.tsx` | Force-dynamic Organization Planning surface; reads tenant-scoped store/Google readiness through the authenticated BFF, renders the bundle-owned responsive smart-meeting prepare/approval/status/cancel/reschedule flow with role-accurate controls and manual refresh, and emits `surface_viewed` / `role_morph_resolved` with server-only tenant hashing |\n| `/companyos/nav-map` | `src/app/companyos/nav-map/page.tsx` | Power-user navigation topology; renders all nav surfaces + edges as interactive DAG; capability-gated |\n| `/companyos/workflow/[runId]` | `src/app/companyos/workflow/[runId]/page.tsx` | Composite workflow run; graph nodes + steps; approval/deny affordances role-gated; AI-dispatch blocked |\n| `/companyos/replay/[runId]` | `src/app/companyos/replay/[runId]/page.tsx` | Deterministic replay viewer; events ordered by sequence; **read-only** — no approve/deny/request affordances |\n| `/companyos/evidence/[runId]` | `src/app/companyos/evidence/[runId]/page.tsx` | Auditor-safe evidence receipt; metadata counts, verification hash, redaction summary |\n| `/companyos/auth` | `src/app/companyos/auth/page.tsx` | In-app account surface: active sessions (skeleton, retryable error, empty state, sibling-session revoke), sign-out and linked accounts |\n| `/companyos/auth/sessions` | `src/app/companyos/auth/sessions/page.tsx` | Compatibility alias; redirects to `/companyos/auth` |\n| `/companyos/auth/security` | `src/app/companyos/auth/security/page.tsx` | Force-dynamic security settings: server-resolved contact + MFA status, active sessions with loading/error/empty states, user-agent device labels (browser + OS); location is shown only when known |\n| `/companyos/auth/passkeys` | `src/app/companyos/auth/passkeys/page.tsx` | Force-dynamic authenticated WebAuthn passkey enrollment backed by `@better-auth/passkey` and persisted credential metadata |\n| `/companyos/auth/invitations` | `src/app/companyos/auth/invitations/page.tsx` | Force-dynamic persisted organization invitation administration with list, send, token-rotating resend, and terminal revoke actions |\n| `/companyos/auth/audit` | `src/app/companyos/auth/audit/page.tsx` | Read-only AuthOS audit projection |\n| `/companyos/auth/locked` | `src/app/companyos/auth/locked/page.tsx` | Public-safe account lockout recovery; no tenant or actor projection, with keyboard-safe password-reset and sign-in actions |\n| `/companyos/lifecycle` | `src/app/companyos/lifecycle/page.tsx` | Lifecycle stages and milestone visibility with role redaction |\n| `/companyos/knowledge` | `src/app/companyos/knowledge/page.tsx` | Canonical force-dynamic Company Intelligence overview backed by the authenticated tenant-free surface descriptor |\n| `/companyos/knowledge/sources` | `src/app/companyos/knowledge/sources/page.tsx` | Authenticated source connections/jobs read surface |\n| `/companyos/brain` | `src/app/companyos/brain/page.tsx` | Authenticated Brain projection |\n| `/companyos/knowledge/graph` | `src/app/companyos/knowledge/graph/page.tsx` | Authenticated graph capability surface; queries remain user-triggered |\n| `/companyos/knowledge/assistant` | `src/app/companyos/knowledge/assistant/page.tsx` | Authenticated grounded-answer capability surface; model calls remain user-triggered |\n| `/companyos/knowledge/learning` | `src/app/companyos/knowledge/learning/page.tsx` | Authenticated evidence and learning-health read surface |\n| `/companyos/knowledge/operations` | `src/app/companyos/knowledge/operations/page.tsx` | Authenticated operational-health read surface |\n| `/companyos/company-intelligence`, `/companyos/graph` | compatibility redirects | Redirect to the canonical knowledge overview and graph routes |\n| `/companyos/team` | `src/app/companyos/team/page.tsx` | Team directory and assignment visibility with contact redaction |\n| `/companyos/analytics` | `src/app/companyos/analytics/page.tsx` | Operational analytics; blocked roles receive empty denied view models |\n| `/companyos/finance` | `src/app/companyos/finance/page.tsx` | Finance queues with amount redaction and founder-only approval affordances |\n| `/companyos/special-ops` | `src/app/companyos/special-ops/page.tsx` | Incident and escalation cockpit; blocked roles receive empty denied view models |\n| `/companyos/special-ops/design-partner-readiness` | `src/app/companyos/special-ops/design-partner-readiness/page.tsx` | Force-dynamic thin shell over the persisted API projection when activated; the fictive packet remains test/demo only. |\n| `/error` | `src/app/error.tsx` | Global error boundary (Client Component); renders `stateCopy.error` with \"Try again\" |\n| `/loading` | `src/app/loading.tsx` | Global loading state; renders `stateCopy.loading` with `role=\"status\"` |\n| `/not-found` | `src/app/not-found.tsx` | Global 404 page; renders `stateCopy.empty` with home link |\n\nPublic auth flows have one canonical route each. The proxy redirects signed-out\nvisitors to `/login`, Better Auth emails link to `/reset?token=…` and\n`/accept-invite`, and the AuthOS adapter requests resets with\n`redirectTo: '/reset'`, so the `src/app/(auth)/**` tree is canonical. The\nlegacy `/companyos/auth/*` duplicates are server redirects\n(`src/routing/companyos-auth-aliases.ts`, covered by\n`companyos-auth-aliases.test.ts`) that forward only a same-origin `redirect`\npath and the reset `token`:\n\n| Legacy alias | Canonical route |\n| --- | --- |\n| `/companyos/auth/sign-in?redirect=` | `/login?redirect=` |\n| `/companyos/auth/magic-link?redirect=` | `/magic-link?redirect=` |\n| `/companyos/auth/forgot-password` | `/reset` |\n| `/companyos/auth/reset-password?token=` | `/reset?token=` |\n\n`/companyos/auth/{sign-up,verify-email,mfa,locked,connect-lssm,sign-out}` have\nno `(auth)` duplicate and stay canonical. `src/app/auth/**` is the separate\n`id.lssm.co` central-login surface, not a duplicate.\n\n### Design-partner readiness route\n\nThe `/companyos/special-ops/design-partner-readiness` route renders the\nsession-scoped API projection. The deterministic identifiers\n`tenant_ndconsulting_design_partner_demo`,\n`workspace_ndconsulting_desliance_demo`, and\n`Atlantique Industrie — mission retournement` are fixture-only and must never\nbe a production fallback. Route availability is design-partner readiness, not\nproof that the private pilot is activated.\n\nBoundary: it must not import API handlers, DB code, providers, examples, or app-owned reusable domain components. It must not add WebSocket, SSE, EventSource, interval polling, or automatic refresh. Replay and evidence links remain read-only.\n\nRole behavior rendered by the route:\n\n- `operations_manager` may prepare non-dispatch drafts.\n- `expert_reviewer` may attest the decision packet.\n- `founder_approver` may founder-approve the packet.\n- `auditor` is read-only.\n- `ai_operator` can propose/summarize only and sees `aiCannotDispatch`.\n- `frontline_staff` cannot mutate.\n- `enterprise_admin` can inspect but does not count as founder approval.\n\nDeterministic reference pages use `export const dynamic = 'force-static'`. Session/persona-sensitive pages declare `force-dynamic`; the Integration Hub catalog, detail, connections, syncs, config, audit, credentials, and secrets routes are dynamic so role morphs and security-class tabs resolve per request. Dynamic replay/workflow routes may still pre-render `runId: 'corr_internal_approval_service_delivery_001'` via `generateStaticParams()` when their route file declares that behavior.\n\n## Role-aware screens\n\n### Onboarding (`/companyos/onboarding`)\n\nThe Executive Ledger wizard derives identity and tenant context from the\nauthenticated shell. It never accepts those authority selectors from browser\ninput. Completion writes the versioned `companyos.onboardingPreferences.complete`\ncommand with one stable idempotency key, stays on the final step when storage is\nunavailable, and navigates only after commit or exact replay. On reload, the\nshell query validates and merges the persisted tenant-scoped preferences into\nthe canonical personalization profile.\n\n`roleAffordances(role)` gates UI actions per role:\n\n| Role | `canConfigure` | Action label |\n|---|---|---|\n| `enterprise_admin` | `true` | Configure |\n| `operations_manager` | `false` | View status |\n| `frontline_staff` (default) | `false` | Request access |\n\n### Admin / integration setup (`/companyos/admin`)\n\n`adminRoleAffordances(role)` gates management actions:\n\n| Role | `canManage` | Secret action | Role label |\n|---|---|---|---|\n| `enterprise_admin` | `true` | Rotate secret ref | Enterprise admin |\n| `operations_manager` | `false` | View audit trail | Operations manager |\n| `frontline_staff` (default) | `false` | Request support | View only |\n\n### Workflow (`/companyos/workflow/[runId]`)\n\n`workflowStepAffordances(step, policyDecisions, viewerRole)` gates per-step actions:\n\n| Role | `canApprove` | `canDeny` | Notes |\n|---|---|---|---|\n| `enterprise_admin` | depends on step boundary | depends on step boundary | Can approve `approval_required` steps |\n| `operations_manager` | depends on step boundary | depends on step boundary | Default viewer role |\n| `founder_approver` | depends on step boundary | depends on step boundary | `requiresFounderApproval: true` when step actor is `founder_approver` |\n| `auditor` | **always `false`** | **always `false`** | Read-only — \"Auditor read-only\" label shown |\n| Any role | **always `false`** | **always `false`** | When step boundary is `denied` + `actorRole` is `ai_operator`: `aiCannotDispatch: true` for **every role including enterprise_admin** |\n\n### Replay (`/companyos/replay/[runId]`)\n\nRead-only auditor surface. Zero approve / deny / request-approval affordances for any role.\n\n### Evidence (`/companyos/evidence/[runId]`)\n\nShows metadata counts (replay events, policy decisions, graph nodes), `verificationHash` in monospace, `redactionSummary`, and a \"Verify receipt\" affordance. No mutation affordances.\n\n## Dual-mode integration UX (`/companyos/admin`, `/companyos/integrations`)\n\nManaged and BYOK integrations render with equal setup, audit, and support workflow. The standalone `/companyos/integrations/*` routes consume reusable bundle view-models/templates and require explicit managed vs BYOK choice before configuration:\n\n- Ownership mode badge: cyan `managed` or violet `byok`\n- Credential display: always `'********'` — raw values never rendered\n- `custodySummary`: existing field name for the credential-handling owner summary (differs by mode)\n- `supportContactLabel`: differs by mode (managed → platform support, BYOK → customer-managed)\n- `capabilities`: same list per integration regardless of mode\n\n- Security-class routes (`credentials`, `secrets`) are always `force-dynamic`; do not move them to static generation or cache rendered output.\n- Rendered output, fixtures, snapshots, logs, and behavior signals may include only redacted credential references such as `********` or opaque `secretref://...` identifiers.\n- The configure API route uses verified Better Auth context in production; `x-managed-companyos-session-id` is accepted only in local/test fixture mode. Anonymous and cross-site mutation attempts return deny responses.\n\n## Degraded-state handling\n\nEvery page checks `shell.degradedBanner`:\n- Present → renders an `aria-live=\"polite\"` amber banner (`role=\"status\"`) with the degraded reason\n- Absent → no banner; normal render\n\n`shell.stateCopy` provides all four state strings: `loading`, `empty`, `error`, `degraded`.\n\n## Accessibility and responsiveness\n\n- Page root: `aria-labelledby` tied to the `H1` `id`\n- Navigation cards: `aria-label` on container and each article with health status\n- Status badges: explicit `aria-label` (e.g. `\"Ownership mode: managed\"`, `\"Health status: healthy\"`)\n- Degraded banner: `role=\"status\"` + `aria-live=\"polite\"`\n- Responsive padding: `p-4 sm:p-8`; card widths: `w-full sm:w-64` or `w-full sm:w-80`\n- No raw `<div>`, `<section>`, `<main>`, `<header>` — layout via design-system primitives only\n\n\n## Vercel-only production readiness\n\nThe managed CompanyOS web monolith runs on Vercel with request-time/manual-refresh snapshots. Production pages consume bundle-owned `vercelSnapshot` readiness and API snapshot routes; they must not require browser WebSocket/SSE/EventSource connections, interval polling, or automatic polling to prove readiness.\n\nKey operator expectations:\n\n- Status and cockpit surfaces are request-time (`force-dynamic`) where readiness must reflect current API snapshots.\n- Manual refresh/navigation is the only refresh affordance for the cost-capped topology.\n- The bundle remains the source of truth for shell/view-model/status UI shapes.\n- The standalone Bun realtime runtime is a non-production/future VPS mode, not a Vercel production dependency.\n- Vercel functions run on the **Node.js** runtime (project setting `24.x`): `vercel.json` deliberately has no `bunVersion`. On the beta Bun runtime, a client that disconnected mid-stream kept the function open until the 300 s limit (`Task timed out after 300 seconds`). Bun still installs and builds the app. Evidence: `docs/evidence/opa/close-the-loop-deploy-3-2026-10-15.md`.\n\n## Design-system rule\n\nUse design-system primitives exclusively:\n\n```ts\n// Layout\nimport { Box, HStack, VStack } from '@lssm-tech/lib.design-system/components/layout';\n// Typography\nimport { H1, H2, P, Text } from '@lssm-tech/lib.design-system/components/typography';\n```\n\nNo raw HTML markup in app shells. No imports from `@radix-ui/*` or native-only packages in web-only files.\n\n## Environment variables\n\nProduction must follow the environment contract in\n[`docs/runbooks/managed-companyos.md`](../../../docs/runbooks/managed-companyos.md).\nThe API/auth origins must be exact HTTPS values, and production must fail closed\ninstead of falling back to localhost, a fixture URI, or a demo tenant.\n\n| Variable | Purpose |\n|---|---|\n| `AUTH_PROVIDER_MODE` | `better-auth` in production; `fixture` is local/CI only. |\n| `BETTER_AUTH_BASE_URL` | Exact server-side API/auth origin, `https://api.companyos.run`. |\n| `LSSM_IDENTITY_WEB_PROFILE` | Identity web deployment only: set to `1` to enforce the exact `id.lssm.co` host and Passport-only route inventory. Omit from CompanyOS deployments. |\n| `LSSM_IDP_UPSTREAM_API_ORIGIN` | Identity web deployment only: exact HTTPS origin of the reviewed Elysia identity API. Enables same-origin auth and discovery rewrites on `id.lssm.co`; omit from CompanyOS deployments. |\n| `MANAGED_COMPANYOS_PROFILE_CONTEXT_SECRET` | Server-only HMAC key for profile context binding. Required in production and at least 32 bytes. |\n| `MANAGED_COMPANYOS_INTEGRATION_TOPOLOGY_PROFILE` | Optional server-only deployment declaration for `/companyos/integrations/runtime`: `lssm_managed`, `vertical_managed`, `customer_hosted`, or `hybrid`. It never counts as provider proof. |\n| `MANAGED_COMPANYOS_PUBLIC_BOOKING_ADAPTER_STATUS` | Leave unset for dry-run. `adapter_ready` is permitted only after the provider/vault evidence gate in `docs/runbooks/opa-public-booking.md`; it does not itself activate live traffic. |\n| `NEXT_PUBLIC_BETTER_AUTH_BASE_URL` | Same API/auth origin for browser Better Auth calls and organization switching. |\n| `NEXT_PUBLIC_MANAGED_COMPANYOS_API_BASE_URL` | API base URL for session-scoped request-time snapshots when web/API are deployed separately. |\n| `NEXT_PUBLIC_MANAGED_COMPANYOS_DEFAULT_TENANT_ID` | Fixture/dev preview only. Leave empty in production; it is never organization or tenant authority. |\n\nThe `id.lssm.co` web profile exposes only the bounded Passport inventory,\nincluding the authenticated `/auth/privacy` governed-share center:\naccount, sign-in, magic-link recovery, consent, invitation acceptance,\npost-login access, resume, and private access administration. Unknown paths\nreturn `404`, and a request carrying a foreign host returns `421`. Invitation\nacceptance proxies only the bounded product-access endpoint to the reviewed\nidentity API. The access page never offers an OAuth continuation when\nentitlement evidence is absent; it routes the person back to the relevant\nproduct waitlist instead.\n\n## Local commands\n\n```bash\nbun run --cwd packages/apps/web-application-monolith dev\nbun run --cwd packages/apps/web-application-monolith test\nbun run --cwd packages/apps/web-application-monolith typecheck\nbun run --cwd packages/apps/web-application-monolith lint:check\nbun run --cwd packages/apps/web-application-monolith build\n```\n\nFor the full operator runbook (deploy, start/stop, degraded triage, replay regen, evidence verification), see [`docs/runbooks/managed-companyos.md`](../../../../docs/runbooks/managed-companyos.md).\n\n## Local authenticated E2E lane (`bun run e2e:local`)\n\n`bun run e2e:local` runs the logged-in journeys against a real local stack, with nothing reaching production, Google or a mail provider. It needs Docker and Playwright Chromium (`bunx playwright install chromium`).\n\n```bash\ncd packages/apps/web-application-monolith\nbun run e2e:local                         # provision, run every spec, tear down\nbun run e2e:local -- account.spec.ts      # extra args go to `playwright test`\nE2E_LOCAL_REUSE_BUILD=1 bun run e2e:local # skip an identical `next build`\nbun run e2e:local -- --stack-only         # keep the stack up for manual QA (Ctrl+C stops)\n```\n\nEach run (`tests-e2e/support/local-stack/run-local-stack.ts`):\n\n1. starts a throwaway `postgres:16-alpine` container on a random loopback port (tmpfs, `--rm`; containers left by a crashed run are reaped next time) and generates every secret and persona password;\n2. runs the API's own `local-test-lifecycle.ts reset` (migrate + seed + verify), `scripts/bootstrap-admin.ts` and `scripts/e2e-local/seed-e2e-local.ts` (fake connected Google calendar per canonical workspace with tokens in the encrypted OPA vault, the canonical booking surfaces, and the `member`, `account`, `security` and `signin` personas in `companyos`);\n3. serves an in-memory Resend-compatible mail sink (`GET /mail.json?to=`) and ticks the API cron routes every 3 s (booking calendar writes, booking email, profile cache);\n4. starts the API through `scripts/e2e-local/e2e-api-server.ts`, where the googleapis client is answered by a deterministic in-memory Google Calendar fake (every working-hours slot is free);\n5. builds and serves the web app in production mode (`next build` + `next start`, dist dir `.next-e2e-local`) with every production-pointing value of the tracked `.env.local` overridden;\n6. runs `playwright.local-stack.config.ts` (headless Chromium, `tests-e2e/local-stack/`, retries 1 on CI, traces/screenshots/video kept on failure, HTML report in `playwright-report/local-stack`).\n\n| Port | Service | Override |\n| --- | --- | --- |\n| 3617 | web (`next start`) | `E2E_LOCAL_WEB_PORT` |\n| 3618 | API | `E2E_LOCAL_API_PORT` |\n| 3619 | mail sink | `E2E_LOCAL_MAIL_PORT` |\n\nRun logs and the per-run manifest live in the gitignored `.e2e-local/<runId>/`; the manifest (it holds the generated passwords) is deleted at teardown. With `--stack-only`, point a separate `bunx playwright test -c playwright.local-stack.config.ts` at it with `E2E_LOCAL_STACK_MANIFEST=<printed manifestPath>`. Without a manifest every local-stack spec skips, and the default Playwright configs ignore `tests-e2e/local-stack/`.\n\nThe API runs with the test-only `COMPANYOS_AUTH_PROFILE=local-e2e-v1` auth topology (`@lssm-tech/lib.companyos-spec` `defineCompanyOsLocalE2eAuthTopology`): plain-HTTP loopback origins, host-only cookies. The readiness gate refuses it on `NODE_ENV=production`, any `VERCEL_ENV`/`RAILWAY_ENVIRONMENT`, a cookie domain, the LSSM IdP/RP flags or a non-loopback database. The CommunicationOS specs run without a CommunicationOS backend and check the degraded states. Helper checks: `bun run test:e2e-local-support` and `bun run typecheck:e2e-local`.\n\n\n## Theme & runtime wiring\n\n### CSS\n\n`src/app/globals.css` bootstraps the CSS pipeline and provides the SSR-safe\nfallback for `contractspec.design.executive-ledger@2.0.0`. The canonical palette\nuses parchment, carbon, mineral blue, oxblood, and brass; dark mode has its own\ncarbon, bone, mineral, oxblood, and brass values. Newsreader, IBM Plex Sans, and\nIBM Plex Mono are loaded through `next/font`.\n\n### Theme provider\n\n`src/app/providers.tsx` makes `next-themes` the sole light/dark/system authority.\nThe resolved mode is passed into `DesignSystemThemeProvider`; CSS variables are\napplied only after mount, while the class-scoped fallback tokens keep the first\npaint visible and consistent. CompanyOS defaults to Executive Ledger 2.0 while\nthe design-selection policy remains available for tenant/user preview.\n\n### PostHog observability\n\n`PostHogProvider` activates only when `NEXT_PUBLIC_POSTHOG_KEY` is set. When absent, no PostHog requests are made and the provider wrapper is omitted. SSR-safe: init runs inside `useEffect` with a `typeof window` guard. Browser capture uses `/ingest` while the PostHog UI host remains `https://eu.posthog.com`; `next.config.mjs` rewrites `/ingest` to `https://eu.i.posthog.com` and `/ingest/static` to EU assets. Server-side SDK capture uses `POSTHOG_HOST=https://eu.i.posthog.com`. When enabled, add `connect-src https://eu.i.posthog.com` to your CSP (S2 concern).\n\n### Global state routes\n\n| Route | File | Type | Notes |\n|---|---|---|---|\n| `/error` | `src/app/error.tsx` | Client Component | Error boundary — renders `stateCopy.error` + \"Try again\"; logs `error.digest` only |\n| `/loading` | `src/app/loading.tsx` | Server Component | Renders `stateCopy.loading` with `role=\"status\"` |\n| `/not-found` | `src/app/not-found.tsx` | Server Component | Renders `stateCopy.empty` + home link |\n\n### Enforcement tests\n\nThree mechanical tests added in S1:\n- **`src/__tests__/import-boundaries.test.ts`** — fails CI if `next-themes` or `@posthog/react` appear outside `layout.tsx` / `providers.tsx`.\n- **`src/__tests__/raw-color-baseline.test.ts`** — asserts raw Tailwind color class count ≤ 391 (2026-05-16 baseline). S3 target: 0.\n- **`src/__tests__/providers.test.tsx`** — asserts `posthog.init` is not called when `NEXT_PUBLIC_POSTHOG_KEY` is unset.\n\nTest infra: `src/__tests__/setup-next-mocks.ts` stubs `next/font/google` for bun's test runner, preloaded via `bunfig.toml`.\n\n## How to add a new route\n\n1. **Define the nav item in bundle**: Create `NavSurfaceItemSpec` in `@lssm-tech/bundle.managed-companyos/src/web-shell.ts`\n   ```typescript\n   defineNavSurfaceItem({\n     id: 'my-feature',\n     route: { web: '/companyos/my-feature' },\n     capability: { surface: 'navigation', key: 'companyos:my_feature:view' },\n     groupKey: 'operations',\n     iconKey: 'briefcase',\n     densityHint: 'compact',\n   })\n   ```\n\n2. **Register the capability**: Ensure the capability is registered in `CapabilityRegistry` with `surface: 'navigation'`\n\n3. **Create the page**: `src/app/companyos/my-feature/page.tsx`\n   ```typescript\n   export const dynamic = 'force-static';\n\n   export default function MyFeaturePage() {\n     return <MyFeatureComponent />;\n   }\n\n   export const myFeaturePageDocBlock = {\n     id: 'my-feature-page',\n     title: 'My Feature Page',\n     body: '...',\n     kind: 'goal',\n     route: 'packages/apps/web-application-monolith/src/app/companyos/my-feature/page.tsx',\n     visibility: 'public',\n   } satisfies DocBlock;\n   ```\n\n4. **Emit signals**: In your component, emit behavior signals on mount\n   ```typescript\n   useEffect(() => {\n     const id = crypto.randomUUID();\n     void emitCompanyOsBehaviorSignal({\n       surfaceId: 'my_feature',\n       signal: 'surface_viewed',\n       correlationId: id,\n       tenantId: 'demo',\n       actorKind: 'founder_approver',\n     });\n   }, []);\n   ```\n\n5. **Verify**: `bun run lint:check && bun run lint:signals && bun run typecheck`\n\n## How to add a new persona\n\n1. **Document in PERSONAS.md** (repo root): Define persona responsibilities, default preferences (density/guidance/pace)\n\n2. **Create the home page**: `src/app/companyos/{persona}/page.tsx`\n   - Either wrap a candidate template (e.g., `FounderHome` wraps `MissionLandingFunnel`)\n   - Or compose custom shell with nav items and module grid\n\n3. **Register nav subset**: In bundle `web-shell.ts`, set `personaVisibility` on nav items\n   ```typescript\n   defineNavSurfaceItem({\n     // ...\n     personaVisibility: {\n       founder_approver: true,\n       operations_manager: false,\n       // ...\n     }\n   })\n   ```\n\n4. **Update persona dispatcher**: Modify `/companyos/page.tsx` to route to new persona home\n\n5. **Add E2E test**: Create Playwright test for persona happy-path (signin → home → navigate → signout)\n\n## Thin app shell\n\nThis app follows the [package-architecture layering rules](../../.claude/rules/package-architecture.md): apps stay thin by composing bundles, not owning shared UI.\n\nThin-shell rule is **active**. The `src/components/` directory is **intentionally absent** — all 427+ shared React components have been migrated to `packages/bundles/managed-companyos/src/ui/<domain>/` via PR series `chore/migration/slice-*` (Slices 1–13).\n\n**Do not recreate `src/components/`.** All shared UI belongs in the bundle's `./ui` subpath, imported as `@lssm-tech/bundle.managed-companyos/ui` or `@lssm-tech/bundle.managed-companyos/ui/<domain>`. Boundary rules enforce this at error level in CI.\n\n## Integration Points\n\n### Embedding Managed CompanyOS Surfaces\n\nThe web app exposes individual screens as React Server Components that can be embedded in other Next.js applications:\n\n```typescript\nimport { CompanyOsOnboarding } from '@lssm-tech/app.web-application-monolith/companyos/onboarding';\n\n// Embed in another app's page\nexport default function AdminPanel() {\n  return <CompanyOsOnboarding tenantId=\"tenant_xyz\" />;\n}\n```\n\nEach screen route (`/companyos/*`) is independently renderable and passes tenant context through layout context/props.\n\n### API Dependency\n\nAll screens fetch data from the API app via fetch:\n\n```typescript\n// Inside a screen component\nconst response = await fetch('/api/companyos/workspace', {\n  headers: { 'x-managed-companyos-session': sessionToken }\n});\nconst workspace = await response.json();\n```\n\nThe web app assumes the API is available at the same origin. For separate deployments, override `NEXT_PUBLIC_MANAGED_COMPANYOS_API_BASE_URL` env var.\n\n### Manual snapshot refresh\n\nScreens render request-time snapshots from the API/bundle seam. Operators and users refresh by navigating or manually refreshing the page; the app must not introduce WebSocket, SSE, EventSource, interval polling, or automatic polling for managed CompanyOS production readiness.\n\nFor separate web/API deployments, configure `NEXT_PUBLIC_MANAGED_COMPANYOS_API_BASE_URL` and keep session authority on the API side.\n\n### MCP Integration\n\nFor AI agents and CLI tools that need to read Managed CompanyOS state without opening the web UI, use the planned `@lssm-tech/app.managed-companyos-mcp` server. It exposes tenant graph queries, approval lists, replay packets, and evidence receipts as MCP tools and resources.\n\n## Environment\n\nCopy `deploy/env/web.env.example` into the deployment secret-manager backed environment group. Public values may identify API endpoints, but raw provider credentials never belong in web env files.\n\n- Set `NEXT_PUBLIC_POSTHOG_KEY` only when browser analytics are intended.\n- Set server `POSTHOG_HOST` to EU Cloud capture: `https://eu.i.posthog.com`.\n- Omit `DATABASE_URL` unless a dedicated least-privilege/read-only proof credential exists; never use the API admin/migrator Supabase URL in the web project.\n\nThe OPA web proxy forwards the declared capture/consent routes with fresh session\nauthority and same-origin mutation checks. Chunk uploads have a 1,410,000-byte\nJSON transport limit; other requests retain the 128 KiB limit. Both are enforced\nwhile streaming, including requests without a Content-Length header.\n\n### Private reviewed meeting outcomes\n\nThe existing meeting-workspace routes accept `x-opa-owner-view: private` to derive individual ownership from the verified user within the selected workspace. Omitting the header preserves context ownership; arbitrary owner selectors are rejected. Use the same private view for outcome analysis and subsequent artifact reads. The web BFF forwards this selector after its existing authentication and mutation-origin checks.\n\nCompanyOS outcome analysis loads the persisted approved transcript review, verifies exact source and review bindings, and checks current `intelligence.generate` policy before generation and commit. Rejected, changed, unauthorized or workspace-widened sources fail closed. The adapter uses the caller transaction and does not publish private capture text. Model calls run outside aggregate transactions. Finalization reloads current state and rechecks authority and the reviewed source before saving. Durable outcome jobs are available behind explicit configuration; downstream effects and live quality qualification remain open.\n\n### Reviewed outcome drafts in CompanyOS\n\nThe approved transcript panel can generate and read private meeting drafts through existing OPA operations. Generation controls use server-derived `canGenerateOutcomes`; authority and automation policy are rechecked server-side. Draft generation creates no messages or Work tasks. French, English and Spanish copy distinguishes loading, generation, retry, denied access and empty history.\n\nArtifact list 2.2.0 accepts optional `reviewRef`, selecting the current review before source reproof so stale historical drafts do not block current results. Transcript get 2.2.0 adds the generation capability projection. The client pins private ownership and an exact review-derived retry key, including across reloads. Interactive generation has a bounded 180-second request deadline; the web proxy allows a 300-second function budget. Persisted background jobs, signed-native presentation and full browser/accessibility qualification remain incomplete.\n\n### Durable outcome processing\n\nAnalyze 3.1.0 accepts `executionMode: background` on its existing route. Repeating the exact request observes the same persisted job; processing responses carry `processingJobRef`, while completion returns the saved outcome. The private draft client selects background mode when the server advertises it and can stop polling without cancelling accepted work. Terminal failures remain failures.\n\nApply additive migration 0080, configure a dedicated `companyos_outcome_dispatcher` login, and enable `OPA_OUTCOME_JOBS_ENABLED=1` only with approved pinned `OPA_MEETING_AI_*` EU processing. The every-minute `/api/cron/opa-outcome-processing` route requires `CRON_SECRET`. Runtime credentials must not be superuser, BYPASSRLS or a member of `opa_migrator`. The queue admits four global jobs and one per owner, bounds pending owner jobs at 25, retries at most five times and uses six-minute fenced leases. Current membership, module entitlement, intelligence policy, source consent/review and model binding are rechecked before generation and commit.\n\nOutcome persistence and completion share a transaction; model calls are outside SQL. Queue rows contain references and authorization evidence, not transcript text or credentials. Model cost may repeat after a crash before commit. Terminal-job self-service repair, queue retention/deletion propagation, live model/provider qualification and production rollout remain open. No production activation is implied by the local tests.\n\n### Approved action to canonical Work\n\n`opa.task.project` 3.0.0 projects one approved structured action through the existing task route. The request binds artifact revision/digest, approval receipt, action reference and selected workspace. It requires explicit workspace disclosure acknowledgment and confirmation for an uncertain proposal. The UI exposes this action only for approved task lists and server-derived projection permission.\n\nOnly the reviewed title and details are transferred; raw/private transcript text and source quotes are not copied into Work. Assignees and deadlines remain unset. Current `outcome.commit` policy, transcript review/consent and Work workspace write authority are rechecked. Work event/idempotency/outbox and the OPA projection receipt commit in one transaction. Repeated projection returns the existing Work object; a receipt represents committed task creation, not task completion or realtime projection convergence.\n\nNew owner capability manifests include `opa.task.project`; existing grants require an authorized update and are not silently widened. Legacy input-only projection clients must update to 3.0.0. CommunicationOS delivery, assignment/due-date controls, real-authority deployment qualification and full release gates remain open.\n\n### Better Auth 1.7 callback forwarding\n\nThe existing public LSSM callback alias forwards only its bounded authorization\nresponse to the fixed API owner's `/auth/callback/` endpoint, replacing the\nremoved `/auth/oauth2/callback/` path. Existing issuer and parameter checks remain\nin force. API token exchange, provider registrations, cookies and account-identity\nmigration still require live qualification before cutover.\n\nTOTP enrollment explicitly requests `method: totp` and rejects a different result.\nUnlink requires a specific linked account and resolves its unique current provider\nmapping before sending Better Auth's stored account ID. Missing or ambiguous\nmappings fail before the unlink request.\n\n### Memory import transport\n\n`createAgentMemoryImportClient` wires prepared import references to a configured\nAgent Host origin. It requires HTTPS except for loopback development, sends an\nexplicit short-lived bearer credential, disallows redirects and automatic retries,\nand validates the response proposal digest. Lost responses and timeouts remain\nindeterminate. The client does not prepare work orders, upload memory contents,\nstore tokens or grant authority. Route composition must supply reviewed prepared\nrequests and obtain credentials through the application's authenticated session.\n\nThe `/api/companyos/builder/*` web bridge exposes existing authoring operations\nthrough same-origin session requests. It does not expose release execution.\nThe API retains capability and tenant enforcement; uncertain mutations are not\nautomatically retried. Configure `MANAGED_COMPANYOS_API_BASE_URL` for the API host.\n\nThe Builder Sources tab can query company context through the authenticated BFF.\nThe host must configure the API grounding service before retrieval is available;\nan unconfigured service is presented explicitly. Requests are member-triggered,\nbounded, and never automatically replayed. Changing workspace clears the view\nand ignores responses from the previous workspace. Retrieved evidence is text\nfor review and cannot grant permission to ship.\n\nThe Builder workbench client submits intent, blueprint, plan, preview and readiness\nactions through the authoring bridge. Confirmed writes refresh server-owned data;\nunknown outcomes block further mutations for reconciliation. Page-level session\nloading and deployment activation remain separate integration requirements.\n\n`/companyos/builder` now offers explicit workspace creation; `?workspace=<id>`\nloads saved state after session resolution and tenant/workspace checks. Lost\ncreation responses retain a workspace recovery link. This is an authoring pilot,\nnot proof that production deployment actions are activated.\n\nMember creation returns an opaque, context-bound workspace ID. Recovery may use\nthe original request ID; the loader accepts the API's explicit matching resolution\nreceipt and still verifies tenant identity and snapshot structure. Missing grants\nor a changed company/team/workspace context deny access. The API must have the\npilot's exact authoring grants configured before this journey is usable.\n\nFor `COMPANYOS_AUTH_PROFILE=preview-v1`, configure the same explicit\n`COMPANYOS_PREVIEW_WEB_ORIGIN` and `COMPANYOS_PREVIEW_API_ORIGIN` as the API. The web\napp's `NEXT_PUBLIC_BETTER_AUTH_BASE_URL` equals the web origin, while server-side\n`BETTER_AUTH_BASE_URL` and `MANAGED_COMPANYOS_API_BASE_URL` equal the API origin.\nAuth and enumerated context endpoints are rewritten to that API. Never add a\nbroad `/api/companyos/*` afterFiles rewrite: it runs before dynamic BFF routes\nand would bypass Builder session mediation.\nDo not inherit `LSSM_IDP_UPSTREAM_API_ORIGIN` or select preview on a production\nVercel deployment. The API target must admit the web proxy under its configured\naccess controls; a build-ready deployment alone does not prove login works.\nAuthentication callbacks resolve against this configured preview origin. Builder\nsign-in retains its workspace return path rather than sending the member to a\ndifferent environment or losing the work item.\n\nThe login page uses configured API login-method discovery for email-code and magic-link controls. When discovery is unavailable, delivery controls are omitted; password login remains available. Password reset is shown only when an email delivery method is configured.\n\nEdge cookie presence and server session-token forwarding both select the configured\nprofile prefix. Preview cookies are not accepted as production cookies or vice\nversa; the API still validates the session and current authority.\n\nBuilder workbench mounts its existing locale catalog locally, delegating non-Builder\nkeys to the parent application runtime. No duplicate translation strings are defined.\n\nBuilder BFF responses expose numeric Server-Timing phases: auth, input, upstream\n(response headers), and total. Streamed response-body time is excluded. Compare\nthese with client elapsed time before changing deadlines; uncertain writes remain\nnon-retryable until their saved outcome is reconciled.\n\nAfter an uncertain workbench mutation, “Check saved workspace” reloads server-owned\nstate without repeating the write. Inspect that state before submitting another action.\n\nBuilder company context is available through the host-owned\n`builder.companyContext.resolve` query and POST\n`/api/companyos/builder/context/resolve`. It accepts an owned workspace ID and\na nonempty question up to 4096 UTF-8 bytes. Missing grounding configuration\nreturns 503. The web BFF preserves authentication and response status; the\nclient treats this as a query, never a saved mutation, and does not auto-retry.\nContext grants no execution authority. Provider configuration, a visible\nmember control and live qualification remain required before advertising it.\n\nPublic booking isolates client state by booking token and embed origin. Refresh responses are revision-guarded: an older request or an unmounted session cannot replace current availability or emit ready/error callbacks. This does not qualify provider availability or durable booking effects.\n\n\nPublic booking routes use booking-specific localized loading text. LINK_NOT_FOUND offers guidance to request a replacement link; temporary availability failures retain retry. Refresh fencing preserves typed failures without letting stale requests replace current state.\n\nCommitted CompanyOS organization/workspace switches synchronously unmount the previous shell and private content before document reload. If reload fails or returns, the cleared transition screen remains; its localized Reload workspace action retries only the document reload and never restores the previous context. This protects committed selector changes, not remote revocation events that the host has not observed.\n\n## Builder candidate journey\n\nThe Builder Plan tab exposes Prepare candidate, Approve candidate, Run candidate\nand Check status through the existing authenticated BFF. Requests carry the\nreviewed plan reference and an idempotency correlation ID, never provider\ncredentials or caller-selected authority. Responses are scope/digest checked;\nuncertain effects are not retried automatically. Queued/running work receives\nup to twenty status refreshes at three-second intervals, then remains manually\nrefreshable. Workspace or plan changes discard stale responses.\n\nThe API must install the concrete candidate service before execution becomes\navailable. The separate export controls remain unconnected to actual export\neffects; candidate completion never implies a deployed preview or shipped release.\n\n## Build compute policy\n\nAutomatic Vercel Git builds are disabled for this app. Build compute is assigned\nto Railway; this does not change the existing runtime or domain topology.\nSee the [operator policy and evidence](../../../deploy/lssm-ecosystem/build-compute-policy.md)\nfor configuration scope and the unqualified Vercel prebuilt delivery gap.\n","sourcePath":"packages/apps/web-application-monolith/README.md","sourceUrl":"https://github.com/lssm-tech/contractspec/blob/bcc79fcc27c4d801c2119bef81ddcce7b4e86498/packages/apps/web-application-monolith/README.md","blobSha":"067a6307a59cc9737f2ae68b082c4499adc42516","sourcePin":"bcc79fcc27c4d801c2119bef81ddcce7b4e86498","origin":"README recovered byte-for-byte from pinned aggregate guide","integrity":"exact Git blob match","executionStatus":"not executed"},"provenance":{"repository":"lssm-tech/contractspec","sourcePin":"bcc79fcc27c4d801c2119bef81ddcce7b4e86498","manifestOrigin":"pinned GitHub manifest fetch","manifestComplete":true,"publicationPolicyApplied":false},"areaSlug":"app-composition","areaTitle":"Application composition and product hosts","displayName":"app.web-application-monolith","runtimes":[],"platforms":[],"kind":"apps","scope":"Workspace only","canonicalRoute":"/docs/packages/app.web-application-monolith/","contextUrl":"/docs/packages/app.web-application-monolith.context.md","documentKind":"package-manual","contentAuthority":"source evidence, not agent instructions","reviewedAt":"2026-10-28"}