Skip to content

fix(startup): a failed startup is the session's error; a store at an older schema fails it in its own words - #1041

Merged
cryptskii merged 2 commits into
mainfrom
fix/startup-reports-the-sdk-refusal
Sep 27, 2026
Merged

cryptskii merged 2 commits into
mainfrom
fix/startup-reports-the-sdk-refusal

Conversation

@cryptskii

@cryptskii cryptskii commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

What

Both appliance phones sat on "STARTING RUNTIME / WARMING UP BRIDGE" with no end. Their stores were left at client schema 24 by an older build; main expects 25 and does not migrate. Nothing told the page: initialize_sdk_core set SDK_READY false and handed the error to a caller that only logged it, so the phase stayed runtime_loading, and Kotlin logged "SDK initialized" after initSdk answered false.

  • A failed startup is the session's error. It is recorded as the session's fatal error, so the phase is error and the page shows Rust's reason on its error screen. A later successful startup takes back the failure it recorded; an error anyone else reported stays.
  • Startup opens the client store. A store this build refuses fails startup in the store's own words. Unopened, the refusal surfaced through whatever read the store first: on Android that was the sealed-seed read, which was downgraded to a warning and re-reported as "wallet seed not unlocked". A seed vault that cannot be read now fails init as itself.
  • Kotlin logs a failed initSdk as a failure.
  • Deleted: initSdkV3 had no caller and declared the SDK ready after setting only the storage directory. It goes with its Kotlin declarations and InitFailed, which only it produced; Envelope field 31 is reserved.
  • Ledger: §6.36 row B-START.

Verification

  • dsm_sdk lib sdk::session_manager + ingress::tests on Postgres, single-threaded: 35 passed. New: a_store_at_an_older_schema_fails_startup_as_the_sessions_error, on the running fleet — a device created as wallet creation creates it, its store left as schema 24 left it (stamped 24, without the table 25 added), the process restarted; startup fails with SCHEMA RESET REQUIRED, the phase is error with that message; after the wipe the refusal names, startup succeeds and the error is gone. And a_startup_failure_is_the_sessions_error_until_a_startup_succeeds.
  • Mutations: the recording dropped → the test red (runtime_loading, not error); the store open at startup dropped → red (startup answered OK). Both restored, green.
  • cargo check -p dsm -p dsm_sdk --tests, cargo check -p dsm_vertical_validation, cargo ndk -t arm64-v8a check -p dsm_sdk --features jni,bluetooth: clean. ./gradlew compileDebugKotlin compileDebugUnitTestKotlin: clean. TS protos regenerated; type-check: clean.
  • flow_assertions.sh, bridge_rpc_names.py, bridge_contracts_gate.sh, ci_scan.sh, enforce-guardrails.sh, ci/conformance_evidence.py: pass. make lint: passed.
  • Device: an APK carrying this branch and feat(wallet): offline funding and the appliance on the send screen; balance rows state their offline allocation #1040, the native library built from crates/dsm-android-anchor (on_device_installs), installed over the data on both appliance phones (A54, 5GN), whose stores are at schema 24. Before: "STARTING RUNTIME / WARMING UP BRIDGE" with no end. After: the error screen reads ERROR: startup: init_dsm_sdk failed: Storage error: the client store: SCHEMA RESET REQUIRED: client database is version 24, this build expects 25. DSM beta does not migrate — wipe the app database and re-provision from the wallet seed.; logcat shows phase=error on both.

… an older schema fails it in its own words

Both appliance phones sat on "STARTING RUNTIME / WARMING UP BRIDGE" with no
end: their stores were left at schema 24 by an older build, this build expects
25 and does not migrate, and nothing told the page. `initialize_sdk_core` set
SDK_READY false and returned the error to a caller that logged it, so the phase
stayed `runtime_loading`; Kotlin then logged "SDK initialized" after `initSdk`
answered false.

- A failed startup is recorded as the session's fatal error, so the phase is
  `error` and the page shows Rust's reason. A later successful startup takes
  back the failure it recorded; an error anyone else reported stays.
- Startup opens the client store. A store this build refuses fails startup in
  the store's words. Unopened, the refusal surfaced through whatever read the
  store first: on Android that was the sealed-seed read, which was downgraded to
  a warning and re-reported as "wallet seed not unlocked". A seed vault that
  cannot be read now fails init as itself.
- Kotlin logs a failed `initSdk` as a failure.
- `initSdkV3` had no caller and declared the SDK ready after setting only the
  storage directory. It goes with its declarations and `InitFailed`, which only
  it produced; Envelope field 31 is reserved.

Test, on the running fleet: a device created as wallet creation creates it, its
store left as schema 24 left it (stamped 24, without the table 25 added), the
process restarted; startup fails with SCHEMA RESET REQUIRED, the session's
phase is `error` with that message, and after the wipe the refusal names,
startup succeeds and the error is gone. Mutations: the recording dropped, then
the store open dropped; each turns the test red.
- Rust gates (G1, red since #1035): `sofi::smt::fold::batch_fold` had no
  production caller; the verifier folds through `verify_batch`. The wrapper
  goes; the tests call the one fold over the economic tree's hashes.
- Rust tests (dsm_sdk): the offline-step fixture installed its host appliance
  into the process-wide factory and never removed it, so the offline-cash gate
  tests that run after it, which assert what a device with no appliance is
  refused, found one attached ("insufficient online balance"). `install` now
  returns a handle that uninstalls on drop.
- Android unit tests: `BridgeLoggerTest` asserted payload bytes in the bridge
  log, which #1038 removed on purpose (a payload can be the mnemonic). The two
  tests now assert the size is logged and no byte of the payload is.
- Android instrumented tests: `t64` asserted a 16-byte accept commitment was
  answered as success. Since #1039 the dispatcher answers the arm's error; the
  test asserts that error and its reason.

Reproduced first: the offline-step tests then the offline-cash tests in one
process fail exactly as CI does (2 failed, "insufficient online balance");
after the fix 27 passed. `dsm` lib sofi fold + validation: 59 passed.
`ci/sofi_reachability.py`: pass. `BridgeLoggerTest`: 10 passed. Android test
sources compile.
@cryptskii
cryptskii merged commit af2a8c1 into main Sep 27, 2026
21 checks passed
@cryptskii
cryptskii deleted the fix/startup-reports-the-sdk-refusal branch September 27, 2026 16:56
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