Round 23 produced **no inline comments** — nothing wrong in the code it had
not already seen. The five suppressed ones are all in unchanged code: one real
misdiagnosis and four pieces of drift.
**A decrypted-but-corrupt payload was reported as a wrong passphrase.** By the
time `JSON.parse` runs, GCM has verified the tag — so the passphrase is
*proven correct*, and a `SecretFileKeyMismatchError` there tells the user to
restore a secret they never lost. `asSecretMap` was already outside that catch
for exactly this reason; `JSON.parse` was not, so the same class of mistake
survived one line above the fix for it. The decrypt now has its own narrow
catch and everything after it reports a corrupt file.
The test fixture could not express this case — it `JSON.stringify`s its
payload, so it can only produce valid JSON. Split into a raw-plaintext variant
so a fixture can carry bytes that are authentic under the passphrase and still
unparseable, which is the only shape that reaches the branch.
The rest is documentation that had drifted from the code it describes:
- `handOff`'s doc still described the re-read-and-compare protocol that the
atomic claim replaced two rounds ago — an obsolete concurrency invariant is
worse than none, because the next reader takes it as the contract.
- The spec still described that same hand-off, and still called Client and
Server Settings "the only two dialogs that accept a secret". Both now match
the code, and the dialog count is called out as load-bearing: the one that
was missing from it was the one taking secrets with no disclosure at all.
- The `void load()` in `refresh` carries the justification the repo's
no-floating-promises rule requires — `load` owns its failures, and `refresh`
is deliberately synchronous because `refreshingPersist` calls it from a
`finally`.
Full `npm run ci` green, staged tree verified first.
Signed-off-by: cliffhall <cliff@futurescale.com>