Files
astrolabe/docs/spec-implementation-audit.md
T

10 KiB
Raw Blame History

Spec ⇄ Implementation Audit

Generated 2026-06-07 against main @ 807d3c8 (version 0.1.0). Method: one read-only auditor per spec section (0010) classified every discrete requirement, then an adversarial verify pass re-checked each claimed gap to refute false negatives. Baseline at audit time: typecheck clean, 470 tests passing (40 files).

Verdict

Astrolabe is substantially complete against its own spec. Across the 11 spec sections, 320 of 344 discrete requirements are fully implemented (93%). Of the remaining 24:

  • 16 are deferred-by-plan to M6 (the milestone in progress) — planned, not forgotten.
  • 3 are genuine gaps the plan does not account for (one missing feature, one divergence, one partial animation).
  • 3 are partials that are really "core done, UI deferred to M6."
  • 2 are documented/intentional divergences (Settings architecture) — these are spec drift to reconcile, not implementation bugs.

The MVP loop (M1) and everything through M5 is solid. The only work that escapes the M6 net is small and listed under Verified gaps below.

Scorecard

§ Section State impl gaps (non-impl)
00 Product Overview complete 28
01 Application Shell & Nav 🟢 mostly 27 3 partial · 1 deferred · 1 diverges
02 Snippet Library 🟡 partial 28 11 deferred (search/sort/storage-mon)
03 Editor & Draft/Published 🟢 mostly 38 1 missing (auto-format)
04 Live Preview 🟢 mostly 31 1 deferred (busy indicator)
05 Datasets complete 31 1 diverges (Save not disabled)
06 Chart Builder complete 25
07 Settings complete 27
08 Import & Export 🟢 mostly 33 1 deferred (atomic rollback on quota)
09 Data Model & Persistence 🟡 partial 18 1 deferred (sort preference §09D)
10 Non-Functional 🟡 partial 34 2 partial · 1 deferred

partial totals are concentrated in §10 because they reflect "infrastructure built, M6 UI pending" rather than absent capability.


Verified gaps (NOT covered by the M6 plan)

These survived the adversarial verify pass and have no deferral note in IMPLEMENTATION-PLAN.md. Worth a decision now.

1. §03A — Automatic JSON reformatting on type/paste · MISSING

Spec §03A requires that pasting and typing trigger automatic reformatting so JSON stays consistently indented. No document-formatting provider is registered anywhere in src/. The editor buffers raw text as-typed; only programmatic rewrites (Extract) emit pretty JSON via JSON.stringify(…, null, 2).

  • M2 is marked complete, and architecture/08 explicitly lists the formatter in the M2 borrow list (json-stringify-pretty-compact format action) — so this was scoped for M2 and dropped, not deferred.
  • Evidence: SpecEditor.tsx:264-267 (raw getValue(), no format); monaco-schema.ts registers diagnostics only; grep registerDocumentFormattingEditProvider → no hits.
  • Decision needed: implement the formatter (small, self-contained), or amend §03A / architecture/08 to drop the requirement if auto-reformat-on-type is no longer wanted.

2. §05 — Dataset Save button not disabled until valid · DIVERGES

Spec §05 says Save is disabled until a name and valid data/URL are present. Implementation keeps the button always clickable and validates on submit (save() returns false, sets formError). Functionally safe, but the spec'd affordance (disabled state) never appears. No deferral note exists.

  • Evidence: DatasetsModal.tsx:436 (no disabled); contrast ChartBuilderModal.tsx:483 which already does disabled={!valid} — the precedent/pattern exists in-repo.
  • Decision needed: cheap to align (derive a canSave from the existing validation), or accept submit-time validation and note it in §05.

3. §01F — Toast fade-out missing · PARTIAL

Spec §01F: toasts "appear/disappear with a brief fade." Toasts fade in (@keyframes toast-in, reduced-motion respected) but on dismiss are removed from the store immediately — React unmounts with no fade-out.

  • Evidence: Toaster.module.css:62-71 (in only); NotificationStore.ts:77 (immediate filter-out); Toaster.tsx:57 (timeout → immediate dismiss).
  • This is M6 polish territory but isn't itemized in the M6 list. Add an exit animation (delay-before-removal or AnimatePresence-style pattern).

Spec drift to reconcile (not a code bug)

§01B/§01C vs §07 — Settings: modal or popover?

