PulseCheck OS: Mission Control + /graph 3D viewers + autonomy/realtime#56
Open
PulseCheckAI wants to merge 22 commits into
Open
PulseCheck OS: Mission Control + /graph 3D viewers + autonomy/realtime#56PulseCheckAI wants to merge 22 commits into
PulseCheckAI wants to merge 22 commits into
Conversation
Theme:
- styles.css: swap accent palette to pulse-orange (#FF6B35 gradient through
#E63946 red end), primary palette mapped to mc-bg-* surface tones
(pure-black through near-white in .dark mode), Bricolage Grotesque + Inter
+ JetBrains Mono fonts. Add --pulse-{red,orange,amber,yellow} brand vars.
- Codebase-wide token swap (78 .tsx + 4 .ts/.css): neutral-* -> primary-*,
orange-* -> accent-*. Semantic colors (red, emerald, yellow, blue, amber)
untouched.
SSR hydration crash fix ("<AwaitInner> setState undefined"):
- package.json: pin @tanstack/react-router exact 1.166.7 (was ^1.166.7,
resolved to 1.169.2 and shipped its own router-core, causing state-shape
drift). Add npm overrides forcing single router-core@1.166.7 + store@0.9.1
everywhere. Same exact-pinning for react-router-devtools, ssr-query,
router-plugin.
- src/routes/__root.tsx: ssr: false root-wide (pure SPA mode while outsourc-e#53
upstream hydration crash is unresolved). Add manual rehydrate calls
for the 4 persist'd zustand stores in the post-mount useEffect.
- src/stores/{workspace,terminal-panel,task,mission}-store.ts: add
skipHydration: true to persist config (prevents SSR localStorage access).
- src/routes/index.tsx: simplify to static redirect to /dashboard (was
doing client-only conditional redirect that SSR couldn't match).
Gateway RPC drift (OpenClaw 2026.5.7):
- src/routes/api/gateway/nodes.ts: nodes.list -> node.list (renamed upstream).
- src/routes/api/session-status.ts: drop sessionKey param + non-existent
session.status/sessions.status fallbacks; current gateway only accepts
bare sessions.usage.
Dev ergonomics:
- src/routes/api/gateway/agents.ts: add CORS + dev-mode auth bypass on GET
so the founder dashboard at file:///.../os/dashboard/index.html can fetch
this endpoint as part of its ClawSuite-bridge feature.
- package.json scripts: vite dev --port 3010 --strictPort, drop the
Windows-incompatible NODE_OPTIONS="..." prefix (cmd.exe parse error).
… + navy theme
Screen + components:
- New MissionControlScreen at /dashboard (replaces old gateway-backed DashboardScreen)
with 4-KPI row, Action Required panel (stale >24h + off-track + recent failures),
30-day activity tracker, top-agents/top-categories bar lists, recent activity feed.
Reads live command_center.todos + command_center.agent_logs via Supabase REST.
Health-first layout per dashboard-builder skill; thresholds drive deltaTone
(success rate green ≥95%, amber 80-95%, red <80%).
- 9 vendored Mission Control components (src/components/mission-control/):
KpiCard, SparkArea, StatusDot, BadgeDelta, Tracker, BarList, Callout,
DonutGauge, PulseLogo.
- Deleted unused screens/dashboard/dashboard-screen.tsx (no longer imported).
- Widget shell pulse-gradient top bar (signature Mission Control bar).
Theme tokens (styles.css):
- RICH NAVY primary palette in .dark (oklch h=246, chroma 0.10-0.12) —
unmistakably blue, premium against pulse-orange.
- Self-hosted variable fonts via @fontsource-variable: Bricolage Grotesque,
Inter, JetBrains Mono. Family names match the bundled 'X Variable' files.
- PulseCheck AI Liquid Glass utility classes (glass-panel, glass-card,
glass-tile, glass-pulse, glass-prominent, glass-bar) — backdrop-filter
blur(20px) saturate(180%), color-mix primary, pulse-orange tinted borders,
hover lift, prefers-reduced-motion honored.
- Decorative pulse-* utilities: pulse-text gradient, pulse-border-top,
pulse-glow, pulse-status-dot, pulse-bar.
- Global Mission Control treatment (:where(h1-h4) -> Bricolage, focus rings
pulse-orange, sidebar active gradient bar matching real .bg-accent-500/10
+ .text-accent-500 signature, code/kbd -> JetBrains Mono tabular-nums).
- Film grain overlay on body::before (subtle noise via SVG turbulence).
SMIL -> CSS conversions (DevTools performance advisory):
- icons/clawsuite.tsx cursor blink -> .oc-cursor-blink class
- orchestrator-avatar.tsx orchestrating rings x3 -> oa-opacity-pulse
- Dragon thinking smoke (cy + opacity SMIL) -> oa-smoke-rise translateY
- __root.tsx splash cursor SMIL -> inline <style> + .splashCur class
- Zero <animate> tags remain in src/.
Defensive hardening:
- vite.config.ts: clawsuite-hardening-headers plugin via configureServer —
Permissions-Policy (denies unload, beforeunload, camera, mic, geo, etc.),
Referrer-Policy strict-origin-when-cross-origin, X-Content-Type-Options
nosniff, X-Frame-Options DENY, COOP same-origin-allow-popups.
Deps:
- Added sonner (toast library, common Tremor/shadcn-admin peer).
- Added @fontsource-variable/{bricolage-grotesque,inter,jetbrains-mono}.
…-restart
Root cause: once GatewayClient.shutdown() ran (HMR cleanup, SIGTERM during
SSR boundary, or manual restart), the module-scope singleton stayed in a
permanent "destroyed" state. Every subsequent gatewayRpc/onGatewayEvent
call routed to the corpse and threw "Gateway client is shut down" until a
hard dev-server restart.
Fix is two-layered:
1. getLiveClient() helper auto-replaces the destroyed singleton with a
fresh instance on first use. The public facade (gatewayRpc,
onGatewayEvent, gatewayConnectCheck) now routes through it, so every
one of the 40 importing SSR routes transparently recovers — no manual
click required.
2. /api/gateway-restart no longer calls the removed OpenClaw RPC
gateway.restart. It now: (a) best-effort tries the upstream RPC for
older OpenClaw, (b) ALWAYS runs the local gatewayReconnect() — which
is the actual recovery path for the WS client. Returns 503 with an
actionable hint when the gateway daemon itself is unreachable.
Verified live: POST /api/gateway-restart returns
{ok:true, reconnected:true, upstreamMessage:"unknown method: gateway.restart"}.
Drive-by: tighten CORS_HEADERS_GET type in agents.ts to
Record<string,string> so the conditional spread doesn't widen to a union
with undefined props.
…oster + System Integrations
Mission Control dashboard becomes a fully operational PulseOS Command Center.
Branding
- Rename 74 user-visible "ClawSuite" refs → "PulseOS" (sidebar wordmark with
gradient OS, splash, page titles, onboarding, settings dialog, login,
gateway-setup wizard, connection-error copy, format-session-name).
- "Mission Control" → "Command Center" everywhere user-visible.
- Real PulseCheck wave logo: ship docs/Logos/pulsecheck-logo-dark.{png,webp}
as public/pulsecheck-wave.{png,webp}; pulse-logo component renders the
source PNG cropped via object-fit:cover + object-position:left (1.4:1
aspect captures the full wave, drops the wordmark whitespace).
Tier 1 autonomy loop (new)
- src/server/autonomy-loop.ts: polls command_center.todos, soft-locks via
status=in_progress, maps category→agent (Marketing→jordan, Development→
dev, Personal→alex, Work→main), dispatches via gatewayRpc('agent', {…,
idempotencyKey: randomUUID()}), updates todo + writes agent_logs row.
Opt-in self-start via PULSEOS_AUTONOMY_LOOP_ENABLED=true (default 60s).
HMR-safe via globalThis interval guard.
- src/routes/api/autonomy-tick.ts (POST): manual + cron-driven trigger,
auth + CSRF + rate-limited (30/min/IP).
- src/routes/api/autonomy-status.ts (GET): in-memory state for the panel.
- Mission Control "Autonomy Loop · Tier 1" panel: 4 stat tiles, last-tick
row with picked title + agent + status, Tick now button.
Agent Roster + System Integrations panels
- Agent Roster (5 cards: Alex/Maya/Jordan/Dev/Sam) with per-agent glow
color, role label, live last-task / today-count / model / last-active
from command_center.agent_logs.
- New /api/system-integrations route probes filesystem + gateway for
Codex CLI, Claude Dreams runner, Telegram channel, default-agent
orchestrator; Mission Control renders the 4 honestly (no fabrication).
- Gateway Agents page no longer hardcodes Aurora-Main / Codex /
Memory consolidator / Telegram in FALLBACK_AGENT_REGISTRY — only real
registered agents render.
Theme harmonization (global)
- pulsecheck-navy as the default dark enterprise theme. One-shot localStorage
migration in __root.tsx auto-upgrades ops-dark/premium-dark/sunset-brand
to navy on first boot.
- Add Mission Control Navy to ThemeId union + DARK_THEMES + THEME_SET +
THEMES registry + both Settings pickers.
- Global CSS rule under [data-theme='pulsecheck-navy']:
body bg + --theme-bg = oklch(0.18 0.08 248)
every text-primary-{500..950} → #ffffff
every bg-white / bg-zinc / bg-gray / bg-slate / bg-neutral / bg-stone /
bg-primary-{500..950} → var(--theme-bg) (excludes status bg-red/amber/
yellow/emerald/green/accent so semantic colors keep their tint)
every border-primary-{500..950} + border-white/zinc/gray/etc. →
silver hairline rgba(170,178,195,0.4) (excludes status borders)
- Apple Liquid Glass treatment globally on every rounded-xl/2xl/3xl
bordered surface that isn't a button/input/.glass-*/status-tinted:
backdrop-filter: blur(20px) saturate(180%) + silver hairline +
color-mix navy fill + depth shadow.
- Swap hardcoded dark hex bgs (#0d1117 / #0d0d0d / #0b0f1a / #232b3b /
#fdfdf8) to var(--theme-bg) across agent-swarm, terminal, mobile-prompt,
office-view.
- xterm terminal reads --theme-bg at instantiation.
Per-screen cleanup
- Sweep light-mode semantic palettes (border-emerald-200, bg-amber-100/70,
text-red-700, etc.) → dark-mode tones (border-{c}-500/30, bg-{c}-500/10,
text-{c}-400) across 30+ screen/component files.
- Outer padding normalized to px-6 md:px-8 + pt-6 md:pt-8 across every
main screen.
- Debug Console: compact ActivityEventRow (single-line layout, ~28px from
~80px), gateway-unreachable amber callout with actionable remedy, raw
JSON pre re-tinted to glass-translucent, standardized button heights
(h-7 px-2 text-xs tabular-nums).
- Usage Analytics: StatCard adds tone prop (positive/negative/cost/volume/
neutral) — Messages/Tool Calls/Avg Tokens/Sessions = blue, Cost = pulse-
orange, Errors + Cache Hit Rate threshold-based green/red, Throughput
green; values rendered in font-display.
- Activity tracker: unknown cells now silver-translucent (visible) instead
of bg-primary-500/60 which the global crush was collapsing to body navy;
monthly tracker bigger (14×42 cells), uppercase mono labels.
- Tasks/Skills/Files/Memory/Settings forms: add id/name/aria-label to every
unnamed input/select/textarea/file (9 violations across 5 routes).
Fixes
- gateway-restart route uses local gatewayReconnect() (the upstream
gateway.restart RPC was removed in OpenClaw 2026.5.7).
- Files page: replace Monaco (lazy-loaded from cdn.jsdelivr, blocked by
CSP script-src 'self') with a styled textarea; reorders empty-state
checks before the loadingFile check so a stale loading flag can't lock
the pane.
- chat-sidebar gateway-status-dot uses brand pulse-orange when connected;
buttonVariants.ghost dark-mode text fixed (was dark:text-primary-100
which is dark navy in the inverted ramp — invisible on dark bg).
- Skills marketplace API: dedupe by slug (was returning each entry 10x)
+ CJK-ratio filter (<30% CJK chars in summary) so the English UI shows
English-content skills only.
Hard-gate logging rule appended to all 5 agent-os/.md files so every agent
must write a row to command_center.agent_logs before final reply (powers
Agent Roster + Recent Activity + KPIs).
Skip vite.config.ts hardening-headers plugin (intentional — separate
concern; the headers ship as a meta-CSP via __root.tsx for now).
Final sweep against `[data-theme='pulsecheck-navy']` after the audit found 5
non-canonical surfaces. Every card-like element across all 16 sidebar routes
now resolves to one of:
- oklch(0.18 0.08 248) — solid navy body / panel
- oklab(0.18 -0.03 -0.07 / 0.62) — Liquid Glass translucent navy
- rgba(255, 255, 255, 0.04|0.06) — subtle inner-row overlay (BarList
tracks, Recent Activity rows, model
badges with no semantic tint)
- bg-{red,amber,yellow,emerald,green,blue,violet,...}-500/10 — status /
persona tint (kept; tint IS signal)
Fixed:
- mission-control/bar-list.tsx — bar track from bg-primary-100 lift
to rgba white 4%
- gateway/agent-avatar.tsx — 10 AGENT_ACCENT_COLORS persona tokens
from light pastels (bg-blue-100 +
text-blue-600) to dark-mode tints
(bg-blue-500/10 + text-blue-400);
same for violet/rose/cyan/fuchsia/
lime/sky and the accent + emerald +
amber entries to match the pattern
- gateway/team-panel.tsx — 9 MODEL_BADGE_COLOR entries swept;
`auto` model now neutral white-6%
- gateway/config-wizards.tsx — tier badge bg-green-100 / bg-blue-100
→ bg-emerald-500/10 / bg-blue-500/10
- mission-control screen — Action Required + Recent Agent
Activity row patterns from
bg-primary-100 lift to white-4% with
white-8% hover, matching BarList
Closes the two integrity gaps from the Tier 1 audit:
HIGH — No stale-recovery: if dispatchToAgent hangs and the process
dies, a row sticks at status='in_progress' forever.
MEDIUM — No retry counter: a permanently-broken task gets re-picked
every tick, logging endless failure rows.
Schema (command_center.todos, idempotent ALTER):
- attempts integer NOT NULL DEFAULT 0
- last_attempt_at timestamptz
Module changes (src/server/autonomy-loop.ts):
- MAX_ATTEMPTS = 3 → after this many failures the task is demoted
to track_status='Off Track' so listPendingTodos stops returning it.
- STALE_TIMEOUT_MS = 10 min → in_progress rows older than this get
swept back to 'todo' with attempts++ at the top of every tick.
- DISPATCH_TIMEOUT_MS = 90s → Promise.race ceiling on gatewayRpc
so the loop can't hang forever waiting on an unresponsive agent.
- rescueStaleInProgress() runs first; results counted as totalRescued.
- lockTodo writes last_attempt_at on every lock.
- completeTodo takes the current attempts count, bumps to attempts+1,
and demotes to Off Track when newAttempts >= MAX_ATTEMPTS on failure.
- dispatchToAgent takes (taskId, attempt) and builds a deterministic
idempotencyKey of shape `${taskId}:${attempt}:${rand8}` — same
task+attempt yields a key the gateway can dedupe on protocol level.
- AutonomyTickResult.reason narrows to a literal union:
'no-pending-tasks' | 'inflight-already' | 'lost-race-to-lock' —
honest reporting on why a tick was a no-op (was conflated before).
- getAutonomyState() exposes totalRescued in the panel/curl probe.
Verified live with synthetic STALE probe: row inserted at status=
in_progress + attempts=1 + last_attempt_at=30min ago → tick rescued
it (attempts→2, status→todo, At Risk) → picked + dispatched + done
(attempts→3, On Track). Probes cleaned up after.
Two LOW gaps from the audit remain by design:
- counter persistence (totals reset on dev-server restart) — fine for
dev; production would need a stats table
- /api/autonomy-tick GET returns SPA HTML instead of 405 — minor
Closes the two LOW gaps from the prior audit.
Counter persistence
- New table `command_center.autonomy_stats` (singleton row, id='singleton')
with total_ticks / total_dispatched / total_failed / total_rescued /
updated_at columns.
- `hydrateStatsFromSupabase()` runs at the top of every autonomyTick() —
guarded by state.statsHydrated so it only fires once per module load.
Seeds in-memory counters from the persisted row.
- `flushStatsToSupabase()` async-fires after each tick. Not awaited so
it never blocks the dispatch happy path. Loss of one write is
acceptable; the next tick re-flushes.
- Dev-server restarts no longer zero the dashboard's "Ticks / Dispatched
/ Failed" KPIs.
Method handling on /api/autonomy-tick
- POST behavior unchanged (auth + CSRF + rate-limit + dispatch).
- GET / PUT / PATCH / DELETE now return 405 with body
`{ ok:false, error: "Method not allowed. Use POST." }` and an
`Allow: POST` header. Ops debugging with `curl` no longer gets the
SPA fallback HTML.
Verified live:
- `curl -i GET /api/autonomy-tick` → `HTTP/1.1 405` ✓
- `POST` after restart loads totals from the singleton row, increments
to total_ticks=1, flushes updated_at=15:59:06 ✓
The Mission Control build process this branch landed (PulseOS branding,
Tier 1 autonomy, theme harmonization) leaned heavily on ad-hoc Playwright
probes that wrote screenshots to the repo root. Deleted the 146 stale
artifacts and added explicit patterns so `git status` stays readable for
the next person who walks into this repo.
Also ignores autonomy-dev.log (background dev-server stdout when started
with `npm run dev > autonomy-dev.log`) and the audit-shots/ + audit{N}/
output directories used by the bg-consistency / form-field / route-walk
audits.
Replaces the 30s REST poll on Mission Control with a postgres_changes
WebSocket subscription. The dashboard now updates the instant rows are
written to command_center.{todos, agent_logs} — by the autonomy loop,
by Claude on the side, by SQL from psql, by anything.
Backing infra (already migrated in this turn):
- ALTER PUBLICATION supabase_realtime ADD TABLE command_center.todos
- ALTER PUBLICATION supabase_realtime ADD TABLE command_center.agent_logs
- ALTER PUBLICATION supabase_realtime ADD TABLE command_center.autonomy_stats
Client:
- New lib/supabase-client.ts singleton (HMR-safe via globalThis cache)
using @supabase/supabase-js ^2.105.4 (added to package.json).
- Project-scoped publishable key + persistSession:false (no auth flow
needed; tables ship `FOR ALL USING (true)` RLS policies).
- eventsPerSecond:5 cap on the realtime params.
Mission Control screen:
- Keeps the existing supabaseGet REST fetch on mount for cold-start
hydration of the last 200 todos + last 200 logs.
- Subscribes to channel `mission-control-live`:
.on('postgres_changes', { schema:'command_center', table:'todos' })
.on('postgres_changes', { schema:'command_center', table:'agent_logs' })
- Each payload: INSERT → prepend (clipped to 200), UPDATE → replace by id,
DELETE → filter by id. Local state stays in sync with DB.
- Cleans up the channel via supabase.removeChannel() on unmount.
- The 60s polling effect is gone; the only timer left is the 30s `now`
re-render (for relative-time labels) and the 10s autonomy-status poll
(which reads in-process state, not Supabase, so realtime doesn't help).
Verified: WS opens to wss://…/realtime/v1/websocket on dashboard mount;
TS compiles clean; no autonomy-loop interactions changed.
Run the ln-614-docs-fact-checker against os/dashboard-clawsuite/**/*.md and
fix every finding except the editorial one. Audit report + remediation log
captured at docs/audits/ln-614--global.md.
HIGH-1 resolved -- dev-server port mismatch (docs said 3000, vite binds 3010)
README.md (4 lines), SETUP.md (5 lines, incl. scripts table + .env mistake
row), CONTRIBUTING.md (1 line), docs/remote-access.md (Tailscale target /
LAN example / WSL portproxy + Docker callout now annotated), docs/mobile-
setup.md (3 lines, incl. tailscale-ip examples).
docker-compose 3000:3000 prod mapping intentionally preserved -- prod build
defaults to PORT=3000 and is now explicitly distinguished from dev.
HIGH-2 resolved -- docs/workspace-ux-architecture.md (2026-03-10 spec, 7 of 11
"implemented" file paths missing in this repo) now leads with an ARCHIVED
banner pointing at the current src/screens/mission-control/ layout.
HIGH-3 partial -- README badge 3.0.0 -> 4.0.0 (matches package.json). CHANGELOG
v4.0.0 entry still missing -- left for editorial decision.
HIGH-4 resolved -- docs/CLAWSUITE-ARCHITECTURE.md skills count 3,000+ -> 2,000+
to align with README.
HIGH-5 resolved -- added "typecheck": "tsc --noEmit" to package.json scripts
(was documented in CONTRIBUTING.md but not implemented).
HIGH-6 resolved -- docs/CLAWSUITE-ARCHITECTURE.md "Existing (already
integrated)" endpoint block replaced phantom POST /api/chat and
GET /api/sessions/:id/history with the real chat-and-session routes that
actually live in src/routes/api/ (sessions.ts, sessions/$sessionKey.status,
sessions/send, chat-events, chat-abort).
MEDIUM-1 withdrawn -- /api/mission-history at AGENT-HUB-AUDIT.md:145 is
future-tense ("Consider..."), not a false claim. Should never have been
flagged.
MEDIUM-2 rolled into HIGH-2 (foreign repo path called out in archive banner).
MEDIUM-3 resolved -- README "66+ components" softened to "across the entire
app" (number was unverifiable against any concrete asset set).
Prettier's PostToolUse hook normalized markdown-table padding across the
edited docs -- semantically identical to the pre-edit content.
Closes the last remaining HIGH-3 audit gap (CHANGELOG had no v4 entry despite package.json being at 4.0.0). Marked as Draft, 2026-05-14 since the tag has not been cut yet. 782 commits since v3.0.0 grouped into 9 sections: - Mission Control + Tier 1 Autonomy (new) - Conductor (renamed from Agent Hub) - PulseOS Branding + Apple iOS 26 Liquid Glass theme - Operations + Mobile - Skills / Cron / Files - Electron Desktop App - Remote & Mobile Access - Bug Fixes - Security - Tooling & Infrastructure - Docs - Architecture Header notes that PulseCheck-fork-specific items (autonomy loop, Realtime, PulseOS branding) are mixed with generic ClawSuite features (Conductor, Electron, perf, a11y), since this CHANGELOG lives in the fork at pulsecheck-ai/os/dashboard-clawsuite/. Editorial — review and adjust before tagging v4.0.0.
Add a /graph surface that iframes the generated viewers from public/graphs/ with a Code/Knowledge/Temple toggle, plus a command-palette entry. CSP scoping (vite.config) and the mobile overflow-nav item are left in the working tree — those files carry unrelated uncommitted WIP. Co-Authored-By: claude-flow <ruv@ruv.net>
Move CSP into a path-scoped Vite plugin: strict 'self' for the app, relaxed (jsdelivr + Google Fonts + frame-ancestors 'self') only for /graphs/* viewers. Add the Graph item to the mobile overflow nav. Note: both files also carried pre-existing uncommitted working-tree changes that came along — WS proxy auth gating in vite.config.ts, and ask-brain/operations nav fixes in the overflow panel. Co-Authored-By: claude-flow <ruv@ruv.net>
…cial stack
Checkpoint of all uncommitted work on this feature branch. This spans MULTIPLE
prior sessions (intel cockpit, LinkedIn / Postiz / RSS / Media / Gmail / HubSpot
modules, mission-control, gateway) PLUS this session's additions below — committed
together as a safety checkpoint, not all authored in one session.
This session's additions:
- GraphQL gateway (Yoga + Pothos) at /api/graphql:
- Intel read surface: intelSources, intelItems (+ DataLoader source),
askIntel (LightRAG RAG, capped at 30s), knowledgeHealthy
- Margin reads (tenant-scoped): marginLeaksTop, marginOpsSummary
- Integration Hub module: /integrations screen (KPIs + connection & catalog
cards), /api/integrations (+/catalog) over the real integrations.* schema
(504-row catalog, tenant_connections, source_systems), sidebar entry
- Knowledge Graph sidebar module wired to /graph (LightRAG / code / temple)
- Pollinations free image-per-post toggle in the RSS autopost pipeline
- Audit fix-pass: getHubStats double-fetch, catalog error state, NaN guards,
search-filter sanitizer whitelist, documentationUrl scheme guard,
a11y (focus rings, text/pill contrast, aria-pressed, heading levels)
- pnpm: packageManager pin (pnpm@11.1.2) + engines node 22.x; removed stray
package-lock.json (pnpm authoritative)
- .gitignore: exclude env backups / scratch / build cache
- docs: graphql-gateway-architecture, pulseos-unification-program
Secrets + scratch excluded via .gitignore (.env.bak*, env-verify.txt,
_tc_rss.txt, *.tsbuildinfo, .claude-flow/). Verified no secret/credential
files staged.
Co-Authored-By: claude-flow <ruv@ruv.net>
Realistic animated aurora background (Nano Banana 2 / gemini-3-pro-image), full-bleed with ken-burns pan+zoom and a subtle aurora shimmer; glass auth card; 'Pulse' white + 'OS' orange->gold gradient matching the dashboard. Boot splash recolored to the warm brand palette to match. All decorative motion gated behind prefers-reduced-motion; auth contract unchanged.
The TanStack Start (Vite-native) build emits a Web fetch handler that does not listen, serve static client assets, or proxy the gateway. serve.mjs wraps it into a self-listening Node server: static dist/client, SSR/API/server-fns via the fetch handler, the /ws-gateway (auth-gated through /api/auth-check) + /api/gateway-proxy + /gateway-ui + /workspace-api proxies, and the dev server's hardening headers + path-scoped CSP. package.json start + ecosystem.config.cjs pulseos now run it (NODE_ENV=production, node --env-file-if-exists=.env). Verified live: os.pulsecheckai.app serves the prod build behind the password gate; pm2 online restarts=0.
…ty audit) P0: serve.mjs now auth-gates the same-origin gateway/workspace proxies (/api/gateway-proxy, /gateway-ui, /workspace-api, /ws-gateway). They previously bypassed the CLAWSUITE_PASSWORD perimeter and exposed the OpenClaw gateway unauthenticated on the public origin (verified: tunnel /api/gateway-proxy/ went 200 -> 401). S1: extracted CSP + hardening headers + proxy topology to one shared module (src/server/security-headers.mjs + .d.mts) imported by both vite.config.ts (dev) and serve.mjs (prod). No more dev/prod drift. S2: scripts/dashboard-smoke.mjs is now a prod-aware smoke test + P0 regression guard (asserts proxies return 401 unauthenticated). D1: opt-in durable sessions in auth-middleware.ts (CLAWSUITE_PERSIST_SESSIONS=1, default off, graceful fallback) so 30-day sessions can survive a restart. Deferred: D2 (remove CSP 'unsafe-inline') needs an app-wide nonce rollout. Verified: tsc 0 errors, vite build OK, smoke 8/0 against os.pulsecheckai.app.
…OT, unit tests Two review agents (security + typescript) re-audited the prior fix and found a real P1 plus cleanups; all addressed and verified live. P1: isAuthed() now fails CLOSED — it denied-by-default on the SSR /api/auth-check 4s timeout/parse-error before (the timeout returned authRequired:false, which opened the proxy gate under server slowness). auth-check.ts timeout fallback also now returns authRequired:true. P1-hardening: /api/gateway/agents GET gate is tied to isPasswordProtectionEnabled() instead of an IS_DEV/NODE_ENV module constant that could bake wrong at build time. S-fix-1: serve.mjs routing is now driven by PROXY_ROUTES (no more fake/unused SSOT); matchProxyRoute + safeStaticPath extracted to the shared module and unit-tested (20 vitest cases incl. path-traversal + encoded/absolute invariants). S-fix-2: serve.mjs runs a boot-time perimeter self-check that logs whether the gateway proxy is gated, every start. P2: strip any upstream clawsuite-auth Set-Cookie; auth-middleware consolidates vite-detection and warns at runtime about the 0600/NTFS caveat when CLAWSUITE_PERSIST_SESSIONS is on. Verified: tsc 0 errors, vitest 20/20, vite build OK, tunnel /api/gateway-proxy + /api/gateway/agents return 401 unauthenticated, boot self-check OK.
…quest CSP nonce
serve.mjs generates a per-request nonce, injects it as the x-csp-nonce request header, and emits script-src 'self' 'nonce-<n>' (no more 'unsafe-inline' for scripts in prod). style-src 'unsafe-inline' is retained on purpose — React's inline style={{}} props are style attributes that nonces don't cover.
getRouter() reads the nonce via createIsomorphicFn (src/csp-nonce.ts) so the server-only getRequestHeader import is stripped from the client bundle (satisfies TanStack's import-protection plugin) -> router.options.ssr.nonce -> TanStack stamps nonce= on its SSR-injected scripts. __root.tsx stamps the same nonce on the 4 inline IIFEs (theme, themeColor, splash, readiness).
dev keeps 'unsafe-inline' (vite injects no nonce header) so HMR is unaffected; /graphs/* static viewers keep 'unsafe-inline' (their inline scripts aren't nonced).
Verified: tsc 0, vite build OK; on :3011 AND the live tunnel the CSP nonce == the script nonce (1 nonce across 22 scripts) and the full authenticated dashboard hydrates with ZERO CSP violations; proxies still 401.
…der, twilio/users/security/workspace routes, auth-users, i18n (96 new + 32 modified, secret-scanned clean)
… + build now clean)
… (voice/twilio/supabase/linkedin/oauth) as placeholders, zero secret values
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings the PulseCheck Mission Control design language and several platform capabilities to ClawSuite, plus a new 3D knowledge-graph surface. 212 files changed.
Highlights
/graph— 3D knowledge & code graph viewers — a new/graphroute embedding three Three.js viewers (Code-dependency graph, LightRAG Knowledge graph, and a "Temple" view) with PulseCheck brand styling (navy/cyan + pulse-gradient, Inter/Bricolage, liquid-glass control panel), 4-way label modes (All / Important / Hover / None), and a path-scoped CSP so the app stays strict'self'while only/graphs/*get the viewer's CDN/font allowances.todos+agent_logs./api/gateway-restart.Commits (13)
d3c6b9dfeat(theme): PulseCheck Mission Control harmony + SSR crash fix239e87dfeat(mission-control): full Mission Control screen + Liquid Glass kit + navy themea50456efix(gateway): auto-heal shut-down singleton; recover via /api/gateway-restart0ba1cfbfeat(command-center): PulseOS branding, Tier 1 autonomy loop, Agent Roster + System Integrations0de115efix(theme): card-bg consistency sweep — 16/16 routes on canonical navyf0a7a0ffix(autonomy): stale-recovery sweep + retry-counter demotionb2c0f1dfix(autonomy): persist counters across restarts + 405 on non-POST tick74710e2chore(gitignore): ignore Playwright/audit screenshots + autonomy dev log90ec44bfeat(dashboard): Supabase Realtime for live todos + agent_logsed89a80docs: remediate ln-614 fact-check audit411555adocs(changelog): draft v4.0.0 release notes34dba09feat: /graph route — embed Code/Knowledge/Temple 3D viewers in PulseOSa5acca8feat: scope /graphs CSP + Graph nav entry (completes /graph wiring)Test plan
Verified locally:
pnpm typecheck— 0 errors/graphloads; Code / Knowledge / Temple toggle switches viewers; label modes workframe-ancestors 'none',script-src 'self');/graphs/*load Three.js + fonts under the scoped relaxed CSPTo verify by reviewers (prior-WIP areas on this branch):
todos/agent_logsliveGenerated with claude-flow (https://github.com/ruvnet/claude-flow)