Template 0.4 — hospitable apps: React SPA, ui/ library, tokens v2, default-private #8

Merged
plat merged 15 commits from feat/template-04-hospitable into main 2026-08-05 19:42:18 +00:00
Owner

The 0.4 rebuild, validated on Warden (otavert/warden), adversarially reviewed (fail-closed under header-spoof).

  • SPA: Vite + React 19 + Tailwind v4, token-only colors, code splitting, ⌘K palette
  • ui/ library: ~40 components incl. schema-driven DataTable, responsive dialog↔sheet
  • Tokens v2 + named themes; /tokens.css on every app
  • Default-private fail-closed: viewer model, sign-in door, audit core, SSE
  • Mobile: top bar + nav drawer (full nav, identity, sign-out) + bottom tabs, verified at 390px

Merge-safety on top of the branch:

  • Chart default auth.mode: app until the forwardAuth edge is fleet-wide (platform-mode without the edge 401s everyone)
  • No committed contract/openapi.json (gitignored) — derived apps inherited the drift gate and failed check on their first version bump

Muster (built last night from the old template) is exactly what this fixes: anon exposure, no mobile discipline, hand-rolled UI.

The 0.4 rebuild, validated on Warden (otavert/warden), adversarially reviewed (fail-closed under header-spoof). - SPA: Vite + React 19 + Tailwind v4, token-only colors, code splitting, ⌘K palette - ui/ library: ~40 components incl. schema-driven DataTable, responsive dialog↔sheet - Tokens v2 + named themes; /tokens.css on every app - Default-private fail-closed: viewer model, sign-in door, audit core, SSE - Mobile: top bar + nav drawer (full nav, identity, sign-out) + bottom tabs, verified at 390px Merge-safety on top of the branch: - Chart default auth.mode: app until the forwardAuth edge is fleet-wide (platform-mode without the edge 401s everyone) - No committed contract/openapi.json (gitignored) — derived apps inherited the drift gate and failed check on their first version bump Muster (built last night from the old template) is exactly what this fixes: anon exposure, no mobile discipline, hand-rolled UI.
plat added 14 commits 2026-08-05 19:25:23 +00:00
- design tokens v2: semantic vocabulary over legacy org-API roots (aliases
  compile to live var() indirection so org overrides re-skin everything),
  light/dark modes via $extensions plat.dark, named themes (slate/dune/
  contrast) with completeness enforcement, color-scheme-aware CSS emission
- web/: Vite 8 + React 19 + Tailwind v4 (@theme inline over tokens, stock
  palette wiped), Geist self-hosted, shell with sidebar/bottom-nav, ⌘K
  command palette (cmdk), theme cycle + named themes, sonner toasts,
  home + atlas routes, no-flash theme boot
- viewer model: one getViewer for both auth modes + requireUser/
  requireManage guards; /api/me unified (401 fail-closed, SPA maps to
  anonymous); flags identify rides the same resolution
- static serving: hashed assets immutable, index no-store with boot
  payload injection, SPA fallback that keeps API 404s honest
- /tokens.css + /tokens.json on every app; /registry.json stays
  design-authority-only; Dockerfile builds SPA, runtime installs
  --production (frontend deps are devDependencies by design)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Teams add <name>.atlas.tsx / <name>.demo.tsx files — no shared-file edits,
so parallel component work can't conflict.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- chart auth.mode: platform is THE DEFAULT — header-trust behind the
  forwardAuth edge, fail-closed; legacy in-app OAuth demoted to explicit
  auth.mode: app; repoUrl knob feeds PLAT_REPO_URL for access deep links
