docs: correct the jvm plan against what phase 1 actually did

Phase 1 is implemented and verified in the lightning-kmp-app fork on
claude/jvm-target-actuals (27a0054). Four things in the plan were wrong,
and doing the work is what surfaced them.

**jvm() belongs at the start of phase 1, not phase 4 -- for the library.**
The plan said leave it off in both builds until phase 4. That is right for
mantra and wrong for the fork: library/src/jvmMain/ is an orphan source
set until the library declares the target, so phases 1-3 would all have
been written blind. Declared first, `:library:compileKotlinJvm` names the
remaining expects, and that list beats grepping for `expect ` -- it
shrinks by exactly what you implement and cannot drift from the truth. The
build stays red across phases 1-3 by design.

That checklist is now recorded as the phase 1 exit condition: exactly
eight expects should remain, and exactly which eight. Anything else means
something in the phase is wrong.

**Phase 3 is two decisions, not four.** gracefulSingleSeedDecryption and
gracefulMultiSeedDecryption are pure exception mapping into a
DecryptSeedResult, and the exception they branch on is
java.security.KeyStoreException -- a plain JCA type that exists on the jvm.
Both are near-copies of the android actuals and need nothing settled
first, so they move alongside phase 2. Only keyStoreEncryption and
keyStoreDecryption are the security decision, and that part of the
analysis stands.

**The Fibonacci template must not be deleted.** The plan said to drop it
"assuming nothing references them". Things do: generateFibi is exercised
by template tests in commonTest, androidHostTest, iosTest, jvmTest and
linuxX64Test, and JvmFibiTest asserts a value that depends on precisely
the two properties fibiprops.jvm.kt defines. That file already satisfies
two of the 25 expects, which is why the count was 23 missing rather than
25. Removing the template is five test files plus four fibiprops.*
actuals, and it is a separate cleanup.

**Phase 1 is fifteen actuals, not fourteen**, and two of them are not
copies of android -- platformElectrumRegtestConf (10.0.2.2 is the
emulator's alias for the host loopback; a jvm process is already on the
host) and phoenixLogWriters (android routes kermit into slf4j because
android tooling reads that back).

Also recorded, because it cost time: a worktree cannot run gradle at all
until the submodules are checked out *and* local.properties exists at five
levels. Neither is version controlled, so a fresh worktree has neither,
and the failure surfaces four builds down at
:...:secp256k1-kmp:jni:android as "SDK location not found" rather than
anywhere obviously related.

Both builds were run: `:library:compileKotlinJvm` fails only on the known
eight, and `:composeApp:compileDebugKotlinAndroid` still passes with the
library's jvm target declared -- the check that matters, since a new
variant must not change how the android target resolves the library.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kgothatso Ngako
2026-09-06 00:53:51 +02:00
parent 2aaa7b99a6
commit 0757e50dc5

View File

