|
Download SOVEREIGN_NOTEBOOK_UI_SPEC.md from Snapkitty/Sovereign-Event-Bus: direct link, hf CLI and curl.
- Browser
- Download file 42.7 kB
-
https://huggingface.co/Snapkitty/Sovereign-Event-Bus/resolve/main/SOVEREIGN_NOTEBOOK_UI_SPEC.md
- Command line
-
hf download hf://Snapkitty/Sovereign-Event-Bus/SOVEREIGN_NOTEBOOK_UI_SPEC.md
-
curl -L -o SOVEREIGN_NOTEBOOK_UI_SPEC.md https://huggingface.co/Snapkitty/Sovereign-Event-Bus/resolve/main/SOVEREIGN_NOTEBOOK_UI_SPEC.md
42.7 kB
| # SOVEREIGN NOTEBOOK UI & ANIMATION SPEC | |
| **Frontend/UX Engineer Specification** | |
| **Version**: 1.0 | |
| **Status**: DESIGN COMPLETE | |
| **Date**: 2026-07-27 | |
| **Authority**: Jessica (jessi@snapkitty.com) | |
| --- | |
| ## Executive Summary | |
| The Sovereign Notebook is a cryptographically-sealed, reactive notebook interface that bridges: | |
| - **Cell Editing**: Code, Markdown, Math (EmojiCode, HolyC, Python, JavaScript, Ada) | |
| - **Execution Flow**: Isomorphic transformation pipeline (M1βM2βM3βRuntimeβM5βM6) | |
| - **Receipt Ledger**: WORM-sealed execution records with Ed25519 signatures | |
| - **Dependency Graph**: Visual data flow between cells | |
| - **Responsive Animations**: Unicode glyphs, hash chains, cryptographic locks | |
| **Design Philosophy**: Minimal framework, vanilla Web Components, Canvas/SVG for animation, reactive state binding. Dark mode primary with light mode support. | |
| --- | |
| ## 1. LAYOUT & PANEL ARCHITECTURE | |
| ### 1.1 Desktop Layout (1440px+) | |
| ``` | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β HEADER: Notebook Title | Run | Interrupt | Save | Export | Verify β | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ | |
| β TOOLBAR: [β‘ EmojiCode] [Python] [Ada] [Math] | π Search β | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ | |
| β β | |
| β MAIN CONTENT AREA (3 columns) β | |
| β β | |
| β ββ LEFT SIDEBAR βββββ¬β CENTER EDITOR βββββββββ¬β RIGHT PANEL βββββββ β | |
| β β β β β β | |
| β β β’ Metadata β Cell 1 [CODE] β β’ WORM Ledger β β | |
| β β - Cell count β ββββββββββββββββββ β βββββββββββββββ β β | |
| β β - Depends on β β β‘ fn:1618 β β β Receipt ID β β β | |
| β β - Trust level β ββββββββββββββββββ β β βββββββββββ β β β | |
| β β β βΆοΈ [Output] β β Hash: 0xab β β β | |
| β β β’ Dep Graph β β β Sig: Ed... β β β | |
| β β ββ Visual β Cell 2 [MARKDOWN] β β Time: 10:30 β β β | |
| β β grid β ββββββββββββββββββ β βββββββββββββββ β β | |
| β β β β # Results β β β β | |
| β β β’ Trust Policies β ββββββββββββββββββ β β’ Dependency Graph β β | |
| β β - Execute as β β (small visual) β β | |
| β β - Verify with β Cell 3 [MATH] β β β | |
| β β - Seal with β ββββββββββββββββββ β β’ Cell Cache β β | |
| β β β β β Ξ»(x)dx β β - Hit ratio β β | |
| β β β ββββββββββββββββββ β - Memory used β β | |
| β β β βΆοΈ [Output] β β β | |
| β β β β β β | |
| β βββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββ β | |
| β β | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ | |
| β FOOTER: Execution time | Cell count | Status | Connected to runtimeβ | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ``` | |
| ### 1.2 Mobile Layout (< 768px) | |
| ``` | |
| βββββββββββββββββββββββββββββββ | |
| β HEADER (compact) β | |
| βββββββββββββββββββββββββββββββ€ | |
| β TAB BAR: [Editor] [Ledger] β | |
| βββββββββββββββββββββββββββββββ€ | |
| β β | |
| β MAIN CONTENT (single col) β | |
| β β | |
| β β’ Cell 1 (full width) β | |
| β β’ Cell 2 (full width) β | |
| β β | |
| βββββββββββββββββββββββββββββββ€ | |
| β BOTTOM SHEET: Metadata β | |
| βββββββββββββββββββββββββββββββ | |
| ``` | |
| --- | |
| ## 2. UI COMPONENTS | |
| ### 2.1 Web Component Definitions | |
| #### `<notebook-container>` | |
| **Purpose**: Root container for entire notebook UI | |
| **Attributes**: | |
| - `notebook-id` (string): UUID of notebook | |
| - `mode` (enum): 'edit' | 'view' | 'locked' | |
| - `theme` (enum): 'dark' | 'light' | |
| **State**: | |
| ```typescript | |
| interface NotebookContainerState { | |
| notebookId: string; | |
| title: string; | |
| cells: Cell[]; | |
| executionQueue: ExecutionTask[]; | |
| receipts: ReceiptRecord[]; | |
| dependencyGraph: CellDependency[]; | |
| metadata: { | |
| createdAt: number; | |
| modifiedAt: number; | |
| author: string; | |
| trustLevel: 'low' | 'medium' | 'high' | 'sovereign'; | |
| }; | |
| } | |
| ``` | |
| **Events**: | |
| - `cell:created` β new cell added | |
| - `cell:executed` β cell completed execution | |
| - `receipt:sealed` β receipt added to WORM | |
| - `graph:updated` β dependency graph changed | |
| --- | |
| #### `<notebook-cell>` (Base Class) | |
| **Purpose**: Editable/viewable cell with multiple types | |
| **Attributes**: | |
| - `cell-id` (string): UUID | |
| - `cell-type` (enum): 'code' | 'markdown' | 'math' | |
| - `language` (enum): 'emoji' | 'python' | 'holyc' | 'javascript' | 'ada' | |
| - `readonly` (boolean): read-only mode | |
| **Subcomponents**: | |
| - `<notebook-cell-editor>` β editable textarea with syntax highlighting | |
| - `<notebook-cell-output>` β rendered output display | |
| - `<notebook-cell-controls>` β run/delete/move buttons | |
| **State**: | |
| ```typescript | |
| interface Cell { | |
| id: string; | |
| type: 'code' | 'markdown' | 'math'; | |
| language: Language; | |
| source: string; | |
| output: CellOutput | null; | |
| executionTime: number; // ms | |
| dependsOn: string[]; // cell IDs | |
| metadata: { | |
| createdAt: number; | |
| executedAt: number | null; | |
| executedBy: string; | |
| proof: ProofStatus; | |
| }; | |
| sealStatus: 'pending' | 'sealed' | 'verified' | 'revoked'; | |
| } | |
| interface CellOutput { | |
| type: 'text' | 'table' | 'graph' | 'math' | 'error'; | |
| data: any; | |
| hash: string; // blake3 | |
| receiptId: string | null; | |
| } | |
| ``` | |
| --- | |
| #### `<cell-editor>` | |
| **Purpose**: Monaco-like syntax highlighting for code cells | |
| **Attributes**: | |
| - `language` (enum): programming language | |
| - `theme` (enum): 'dark' | 'light' | |
| **Features**: | |
| - Syntax highlighting via Highlight.js | |
| - Line numbers | |
| - Code folding | |
| - Inline error markers | |
| - Autocomplete hints (from semantic registry) | |
| - Undo/redo | |
| **Keyboard Bindings**: | |
| - `Shift+Enter` β run this cell | |
| - `Ctrl+Enter` β run and select next | |
| - `Ctrl+/` β toggle comment | |
| - `Tab` β indent (4 spaces) | |
| - `Escape` β exit edit mode | |
| --- | |
| #### `<cell-output>` | |
| **Purpose**: Render cell execution results | |
| **Supported Types**: | |
| ``` | |
| TEXT OUTPUT | |
| βββββββββββββββββββββββββββββββ | |
| β Output: 42 β | |
| β Type: integer β | |
| β Hash: 0xabcd... β | |
| βββββββββββββββββββββββββββββββ | |
| TABLE OUTPUT | |
| βββββββββββββββββββββββββββββββ | |
| β col1 | col2 | col3 β | |
| β βββββββββββββββββββββββββ β | |
| β val1 | val2 | val3 β | |
| β val4 | val5 | val6 β | |
| βββββββββββββββββββββββββββββββ | |
| GRAPH OUTPUT | |
| βββββββββββββββββββββββββββββββ | |
| β (rendered via D3/SVG) β | |
| β β’ Scatter plot β | |
| β β’ Bar chart β | |
| β β’ Network graph β | |
| βββββββββββββββββββββββββββββββ | |
| MATH OUTPUT | |
| βββββββββββββββββββββββββββββββ | |
| β β Ξ»(x)dx = 1618 (sealed) β | |
| β Verified by: Lean4 β β | |
| βββββββββββββββββββββββββββββββ | |
| ``` | |
| **Renderers**: | |
| - Text: plain or markdown-rendered | |
| - Table: HTML table with sortable columns | |
| - Graph: D3/Canvas visualization | |
| - Math: MathJax/KaTeX rendering | |
| - Error: syntax highlighted traceback | |
| --- | |
| #### `<notebook-sidebar>` | |
| **Purpose**: Left metadata and navigation panel | |
| **Sections**: | |
| 1. **Notebook Info** | |
| - Title (editable) | |
| - Cell count | |
| - Last modified | |
| - Total execution time | |
| 2. **Cell List** (scrollable) | |
| - Quick jump to cell | |
| - Execution status icon | |
| - Dependency count | |
| 3. **Dependency Graph** (miniature) | |
| - Small SVG visualization | |
| - Highlight current cell | |
| - Click to jump | |
| 4. **Trust Policies** | |
| - Execute as: [agent name] | |
| - Verify with: [verifier] | |
| - Seal with: [key fingerprint] | |
| --- | |
| #### `<worm-ledger>` | |
| **Purpose**: Right panel showing receipt chain | |
| **Display**: | |
| ``` | |
| WORM LEDGER VIEWER | |
| βββββββββββββββββββββββββββββ | |
| Receipt #1 [βΌ] | |
| ββ ID: 0xfab1... | |
| ββ Event: Cell 2 execute | |
| ββ Agent: builder | |
| ββ Status: β sealed | |
| ββ Time: 10:30:45 | |
| ββ Hash: 0x7a8b... | |
| ββ Sig: Ed25519(0x1234...) | |
| Receipt #2 [βΌ] | |
| ββ ID: 0xfab2... | |
| ββ Event: Cell 3 verify | |
| ββ ... | |
| [Load More β] | |
| ``` | |
| **Features**: | |
| - Expandable receipt details | |
| - Verify signature button | |
| - Export receipt as JSON | |
| - Copy hash to clipboard | |
| - Filter by status/agent | |
| --- | |
| #### `<execution-trace>` | |
| **Purpose**: Animation overlay showing live execution flow | |
| **Display**: | |
| ``` | |
| EXECUTION TRACE (Canvas overlay, semi-transparent) | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| Cell 1 βββ [Ξ» Ξ» Ξ»] βββ Cell 2 βββ [β] βββ OUTPUT | |
| Running (0.2s) Queued Ready | |
| Legend: | |
| βββ data flow (animated arrow) | |
| Ξ» unicode glyph (flowing) | |
| β completion marker | |
| ``` | |
| --- | |
| #### `<cryptographic-stamp>` | |
| **Purpose**: Animated WORM seal effect | |
| **Visual**: | |
| ``` | |
| π SEALED | |
| β±ββββββ² | |
| β blake3 β (rotating, glowing) | |
| β²ββββββ± | |
| wormβ» | |
| ``` | |
| **Animation**: | |
| - Rotating background every 2s | |
| - Blake3 hash scrolls | |
| - Glow effect pulses | |
| - Ed25519 signature in tooltip | |
| --- | |
| ### 2.2 CSS Architecture | |
| #### File Structure | |
| ``` | |
| styles/ | |
| βββ global.css # resets, tokens, dark/light theme | |
| βββ components/ | |
| β βββ notebook-cell.css | |
| β βββ cell-editor.css | |
| β βββ cell-output.css | |
| β βββ notebook-sidebar.css | |
| β βββ worm-ledger.css | |
| β βββ execution-trace.css | |
| βββ animations.css # keyframe definitions | |
| βββ layout.css # grid, flexbox layouts | |
| βββ theme/ | |
| βββ dark.css | |
| βββ light.css | |
| ``` | |
| #### Design Tokens (CSS Variables) | |
| ```css | |
| /* Color Palette */ | |
| --color-bg-primary: #070b1e; /* dark navy */ | |
| --color-bg-secondary: #0f0f2e; /* lighter navy */ | |
| --color-text-primary: #eef9ff; /* light cyan */ | |
| --color-text-secondary: #a0a7d0; /* muted blue */ | |
| --color-accent-cyan: #7ef9ff; /* bright cyan */ | |
| --color-accent-magenta: #ff79dc; /* magenta */ | |
| --color-accent-gold: #ffe98a; /* gold */ | |
| --color-error: #ff4fd8; /* bright magenta */ | |
| --color-success: #00d9a3; /* teal */ | |
| --color-warning: #ffd86b; /* orange */ | |
| /* Typography */ | |
| --font-mono: 'Fira Code', 'Courier New', monospace; | |
| --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| --font-size-code: 14px; | |
| --font-size-label: 12px; | |
| --font-size-body: 16px; | |
| --line-height-code: 1.6; | |
| /* Spacing */ | |
| --spacing-xs: 4px; | |
| --spacing-sm: 8px; | |
| --spacing-md: 16px; | |
| --spacing-lg: 24px; | |
| --spacing-xl: 32px; | |
| /* Borders & Shadows */ | |
| --border-radius-sm: 4px; | |
| --border-radius-md: 8px; | |
| --border-radius-lg: 16px; | |
| --shadow-sm: 0 2px 4px rgba(0,0,0,0.3); | |
| --shadow-md: 0 8px 24px rgba(0,0,0,0.5); | |
| --shadow-glow: 0 0 20px rgba(126,249,255,0.4); | |
| /* Transitions */ | |
| --transition-fast: 150ms ease-out; | |
| --transition-normal: 300ms ease-out; | |
| --transition-slow: 600ms ease-out; | |
| ``` | |
| #### Component Styles (Example: Cell Editor) | |
| ```css | |
| notebook-cell-editor { | |
| display: block; | |
| position: relative; | |
| background: var(--color-bg-secondary); | |
| border: 1px solid var(--color-accent-cyan); | |
| border-radius: var(--border-radius-md); | |
| overflow: hidden; | |
| } | |
| notebook-cell-editor .editor-wrapper { | |
| display: grid; | |
| grid-template-columns: auto 1fr; | |
| height: 200px; | |
| font-family: var(--font-mono); | |
| font-size: var(--font-size-code); | |
| line-height: var(--line-height-code); | |
| } | |
| notebook-cell-editor .line-numbers { | |
| background: var(--color-bg-primary); | |
| border-right: 1px solid var(--color-accent-cyan); | |
| padding: var(--spacing-sm); | |
| color: var(--color-text-secondary); | |
| user-select: none; | |
| } | |
| notebook-cell-editor .line-number { | |
| display: block; | |
| height: 1.6em; | |
| text-align: right; | |
| opacity: 0.5; | |
| } | |
| notebook-cell-editor textarea { | |
| background: transparent; | |
| color: var(--color-text-primary); | |
| border: none; | |
| outline: none; | |
| padding: var(--spacing-sm); | |
| font-family: inherit; | |
| font-size: inherit; | |
| resize: none; | |
| z-index: 2; | |
| } | |
| notebook-cell-editor .highlights { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| pointer-events: none; | |
| z-index: 1; | |
| overflow: hidden; | |
| } | |
| notebook-cell-editor .highlight-line { | |
| height: 1.6em; | |
| padding: var(--spacing-sm); | |
| font-family: var(--font-mono); | |
| white-space: pre; | |
| word-break: break-all; | |
| } | |
| /* Syntax highlighting */ | |
| .hljs-keyword { color: var(--color-accent-magenta); } | |
| .hljs-string { color: var(--color-success); } | |
| .hljs-number { color: var(--color-warning); } | |
| .hljs-function { color: var(--color-accent-cyan); } | |
| .hljs-comment { color: var(--color-text-secondary); opacity: 0.7; } | |
| /* Focus state */ | |
| notebook-cell-editor:focus-within { | |
| box-shadow: 0 0 12px var(--color-accent-cyan); | |
| border-color: var(--color-accent-cyan); | |
| } | |
| ``` | |
| --- | |
| ## 3. ANIMATION ENGINE | |
| ### 3.1 Animation Categories | |
| #### A. Execution Flow Animation | |
| **Purpose**: Visualize data flowing between cells | |
| **Implementation**: Canvas + requestAnimationFrame | |
| ```javascript | |
| class ExecutionFlowAnimator { | |
| constructor(canvas) { | |
| this.canvas = canvas; | |
| this.ctx = canvas.getContext('2d'); | |
| this.particles = []; // flowing glyphs | |
| this.arrows = []; // connection arrows | |
| this.isAnimating = false; | |
| } | |
| startExecution(fromCell, toCell) { | |
| // Start arrow from fromCell to toCell | |
| // Spawn unicode glyphs flowing along path | |
| // Duration: execution time + 0.5s decay | |
| } | |
| completeExecution(cellId) { | |
| // Show checkmark at cell | |
| // Fade out incoming arrows | |
| // Seal indicator appears | |
| } | |
| animate() { | |
| // Update particle positions | |
| // Render arrows and particles | |
| // Handle collisions/bunching | |
| if (this.isAnimating) requestAnimationFrame(() => this.animate()); | |
| } | |
| } | |
| ``` | |
| **Visual Details**: | |
| - Arrow color: cyan to magenta gradient | |
| - Unicode glyphs: Ξ», Ξ©, β, β, Ο, β (50px size, semi-transparent) | |
| - Speed: 200px/sec | |
| - Trail effect: 0.3s fade tail | |
| --- | |
| #### B. Dependency Graph Animation | |
| **Purpose**: Show data dependencies between cells | |
| **Implementation**: D3.js force-directed graph or custom SVG | |
| ``` | |
| Cell1 ββββββ | |
| βββ Cell3 ββ Cell5 | |
| Cell2 ββββββ€ β | |
| βββ Cell4 βββββ | |
| ``` | |
| **Features**: | |
| - Nodes: circles labeled with cell ID | |
| - Edges: directed arrows | |
| - Hover: highlight path to/from cell | |
| - Click: jump to cell | |
| - Animation: smooth force simulation | |
| **Code Example**: | |
| ```javascript | |
| class DependencyGraphVisualizer { | |
| constructor(svgElement) { | |
| this.svg = d3.select(svgElement); | |
| this.nodes = []; | |
| this.links = []; | |
| this.simulation = null; | |
| } | |
| update(cells, dependencies) { | |
| // Convert cells and dependencies to nodes/links | |
| // Run force simulation | |
| // Bind data and render | |
| // Add hover/click handlers | |
| } | |
| highlightPath(cellId) { | |
| // Dim non-related nodes | |
| // Highlight nodes on dependency path | |
| // Animate edges along path | |
| } | |
| } | |
| ``` | |
| --- | |
| #### C. Receipt Chain Animation | |
| **Purpose**: Visualize hash chain and sealing | |
| **Implementation**: SVG + Canvas for effects | |
| ``` | |
| Hash1 βββ [blake3] βββ Hash2 βββ [blake3] βββ Hash3 | |
| β β β | |
| [Ed25519] [Ed25519] [Ed25519] | |
| β β β | |
| Sig1 Sig2 Sig3 [SEALED β] | |
| ``` | |
| **Animation Sequence**: | |
| 1. New receipt appears (fade in) | |
| 2. Hash scrolls left-to-right (1.5s) | |
| 3. Blake3 badge spins (rotation 360Β°) | |
| 4. Ed25519 signature glows (pulse effect) | |
| 5. Sealed checkmark animates (scale up) | |
| **Code Structure**: | |
| ```javascript | |
| class ReceiptChainAnimator { | |
| constructor(containerElement) { | |
| this.container = containerElement; | |
| this.receipts = []; // Array of ReceiptRecord | |
| } | |
| addReceipt(receipt) { | |
| // Create receipt element | |
| // Animate fade-in + scale | |
| // Animate hash scroll | |
| // Animate signature glow | |
| // Add to receipts array | |
| } | |
| animateHash(hashElement, hashValue) { | |
| // Scroll hash characters left-to-right | |
| // Duration: 1.5s | |
| // Monospace font, cyan color | |
| } | |
| animateSignature(sigElement) { | |
| // Glow pulse effect | |
| // Duration: 2s, repeat | |
| // Color: gold to magenta | |
| } | |
| } | |
| ``` | |
| --- | |
| #### D. Unicode Glyph Animation | |
| **Purpose**: Flowing mathematical symbols through cells | |
| **Implementation**: Canvas particles | |
| ```javascript | |
| class UnicodeGlyphAnimator { | |
| constructor(canvas) { | |
| this.canvas = canvas; | |
| this.ctx = canvas.getContext('2d'); | |
| this.glyphs = ['Ξ»', 'Ξ©', 'β', 'β', 'Ο', 'β', 'β', 'β', 'β·', 'β']; | |
| } | |
| spawnGlyph(startCell, endCell, duration) { | |
| // Create glyph particle | |
| // Interpolate position from start to end over duration | |
| // Fade in/out at edges | |
| // Rotate glyph during flight | |
| } | |
| render() { | |
| // Clear canvas | |
| // For each active glyph: | |
| // - Compute position | |
| // - Rotate based on velocity | |
| // - Draw with alpha | |
| // - Remove if expired | |
| } | |
| } | |
| ``` | |
| **Glyph Semantics**: | |
| - Ξ»: lambda (function) | |
| - Ξ©: omega (loop) | |
| - β: summation | |
| - β: XOR operation | |
| - Ο: golden ratio | |
| - β: infinity | |
| - β: bidirectional | |
| - β: reversible | |
| --- | |
| #### E. Cryptographic Seal Animation | |
| **Purpose**: Rotating WORM seal effect on sealed cells | |
| **Implementation**: Canvas 2D transforms | |
| ```javascript | |
| class CryptoSealAnimator { | |
| constructor(canvas, receiptId) { | |
| this.canvas = canvas; | |
| this.receiptId = receiptId; | |
| this.rotation = 0; | |
| } | |
| animate() { | |
| this.ctx.save(); | |
| // Draw rotating circle | |
| this.ctx.translate(centerX, centerY); | |
| this.ctx.rotate(this.rotation); | |
| this.ctx.strokeStyle = 'rgba(255,233,138,0.8)'; | |
| this.ctx.lineWidth = 3; | |
| this.ctx.beginPath(); | |
| this.ctx.arc(0, 0, 30, 0, Math.PI * 2); | |
| this.ctx.stroke(); | |
| // Draw scrolling hash | |
| this.ctx.rotate(-this.rotation); | |
| this.ctx.font = '12px monospace'; | |
| this.ctx.fillStyle = 'rgba(255,233,138,0.6)'; | |
| this.ctx.textAlign = 'center'; | |
| this.ctx.fillText(this.receiptId.slice(0, 8) + '...', 0, -45); | |
| // Draw glow effect | |
| this.ctx.shadowColor = 'rgba(255,233,138,0.4)'; | |
| this.ctx.shadowBlur = 20; | |
| this.ctx.arc(0, 0, 35, 0, Math.PI * 2); | |
| this.ctx.stroke(); | |
| this.ctx.restore(); | |
| this.rotation += 0.05; // ~2s per rotation | |
| requestAnimationFrame(() => this.animate()); | |
| } | |
| } | |
| ``` | |
| **Visual Appearance**: | |
| - Rotating gold circle | |
| - Blake3 hash scrolls around perimeter | |
| - Inner glow pulses | |
| - Semi-transparent overlay on cell | |
| --- | |
| ### 3.2 Keyframe Definitions (CSS) | |
| ```css | |
| @keyframes fadeInScale { | |
| from { | |
| opacity: 0; | |
| transform: scale(0.95); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: scale(1); | |
| } | |
| } | |
| @keyframes glowPulse { | |
| 0%, 100% { | |
| box-shadow: 0 0 10px var(--color-accent-cyan); | |
| } | |
| 50% { | |
| box-shadow: 0 0 30px var(--color-accent-magenta); | |
| } | |
| } | |
| @keyframes scrollHash { | |
| from { | |
| transform: translateX(-100%); | |
| } | |
| to { | |
| transform: translateX(100%); | |
| } | |
| } | |
| @keyframes rotateSeal { | |
| from { | |
| transform: rotate(0deg); | |
| } | |
| to { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| @keyframes flowingGlyph { | |
| 0% { | |
| opacity: 0; | |
| transform: translate(0, 0); | |
| } | |
| 10% { | |
| opacity: 1; | |
| } | |
| 90% { | |
| opacity: 1; | |
| } | |
| 100% { | |
| opacity: 0; | |
| transform: translate(var(--distance), 0); | |
| } | |
| } | |
| @keyframes highlightBorder { | |
| 0%, 100% { | |
| border-color: var(--color-accent-cyan); | |
| } | |
| 50% { | |
| border-color: var(--color-accent-magenta); | |
| } | |
| } | |
| ``` | |
| --- | |
| ## 4. STATE MANAGEMENT | |
| ### 4.1 Reactive State Architecture | |
| **Pattern**: Simple event emitter + reactive getters | |
| ```typescript | |
| class NotebookStore { | |
| private cells: Map<string, Cell> = new Map(); | |
| private receipts: ReceiptRecord[] = []; | |
| private listeners: Map<string, Set<Callback>> = new Map(); | |
| // Observable properties (via Proxy) | |
| public state: { | |
| cells: Cell[]; | |
| receipts: ReceiptRecord[]; | |
| executionQueue: ExecutionTask[]; | |
| selectedCellId: string | null; | |
| }; | |
| subscribe(event: string, callback: Callback) { | |
| if (!this.listeners.has(event)) { | |
| this.listeners.set(event, new Set()); | |
| } | |
| this.listeners.get(event)!.add(callback); | |
| return () => this.listeners.get(event)!.delete(callback); | |
| } | |
| emit(event: string, data?: any) { | |
| this.listeners.get(event)?.forEach(cb => cb(data)); | |
| } | |
| // Mutations | |
| addCell(cell: Cell) { | |
| this.cells.set(cell.id, cell); | |
| this.emit('cell:created', cell); | |
| } | |
| updateCellOutput(cellId: string, output: CellOutput) { | |
| const cell = this.cells.get(cellId); | |
| if (cell) { | |
| cell.output = output; | |
| this.emit('cell:updated', { cellId, output }); | |
| } | |
| } | |
| addReceipt(receipt: ReceiptRecord) { | |
| this.receipts.push(receipt); | |
| this.emit('receipt:sealed', receipt); | |
| } | |
| // Queries | |
| getCellDependencies(cellId: string): string[] { | |
| const cell = this.cells.get(cellId); | |
| return cell?.dependsOn || []; | |
| } | |
| getDependencyGraph(): CellDependency[] { | |
| return Array.from(this.cells.values()).flatMap(cell => | |
| cell.dependsOn.map(depId => ({ from: depId, to: cell.id })) | |
| ); | |
| } | |
| getExecutionOrder(): string[] { | |
| // Topological sort of dependency graph | |
| const visited = new Set<string>(); | |
| const order: string[] = []; | |
| const visit = (id: string) => { | |
| if (visited.has(id)) return; | |
| visited.add(id); | |
| const cell = this.cells.get(id); | |
| cell?.dependsOn.forEach(depId => visit(depId)); | |
| order.push(id); | |
| }; | |
| this.cells.forEach((_, id) => visit(id)); | |
| return order; | |
| } | |
| } | |
| // Global singleton | |
| export const notebook = new NotebookStore(); | |
| ``` | |
| ### 4.2 Component State Binding | |
| **Pattern**: Web Components with reactive properties | |
| ```typescript | |
| class NotebookCell extends HTMLElement { | |
| private _state: Cell = null; | |
| private unsubscribe: (() => void)[] = []; | |
| connectedCallback() { | |
| // Subscribe to cell updates | |
| this.unsubscribe.push( | |
| notebook.subscribe('cell:updated', (data) => { | |
| if (data.cellId === this._state.id) { | |
| this.render(); | |
| } | |
| }) | |
| ); | |
| // Initial render | |
| this.render(); | |
| } | |
| disconnectedCallback() { | |
| // Clean up subscriptions | |
| this.unsubscribe.forEach(fn => fn()); | |
| } | |
| set state(cell: Cell) { | |
| this._state = cell; | |
| this.render(); | |
| } | |
| get state(): Cell { | |
| return this._state; | |
| } | |
| private render() { | |
| // Update DOM based on state | |
| this.innerHTML = ` | |
| <div class="cell"> | |
| <div class="cell-header"> | |
| ${this._state.type === 'code' ? 'β‘' : 'π'} ${this._state.id} | |
| </div> | |
| <div class="cell-editor"> | |
| ${this._state.source} | |
| </div> | |
| ${this._state.output ? ` | |
| <div class="cell-output"> | |
| ${this.renderOutput(this._state.output)} | |
| </div> | |
| ` : ''} | |
| </div> | |
| `; | |
| } | |
| private renderOutput(output: CellOutput): string { | |
| switch (output.type) { | |
| case 'text': return `<pre>${escapeHtml(output.data)}</pre>`; | |
| case 'table': return this.renderTable(output.data); | |
| case 'math': return `<div class="math">${output.data}</div>`; | |
| case 'error': return `<div class="error">${escapeHtml(output.data)}</div>`; | |
| default: return ''; | |
| } | |
| } | |
| } | |
| customElements.define('notebook-cell', NotebookCell); | |
| ``` | |
| --- | |
| ## 5. INTEGRATION POINTS | |
| ### 5.1 Runtime Communication | |
| **Channel**: WebSocket or HTTP POST | |
| ```typescript | |
| interface ExecutionRequest { | |
| cellId: string; | |
| source: string; | |
| language: Language; | |
| dependencies: string[]; | |
| executorAgent: 'builder' | 'oracle' | 'sentinel'; | |
| } | |
| interface ExecutionResponse { | |
| cellId: string; | |
| status: 'running' | 'success' | 'error' | 'timeout'; | |
| output: CellOutput; | |
| executionTime: number; | |
| receiptId: string; | |
| wormHash: string; | |
| } | |
| class RuntimeBridge { | |
| async executeCell(req: ExecutionRequest): Promise<ExecutionResponse> { | |
| const response = await fetch('/api/execute', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(req), | |
| }); | |
| const data = await response.json() as ExecutionResponse; | |
| // Update notebook state | |
| notebook.updateCellOutput(req.cellId, data.output); | |
| notebook.addReceipt({ | |
| id: data.receiptId, | |
| eventId: req.cellId, | |
| hash: data.wormHash, | |
| timestamp: Date.now(), | |
| }); | |
| return data; | |
| } | |
| async verifyReceipt(receiptId: string): Promise<boolean> { | |
| const response = await fetch(`/api/verify/${receiptId}`); | |
| return response.ok; | |
| } | |
| } | |
| export const runtime = new RuntimeBridge(); | |
| ``` | |
| ### 5.2 Animation Event Hooks | |
| ```typescript | |
| // Listen to execution events and trigger animations | |
| notebook.subscribe('cell:executing', (cellId) => { | |
| executionAnimator.startCell(cellId); | |
| }); | |
| notebook.subscribe('cell:updated', ({ cellId, output }) => { | |
| executionAnimator.completeCell(cellId); | |
| // Update dependent cells' incoming arrows | |
| const deps = notebook.getDependencyGraph(); | |
| deps.filter(d => d.from === cellId).forEach(d => { | |
| executionAnimator.startArrow(cellId, d.to); | |
| }); | |
| }); | |
| notebook.subscribe('receipt:sealed', (receipt) => { | |
| receiptAnimator.addReceipt(receipt); | |
| }); | |
| ``` | |
| --- | |
| ## 6. INTERACTION PATTERNS | |
| ### 6.1 Keyboard Shortcuts | |
| | Shortcut | Action | | |
| |----------|--------| | |
| | `Shift+Enter` | Run current cell | | |
| | `Ctrl+Enter` | Run and advance | | |
| | `Ctrl+S` | Save notebook | | |
| | `Ctrl+Z` / `Ctrl+Shift+Z` | Undo / Redo | | |
| | `Ctrl+/` | Toggle comment | | |
| | `Ctrl+H` | Open find/replace | | |
| | `Ctrl+L` | Focus ledger | | |
| | `Escape` | Exit edit mode | | |
| | `Cmd+Click` | Multi-select cells | | |
| | `Alt+Up/Down` | Move cell | | |
| ### 6.2 Mouse Gestures | |
| - **Drag cell**: Reorder in notebook | |
| - **Hover receipt**: Show full hash in tooltip | |
| - **Right-click cell**: Context menu (delete/clone/move/seal) | |
| - **Hover edge in graph**: Highlight dependency path | |
| - **Click node in graph**: Jump to cell | |
| ### 6.3 Touch Gestures (Mobile) | |
| - **Tap cell**: Enter edit mode | |
| - **Swipe left**: Reveal delete/options | |
| - **Pinch**: Zoom dependency graph | |
| - **Long-press**: Context menu | |
| --- | |
| ## 7. DARK MODE & LIGHT MODE | |
| ### 7.1 Theme Variables | |
| **Dark Theme** (default): | |
| ```css | |
| [data-theme="dark"] { | |
| --color-bg-primary: #070b1e; | |
| --color-bg-secondary: #0f0f2e; | |
| --color-text-primary: #eef9ff; | |
| --color-accent-cyan: #7ef9ff; | |
| } | |
| ``` | |
| **Light Theme**: | |
| ```css | |
| [data-theme="light"] { | |
| --color-bg-primary: #f8f9fa; | |
| --color-bg-secondary: #ffffff; | |
| --color-text-primary: #1a1a2e; | |
| --color-accent-cyan: #0066cc; | |
| } | |
| ``` | |
| ### 7.2 Theme Switching | |
| ```typescript | |
| class ThemeManager { | |
| static set(theme: 'dark' | 'light') { | |
| document.documentElement.setAttribute('data-theme', theme); | |
| localStorage.setItem('theme', theme); | |
| // Notify all listeners | |
| window.dispatchEvent(new CustomEvent('theme-changed', { detail: theme })); | |
| } | |
| static get(): 'dark' | 'light' { | |
| return localStorage.getItem('theme') as any || 'dark'; | |
| } | |
| static auto() { | |
| const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; | |
| this.set(isDark ? 'dark' : 'light'); | |
| } | |
| } | |
| ``` | |
| --- | |
| ## 8. PERFORMANCE OPTIMIZATION | |
| ### 8.1 Virtualization | |
| For notebooks with 100+ cells: | |
| - Only render visible cells in viewport | |
| - Use `IntersectionObserver` to detect visibility | |
| - Keep execution trace canvas at fixed size | |
| ```typescript | |
| class VirtualNotebook { | |
| constructor(container: HTMLElement) { | |
| this.container = container; | |
| this.visibleRange = { start: 0, end: 10 }; | |
| this.cellHeight = 200; | |
| this.observer = new IntersectionObserver( | |
| (entries) => this.updateVisibleRange(entries), | |
| { rootMargin: '200px' } | |
| ); | |
| } | |
| render(cells: Cell[]) { | |
| const startIdx = Math.max(0, Math.floor(this.container.scrollTop / this.cellHeight) - 2); | |
| const endIdx = Math.min(cells.length, startIdx + 12); | |
| this.container.innerHTML = cells | |
| .slice(startIdx, endIdx) | |
| .map(cell => `<notebook-cell cell-id="${cell.id}"></notebook-cell>`) | |
| .join(''); | |
| } | |
| } | |
| ``` | |
| ### 8.2 Canvas Optimization | |
| - Use `OffscreenCanvas` for animations | |
| - Render only changed regions | |
| - Batch DOM updates | |
| ```javascript | |
| class OptimizedAnimationEngine { | |
| constructor() { | |
| this.offscreen = new OffscreenCanvas(1440, 900); | |
| this.ctx = this.offscreen.getContext('2d'); | |
| this.mainCanvas = document.querySelector('canvas'); | |
| } | |
| render() { | |
| // Clear only changed regions | |
| this.ctx.clearRect(0, 0, 1440, 900); | |
| // Render to offscreen | |
| this.drawParticles(); | |
| this.drawArrows(); | |
| // Transfer to main canvas | |
| const bitmap = this.offscreen.convertToImageBitmap(); | |
| const ctxMain = this.mainCanvas.getContext('2d'); | |
| ctxMain.drawImage(bitmap, 0, 0); | |
| } | |
| } | |
| ``` | |
| --- | |
| ## 9. ACCESSIBILITY | |
| ### 9.1 WCAG 2.1 AA Compliance | |
| - **Semantic HTML**: Use proper heading hierarchy | |
| - **Color Contrast**: All text β₯ 4.5:1 WCAG AA ratio | |
| - **Keyboard Navigation**: All interactive elements keyboard-accessible | |
| - **Screen Reader Support**: ARIA labels on animations, aria-live regions for status | |
| ### 9.2 ARIA Annotations | |
| ```html | |
| <notebook-cell role="region" aria-label="Code cell 1" aria-live="polite"> | |
| <div class="cell-header" aria-label="Cell 1: Execute status"> | |
| β‘ Cell 1 | |
| </div> | |
| <cell-editor aria-label="Code editor"></cell-editor> | |
| <button aria-label="Run cell 1" aria-controls="cell-1-output">βΆ Run</button> | |
| <cell-output id="cell-1-output" aria-live="assertive" aria-atomic="true"></cell-output> | |
| </notebook-cell> | |
| ``` | |
| --- | |
| ## 10. TESTING STRATEGY | |
| ### 10.1 Unit Tests (Vitest) | |
| ```typescript | |
| // test/components/notebook-cell.test.ts | |
| import { describe, it, expect } from 'vitest'; | |
| import { NotebookCell } from '../../src/components/notebook-cell'; | |
| describe('NotebookCell', () => { | |
| it('renders cell with code content', () => { | |
| const cell = document.createElement('notebook-cell'); | |
| cell.state = { | |
| id: 'test-1', | |
| type: 'code', | |
| language: 'python', | |
| source: 'print("hello")', | |
| output: null, | |
| dependsOn: [], | |
| metadata: { createdAt: Date.now() }, | |
| }; | |
| expect(cell.innerHTML).toContain('print("hello")'); | |
| }); | |
| it('displays output when available', () => { | |
| const cell = document.createElement('notebook-cell'); | |
| cell.state = { | |
| id: 'test-2', | |
| type: 'code', | |
| output: { type: 'text', data: 'hello' }, | |
| // ... other props | |
| }; | |
| expect(cell.innerHTML).toContain('hello'); | |
| }); | |
| }); | |
| ``` | |
| ### 10.2 Integration Tests | |
| ```typescript | |
| // test/integration/execution-flow.test.ts | |
| import { notebook, runtime } from '../../src'; | |
| describe('Execution Flow', () => { | |
| it('executes cell and updates receipt ledger', async () => { | |
| const cell: Cell = { | |
| id: 'int-test-1', | |
| type: 'code', | |
| language: 'python', | |
| source: 'x = 42', | |
| output: null, | |
| dependsOn: [], | |
| metadata: { createdAt: Date.now() }, | |
| }; | |
| notebook.addCell(cell); | |
| const response = await runtime.executeCell({ | |
| cellId: 'int-test-1', | |
| source: 'x = 42', | |
| language: 'python', | |
| dependencies: [], | |
| executorAgent: 'builder', | |
| }); | |
| expect(response.status).toBe('success'); | |
| expect(notebook.state.receipts.length).toBe(1); | |
| }); | |
| }); | |
| ``` | |
| --- | |
| ## 11. COMPONENT HIERARCHY | |
| ``` | |
| notebook-container | |
| βββ notebook-header | |
| β βββ notebook-title (editable) | |
| β βββ execution-controls (run/interrupt/save) | |
| β βββ export-menu | |
| β βββ theme-switcher | |
| βββ notebook-toolbar | |
| β βββ language-selector | |
| β βββ cell-add-button | |
| β βββ search-bar | |
| βββ notebook-layout (3-column grid) | |
| β βββ notebook-sidebar | |
| β β βββ notebook-info | |
| β β βββ cell-list (virtualized) | |
| β β βββ dependency-graph-mini | |
| β β βββ trust-policies | |
| β βββ notebook-editor-area | |
| β β βββ execution-trace-overlay (canvas) | |
| β β βββ notebook-cells (virtualized list) | |
| β β βββ notebook-cell (repeated) | |
| β β β βββ cell-header | |
| β β β βββ cell-editor | |
| β β β βββ cell-controls | |
| β β β βββ cell-output | |
| β β βββ cell-separator | |
| β βββ worm-ledger | |
| β βββ ledger-header | |
| β βββ receipt-list (scrollable) | |
| β β βββ receipt-item (repeated) | |
| β β β βββ receipt-header | |
| β β β βββ receipt-details (collapsible) | |
| β β β βββ receipt-actions | |
| β β βββ load-more-button | |
| β βββ ledger-filter | |
| βββ dependency-graph-viewer (modal, full-screen) | |
| β βββ graph-canvas | |
| β βββ graph-legend | |
| β βββ graph-controls | |
| βββ notebook-footer | |
| βββ execution-stats | |
| βββ cell-count | |
| βββ runtime-status | |
| βββ connection-indicator | |
| ``` | |
| --- | |
| ## 12. DEVELOPMENT ROADMAP | |
| ### Phase 1: Foundation (Week 1-2) | |
| - [ ] Web Component definitions (base classes) | |
| - [ ] CSS architecture and design tokens | |
| - [ ] Dark/light theme support | |
| - [ ] Responsive layout (desktop + mobile) | |
| ### Phase 2: Core Components (Week 3-4) | |
| - [ ] Cell editor with syntax highlighting | |
| - [ ] Cell output display (text, table, error) | |
| - [ ] Sidebar with metadata browser | |
| - [ ] WORM ledger viewer | |
| ### Phase 3: Animation Engine (Week 5-6) | |
| - [ ] Execution flow animation (Canvas) | |
| - [ ] Dependency graph visualization (D3) | |
| - [ ] Receipt chain animation | |
| - [ ] Unicode glyph particles | |
| ### Phase 4: State & Integration (Week 7-8) | |
| - [ ] Reactive state management | |
| - [ ] Runtime communication bridge | |
| - [ ] Keyboard/mouse interactions | |
| - [ ] Error handling and recovery | |
| ### Phase 5: Polish & Testing (Week 9-10) | |
| - [ ] Performance optimization (virtualization) | |
| - [ ] Accessibility audit (WCAG 2.1 AA) | |
| - [ ] Unit tests (80%+ coverage) | |
| - [ ] Integration tests | |
| - [ ] End-to-end tests | |
| ### Phase 6: Deployment (Week 11-12) | |
| - [ ] Production build and optimization | |
| - [ ] Performance monitoring | |
| - [ ] Browser compatibility testing | |
| - [ ] Documentation and examples | |
| --- | |
| ## 13. FILE STRUCTURE | |
| ``` | |
| frontend/ | |
| βββ index.html # Entry point | |
| βββ styles/ | |
| β βββ global.css | |
| β βββ variables.css # Design tokens | |
| β βββ animations.css | |
| β βββ layout.css | |
| β βββ theme/ | |
| β β βββ dark.css | |
| β β βββ light.css | |
| β βββ components/ | |
| β βββ notebook-cell.css | |
| β βββ cell-editor.css | |
| β βββ cell-output.css | |
| β βββ notebook-sidebar.css | |
| β βββ worm-ledger.css | |
| β βββ execution-trace.css | |
| βββ src/ | |
| β βββ index.ts # App entry | |
| β βββ store/ | |
| β β βββ notebook.store.ts # Reactive state | |
| β β βββ theme.store.ts | |
| β βββ components/ | |
| β β βββ notebook-container.ts | |
| β β βββ notebook-cell.ts | |
| β β βββ cell-editor.ts | |
| β β βββ cell-output.ts | |
| β β βββ notebook-sidebar.ts | |
| β β βββ worm-ledger.ts | |
| β β βββ notebook-header.ts | |
| β βββ animation/ | |
| β β βββ execution-flow.ts | |
| β β βββ dependency-graph.ts | |
| β β βββ receipt-chain.ts | |
| β β βββ unicode-glyphs.ts | |
| β β βββ crypto-seal.ts | |
| β βββ services/ | |
| β β βββ runtime-bridge.ts # API client | |
| β β βββ theme-manager.ts | |
| β β βββ keyboard-handler.ts | |
| β βββ types/ | |
| β β βββ cell.ts | |
| β β βββ receipt.ts | |
| β β βββ execution.ts | |
| β βββ utils/ | |
| β βββ dom-utils.ts | |
| β βββ animation-utils.ts | |
| β βββ format-utils.ts | |
| βββ test/ | |
| β βββ components/ | |
| β β βββ notebook-cell.test.ts | |
| β βββ animation/ | |
| β β βββ execution-flow.test.ts | |
| β βββ integration/ | |
| β β βββ execution-flow.test.ts | |
| β βββ e2e/ | |
| β βββ notebook.spec.ts | |
| βββ vitest.config.ts | |
| βββ tsconfig.json | |
| βββ package.json | |
| βββ README.md | |
| ``` | |
| --- | |
| ## 14. KEY DESIGN DECISIONS | |
| | Decision | Rationale | | |
| |----------|-----------| | |
| | **Web Components** (not React/Vue) | Lightweight, framework-agnostic, WASM-ready | | |
| | **Canvas animations** | Better performance than DOM for particle systems | | |
| | **CSS Variables for theming** | Dynamic theme switching without JS overhead | | |
| | **D3 for graphs** | Mature, battle-tested force-directed layout | | |
| | **Event emitter pattern** | Simple, explicit, easy to debug | | |
| | **Virtualization for large notebooks** | Handle 1000+ cells efficiently | | |
| | **Dark mode primary** | Sovereign aesthetic, reduced eye strain | | |
| | **WCAG 2.1 AA from start** | Accessibility is not an afterthought | | |
| | **TypeScript throughout** | Type safety, better IDE support, self-documenting | | |
| | **Monospace Unicode symbols** | Aesthetic + semantic meaning for glyphs | | |
| --- | |
| ## 15. SUCCESS METRICS | |
| - **Performance**: Animation frame rate β₯ 60fps (Cell 6 animation engine) | |
| - **Responsiveness**: Cell execution trace appears within 100ms | |
| - **Accessibility**: 100% keyboard navigable, 0 WCAG AA violations | |
| - **Code Quality**: β₯80% test coverage, <10 code smells | |
| - **User Experience**: Task completion time β€ 5 minutes for common workflows | |
| --- | |
| ## APPENDIX A: SVG Animation Examples | |
| See: `/WORM.svg` and `rowm_worm_morphing_notebook.svg` for reference animations | |
| ### WORM.svg Features: | |
| - Rotating dashed border frame | |
| - Animated Unicode streams (bidirectional) | |
| - Particle glyph markers | |
| - Notebook container with grid background | |
| - Sealed cryptographic stamp | |
| - Matrix-like aesthetic | |
| --- | |
| ## APPENDIX B: Integration with Isomorphic Shift Layer | |
| The UI bridges the notebook to the formal isomorphic shift system: | |
| ``` | |
| User Input (EmojiCode cell) | |
| β | |
| M1: SurfaceInstruction β CanonicalInstruction [UI renders this] | |
| β | |
| M2: CanonicalInstruction β LogicTerm [UI queries from store] | |
| β | |
| M3: AuthorizedLogicDecision β RuntimeCommand [Animation triggers] | |
| β | |
| EXECUTION [Receipt LED indicator shows "running"] | |
| β | |
| M5: ExecutionEvent β LogicEventFact [Animation completes] | |
| β | |
| M6: ExecutionEvent β ReceiptRecord [WORM ledger updates] | |
| β | |
| User sees: Output + Receipt + Seal animation | |
| ``` | |
| The UI is **purely presentational** β all authority/verification/sealing happens in the backend Prolog + Rust layer. | |
| --- | |
| **Document Finalized**: 2026-07-27 14:30 UTC | |
| **Next Review**: After Phase 1 completion | |