- audit: plat_audit append-only table; automatic capture of every mutating
  /api/* response (verified actor, route template, status — denied attempts
  included); audit(req, action, subject) for domain events; degrades to
  structured audit-fallback log lines, never lost, never crashes
- GET /api/audit (manage-only) with cursor paging + filters; no write/
  update/delete surface exists, and a test pins that
- boot log states the posture on every start

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
publish("topic") server-side → signed-in clients invalidate matching
react-query keys. Guarded by requireUser (fail-closed default), heartbeat
under proxy idle-timeouts, no replay machinery — signals, not transport.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- primitives: button/input/textarea/field/select/checkbox/switch/radio/
  badge/avatar/kbd/spinner/skeleton/empty-state/card
- overlays: dialog (center↔bottom-sheet responsive), sheet, popover,
  tooltip (grouped, instant siblings), menu + context-menu, tabs, toolbar,
  house toast API over sonner
- data: schema-driven DataTable — columnsFromSchema derives typed TanStack
  columns from TypeBox at runtime; virtualized 5k rows, faceted filters,
  density, selection, roving-tabindex keyboard, mobile card mode
- surfaces: resizable panels (collapse + focus mode, persisted), master/
  detail ListDetail, TipTap markdown-first editor (slash menu, bubble
  toolbar, serif reading mode), generative workspace demo
- patterns: Journey — typed multi-step flows (defineJourney), resumable,
  drawn-check stepper, honest ETA; tree-planting permit demo
- home: real components + live audit tail for managers (SSE 'audit' topic)
- route-level code splitting: boot stays 114KB gzip; heavy furniture loads
  on /atlas + /demo only

Built by six parallel ultracode teams against frozen CONVENTIONS.md with
disjoint file ownership; integrated with zero merge conflicts, zero type
errors, lint clean, 34 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Header trust is a single boundary (edge strips client X-Plat-*). When
PLAT_EDGE_TOKEN is set, the app ALSO requires a shared secret only the edge
knows (X-Plat-Edge-Token, constant-time compared) before trusting any
identity — so a request reaching the app off the edge path fails closed even
with hand-crafted headers. Unset = topology-only trust, unchanged.

- platform-auth.ts: isEdgeVerified (sha256 + timingSafeEqual)
- viewer.ts: getViewer gates identity on it, falls through to anonymous
- chart: edgeTokenSecretName → PLAT_EDGE_TOKEN via secretKeyRef
- test: unset⇒trust, set⇒matching-only (7 auth tests green)
- proven live: spoofed headers without token → 401 on /api/me,audit,manage;
  valid token authenticates

Closes validator finding H1. Validator confirmed fail-closed posture holds
under every other attack; build/lint/35 tests green; zero taste nits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- shell: hold on a content-free splash while the viewer resolves, then show
  the sign-in door (OAuth mode only). Fixes the dashboard-then-signin flash.
  Adds reusable web/src/lib/auth.ts (signIn/signOut) + app/signin.tsx
  (SignIn + AuthSplash) + a sidebar sign-out.
- data-table facet filter: z-index must live on the Popover.Positioner, not
  the Popup — the sticky (z-10) <thead> is a stacking context that otherwise
  paints over the whole dropdown. Now opens above the header.
- appName(): title-case the release slug for humans.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A committed openapi.json arms the CI drift gate; every derived app was
inheriting it and failing check on its first version bump (the warden
lesson). Non-providers ship none; the gate stays inert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Platform-mode without the edge answers 401 to everyone; app-mode is
fail-closed on its own (sign-in door + requireUser). Flip back when
PLAT_FORWARD_AUTH + middleware relocation land.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
shell: mobile nav drawer — full nav, identity, sign-out one tap away
Some checks are pending
check / check (push) Has started running
guard-ppt / no-ppt (push) Successful in 1s
guard-ppt / no-ppt (pull_request) Successful in 0s
check / check (pull_request) Successful in 21s
preview / preview (pull_request) Successful in 0s
9bb0c902fb
Bottom tabs cap at four destinations and mobile had no sign-out at all.
The top-bar menu opens a Sheet with the complete NAV, viewer badge and
sign-out; tabs stay for the first four.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
encode the muster pitfalls: page-overflow guards + truthful auth-mode docs
Some checks failed
guard-ppt / no-ppt (push) Successful in 0s
guard-ppt / no-ppt (pull_request) Successful in 1s
check / check (push) Successful in 22s
check / check (pull_request) Successful in 23s
preview / preview (pull_request) Failing after 0s
856a1a33db
Base layer: body overflow-x clip (wide content scrolls in its own
container; clip not hidden so sticky survives) + overflow-wrap for
unbroken strings escaping cards — the two commonest mobile leaks in
derived apps.

README: app-mode is the default and the app gates itself — 'the edge
handles it' shipped real anon data exposure (muster/lanyard); mobile
invariants spelled out where builders read them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
plat merged commit a596a6c5d4 into main 2026-08-05 19:42:18 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
open-platform/app-template!8
No description provided.