Tapping a day tile on the mobile draft board puts it in the swapping state (bg + 2 px grape inset ring, kicker "FRI · SWAPPING…", muted name) and opens a bottom Sheet with a "Quest log · N | Regulars · N" segmented control listing dinners (48 px photo, name, difficulty/minutes); choosing one calls PUT /plans/{weekStart}/days/{date} optimistically (rollback + ErrorBanner on failure), closes the sheet and updates the tile and NEW highlight. Dinners already in the week are marked "Already this week" and disabled. Vitest tests cover open/close, swapping state, successful swap, rollback on failure and disabled duplicates. Typecheck, test and build pass.
Scope
Mobile swap sheet only. Explicitly out of scope and left to later tasks: the tablet/desktop plan manager (4657cba1) and drag-and-drop / keyboard swapping (5c2842a8). No backend, migration, skill or API-contract change — the swap endpoint and both dinner lists already exist on the base.
Implementation notes
New SwapSheet built from the existing Sheet, Segmented, ListRow, Photo, Tag, Skeleton and ErrorBanner; lists come from the existing useQuestLog and useRegulars.
useSwapDay: an optimistic swap that lands the server's plan, rolls that day back on failure, and re-reads the Quest log when the swap settles.
Day tiles are buttons with a swapping state; one swap is sent at a time so a later reply cannot overwrite an in-flight one; a swap failure shows an ErrorBanner with Retry.
weekdayName added to web/src/plan/week.ts; QuestLogSheet now exports its details line so difficulty/minutes is written in one place.
Evidence (orchestrator-run pre-review gate at cd3771f9ec)
gofmt -l . — empty (no Go file changed)
cd web && npm run typecheck — PASS
cd web && npm test — PASS, 44 files / 690 tests (includes the colour-literal lint)
cd web && npm run build — PASS (includes lint:colors and check:dist-fonts)
Not verified in a real browser by the developer; behaviour is covered by jsdom tests with a mocked API.
## Task
Maestro task `f231813c-992e-4a3b-a1a4-868cbad47415` — "Mobile swap sheet" (Feature 14, Weekly Plan Builder).
## Done-condition
Tapping a day tile on the mobile draft board puts it in the swapping state (bg + 2 px grape inset ring, kicker "FRI · SWAPPING…", muted name) and opens a bottom Sheet with a "Quest log · N | Regulars · N" segmented control listing dinners (48 px photo, name, difficulty/minutes); choosing one calls `PUT /plans/{weekStart}/days/{date}` optimistically (rollback + ErrorBanner on failure), closes the sheet and updates the tile and NEW highlight. Dinners already in the week are marked "Already this week" and disabled. Vitest tests cover open/close, swapping state, successful swap, rollback on failure and disabled duplicates. Typecheck, test and build pass.
## Scope
Mobile swap sheet only. Explicitly out of scope and left to later tasks: the tablet/desktop plan manager (`4657cba1`) and drag-and-drop / keyboard swapping (`5c2842a8`). No backend, migration, skill or API-contract change — the swap endpoint and both dinner lists already exist on the base.
## Implementation notes
- New `SwapSheet` built from the existing `Sheet`, `Segmented`, `ListRow`, `Photo`, `Tag`, `Skeleton` and `ErrorBanner`; lists come from the existing `useQuestLog` and `useRegulars`.
- `useSwapDay`: an optimistic swap that lands the server's plan, rolls that day back on failure, and re-reads the Quest log when the swap settles.
- Day tiles are buttons with a swapping state; one swap is sent at a time so a later reply cannot overwrite an in-flight one; a swap failure shows an `ErrorBanner` with Retry.
- `weekdayName` added to `web/src/plan/week.ts`; `QuestLogSheet` now exports its `details` line so difficulty/minutes is written in one place.
## Evidence (orchestrator-run pre-review gate at cd3771f9ec13a5c747afb7cc453861fa268196bb)
- `gofmt -l .` — empty (no Go file changed)
- `cd web && npm run typecheck` — PASS
- `cd web && npm test` — PASS, 44 files / 690 tests (includes the colour-literal lint)
- `cd web && npm run build` — PASS (includes `lint:colors` and `check:dist-fonts`)
Implementation commit: `cd3771f9ec13a5c747afb7cc453861fa268196bb`
Not verified in a real browser by the developer; behaviour is covered by jsdom tests with a mocked API.
Tapping a day tile puts it in the swapping state and opens a bottom sheet
segmented "Quest log · N | Regulars · N". Choosing a dinner swaps the day
optimistically with PUT /api/v1/plans/{weekStart}/days/{date}, landing the
server's plan on success and rolling the day back with an ErrorBanner and
Retry on failure. Dinners already this week are marked and not selectable.
Maestro task f231813c-992e-4a3b-a1a4-868cbad47415.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Draft board's swap guard was its mounted useMutation observer, so
leaving the week and coming back let a second swap start while the first
PUT was still pending, and Rebuild week's reset() detached a pending swap,
hiding its failure.
The week's swaps now live in the mutation cache, keyed by week: the guard,
the failure shown and the success handler all read them from there. Only
the latest swap of the week writes the plan it is answered with, and only
its failure is the week's. Rebuild week waits for a pending swap (its tile
is disabled meanwhile), and dismissing settled swaps is refused while one
is pending. A failed swap puts its day back only while the day is still,
field for field, the day it wrote, so a newer plan read survives.
Maestro task f231813c-992e-4a3b-a1a4-868cbad47415.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Task
Maestro task
f231813c-992e-4a3b-a1a4-868cbad47415— "Mobile swap sheet" (Feature 14, Weekly Plan Builder).Done-condition
Tapping a day tile on the mobile draft board puts it in the swapping state (bg + 2 px grape inset ring, kicker "FRI · SWAPPING…", muted name) and opens a bottom Sheet with a "Quest log · N | Regulars · N" segmented control listing dinners (48 px photo, name, difficulty/minutes); choosing one calls
PUT /plans/{weekStart}/days/{date}optimistically (rollback + ErrorBanner on failure), closes the sheet and updates the tile and NEW highlight. Dinners already in the week are marked "Already this week" and disabled. Vitest tests cover open/close, swapping state, successful swap, rollback on failure and disabled duplicates. Typecheck, test and build pass.Scope
Mobile swap sheet only. Explicitly out of scope and left to later tasks: the tablet/desktop plan manager (
4657cba1) and drag-and-drop / keyboard swapping (5c2842a8). No backend, migration, skill or API-contract change — the swap endpoint and both dinner lists already exist on the base.Implementation notes
SwapSheetbuilt from the existingSheet,Segmented,ListRow,Photo,Tag,SkeletonandErrorBanner; lists come from the existinguseQuestLoganduseRegulars.useSwapDay: an optimistic swap that lands the server's plan, rolls that day back on failure, and re-reads the Quest log when the swap settles.ErrorBannerwith Retry.weekdayNameadded toweb/src/plan/week.ts;QuestLogSheetnow exports itsdetailsline so difficulty/minutes is written in one place.Evidence (orchestrator-run pre-review gate at
cd3771f9ec)gofmt -l .— empty (no Go file changed)cd web && npm run typecheck— PASScd web && npm test— PASS, 44 files / 690 tests (includes the colour-literal lint)cd web && npm run build— PASS (includeslint:colorsandcheck:dist-fonts)Implementation commit:
cd3771f9ec13a5c747afb7cc453861fa268196bbNot verified in a real browser by the developer; behaviour is covered by jsdom tests with a mocked API.
Tapping a day tile puts it in the swapping state and opens a bottom sheet segmented "Quest log · N | Regulars · N". Choosing a dinner swaps the day optimistically with PUT /api/v1/plans/{weekStart}/days/{date}, landing the server's plan on success and rolling the day back with an ErrorBanner and Retry on failure. Dinners already this week are marked and not selectable. Maestro task f231813c-992e-4a3b-a1a4-868cbad47415. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.
Independent Codex gpt-6-sol review: APPROVED for this exact commit.