Repository navigation
MEMBERS-DIRECTORY-001M — Restore surviving photo-control focus after rejected removal #643
Description
Parent: #504. Connected release remains owned by #507.
Officer impact: In the preserved future My Account interface, a keyboard or screen-reader user whose focused Remove action disappears after a rejected removal and authoritative profile refresh will be returned to the closest surviving photo action. 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 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
Restore keyboard focus to the correct surviving photo control after a focused Remove current saved photo receives a definitive rejection and a successful authoritative profile readback.
Defect
On exact released main f3da2f3, a Remove mutation disables the focused Remove action while pending. A definitive rejection then performs an authoritative profile read. If that current read says hasPhoto:false, Remove disappears. Unlike confirmed removal, the definitive-rejection path records no ready-state focus handoff. Browser-disabled or removed-control focus can therefore remain on the document body.
The existing rejection test returns a profile that still has a saved photo and does not assert focus, so it does not exercise the missing-control branch.
Invariants and transitions
- Create a rejection-focus intent only after request-ID creation and startMutation('remove') admit the exact current operation into pending.
- Create it only when Remove current saved photo owns focus at that point.
- Store only the current mounted application/account lifetime and exact mutation-operation identity. Store no photo bytes, profile value, request ID, revision, provider value, or error.
- Programmatic or outside-focused Remove invocation, request-ID failure, and rejected mutation start create no intent.
- After definitive Remove rejection and successful current authoritative readback, consume the matching intent after ready controls render.
- Choose the current rendered destination: surviving Remove; otherwise current enabled Save profile photo when the exact local draft is ready; otherwise the persistent Add/Replace file input, including no draft, reading, or not-yet-ready draft.
- If the target already owns focus, leave it alone. Restore only from body, the document root element, absent, or disconnected focus. Preserve any other connected focus deliberately selected during pending.
- Keep the fixed rejection role=alert. If Remove survives, retain its error description. If Remove disappears, leave the alert standalone; do not associate it with Save or the file input.
- Clear/fence the intent on confirmed success, unknown outcome, failed confirming read, application change, UID change, unmount, or obsolete operation.
- Preserve confirmed-success focus behavior, MEMBERS-DIRECTORY-001J — Preserve profile-recovery keyboard focus #637 Reload recovery focus, local draft identity/bytes/render state/revision rules, and all mutation/read/render/context fences.
- Focus creates no request ID, callable, retry, automatic mutation, draft, data URL, confirmation, audit, or provider/data action.
- Availability stays false; the default branch remains inert and zero-context/zero-ID/zero-call.
- 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 the exact-operation focused-Remove rejection intent, cleanup guards, and guarded current-state focus handoff.
- src/pages/account/MemberDirectoryProfile.test.tsx: one separately named MEMBERS-DIRECTORY-001M block plus minimum directly affected helpers/assertions.
- SYSTEM_DESIGN.md: one separately named 001M source-only paragraph.
- IMPLEMENTATION_PLAN.md: one separately named 001M current-source paragraph.
- SECURITY.md: one separately named 001M/RISK-042 row immediately after released 001L; 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 001M source-evidence procedure; no Mermaid or unrelated procedure change.
No CSS change: the same native controls and existing focus styles are used.
Trustworthy RED and required tests
- On unchanged runtime, focus Remove, begin a deferred valid removal, assert disabled, explicitly blur to model native focus eviction, reject definitively, resolve confirming read with hasPhoto:false, and prove focus remains off the surviving file input.
- Repeat with a current ready replacement and prove old source does not focus Save profile photo.
- Corrected behavior restores surviving Remove, ready Save, or file input for no/reading/not-ready draft.
- Surviving Remove retains aria-describedby to the fixed error; vanished Remove leaves a standalone alert and does not attach it to Save/input.
- Programmatic/outside-focused invocation creates no intent; deliberate outside focus during pending is preserved; already-retained Remove is not redundantly focused.
- Request-ID failure remains enabled/focused with zero removal call.
- Failed confirming read retains existing unavailable/Reload focus; ordinary unknown outcome retains existing uncertain/Reload focus; confirmed Remove behavior remains unchanged.
- Application change, UID change, unmount, and stale operation completion are focus-inert.
- No extra request ID, remove/upload/visibility call, retry, confirmation, draft, or data URL.
- Default preview remains zero-context/zero-ID/zero-call.
- Existing profile, draft, rejection, uncertainty, recovery-focus, response-boundary, accessibility, privacy, race, and mobile suites remain green.
Explicit exclusions
No Account.tsx/test, CSS, service adapter, availability, inbound WebP validation, admin finder, AdminGuard, routes, backend Function/Rule/index/schema/repair/package/lock/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. This focus handoff is accessibility state, not server acknowledgement, deletion proof, reconciliation, authorization, provider evidence, or proof that removal succeeded. A definitive rejection and a concurrent authoritative state with no photo may both be true; the rejection alert therefore remains separate from the current surviving action.
Availability stays false and live #623 remains inert. #507 retains every connection and release gate.
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
daliu commentedon Aug 14, 2026
CLAIMED by /root at 2026-08-14T00:40:00Z on branch
codex/issue-643-remove-rejection-focus, exact released basef3da2f351513f947b96d2cb9b883840d55919da9.Exact ownership:
src/pages/account/MemberDirectoryProfile.tsx: only the exact-operation focused-Remove definitive-rejection intent, current-state no-steal guards, cleanup, and post-render handoff.src/pages/account/MemberDirectoryProfile.test.tsx: one separately named MEMBERS-DIRECTORY-001M block plus minimum directly affected helpers/assertions.SYSTEM_DESIGN.mdandIMPLEMENTATION_PLAN.md;SECURITY.md;docs/officers/EVENTS_SHOP_MEMBERS.md; no CSS or Mermaid.The focus intent exists only for a focused Remove mutation admitted into pending and is bound to that operation/lifetime. After definitive rejection plus current successful readback it restores only from body/root/absent/disconnected focus to surviving Remove, otherwise current ready Save, otherwise the persistent file input; any deliberately chosen connected focus is preserved. If Remove disappears, its fixed rejection remains a standalone alert and is not falsely attached to Save/input. Confirmed-success focus and #637 Reload recovery stay unchanged.
Everything else is immutable: Account wiring/tests, CSS, services, availability, admin finder, backend/Rules/indexes/packages/workflows/release controls, #507 gates, Firebase/provider/account/sign-in/data/deploy/live work, and both #616 Strava files plus its separate OAUTH/RISK-024 SECURITY hunk. No source edit preceded this marker. Claim remains active through reviewed merge, exact-main/deployment audit, closure, and explicit release.
daliu commentedon Aug 14, 2026
CLAIM TIMESTAMP CORRECTION — GitHub created the claim marker at authoritative server time
2026-08-14T00:39:35Z. The body's00:40:00Zwas a transcription error and must not be used as the claim time. Exact branch/base/six-path ownership, invariants, exclusions, and claim lifecycle are unchanged. No source edit has occurred; the worktree remains clean. This correction is part of the pre-edit claim record.daliu commentedon Aug 14, 2026
#643 candidate evidence — 2026-08-14T01:03:16Z
Exact released base and current remote
main:f3da2f351513f947b96d2cb9b883840d55919da9(tree0a2deecc78c1c1645c50db8a1b79f991df41f580).The reviewed working diff is exactly the six claimed paths:
src/pages/account/MemberDirectoryProfile.tsxsrc/pages/account/MemberDirectoryProfile.test.tsxSYSTEM_DESIGN.mdIMPLEMENTATION_PLAN.mdSECURITY.mddocs/officers/EVENTS_SHOP_MEMBERS.mdExact binary diff SHA-256:
0497c75100eddc38b7b3b38d7fd8403cb60d01b7bbad9a50bbb79472ec3d28b7.Trustworthy RED on unchanged runtime: the separately named MEMBERS-DIRECTORY-001M block had 5 focus-destination failures and 9 control passes. Corrected source: 15/15 named tests and 111/111 full
MemberDirectoryProfiletests pass.Verification is green:
--noEmit;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, and Mermaid bytes are unchanged.
The source-controlled availability file remains literal
falseat blob295909b0df8dd7c54a0f164f99d4943656aece4b. Active #616 files remain byte-identical:functions/strava.jsblob2eb48945061ab5ad2a896c6835f308cefd7fe63aandfunctions/strava.test.jsblobd1792d390bd57608fecab84341a6e5916fcf8196. 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.Restore focus after rejected photo removal (#643) (#644)
daliu commentedon Aug 14, 2026
COMPLETE / CLOSED / CLAIM RELEASED — 2026-08-14T01:13:26Z
PR #644 merged as exact
maincommit6b6a597914b3322275a1afe3e5a3c7133ab2d0a9, sole parentf3da2f351513f947b96d2cb9b883840d55919da9, treea07d391b8e23e28dd39785329be444f452fa2953. The tree is identical to reviewed head5ac46b43b7d2e18e338bb75d23106a851d6c7942; the parent-to-main binary diff SHA-256 is0497c75100eddc38b7b3b38d7fd8403cb60d01b7bbad9a50bbb79472ec3d28b7across exactly the six claimed paths.Exact-main CI run
31759745715completed successfully with all five jobs green. Netlify main attempt6a7e6ae13dd14f00086801bais terminalerror, exact commit6b6a597...,production/main, and unpublished (published_at:null) with no Functions or Edge Functions. No GitHub deployment exists for this SHA.The public production pointer remains inert #623 deploy
6a7e072f8f346b0008510d29, control9d5cc8612b4321172370bd949d307e7e4ac0ec7d. The public release marker remains byte-identical at SHA-256b74498854585346aa66c0341e29c829245445ce986806c3864cad526b267876f. Availability remains literalfalseat blob295909b0df8dd7c54a0f164f99d4943656aece4b.Active #616 files remain byte-identical (
2eb48945061ab5ad2a896c6835f308cefd7fe63a,d1792d390bd57608fecab84341a6e5916fcf8196) and its separate SECURITY hunk remains outside this change.No website, Firebase, provider, account/sign-in, production-data, or connected-live behavior was changed. This closes only the source/test accessibility hardening. #507 retains every connection and release gate.
The exact file/hunk claim is released. The remote working branch is retired. Remove the assignee and
status:in-progress; later work must take a fresh claim from the then-current released main.