Skip to content

AUTH-006F — Make My Account profile saves one-attempt and context-safe #531

Description

@daliu

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, 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

  1. The effective save context is the exact UID, Firebase app, Firestore service, and Identity service.
  2. Every real context transition and unmount invalidates the prior monotonic generation, including A → unavailable → the same A resources.
  3. At most one save starts synchronously in one generation; a rapid repeat performs no second write or confirmation read.
  4. A save captures its exact context, generation, and attempt token before the first write.
  5. After the update resolves, a stale attempt stops before a confirmation read.
  6. Success, failure, and finally state changes occur only for the still-mounted current attempt and generation.
  7. A new context resets browser-only edit/save state without allowing an old completion to alter it.
  8. The current exact validation, captured write target, confirmation-read requirement, generic uncertain-result message, accessible controls, and ordinary successful save remain compatible.
  9. 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.

Dependencies and coordination

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.

Metadata

Metadata

Assignees

Labels

area:authAuthentication and authorizationarea:privacyPersonal data, consent, minimization, retention, and privacy operationsarea:webWeb application and hostingbugSomething isn't workingpriority:P0Launch blocker or urgent security risksize:SSmall focused issuetype:reliabilityReliability and recoverytype:securitySecurity or privacy boundarytype:testingTest infrastructure and quality gates

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions