Skip to content

MEMBERS-DIRECTORY-001N — Preserve deliberate focus during confirmed photo changes #645

Closed

Description

@daliu

Parent: #504. Connected release remains owned by #507.

Officer impact: No live officer workflow changes. In the preserved future My Account interface, a confirmed profile-photo Save or removal will return keyboard focus only when that exact focused initiating control was displaced; it will not steal focus that the user deliberately moved to another connected control while work was pending. The connected interface remains unavailable on production.

Officer documentation: Update SYSTEM_DESIGN.md, IMPLEMENTATION_PLAN.md, SECURITY.md, and only the optional-directory one-sentence text alternative plus one no-terminal source-evidence procedure in docs/officers/EVENTS_SHOP_MEMBERS.md. No Mermaid change is needed because page topology, permissions, and data movement do not change.

Deployment evidence: None at issue creation. This is source-only accessibility/state hardening. memberDirectoryAvailability.ts must remain byte-for-byte false; no website, Firebase, provider, account/sign-in, production-data, deployment, or connected-live action is authorized.

Atomic outcome

Contain confirmed profile-photo upload/removal focus handoffs so they restore only focus displaced from the exact initiating Save or Remove control and preserve any other connected focus deliberately selected while the mutation or authoritative readback is pending.

Exact base

  • Source: 6b6a597914b3322275a1afe3e5a3c7133ab2d0a9
  • Tree: a07d391b8e23e28dd39785329be444f452fa2953
  • Availability blob: 295909b0df8dd7c54a0f164f99d4943656aece4b

Defect

The preserved connected Account branch currently sets postMutationFocusRef after every confirmed photo upload or removal and then focuses File, Remove, or Save unconditionally. That can avoid stranding focus when an initiating control disappears, but it also steals focus if the user deliberately selects another connected control while the mutation or authoritative profile readback is pending. A programmatic or outside-focused invocation can also create an unrelated handoff.

The released People-finder and rejected-removal paths already distinguish displaced focus from meaningful connected focus. Confirmed photo success needs the same containment without changing its established destination priority.

Invariants and transitions

  • Create a confirmed-photo focus intent only after secure request-ID creation and startMutation('upload'|'remove') admit the exact current operation.
  • Create it only when Save profile photo owns focus for upload or Remove current saved photo owns focus for removal.
  • Bind it to the mounted application/account lifetime, exact operation symbol, and action only. Store no name, profile, revision, request ID, photo bytes, data URL, provider value, response, or error.
  • Transfer only a matching current intent after mutation success and a current successful authoritative profile readback.
  • Confirmed upload keeps the existing destination: the persistent Add/Replace file input.
  • Confirmed removal keeps the existing priority: surviving Remove; otherwise enabled Save for the exact current ready draft; otherwise the persistent file input.
  • Leave an already-focused destination alone.
  • Restore only from absent focus, document.body, document.documentElement, or a disconnected element.
  • Preserve any other connected focus selected while pending.
  • Clear or fail the intent on request-ID failure, rejected mutation admission, definitive rejection, unknown outcome, failed readback, application change, UID change, unmount, or obsolete completion.
  • Preserve MEMBERS-DIRECTORY-001M — Restore surviving photo-control focus after rejected removal #643's distinct rejected-removal focus/error behavior and MEMBERS-DIRECTORY-001J — Preserve profile-recovery keyboard focus #637 Reload recovery.
  • Focus creates no request ID, callable, retry, mutation, draft, data URL, confirmation, audit, provider action, or data action.
  • Availability stays literal false; the default branch and live WEB-002C — Publish the bounded inert member-directory interface preview #623 preview remain inert.
  • No photo query, facial recognition, matching, embedding, similarity, biometric processing, roster authority, or membership proof is added.

Exact ownership

  • src/pages/account/MemberDirectoryProfile.tsx: only exact-operation confirmed upload/remove focus intent, cleanup guards, transfer, and no-steal ready handoff.
  • src/pages/account/MemberDirectoryProfile.test.tsx: one separately named MEMBERS-DIRECTORY-001N block plus minimum directly affected assertions.
  • SYSTEM_DESIGN.md: one separately named 001N source-only paragraph.
  • IMPLEMENTATION_PLAN.md: one separately named 001N current-source paragraph.
  • SECURITY.md: one separately named 001N/RISK-042 row immediately after released 001M; preserve active OAUTH-001A2L — Bound Strava activity/statistics JSON and cancel the unread statistics body #616's future OAUTH-001A2L/RISK-024 hunk.
  • docs/officers/EVENTS_SHOP_MEMBERS.md: only the optional-directory one-sentence text alternative and one no-terminal 001N evidence procedure; no Mermaid or unrelated procedure change.

