
timothyandClaude Opus 4.8
5c9f04fdec
feat(#253 PR2): optimistic-concurrency on schedule-items aggregate
Wire the frozen #253 ETag/If-Match/412 recipe onto ProgramSchedule /
schedule-items, keeping the PR#258 positional in-place reconcile intact.
Backend:
- ReplaceProgramScheduleItems command gains Option<int> ExpectedVersion;
ReplaceScheduleItemsRequest.ToCommand threads it.
- Handler: standalone CheckVersion Either AFTER validation (so 412 isn't
flattened to 422), unconditional Version++ before save, guarded save via
SaveChangesWithConcurrencyGuard, and 412 propagated without running the
post-save reload/enqueue.
- ProgramScheduleViewModel + Mapper carry Version.
- ScheduleController: GET /items emits ETag; PUT /items parses If-Match
(malformed -> 400), threads ExpectedVersion, re-queries for the new ETag,
and advertises 400/412.
- Sibling config-writers (Add/Delete item, Update schedule) bump Version.
Frontend:
- schedules.ts: getScheduleItemsWithMeta + replaceScheduleItems(ifMatch)
returning ResponseWithMeta.
- SchedulesScreen: etagRef threaded through the #242 dirty-guard (set from
load + every successful save); 412 opens a conflict ConfirmDialog whose
Reload discards the draft and re-runs loadItems.
Tests: handler concurrency suite (stale->412 no mutation + fill-group state
untouched, match/absent success+bump, no-op still bumps, racing save->412);
controller ETag/If-Match/412 cases; SchedulesScreen 412-conflict-dialog test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 18:36:25 +02:00
..
2026-07-11 18:36:25 +02:00
2026-07-07 22:43:47 +02:00
2026-07-03 23:32:22 +02:00
2026-07-11 16:51:59 +02:00
2026-07-11 11:44:58 +02:00
2026-07-06 18:43:39 +02:00
2026-07-09 22:05:51 +02:00
2026-07-07 19:22:39 +02:00
2026-07-07 19:51:00 +02:00
2026-07-11 15:47:42 +02:00
2026-07-07 14:06:26 +02:00
2026-07-10 23:40:47 +02:00
2026-07-09 07:52:14 +02:00
2026-07-03 21:14:42 +02:00
2026-07-07 22:43:47 +02:00
2026-07-11 15:47:42 +02:00
2026-07-10 23:40:47 +02:00
2026-07-11 12:38:08 +02:00
2026-07-07 22:43:47 +02:00
2026-07-11 15:43:10 +02:00
2026-07-11 00:10:20 +02:00
2026-06-30 18:49:35 +02:00
2026-07-07 22:43:47 +02:00
2026-07-03 23:02:58 +02:00
2026-07-07 22:43:47 +02:00
2026-07-08 18:18:32 +02:00
2026-07-09 23:56:50 +02:00
2026-07-10 23:34:23 +02:00
2026-07-11 16:52:05 +02:00
2026-07-08 18:30:06 +02:00
2026-07-07 09:22:51 +02:00
2026-06-30 18:49:35 +02:00
2026-07-11 18:36:25 +02:00
2025-11-04 16:08:07 -06:00
2026-07-09 23:56:50 +02:00
2026-07-07 22:43:47 +02:00
2025-09-04 16:36:26 +00:00
2026-07-07 09:22:51 +02:00
2026-07-07 22:43:47 +02:00
2026-06-30 18:49:35 +02:00
2026-07-07 18:31:17 +02:00
2026-07-07 15:26:01 +02:00
2026-07-11 13:44:47 +02:00
2025-10-13 11:25:56 -05:00
2026-07-07 14:06:26 +02:00