Skip to content

fix(wallet): preserve transaction ownership and sync basket mappings - #561

Merged
ty-everett merged 12 commits into
mainfrom
codex/wallet-storage-correctness
Sep 23, 2026
Merged

ty-everett merged 12 commits into
mainfrom
codex/wallet-storage-correctness

Conversation

@ty-everett

@ty-everett ty-everett commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator

Program and scope

Closes #540. Closes #543. Coordination: #558.

A cold raw-transaction read currently calls store-wide startup from inside its caller's transaction, attempting to acquire SQLite's already-owned single connection. Read settings through the caller's transaction and keep them out of the shared cache; do not start prepared-BEEF background work before commit. The same lookup now tolerates an absent optional inputBEEF instead of throwing while returning stored raw bytes.

Output sync currently converts a missing basket mapping into an unbasketed output. Validate the source and mapped IDs before changing a new entity or applying a newer update. Newer valid updates now apply basket assignment/removal. Equal or older updates retain existing conflict behavior, including deliberate local relinquishment.

Impact and release

  • Affected: @bsv/wallet-toolbox, compatible fixes in the existing unpublished 2.13.2 candidate. No public signature, wire, schema, runtime-target or dependency change.
  • README, changelog, governed release notes and generated package ledger are updated. No schema migration. Incoherent sync pages must retry after the missing basket mapping is available. Previously lost baskets require a verified newer source update; no historical state is guessed or rewritten automatically.
  • Persistence and browser/shared entity boundaries are treated as critical. The cache is never temporarily populated with transaction-local settings, so concurrent callers cannot observe rolled-back values.

Verification

Declared Node 24.18.0 / pnpm 10.33.2:

  • Workspace build; health, lint, formatting and typecheck: passed.
  • Wallet packed CJS/export/declaration consumers: passed.
  • Full wallet coverage: 279 suites, 2,968 tests passed, one pre-existing skipped test.
  • Patch coverage: 95.92%, 47/49 changed line/branch points, against main 57d72e2.
  • Focused integration: 20 tests passed. Real one-connection SQLite tests cover full and sliced reads, proven/request rows, absent optional BEEF, empty/out-of-range/missing data, cold/warm cache, commit/rollback and no premature backfill. Basket tests cover missing/invalid IDs, retry without entity mutation, unbasketed outputs, newer repair/removal, idempotence, equal/older relinquishment and rollback of preceding writes in a real database transaction.
  • Negative proof against the old production files: 10 of the 13 new regression tests fail, including the actual connection-pool timeout and silently lost mapping.
  • Complete diff critically self-reviewed for transaction ownership, rollback, shared-state isolation, referential integrity, conflict precedence, compatibility, artifacts and operational recovery.
  • No dependency change, quality suppression, weakened threshold or new skipped test.
  • Exact-head hosted CI, browser/mobile profiles, CodeQL, Codecov patch coverage and zero-new-Sonar gate passed on 4be36b1908e6e7578f61c50af61c742d3af25646: CI run.
  • No open review threads or CodeQL alerts on the current merge ref. Final head verified. The prospective merge tree onto main 77df0ca8 exactly matches the tested head tree.

Publication and deployment remain in the coordinated release after integration and full main acceptance.

Coverage follow-up adds a public StorageKnex MySQL query-compilation regression through transaction-local settings (no live MySQL claim) and a spent-output mapping comparison regression. All 22 focused cases pass. Combined integration with #539 and the current dependency-governance correction passes; thresholds and exclusions are unchanged.

E-Jacko and others added 7 commits September 16, 2026 15:57
…nsaction

With transactions disabled for SQLite, each DDL statement autocommits while
knex records the migration in knex_migrations only after the whole file's up()
resolves. A process killed mid-file — or between a file's last statement and
the journal insert — leaves objects the journal never recorded, and every later
migrate.latest() re-runs that file from its first statement and fails on
"table ... already exists" or "duplicate column name", permanently, with no
in-package recovery path. For a wallet with a local SQLite store, a force-quit
during first-run bring-up is enough.

SQLite DDL is transactional, so letting knex wrap each migration file rolls an
interrupted migration back whole and keeps its journal insert atomic with its
DDL. This also covers the alterTable and index-adding migrations, which
statement-level idempotence guards cannot: knex exposes no portable hasIndex,
and guards never close the journal-write window.

PRAGMA foreign_keys is still issued outside migrate.latest(). SQLite's
single-connection pool means the migration transaction inherits it, and knex's
own alter-table rebuild deliberately leaves an ambient pragma alone while
transacting (sqlite3/schema/ddl.js: enforceForeignCheck = transacting ? null :
false), so the knex#4155 constraint the previous comment describes still holds.

MySQL behaviour is unchanged: isSQLite was already false there, so
disableTransactions was already false. dropAllData()'s own setting is
deliberately untouched.

Closes #538
…sqlite-migration-integration

# Conflicts:
#	packages/wallet/wallet-toolbox/src/storage/StorageKnex.ts
…n-integration

# Conflicts:
#	docs/reference/package-api-migrations.md
@codecov

codecov Bot commented Sep 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sonarqubecloud

Copy link
Copy Markdown

@ty-everett ty-everett left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final maintainer critical review of 4be36b1: reviewed transaction-owned connection use, uncached transaction-local settings, background-work isolation, optional BEEF handling, SQLite/MySQL substring selection, basket-map validation before mutation, newer-update repair and equal/older conflict preservation. Regression evidence covers real SQLite rollback/retry, negative proof against the prior implementation, full wallet coverage and exact-tarball browser/mobile profiles. CI 35812837677 and every required check pass, including Codecov patch and zero-new-Sonar; no open review threads or CodeQL merge-ref alerts. Prospective merge tree onto current main 77df0ca equals the tested head tree. No blocking finding remains. Self-review is recorded as a comment because GitHub cannot approve the author’s own PR. Coordinated release remains gated on combined-main acceptance.

@ty-everett
ty-everett marked this pull request as ready for review September 23, 2026 03:20
@ty-everett
ty-everett merged commit 476b135 into main Sep 23, 2026
43 checks passed
@ty-everett
ty-everett deleted the codex/wallet-storage-correctness branch September 23, 2026 03:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants