fix(mobile): give the recovery-blocked state an exit so no device stays unable to start - #514
Merged
Merged
Conversation
…ys unable to start `recovery-blocked` is entered when the encrypted store throws on a read, a write, or a clear. Nothing left it. The snapshot had no controls, every start and join stayed refused, and the only advice was to restart the app and contact district technology support. A keychain that keeps failing therefore produced a device that could never raise an emergency again, which completes the same class of defect as the two unresolved fences: retained client state that permanently disables the one action the app exists to perform. The operator can now choose to carry on without durable recovery. `continueWithoutRecovery` suspends the store for the rest of the process and returns to idle, so start and join work. Suspension never rewrites or deletes what is already stored: a store that cannot be trusted to read or write is also not trusted to erase. The copy states plainly that a request interrupted by the app closing will not be recoverable on this device, which is the real consequence of proceeding, and the control is disabled while offline. `pending-other-session` was audited in the same pass and left alone. It is reachable only while another session's request is in flight inside this process; an owner change quarantines that pending state and it settles, and a relaunch turns it into the unresolved record, which is now clearable. It is transient rather than absorbing. Coverage: a store that fails every write blocks the first submission and then, after the operator continues, admits the next start through to success; the choice is refused when the state is not blocked and when the caller is not the signed-in owner; the blocked screen exposes the control, fires it once, and says both that nothing stored is deleted and that an interrupted request will not be recoverable. Gate: Prettier, ESLint and typecheck clean across the repository; 6/6 test shards green and 61 mobile native tests pass.
ReviewReviewed the diff against Summary: clean. No IMPORTANT findings. The
Two minor observations, not blocking:
No FERPA/PII, secrets, or injection concerns — this is client-only mutation-state plumbing with no new data leaving the device. No tests were removed or skipped. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The sweep
After #512 and #513 closed the two unresolved fences, this audits every remaining client state that can refuse start and join, applying one rule: no retained state on the device may permanently prevent raising an emergency.
unresolved(owner's own)unresolved-other-sessionrecovery-blockedpending-other-sessionchecking-recoveryrecovery-blockedEntered when the encrypted store throws on a read, write, or clear — four call sites. Nothing left it. The screen carried no controls, every start and join stayed refused, and the only guidance was to restart the app and contact district technology support. A keychain that keeps failing produced a device that could never raise an emergency again.
continueWithoutRecoverylets the operator carry on with no durable recovery for the rest of the process, returning to idle so start and join work.Deliberate choices:
pending-other-session, audited and leftReachable only while another session's request is in flight inside this process. An owner change quarantines that pending state and it settles; a relaunch turns it into the unresolved record, which #513 made clearable. Transient, not absorbing — no change needed.
Coverage
Gate
Prettier, ESLint, typecheck clean repo-wide. 6/6 shards green; 61 mobile native tests pass.
Platform parity
Client-only logic and shared React Native UI. Needs verifying on iOS and Android.