harden terminal view, add $impeccable design docs, update AGENTS.md

This commit is contained in:
dirtydishes 2026-05-14 18:26:46 -04:00
parent 1089174264
commit 9644e9ceef
10 changed files with 1716 additions and 42 deletions

View file

@ -8,6 +8,8 @@
{"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-1f5","title":"Adapt terminal view for responsive use","description":"Improve the terminal view so it remains usable across desktop, tablet, and small-screen contexts without hiding core workflow functionality.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T22:22:18Z","created_by":"dirtydishes","updated_at":"2026-05-14T22:25:22Z","started_at":"2026-05-14T22:22:25Z","closed_at":"2026-05-14T22:25:22Z","close_reason":"Terminal view adapted for responsive and touch-first contexts; tests and web build passed.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-uhi","title":"Publish terminal turn document to GitHub Pages","description":"Why: the completed turn document should be reachable on the user's GitHub Pages site. What: determine the GitHub Pages publishing path for dirtydishes.github.io, place the terminal hardening turn document at a stable HTML URL, validate the file location, and update beads status for the publishing work.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T22:15:23Z","created_by":"dirtydishes","updated_at":"2026-05-14T22:17:39Z","started_at":"2026-05-14T22:15:34Z","closed_at":"2026-05-14T22:17:39Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-6ri","title":"Harden terminal shell view","description":"Why: the terminal shell needs production hardening for focus visibility, long labels, and ticker entry edge cases so the main workflow remains stable under constrained widths and imperfect input. What: tighten shell semantics and input handling, prevent overflow in the top bar and rail, and add regression tests for the ticker filter normalization path.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T08:56:45Z","created_by":"dirtydishes","updated_at":"2026-05-14T08:58:46Z","started_at":"2026-05-14T08:56:53Z","closed_at":"2026-05-14T08:58:46Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-6ri","title":"Harden terminal shell view","description":"Why: the terminal shell needs production hardening for focus visibility, long labels, and ticker entry edge cases so the main workflow remains stable under constrained widths and imperfect input. What: tighten shell semantics and input handling, prevent overflow in the top bar and rail, and add regression tests for the ticker filter normalization path.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T08:56:45Z","created_by":"dirtydishes","updated_at":"2026-05-14T08:58:46Z","started_at":"2026-05-14T08:56:53Z","closed_at":"2026-05-14T08:58:46Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-dod","title":"Publish terminal audit to GitHub Pages","description":"Why this issue exists and what needs to be done: publish the generated terminal audit HTML to dirtydishes.github.io at /terminal-audit.html so it can be shared publicly.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T08:39:45Z","created_by":"dirtydishes","updated_at":"2026-05-14T08:42:59Z","started_at":"2026-05-14T08:40:02Z","closed_at":"2026-05-14T08:42:59Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-dod","title":"Publish terminal audit to GitHub Pages","description":"Why this issue exists and what needs to be done: publish the generated terminal audit HTML to dirtydishes.github.io at /terminal-audit.html so it can be shared publicly.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T08:39:45Z","created_by":"dirtydishes","updated_at":"2026-05-14T08:42:59Z","started_at":"2026-05-14T08:40:02Z","closed_at":"2026-05-14T08:42:59Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-dxu","title":"Document terminal audit findings as HTML","description":"Why this issue exists and what needs to be done: capture the completed terminal view audit findings in a user-readable HTML document under docs/ with the full score summary and all detailed findings preserved.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T08:32:22Z","created_by":"dirtydishes","updated_at":"2026-05-14T08:34:57Z","started_at":"2026-05-14T08:32:30Z","closed_at":"2026-05-14T08:34:57Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-dxu","title":"Document terminal audit findings as HTML","description":"Why this issue exists and what needs to be done: capture the completed terminal view audit findings in a user-readable HTML document under docs/ with the full score summary and all detailed findings preserved.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T08:32:22Z","created_by":"dirtydishes","updated_at":"2026-05-14T08:34:57Z","started_at":"2026-05-14T08:32:30Z","closed_at":"2026-05-14T08:34:57Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}

210
.impeccable/design.json Normal file
View file

@ -0,0 +1,210 @@
{
"schemaVersion": 2,
"generatedAt": "2026-05-14T08:06:45Z",
"title": "Design System: Islandflow Terminal",
"extensions": {
"colorMeta": {
"bg-core": {
"role": "neutral",
"displayName": "Command Black",
"canonical": "#06080b",
"tonalRamp": ["#050608", "#0b0f14", "#111821", "#1a2430", "#263445", "#394a5f", "#6f8095", "#dce3ea"]
},
"bg-pane": {
"role": "neutral",
"displayName": "Panel Graphite",
"canonical": "#111820",
"tonalRamp": ["#0b0f14", "#121922", "#1a2430", "#253444", "#314658", "#4a6076", "#7b90a5", "#dbe3ec"]
},
"text-primary": {
"role": "neutral",
"displayName": "Data Ink",
"canonical": "#e6edf4",
"tonalRamp": ["#1c232b", "#2a333e", "#3a4654", "#4f6072", "#6a7f93", "#92a4b5", "#bcc8d3", "#e6edf4"]
},
"signal-amber": {
"role": "primary",
"displayName": "Signal Amber",
"canonical": "#f5a623",
"tonalRamp": ["#2f1f06", "#5b3c0b", "#865913", "#b5761a", "#d89220", "#f5a623", "#f8c069", "#fce3bc"]
},
"confirm-green": {
"role": "tertiary",
"displayName": "Confirm Green",
"canonical": "#25c17a",
"tonalRamp": ["#062716", "#0d4b2a", "#13703f", "#1a9554", "#20ae6a", "#25c17a", "#6ed5a6", "#c9f1df"]
},
"risk-red": {
"role": "tertiary",
"displayName": "Risk Red",
"canonical": "#ff6b5f",
"tonalRamp": ["#320d0a", "#611914", "#91261f", "#bf362f", "#e04f48", "#ff6b5f", "#ff9b93", "#ffd9d5"]
},
"info-blue": {
"role": "secondary",
"displayName": "Info Blue",
"canonical": "#4da3ff",
"tonalRamp": ["#0a1f33", "#143c61", "#1f5a8f", "#2b78bd", "#3a91e0", "#4da3ff", "#8cc4ff", "#d8ebff"]
}
},
"typographyMeta": {
"display": {
"displayName": "Display",
"purpose": "Primary wayfinding headers and route-level titles."
},
"body": {
"displayName": "Body",
"purpose": "Default transactional and descriptive copy in panes and controls."
},
"label": {
"displayName": "Label/Mono",
"purpose": "Data labels, numeric cells, chips, and compact control text."
}
},
"shadows": [
{
"name": "overlay-lift",
"value": "0 24px 60px rgba(0, 0, 0, 0.42)",
"purpose": "Filter popover separation from live content."
},
{
"name": "drawer-lift",
"value": "0 24px 70px rgba(0, 0, 0, 0.5)",
"purpose": "Right-side detail drawer emphasis."
},
{
"name": "tooltip-lift",
"value": "0 16px 40px rgba(0, 0, 0, 0.45)",
"purpose": "Transient metadata tooltip depth."
}
],
"motion": [
{
"name": "fast-state",
"value": "150ms ease",
"purpose": "Button and hover state transitions."
},
{
"name": "focus-rail",
"value": "160ms ease",
"purpose": "Input underline and glow transitions."
},
{
"name": "count-reveal",
"value": "180ms ease",
"purpose": "Missed counter width/position reveal."
}
],
"breakpoints": [
{
"name": "lg",
"value": "1180px"
},
{
"name": "md",
"value": "980px"
},
{
"name": "sm",
"value": "720px"
}
]
},
"components": [
{
"name": "Terminal Action Button",
"kind": "button",
"refersTo": "button-base",
"description": "Default compact control for tape actions and utility toggles.",
"html": "<button class=\"ds-btn\">Pause Tape</button>",
"css": ".ds-btn { border: 1px solid rgba(255,255,255,0.08); border-radius: 8px; padding: 8px 10px; background: rgba(255,255,255,0.03); color: #e6edf4; font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; cursor: pointer; transition: border-color 150ms ease, background 150ms ease, color 150ms ease; } .ds-btn:hover { border-color: rgba(255,177,48,0.35); background: rgba(245,166,35,0.08); color: #ffd89a; } .ds-btn:focus-visible { outline: none; border-color: rgba(255,177,48,0.45); box-shadow: 0 0 0 2px rgba(245,166,35,0.2); } .ds-btn:active { background: rgba(245,166,35,0.12); }"
},
{
"name": "Rail Navigation Link",
"kind": "nav",
"refersTo": "nav-link",
"description": "Primary route selector in the left terminal rail.",
"html": "<a class=\"ds-nav-link\" href=\"#\">Signals</a>",
"css": ".ds-nav-link { display: inline-block; padding: 12px 14px; border: 1px solid transparent; border-radius: 10px; color: #90a0b2; background: transparent; font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; text-decoration: none; transition: border-color 150ms ease, background 150ms ease, color 150ms ease; } .ds-nav-link:hover { border-color: rgba(255,255,255,0.08); background: rgba(255,255,255,0.03); color: #e6edf4; } .ds-nav-link:focus-visible { outline: none; border-color: rgba(255,177,48,0.35); } .ds-nav-link.ds-nav-link-active { border-color: rgba(255,177,48,0.35); background: linear-gradient(90deg, rgba(245,166,35,0.12), rgba(245,166,35,0.04)); color: #e6edf4; }"
},
{
"name": "Filter Underline Input",
"kind": "input",
"refersTo": "pane-surface",
"description": "Global tape filter field with amber under-rail focus behavior.",
"html": "<label class=\"ds-filter\"><span class=\"ds-filter-label\">Contract Filter</span><span class=\"ds-filter-line\"><input class=\"ds-filter-input\" placeholder=\"Type symbol, contract, or expiry\" /></span></label>",
"css": ".ds-filter { display: inline-flex; flex-direction: column; gap: 4px; min-width: 260px; } .ds-filter-label { color: #6e7b8c; font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase; } .ds-filter-line { position: relative; display: block; padding-bottom: 6px; } .ds-filter-line::before { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: linear-gradient(90deg, rgba(245,166,35,0.88), rgba(245,166,35,0.14)); } .ds-filter-line::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: linear-gradient(90deg, rgba(255,216,154,0.98), rgba(245,166,35,0.92)); transform: scaleX(0.2); transform-origin: left center; opacity: 0; transition: transform 160ms ease, opacity 160ms ease, box-shadow 160ms ease; } .ds-filter-input { width: 100%; border: 0; background: transparent; color: #e6edf4; font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.92rem; font-weight: 600; letter-spacing: 0.01em; } .ds-filter-input::placeholder { color: rgba(193,203,224,0.58); font-size: 0.86rem; } .ds-filter-input:focus-visible { outline: none; } .ds-filter:focus-within .ds-filter-label { color: #ffd89a; } .ds-filter:focus-within .ds-filter-line::after { transform: scaleX(1); opacity: 1; box-shadow: 0 0 18px rgba(245,166,35,0.34); }"
},
{
"name": "Semantic Status Chip",
"kind": "chip",
"refersTo": "status-chip",
"description": "Pill used for direction, severity, and condition tags with explicit label text.",
"html": "<span class=\"ds-chip ds-chip-risk\">Bearish</span>",
"css": ".ds-chip { display: inline-flex; align-items: center; padding: 3px 8px; border-radius: 999px; border: 1px solid rgba(255,255,255,0.08); font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; } .ds-chip-neutral { background: rgba(77,163,255,0.14); border-color: rgba(77,163,255,0.26); color: #bddcff; } .ds-chip-good { background: rgba(37,193,122,0.12); border-color: rgba(37,193,122,0.34); color: #98f0c0; } .ds-chip-risk { background: rgba(255,107,95,0.14); border-color: rgba(255,107,95,0.34); color: #ffc3bd; }"
},
{
"name": "Terminal Pane",
"kind": "card",
"refersTo": "pane-surface",
"description": "Default data region container for tape, alerts, and chart modules.",
"html": "<section class=\"ds-pane\"><header class=\"ds-pane-head\"><h3 class=\"ds-pane-title\">Flow Packets</h3><button class=\"ds-btn-mini\">Refresh</button></header><div class=\"ds-pane-body\">Pane content</div></section>",
"css": ".ds-pane { border: 1px solid rgba(255,255,255,0.08); border-radius: 14px; background: linear-gradient(180deg, rgba(255,255,255,0.03), transparent 40%), #111820; color: #e6edf4; overflow: hidden; } .ds-pane-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 18px; border-bottom: 1px solid rgba(255,255,255,0.08); background: rgba(255,255,255,0.02); } .ds-pane-title { margin: 0; font-family: var(--font-display, Quantico, sans-serif); font-size: 1rem; letter-spacing: 0.08em; text-transform: uppercase; } .ds-pane-body { padding: 16px 18px 18px; } .ds-btn-mini { border: 1px solid rgba(255,255,255,0.08); border-radius: 8px; padding: 8px 10px; background: rgba(255,255,255,0.03); color: #e6edf4; font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; cursor: pointer; transition: border-color 150ms ease, background 150ms ease; } .ds-btn-mini:hover { border-color: rgba(255,177,48,0.35); background: rgba(245,166,35,0.08); }"
},
{
"name": "Flow Filter Popover Surface",
"kind": "custom",
"refersTo": "pane-surface",
"description": "Floating filter inspector with dedicated overlay elevation.",
"html": "<aside class=\"ds-popover\"><h4 class=\"ds-popover-title\">Flow Filters</h4><p class=\"ds-popover-copy\">Changes apply immediately.</p></aside>",
"css": ".ds-popover { width: min(420px, 90vw); border: 1px solid rgba(245,166,35,0.24); border-radius: 18px; padding: 16px; background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02)), rgba(11,16,22,0.92); box-shadow: 0 24px 60px rgba(0,0,0,0.42), inset 0 1px 0 rgba(255,255,255,0.04); backdrop-filter: blur(18px); color: #e6edf4; } .ds-popover-title { margin: 0; font-family: var(--font-display, Quantico, sans-serif); font-size: 0.9rem; letter-spacing: 0.12em; text-transform: uppercase; } .ds-popover-copy { margin: 6px 0 0; color: #90a0b2; font-family: var(--font-sans, 'IBM Plex Sans', sans-serif); font-size: 0.78rem; }"
}
],
"narrative": {
"northStar": "The Evidence Console",
"overview": "Islandflow's interface behaves like an investigation instrument, not a presentation layer. The system is tuned for fast read accuracy under volatility: hierarchy is built from contrast, casing, and spacing cadence rather than decorative effects.\n\nThe visual atmosphere is dark and controlled, with amber used as a directional signal rather than ambient decoration. Surfaces are compact and information-dense, but each zone is explicit about purpose so the user can move from detection to validation without losing context.\n\nThis system explicitly rejects the anti-references in PRODUCT.md: no meme-stock hype aesthetics, no generic SaaS card fog, and no Bloomberg cosplay density unless density is earning its keep with decision value.",
"keyCharacteristics": [
"Operational contrast over ornamental contrast.",
"Dense layout with stable rhythm.",
"Accent color treated as scarce signal.",
"Monospace-assisted precision for time, numeric, and status data.",
"Readability preserved during bursty live updates."
],
"rules": [
{
"name": "The Signal Scarcity Rule",
"body": "Amber is a control and attention signal, not a wash. Keep it concentrated on actions, state edges, and critical counters.",
"section": "colors"
},
{
"name": "The Semantic Color Rule",
"body": "Red and green never stand alone for meaning. Every directional or severity cue must include text, shape, or positional confirmation.",
"section": "colors"
},
{
"name": "The Instrument Label Rule",
"body": "Labels are short, uppercase, and spaced. They identify system state fast, without narrative phrasing.",
"section": "typography"
},
{
"name": "The Flat-By-Default Rule",
"body": "If a surface is not floating over active workflow content, it does not get shadow lift.",
"section": "elevation"
}
],
"dos": [
"Do keep status and direction semantic with both color and text labels (`severity-high`, `direction-bullish`, explicit words).",
"Do preserve compact control density (`8px-12px` padding range) so investigation actions stay within a short scan path.",
"Do use amber as a sparse decision signal for active controls, focus rails, and key counters.",
"Do keep overlays visually separated with dedicated shadow roles while leaving primary panes flat.",
"Do design live updates to avoid flashing, excessive animation, and layout shifts during high-volume periods."
],
"donts": [
"Don't make Islandflow feel like a meme-stock or finfluencer trading app with hype, gamification, urgency theater, or promotional calls to action.",
"Don't make Islandflow feel like a generic SaaS analytics dashboard with decorative gradients, vague card stacks, and non-actionable vanity metrics.",
"Don't make Islandflow feel like Bloomberg-style visual density used as aesthetic cosplay instead of as a genuinely useful information structure.",
"Don't rely on red/green alone for directional meaning or severity.",
"Don't use colored side-stripe accents on rows/cards as the primary signifier; use complete semantic chips and labels instead."
]
}
}

106
AGENTS.md
View file

@ -69,17 +69,103 @@ Working style that avoids common problems here:
- Keep `.env` aligned with `.env.example`; adapters default to synthetic modes for local development. - Keep `.env` aligned with `.env.example`; adapters default to synthetic modes for local development.
- Dev runners persist child PID state in `.tmp/`; if a previous run crashed, restart via the standard `bun run dev*` commands so stale processes are cleaned up. - Dev runners persist child PID state in `.tmp/`; if a previous run crashed, restart via the standard `bun run dev*` commands so stale processes are cleaned up.
Always do the following when you finish a task and make a commit: ## Required Turn Documentation
At the end of every completed implementation task, before final handoff, create a user-readable HTML document describing the work.
This documentation is mandatory whenever code, configuration, tests, or project files were changed.
### Location
Save the document in:
```text
docs/turns/
```
Use a clear timestamped filename:
```text
docs/turns/YYYY-MM-DD-short-task-name.html
```
Example:
```text
docs/turns/2026-05-14-add-market-replay-controls.html
```
### Format
Use the impeccable skill to structure the document as clean, readable HTML.
If the impeccable skill is unavailable, still create a well-structured standalone HTML file with:
- A concise summary at the top
- A detailed explanation of what changed
- Relevant context or background
- Specific code snippets or examples when helpful
- Issues, limitations, tradeoffs, or mitigations
- Validation performed, including tests, builds, linters, or manual checks
- Any remaining follow-up work, with corresponding Beads issue IDs when applicable
### Required Sections
Each turn document must include these sections:
1. **Summary**
2. **Changes Made**
3. **Context**
4. **Important Implementation Details**
5. **Validation**
6. **Issues, Limitations, and Mitigations**
7. **Follow-up Work**
### Completion Rule
A task is not complete until:
1. The Beads workflow is updated
2. The turn document is created in `docs/turns`
3. Relevant quality gates have passed or failures are documented
4. Changes are committed
5. `bd dolt push` succeeds
6. `git push` succeeds
7. `git status` shows the branch is up to date with origin
For trivial changes, the document may be brief, but it must still exist and clearly explain what changed and how it was validated.
## Plan Mode Documentation
When working in plan mode, do not modify implementation files.
At the end of plan mode, provide a concise summary of the plan and ask the user whether they want to proceed with implementation.
If the user asks to save the plan, create a user-readable HTML plan document in:
```text
docs/plans/
```
Use a clear timestamped filename:
```text
docs/plans/YYYY-MM-DD-short-plan-name.html
```
The plan document should be labeled clearly as a plan and should include:
1. **Plan Summary**
2. **Goals**
3. **Proposed Changes**
4. **Relevant Context**
5. **Implementation Steps**
6. **Risks, Limitations, and Mitigations**
7. **Open Questions**
Always do the following when you finish a task, finish the beads workflow and and make a commit:
- Document the changes in a user-readable format - Document the changes in a user-readable format
- Use the impeccable skill to structure the document as HTML - Use the impeccable skill to structure the document as HTML
- Create a clear, concise summary of the changes at the top, followed by a detailed description of the changes, including any relevant context or background as well as specific code snippets or examples. - Create a clear, concise summary of the changes at the top, followed by a detailed description of the changes, including any relevant context or background as well as specific code snippets or examples.
- Note any relevant issues or limitations that were addressed or mitigated by the changes. - Note any relevant issues or limitations that were addressed or mitigated by the changes.
- The document should be stored in the `docs/turns` directory. - The HTML file should be stored in the `docs/turns` directory. It should include the current date and time, as well as a brief explanation of changes. e.g. docs/turns/YYYY-MM-DD-{description}.html
Always do the following when you finish a task and make a commit:
- Give a conscise summary of the plan and the changes made.
- Use the impeccable skill to structure the document as HTML
- Create a clear, concise summary of the changes at the top, followed by a detailed description of the changes, including any relevant context or background as well as specific code snippets or examples.
- Note any relevant issues or limitations that would be addressed or mitigated by the changes.
- The document should be stored in the `docs/plans` directory.
- It should be labeled as a plan with a brief description of the changes.

230
DESIGN.md Normal file
View file

@ -0,0 +1,230 @@
---
name: Islandflow Terminal
description: Evidence-linked market intelligence terminal for real-time and replay investigation
colors:
bg-core: "#06080b"
bg-elevated: "#0b1016"
bg-pane: "#111820"
bg-pane-2: "#0d141b"
bg-soft: "#ffffff08"
border-subtle: "#ffffff14"
border-accent: "#ffb13059"
text-primary: "#e6edf4"
text-dim: "#90a0b2"
text-faint: "#6e7b8c"
signal-amber: "#f5a623"
signal-amber-soft: "#f5a6231f"
confirm-green: "#25c17a"
confirm-green-soft: "#25c17a1f"
risk-red: "#ff6b5f"
risk-red-soft: "#ff6b5f24"
info-blue: "#4da3ff"
info-blue-soft: "#4da3ff24"
typography:
display:
fontFamily: "Quantico, sans-serif"
fontSize: "clamp(2rem, 3vw, 2.8rem)"
fontWeight: 700
lineHeight: 1.05
letterSpacing: "0.08em"
body:
fontFamily: "IBM Plex Sans, sans-serif"
fontSize: "0.92rem"
fontWeight: 400
lineHeight: 1.45
label:
fontFamily: "IBM Plex Mono, monospace"
fontSize: "0.72rem"
fontWeight: 600
lineHeight: 1.2
letterSpacing: "0.12em"
rounded:
sm: "8px"
md: "10px"
lg: "12px"
xl: "14px"
pill: "999px"
spacing:
xs: "4px"
sm: "8px"
md: "12px"
lg: "16px"
xl: "24px"
components:
button-base:
backgroundColor: "{colors.bg-soft}"
textColor: "{colors.text-primary}"
typography: "{typography.label}"
rounded: "{rounded.sm}"
padding: "8px 10px"
button-active:
backgroundColor: "{colors.signal-amber-soft}"
textColor: "{colors.signal-amber}"
typography: "{typography.label}"
rounded: "{rounded.sm}"
padding: "8px 10px"
nav-link:
backgroundColor: "{colors.bg-core}"
textColor: "{colors.text-dim}"
typography: "{typography.label}"
rounded: "{rounded.md}"
padding: "12px 14px"
nav-link-active:
backgroundColor: "{colors.signal-amber-soft}"
textColor: "{colors.text-primary}"
typography: "{typography.label}"
rounded: "{rounded.md}"
padding: "12px 14px"
pane-surface:
backgroundColor: "{colors.bg-pane}"
textColor: "{colors.text-primary}"
rounded: "{rounded.xl}"
padding: "16px 18px"
status-chip:
backgroundColor: "{colors.bg-soft}"
textColor: "{colors.text-primary}"
typography: "{typography.label}"
rounded: "{rounded.pill}"
padding: "3px 8px"
---
# Design System: Islandflow Terminal
## Overview
**Creative North Star: "The Evidence Console"**
Islandflow's interface behaves like an investigation instrument, not a presentation layer. The system is tuned for fast read accuracy under volatility: hierarchy is built from contrast, casing, and spacing cadence rather than decorative effects.
The visual atmosphere is dark and controlled, with amber used as a directional signal rather than ambient decoration. Surfaces are compact and information-dense, but each zone is explicit about purpose so the user can move from detection to validation without losing context.
This system explicitly rejects the anti-references in PRODUCT.md: no meme-stock hype aesthetics, no generic SaaS card fog, and no Bloomberg cosplay density unless density is earning its keep with decision value.
**Key Characteristics:**
- Operational contrast over ornamental contrast.
- Dense layout with stable rhythm.
- Accent color treated as scarce signal.
- Monospace-assisted precision for time, numeric, and status data.
- Readability preserved during bursty live updates.
## Colors
The palette is operational and role-first: neutral cold surfaces carry most of the interface, with amber, green, red, and blue reserved for state and meaning.
### Primary
- **Signal Amber** (`#f5a623`): active controls, focus rails, status emphasis, and live interaction highlights.
### Secondary
- **Info Blue** (`#4da3ff`): replay states, neutral directional tags, and non-critical positive context.
### Tertiary
- **Confirm Green** (`#25c17a`): healthy connectivity and positive directional markers.
- **Risk Red** (`#ff6b5f`): stale/disconnected/error states and bearish risk markers.
### Neutral
- **Command Black** (`#06080b`): base shell and deepest background.
- **Panel Graphite** (`#111820`): primary container surfaces.
- **Elevation Slate** (`#0b1016`): raised or overlay-adjacent planes.
- **Data Ink** (`#e6edf4`): default text on dark surfaces.
- **Support Ink** (`#90a0b2`): secondary labels and metadata.
- **Trace Ink** (`#6e7b8c`): tertiary labels and low-priority framing.
### Named Rules
**The Signal Scarcity Rule.** Amber is a control and attention signal, not a wash. Keep it concentrated on actions, state edges, and critical counters.
**The Semantic Color Rule.** Red and green never stand alone for meaning. Every directional or severity cue must include text, shape, or positional confirmation.
## Typography
**Display Font:** Quantico (fallback: sans-serif)
**Body Font:** IBM Plex Sans (fallback: sans-serif)
**Label/Mono Font:** IBM Plex Mono (fallback: monospace)
**Character:** The pairing is technical and composed. Quantico provides assertive waypoint headings, IBM Plex Sans keeps body copy readable, and IBM Plex Mono anchors temporal/numeric trust.
### Hierarchy
- **Display** (700, `clamp(2rem, 3vw, 2.8rem)`, 1.05): page-level and major section titles.
- **Headline** (700, `1.8rem`, 1.1): rail brand mark and high-salience panel titles.
- **Title** (600, `1rem`, 1.2): pane headings and focused section labels.
- **Body** (400, `0.92rem`, 1.45): default transactional and descriptive copy.
- **Label** (600, `0.72rem`, `0.12em`, uppercase): controls, chips, table headers, and instrumentation micro-labels.
### Named Rules
**The Instrument Label Rule.** Labels are short, uppercase, and spaced. They identify system state fast, without narrative phrasing.
## Elevation
The system is flat by default. Depth is primarily tonal (background and border deltas), with shadows reserved for overlays that require separation from live data.
### Shadow Vocabulary
- **Overlay Lift** (`0 24px 60px rgba(0, 0, 0, 0.42)`): filter popovers and floating control surfaces.
- **Drawer Lift** (`0 24px 70px rgba(0, 0, 0, 0.5)`): detail drawers and deep inspection layers.
- **Tooltip Lift** (`0 16px 40px rgba(0, 0, 0, 0.45)`): short-lived contextual tooltips.
### Named Rules
**The Flat-By-Default Rule.** If a surface is not floating over active workflow content, it does not get shadow lift.
## Components
### Buttons
- **Shape:** compact rounded rectangle (`8px radius`) for standard controls, pill (`999px`) for segment toggles.
- **Primary:** subtle dark fill with bordered edge (`1px`, `rgba(255,255,255,0.08)`), label typography in uppercase mono (`0.72rem`).
- **Active State:** amber-tinted gradient/fill (`rgba(245,166,35,0.18 -> 0.08)`), stronger border and warmer text.
- **Focus/Interaction:** no bounce effects; state transitions stay short (`~150-180ms`) with opacity/color emphasis.
### Chips
- **Style:** pill chips (`999px`) with thin border and semantic soft fill.
- **State:** direction/severity/status chips map to green/red/blue semantic channels with text labels always present.
### Cards / Containers
- **Corner Style:** medium-soft corners (`12px` or `14px`) depending on container prominence.
- **Background:** layered dark surfaces (`#111820`, `#0d141b`) with restrained top-to-bottom sheen.
- **Shadow Strategy:** no default card shadow; only overlays and floating inspectors use lift shadows.
- **Border:** subtle perimeter lines (`rgba(255,255,255,0.08)` baseline).
- **Internal Padding:** primarily `16px-18px` with tighter inner rhythm (`8px-12px`) for controls.
### Inputs / Fields
- **Style:** mostly transparent text fields with underlined focus rails for global filter/search workflows.
- **Focus:** amber underline amplification and glow, paired with brighter field text.
- **Error/Disabled:** disabled uses opacity reduction; error state should be paired with label text, not color only.
### Navigation
- **Style:** rail links in uppercase label typography with `10px` radius and low-contrast base fill.
- **Hover/Active:** hover introduces border + subtle fill; active introduces amber-tinted background and stronger contrast.
- **Mobile Treatment:** rail collapses to top flow, controls stack vertically under `720px` while preserving full-width hit targets.
### Signature Component
- **Virtualized Data Tables:** fixed-height row lanes (`36px` and `44px` families), mono numeric columns, semantic row tinting, and stable scroll performance for live bursts.
## Do's and Don'ts
### Do:
- **Do** keep status and direction semantic with both color and text labels (`severity-high`, `direction-bullish`, explicit words).
- **Do** preserve compact control density (`8px-12px` padding range) so investigation actions stay within a short scan path.
- **Do** use amber as a sparse decision signal for active controls, focus rails, and key counters.
- **Do** keep overlays visually separated with dedicated shadow roles while leaving primary panes flat.
- **Do** design live updates to avoid flashing, excessive animation, and layout shifts during high-volume periods.
### Don't:
- **Don't** make Islandflow feel like a meme-stock or finfluencer trading app with hype, gamification, urgency theater, or promotional calls to action.
- **Don't** make Islandflow feel like a generic SaaS analytics dashboard with decorative gradients, vague card stacks, and non-actionable vanity metrics.
- **Don't** make Islandflow feel like Bloomberg-style visual density used as aesthetic cosplay instead of as a genuinely useful information structure.
- **Don't** rely on red/green alone for directional meaning or severity.
- **Don't** use colored side-stripe accents on rows/cards as the primary signifier; use complete semantic chips and labels instead.

38
PRODUCT.md Normal file
View file

@ -0,0 +1,38 @@
# Product
## Register
product
## Users
Islandflow is for serious individual traders and researchers working in live market conditions. They use real-time options flow, equity prints, inferred dark/off-exchange signals, and deterministic replay to investigate market behavior under pressure, where speed and confidence both matter.
## Product Purpose
Islandflow exists to help users quickly decide whether unusual market activity is meaningful, explainable, and actionable. The product should surface evidence fast enough to support real-time decisions, while preserving enough context and traceability to trust or dismiss a signal with confidence.
## Brand Personality
Precise, composed, forensic (with tactical tone when needed). The interface should feel like an instrument panel: utility-first, calm under load, and trustworthy. Brand voice should appear in orientation moments, empty states, onboarding, and high-level framing, while core workflows prioritize clarity, speed, and evidence.
## Anti-references
- Meme-stock or finfluencer-style trading apps that rely on hype, gamification, urgency theater, or promotional calls to action.
- Generic SaaS analytics dashboards with decorative gradients, vague card stacks, and non-actionable vanity metrics.
- Bloomberg-style visual density used as aesthetic cosplay instead of as a genuinely useful information structure.
## Design Principles
- Evidence before impression: every important signal should be explainable with clear supporting context.
- Utility over theater: visual choices must improve legibility, prioritization, and decision speed.
- Composure under volatility: interactions and layouts should remain stable and readable during bursts of market activity.
- Trust through precision: labels, states, and data semantics should be explicit, unambiguous, and internally consistent.
- Workflow-first framing: the interface should support investigative flow from detection to validation to action.
## Accessibility & Inclusion
- Target WCAG AA contrast at minimum across all core interfaces.
- Support reduced motion preferences, especially for live ticks, pulses, chart transitions, and alert animations.
- Never rely on red/green color alone for directionality or status; pair with text, icons, shape, and/or position.
- Keep real-time updates readable by avoiding flashing effects, excessive animation, and layout shifts during high-volume periods.

View file

@ -46,6 +46,40 @@ a {
text-decoration: none; text-decoration: none;
} }
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.skip-link {
position: absolute;
top: 12px;
left: 12px;
z-index: 40;
padding: 8px 10px;
border: 1px solid rgba(255, 216, 154, 0.44);
border-radius: 8px;
background: rgba(7, 10, 14, 0.98);
color: #ffe2aa;
font-family: var(--font-mono), monospace;
font-size: 0.72rem;
letter-spacing: 0.12em;
text-transform: uppercase;
transform: translateY(-160%);
transition: transform 160ms ease;
}
.skip-link:focus-visible {
transform: translateY(0);
}
button, button,
input { input {
font: inherit; font: inherit;
@ -88,10 +122,12 @@ input {
} }
.terminal-brand-name { .terminal-brand-name {
min-width: 0;
font-family: var(--font-display), sans-serif; font-family: var(--font-display), sans-serif;
font-size: 1.8rem; font-size: 1.8rem;
letter-spacing: 0.08em; letter-spacing: 0.08em;
text-transform: uppercase; text-transform: uppercase;
overflow-wrap: anywhere;
} }
.terminal-nav { .terminal-nav {
@ -100,6 +136,8 @@ input {
} }
.terminal-nav-link { .terminal-nav-link {
min-width: 0;
min-height: 44px;
padding: 12px 14px; padding: 12px 14px;
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 10px; border-radius: 10px;
@ -116,6 +154,13 @@ input {
background: var(--bg-soft); background: var(--bg-soft);
} }
.terminal-nav-link:focus-visible,
.terminal-button:focus-visible,
.instrument-focus-chip button:focus-visible {
outline: 2px solid rgba(255, 216, 154, 0.88);
outline-offset: 2px;
}
.terminal-nav-link-active { .terminal-nav-link-active {
border-color: var(--border-strong); border-color: var(--border-strong);
color: var(--text); color: var(--text);
@ -212,6 +257,7 @@ input {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
flex-wrap: wrap;
gap: 12px; gap: 12px;
min-width: 0; min-width: 0;
width: auto; width: auto;
@ -222,6 +268,7 @@ input {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
flex-wrap: wrap;
gap: 10px; gap: 10px;
min-width: 0; min-width: 0;
flex: 0 1 auto; flex: 0 1 auto;
@ -335,6 +382,7 @@ input {
.interval-button, .interval-button,
.overlay-toggle, .overlay-toggle,
.drawer-close { .drawer-close {
min-height: 36px;
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 8px;
padding: 8px 10px; padding: 8px 10px;
@ -366,6 +414,7 @@ input {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
min-width: 0;
min-height: 32px; min-height: 32px;
max-width: min(360px, 32vw); max-width: min(360px, 32vw);
padding: 5px 8px 5px 10px; padding: 5px 8px 5px 10px;
@ -379,6 +428,7 @@ input {
} }
.instrument-focus-chip span { .instrument-focus-chip span {
min-width: 0;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@ -1090,27 +1140,23 @@ h3 {
} }
.data-table-row-classified.is-classified { .data-table-row-classified.is-classified {
border-left: 3px solid rgba(var(--classifier-rgb), calc(0.35 + var(--classifier-intensity) * 0.45)); box-shadow: inset 0 0 0 1px rgba(var(--classifier-rgb), calc(0.28 + var(--classifier-intensity) * 0.24));
padding-left: 7px;
} }
.data-table-row-warn, .data-table-row-warn,
.data-table-row-severity-high, .data-table-row-severity-high,
.data-table-row-direction-bearish { .data-table-row-direction-bearish {
border-left: 3px solid rgba(255, 107, 95, 0.58); box-shadow: inset 0 0 0 1px rgba(255, 107, 95, 0.46);
padding-left: 7px;
} }
.data-table-row-severity-medium, .data-table-row-severity-medium,
.data-table-row-direction-neutral { .data-table-row-direction-neutral {
border-left: 3px solid rgba(77, 163, 255, 0.46); box-shadow: inset 0 0 0 1px rgba(77, 163, 255, 0.36);
padding-left: 7px;
} }
.data-table-row-severity-low, .data-table-row-severity-low,
.data-table-row-direction-bullish { .data-table-row-direction-bullish {
border-left: 3px solid rgba(37, 193, 122, 0.5); box-shadow: inset 0 0 0 1px rgba(37, 193, 122, 0.38);
padding-left: 7px;
} }
.data-table-options .data-table-head, .data-table-options .data-table-head,
@ -1220,8 +1266,7 @@ h3 {
.options-table-row.is-classified { .options-table-row.is-classified {
cursor: pointer; cursor: pointer;
border-left: 3px solid rgba(var(--classifier-rgb), calc(0.35 + var(--classifier-intensity) * 0.45)); box-shadow: inset 0 0 0 1px rgba(var(--classifier-rgb), calc(0.28 + var(--classifier-intensity) * 0.24));
padding-left: 7px;
} }
.options-table-row > span { .options-table-row > span {
@ -1764,15 +1809,57 @@ h3 {
} }
.terminal-rail { .terminal-rail {
position: static; position: sticky;
top: 0;
z-index: 35;
height: auto; height: auto;
display: grid;
grid-template-columns: minmax(170px, auto) minmax(0, 1fr);
align-items: center;
gap: 14px 18px;
padding: 14px 16px;
border-right: 0; border-right: 0;
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
.terminal-brand {
gap: 2px;
}
.terminal-brand-name {
font-size: 1.25rem;
}
.terminal-nav {
display: flex;
min-width: 0;
gap: 8px;
overflow-x: auto;
scrollbar-width: thin;
}
.terminal-nav-link {
flex: 0 0 auto;
white-space: nowrap;
}
.shell-metrics { .shell-metrics {
grid-column: 1 / -1;
margin-top: 0; margin-top: 0;
grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-columns: repeat(4, minmax(136px, 1fr));
gap: 8px;
overflow-x: auto;
padding-bottom: 2px;
scrollbar-width: thin;
}
.shell-metric {
min-width: 136px;
padding: 10px 12px;
}
.terminal-topbar {
position: static;
} }
} }
@ -1811,7 +1898,6 @@ h3 {
} }
.terminal-topbar { .terminal-topbar {
position: static;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
padding: 10px 16px; padding: 10px 16px;
@ -1833,8 +1919,60 @@ h3 {
} }
@media (max-width: 720px) { @media (max-width: 720px) {
.terminal-shell {
background-size: 24px 24px, 24px 24px, 100% 100%, auto;
}
.terminal-rail {
position: static;
grid-template-columns: minmax(0, 1fr);
gap: 12px;
padding: 12px;
}
.terminal-brand {
grid-template-columns: auto minmax(0, 1fr);
align-items: baseline;
gap: 10px;
}
.terminal-brand-kicker {
font-size: 0.7rem;
}
.terminal-brand-name {
font-size: 1rem;
}
.terminal-nav {
padding-bottom: 2px;
}
.terminal-nav-link {
padding: 12px;
font-size: 0.72rem;
}
.shell-metrics {
display: flex;
gap: 8px;
}
.shell-metric {
flex: 0 0 156px;
}
.terminal-content { .terminal-content {
padding: 18px 14px 22px; padding: 16px 10px 22px;
}
.page-shell {
gap: 14px;
}
.page-title {
font-size: 1.55rem;
line-height: 1.06;
} }
.page-header, .page-header,
@ -1849,6 +1987,27 @@ h3 {
.terminal-pane-title-row { .terminal-pane-title-row {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 8px;
}
.terminal-topbar {
position: sticky;
top: 0;
z-index: 30;
padding: 12px 10px;
}
.terminal-button,
.mode-button,
.filter-clear,
.jump-button,
.pause-button,
.interval-button,
.overlay-toggle,
.drawer-close,
.contract-filter-button,
.filter-chip {
min-height: 44px;
} }
.terminal-topbar-actions, .terminal-topbar-actions,
@ -1864,6 +2023,19 @@ h3 {
align-items: stretch; align-items: stretch;
} }
.terminal-topbar-mode .terminal-button,
.terminal-topbar-controls > .terminal-button,
.page-actions > .terminal-button,
.page-actions > .flow-filter-popover {
width: 100%;
}
.instrument-focus-chip {
max-width: none;
min-height: 44px;
justify-content: space-between;
}
.terminal-filter { .terminal-filter {
width: 100%; width: 100%;
min-width: 0; min-width: 0;
@ -1873,10 +2045,46 @@ h3 {
.terminal-input { .terminal-input {
width: 100%; width: 100%;
min-height: 38px;
padding-bottom: 8px;
font-size: 1rem;
}
.terminal-pane {
border-radius: 12px;
}
.terminal-pane-head,
.terminal-pane-body {
padding: 14px 12px;
}
.terminal-pane-actions,
.card-controls,
.chart-controls,
.tape-controls {
width: 100%;
flex-wrap: wrap;
justify-content: flex-start;
}
.tape-controls button {
flex: 1 1 112px;
}
.status-inline {
flex-wrap: wrap;
row-gap: 4px;
}
.status-inline-counter {
min-width: 0;
} }
.page-actions { .page-actions {
width: 100%; width: 100%;
flex-direction: column;
align-items: stretch;
} }
.flow-filter-popover { .flow-filter-popover {
@ -1890,11 +2098,13 @@ h3 {
.flow-filter-popover-panel { .flow-filter-popover-panel {
position: fixed; position: fixed;
top: calc(var(--topbar-height) + 26px); top: auto;
left: 14px; bottom: calc(10px + env(safe-area-inset-bottom));
right: 14px; left: 10px;
right: 10px;
width: auto; width: auto;
max-height: min(68vh, 560px); max-height: min(72vh, 560px);
border-radius: 16px;
} }
.flow-filter-checkbox-grid, .flow-filter-checkbox-grid,
@ -1908,6 +2118,39 @@ h3 {
align-items: flex-start; align-items: flex-start;
} }
.data-table-wrap {
margin-inline: -12px;
border-radius: 0;
scroll-snap-type: x proximity;
}
.data-table {
min-width: 860px;
scroll-snap-align: start;
}
.data-table-options,
.data-table-flow {
min-width: 1080px;
}
.data-table-head,
.data-table-row {
padding-inline: 8px;
}
.data-table-row-options,
.data-table-row-equities {
height: 40px;
}
.data-table-row-flow,
.data-table-row-alerts,
.data-table-row-classifier,
.data-table-row-dark {
height: 48px;
}
.time { .time {
text-align: left; text-align: left;
} }
@ -1917,10 +2160,31 @@ h3 {
} }
.drawer { .drawer {
position: static; position: fixed;
inset: auto 10px calc(10px + env(safe-area-inset-bottom));
width: auto; width: auto;
max-height: none; max-height: min(78vh, 640px);
margin-top: 14px; margin-top: 0;
border-radius: 16px;
}
}
@media (max-width: 420px) {
.terminal-content {
padding-inline: 8px;
}
.terminal-pane-head,
.terminal-pane-body {
padding-inline: 10px;
}
.shell-metric {
flex-basis: 142px;
}
.data-table-wrap {
margin-inline: -10px;
} }
.synthetic-control-gear { .synthetic-control-gear {

View file

@ -27,8 +27,10 @@ import {
getTapeVirtualConfig, getTapeVirtualConfig,
mergeNewestWithOverflow, mergeNewestWithOverflow,
normalizeAlertSeverity, normalizeAlertSeverity,
normalizeTickerFilterInput,
nextFlowFilterPopoverState, nextFlowFilterPopoverState,
isSyntheticAdminVisible, isSyntheticAdminVisible,
parseTickerFilterInput,
prunePinnedEntries, prunePinnedEntries,
projectPausableTapeState, projectPausableTapeState,
reducePausableTapeData, reducePausableTapeData,
@ -412,6 +414,17 @@ describe("synthetic admin visibility", () => {
it("shows the internal control rail only when the public admin flag is enabled", () => { it("shows the internal control rail only when the public admin flag is enabled", () => {
expect(isSyntheticAdminVisible("1")).toBe(true); expect(isSyntheticAdminVisible("1")).toBe(true);
expect(isSyntheticAdminVisible("0")).toBe(false); expect(isSyntheticAdminVisible("0")).toBe(false);
expect(isSyntheticAdminVisible(undefined)).toBe(false);
});
});
describe("ticker filter helpers", () => {
it("normalizes pasted ticker input into a stable terminal format", () => {
expect(normalizeTickerFilterInput(" spy\n nvda\u0000 aapl ")).toBe(" SPY, NVDA AAPL ");
});
it("parses, uppercases, and deduplicates ticker tokens", () => {
expect(parseTickerFilterInput("spy, nvda spy\nqqq")).toEqual(["SPY", "NVDA", "QQQ"]);
}); });
}); });

View file

@ -8,6 +8,7 @@ import {
useCallback, useCallback,
useContext, useContext,
useEffect, useEffect,
useId,
useLayoutEffect, useLayoutEffect,
useMemo, useMemo,
useRef, useRef,
@ -5054,6 +5055,25 @@ const formatFlowMetric = (value: number, suffix?: string): string => {
return value.toLocaleString(); return value.toLocaleString();
}; };
const TICKER_FILTER_INPUT_MAX_LENGTH = 120;
export const normalizeTickerFilterInput = (value: string): string =>
value
.normalize("NFKC")
.replace(/[\u0000-\u001f\u007f]+/g, " ")
.replace(//g, ",")
.replace(/\s+/g, " ")
.toUpperCase()
.slice(0, TICKER_FILTER_INPUT_MAX_LENGTH);
export const parseTickerFilterInput = (value: string): string[] => {
const parts = normalizeTickerFilterInput(value)
.split(/[,\s]+/)
.map((part) => part.trim())
.filter(Boolean);
return Array.from(new Set(parts));
};
const useTerminalState = () => { const useTerminalState = () => {
const pathname = usePathname(); const pathname = usePathname();
const routeFeatures = useMemo(() => getRouteFeatures(pathname), [pathname]); const routeFeatures = useMemo(() => getRouteFeatures(pathname), [pathname]);
@ -5069,13 +5089,7 @@ const useTerminalState = () => {
const [filterInput, setFilterInput] = useState<string>(""); const [filterInput, setFilterInput] = useState<string>("");
const [flowFilters, setFlowFilters] = useState<OptionFlowFilters>(() => buildDefaultFlowFilters()); const [flowFilters, setFlowFilters] = useState<OptionFlowFilters>(() => buildDefaultFlowFilters());
const [chartIntervalMs, setChartIntervalMs] = useState<number>(CANDLE_INTERVALS[0].ms); const [chartIntervalMs, setChartIntervalMs] = useState<number>(CANDLE_INTERVALS[0].ms);
const activeTickers = useMemo(() => { const activeTickers = useMemo(() => parseTickerFilterInput(filterInput), [filterInput]);
const parts = filterInput
.split(/[,\s]+/)
.map((value) => value.trim().toUpperCase())
.filter(Boolean);
return Array.from(new Set(parts));
}, [filterInput]);
const tickerSet = useMemo(() => new Set(activeTickers), [activeTickers]); const tickerSet = useMemo(() => new Set(activeTickers), [activeTickers]);
const instrumentUnderlying = selectedInstrument?.underlyingId.toUpperCase() ?? null; const instrumentUnderlying = selectedInstrument?.underlyingId.toUpperCase() ?? null;
const isOptionContractFocused = selectedInstrument?.kind === "option-contract"; const isOptionContractFocused = selectedInstrument?.kind === "option-contract";
@ -8348,20 +8362,26 @@ function SyntheticControlDock() {
export function TerminalAppShell({ children }: { children: ReactNode }) { export function TerminalAppShell({ children }: { children: ReactNode }) {
const state = useTerminalState(); const state = useTerminalState();
const pathname = usePathname(); const pathname = usePathname();
const tickerFieldId = useId();
const tickerHintId = useId();
return ( return (
<TerminalContext.Provider value={state}> <TerminalContext.Provider value={state}>
<div className="terminal-shell"> <div className="terminal-shell">
<a className="skip-link" href="#terminal-content">
Skip to terminal content
</a>
<aside className="terminal-rail"> <aside className="terminal-rail">
<div className="terminal-brand"> <div className="terminal-brand">
<span className="terminal-brand-kicker">IF</span> <span className="terminal-brand-kicker">IF</span>
<span className="terminal-brand-name">Islandflow</span> <span className="terminal-brand-name">Islandflow</span>
</div> </div>
<nav className="terminal-nav"> <nav aria-label="Primary" className="terminal-nav">
{NAV_ITEMS.map((item) => { {NAV_ITEMS.map((item) => {
const active = pathname === item.href; const active = pathname === item.href;
return ( return (
<Link <Link
aria-current={active ? "page" : undefined}
className={`terminal-nav-link${active ? " terminal-nav-link-active" : ""}`} className={`terminal-nav-link${active ? " terminal-nav-link-active" : ""}`}
href={item.href} href={item.href}
key={item.href} key={item.href}
@ -8387,31 +8407,46 @@ export function TerminalAppShell({ children }: { children: ReactNode }) {
</span> </span>
) : null} ) : null}
<label className="terminal-filter"> <label className="terminal-filter">
<span className="terminal-filter-label">Ticker</span> <span className="terminal-filter-label" id={tickerHintId}>
Ticker
</span>
<span className="terminal-filter-field"> <span className="terminal-filter-field">
<input <input
id={tickerFieldId}
aria-describedby={tickerHintId}
autoCapitalize="characters"
autoComplete="off"
autoCorrect="off"
className="terminal-input" className="terminal-input"
value={state.filterInput} value={state.filterInput}
onChange={(event) => state.setFilterInput(event.target.value)} inputMode="text"
maxLength={TICKER_FILTER_INPUT_MAX_LENGTH}
name="ticker-filter"
onChange={(event) => state.setFilterInput(normalizeTickerFilterInput(event.target.value))}
placeholder="SPY, NVDA, AAPL" placeholder="SPY, NVDA, AAPL"
spellCheck={false} spellCheck={false}
/> />
</span> </span>
</label> </label>
<button <button
aria-label="Clear ticker filter"
className="terminal-button" className="terminal-button"
type="button" type="button"
onClick={() => state.setFilterInput("")} onClick={() => state.setFilterInput("")}
disabled={state.filterInput.trim().length === 0} disabled={state.filterInput.trim().length === 0}
title="Clear ticker filter"
> >
Clear Clear
</button> </button>
</div> </div>
<div className="terminal-topbar-mode"> <div className="terminal-topbar-mode">
<button <button
aria-label={state.mode === "live" ? "Switch to replay mode" : "Switch to live mode"}
aria-pressed={state.mode !== "live"}
className="terminal-button terminal-button-primary" className="terminal-button terminal-button-primary"
type="button" type="button"
onClick={state.toggleMode} onClick={state.toggleMode}
title={state.mode === "live" ? "Switch to replay mode" : "Switch to live mode"}
> >
{state.mode === "live" ? "Replay" : "Live"} {state.mode === "live" ? "Replay" : "Live"}
</button> </button>
@ -8419,7 +8454,9 @@ export function TerminalAppShell({ children }: { children: ReactNode }) {
</div> </div>
</header> </header>
<main className="terminal-content">{children}</main> <main className="terminal-content" id="terminal-content">
{children}
</main>
</div> </div>
<SyntheticControlDock /> <SyntheticControlDock />

View file

@ -0,0 +1,486 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Terminal Audit - 2026-05-14 04:32</title>
<style>
:root {
color-scheme: dark;
--bg: #0b1016;
--panel: #111820;
--panel-2: #0d141b;
--border: rgba(255, 255, 255, 0.1);
--text: #e6edf4;
--text-dim: #90a0b2;
--text-faint: #6e7b8c;
--accent: #f5a623;
--accent-soft: rgba(245, 166, 35, 0.14);
--green: #25c17a;
--green-soft: rgba(37, 193, 122, 0.16);
--red: #ff6b5f;
--red-soft: rgba(255, 107, 95, 0.16);
--blue: #4da3ff;
--blue-soft: rgba(77, 163, 255, 0.16);
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: Inter, "IBM Plex Sans", Arial, sans-serif;
background: linear-gradient(180deg, #081017 0%, #05070a 100%);
color: var(--text);
}
main {
width: min(1100px, calc(100vw - 32px));
margin: 0 auto;
padding: 32px 0 48px;
}
h1,
h2,
h3,
h4,
p,
ul,
ol,
table {
margin-top: 0;
}
h1,
h2,
h3 {
letter-spacing: 0.04em;
}
h1 {
font-size: 2rem;
margin-bottom: 10px;
}
h2 {
font-size: 1.15rem;
margin-bottom: 14px;
}
h3 {
font-size: 0.95rem;
margin-bottom: 10px;
text-transform: uppercase;
color: var(--text-dim);
}
p,
li,
td,
th {
line-height: 1.55;
color: var(--text);
}
.lede {
color: var(--text-dim);
max-width: 76ch;
}
.summary {
padding: 24px;
border: 1px solid var(--border);
border-radius: 16px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.02));
margin-bottom: 24px;
}
.score-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
margin: 14px 0 18px;
}
.score-badge,
.pill {
display: inline-flex;
align-items: center;
gap: 8px;
min-height: 32px;
padding: 6px 10px;
border-radius: 999px;
border: 1px solid var(--border);
background: rgba(255, 255, 255, 0.03);
font-size: 0.82rem;
}
.score-badge {
border-color: rgba(245, 166, 35, 0.35);
background: var(--accent-soft);
color: #ffe2aa;
font-weight: 700;
}
.pill-good {
border-color: rgba(37, 193, 122, 0.35);
background: var(--green-soft);
}
.pill-warn {
border-color: rgba(245, 166, 35, 0.35);
background: var(--accent-soft);
}
.pill-risk {
border-color: rgba(255, 107, 95, 0.35);
background: var(--red-soft);
}
.panel {
padding: 22px 24px;
border: 1px solid var(--border);
border-radius: 16px;
background: var(--panel);
margin-bottom: 20px;
}
.subpanel {
padding: 18px;
border: 1px solid var(--border);
border-radius: 14px;
background: var(--panel-2);
margin-top: 16px;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.94rem;
}
th,
td {
padding: 12px 10px;
border-bottom: 1px solid var(--border);
text-align: left;
vertical-align: top;
}
th {
color: var(--text-dim);
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.08em;
}
code {
font-family: "IBM Plex Mono", Menlo, monospace;
font-size: 0.88em;
color: #ffe2aa;
}
.finding {
border-top: 1px solid var(--border);
padding-top: 18px;
margin-top: 18px;
}
.finding:first-of-type {
border-top: 0;
padding-top: 0;
margin-top: 0;
}
.meta {
color: var(--text-dim);
font-size: 0.92rem;
}
.meta strong {
color: var(--text);
}
.note {
color: var(--text-dim);
}
.verdict-pass {
color: #bfe7cf;
}
.footer-note {
color: var(--text-dim);
font-size: 0.95rem;
}
@media (max-width: 720px) {
main {
width: min(100vw - 20px, 1100px);
padding: 20px 0 32px;
}
.summary,
.panel {
padding: 18px;
}
th,
td {
padding: 10px 8px;
font-size: 0.9rem;
}
}
</style>
</head>
<body>
<main>
<section class="summary">
<h1>Terminal View Audit</h1>
<p class="lede">
Audit report for the Islandflow terminal view, formatted for handoff and review. This preserves the
full findings set: scorecard, anti-pattern verdict, executive summary, detailed issues, systemic
patterns, positive findings, and recommended follow-up commands.
</p>
<div class="score-row">
<span class="score-badge">Overall Score: 11/20</span>
<span class="pill pill-warn">Rating Band: Acceptable</span>
<span class="pill pill-risk">Severity Mix: P0 0, P1 5, P2 3, P3 1</span>
<span class="pill">Generated: 2026-05-14 04:32</span>
</div>
<p>
The terminal does <strong>not</strong> read as generic AI-generated UI overall. It has a coherent
instrument-panel identity, consistent density, and restrained accent use. The biggest problems are
implementation quality issues: invalid nested interactive controls, inaccessible drawer behavior,
weak focus treatment, mobile layouts that depend on horizontal scrolling, token drift, and repeated
banned side-stripe accents.
</p>
</section>
<section class="panel">
<h2>Audit Health Score</h2>
<table>
<thead>
<tr>
<th>#</th>
<th>Dimension</th>
<th>Score</th>
<th>Key Finding</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Accessibility</td>
<td>2/4</td>
<td>Invalid nested interactive controls in options rows.</td>
</tr>
<tr>
<td>2</td>
<td>Performance</td>
<td>3/4</td>
<td>Virtualization is good, but blur-heavy chrome and overlays add avoidable cost.</td>
</tr>
<tr>
<td>3</td>
<td>Responsive Design</td>
<td>2/4</td>
<td>Core tables rely on large fixed minimum widths and horizontal scrolling.</td>
</tr>
<tr>
<td>4</td>
<td>Theming</td>
<td>2/4</td>
<td>Token base exists, but many hard-coded colors and undefined vars bypass it.</td>
</tr>
<tr>
<td>5</td>
<td>Anti-Patterns</td>
<td>2/4</td>
<td>Repeated side-stripe accents violate the stated design bans.</td>
</tr>
<tr>
<td><strong>Total</strong></td>
<td></td>
<td><strong>11/20</strong></td>
<td><strong>Acceptable</strong></td>
</tr>
</tbody>
</table>
</section>
<section class="panel">
<h2>Anti-Patterns Verdict</h2>
<p class="verdict-pass">
Pass, with caveats.
</p>
<p>
The terminal does not look AI-generated overall. It has a coherent instrument-panel identity,
consistent density, and restrained accent use. The main tells are implementation-level:
banned side-stripe accents on live rows, decorative blur-heavy chrome, and some product-UI
typography choices that drift toward display styling.
</p>
</section>
<section class="panel">
<h2>Executive Summary</h2>
<ul>
<li><strong>Audit Health Score:</strong> 11/20 (Acceptable)</li>
<li><strong>Total issues found:</strong> 9</li>
<li><strong>Severity mix:</strong> P0: 0, P1: 5, P2: 3, P3: 1</li>
<li>
<strong>Top issues:</strong> nested buttons inside clickable options rows, drawers that are not true
accessible dialogs, suppressed focus indicators, mobile dependence on oversized horizontal tables,
and repeated banned side-stripe row styling.
</li>
</ul>
</section>
<section class="panel">
<h2>Detailed Findings By Severity</h2>
<article class="finding">
<h3>[P1] Nested Interactive Controls Inside Clickable Row</h3>
<p class="meta"><strong>Location:</strong> <code>apps/web/app/terminal.tsx:7118-7135</code>, <code>apps/web/app/terminal.tsx:7155-7179</code></p>
<p class="meta"><strong>Category:</strong> Accessibility</p>
<p><strong>Impact:</strong> Decorated option rows render as outer <code>&lt;button&gt;</code> elements containing inner contract-focus <code>&lt;button&gt;</code> elements. This is invalid HTML and can create inconsistent tab order, click handling, and screen-reader output.</p>
<p><strong>WCAG/Standard:</strong> WCAG 4.1.2 Name, Role, Value; HTML interactive content nesting rules</p>
<p><strong>Recommendation:</strong> Split row selection and contract focus into non-nested controls. Use a non-button row container with one explicit action button, or keep the row as the only button and turn inner controls into non-interactive text.</p>
<p><strong>Suggested command:</strong> <code>$impeccable harden terminal view</code></p>
</article>
<article class="finding">
<h3>[P1] Drawer Panels Are Visually Drawers, Not Accessible Dialogs</h3>
<p class="meta"><strong>Location:</strong> <code>apps/web/app/terminal.tsx:4524-4629</code>, <code>4639-4737</code>, <code>4747-4841</code>, <code>4850-4952</code>, close handling at <code>5070-5102</code></p>
<p class="meta"><strong>Category:</strong> Accessibility</p>
<p><strong>Impact:</strong> The drawers close on outside click and <code>Escape</code>, but they lack <code>role="dialog"</code>, <code>aria-modal</code>, focus entry, focus return, and trap behavior. Keyboard users can tab behind the drawer and lose context.</p>
<p><strong>WCAG/Standard:</strong> WCAG 2.1.1 Keyboard, 2.4.3 Focus Order, 4.1.2 Name, Role, Value</p>
<p><strong>Recommendation:</strong> Promote drawers to true modal dialogs with labelled titles, initial focus, focus containment, inert background, and focus restoration on close.</p>
<p><strong>Suggested command:</strong> <code>$impeccable harden terminal view</code></p>
</article>
<article class="finding">
<h3>[P1] Focus Indicators Are Suppressed In Multiple Core Controls</h3>
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:325-327</code>, <code>413-415</code>, <code>1054-1058</code>, <code>1213-1218</code></p>
<p class="meta"><strong>Category:</strong> Accessibility</p>
<p><strong>Impact:</strong> Several controls explicitly remove the browser outline. Some surfaces get only a subtle background shift, which is weaker than a reliable visible focus ring, especially in dense data views.</p>
<p><strong>WCAG/Standard:</strong> WCAG 2.4.7 Focus Visible</p>
<p><strong>Recommendation:</strong> Restore strong <code>:focus-visible</code> treatment on inputs, row buttons, and inline instrument actions using a consistent high-contrast ring or border treatment.</p>
<p><strong>Suggested command:</strong> <code>$impeccable harden terminal view</code></p>
</article>
<article class="finding">
<h3>[P1] ARIA Table Semantics Are Incomplete</h3>
<p class="meta"><strong>Location:</strong> <code>apps/web/app/terminal.tsx:7061-7075</code>, <code>7251-7259</code>, <code>7348-7359</code>, <code>7496-7505</code>, <code>7609-7616</code>, <code>7732-7739</code></p>
<p class="meta"><strong>Category:</strong> Accessibility</p>
<p><strong>Impact:</strong> The app uses <code>role="table"</code> and <code>role="row"</code> but not full table semantics such as <code>rowgroup</code>, <code>columnheader</code>, and cell roles. Screen readers will get a weaker structural model than a real table or fully formed ARIA grid.</p>
<p><strong>WCAG/Standard:</strong> WCAG 1.3.1 Info and Relationships</p>
<p><strong>Recommendation:</strong> Prefer semantic <code>&lt;table&gt;</code> markup where possible, or complete the ARIA table structure consistently.</p>
<p><strong>Suggested command:</strong> <code>$impeccable harden terminal view</code></p>
</article>
<article class="finding">
<h3>[P1] Narrow-Screen Experience Depends On Oversized Horizontal Tables</h3>
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:967-1009</code>, <code>1116-1144</code>, <code>1645-1714</code></p>
<p class="meta"><strong>Category:</strong> Responsive Design</p>
<p><strong>Impact:</strong> Major views keep <code>min-width</code> values like <code>1280px</code>, <code>1260px</code>, <code>900px</code>, and <code>820px</code>. The mobile fallback is horizontal scroll rather than structural adaptation, which increases cognitive load and makes comparison harder on phones and small tablets.</p>
<p><strong>WCAG/Standard:</strong> Responsive design best practice</p>
<p><strong>Recommendation:</strong> Define compact column sets, progressive disclosure, or cardless stacked row summaries under mobile breakpoints instead of preserving full desktop schema.</p>
<p><strong>Suggested command:</strong> <code>$impeccable adapt terminal view</code></p>
</article>
<article class="finding">
<h3>[P2] Touch Targets Are Below Recommended Minimum In Key Controls</h3>
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:255</code>, <code>330-347</code>, <code>365-379</code>, <code>461-468</code></p>
<p class="meta"><strong>Category:</strong> Responsive Design</p>
<p><strong>Impact:</strong> Controls and chips commonly bottom out around <code>32px</code> height. That is workable on desktop, but it is tight for touch use and increases mis-taps on mobile.</p>
<p><strong>WCAG/Standard:</strong> WCAG 2.5.5 Target Size (AAA), platform mobile guidance</p>
<p><strong>Recommendation:</strong> Raise interactive height to at least <code>40px</code>, ideally <code>44px</code>, for topbar controls, focus chips, and filter triggers under touch breakpoints.</p>
<p><strong>Suggested command:</strong> <code>$impeccable adapt terminal view</code></p>
</article>
<article class="finding">
<h3>[P2] Token Discipline Is Partial, Not Consistent</h3>
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:41</code>, <code>306</code>, <code>321</code>, <code>362</code>, <code>375</code>, <code>479</code>, <code>502</code>, <code>565</code>, <code>616</code>, <code>763</code>, <code>1452-1470</code></p>
<p class="meta"><strong>Category:</strong> Theming</p>
<p><strong>Impact:</strong> The file starts with a clear token layer, but many later rules bypass it with hard-coded hex values. That makes palette evolution and future theme work harder.</p>
<p><strong>WCAG/Standard:</strong> Theming and system quality</p>
<p><strong>Recommendation:</strong> Replace one-off literals with named variables, especially amber text variants, chart surface background, and severity-strip foreground colors.</p>
<p><strong>Suggested command:</strong> <code>$impeccable polish terminal view</code></p>
</article>
<article class="finding">
<h3>[P2] Undefined CSS Variables Create Silent Theming Bugs</h3>
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:398</code>, <code>1186</code></p>
<p class="meta"><strong>Category:</strong> Theming</p>
<p><strong>Impact:</strong> <code>var(--text-muted)</code> and <code>var(--muted)</code> are referenced but not defined in <code>:root</code>. Those declarations will fail and fall back to inherited color, which makes the result fragile and inconsistent.</p>
<p><strong>WCAG/Standard:</strong> CSS correctness</p>
<p><strong>Recommendation:</strong> Replace them with existing tokens such as <code>--text-dim</code> or define the missing variables explicitly.</p>
<p><strong>Suggested command:</strong> <code>$impeccable harden terminal view</code></p>
</article>
<article class="finding">
<h3>[P2] Blur-Heavy Chrome Is Overused For Product UI</h3>
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:174-176</code>, <code>518-525</code>, <code>1504-1506</code></p>
<p class="meta"><strong>Category:</strong> Performance / Anti-Pattern</p>
<p><strong>Impact:</strong> <code>backdrop-filter: blur(12px)</code> and <code>blur(18px)</code> on persistent UI surfaces add cost and push the product UI slightly toward decorative glass treatment, which the design rules explicitly warn against as a default.</p>
<p><strong>WCAG/Standard:</strong> Performance and product-design guidance</p>
<p><strong>Recommendation:</strong> Keep blur only where separation is essential, or replace it with tonal contrast and border treatment.</p>
<p><strong>Suggested command:</strong> <code>$impeccable quieter terminal view</code></p>
</article>
<article class="finding">
<h3>[P3] Side-Stripe Row Accents Are Repeated Across Tables</h3>
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:1092-1114</code>, <code>1221-1224</code></p>
<p class="meta"><strong>Category:</strong> Anti-Pattern</p>
<p><strong>Impact:</strong> The interface repeatedly uses <code>border-left: 3px</code> to communicate severity, direction, and classifier state. That is one of the skill's explicit banned patterns and makes rows feel more template-like than intentional.</p>
<p><strong>WCAG/Standard:</strong> Design-system rule</p>
<p><strong>Recommendation:</strong> Move semantic emphasis into full-row tinting, chips, iconography, or stronger text hierarchy instead of colored side rails.</p>
<p><strong>Suggested command:</strong> <code>$impeccable polish terminal view</code></p>
</article>
</section>
<section class="panel">
<h2>Patterns And Systemic Issues</h2>
<ul>
<li>Accessibility semantics are strongest at the surface level, labels and buttons exist, but weaker in composite patterns, drawers, virtualized tables, and focus handling.</li>
<li>The responsive strategy is mostly preserve desktop density and allow scrolling, not restructure the workflow for narrow screens.</li>
<li>The CSS starts from a tokenized system, then drifts into literal color values in later component rules.</li>
<li>The visual system is disciplined overall, but a few repeated product bans, side stripes and default blur, show up across multiple components.</li>
</ul>
</section>
<section class="panel">
<h2>Positive Findings</h2>
<ul>
<li>The terminal has a clear, distinctive product identity without falling into meme-trader styling.</li>
<li>Virtualized list rendering is the right performance baseline for these dense live data views.</li>
<li>The top-level shell and pane structure are predictable and support fast scanning.</li>
<li>Core inputs are labelled, and many actionable rows are implemented as real buttons instead of click-only divs.</li>
<li>Color usage is generally restrained and semantically meaningful, even where implementation cleanup is still needed.</li>
</ul>
</section>
<section class="panel">
<h2>Recommended Actions</h2>
<ol>
<li><strong>[P1] <code>$impeccable harden terminal view</code>:</strong> Fix nested buttons, accessible dialog behavior, focus visibility, and incomplete ARIA table semantics.</li>
<li><strong>[P1] <code>$impeccable adapt terminal view</code>:</strong> Redesign narrow-screen table behavior and increase touch target sizes in the shell and filter controls.</li>
<li><strong>[P2] <code>$impeccable quieter terminal view</code>:</strong> Reduce default blur and glass treatment in topbar, popover, and drawer chrome.</li>
<li><strong>[P2] <code>$impeccable polish terminal view</code>:</strong> Normalize tokens, remove hard-coded color drift, and replace banned side-stripe accents.</li>
<li><strong>[P2] <code>$impeccable polish terminal view</code>:</strong> Final cleanup pass once the structural fixes are in.</li>
</ol>
<div class="subpanel">
<p class="footer-note">
You can ask me to run these one at a time, all at once, or in any order you prefer.
</p>
<p class="footer-note">
Re-run <code>$impeccable audit</code> after fixes to see the score improve.
</p>
</div>
</section>
</main>
</body>
</html>

View file

@ -0,0 +1,308 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Turn Document - Harden Terminal View</title>
<style>
:root {
color-scheme: dark;
--bg: #06080b;
--panel: #111820;
--panel-2: #0d141b;
--border: rgba(255, 255, 255, 0.1);
--text: #e6edf4;
--text-dim: #90a0b2;
--text-faint: #6e7b8c;
--accent: #f5a623;
--accent-soft: rgba(245, 166, 35, 0.14);
--green-soft: rgba(37, 193, 122, 0.16);
--red-soft: rgba(255, 107, 95, 0.16);
--blue-soft: rgba(77, 163, 255, 0.16);
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: "IBM Plex Sans", Inter, Arial, sans-serif;
background:
radial-gradient(circle at top left, rgba(245, 166, 35, 0.08), transparent 24%),
linear-gradient(180deg, #081017 0%, #05070a 100%);
color: var(--text);
}
main {
width: min(1040px, calc(100vw - 32px));
margin: 0 auto;
padding: 28px 0 40px;
}
h1,
h2,
h3,
p,
ul,
pre {
margin-top: 0;
}
h1,
h2 {
font-family: Quantico, "IBM Plex Sans", sans-serif;
text-transform: uppercase;
letter-spacing: 0.08em;
}
h1 {
font-size: clamp(1.8rem, 3vw, 2.5rem);
margin-bottom: 10px;
}
h2 {
font-size: 1rem;
margin-bottom: 14px;
}
p,
li {
line-height: 1.6;
}
.summary,
.panel {
border: 1px solid var(--border);
border-radius: 16px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.02));
}
.summary {
padding: 24px;
margin-bottom: 22px;
}
.panel {
padding: 22px 24px;
margin-bottom: 18px;
background: var(--panel);
}
.lede,
.meta,
.note {
color: var(--text-dim);
}
.meta-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 16px 0 0;
}
.pill {
display: inline-flex;
align-items: center;
min-height: 32px;
padding: 6px 10px;
border-radius: 999px;
border: 1px solid var(--border);
background: rgba(255, 255, 255, 0.03);
font-size: 0.82rem;
}
.pill-accent {
border-color: rgba(245, 166, 35, 0.36);
background: var(--accent-soft);
color: #ffe2aa;
}
.pill-good {
background: var(--green-soft);
}
.pill-info {
background: var(--blue-soft);
}
.pill-risk {
background: var(--red-soft);
}
ul {
padding-left: 18px;
margin-bottom: 0;
}
li + li {
margin-top: 8px;
}
code,
pre {
font-family: "IBM Plex Mono", Menlo, monospace;
}
code {
color: #ffe2aa;
}
pre {
overflow-x: auto;
padding: 14px;
border: 1px solid var(--border);
border-radius: 12px;
background: var(--panel-2);
color: var(--text);
}
.two-col {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
}
@media (max-width: 760px) {
main {
width: min(100vw - 20px, 1040px);
padding: 20px 0 28px;
}
.summary,
.panel {
padding: 18px;
}
.two-col {
grid-template-columns: minmax(0, 1fr);
}
}
</style>
</head>
<body>
<main>
<section class="summary">
<h1>Harden Terminal View</h1>
<p class="lede">
Turn document for the terminal shell hardening pass in <code>apps/web/app/terminal.tsx</code>,
<code>apps/web/app/globals.css</code>, and <code>apps/web/app/terminal.test.ts</code>.
</p>
<p>
The work focused on production resilience in the main terminal shell: keyboard access, focus visibility,
long-text behavior, topbar wrapping, and ticker filter normalization for pasted or malformed input.
</p>
<div class="meta-row">
<span class="pill pill-accent">Generated: 2026-05-14 11:24 EDT</span>
<span class="pill pill-good">Tests: Passed</span>
<span class="pill pill-good">Web Build: Passed</span>
<span class="pill pill-info">Beads: islandflow-6ri closed</span>
</div>
</section>
<section class="panel">
<h2>Summary</h2>
<p>
The terminal shell now behaves more predictably under constrained widths and less-perfect input. The
changes stay small and local, but improve accessibility and reduce UI breakage risk in the top-level
workflow.
</p>
</section>
<section class="panel">
<h2>Changes Made</h2>
<ul>
<li>Added a skip link targeting the main terminal content region.</li>
<li>Added primary navigation semantics with <code>aria-label</code> and <code>aria-current</code>.</li>
<li>Added visible keyboard focus treatment for nav links, shell buttons, and the instrument chip action.</li>
<li>Allowed topbar action groups to wrap instead of forcing overflow at narrower widths.</li>
<li>Added long-text hardening for the brand name and selected instrument chip.</li>
<li>Added ticker filter input normalization, uppercase handling, control-character cleanup, and length limits.</li>
<li>Added unit tests for ticker filter normalization and parsing.</li>
</ul>
</section>
<section class="panel">
<h2>Context</h2>
<p>
Islandflow's terminal is an evidence-first product surface used under time pressure. The shell is not a
decorative wrapper. It controls navigation, global filter state, and mode switching, so failures here can
degrade every route at once.
</p>
<p>
The hardening pass stayed focused on shell-level reliability rather than introducing broader layout or
component refactors.
</p>
</section>
<section class="panel">
<h2>Important Implementation Details</h2>
<div class="two-col">
<div>
<p>
The ticker filter path now normalizes paste-heavy input before it reaches state-dependent parsing.
This covers full-width commas, repeated whitespace, control characters, and casing drift.
</p>
<pre><code>export const normalizeTickerFilterInput = (value: string): string =&gt;
value
.normalize("NFKC")
.replace(/[\u0000-\u001f\u007f]+/g, " ")
.replace(//g, ",")
.replace(/\s+/g, " ")
.toUpperCase()</code></pre>
</div>
<div>
<p>
Shell semantics were strengthened without changing the route structure. The new skip link and current-page
annotation improve keyboard and assistive navigation while staying visually quiet during normal use.
</p>
<pre><code>&lt;a class="skip-link" href="#terminal-content"&gt;Skip to terminal content&lt;/a&gt;
&lt;nav aria-label="Primary" className="terminal-nav"&gt;
&lt;Link aria-current={active ? "page" : undefined} ... /&gt;
&lt;/nav&gt;</code></pre>
</div>
</div>
</section>
<section class="panel">
<h2>Validation</h2>
<ul>
<li><code>bun test apps/web/app/terminal.test.ts</code> passed.</li>
<li><code>bun --cwd=apps/web run build</code> passed.</li>
<li>Regression coverage was added for normalization and token parsing of ticker input.</li>
</ul>
</section>
<section class="panel">
<h2>Issues, Limitations, and Mitigations</h2>
<ul>
<li>
<strong>Dolt sync limitation:</strong> <code>bd dolt pull</code> failed earlier in the session because no Dolt
remote is configured in this workspace. The code work continued, but beads remote sync was not available.
</li>
<li>
<strong>Scope control:</strong> this pass hardened the shell only. It did not audit every downstream pane,
drawer, or popover for similar edge cases.
</li>
<li>
<strong>Workflow status:</strong> this document records the implementation and validation, but the work was not
committed or pushed as part of this turn.
</li>
</ul>
</section>
<section class="panel">
<h2>Follow-up Work</h2>
<ul>
<li>No follow-up issue was created from this hardening pass beyond the completed beads item <code>islandflow-6ri</code>.</li>
<li>If terminal adaptation work continues, the next pass should examine small-screen drawer behavior and popover placement under dense live states.</li>
</ul>
<p class="note">
Document created to satisfy the required turn-documentation step for implementation changes.
</p>
</section>
</main>
</body>
</html>