@@ -141,14 +141,37 @@ sharing one submodule git dir. That is fine while both want the same commit —
check with `git submodule status` in each — and worth being careful about when
they do not.
Then the fresh clones need `local.properties`, which is gitignored and therefore
absent, at **five** levels — the mantra root and each of the four nested builds
down to secp256k1-kmp. Without it configuration fails at
`:lightning-kmp-app:lightning-kmp:bitcoin-kmp:secp256k1-kmp:jni:android` with
"SDK location not found":
```bash
for d in . lightning-kmp-app \
lightning-kmp-app/experimental/lightning-kmp \
lightning-kmp-app/experimental/lightning-kmp/experimental/bitcoin-kmp \
lightning-kmp-app/experimental/lightning-kmp/experimental/bitcoin-kmp/experimental/secp256k1-kmp; do
echo "sdk.dir=$HOME/Android/Sdk" > "$d/local.properties"
done
```
---
## Phase 1 — phoenix: the mechanical actuals
**~12 days. Blocked by nothing. Do this first.**
Fourteen of the 23. None of them requires a decision — each is either a direct
copy of the android implementation or a handful of lines of JVM file handling.
**Start by turning on `jvm()` in the library** —
`lightning-kmp-app/library/build.gradle.kts:18`, not mantra's, which still waits
for Phase 4. Without it `library/src/jvmMain/` is an orphan source set that
nothing compiles, and every actual in Phases 13 would be written blind. With it,
`./gradlew :library:compileKotlinJvm` prints the remaining expects by name, and
that list is a better worklist than any grep — it shrinks by exactly what you
implement and cannot drift from the truth.
Fifteen of the 23 are mechanical. None requires a decision — each is either a
direct copy of the android implementation or a few lines of JVM file handling.
**`DbHooks.jvm.kt` — six functions, and this one is free.** The android
implementation is 15 lines and every function is an empty body; the hooks only do
@@ -176,13 +199,36 @@ The four paths (`getApplicationFilesDirectoryPath`,
directory, not `java.io.tmpdir`. The old Aux code used tmpdir and left a `TODO`
about it; do not inherit that.
**The remaining singles:** `platformElectrumRegtestConf`, `AppVersion`,
`phoenixLogWriters` (a console writer is fine), `computePreferencePath`, and the
two Fibonacci template properties — which should be deleted along with the
template file rather than reimplemented, assuming nothing references them.
**The remaining singles:** `platformElectrumRegtestConf` (*not* a copy — android
uses `10.0.2.2`, the emulator's alias for the host loopback, and a jvm process is
already on the host), `AppVersion`, `phoenixLogWriters` (kermit's `CommonWriter`;
android routes into slf4j because android tooling reads that, and the jvm has no
equivalent convention), and `computePreferencePath`.
**Verification:** still nothing compiles for JVM at this point, because Phases 2
and 3 are outstanding. Work against the expect list, not the compiler.
**Leave the Fibonacci template alone.** `fibiprops.jvm.kt` looks like stray
scaffolding, but it is the jvm half of a pair: `generateFibi` is exercised by
template tests in `commonTest`, `androidHostTest`, `iosTest`, `jvmTest` and
`linuxX64Test`, and `JvmFibiTest` asserts a value that depends on exactly the two
properties that file defines. It already satisfies two of the 25 expects, which
is why 23 are missing rather than 25. Deleting the template is a reasonable
cleanup of a lightning wallet library, but it is five test files plus four
`fibiprops.*` actuals, and it is not this work.
**Verification:** `./gradlew :library:compileKotlinJvm` from inside
`lightning-kmp-app/`. It still fails at the end of this phase — that is expected,
and the failure is the point. It should report **exactly eight** remaining
expects, and they should be exactly the contents of Phases 2 and 3:
```
DbFactory.kt createChannelsDbDriver, createPaymentsDbDriver, createAppDbDriver
NetworkMonitor.kt NetworkMonitor
KeyStoreFunctions.kt keyStoreDecryption, keyStoreEncryption
TechnicalExtensions.kt gracefulSingleSeedDecryption, gracefulMultiSeedDecryption
```
Anything else in that list means something in this phase is wrong. Also re-run
`./gradlew :composeApp:compileDebugKotlinAndroid` from the mantra root: adding a
jvm target to the library must not disturb how the android target resolves it.
---
@@ -225,9 +271,16 @@ schema-creation path against a scratch `main()` if you want feedback sooner.
**A decision, not a port. Unbounded until the decision is made.**
Four of the 23: `keyStoreEncryption`, `keyStoreDecryption`, and the two inline
wrappers `gracefulSingleSeedDecryption` and `gracefulMultiSeedDecryption` that
translate keystore exceptions into a `DecryptSeedResult`.
Four of the 23, but only **two** of them are actually a decision.
`gracefulSingleSeedDecryption` and `gracefulMultiSeedDecryption` are not. They
are pure exception mapping into a `DecryptSeedResult`, and the exception they
branch on is `java.security.KeyStoreException` — which exists on the jvm, since
`KeyStore` is a plain JCA type. Both are a near-copy of the android actuals and
can be written before any of the below is settled. Do them with Phase 2 and leave
two errors outstanding rather than four.
The decision is `keyStoreEncryption` and `keyStoreDecryption`.
The android implementation delegates to `KeystoreHelper.kt` — 116 lines against
`AndroidKeyStore`, with `KeyGenParameterSpec`, and `setIsStrongBoxBacked(true)`