You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Bind each My Account profile save to exactly one mounted account/service lifecycle. A completion started for account A must never display private profile data, errors, or control state after the page moves to account B or a newer lifecycle.
This is a provider-neutral corrective child. It does not add Google sign-in, link accounts, grant membership, or change server authorization.
Current defect
On exact released main 9e61a023301d10c0580253738e67cbc7867ea38b, handleSave captures the old Firestore service and UID, then unconditionally changes shared React state after its update and confirmation-read awaits.
If account B loads while account A save is pending, the A confirmation can finish afterward and replace profile with A private data while profileContext still names B. The existing context check therefore treats A data as B data and can render it. A stale A rejection can also replace the ready B page with an unavailable result, and stale finally work can clear the disabled state for a newer B save.
Privacy and lifecycle invariant
The effective save context is the exact UID, Firebase app, Firestore service, and Identity service.
Every real context transition and unmount invalidates the prior monotonic generation, including A → unavailable → the same A resources.
At most one save starts synchronously in one generation; a rapid repeat performs no second write or confirmation read.
A save captures its exact context, generation, and attempt token before the first write.
After the update resolves, a stale attempt stops before a confirmation read.
Success, failure, and finally state changes occur only for the still-mounted current attempt and generation.
A new context resets browser-only edit/save state without allowing an old completion to alter it.
The current exact validation, captured write target, confirmation-read requirement, generic uncertain-result message, accessible controls, and ordinary successful save remain compatible.
This browser fence does not make the underlying Firestore write idempotent and does not claim that an already-started old write was cancelled.
Exact scope
src/pages/account/Account.tsx: only the profile-save generation/context/one-attempt/reset hunk.
src/pages/account/Account.test.tsx: one separately named AUTH-006F profile-save context isolation block plus minimal existing-harness support.
Every other path and hunk is immutable.
Acceptance checks
Trustworthy RED proves stale A success can render A private data after B is ready.
A stale update completion performs no old-context confirmation read.
A stale rejection leaves B ready and exposes no rejected detail.
Settling A cannot clear a newer pending B save or enable its input/actions.
A → unavailable → same A resources rejects the first A generation.
A rapid same-generation repeat makes one update call.
Unmount makes a late completion inert.
Existing validation, current success, current failure, missing-confirmation, profile recovery, sign-out, registration, directory, Strava, and Account tests remain green.
Full relevant local and hosted gates pass, with independent privacy/security, test/mutation, and scope/compatibility review.
Three independent read-only audits found no issue, pull request, branch, or path claim on either exact Account file/hunk.
Explicit exclusions
No account service, Identity service, member-directory component/service, Login form, Google/provider behavior, Functions, Firestore Rules/schema, membership, discounts, payments, Strava, package/lock, lint baseline/count, root/officer documentation, workflow, provider configuration, deployment, production account/data, or live testing.
Migration impact: none. No schema, stored record, account, credential, claim, provider, or production-state change.
Officer impact: None — this is an internal browser lifecycle/privacy containment with no new officer action or visible procedure.
Officer documentation: None — no officer procedure, permission, page structure, data movement, provider configuration, deployment, backup, or recovery action changes.
Deployment evidence: issue design only. Source change, tests, merge, website publication, runmprc.com verification, Firebase deployment, provider configuration, production data, and live behavior are separate and currently unperformed/unproven.
Claim protocol
Creation is not a claim. Before editing, assign this issue, create and push a unique branch from exact current main, post and re-read a UTC CLAIMED by <canonical agent> at <UTC>; branch <branch>; base <SHA>; paths/hunks <exact scope> marker, and change status:ready to status:in-progress. Earliest valid claim wins; any exact overlap is STOP-and-coordinate.
Parent: #109.
Outcome
Bind each My Account profile save to exactly one mounted account/service lifecycle. A completion started for account A must never display private profile data, errors, or control state after the page moves to account B or a newer lifecycle.
This is a provider-neutral corrective child. It does not add Google sign-in, link accounts, grant membership, or change server authorization.
Current defect
On exact released main
9e61a023301d10c0580253738e67cbc7867ea38b,handleSavecaptures the old Firestore service and UID, then unconditionally changes shared React state after its update and confirmation-read awaits.If account B loads while account A save is pending, the A confirmation can finish afterward and replace
profilewith A private data whileprofileContextstill names B. The existing context check therefore treats A data as B data and can render it. A stale A rejection can also replace the ready B page with an unavailable result, and stalefinallywork can clear the disabled state for a newer B save.Privacy and lifecycle invariant
finallystate changes occur only for the still-mounted current attempt and generation.Exact scope
src/pages/account/Account.tsx: only the profile-save generation/context/one-attempt/reset hunk.src/pages/account/Account.test.tsx: one separately namedAUTH-006F profile-save context isolationblock plus minimal existing-harness support.Every other path and hunk is immutable.
Acceptance checks
Dependencies and coordination
9e61a023301d10c0580253738e67cbc7867ea38b; preserve all merged directory behavior byte-for-byte.functions/strava.jsandfunctions/strava.test.js.Explicit exclusions
No account service, Identity service, member-directory component/service, Login form, Google/provider behavior, Functions, Firestore Rules/schema, membership, discounts, payments, Strava, package/lock, lint baseline/count, root/officer documentation, workflow, provider configuration, deployment, production account/data, or live testing.
Migration impact: none. No schema, stored record, account, credential, claim, provider, or production-state change.
Officer impact: None — this is an internal browser lifecycle/privacy containment with no new officer action or visible procedure.
Officer documentation: None — no officer procedure, permission, page structure, data movement, provider configuration, deployment, backup, or recovery action changes.
Deployment evidence: issue design only. Source change, tests, merge, website publication,
runmprc.comverification, Firebase deployment, provider configuration, production data, and live behavior are separate and currently unperformed/unproven.Claim protocol
Creation is not a claim. Before editing, assign this issue, create and push a unique branch from exact current main, post and re-read a UTC
CLAIMED by <canonical agent> at <UTC>; branch <branch>; base <SHA>; paths/hunks <exact scope>marker, and changestatus:readytostatus:in-progress. Earliest valid claim wins; any exact overlap is STOP-and-coordinate.