Skip to content

feat(#287): Dashboard v1 Phase 5 — portable import/export + import planner + workspace aggregate source of truth#301

Merged
BorisTyshkevich merged 2 commits into
mainfrom
feat/portable-import-export-287
Jul 18, 2026
Merged

feat(#287): Dashboard v1 Phase 5 — portable import/export + import planner + workspace aggregate source of truth#301
BorisTyshkevich merged 2 commits into
mainfrom
feat/portable-import-export-287

Conversation

@BorisTyshkevich

Copy link
Copy Markdown
Collaborator

What & why

Dashboard v1 Phase 5 of epic #280: portable open/import/export with a transactional import planner, and — by owner decision — making the StoredWorkspaceV1 aggregate the single source of truth for the saved-query collection.

Closes #287. (See the deferral note below — the trust preflight moved to #288 by owner decision, recorded in #68/#280.)

Delivered

  • One canonical interchange format (altinity-sql-browser/portable-bundle, PortableBundleV1) through the deterministic canonical encoder. Export Dashboard = dependency closure (Dashboard + only its referenced queries); Export workspace = full catalog + zero/one Dashboard. Export never mutates identity/revision.
  • Transactional import planner (src/workspace/import-planner.ts): parse → schema validation → select Dashboard → dependency closure → conflict detection → decisions → complete ID remapping → central reference rewrite → candidate workspace → whole-workspace validation → atomic commit. Conflict actions use-existing (auto only on canonical equality), copy (fresh id + full rewrite), replace, skip (skipping a required Dashboard dep invalidates the import, never silently drops a tile). Multi-Dashboard bundles ask which one.
  • Legacy Library v1/v2 files remain importable (normalized to an in-memory bundle with dashboards: []); no new Library-only JSON is written.
  • File menu reworked (Append gone): New workspace / Import queries / Import Dashboard / Replace workspace / Export Dashboard / Export workspace, with a global-default + per-row-override conflict dialog and a multi-Dashboard picker. Import Dashboard/Replace workspace/New workspace confirm before destructive commits.

Re-scope (owner decisions during planning)

Review (adversarial, pre-PR) — fixes applied in this PR

  • CRITICAL: basic-auth connect() didn't run loadWorkspaceOnBoot, stranding legacy migration → fixed + verified live.
  • MAJOR: Import Dashboard silently discarded an existing Dashboard → now confirms first.
  • MAJOR: concurrent CRUD commits could resurrect a deleted query → serialized via app.serializeWrite (with a regression test).
  • Minor: import-planner aliasing (now deep-cloned), dirty-dot tooltip wording.

Follow-ups filed

Verification

  • npm test3435 passing, per-file coverage gate, tsc --noEmit clean, node build/check-boundaries.mjs OK, npm run build clean.
  • npm run test:e2e — chromium + webkit, 90 passing.
  • Live: basic-auth login (exercises the connect() fix) → File menu (Append gone, all new ops present) → Export workspace downloads a valid .json, against the served build.

Checklist

  • npm test passes (the per-file coverage gate is non-negotiable)
  • Tests added/updated in the same change as the code
  • npm run build succeeds (single-file dist/sql.html)
  • Layers kept honest: pure logic in src/core/src/dashboard/model/src/workspace, DOM in src/ui, injected seams; boundaries check passes
  • No new runtime dependency
  • README / CHANGELOG.md ([Unreleased]) updated
  • Reconciled affected tracked work (roadmap Roadmap to 1.0.0 #68, epic Make Dashboard a first-class module with one-dashboard-per-current-workspace v1 UX #280, ADR-0001 addendum, CHANGELOG)

🤖 Generated with Claude Code

https://claude.ai/code/session_01GLMprUnwnz4oaz3cnLRKcz

BorisTyshkevich and others added 2 commits July 18, 2026 13:08
…onal import planner + workspace aggregate as source of truth

Replace the ambiguous Library Append UX with resource-oriented portable-bundle
operations and make the StoredWorkspaceV1 aggregate the single source of truth
for the saved-query collection.

Model layer (100% covered):
- src/dashboard/model/dashboard-export.ts — dependency-closure + full-workspace
  export builders (deep-clone; never mutate identity/revision).
- src/dashboard/model/legacy-bundle.ts — legacy Library v1/v2 → in-memory
  PortableBundleV1 (dashboards: []), re-validated, never partial.
- src/workspace/import-planner.ts — the transactional planner: conflict
  detection (by id, then canonicalEqual), auto use-existing only on canonical
  equality, complete ID mapping (copy → fresh id), bulk reference rewrite,
  skip-invalidates-required-dep, candidate validated before commit.

Persistence unification (owner decision — strict async migration):
- All query CRUD (create/edit/rename/delete/star) commits the whole workspace
  through WorkspaceRepository (validate-before-publish, #280), retiring the flat
  asb:saved write path (read once only for legacy migration). state.savedQueries
  is now a projection; boot loads + projects the aggregate. Writes are serialized
  (app.serializeWrite) so concurrent ops can't resurrect a deleted query.

File menu (src/ui/file-menu.ts):
- New workspace / Import queries / Import Dashboard / Replace workspace /
  Export Dashboard / Export workspace, with a global-default + per-row-override
  conflict dialog and a multi-Dashboard picker. Import Dashboard confirms before
  replacing an existing Dashboard. Markdown/SQL one-way share downloads kept.

Trust preflight + durable transport deferred to Phase 6 (#288): every Phase-5
file op only commits to the workspace, never executes queries.

Reconciled: roadmap #68, epic #280, CHANGELOG [Unreleased], README file-ops
section, ADR-0001 addendum. Follow-ups filed: #299 (star → tile wiring),
#300 (corrupt-aggregate surfacing).

Gate: npm test 3435 passing (per-file coverage), tsc clean, boundaries OK,
build clean; e2e chromium+webkit 90 passing; live basic-auth login + File-menu
export verified against the served build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLMprUnwnz4oaz3cnLRKcz
…consistency

The File-menu rework renamed the document concept Library → Workspace (New
workspace / Import queries / Export workspace), but the left drawer tab and the
Dashboard empty-state still said "Library". Rename the tab to "Queries" (it
lists the query collection, matching the "Import queries" language and reading
naturally next to "History"), update the Dashboard empty-state and the README /
ontime-demo panel references, and the tab-label test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLMprUnwnz4oaz3cnLRKcz
@BorisTyshkevich
BorisTyshkevich merged commit 367a817 into main Jul 18, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dashboard v1 phase 5 (#280): portable open/import/export + transactional import planner

1 participant