Hit today while building the artifact for the two-phone current-format run. Caught only by checking the shipping binary for code markers; every conventional signal said the build was good.
What happened
make android at 5a0b4cb3 (clean tree) produced an APK whose libdsm_sdk.so was byte-identical to an August 8th libdsm_android_anchor.so — three days stale, predating #635, #638, #640 and #641 entirely. The armv7 and x86_64 cdylibs were from August 2nd.
Everything looked right: correct SHA, clean tree, BUILD SUCCESSFUL, fresh jniLibs timestamps (06:10:42), and cargo genuinely did compile the new code — its own output at deterministic_state_machine/target/aarch64-linux-android/release/libdsm_sdk.so (06:08, sha a722ef6b…) contains the new strings.
Gradle's refreshDsmJniLibs then overwrote both jniLibs directories with the stale cdylib (sha 4affa99f…), and that is what got packaged.
Why
refreshDsmJniLibs (dsm_client/android/app/build.gradle.kts:299+) resolves the shipping library as, in order:
crates/dsm-android-anchor/target/<triple>/{release,debug}/libdsm_android_anchor.so
- the staged
jniLibs copies
That preference is correct — the anchor cdylib is the shipping library; it links dsm_sdk statically and adds the anchor glue, and a plain dsm_sdk cdylib silently loses the offline/anchor paths.
The problem is that nothing in make android builds that cdylib. android-libs builds --package dsm_sdk only. So any previously-built cdylib — however old — wins over a freshly compiled dsm_sdk, every time.
The task's own comment documents the same wound from 2026-07-26 in the opposite direction, where a stale plain dsm_sdk .so overwrote a fresh anchor build and shipped an APK "whose native library had no token.mint/token.burn at all". The fix then made "a freshly built cdylib always win outright". It does — including when it is not fresh at all.
Suggested fix
Make make android-libs build the artifact that actually ships:
cd crates/dsm-android-anchor && cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 \
--platform 23 build --release --features on_device_installs
(The crate is excluded from the workspace because it transitively pulls tropic01, so it needs its own invocation.)
Belt-and-braces, independent of the above: refreshDsmJniLibs should refuse to use a cdylib older than the newest dsm_sdk cargo output for the same triple, and fail the build rather than silently prefer it. A stale artifact should be a build error, not a log line.
Why this matters beyond one bad APK
A hardware run against this artifact would have "proven" behaviour of code from three days earlier while reporting success against the intended SHA. Nothing in the pipeline would have contradicted it — the failure is invisible unless you check the shipping binary for content you expect to be there.
Worth a CI assertion too: after packaging, extract lib/arm64-v8a/libdsm_sdk.so from the APK and require a marker string from the current commit.
Hit today while building the artifact for the two-phone current-format run. Caught only by checking the shipping binary for code markers; every conventional signal said the build was good.
What happened
make androidat5a0b4cb3(clean tree) produced an APK whoselibdsm_sdk.sowas byte-identical to an August 8thlibdsm_android_anchor.so— three days stale, predating #635, #638, #640 and #641 entirely. The armv7 and x86_64 cdylibs were from August 2nd.Everything looked right: correct SHA, clean tree,
BUILD SUCCESSFUL, freshjniLibstimestamps (06:10:42), and cargo genuinely did compile the new code — its own output atdeterministic_state_machine/target/aarch64-linux-android/release/libdsm_sdk.so(06:08, shaa722ef6b…) contains the new strings.Gradle's
refreshDsmJniLibsthen overwrote bothjniLibsdirectories with the stale cdylib (sha4affa99f…), and that is what got packaged.Why
refreshDsmJniLibs(dsm_client/android/app/build.gradle.kts:299+) resolves the shipping library as, in order:crates/dsm-android-anchor/target/<triple>/{release,debug}/libdsm_android_anchor.sojniLibscopiesThat preference is correct — the anchor cdylib is the shipping library; it links
dsm_sdkstatically and adds the anchor glue, and a plaindsm_sdkcdylib silently loses the offline/anchor paths.The problem is that nothing in
make androidbuilds that cdylib.android-libsbuilds--package dsm_sdkonly. So any previously-built cdylib — however old — wins over a freshly compileddsm_sdk, every time.The task's own comment documents the same wound from 2026-07-26 in the opposite direction, where a stale plain
dsm_sdk.so overwrote a fresh anchor build and shipped an APK "whose native library had no token.mint/token.burn at all". The fix then made "a freshly built cdylib always win outright". It does — including when it is not fresh at all.Suggested fix
Make
make android-libsbuild the artifact that actually ships:(The crate is excluded from the workspace because it transitively pulls
tropic01, so it needs its own invocation.)Belt-and-braces, independent of the above:
refreshDsmJniLibsshould refuse to use a cdylib older than the newestdsm_sdkcargo output for the same triple, and fail the build rather than silently prefer it. A stale artifact should be a build error, not a log line.Why this matters beyond one bad APK
A hardware run against this artifact would have "proven" behaviour of code from three days earlier while reporting success against the intended SHA. Nothing in the pipeline would have contradicted it — the failure is invisible unless you check the shipping binary for content you expect to be there.
Worth a CI assertion too: after packaging, extract
lib/arm64-v8a/libdsm_sdk.sofrom the APK and require a marker string from the current commit.