Skip to content

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

Closed

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.

Activity

  1. self-assigned this
    on Aug 1, 2026
  2. added
    bugSomething isn't working
    priority:P0Launch blocker or urgent security risk
    type:securitySecurity or privacy boundary
    area:authAuthentication and authorization
    area:webWeb application and hosting
    size:SSmall focused issue
    status:readyDependencies complete and ready to claim
    type:testingTest infrastructure and quality gates
    area:privacyPersonal data, consent, minimization, retention, and privacy operations
    on Aug 1, 2026
  3. daliu commented on Aug 1, 2026

    @daliu
    ContributorAuthor

    CLAIMED by /root at 2026-08-01T18:33:42Z; branch codex/issue-531-account-save-context; exact released base 9e61a023301d10c0580253738e67cbc7867ea38b.

    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 named AUTH-006F profile-save context isolation block 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.

  4. added
    status:in-progressClaimed and actively being implemented
    and removed
    status:readyDependencies complete and ready to claim
    on Aug 1, 2026
  5. daliu commented on Aug 1, 2026

    @daliu
    ContributorAuthor

    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.

    • Stale A success issued a third private confirmation read after B was ready.
    • Stale A rejection replaced B with the generic unavailable state.
    • B inherited A Saving... state and could not start its own save.
    • A → unavailable → same A allowed the first-generation completion to issue a third read.
    • Two rapid activations started two writes; a later unmount would not stop the old confirmation read.

    Only the separately named test/harness hunk differs at this marker. src/pages/account/Account.tsx remains exact released source. Synthetic mocks only: no Firebase/provider/network, production account/data, deployment, or live action occurred. Claim remains held.

  6. daliu commented on Aug 1, 2026

    @daliu
    ContributorAuthor

    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 A return 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 owns AccountCommitProbe assertions inside the existing AUTH-006F profile-save context isolation block 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.

  7. daliu commented on Aug 1, 2026

    @daliu
    ContributorAuthor

    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:

    • Firestore-only transition committed Synthetic Member, member@example.com, Old Account Private Race, and $43.21 instead of Loading profile....
    • A -> services unavailable -> same A return 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.

  8. daliu commented on Aug 1, 2026

    @daliu
    ContributorAuthor

    CONTROLLED REFRESH / GREEN CHECKPOINT

    The reviewed candidate is now rebased directly onto released main 98f1e5d3be1402ab0d9eff139182086c905fc602, preserving #526 and #530 byte-for-byte.

    • Exact candidate head: 8714511032213ff570cc4d15fe0f7a31175b16e8
    • Exact two-file binary diff SHA-256: 5fe87dd506abee7186ae59e4f2e119a381123907ed3a878ef0cb8b7a41297a63 (unchanged across refresh)
    • Three independent security/privacy, mutation-test, and scope/compatibility reviews: GO, no findings
    • Focused AUTH-006F: 12/12 passed
    • Full Account: 152/152 passed on exact head
    • Full frontend: 940/940 passed; diagnostic build passed
    • Full Functions on exact head: 6,572 passed; 64 intentional skips
    • Firestore Rules: 418/418 passed against demo emulators
    • Repository safety: 80/80 passed; SPA navigation: 11/11 passed
    • Frontend lint baseline: 118 files / 113 reviewed legacy errors / 6 reviewed legacy warnings, unchanged
    • Direct Account ESLint, TypeScript, and diff checks passed

    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.

  9. daliu commented on Aug 1, 2026

    @daliu
    ContributorAuthor

    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:

    • Pull request: AUTH-006F: fence My Account profile saves by current context #534
    • Reviewed head: 8714511032213ff570cc4d15fe0f7a31175b16e8
    • Squash merge / exact main: 86960409ffb527432cf4ae6f1713d663420c267e
    • Reviewed and merge tree: 067b6c05f1302ab61b733b6ccefb8b544f5113b1 (byte-identical)
    • Exact two-file binary diff SHA-256: 5fe87dd506abee7186ae59e4f2e119a381123907ed3a878ef0cb8b7a41297a63
    • PR CI run 30713640075: all five required jobs passed
    • Exact-main CI run 30713736965: all five required jobs passed
    • Three independent security/privacy, mutation-test, and scope/compatibility reviews: GO, no findings
    • Local final evidence included Account 152/152, frontend 940/940, Functions 6,572 passed with 64 intentional skips, Rules 418/418, repository safety 80/80, SPA 11/11, TypeScript, lint, and build

    Deployment truth:

    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.tsx and the named AUTH-006F block/harness in src/pages/account/Account.test.tsx are RELEASED. The remote branch is deleted.

  10. daliu commented on Aug 14, 2026

    @daliu
    ContributorAuthor

    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.

  11. daliu commented on Aug 14, 2026

    @daliu
    ContributorAuthor

    RELEASE NOTICE from /root: AUTH-006G #651 merged through PR #652 at exact main cfe03c66b8f443cb4e160a80d335d966ba268e27 (tree cd8c32d73021b026e145325d968f0a3cedf9bbff) 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 literal false, every #507 connection/release gate, and active #616 files/hunk. Exact-main CI 31768544349 passed 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.

  12. daliu commented on Aug 14, 2026

    @daliu
    ContributorAuthor

    PRECLAIM — AUTH-006H current-user profile-save unconfirmed-result focus (source only).

    Base: exact current origin/main cfe03c66b8f443cb4e160a80d335d966ba268e27; tree cd8c32d73021b026e145325d968f0a3cedf9bbff.

    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 focus is empty; #651 names this as a separate follow-up. #504/#507 remain coordination/release gates. Active #616 owns functions/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.

  13. daliu commented on Aug 14, 2026

    @daliu
    ContributorAuthor

    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.

  14. daliu commented on Aug 14, 2026

    @daliu
    ContributorAuthor

    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 / tree 6036ac48fd2c28f0cc0c05d5b1be09a7d812783e. 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.

  15. daliu commented on Aug 14, 2026

    @daliu
    ContributorAuthor

    RELEASE notice from /root: AUTH-006I #655 / PR #656 is complete and explicitly released. Exact main is 3265a55cb92df1ebb5401f2647ff81e4ca3af0b8, tree 322468cc75a57f7ea392eb9f57a201fb11c4b35a; exact-main CI 31772914108 passed 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 295909b0df8dd7c54a0f164f99d4943656aece4b and literal false; #507 retains every connection/live-proof gate. Active #616 Strava blobs remain 2eb48945061ab5ad2a896c6835f308cefd7fe63a and d1792d390bd57608fecab84341a6e5916fcf8196, and its separately named RISK-024 hunk is unchanged. Production remains inert #623.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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