Decisions index¶
Status: living | Audience: anyone looking for where a decision is recorded | See also: ../README.md
Decisions are recorded inline, where they're made — in the design or development doc discussing the tradeoff, stamped (decision YYYY-MM-DD).
This index exists only for discoverability: one row per decision, pointing at the doc that records it.
Adding an inline stamp means adding a row here in the same commit; a row with no matching stamp, or a stamp with no row, means someone skipped half the rule.
The 2026-07-18 rows below were decided in the pre-code planning session, so they carry no issue references.
Once work runs through issues, stamps take the form (decision YYYY-MM-DD, #NN).
| Date | Decision (one line) | Recorded in | Status |
|---|---|---|---|
| 2026-07-18 | Stack locked in one pass: Electron + TypeScript + React with electron-vite / electron-builder and the seed kit's governance toolchain | development/TechStack.md § The stack | Adopted |
| 2026-07-18 | ATC audio commits to all four behaviors: activity lights, one-click solo, stereo pan, priority auto-duck | design/Audio.md § The unifying principle | Adopted |
| 2026-07-18 | Ducking rule: duck only under a strictly-higher-priority active stream, to −12 dB, fast duck / slow release | design/Audio.md § ATC streams | Adopted |
| 2026-07-18 | Per-stream output-device routing for ATC streams | design/Audio.md § ATC streams; development/TechStack.md § Why this stack | Adopted |
| 2026-07-18 | Build sequencing within audio: lights → solo → pan → duck | Implementation-Plan.md § Phase 2b | Adopted |
| 2026-07-18 | Curated defaults + human-editable config file; no in-app management UI in alpha | design/Video.md § Feed sourcing; design/Stakeholders/Command-Center-Enthusiast.md § Domain of awareness | Adopted |
| 2026-07-18 | Live-stream discovery is on-demand only — no background polling | design/Video.md § Feed sourcing | Adopted |
| 2026-07-18 | Restore last session on relaunch: layouts, feed assignments, pop-outs, per-stream settings | design/Video.md § Pop-outs and restore | Adopted |
| 2026-07-18 | Tracking panel is a genuine embedded browser (the site refuses lightweight embedding — verified) | design/Tracking.md § Features | Adopted |
| 2026-07-18 | Tracking navigation stays minimal: back / forward / reload / home preset on Oshkosh | design/Tracking.md § Features | Adopted |
| 2026-07-18 | Tracking session and login persist across relaunches | design/Tracking.md § Features | Adopted |
| 2026-07-18 | Tracking panel lands in the walking skeleton — bounds-sync is the foundational layout risk | Implementation-Plan.md § Phase 1 | Adopted |
| 2026-07-18 | Kit's one-phase-in-progress rule adapted to one phase per track for the agent-assisted sprint | Implementation-Plan.md § header + intro | Adopted |
| 2026-07-18 | Alpha ships config-file-driven with unsigned builds; Casual Spotter gaps accepted and named | design/Personas.md § Alpha priority; design/Stakeholders/Casual-Spotter.md § Value | Adopted |
| 2026-07-18 | Public GitHub releases from a public repository | design/Stakeholders/Programmer-Aviator.md § Value | Adopted |
| 2026-07-18 | Governance solo-tuned for the sprint (no required reviews; protection on main only); tightens post-show | design/Stakeholders/Programmer-Aviator.md § Not expected to | Adopted |
| 2026-07-18 | Docs website is generated from docs/ by MkDocs Material — machinery in website/, Pages source moves to Actions, landing page becomes docs/index.md; ships with Phase 5 |
Implementation-Plan.md § Phase 5; development/TechStack.md § The stack | Adopted |
| 2026-07-18 | Binary assets tracked with Git LFS from first commit; legacy-Pages pointer caveat accepted until the Phase 5 site build | development/TechStack.md § The stack + Known limitations | Adopted |
| 2026-07-19 | Packaged renderer served from a loopback HTTP server (127.0.0.1); app:// demoted to a logged fallback, because YouTube's IFrame API rejects the app:// origin (error 153) |
development/TechStack.md § Known limitations | Adopted |
| 2026-07-19 | A muted higher-priority channel does not duck lower channels — its light still shows activity, but it never ducks audio the operator is actually listening to | design/Audio.md § ATC streams | Adopted |
| 2026-07-19 | Project renamed airshow-traffic-monitor / "Airshow Traffic Monitor" — airshow-generic identity; EAA AirVenture stays the first target, not the boundary |
README.md § intro | Adopted |
| 2026-07-19 | Docs-site home is docs/README.md, not a new docs/index.md — MkDocs collides the two at the site root, and the plan-frozen Implementation-Plan.md links to README.md, so README.md stays the home and index.html is deleted (refines the 2026-07-18 "landing page becomes docs/index.md" row) |
development/Pages-deployment-runbook.md § Sequencing gotcha | Adopted |
| 2026-07-19 | Session restore is one session.json (electron-store), held authoritative in memory and flushed atomically on a ~500 ms debounce (guaranteed flush on quit) |
development/TechStack.md § Session restore and pop-outs | Adopted |
| 2026-07-19 | Missing-display window bounds are validated by a pure, Electron-free function that recentres/shrinks an off-screen window onto a connected display | development/TechStack.md § Session restore and pop-outs | Adopted |
| 2026-07-19 | Per-stream volume/mute/pan are session-restored; priority stays config-owned (re-derived from config.json each launch, never session-pinned) |
development/TechStack.md § Session restore and pop-outs | Adopted |
| 2026-07-19 | Pop-outs are grid-only windows loading the same bundle at ?window=popout&id=N; feeds hand off from the main grid and return on close, and quit preserves pop-outs for restore |
development/TechStack.md § Session restore and pop-outs | Adopted |
| 2026-07-19 | Field weather sourced from the free, keyless aviationweather.gov Data API (METAR+TAF JSON); fetched main-process-only with a descriptive User-Agent, polled no more than every 5 minutes | development/TechStack.md § Known limitations | Adopted |
| 2026-07-19 | ATC streams are on-demand: they start disconnected (no network activity), the status pill is the connect toggle, and the connected set is session-persisted and restored (staggered) on relaunch | design/Audio.md § ATC streams | Adopted |
| 2026-07-19 | Reconnect back-off for a wanted-but-down stream eases from fast retries to a slow ~60 s cadence after ~5 consecutive failures, shown as a calm "feed down" state instead of a climbing counter | design/Audio.md § ATC streams | Adopted |
| 2026-07-19 | v0.1.0 is a personal-use milestone and pipeline validation, not a launch: no promotion or distribution beyond the primary operator until LiveATC.net grants stream-use clearance (a hard gate on any announcement) | Implementation-Plan.md § Milestone | Adopted |
| 2026-07-19 | Channels are managed in-panel — an add-channel dialog lists an airport's LiveATC feeds; the panel rewrites the streams block of config.json (kept hand-editable and atomically written), superseding the 2026-07-18 "no in-app management UI in alpha" for ATC audio |
design/Audio.md § ATC streams | Adopted |
| 2026-07-19 | Priority is the channel list's vertical order, set by drag or keyboard (rank 1 = top); it stays config-owned and re-derived each launch, so a hand-edit and a drag are the same edit | design/Audio.md § ATC streams | Adopted |
| 2026-07-19 | A bundled KOSH feed snapshot is the add-channel fallback when the LiveATC directory refuses automated access (Cloudflare); it can add channels offline but cannot report live feed status | design/Audio.md § ATC streams | Adopted |
| 2026-07-19 | Panel layout is a serializable split tree in session.panelLayout, rendered by a single-container canvas, retiring rrp and its LayoutStorage strings |
shared/panelLayout.ts | Adopted |
| 2026-07-19 | design/brand/tokens.css is imported canonically (straight from design/brand/) into the renderer rather than copied, so there is one source of truth for the Wyvern Watch color/type/motion system |
renderer/src/main.tsx | Adopted |
| 2026-07-19 | Barlow Semi Condensed + Inter ship as woff2 files committed to the repo (latin subset, ~141 KB total) rather than a Google Fonts CDN import, so type renders correctly offline at the airfield | renderer/src/assets/fonts.css | Adopted |
| 2026-07-19 | The System/Cream/Ember theme toggle drives nativeTheme.themeSource in the main process (not per-renderer CSS) and persists in SessionState.theme, so every window — including pop-outs — and the OS chrome follow one change instantly |
main/ipc.ts | Adopted |
| 2026-07-19 | Panel-canvas leaves render in fixed id-sorted DOM order, never by tree position, so rearranging a panel can never force React to reorder (and thus reload) an embedded video iframe | renderer/src/layout/canvasRenderOrder.ts | Adopted |
| 2026-07-19 | The main window's uniform/emphasized/fill-panel video grid modes are retired in favor of per-panel maximize plus a per-feed fit/fill toggle; pop-outs keep their own uniform/emphasized/fill grid | renderer/src/state/store.ts | Adopted |
| 2026-07-20 | A returning video panel (a pop-out closing) always docks to the BOTTOM ROW of the video grid via a video-only-region insertVideoLeafBottom op, replacing insertPanelBalanced's largest-all-leaf-group heuristic for video reopens specifically, which could otherwise land the panel in the audio/weather column |
shared/panelLayout.ts | Adopted |
| 2026-07-20 | Pop-out windows are combined via an explicit "Merge into…" control (no window-to-window drag), moving feeds into a chosen target window and closing the source | design/Video.md § Pop-outs and restore | Adopted |
| 2026-07-20 | Native menus (a Panels checkbox per panel id) plus a DOM Move-panel modal are the FR24-safe/accessible move paths landed first; pointer-driven header-drag-to-dock is deliberately deferred to feature/panel-drag-dock |
main/menu.ts | Adopted |
| 2026-07-20 | Header drag-to-dock is custom pointer-event handling plus pure hit-testing against the canvas's own computed leaf rects — never elementFromPoint and not the browser's native HTML5 Drag and Drop API — with the native Panels/Layout menus and the DOM Move-panel modal kept as the FR24-safe, accessible fallbacks for the same move |
renderer/src/layout/useHeaderDrag.ts | Adopted |
| 2026-07-20 | The react-resizable-panels dependency is removed: the panel layout is fully in-house (a serializable split tree, a single-container canvas render, custom splitters) after the last consumer (the main-window layout) moved off it in feature/panel-canvas-shell |
development/TechStack.md § Panel layout system | Adopted |
| 2026-07-20 | The panel canvas (PanelCanvas/LeafFrame/Splitter/useHeaderDrag/DragOverlay, plus the shared leaf chrome in PanelChromeButtons) is window-agnostic: it reads/writes layout state only through a LayoutController interface delivered via React context, never the main window's useAppStore directly, so a future pop-out window can reuse the same canvas and video leaf body against its own state |
renderer/src/layout/LayoutController.ts | Adopted |
| 2026-07-20 | Pop-out windows use the same panel canvas as the main window (per-window layout in PopoutState; the pop-out's own uniform/emphasized/fill-panel grid is retired in favor of maximize + per-feed fit/fill, refining the earlier 2026-07-19 row which still had pop-outs keeping that grid; the Layout Manager and named profiles stay main-window-only) |
design/Layout.md § Features | Adopted |
| 2026-07-20 | The docs site is skinned with the Wyvern Watch brand — Material's design tokens re-pointed to Cream Classic / Ember, the app's own bundled Barlow Semi Condensed + Inter woff2 (no font CDN), and the brand favicon, logo, and OG social cards wired in (the last Phase 6 docs-site slice) | website/mkdocs.yml + assets/brand/wyvern.css | Adopted |
| 2026-07-20 | The site home is an advertisement: a full-bleed hero, screenshots, three-pillar pitch, and download CTAs render above the documentation index via website/overrides/home.html; docs/README.md keeps its doc-index body and stays the resolvable, plan-frozen root page (the ad sits on top of it) |
website/overrides/home.html | Adopted |
| 2026-07-20 | The docs toolchain is pinned to MkDocs 1.x (mkdocs<2) because MkDocs 2.0 removes the plugin system and rewrites theming with no migration path — breaking Material for MkDocs and this site's overrides; mkdocs-material 9.x does not cap the core itself, so the pin lives in website/pyproject.toml. Zensical (the Material team's 1.x-compatible successor) is the eventual migration path |
development/TechStack.md § Known limitations | Adopted |
| 2026-07-20 | Panels are held to their usable minimum size at RENDER time — clampTreeToMinPx rewrites each split's shares against the current pixel span before computeLayoutRects, so a panel can never collapse into an ungrabbable sliver; a shrunk window and compounding dock/move percentages are both corrected, not just a live splitter drag (the only place the min-px floor was applied before) |
shared/panelLayout.ts | Adopted |
| 2026-07-23 | Docking a panel beside a neighbor whose parent split already runs the dock axis inserts it as a SIBLING of that neighbor, never a new same-orientation split wrapped around it (splitPanel, plus the root-dock join in movePanel) — a split nested inside a same-orientation split is a redundant level that paints a second, real, draggable divider mid-panel, the "phantom divider" that compounded over repeated docks/moves. Deliberately NOT a blanket normalizeTree flatten: that would also collapse the intentional FR24-over-video-grid grouping in the default layout and its resize-the-grid-as-a-unit behavior |
shared/panelLayout.ts splitPanel / movePanel |
Adopted |