Files
chanora/poc/sqlite-storage-spike/VERIFICATION.md
T
EdisonJwa 52e8d43f69 feat(poc/storage): add sqlite-storage spike
Proof-of-concept proving the SQLite-storage exit criterion from
docs/architecture/proof-of-concept-plan.md §2:
  "Schema, migration, and repository pattern are demonstrated."

Also satisfies the SRS-089 acceptance criteria explicitly:
  "Storage implementation uses an embedded local data store and
   migration mechanism."

Implements:
  - A forward-only Migrator over a fixed Migration list, tracking
    the applied version via PRAGMA user_version. Each migration is
    applied inside an IMMEDIATE transaction; rolled back on failure.
  - Three canonical migrations (initial schema, add nickname,
    add last_connected_at) demonstrating ALTER TABLE flows.
  - A LocalDatabaseRepository implementing both BookmarkRepository
    and SettingsRepository traits.
  - Bookmark.identity_ref is a reference to a secret name, never
    a secret value (cross-checked by the secure-storage spike's
    SS-AUD-001/002 scans). This is the SAD-067 separation.

Test suite (11/11 PASS on 2026-05-13):
  - migrator brings fresh DB to latest version
  - migrator is idempotent (no-op when already current)
  - migrator applies only pending versions (catch-up upgrade)
  - migrator rejects out-of-order versions
  - migrator rejects DB newer than known migrations (downgrade guard)
  - failed migration rolls back atomically
  - bookmark CRUD round-trip
  - bookmark list ordered by recency
  - bookmark UNIQUE(host, identity_ref) enforcement
  - settings upsert + delete
  - open creates file and persists across reopen

Surfaced finding for the decision register: DEC-013 does not pin a
SQLite crate. The PoC uses rusqlite with the bundled feature
(no system libsqlite3 dependency); production code needs an
owner ruling on rusqlite vs. sqlx vs. sea-orm.

Authority: PoC plan §2, SRS-089, SDD-077, SAD-067,
SysDes-033/036/049/091.
Not product code; not promoted into chanora_storage.
2026-05-14 12:26:36 +08:00

3.4 KiB

Verification record — sqlite-storage-spike

Result

PASS. The PoC exit criterion (from docs/architecture/proof-of-concept-plan.md §2: "Schema, migration, and repository pattern are demonstrated") is met.

Environment

Field Value
Date 2026-05-13
Host OS Linux (Arch, kernel 7.0.5-arch1-1, x86_64)
Rust toolchain stable 1.95.0
rusqlite 0.32.1 (bundled SQLite)
thiserror 2

Reproduction

cargo test
cargo run --bin sqlite-storage-cli

Test run

running 11 tests
test migrator_rejects_out_of_order_versions ... ok
test migrator_rejects_db_newer_than_known_migrations ... ok
test failed_migration_rolls_back_atomically ... ok
test bookmark_repo_crud_round_trip ... ok
test bookmark_repo_unique_host_identity_is_enforced ... ok
test settings_repo_upsert_and_delete ... ok
test bookmark_repo_list_orders_recent_first ... ok
test migrator_applies_only_pending_versions ... ok
test migrator_is_idempotent ... ok
test open_creates_file_and_persists_across_reopen ... ok
test migrator_brings_fresh_db_to_latest_version ... ok

test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured;
                 0 filtered out; finished in 0.01s

CLI run

INFO spike: opening /tmp/.tmp.../chanora.sqlite
INFO spike: applying migration v1 "initial bookmarks + settings"
INFO spike: applying migration v2 "add bookmarks.nickname"
INFO spike: applying migration v3 "add bookmarks.last_connected_at"
INFO spike: schema version = 3
INFO spike: inserted bookmark ids: 1, 2

Bookmarks (most-recently-connected first):
  [1] Vigorous Pro @ cn.teamspeak.app (identity_ref=identity.primary,
      last_connected_at=Some(1715000000))
  [2] Local Lab @ ts.example.invalid (identity_ref=identity.lab,
      last_connected_at=None)

Settings.audio.aec = Some("true")

OK — schema v3 loaded, repository round-trip verified.

Coverage matrix

Concern Test(s) Result
Schema present (SDD-077) All tests open the schema PASS
Migration mechanism exists (SRS-089) migrator_* (6 tests) PASS
Idempotency migrator_is_idempotent, open_creates_file_and_persists_across_reopen PASS
Partial-upgrade catch-up migrator_applies_only_pending_versions PASS
Out-of-order migrations rejected migrator_rejects_out_of_order_versions PASS
Downgrade-guard migrator_rejects_db_newer_than_known_migrations PASS
Atomic rollback on failure failed_migration_rolls_back_atomically PASS
Repository pattern (CRUD) bookmark_repo_crud_round_trip, settings_repo_upsert_and_delete PASS
Ordering invariants bookmark_repo_list_orders_recent_first PASS
Constraint enforcement bookmark_repo_unique_host_identity_is_enforced PASS
On-disk persistence open_creates_file_and_persists_across_reopen PASS
SAD-067 separation (no secrets in DB) DTO design (identity_ref is a name); cross-checked in secure-storage-spike SS-AUD-001/002 PASS by construction

What this spike does NOT validate

  • Concurrent access from multiple processes or threads.
  • WAL checkpoint behaviour under sustained writes.
  • Database growth / vacuum policy.
  • Encrypted-at-rest variants (SQLCipher / SEE).
  • Behaviour with corrupted or partially-written DB files.
  • Mobile-specific storage paths (Android scoped storage, iOS app group containers).
  • The eventual production choice of migration engine.
  • Performance characteristics under realistic dataset sizes.