No other path is in scope.

Trustworthy RED and required tests

  • On unchanged runtime, demonstrate that a deliberately selected connected outside or in-profile control is overwritten by confirmed upload/removal settlement.
  • Focused Save with modeled native eviction returns to the persistent file input after confirmed upload and authoritative readback.
  • Focused Remove with modeled eviction returns to the file input when no photo/draft remains, to exact current ready Save when a draft remains, or to surviving Remove when the photo remains.
  • An already-focused destination gets no redundant .focus() call.
  • Deliberately connected outside and in-profile focus remains focused for both upload and removal.
  • Outside-focused/programmatic Save or Remove creates no focus handoff.
  • Request-ID failure creates no intent, extra ID, or service call.
  • Definitive rejection, unknown outcome, and failed readback do not use confirmed-success focus; rejected removal retains MEMBERS-DIRECTORY-001M — Restore surviving photo-control focus after rejected removal #643 and recovery retains MEMBERS-DIRECTORY-001J — Preserve profile-recovery keyboard focus #637.
  • Application change, UID change, unmount, stale mutation completion, and stale readback are focus-inert.
  • Preserve exact request/read counts, upload content type and bytes, expected revisions, draft retention/disposal, and confirmation copy.
  • Render/log no raw value, filename, photo byte, provider detail, or caught error.
  • Default unavailable rendering still obtains no directory-service context, creates no request ID, and calls no directory service.
  • Update older confirmed-success tests that use bare fireEvent.click to focus the initiating control first; do not remove their destination, revision, draft, byte, or call-count assertions.

Run the named tests, full component and frontend suites, TypeScript, scoped ESLint, unchanged lint baseline, repository Node tests, diagnostic build, workflow checks, and diff-check.

Explicit exclusions

No CSS, Account.tsx/profile-name editing, People finder, visibility-toggle behavior, Reload-success behavior, service contract, Function, Rule, index, schema, repair, package/lockfile, workflow/release control, privacy/capability policy, Firebase/provider configuration, deployment, account/sign-in, production data, real name/photo, connected/live action, or #616 path/hunk.

Migration and residual risk

No migration. Programmatic focus is interface accessibility state, not server acknowledgement, upload/removal proof, authorization, reconciliation, provider evidence, or live proof. #507 retains every privacy, authorization, staging, backend-first deployment/readback, availability-flip, publication, and connected-live gate.

Stop conditions

Stop if the diff leaves the six paths, changes availability, request counts/bytes/revisions/confirmations, weakens #643/#637, restores stale focus, stores personal/service values in the intent, changes CSS/Mermaid, overlaps #616, connects/deploys the backend, uses real data, or implies source/tests/preview are live.

Claim protocol

Assign the issue and post a timestamped CLAIMED marker naming the exact branch and released base before any edit. The claim remains active through reviewed merge, exact-main/deployment audit, closure, and explicit release.

