diff --git a/apps/dashboard/src/lib/observatory/cognitive-palette.ts b/apps/dashboard/src/lib/observatory/cognitive-palette.ts new file mode 100644 index 0000000..fb228b2 --- /dev/null +++ b/apps/dashboard/src/lib/observatory/cognitive-palette.ts @@ -0,0 +1,154 @@ +/** + * Cognitive Bioluminescent Cortex — the ONE source of truth for the dashboard's + * invented visual language (design council: Opus 4.8 × GPT-5.5, Jul 8 2026). + * + * The dashboard is a dark local brain in a jar. Routes are organs viewed through + * different instruments. This is NOT purple-on-black. The base is blackwater + + * oil-film + enzymatic light + scarlet immune heat. Every color MEANS something + * on a Vestige-only substrate (the discipline test): retention is oxygen, trust + * is membrane thickness, causality is a retrograde axon, suppression is scar. + * + * Rule of the language: magenta is reserved EXCLUSIVELY for backward causality + * (RSB). Indigo appears ONLY as bitemporal transaction-time parallax. Neither is + * ever a route accent. Purple, as a flat brand chrome, does not exist here. + * + * All values are linear-ish sRGB hex; the helpers return 0..1 rgb for WGSL + * uniforms. Keep this file free of Svelte/DOM imports so shaders + adapters + + * components can all share it. + */ + +/** Parse '#rrggbb' → [r,g,b] in 0..1. Falls back to blackwater on bad input. */ +export function rgb01(hex: string): [number, number, number] { + const m = /^#?([0-9a-fA-F]{6})$/.exec(hex.trim()); + if (!m) return [0x02 / 255, 0x03 / 255, 0x07 / 255]; + const v = parseInt(m[1], 16); + return [((v >> 16) & 0xff) / 255, ((v >> 8) & 0xff) / 255, (v & 0xff) / 255]; +} + +// --------------------------------------------------------------------------- +// Base medium — the blackwater the whole organism lives in. +// --------------------------------------------------------------------------- +export const MEDIUM = { + blackwater: '#020307', // absolute background; NEVER tinted purple + anaerobic: '#07100D', // nutrient medium, green-black low field + cyanFog: '#0B171B', // deep cyan-black parallax fog + sediment: '#11140A' // old-memory amber-black sediment +} as const; + +// --------------------------------------------------------------------------- +// Living memory / FSRS retention — the oxygen gradient. +// sediment → amber debt → healthy green → luciferin white as retention rises. +// --------------------------------------------------------------------------- +export const RETENTION = { + luciferin: '#E9FFB7', // >= 0.86 newly retrievable, hot white-green + healthy: '#A8FF5E', // 0.65–0.86 stable + recall: '#29F2A9', // active recall / excitation wave (activation > p90) + bridge: '#1BD6FF', // remote association / semantic bridge + latent: '#315CFF', // deep latent, low activation but structurally present + debt: '#8A4B18', // 0.25–0.45 forgetting debt / dehydrated trace + extinction: '#2A160B' // < 0.25 near-extinction sediment +} as const; + +/** Ordered retention stops (low→high) for a continuous WGSL ramp. */ +export const RETENTION_RAMP: readonly string[] = [ + RETENTION.extinction, + RETENTION.debt, + RETENTION.healthy, + RETENTION.luciferin +]; + +// --------------------------------------------------------------------------- +// Trust / verifier / immune system — membranes and macrophages. +// --------------------------------------------------------------------------- +export const IMMUNE = { + trustMembrane: '#F4F1D0', // high-trust warm ivory edge + caution: '#FFD166', // caution verdict / yellow immune flare + veto: '#FF3B30', // veto / contradiction injury + suppressionScar: '#B90D2B', // permanent red-black suppression lacquer + labile: '#FF7A1A' // reversible labile suppression window +} as const; + +// --------------------------------------------------------------------------- +// Causality / RSB — the signature. Magenta lives ONLY here. +// --------------------------------------------------------------------------- +export const CAUSAL = { + forward: '#00F5D4', // forward recall signal + retrograde: '#FF2DF7', // backward causal backfill axon — RSB ONLY + receiptSpark: '#FFFFFF' // one-frame causal-receipt proof spark at edge write +} as const; + +// --------------------------------------------------------------------------- +// Bitemporal / audit — growth rings. Indigo lives ONLY here. +// --------------------------------------------------------------------------- +export const BITEMPORAL = { + validRing: '#6BFFB8', // valid-time growth ring + txShadow: '#7C6CFF', // transaction-time shadow (indigo, parallax only) + supersession: '#FFB000' // supersession amber cut-line +} as const; + +// --------------------------------------------------------------------------- +// System health / stats — vitals. +// --------------------------------------------------------------------------- +export const VITALS = { + throughput: '#9DFFEB', // cool system flow + backlog: '#FF4FD8' // backlog pressure — ONLY when queue growth is real +} as const; + +// --------------------------------------------------------------------------- +// Retention → color (continuous). Mirrors the WGSL ramp so CPU labels and GPU +// cells agree. `r` is retrievability 0..1. +// --------------------------------------------------------------------------- +export function retentionColor(r: number): [number, number, number] { + const t = Math.max(0, Math.min(1, r)); + // piecewise across the 4 ramp stops + const stops = RETENTION_RAMP.map(rgb01); + const seg = t * (stops.length - 1); + const i = Math.min(stops.length - 2, Math.floor(seg)); + const f = seg - i; + const a = stops[i]; + const b = stops[i + 1]; + return [a[0] + (b[0] - a[0]) * f, a[1] + (b[1] - a[1]) * f, a[2] + (b[2] - a[2]) * f]; +} + +/** + * Event type → the impulse color it injects into the organism. Drives the Feed + * bloodstream, the LiveBridge reactions, and per-organ event pulses. Only real + * VestigeEvent variants appear here (the discipline test lives at the source). + */ +export const EVENT_IMPULSE: Record = { + MemoryCreated: RETENTION.healthy, // cells condense from nutrient noise + SearchPerformed: CAUSAL.forward, // cyan chemoattractant wave + ActivationSpread: RETENTION.recall, // green/teal excitation along real paths + ImportanceScored: BITEMPORAL.supersession, // gold-white enzyme deposit + RetentionDecayed: RETENTION.debt, // amber dehydration front + ConnectionDiscovered: CAUSAL.forward, // a new axon grows + DeepReferenceCompleted: RETENTION.luciferin, // the reasoning organ lights + BackfillFired: CAUSAL.retrograde, // magenta retrograde axon + CausalReceipt: CAUSAL.retrograde, + MemorySuppressed: IMMUNE.suppressionScar, // macrophage engulfs the cell + MemoryUnsuppressed: IMMUNE.labile, + MemoryPromoted: RETENTION.luciferin, + MemoryDemoted: RETENTION.debt, + MemoryPrOpened: IMMUNE.caution, // translucent immune proposal capsule + MemoryPrDecided: IMMUNE.trustMembrane, + HookVerdictRecorded: IMMUNE.veto, + TraceEvent: CAUSAL.forward, + DreamStarted: BITEMPORAL.txShadow, + DreamCompleted: BITEMPORAL.validRing, + Rac1CascadeSwept: IMMUNE.suppressionScar +}; + +/** Impulse rgb for a VestigeEvent type (blackwater fallback for unknowns). */ +export function eventImpulse01(type: string): [number, number, number] { + return rgb01(EVENT_IMPULSE[type] ?? MEDIUM.blackwater); +} + +/** + * Trust → membrane thickness (world units, matched to the metaball edge width in + * the field pass). complete evidence = continuous thick ring; low trust = thin + * perforated ring. `trust` is 0..1. + */ +export function membraneWidth(trust: number): number { + const t = Math.max(0, Math.min(1, trust)); + return 0.003 + (0.018 - 0.003) * t; +} diff --git a/apps/dashboard/src/lib/observatory/route-scene.ts b/apps/dashboard/src/lib/observatory/route-scene.ts new file mode 100644 index 0000000..ef96e0a --- /dev/null +++ b/apps/dashboard/src/lib/observatory/route-scene.ts @@ -0,0 +1,148 @@ +/** + * RouteSceneModel — the shared contract between a route's data adapter and its + * RouteStage WebGPU organ (design council Opus 4.8 × GPT-5.5, Jul 8 2026). + * + * The discipline test is enforced HERE, in the type system: every visual + * primitive MUST carry a `source` provenance (a real memory id, event, receipt, + * or a named real scalar). A hero that can't name its source doesn't ship. + * Swap the backend value for Math.random() and `source` becomes a lie the code + * review catches. + * + * Adapters (`*-scene.ts`) turn API/WebSocket responses into this model; the + * RouteStage renders it through the shared engine + cognitive field pass. Pure + * data — no Svelte/DOM/GPU imports. + */ + +/** The organs of the Cognitive OS — one per transformed route. */ +export type RouteOrgan = + | 'reasoning' + | 'timeline' + | 'feed' + | 'schedule' + | 'duplicates' + | 'contradictions' + | 'patterns' + | 'memories' + | 'explore' + | 'importance' + | 'activation' + | 'dreams' + | 'intentions' + | 'blackbox' + | 'memory-prs' + | 'stats' + | 'settings'; + +/** + * Provenance for a primitive — the discipline-test receipt. Every node/edge/ + * event/label points to something REAL. `kind` says what backend fact backs it. + */ +export interface Provenance { + kind: 'memory' | 'event' | 'receipt' | 'pair' | 'trace' | 'pr' | 'pattern' | 'scalar'; + /** The real id (memory id, receipt id, run id, pair key, …). */ + id: string; + /** For kind:'scalar' — the named metric + its real value (e.g. 'dueForReview'). */ + scalar?: { name: string; value: number }; +} + +/** A living cell — a memory or record rendered in the organism. */ +export interface RouteNode { + source: Provenance; + /** Stable index in the GPU buffer (assigned by the adapter). */ + index: number; + label: string; + /** FSRS retrievability 0..1 — the oxygen level. Drives hue + cracking. */ + retention: number; + /** FSRS stability in days — drives radius (sqrt) + sedimentation depth. */ + stability?: number; + /** ISO last-access — drives live decay recompute (fsrs.ts). */ + lastAccessed?: string; + /** 0..1 activation concentration — halo opacity + chemotaxis. */ + activation?: number; + /** 0..1 trust / verifier confidence — membrane thickness. */ + trust?: number; + /** suppression count (>0 = scarred). */ + suppression?: number; + tags: string[]; + type: string; +} + +/** A tension-bearing fiber (axon), not a line. Weight tightens it. */ +export interface RouteEdge { + source: Provenance; + sourceIndex: number; + targetIndex: number; + weight: number; + /** Real connection type — 'causal' fires the retrograde grammar. */ + kind: string; +} + +/** A discrete cognitive event to animate (from the live feed or a receipt). */ +export interface RouteEvent { + source: Provenance; + /** VestigeEvent type — maps to an impulse color (cognitive-palette). */ + type: string; + /** Target node index if the event binds to a cell, else -1. */ + targetIndex: number; + /** Monotonic frame it fired (assigned by the LiveBridge / adapter). */ + frame: number; + /** Free scalar (energy, confidence, cascade count, …). */ + energy: number; +} + +/** A named proof the organ can etch as an MSDF scar / open on click. */ +export interface RouteReceipt { + source: Provenance; + label: string; + /** Node indices this receipt lights (evidence, path, pair). */ + nodeIndices: number[]; +} + +/** + * The full scene an organ renders. `scalars` holds route-level real metrics + * (endangered fraction, due count, similarity threshold, …) that drive global + * field behavior. Every entry traces to a real backend fact. + */ +export interface RouteSceneModel { + organ: RouteOrgan; + nodes: RouteNode[]; + edges: RouteEdge[]; + events: RouteEvent[]; + receipts: RouteReceipt[]; + /** Route-level real metrics (name → value). */ + scalars: Record; + /** True when the organ has real data; false → honest empty state. */ + alive: boolean; +} + +/** An empty, honest scene — the field breathes, nothing is invented. */ +export function emptyScene(organ: RouteOrgan): RouteSceneModel { + return { organ, nodes: [], edges: [], events: [], receipts: [], scalars: {}, alive: false }; +} + +/** + * Dev-only discipline assertion: every node/edge/event/receipt must carry a + * `source`. Call in adapters (behind import.meta.env.DEV) so a screensaver + * primitive — one with no real provenance — trips the review before it ships. + */ +export function assertProvenance(scene: RouteSceneModel): void { + const bad: string[] = []; + const check = (arr: { source?: Provenance }[], what: string) => { + arr.forEach((p, i) => { + if (!p.source || !p.source.kind || (!p.source.id && !p.source.scalar)) { + bad.push(`${what}[${i}]`); + } + }); + }; + check(scene.nodes, 'node'); + check(scene.edges, 'edge'); + check(scene.events, 'event'); + check(scene.receipts, 'receipt'); + if (bad.length > 0) { + throw new Error( + `[discipline-test] ${scene.organ}: ${bad.length} primitive(s) without real provenance: ${bad + .slice(0, 8) + .join(', ')}. Every hero primitive must point to a real memory/event/receipt/scalar.` + ); + } +}