feat(#287): Dashboard v1 Phase 5 — portable import/export + import planner + workspace aggregate source of truth#301
Merged
Conversation
…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
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Dashboard v1 Phase 5 of epic #280: portable open/import/export with a transactional import planner, and — by owner decision — making the
StoredWorkspaceV1aggregate 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
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.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.dashboards: []); no new Library-only JSON is written.Re-scope (owner decisions during planning)
WorkspaceRepository(strict async, validate-before-publish per Make Dashboard a first-class module with one-dashboard-per-current-workspace v1 UX #280), retiring the flatasb:savedwrite path (read once only for legacy migration).state.savedQueriesis a projection; boot loads + projects the aggregate. Writes are serialized (app.serializeWrite) so overlapping ops can't resurrect a deleted query.Review (adversarial, pre-PR) — fixes applied in this PR
connect()didn't runloadWorkspaceOnBoot, stranding legacy migration → fixed + verified live.app.serializeWrite(with a regression test).Follow-ups filed
Verification
npm test— 3435 passing, per-file coverage gate,tsc --noEmitclean,node build/check-boundaries.mjsOK,npm run buildclean.npm run test:e2e— chromium + webkit, 90 passing.connect()fix) → File menu (Append gone, all new ops present) → Export workspace downloads a valid.json, against the served build.Checklist
npm testpasses (the per-file coverage gate is non-negotiable)npm run buildsucceeds (single-filedist/sql.html)src/core/src/dashboard/model/src/workspace, DOM insrc/ui, injected seams; boundaries check passesCHANGELOG.md([Unreleased]) updated🤖 Generated with Claude Code
https://claude.ai/code/session_01GLMprUnwnz4oaz3cnLRKcz