8.8 KiB
Astrolabe — Architecture Playbook
These documents capture the architectural patterns Astrolabe is built on. They are self-contained: everything needed to implement a pattern lives here, in Astrolabe's own domain terms (snippets, datasets, settings, Vega-Lite specs). You do not need any other repository to work from them.
They are the architectural counterpart to
docs/spec/: the spec says what the app does (behavior, acceptance points); this playbook says how we build it (state, persistence, modals, routing, rendering, inference, relationships, and the visual + interaction language).
Spec vs playbook: cite, don't restate
The two documents overlap most in §09 (visual) and §10 (interaction), which necessarily talk about user-facing widgets. At that overlap, one rule keeps them from drifting:
- The spec owns product behavior — what features exist, what surfaces appear when, what the data does. The playbook owns the how — state shape, persistence, ARIA roles, keyboard models, focus, tokens, motion.
- For product behavior, the spec is the source: cite it (
spec §NN), don't restate it, and never contradict it. A playbook bullet may name the behavior in one clause and cite the spec, then spend its words on the how (the role, the keys, the focus move) and the why (the council/canon citation). When a playbook bullet and the spec disagree, the spec wins and the bullet is the bug. Restatement is the leak: two docs describing the same behavior in their own words drift into contradiction; one cites the other instead.
How to use this playbook
- Building a feature? Read the relevant spec section first (the what), then the matching
playbook doc (the how), then implement core-first per
../IMPLEMENTATION-PLAN.md. - Each doc states the pattern, the rationale (what problem it solves, what it prevents), TypeScript sketches in Astrolabe terms, and Do/Don't rules.
- The sketches are illustrative, not finished code. Adapt them; keep the principles.
The documents
| # | Doc | Covers |
|---|---|---|
| 01 | State & Stores | Zustand stores; one source of truth; selector derivations; central useAppStore vs per-feature stores; testable action functions; debounced auto-save. |
| 02 | Persistence | The infrastructure-adapter boundary; promise-wrapped IndexedDB wrapper; lazy data loading; per-record schema versioning + migration; localStorage prefs with fallback; storage tiers + quota monitoring. |
| 03 | Modal System | Registry + coordinator + shell; one modal at a time; unsaved-change detection via snapshot; focus trap; backdrop/Escape/close dismissal. |
| 04 | Routing & Events | URL hash as view-state (restore/sync, Back/Forward); global keyboard routing; Escape priority chain; the single-source isInInteractiveContext() helper (Monaco-aware). |
| 05 | Rendering, Theming & Preview | vega-embed integration (actions:false, view.finalize()); field-name escaping; theme→config mapping; debounced non-blocking renderer; resilient error display. |
| 06 | Type Inference & Profiling | Pure, portable column-type inference (number/text/date/boolean) and the dataset profile shape. |
| 07 | Naming & Relationships | Unique-name enforcement + import auto-suffix; the bidirectional snippet↔dataset name link; rename propagation into specs. |
| 08 | vega/editor Techniques | Reference brief: borrowable Monaco-schema wiring, vega-embed lifecycle, two-tier validation, and data-flow/debounce techniques distilled from the official Vega-Lite editor — plus where we do better, and our editor-augmentation layer (structural transforms + data-aware hints). |
| 09 | Visual Design Language | The visual contract: principles inspired by IBM/Carbon, deliberate divergences (square chrome, free color/theming), the token system (Plex type, 8px spacing, role-based color, motion), component conventions, and where to mine the Carbon/IBM source repos for more. Companion: visual-specimen.html. |
| 10 | Interaction & Feedback | The interaction contract: the feedback-channel decision table, latency/feedback budgets, the non-happy-path triad, the recovery & data-safety contract, the keyboard/focus contract, and the resolved widget patterns (window splitter, toolbar, segmented controls, selectable lists, search, sort, empty states, modals). Cites spec/ for behavior; owns the how. |
| 11 | Learning Section | The /learn/ deep-dive: a marketing-surface Vite entry reusing core + the landing chart embed; markdown-authored lessons (import.meta.glob) parsed into an ordered block model; the authoring/engine split (pure parser in core; marked only in src/learn). |
The non-negotiable layering (every doc assumes this)
src/core/— portable, pure logic. No browser APIs, no React, no Monaco. Spec operations live here and are unit-tested hardest. (Docs 06, 07, parts of 05 land here.)src/app/stores/— Zustand stores. (Doc 01.)src/app/infrastructure/— the only place that touchesindexedDB,localStorage, orwindow.location. Everything else goes through these typed adapters. (Docs 02, 04.)src/app/services/&orchestration/— coordination that composes stores + infrastructure + core (lifecycle, routing sync, dependency upkeep). (Docs 03, 04, 07.)src/app/components/— React + CSS Modules. Thin; pushes logic down into stores/core so it stays testable. (Docs 03, 05.)
Why a playbook at all
Patterns written down once, in one place, stop two classes of problem: drift (the same
decision re-litigated inconsistently across features) and rediscovery (re-deriving why
something is the way it is). When a pattern here proves wrong, change the doc — don't fork
the convention silently. This is the same discipline docs/spec/ applies to behavior.