Lucas Berger and Claude Sonnet 4.6
ee04aee4fb
fix(20): CR-02 derive editorMember from live query data
...
Store only the member id in editorMemberId state rather than the full
AdminMember object. Derive editorMember from membersQuery.data after each
render, so query invalidations following a per-section save always give
MemberEditorSheet fresh server state — eliminating the stale-reference
silent-overwrite race.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-18 18:00:47 -04:00
Lucas Berger and Claude Sonnet 4.6
72977334fc
fix(20): CR-01 WR-06 atomic last-admin guard + empty-body 400
...
Wrap the last-admin check and UPDATE in a db.transaction with a
SELECT...FOR UPDATE locking read so concurrent PATCH demotions
serialise and cannot both pass the guard, eliminating the TOCTOU
race (CR-01).
Add a .refine() to updateMemberSchema requiring at least one field,
returning 400 via noEchoHook instead of crashing Drizzle with an
empty SET clause (WR-06).
Add Test H asserting empty {} -> 400 { error: 'Invalid request' }.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-18 17:59:56 -04:00
Lucas Berger
5c74ada48b
docs(20): add deep code review report
2026-06-18 17:57:08 -04:00
Lucas Berger and Claude Opus 4.8
f656a0c77b
docs(phase-20): evolve PROJECT.md after phase completion
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-18 17:48:23 -04:00
Lucas Berger and Claude Opus 4.8
dec8220da3
docs(phase-20): complete phase execution — verification passed (9/9), advisory review
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-18 17:47:51 -04:00
Lucas Berger
80fd683877
docs(20-03): complete member editor + declutter plan
2026-06-18 17:40:52 -04:00
Lucas Berger
9b62887f0f
feat(20-03): unify member editor + declutter admin members panel
...
- playwright-cli verified: Members tab shows tappable rows, no retired buttons
- Row tap opens 'Edit member' sheet; per-section saves keep sheet open
- 'Add member' trigger opens 'Add member' sheet in create mode
- Profile save fires 'Profile saved.' toast; sheet stays open (D-05)
- eslint + prettier + typecheck + vitest (275 tests) all pass
- Fix pre-existing prettier drift in docs/*, CLAUDE.md, README.md, api/admin.ts
2026-06-18 17:39:00 -04:00
Lucas Berger
9e6b004541
feat(20-03): rework AdminPage MemberRow + Add-member trigger; remove old surfaces
...
- MemberRow: whole-row role=button, aria-label='Edit {name}', trailing ChevronRight
- Admin badge on member.isAdmin (12px/600, --color-member-0 on --color-surface-dim)
- 'Add member' ghost trigger with Plus icon opens MemberEditorSheet in create mode
- Single MemberEditorSheet replaces CredentialSheet + ResetPasswordSheet mounts
- Removed: inline Local Accounts add-form, ResetPasswordSheet definition, create-form state
- Empty state: 'No members yet' / 'Add a member to get started.' (UI-SPEC copy)
2026-06-18 17:35:37 -04:00
Lucas Berger
b125a69b58
feat(20-03): add MemberEditorSheet with edit/create modes and per-section saves
...
- Edit mode: Profile (display name + admin toggle), Set new password, App password sections
- Create mode: single form with display name, username, initial/confirm password
- Per-section saves keep sheet open; create success closes sheet (D-05, D-07)
- Admin toggle role=switch, aria-checked; last-admin 409 shows inline error + reverts (D-03)
- Section 2 gated on member.hasLocalCredential; passwords never prefilled (T-20-07)
- App password save routes through saveCredential -> CalDAV validation (T-20-08)
- No Rotate/Add credential/Reset password copy (D-06)
2026-06-18 17:31:46 -04:00
Lucas Berger
10149a5966
docs(phase-20): update tracking after wave 1
2026-06-18 17:23:49 -04:00
Lucas Berger
258a188bde
chore: merge executor worktree (worktree-agent-af493496867aaca6d)
2026-06-18 17:21:59 -04:00
Lucas Berger
b377e9cb0a
chore: merge executor worktree (worktree-agent-afcc2c886302ea642)
2026-06-18 17:21:59 -04:00
Lucas Berger
4a179a943d
docs(20-01): complete member-profile update plan — SUMMARY + all CI gates green
2026-06-18 17:21:30 -04:00
Lucas Berger and Claude Sonnet 4.6
618991cf13
docs(20-02): complete plan 20-02 summary
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-18 17:21:26 -04:00
Lucas Berger and Claude Sonnet 4.6
5bcd8180c1
feat(20-02): add updateMemberProfile fetcher + AdminMember.isAdmin
...
- Add isAdmin: boolean to AdminMember interface (after color, before
hasCredential) — feeds the Phase 20 editor toggle initial state (D-02)
- Add updateMemberProfile(memberId, body) fetcher: PATCH /api/admin/members/:id,
credentials:include, redirect:manual, JSON body
- Maps 401/opaqueredirect → SessionExpiredError (existing convention)
- Maps 409/422 → Error('last-admin') sentinel (D-03 last-admin guard)
- Maps other non-ok → generic error
- All 50 tests pass; eslint + prettier + tsc --noEmit exit 0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-18 17:20:13 -04:00
Lucas Berger
bc48632756
feat(20-01): add PATCH /members/:id member-profile update with last-admin guard
...
- Add isAdmin field to GET /members select and mapped member object (D-02)
- Add updateMemberSchema (displayName optional string, isAdmin optional boolean)
- Register adminRouter.patch('/members/:id') with noEchoHook and requireAdmin (inherited)
- Handler: parsePositiveIntParam id validation (400), existence check (404),
D-03 last-admin guard via COUNT(*) query (409), partial set() update (200)
- Fix Test D: switch to adminId2 for GET after self-demotion (adminId1 no longer admin)
- All 44 tests green including 7 new PATCH/isAdmin tests
2026-06-18 17:19:43 -04:00
Lucas Berger and Claude Sonnet 4.6
18da7e9476
test(20-02): add failing tests for updateMemberProfile + AdminMember.isAdmin
...
- 8 RED tests covering: PATCH URL contract, credentials/redirect shape,
void on 200, SessionExpiredError on 401/opaqueredirect, last-admin
sentinel on 409 and 422, generic error on 500
- 1 compile-time shape test for AdminMember.isAdmin: boolean
- All new tests fail (updateMemberProfile is not a function); 42 existing pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-18 17:18:21 -04:00
Lucas Berger
a0a82ac9b6
test(20-01): add failing tests for member-profile update + last-admin guard + isAdmin read
...
- Test A: PATCH displayName happy path → 200, GET reflects change
- Test B: PATCH isAdmin promote → 200, GET shows isAdmin true
- Test C: last-admin guard → 409 when only admin demotes self
- Test D: self-demotion → 200 when second admin exists
- Test E: non-admin PATCH → 403 (requireAdmin boundary)
- Test F: wrong-type body → 400 { error: "Invalid request" }; malformed :id → 400
- Test G: non-existent member id → 404
- Test H: GET /members includes boolean isAdmin per member
All 7 new tests fail RED for the right reasons (route 404 / isAdmin missing)
2026-06-18 17:18:14 -04:00
Lucas Berger
9b8b84edbe
docs(20): finalize phase plan (state, roadmap waves, patterns)
2026-06-18 17:06:40 -04:00
Lucas Berger
666845a192
docs(20): create phase plan
2026-06-18 17:04:21 -04:00
Lucas Berger
a3d89d0da0
docs(20): UI design contract
2026-06-18 16:51:46 -04:00
Lucas Berger
96193831c4
docs(20): UI design contract
2026-06-18 16:50:31 -04:00
Lucas Berger
fe0325ec43
docs(state): record phase 20 context session
2026-06-18 16:42:23 -04:00
Lucas Berger
91948919a8
docs(20): capture phase context
2026-06-18 16:42:19 -04:00
Lucas Berger
51ec9c3e82
docs(roadmap): add Phase 20 — Admin Member Editor & Form Declutter
2026-06-18 15:14:25 -04:00
Lucas Berger
b8d4a69b73
docs(phase-17): add security threat verification (9 closed, 0 open)
CI / changes (pull_request) Successful in 4s
CI / fast-checks (pull_request) Failing after 1m41s
CI / api (pull_request) Successful in 2m2s
CI / security (pull_request) Successful in 54s
CI / gate (pull_request) Has been cancelled
CI / harness (pull_request) Has been cancelled
2026-06-18 14:33:10 -04:00
Lucas Berger
881f2d2d18
test(17): complete UAT - 9 passed, 0 issues (playwright-cli verified)
2026-06-18 14:31:32 -04:00
Lucas Berger
6fa6725fe8
docs(17): add code review fix report (auto-loop, 3 iterations)
2026-06-18 14:06:03 -04:00
Lucas Berger
287ecae2f7
fix(17): CR-01 make focus-trap visibility filter tolerant of jsdom; IN-01 drop inert containment guard
2026-06-18 14:04:33 -04:00
Lucas Berger
89dee4f586
fix(17): IN-03 render visible Redirecting status during OIDC redirect
2026-06-18 13:57:31 -04:00
Lucas Berger
f7575ea2c3
fix(17): IN-02 document hand-maintained favicon.ico coupling
2026-06-18 13:56:48 -04:00
Lucas Berger
7578d48d3d
fix(17): IN-01 pull focus back into dialog when activeElement escapes
2026-06-18 13:56:32 -04:00
Lucas Berger
5b4625b41d
fix(17): WR-01 exclude hidden/zero-size nodes from focus-trap boundaries
2026-06-18 13:56:23 -04:00
Lucas Berger
4bc1e2a820
fix(17): IN-05 normalize AdminPage JSX formatting with Prettier
2026-06-18 13:48:34 -04:00
Lucas Berger
11b6b36cb9
fix(17): IN-07 memoize Intl.DateTimeFormat timezone resolution
2026-06-18 13:47:37 -04:00
Lucas Berger
2317833b74
fix(17): IN-04 remove dead brand placeholder tokens
2026-06-18 13:46:47 -04:00
Lucas Berger
3f4b7eac73
fix(17): IN-02 useCallback handleClose, drop exhaustive-deps disables
2026-06-18 13:46:27 -04:00
Lucas Berger
dd0b76128d
fix(17): WR-08 replace cp icon chain with cross-platform Node copy script
2026-06-18 13:45:36 -04:00
Lucas Berger
1c0f35748d
fix(17): WR-06 WR-07 clamp tz combobox active index; Tab-to-commit, blur-timer unmount cleanup
2026-06-18 13:44:44 -04:00
Lucas Berger
f601c0c408
fix(17): WR-02 WR-03 WR-04 IN-06 admin tab Home/End+wrap; toast re-announce, wrap, z-index 400
2026-06-18 13:43:10 -04:00
Lucas Berger
fb30800e9a
fix(17): WR-01 trap focus in modal dialogs via useFocusTrap
2026-06-18 13:41:33 -04:00
Lucas Berger
a4a7438641
fix(17): WR-05 IN-03 IN-01 resize-aware useIsPhone hook; OidcRedirect side-effect in effect
2026-06-18 13:40:00 -04:00
Lucas Berger
c2ceebf130
docs(17): add deep code review report
2026-06-18 13:35:10 -04:00
Lucas Berger and Claude Opus 4.8
a986c74963
docs(phase-17): mark phase complete (pending human verification)
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-18 13:11:35 -04:00
Lucas Berger and Claude Opus 4.8
fcc02e3833
docs(17): add phase verification report (10/10 decisions delivered; human_needed for device-only checks)
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-18 13:11:23 -04:00
Lucas Berger and Claude Opus 4.8
9730d3dcdb
docs(17): add code review report
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-18 13:06:20 -04:00
Lucas Berger and Claude Opus 4.8
ea49a4dc83
docs(phase-17): update tracking after wave 2
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-18 13:03:12 -04:00
Lucas Berger and Claude Opus 4.8
b5fcd1d172
fix(17): resolve post-merge wave 2 integration issues
...
- SettingsSheet: void the navigate('/login') promise (react-router v7 returns Promise) — eslint no-floating-promises
- InstructionSheet.test: wrap SettingsSheet render in MemoryRouter — 17-05 added useNavigate() which needs Router context
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-18 13:01:39 -04:00
Lucas Berger
4887ba1b5f
chore: merge executor worktree (worktree-agent-a12887546a61a8053)
2026-06-18 12:58:59 -04:00
Lucas Berger
aaf754d938
chore: merge executor worktree (worktree-agent-a65077bbd9885bf7e)
2026-06-18 12:58:58 -04:00