Initial scaffold: spec, architecture playbook, and M0 skeleton

This commit is contained in:
2026-06-04 22:14:33 +03:00
commit 056644450c
51 changed files with 13754 additions and 0 deletions
+204
View File
@@ -0,0 +1,204 @@
# Astrolabe → Syto Integration Analysis
> **Question:** Can Astrolabe (a browser-based Vega-Lite snippet manager) be integrated into
> Syto's functionality? This document compares the Astrolabe specification (`docs/spec/`)
> against Syto in its current state, and recommends an integration path.
>
> **Short answer:** Not as a wholesale port, and not as the "snippet manager" it is today —
> that framing collides with Syto's stated non-goals. But the *valuable parts* of Astrolabe
> (the Chart Builder, the generic spec→render pipeline, the schema-assisted JSON editor) map
> cleanly onto a Syto-native **"chart a model"** feature, and most of the supporting tech already
> exists in the codebase. The recommendation is **harvest, don't port** — and the framing decision
> needs a `SOUL.md` ruling first.
---
## 1. Executive Summary
| | |
|---|---|
| **Conceptual fit** | Partial. Astrolabe and Syto are both local-first, browser-only, Vega-Lite-using, three-pane-ish workspaces. But Astrolabe's *primary entity* (a saved chart spec) is a thing Syto deliberately does not have. |
| **Strategic fit** | **Conflicted.** `SOUL.md` explicitly lists "Not a BI/visualization platform — charts are for exploration during wrangling, not final output" as a non-goal, and "Do One Thing Well." A *snippet library* is chart-authoring-as-product. This is the central tension and must be resolved before any code. |
| **Technical fit** | **Good for the rendering/editing layer, poor for the data-model and shell layers.** Syto already ships Vega-Lite, vega-embed, CodeMirror 6, IndexedDB persistence, a settings system, URL-hash routing, and a far stronger type/schema engine than Astrolabe's profiler. The friction is in the *parallel systems* a verbatim port would introduce. |
| **Recommended path** | **Option B (harvest into a native "Visualize" feature).** Reuse Astrolabe's Chart Builder and rendering contract; bind them to Syto **Models** instead of a new "dataset" entity; drop the snippet-as-primary-entity, the draft/published workflow, the separate dataset library, and the separate import/export envelope. |
---
## 2. The Two Products Side by Side
| Dimension | **Astrolabe** | **Syto** |
|---|---|---|
| Core artifact | A **snippet** = a saved Vega-Lite spec + metadata | A **workflow** = a declarative transform pipeline over a Source |
| Primary verb | *Author & organize charts* | *Clean & reshape tabular data* |
| Data unit | **Dataset** (named blob: JSON/CSV/TSV/TopoJSON, inline or URL) | **Source** (immutable imported table) → **Model** (derived table) |
| Persistence | Snippets (~5 MB tier) + Datasets (high-capacity tier), both local | Sources + Models in IndexedDB (lazy row data), prefs in localStorage |
| Editor | JSON editor w/ Vega-Lite schema autocomplete + live validation | CodeMirror 6 — used for transform JSON + the expression language |
| Rendering | Renders *arbitrary user specs* via reference-resolution + fit-mode transforms | Renders *programmatically generated* EDA specs (`charts.ts`) |
| Shell | 3 panes: library · editor · preview, + modals | Ribbon + sidebar + data table + slide-panel/modal dialogs |
| Routing | URL hash: `#snippet-<id>`, `#datasets/...` | URL hash: active source/model/dialog |
| Export | One JSON envelope of all snippets + datasets | Workflow v2 JSON (transforms, topo-sorted) |
| Stack stance | Implementation-agnostic spec | Preact + Signals + Arquero + CSS Modules, fixed |
**The key observation:** Astrolabe's "dataset" is conceptually Syto's "Source," and the thing you
most want to chart in Syto — a cleaned, transformed **Model** — has *no equivalent in Astrolabe at
all*. Astrolabe charts static blobs; Syto produces living, recomputed tables. A naive port would
bolt a second, weaker data-library (Astrolabe datasets) next to Syto's existing one (Sources/Models),
which directly violates SOUL's **"No Parallel Systems"** value.
---
## 3. The Strategic Tension (resolve this first)
This is not a technical blocker; it is a product-identity decision, and per project convention
(`SOUL.md` is the arbiter for contract/design decisions) it must be settled before implementation.
**What `SOUL.md` currently says:**
- *"Do One Thing Well… It's not trying to become a spreadsheet, a statistical package, a visualization tool, or a database. The EDA features… exist to help users understand their data before transforming it — not to replace dedicated analysis tools."*
- *"Not a BI/visualization platform: Charts are for exploration during wrangling, not final output. Dashboards and reporting are a separate concern."*
A **snippet manager** — a personal, searchable, import/exportable *library of saved charts* — is
squarely "charts as final output" and "a visualization tool." Porting Astrolabe as-is would
contradict two written non-goals.
**However**, there is a reading that is fully *aligned* with the rest of SOUL:
- *"Beginner-Friendly, Not Beginner-Limited"* and *"Progressive Complexity"* — today a user can clean data but has **no way to turn the result into a shareable picture.** A chart is the natural last step of a wrangling session.
- *"Leverage Existing Libraries — Vega-Lite handles charts."* The infrastructure is already paid for.
- Astrolabe's **Chart Builder** (pick a mark, map columns → spec) is the *exact* beginner-friendly, no-JSON affordance Syto favors, and the live JSON editor is the power-user escape hatch.
**The decision to make:** Is "produce a chart as the output of a workflow" *part of* doing the one
thing well (wrangling ends in a usable artifact), or is it the BI/viz scope SOUL rejects?
Two coherent resolutions:
- **(A) Amend SOUL** to permit *single-chart output of a model* (not dashboards, not a chart library-as-product) — and integrate as a native feature (§6, Option B).
- **(B) Keep it separate** — Astrolabe stays its own thing, or lives as a sibling `/tools/` mini-app that merely *shares code* with Syto (§6, Option C). The main app's non-goals stay intact.
I recommend (A) with a tightly-scoped amendment, because the value lands precisely where Syto is
currently weakest (no output artifact), and because doing it natively avoids the parallel-systems
trap. But this is the user's call to make against SOUL.
---
## 4. Feature-by-Feature Reuse Map
Legend: 🟢 already exists / strong reuse · 🟡 partial, needs adaptation · 🔴 net-new build
| Astrolabe feature | Syto today | Verdict | Notes |
|---|---|---|---|
| **Vega-Lite rendering** | `charts.ts` + `vega-embed@7` render programmatic specs into DOM refs | 🟡 | Engine present; needs a *generic* "render this arbitrary spec" path + error surface. The hardcoded EDA specs don't help directly, but the rendering primitive does. |
| **Dataset-reference resolution** (`{data:{name}}` → contents, recursing into layers) | none | 🔴 | New, but small and pure — and in Syto it resolves to a **Model's data**, not a separate dataset store. |
| **Fit-mode transforms** (Original/Width/Height/Full via `"container"`) | none | 🔴 | Small, pure, copy-on-render spec rewrite. Directly portable. |
| **JSON spec editor** | CodeMirror 6 (`CodeMirrorEditor.tsx`, `JsonEditorModal.tsx`) + lint infra (`linters/`) | 🟡 | Editor & lint plumbing exist. Missing: a **Vega-Lite schema service** for autocomplete + validation. (Note: Astrolabe's "minimap" and "VS Light/Dark/High-Contrast" editor themes are Monaco-isms; Syto is on CodeMirror — those exact settings don't carry over.) |
| **Chart Builder** (mark + X/Y/Color/Size + field types → spec) | none | 🟡→🔴 | The single most valuable, most SOUL-aligned piece. Build it against a **Model's columns** using Syto's existing schema types. High reuse of the *dialog* pattern (registry + slide-panel/modal + debounced preview). |
| **Column profiling / type inference** | `schema-engine.ts` (integer/float/date/datetime/boolean/json) | 🟢 | Syto's engine **supersedes** Astrolabe's (number/string/date/boolean). Astrolabe→Vega field-type mapping (numeric→Quantitative, date→Temporal, else Nominal) layers on top trivially. |
| **Datasets library + manager modal** | Sources/Models already *are* the data library | 🔴 *(avoid)* | Do **not** build. Reuse Sources/Models. Building it = parallel systems. |
| **Snippet library** (search/sort/CRUD, draft vs published, status, tags, storage monitor) | none | 🔴 | The genuinely new persistent entity. Only needed if going full snippet-manager (not recommended). Draft/Published has no analog in Syto's undo/redo model. |
| **Settings** (editor/performance/formatting) | `ux-settings.ts` + settings dialog | 🟡 | System exists; add render-debounce + a couple of fields. Editor-theme/minimap fields are Monaco-shaped and mostly drop. |
| **Import/Export envelope** (snippets+datasets JSON) | Workflow v2 export/import | 🔴 *(avoid)* | A second export format competing with workflow v2. If charts are part of a workflow, they belong *in* the workflow spec or alongside it — not in a rival envelope. |
| **App shell / 3-pane layout** | Ribbon + sidebar + table + slide-panel | 🔴 *(avoid)* | Don't graft Astrolabe's shell. A chart view is a *mode/panel within* Syto's shell. |
| **URL-hash routing** | Hash routing for source/model/dialog | 🟡 | Reusable, but Astrolabe's `#snippet-…`/`#datasets/…` scheme would **collide**; must namespace under Syto's existing scheme. |
| **Keyboard shortcuts** | `EventRouter` owns Ctrl+S (save), Escape priority chain, etc. | 🟡 | **Collisions:** Astrolabe binds Ctrl+S (Publish) and Ctrl+K (Datasets). Syto already owns Ctrl+S. Must reconcile, not adopt verbatim. |
| **Offline / PWA / installable** | `vite-plugin-pwa` already configured | 🟢 | Free. |
| **i18n** | i18next, en/uk, namespaced | 🟢 | New strings go in a namespace; framework is there. |
| **Toasts** | Notification system exists | 🟢 | Reuse. |
**Reuse tally:** the *rendering, editing, persistence, settings, schema, i18n, PWA, and toast*
substrate is largely present. The *data-model, shell, routing-scheme, and lifecycle* layers of
Astrolabe are either redundant with Syto or actively conflicting and should be dropped.
---
## 5. Technical Friction Points (if ported verbatim)
1. **Parallel data library.** Astrolabe datasets vs Syto Sources/Models — two stores, two
profilers, two "named data" concepts. Violates *No Parallel Systems*. (The fix: charts reference
Models.)
2. **Parallel persistence + export.** A second IndexedDB store layout and a second JSON envelope
alongside workflow v2. Two backup formats for users to confuse.
3. **Draft/Published has no home.** Syto's non-destructive model is *pipeline steps + undo/redo*,
not a per-document draft/published toggle. Astrolabe's central editing model would be a third,
unrelated state concept.
4. **Shell mismatch.** Astrolabe's library·editor·preview triptych is a *whole app*. Syto's shell is
ribbon-driven with slide-panel dialogs. They don't compose; one must yield.
5. **Routing & shortcut collisions.** Hash schemes overlap; Ctrl+S/Ctrl+K already bound.
6. **Editor-feature gap.** Syto is on CodeMirror (no minimap, different theme model); Astrolabe's
settings assume Monaco. And neither today has a **Vega-Lite schema service** — that autocomplete/
validation is net-new work on either stack.
7. **TopoJSON / arbitrary-JSON data.** Syto Sources are *tabular*. Astrolabe datasets include
TopoJSON and arbitrary JSON. Charting a Model covers the tabular case; map/topology charts would
be out of scope unless Sources grow a non-tabular kind.
None of these are unsolvable — but every one of them is *work created by the port itself*, not by
the user value. That's the signature of "harvest, don't port."
---
## 6. Integration Options
### Option A — Full port (snippet manager inside Syto)
Bring Astrolabe over more-or-less intact: snippet library, dataset manager, draft/published, its
shell, its export.
- **Pros:** Fastest way to "have Astrolabe." Feature-complete chart authoring.
- **Cons:** Maximal parallel-systems debt (§5). Directly contradicts SOUL non-goals. Two data
libraries, two export formats, shell/routing/shortcut conflicts. **Not recommended.**
### Option B — Harvest into a native "Visualize" feature ✅ *recommended*
Add charting as the natural *output* step of a workflow, reusing Syto's own primitives:
- A **"Chart" / "Visualize"** action on a **Model** opens a **Chart Builder** (Astrolabe's mark +
X/Y/Color/Size + field-type controls), populated from the Model's columns and `schema-engine`
types.
- It produces a Vega-Lite spec rendered live via the existing `vega-embed`, using a ported
**reference-resolution + fit-mode** rendering contract where the named data resolves to the
**Model's rows**.
- Power users get the **JSON spec editor** (CodeMirror, with a Vega-Lite schema service added) as the
escape hatch — consistent with *Beginner-Friendly, Not Beginner-Limited*.
- The chart (its spec) is persisted **attached to the Model** (or to the workflow), not as a separate
snippet entity. Export rides along with workflow v2 (or a sibling field), not a rival envelope.
- **Dropped from Astrolabe:** separate dataset library, draft/published, snippet search/sort/tags,
storage monitor, its shell, its import/export, its routing scheme.
- **Pros:** No parallel systems. Lands value exactly where Syto is weak (no output artifact). Maximal
reuse of existing infra. Defensible against SOUL with a *narrow* amendment ("single-chart output of
a model," not dashboards/library).
- **Cons:** Requires the SOUL decision (§3). Loses Astrolabe features that depend on the
snippet/dataset model (TopoJSON/URL datasets, multi-snippet library). Net-new: schema service,
builder dialog, render contract.
### Option C — Sibling `/tools/` mini-app
Port Astrolabe as a self-contained app under `/tools/astrolabe/`, sharing only *code* (vega render
helpers, CodeMirror wrapper, i18n) with the main app — no AppStore/DialogStore coupling.
- **Pros:** Keeps the main app's non-goals pristine (it's a separate utility, like other tools).
Lower conceptual conflict. Astrolabe keeps its own model.
- **Cons:** Syto's `/tools/` layer is designed for *small, single-purpose* utilities; Astrolabe is a
full application — a stretch for that slot. Still carries Astrolabe's whole parallel data model,
just quarantined. "Integration" here means "co-located," not "unified" — limited synergy.
---
## 7. Recommendation
1. **Make the SOUL call first (§3).** Decide whether single-chart *output of a model* is in scope.
If **no**, stop here or pursue Option C as a quarantined sibling. If **yes**, amend SOUL with a
tight scope statement and proceed to Option B.
2. **Pursue Option B.** Harvest the three high-value, well-aligned pieces:
- the **Chart Builder** (bound to a Model, driven by `schema-engine` types),
- the **rendering contract** (reference-resolution + fit modes, resolving to Model data),
- the **schema-assisted JSON editor** (CodeMirror + a new Vega-Lite schema service).
3. **Drop the parallel-systems pieces:** separate dataset library, draft/published, snippet
library + storage monitor, separate import/export envelope, Astrolabe's shell and routing scheme.
4. **Reconcile, don't adopt,** the cross-cutting surfaces: fold settings into `ux-settings`,
namespace any new hash state under Syto's scheme, resolve the Ctrl+S/Ctrl+K shortcut collisions.
This delivers the genuinely useful core of Astrolabe — turning cleaned data into a chart, with a
beginner path and a power-user path — while staying true to *Do One Thing Well* and *No Parallel
Systems*, and reusing the infrastructure Syto has already built.
---
## 8. Open Questions for the User
- **SOUL scope:** Is "a chart as the output of a workflow" inside Syto's mission, or out? (Blocks everything.)
- **Persistence model:** Should a chart spec live *on a Model*, *in the workflow v2 export*, or as a new top-level entity?
- **Non-tabular data:** Do we ever need TopoJSON / arbitrary-JSON charts (maps), which Syto Sources can't currently hold? If not, that simplifies scope considerably.
- **Editor depth:** Is full Vega-Lite schema autocomplete/validation in scope, or is a plain JSON editor + live error surface enough for v1?