Skip to content

feat(storage): add Phase 5 SQLite contract preview - #92

Draft
Yuge Zhang (ultmaster) wants to merge 4 commits into
microsoft:mainfrom
ultmaster:feat/multi-backend-storage-phase-5-sqlite-preview
Draft

feat(storage): add Phase 5 SQLite contract preview#92
Yuge Zhang (ultmaster) wants to merge 4 commits into
microsoft:mainfrom
ultmaster:feat/multi-backend-storage-phase-5-sqlite-preview

Conversation

@ultmaster

@ultmaster Yuge Zhang (ultmaster) commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Note

Reworked onto current main after #93 merged, so this diff contains Phase 5 only.

What changed

  • Add an isolated node:sqlite implementation of the structured storage contracts.
  • Cover lifecycle, strict schema v1, transactional migrations, Space/Node/log/Task repositories, atomic ordered writes, deletion fencing, and connection-lifetime anti-resurrection.
  • Add the missing reusable Tasks/Runs contract and align Disk and SQLite with the Run-completion semantics now present on main.
  • Reuse refactor(storage): Phase 4.5 — move storage-owned layout inside the boundary #93's backend-neutral naming owner and move Canvas record-shape validation to a backend-neutral owner.
  • Recognize SQLite as an available preview adapter while deliberately keeping it non-selectable.
  • Update the storage architecture and authoritative roadmap for the merged Phase 4.5 baseline and completed Phase 5 preview.

Reading the new backend

Nine implementation modules land under backends/sqlite/, plus contract and integration tests, test support, and the immutable v1 migration fixture:

File Owns
structured-store.ts StructuredStore — adapter lifecycle and composition
space-repository.ts SpaceRepository
space-nodes.ts SpaceNodes
space-tasks.ts SpaceTasks
space-logs.ts SpaceEvents, SpaceChanges, and the delta log
space-write.ts SpaceHandle.write
database.ts connection, schema v1, migrations, deletion fencing, and tombstones
rows.ts row statements and JSON codecs
identity.ts title/label collision-key allocation

The first six align with the port surface and reuse the Disk filenames for the same roles, so the parity suites in ports/contracts/ map directly across both adapters. identity.ts remains pure allocation logic and imports #93's backend-neutral naming rules, keeping collision behavior identical without importing the Disk adapter.

Why

This provides a concrete contract and churn preview before SQLite is wired into runtime composition, without changing product capabilities. It tests whether the extracted storage seam supports a transactional backend before committing to production selectability.

Selecting sqlite still fails in validateStorageProfile, now with a distinct preview diagnostic: the adapter is available but not selectable. Physical Disk reads, World bootstrap, Blob placement, import/export, and Workspace remounting still require later composition work.

Scope

  • 25 files, +3,879 / −247 against current main
  • Confined to apps/server/src/modules, docs/architecture/canvas-storage.md, and docs/proposals/multi-backend-storage.md
  • No web/shared protocol, UI, runtime composition, Disk-to-SQLite migration/import, Postgres, Azure, Agenetes, or RFS changes
  • Disk remains the only selectable structured backend

Validation

All re-run on the published head (2bc7a6bb):

  • pnpm run check — passed
    • server: 120 files, 1,096 passed / 2 todo
    • web: 131 files, 990 passed
    • shared: 36 files, 363 passed
    • lint, format, typecheck, i18n parity, agent-team skills, and license headers passed
  • pnpm run build — passed
  • pnpm --filter @huabu/server exec vitest run src/modules/storage — 18 files, 335 passed against Disk, SQLite, and the shared port contracts
  • pnpm --filter @huabu/server typecheck — passed

Yuge Zhang (ultmaster) and others added 4 commits August 18, 2026 12:53
`values.ts` held three unrelated concerns behind a name that described
none of them: JSON codecs, `spaces` row statements, and collision-key
allocation.

Split it where a dependency boundary already ran. `rows.ts` owns
everything that touches a stored column or `DatabaseSync`. `identity.ts`
owns the pure title and label allocation rules, and imports no SQLite at
all — which is what makes the cut a boundary rather than a preference.

Several consumers now depend on less: `space-logs`, `space-tasks` and
`structured-store` need only `rows.ts`, and `space-nodes` takes a single
symbol from `identity.ts` in place of a mixed five-symbol block.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant