Repository navigation
AUTH-006F — Make My Account profile saves one-attempt and context-safe #531
Description
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
- 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
finallystate 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 namedAUTH-006F profile-save context isolationblock 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
- Completed foundations: SAFETY-001 — Preserve callbacks and fail closed on local Firebase isolation #99, CI-001A — Repair the deterministic frontend test baseline #103, AUTH-PROFILE-001 — Restore safe profile setup and editing after the Firebase cutover #118, AUTH-001D1 — Require verified email in browser role projection #213, AUTH-UX-001A — Make My Account sign-out private, truthful, and one-retry-safe #368, AUTH-006C — Make email/password submissions one-attempt #500, AUTH-006E — Bind browser auth projection to the current identity service #519.
- MEMBERS-DIRECTORY-001B — Add bounded officer name search and photo gallery #506 is complete/released at exact main
9e61a023301d10c0580253738e67cbc7867ea38b; preserve all merged directory behavior byte-for-byte. - Active PAY-003A3 — Reject contradictory refund and dispute object realms #526 owns its Stripe webhook paths/hunk.
- Active OAUTH-001B6 — Migrate Strava disconnect to the supported revoke endpoint #530 owns only
functions/strava.jsandfunctions/strava.test.js. - Draft Rollback: prepare exact pre-header Netlify artifact #462 owns only its release-manifest/workflow-test paths.
- 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.
Activity
daliu commentedon Aug 1, 2026
CLAIMED by /root at 2026-08-01T18:33:42Z; branch
codex/issue-531-account-save-context; exact released base9e61a023301d10c0580253738e67cbc7867ea38b.Exact ownership is only:
src/pages/account/Account.tsx: profile-save generation, exact effective-context, synchronous one-attempt guard, stale-completion fence, and new-context reset hunk.src/pages/account/Account.test.tsx: one separately namedAUTH-006F profile-save context isolationblock plus minimal existing-harness support.All #506 merged directory behavior, active #526 Stripe paths/hunk, active #530 Functions Strava paths, draft #462, every other Account hunk, services, lint baseline/count, docs, packages/locks, Functions/Rules, providers, deployment, production data, and live behavior are immutable. Three independent read-only audits returned GO, and no tracked edit preceded this marker. Claim remains held through RED/GREEN evidence, exact-head review/CI, merge, exact-main audit, and explicit release.
daliu commentedon Aug 1, 2026
TRUSTWORTHY RED — exact claimed base
9e61a023301d10c0580253738e67cbc7867ea38b, before any runtime edit.Node 20 command:
CI=true node node_modules/react-scripts/bin/react-scripts.js test --watchAll=false --runInBand src/pages/account/Account.test.tsx -t "AUTH-006F profile-save context isolation"Result: expected 5/5 failures, with 140 unrelated Account tests skipped.
Saving...state and could not start its own save.Only the separately named test/harness hunk differs at this marker.
src/pages/account/Account.tsxremains exact released source. Synthetic mocks only: no Firebase/provider/network, production account/data, deployment, or live action occurred. Claim remains held.daliu commentedon Aug 1, 2026
Scope reconciliation — 2026-08-01T19:02:00Z
Independent security review found that the save continuation fence must be paired with the rendered profile boundary: the released guard omits the exact Firestore service and an opaque account-context generation, so a Firestore-only transition or
A -> services unavailable -> same Areturn can commit one frame of stale private profile/registration content before the passive reload clears it.The exclusive claim remains exactly the same two paths and does not overlap #530 or any other active claim:
src/pages/account/Account.tsx: additionally owns the narrowly named exact-context/generation hunk for loaded profile and registration render admission.src/pages/account/Account.test.tsx: additionally ownsAccountCommitProbeassertions inside the existingAUTH-006F profile-save context isolationblock for Firestore-only and unavailable/same-context transitions.Invariant: no stale profile, email, registration, or save result may appear in any committed render after any exact account-service context transition. No service API, schema, Rules, Functions, provider, documentation, lint baseline, deployment, or production-data scope is added. All other paths remain immutable.
daliu commentedon Aug 1, 2026
Additional trustworthy RED — rendered privacy boundary
With only the previously reviewed save-fence implementation present, the two new commit-probe tests failed for the intended reason:
Synthetic Member,member@example.com,Old Account Private Race, and$43.21instead ofLoading profile....A -> services unavailable -> same Areturn recommitted the same old private account content before the replacement read.Focused command (Node 20):
CI=true node node_modules/react-scripts/bin/react-scripts.js test --watchAll=false --runInBand src/pages/account/Account.test.tsx -t 'never commits old private data after a Firestore-only change|never recommits old private data after unavailable and same-context return'Result: 2 failed for the explicit stale committed-content assertions, 150 unrelated tests skipped. The synthetic registration fixture was corrected first so the recorded RED is the actual privacy defect, not a fixture error. Runtime has not yet been changed for this newly scoped render-boundary correction.
daliu commentedon Aug 1, 2026
CONTROLLED REFRESH / GREEN CHECKPOINT
The reviewed candidate is now rebased directly onto released main
98f1e5d3be1402ab0d9eff139182086c905fc602, preserving #526 and #530 byte-for-byte.8714511032213ff570cc4d15fe0f7a31175b16e85fe87dd506abee7186ae59e4f2e119a381123907ed3a878ef0cb8b7a41297a63(unchanged across refresh)Officer impact: Members do not need a new step. A profile save now remains tied to the exact signed-in account and service context, rapid repeats make one write, and stale private profile/registration content stays hidden during account-service transitions.
Officer documentation: None — this is an invisible internal lifecycle/privacy correction. It changes no officer procedure, page structure, permission, collected field, provider account, or ordinary navigation.
Deployment evidence: Source and synthetic/emulator tests only. No website publication, Firebase deployment, provider call/configuration, production-data action, or live behavior verification occurred.
daliu commentedon Aug 1, 2026
FINAL — COMPLETE / CLOSED / CLAIM RELEASED
Outcome: My Account profile saves are now one-attempt and bound to the exact current Firebase app, Firestore service, identity service, UID, and opaque context generation. Stale writes, confirmation reads, failures, and finalizers cannot overwrite or unlock a newer account. Profile and registration content is admitted to a committed render only for that same exact context.
Exact source and review evidence:
8714511032213ff570cc4d15fe0f7a31175b16e886960409ffb527432cf4ae6f1713d663420c267e067b6c05f1302ab61b733b6ccefb8b544f5113b1(byte-identical)5fe87dd506abee7186ae59e4f2e119a381123907ed3a878ef0cb8b7a41297a6330713640075: all five required jobs passed30713736965: all five required jobs passedDeployment truth:
6a6e41cca813630008037a4aiserror, haspublished_at: null, and did not publish main8696040.6a6dc9ea588b0c0008036312, stateready, at control commit9ad6837756cdd409d296009fde5082eeeae5c059./.well-known/run-mprc-release.jsonindependently still reports that same control commit and bounded WEB-002A — Publish bounded Shop and Events permissions containment #473 source39ab8649df411262c8109a3c81a57bc38f1e168b.[]; exact-main workflows show CI only.runmprc.com, and live behavior is not verified.Officer impact: Members need no new step. Once a future approved website release publishes this source, stale account-save outcomes and stale private profile/registration renders will be fenced.
Officer documentation: None — no officer task, permission, data field, provider account, page structure, or ordinary navigation changed.
Residual: The reusable protected production release remains unavailable; do not bypass the inactive Netlify manifest. A future separately reviewed web release must publish and verify the merged frontend before anyone calls this live.
The exact claims on
src/pages/account/Account.tsxand the named AUTH-006F block/harness insrc/pages/account/Account.test.tsxare RELEASED. The remote branch is deleted.daliu commentedon Aug 14, 2026
PRE-CLAIM coordination proposal from /root after released #649: AUTH-006G — Announce a confirmed profile-name save and preserve focus, from exact main 4f11ddd. Proposed ownership is Account.tsx/test/Account.css plus separate AUTH-006G hunks in SYSTEM_DESIGN, IMPLEMENTATION_PLAN, SECURITY, and only the My Account profile-name diagram/text alternative/no-terminal evidence procedure in docs/officers/EVENTS_SHOP_MEMBERS.md. It builds on released AUTH-006F lifecycle fences without changing save payloads, reads, errors, retries, Firebase, Rules, Functions, or live state. No issue, branch, edit, or claim exists yet. Send STOP only for an exact collision or dependency.
daliu commentedon Aug 14, 2026
RELEASE NOTICE from /root: AUTH-006G #651 merged through PR #652 at exact main
cfe03c66b8f443cb4e160a80d335d966ba268e27(treecd8c32d73021b026e145325d968f0a3cedf9bbff) and its exact seven-path claim is released. The slice changed only confirmed My Account name-save feedback/focus plus separately named documentation. It preserved #531 lifecycle fences, directory availability literalfalse, every #507 connection/release gate, and active #616 files/hunk. Exact-main CI31768544349passed 5/5; the automatic Netlify main attempt stopped unpublished, and production remains inert #623. No Firebase, provider, account/sign-in, production-data, or live action occurred.daliu commentedon Aug 14, 2026
PRECLAIM — AUTH-006H current-user profile-save unconfirmed-result focus (source only).
Base: exact current
origin/maincfe03c66b8f443cb4e160a80d335d966ba268e27; treecd8c32d73021b026e145325d968f0a3cedf9bbff.Invariant: when one exact current admitted full-name Save owned focus and its update rejects or its authoritative confirmation reread is missing/rejected, preserve the existing fixed unconfirmed truth and recovery control, but transfer otherwise-lost focus exactly once to that connected alert. The intent contains only profile generation + attempt ID, transfers only in the current catch, is consumed before target checks, restores only null/body/document-root/disconnected focus, and never steals any other connected focus. Unfocused/programmatic Save and initial/background load failure remain focus-inert. Focus targets the result alert, not
Try profile again; next Tab reaches that existing recovery action.Requested exact paths:
src/pages/account/Account.tsx,src/pages/account/Account.test.tsx,src/pages/account/Account.css,SYSTEM_DESIGN.md,IMPLEMENTATION_PLAN.md,SECURITY.md,docs/officers/EVENTS_SHOP_MEMBERS.md. Runtime hunk is limited to the existing #651 focus token/effect, the current catch, and the existing profile alert; CSS only adds bounded alert/focus rules. Documentation gets a separate AUTH-006H boundary/procedure and matching My Account diagram/text alternative.Explicit exclusions: the settlement after clicking
Try profile again; initial-load focus; validation error association/aria-invalid; Edit-to-input focus; Cancel-to-Edit focus; changed success truth or #651 semantics; service contract/payload/call counts; Functions, Rules, schema, index, package, workflow, provider, account/sign-in, production data, deployment/publication/live proof; directory connection/availability and active #616 Strava work.Collision audit at exact base: #651 is CLOSED and its seven-path claim explicitly RELEASED; open PR list is empty; open repository issue search for
profile save focusis empty; #651 names this as a separate follow-up. #504/#507 remain coordination/release gates. Active #616 ownsfunctions/strava.js,functions/strava.test.js, and a separate SECURITY RISK-024 hunk, all disjoint and to be preserved. No source/doc edit, issue, branch, provider, or data action preceded this notice. Send STOP only for exact path/hunk collision or dependency. The eventual claim remains active until reviewed merge/audit/closure and explicit release.daliu commentedon Aug 14, 2026
RELEASE notice from /root: AUTH-006H #653 / PR #654 is merged and released on exact main
75a4f1ee3b20f61d375ed6d786e67e056a2206ef. Its Account.tsx/test/CSS and separate documentation hunks are free for new coordinated work. The released slice preserves the #531 one-attempt/context foundation and changes no service calls, payloads, Firebase, provider, account, production-data, or live behavior.daliu commentedon Aug 14, 2026
PRECLAIM from /root at 2026-08-14T04:49:31Z — proposed AUTH-006I current-user Profile Edit-to-input focus, source only, from exact main
75a4f1ee3b20f61d375ed6d786e67e056a2206ef/ tree6036ac48fd2c28f0cc0c05d5b1be09a7d812783e. Only when the exact connected current Profile Edit button owns focus as the ready profile enters editing, one generation-only intent may restore otherwise-lost null/body/document-root/disconnected focus to the existing Full name input; the intent is consumed before checks, leaves retained input focus alone, preserves every other connected focus, and is inert for programmatic/outside activation, context/resource/UID/generation change, reload, Save, Cancel, stale work, or unmount. No call, payload, state truth, CSS, backend, provider, account, data, deployment, or live behavior changes. Proposed exact paths: Account.tsx/test; separate AUTH-006I hunks in SYSTEM_DESIGN and IMPLEMENTATION_PLAN; one separate AUTH-006I/RISK-039 SECURITY row after released AUTH-006H; and only the existing My Account profile-name Mermaid/text alternative plus one no-terminal AUTH-006I procedure in the officer guide. #651/#653 claims are released; no open PR or Edit-to-input issue/claim exists. No issue, branch, or source edit preceded these coordination notices. This preserves #531 one-attempt/current-context foundations and every save/update/reread call boundary. Send STOP only for exact overlap or a dependency.daliu commentedon Aug 14, 2026
RELEASE notice from
/root: AUTH-006I #655 / PR #656 is complete and explicitly released. Exact main is3265a55cb92df1ebb5401f2647ff81e4ca3af0b8, tree322468cc75a57f7ea392eb9f57a201fb11c4b35a; exact-main CI31772914108passed 5/5. The six paths (Account.tsx,Account.test.tsx, SYSTEM_DESIGN, IMPLEMENTATION_PLAN, SECURITY, and EVENTS_SHOP_MEMBERS) are free for later coordinated work.No directory connection, backend, Function, Rule, provider, account/sign-in, production-data, publication, or live action occurred. Directory availability remains blob
295909b0df8dd7c54a0f164f99d4943656aece4band literalfalse; #507 retains every connection/live-proof gate. Active #616 Strava blobs remain2eb48945061ab5ad2a896c6835f308cefd7fe63aandd1792d390bd57608fecab84341a6e5916fcf8196, and its separately named RISK-024 hunk is unchanged. Production remains inert #623.