Wizard: no dead ends after cloning a store without its key #16
Loading…
Reference in a new issue
No description provided.
Delete branch "wizard-missing-key"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Closes #9. The wizard could already import a key for a cloned store, but two paths still trapped the user:
originhad everything in it at the last sync. Anything else keeps the store and says why: unpushed or uncommitted (even ignored) files, local-only refs, or no remote..gpg-id(e.g. an empty repo) led to the import step with no recipients. The wizard now asks you to choose a key and sets the store up in place. The same happens if the app is restarted in that state.Blast radius
Tier: trunk
Trunk paths touched:
passcore/src/lib.rs(Store::has_files),passcore/src/sync.rs(sync::discard),src/bridge.rs(has_files,discard_store),src/session.rs(try_repo_lock)Gate / rollback: No existing behaviour changes.
discardandhas_filesare new, anddiscardruns only after the user confirms Start over. To undo, revert this merge commit or reinstall the previous.click.manifest.jsonstays at the unreleased 0.1.0; the version bump belongs to the release steps (#12).Proof
CI:
QMAKE=qmake-qt5 cargo make(fmt, clippy-D warnings, coverage, release check, audit) givestest result: ok. 75 passed; 0 failed,warning: 2 allowed warnings found(the existing allowlist) andBuild Done.QML:
cargo make qml-testgivesTotals: 81 passed, 0 failed. The newtst_SetupWizard.qmlcovers all wizard paths and the Start over dialog against a fake store.Real temp store:
discards_only_what_the_remote_haskeeps the store in each of these cases:It discards a synced store, tags on pushed commits and
refs/touchpass/checkedincluded. It also discards an empty-repo clone, retries a leftover only when it goes ahead, and is idempotent.Real temp store:
sets_up_a_clone_of_an_empty_repositoryclones an empty bare repo, then runsinit, writes and syncs (pushed 1). A second clone decrypts the entry.Fails without the change: I broke each check on purpose and re-ran the tests. All 18 mutations make a test fail:
has_files, the branch/stash checks, the local-ref check,include_ignored, the tag reachability check, thecheckedref allowance, the stash ref, theorigin-only remotes, the leftover cleanup, and cleanup before the checks.needKeyOnly.UI: I grabbed the needKeyOnly step (Start over plus explanation) and needGpgId step 1 under Xvfb in the clickable image with QtTest
grabImageand checked both by eye. They are not attached here. Please look at both screens on a device before merging.Review
Reviewer: fresh-context
code-reviewat high on the full branch, then medium on each of the 4 follow-up commits. That gave 30 findings: 13 fixed and 17 declined or refuted with evidence. Examples of declines:.partialinto place, so a store folder without a repo is unreachableReview-Confidence: 70/100. Trunk was reviewed at high and every finding was fixed or refuted, with strong mutation evidence. The two untested paths are the "busy syncing" refusal and the delete-failure handling; check those by hand.
Confidence by part
passcore/src/sync.rsdiscard/has_local_only_refs: high. Real-repo tests plus a mutation for every check.qml/SetupWizard.qmlflow: high. 6 wizard tests and 4 mutations.src/bridge.rsdiscard_store"busy syncing" (try_repo_lock): medium. Not exercised; read it.discard's delete-failure arms ("cannot remove …", the ignored final delete error): medium. Not exercised; read them.qml/StartOverDialog.qmllook: medium. Xvfb only, not seen on a device.🤖 Generated with Claude Code
@ -94,0 +98,4 @@/// no remote, unpushed commits, refs only here, or uncommitted (even ignored)/// files keep it.pub fn discard(store: &Store) -> Result<(), PasscoreError> {let keep = |why: &str| {This doesn't look particularly reuseable from here. If there are other places where such a utility conversion function could be used, extract it and give it test coverage. Otherwise, leave it for now.
Re the line comment on
passcore/src/sync.rs:101(thekeepclosure): I checked for reuse, and there is none outsidediscard. The "the password store was kept: …" error is built only in this function: bykeep, and once inline at line 144 for the "cannot remove the leftover" case. The otherPasscoreError::InvalidInputsites (ingit.rs,entry.rs,keyring.rs,generator.rsandlib.rs) each write their own message inline, with nothing shared to factor out. So I'm leaving it in place as you suggested, with no extraction or new tests.WIP: Wizard: no dead ends after cloning a store without its keyto Wizard: no dead ends after cloning a store without its key