11 KiB
Spec ⇄ Implementation Audit
Generated 2026-06-07 against
main@807d3c8(version 0.1.0). Method: one read-only auditor per spec section (00–10) classified every discrete requirement, then an adversarial verify pass re-checked each claimed gap to refute false negatives. Baseline at audit time:typecheckclean, 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/08explicitly lists the formatter in the M2 borrow list (json-stringify-pretty-compactformat action) — so this was scoped for M2 and dropped, not deferred. - Evidence:
SpecEditor.tsx:264-267(rawgetValue(), no format);monaco-schema.tsregisters diagnostics only;grep registerDocumentFormattingEditProvider→ no hits. - Decision needed: implement the formatter (small, self-contained), or amend §03A /
architecture/08to 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(nodisabled); contrastChartBuilderModal.tsx:483which already doesdisabled={!valid}— the precedent/pattern exists in-repo. - Decision needed: cheap to align (derive a
canSavefrom 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 → immediatedismiss). - 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-98—Cmd/Ctrl+,opens the editor settings popover, not a modalApp.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 onboarding canvas (welcome + example gallery, replacing the old placeholder seed), and the list's no-search-matches empty state all done. Deferred: search, sort UI+persistence, 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;
datasetRefsrelationships. 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
- Reconcile §01 spec drift with §07 (settings-as-popovers) — pure doc fix, do now.
- Decide §03A auto-format — implement (small) or amend the spec; it's the only true "marked-complete-but-missing" item.
- Align §05 Save-disabled using the existing
ChartBuilderModalpattern (cheap). - 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.