mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
176 lines
10 KiB
Markdown
176 lines
10 KiB
Markdown
# 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: **`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-98` — `Cmd/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.
|