Activity

  1. added
    bugSomething isn't working
    priority:P1High-priority follow-up
    type:securitySecurity or privacy boundary
    area:membershipMembership lifecycle and reconciliation
    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
    status:in-progressClaimed and actively being implemented
    and removed
    status:readyDependencies complete and ready to claim
    on Aug 14, 2026
  2. self-assigned this
    on Aug 14, 2026
  3. daliu commented on Aug 14, 2026

    @daliu
    ContributorAuthor

    CLAIMED by /root at 2026-08-14T01:23:39Z. Branch: codex/issue-645-confirmed-photo-focus. Exact released base: 6b6a597, tree a07d391b8e23e28dd39785329be444f452fa2953. Ownership is exactly the six paths/hunks in the issue. Availability stays false; #616 files and OAUTH/RISK-024 hunk are immutable. No source edit preceded this marker. Claim remains active through reviewed merge, exact-main/non-publication audit, closure, and explicit release.

  4. daliu commented on Aug 14, 2026

    @daliu
    ContributorAuthor

    #645 candidate evidence — 2026-08-14T01:44:28Z

    Exact released base and current remote main: 6b6a597914b3322275a1afe3e5a3c7133ab2d0a9 (tree a07d391b8e23e28dd39785329be444f452fa2953).

    The reviewed working diff is exactly the six claimed paths:

    • src/pages/account/MemberDirectoryProfile.tsx
    • src/pages/account/MemberDirectoryProfile.test.tsx
    • SYSTEM_DESIGN.md
    • IMPLEMENTATION_PLAN.md
    • SECURITY.md
    • docs/officers/EVENTS_SHOP_MEMBERS.md

    Exact binary diff SHA-256: 1419b9bd720e876465c0e63c5d0110f8109b2ce7a84d166747202be2c09f939a.

    Trustworthy RED on unchanged runtime: all four initial MEMBERS-DIRECTORY-001N cases failed because confirmed upload/removal stole deliberately selected connected outside or in-profile focus. Corrected source: 20/20 named tests and 131/131 full MemberDirectoryProfile tests pass.

    Verification is green:

    • full frontend: 18 suites, 1,216 tests;
    • TypeScript --noEmit;
    • scoped ESLint;
    • lint baseline: 120 files, 113 reviewed legacy errors, 6 reviewed warnings;
    • repository Node/workflow/security tests: 105/105;
    • diagnostic production build;
    • git diff --check.

    Independent focus/race, security/privacy, frontend/accessibility, and backup-officer/operations reviews all report GO with no findings. The officer procedure is no-terminal; its text alternative is one sentence; all Mermaid blocks are unchanged.

    The source-controlled availability file remains literal false at blob 295909b0df8dd7c54a0f164f99d4943656aece4b. Active #616 files remain byte-identical: functions/strava.js blob 2eb48945061ab5ad2a896c6835f308cefd7fe63a and functions/strava.test.js blob d1792d390bd57608fecab84341a6e5916fcf8196; its separate RISK-024/OAUTH hunk is untouched.

    This is SOURCE ONLY and NOT LIVE. Live production remains the inert #623 deploy 6a7e072f8f346b0008510d29; no website, Firebase, provider, account/sign-in, production-data, or connected behavior was changed or tested. #507 retains every connection and release gate.

  5. daliu commented on Aug 14, 2026

    @daliu
    ContributorAuthor

    COMPLETE / CLOSED / CLAIM RELEASED — MEMBERS-DIRECTORY-001N

    Merged PR #646 as exact main 0f421e6d1ffc454aa1de09d342d7b70d3fbd2388 (parent 6b6a597914b3322275a1afe3e5a3c7133ab2d0a9, tree 292cc0cd87e3246d1b2976a15f2d48bc7b3dce93). The tree is identical to reviewed head 4a855575eb071a4ec9f605a5b3aa2bfbfbecd050; the exact six-path binary diff SHA-256 remains 1419b9bd720e876465c0e63c5d0110f8109b2ce7a84d166747202be2c09f939a.

    Outcome: confirmed profile-photo Save/removal focus handoff is now bound to the exact admitted operation and initiating control. It restores only genuinely lost focus and preserves any deliberately focused connected control. Existing rejected-removal and Reload recovery paths remain separate. No request, callable, retry, photo bytes, revision, backend, provider, account, sign-in, or production-data behavior changed.

    Evidence:

    • trustworthy old-source RED: 4/4 named cases failed on the former unconditional focus handoff;
    • final MEMBERS-DIRECTORY-001N: 20/20; full profile: 131/131; full frontend: 1,216/1,216;
    • TypeScript, scoped lint, lint baseline, repository Node tests 105/105, diagnostic build, and diff-check passed;
    • three independent implementation/security/frontend reviews plus backup-officer review returned GO;
    • exact-head CI 31761641385 and exact-main CI 31761911555 each passed all five jobs;
    • exact-head Netlify preview 6a7e73629a8f58000829005f was ready and unpublished with no Netlify or Edge Functions;
    • exact-main Netlify attempt 6a7e749c42ee140009ba7759 is error/unpublished;
    • the published pointer remains inert WEB-002C — Publish the bounded inert member-directory interface preview #623 deploy 6a7e072f8f346b0008510d29, and the public marker remains byte-identical SHA-256 b74498854585346aa66c0341e29c829245445ce986806c3864cad526b267876f;
    • memberDirectoryAvailability.ts remains literal false; OAUTH-001A2L — Bound Strava activity/statistics JSON and cancel the unread statistics body #616's two Strava blobs remain byte-identical.

    No Firebase, Rules, indexes, Functions, provider configuration, account/sign-in, production-data, or live connected action occurred. #507 retains every connection and release gate.

    The exact #645 source, test, and separately named documentation hunks are now released for later work. Any later child must refresh from exact current main and preserve #616's active paths and SECURITY hunk.

  6. removed their assignment
    on Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:membershipMembership lifecycle and reconciliationarea:privacyPersonal data, consent, minimization, retention, and privacy operationsarea:webWeb application and hostingbugSomething isn't workingpriority:P1High-priority follow-upsize: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