docs: record what the nsec sign-in plan built, and the twelve places it chose differently
Phase 8 of docs/nsec-sign-in.md is the rollout, which is process rather than code; what is left to write down is what the seven phases before it actually turned out to be. The header moves from "Not built" to "Built", with the table the other phased plans keep: what the plan said against what the implementation did, twelve rows, each one a decision worth reading before touching the code it describes -- the not-found exit as a screen state rather than a route, input problems on the field rather than through ErrorState, the kind 0 written over the placeholder rather than beside it, forget taking the account rows too. Two findings that belong to no phase are recorded with it: the android source set's file-name collision that put the failure classifier in its own file, and DataStoreManager's process-global preferences cache, which only a test reusing a key across directories could have found. And the one rollout fact that matters: the library commit is on claude/nostr-key-store in the submodule, bumped in by the Phase 3 commit, and has to be pushed and tagged with this branch. The docs index now says the plan has been built and points at the table. Replayed onto Mantra by docs/curated-to-mantra.md: README.md: line-set three-way merge, both sides' additions kept and this commit's deletions applied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Pulled-From: curated/curated@2326839e48
This commit is contained in:
@@ -40,7 +40,7 @@ jvm-target note first if you want to know why its adaptive-layout phase exists.
|
||||
The curated-to-mantra note is a phased plan that has not been built, though its dry
|
||||
run has: it is about the repository rather than the app, and reads alone, except that
|
||||
its first decision leans on the derivation note's one rule.
|
||||
The nsec sign-in note is a phased plan that has not been built; it inherits the
|
||||
The nsec sign-in note is a phased plan that has been built; it inherits the
|
||||
key-storage decision from the jvm-target note and drives the navigation state
|
||||
machine `NavigationViewModel.processLocalAccount` implements, so read it with the
|
||||
code open.
|
||||
code open, and read its table of where the build chose differently first.
|
||||
|
||||
@@ -9,10 +9,35 @@ inherits, and with `NavigationViewModel.processLocalAccount` open for the state
|
||||
machine it drives — the machine is already built, and half of this plan is about
|
||||
finding its unreachable entrance.
|
||||
|
||||
**Not built.** Phases 1–8 below, one commit each, in the order given. Phases 1 and
|
||||
2 are independent and can go in parallel; nothing after Phase 3 can start before
|
||||
it, because Phase 3 is where the compiler first checks both halves against each
|
||||
other.
|
||||
**Built**, phases 1–7, one commit each, in the order given; Phase 8 is the rollout
|
||||
and is process rather than code. The phases are kept as written because they are
|
||||
the reasoning, and the code reads better against the argument it came from than
|
||||
against a summary of itself. Where the implementation chose differently the
|
||||
table below says so, and it did so in ways worth reading before touching any of
|
||||
it:
|
||||
|
||||
| what the plan said | what it turned out to be |
|
||||
|---|---|
|
||||
| `WalletManagerExtension.nostrPublicKey()` "can go with" the ten sites | it stays: `CreateProfileViewModel` derives a fresh key's pubkey with it. Wrong by one caller |
|
||||
| pull the failure classification into `TechnicalExtensions.kt` | its own file, `DecryptionFailure.kt`. `androidMain` has a `TechnicalExtensions.kt` in the same package, and a common file of that name may hold only `expect`s — anything with a body is a second `TechnicalExtensionsKt` facade the android target refuses. The `graceful*Seed*` actuals were left as they were |
|
||||
| "lift the write into a helper both managers call" | `AtomicFileWrite.writeVerified`, and `SeedManager.writeSeedToDir` now goes through it with its original check and exception type |
|
||||
| the nsec branch of startup "reports the startup" itself | it only sets the identity; the existing `activeIdentity != null` branch reports it. The mnemonic path reports twice (once from `onStartupSuccess`, once from that branch); the nsec path does not repeat the mistake |
|
||||
| read `loadNostrProfile(startupRoute)` by the active identity | done, with a fallback to the first account when there is no identity — which is how `NavigationRoutingTest` constructs it, and never how production reaches it |
|
||||
| input problems "through `ErrorState`" | on the field, as supporting text, while the prompt is still showing. `ErrorState` is for what happens *after* confirming — the key was already here, the write failed — where there is no field to put the message under |
|
||||
| `SignInToProfileUIState.Confirm(kind, npub, hexKey)` | `Confirm(credential)`, with the `SignInCredential` carrying the pubkey; the two writers are injected as functions so `commit` is a suspend function with a result, testable without a key store |
|
||||
| the not-found exit as a route | a state of the *screen*: `UnsyncedProfileViewModel.decide` over the account, with `NavigationUIState` unchanged. A `processed` request — an event came back, just not a kind 0 — counts as finished; the plan's test sketch said the opposite and was wrong |
|
||||
| "set one up" opens `CreateProfileScreen` | an inline form on the not-found state. `CreateProfileScreen` is built around generating a seed and says so in its copy; and the kind 0 has to be written *over* the placeholder row, not beside it — see `setUpProfileForExistingKey` |
|
||||
| the one hop, unspecified where | in the sync pump, after `saveNostrEvent`, with a per-account set so five indexers answering with the same kind 10002 make one hop |
|
||||
| forget: key, prefs, metadata, active identity | plus the account's unsigned rows (`forgetLocalAccount`), which the plan did not list: the kind 0 that made it a local account, and anything queued that can now never be signed. Published events and the profile cache stay |
|
||||
| assert `platformStartupLogic` was not called | `identity.business == null` and the jvm `BusinessManager.businessFlow` empty afterwards — the observable fact rather than the call |
|
||||
|
||||
Two things found on the way that are not in any phase. `DataStoreManager` caches
|
||||
each id's preferences in a companion object for the life of the process, so a test
|
||||
that reuses a key across temporary directories is served the wrong file
|
||||
(`IdentityWriterJvmTest` uses fresh keys for that reason). And the library commit
|
||||
is on `claude/nostr-key-store` in the submodule, at `59c11ed`, bumped into the app
|
||||
by the Phase 3 commit; it has to be pushed with this branch, and tagged for JitPack
|
||||
consumers, before any of this leaves the machine.
|
||||
|
||||
## The constraint
|
||||
|
||||
@@ -903,6 +928,13 @@ throughout, so no build old or new misreads a wallet.
|
||||
if two people are on it. Phase 3 is where the estimate is least reliable: it is
|
||||
the first time both halves meet, and the startup screen has a history.
|
||||
|
||||
Phases 1–7 are now implemented, in one sitting and in order. What each turned out
|
||||
to require, as against what was predicted here, is in the table at the top and in
|
||||
the commit messages on this branch. The two surprises were not in any phase's
|
||||
description: the android source set's file-name collision in Phase 2, and the
|
||||
process-global preferences cache that only a test with a fixed key could have
|
||||
found.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- **A wallet for an nsec identity.** Not possible from the nsec, for the reason
|
||||
|
||||
Reference in New Issue
Block a user