§01B (header entry points) and §01C (modal set) still describe Settings as a header button opening a modal. §07 and architecture/10 deliberately redesigned Settings into distributed, live-applied per-pane disclosure popovers (no modal, no header button), and the implementation correctly follows §07:

  • modal-registry.ts:14-16 — "Settings is deliberately NOT a modal"
  • EventRouter.ts:91-98Cmd/Ctrl+, opens the editor settings popover, not a modal
  • App.tsx:67-102 — header has Datasets/Import/Export but no Settings button

The code is right; the spec is internally inconsistent. Action: update §01B/§01C to match the §07 distributed-settings decision (per AGENTS.md "fix spec/code drift immediately"). Same note applies to the §01C modal set still listing Settings.


Deferred to M6 (planned — informational)

All of the following are explicitly deferred in IMPLEMENTATION-PLAN.md. They are the real remaining M6 surface area:

Area Spec State
Search (filter, clear, scope) §02 not started; 5 sub-reqs
Sort controls + persistence §02, §09D hardcoded modified/desc (= spec default); no UI / no ux-prefs key
Two distinct empty-state messages §02 single message; tied to search
Storage Monitor UI §02, §10 core + adapter done & tested (storage-estimate); no component
Quota-failure save warning (UI) §02, §10 error→toast path exists; no monitor UI
Render busy indicator (>~1s) §04, §10 non-blocking render done; overlay + aria-busy deferred (TODO)
Atomic rollback on quota during import §08 warns on overage; no hard-fail rollback (TODO transfer.ts:135)
About & Privacy + Donate modals §01 typed in ModalName, no components/registry entries
Offline/PWA manual verification §10 configured (registerType:'prompt'); not yet manually verified

Note the Storage Monitor and sort are "groundwork landed, UI pending" — the core logic ships and is tested (e.g. storage-estimate.test.ts), so M6 is wiring, not building from zero.


Section detail

  • §00 Product Overview — complete. Every overview claim (local-first, no account, JSON editing, live preview, reusable datasets, draft/published, import/export, three-pane + modals) maps to shipped code.
  • §01 Shell — mostly complete. Three-pane layout, resize handles, toggle strip (a11y), keyboard router, URL routing w/ back-forward, toasts, modal system all present. Gaps: About/Donate modals (deferred), toast fade-out (partial), Settings-modal drift.
  • §02 Snippet Library — partial. List, list-item (status + linked datasets), metadata panel (rename/comment/timestamps), create/duplicate/delete, naming, first-run seed all done. Deferred: search, sort UI+persistence, dual empty states, storage monitor.
  • §03 Editor — mostly complete. Draft/Published, auto-save, debounced auto-render, publish/revert + confirm, inline errors, Extract-to-Dataset all working. Gap: auto-reformat-on-type (missing, see above).
  • §04 Live Preview — mostly complete. Rendering contract (ref resolution + fit-mode, recursive, tested), empty/error handling, tailored dataset-not-found message, responsive resize. Deferred: >1s busy indicator.
  • §05 Datasets — complete. Two-pane modal, profile inference, ref resolution, bidirectional links, extract flow. Minor divergence: Save not disabled-until-valid.
  • §06 Chart Builder — complete. Five marks, four channels, Tier-B smart defaults + guards, channel transforms (aggregate/bin/timeUnit), sort/stacking, pre-population, validation, live preview, snippet creation + linking. Matches research doc intent.
  • §07 Settings — complete. Three clusters as distributed disclosure popovers, live apply, theme header toggle, Cmd/Ctrl+,, localStorage load-with-fallback, hydrate before paint.
  • §08 Import/Export — mostly complete. Envelope build, shape detection, normalization, merge/collision/auto-suffix, feedback messaging — all tested. Deferred: atomic rollback on quota.
  • §09 Data Model — partial. Snippet/Dataset/UserSettings schemas with versioned read-time migrations; storage tiers separated; datasetRefs relationships. Deferred: sort preference (§09D) — only panel layout (widths/visibility) persisted today.
  • §10 Non-Functional — partial. Keyboard routing, focus management, modal behavior, reduced-motion, debouncing, error→toast safety all comprehensive. Partial/deferred: storage-monitor warnings, >1s busy indicator, offline manual verification.

Suggested next actions

  1. Reconcile §01 spec drift with §07 (settings-as-popovers) — pure doc fix, do now.
  2. Decide §03A auto-format — implement (small) or amend the spec; it's the only true "marked-complete-but-missing" item.
  3. Align §05 Save-disabled using the existing ChartBuilderModal pattern (cheap).
  4. Proceed with M6 for the rest — search, sort, storage-monitor UI, About/Donate, toast fade-out, busy indicator, atomic rollback, offline verification. Most have core groundwork already landed.