決定(2026-06-19 事故分析で確定)
PATCH /api/tasks/{id}/status を 楽観ロック(If-Match)対象外とする。現状バックエンドが If-Match を required で要求するが OpenAPI 未定義・フロント未送出のため常に 400。本 Issue で If-Match を撤去して 400 を解消する。
根拠(事故分析)
/status は単一フィールドの絶対値 SET。ADR-0012 が防ぐ「多フィールド PATCH で他人の未編集列を上書き」型 lost update が構造的に起きない。
TaskStatus はステートマシン無しの enum、completedAt は status から決定的に導出され torn state にならない。認可は UseCase が同一トランザクションで再読込して判定 → ロック以外で整合が守られている。
version は行単位カウンタのため、付けると無関係なフィールド編集でも status 変更が 412。高頻度操作で誤検知摩擦のみ大きく、正しさの利得はゼロ。
- status の同時変更は last-write-wins(最後の人間の判断が残る)で自然・低リスク。
判定基準と決定マトリクスは ADR-0012 の amendment に記録する。
やること
ADR-0012 に追記する基準
楽観ロック(If-Match)は「呼び出し側が観測していない変更を上書きしうる write」にのみ適用する。判定軸 = 多フィールドを束ねる write / read-modify-write / 上書きの実害が高いか。単一フィールド絶対値 SET で実害が低く高頻度な endpoint(/status)は対象外。低頻度・高実害な endpoint(/visibility)は付与する(別 Issue)。
受入条件
関連
#663(blocks)/ ADR-0012 / /visibility 付与は別 Issue
決定(2026-06-19 事故分析で確定)
PATCH /api/tasks/{id}/statusを 楽観ロック(If-Match)対象外とする。現状バックエンドがIf-Matchを required で要求するが OpenAPI 未定義・フロント未送出のため常に 400。本 Issue で If-Match を撤去して 400 を解消する。根拠(事故分析)
/statusは単一フィールドの絶対値 SET。ADR-0012 が防ぐ「多フィールド PATCH で他人の未編集列を上書き」型 lost update が構造的に起きない。TaskStatusはステートマシン無しの enum、completedAtは status から決定的に導出され torn state にならない。認可は UseCase が同一トランザクションで再読込して判定 → ロック以外で整合が守られている。versionは行単位カウンタのため、付けると無関係なフィールド編集でも status 変更が 412。高頻度操作で誤検知摩擦のみ大きく、正しさの利得はゼロ。判定基準と決定マトリクスは ADR-0012 の amendment に記録する。
やること
TaskController.changeStatusから@RequestHeader(IF_MATCH)を撤去ChangeTaskStatusUseCase.executeからifMatchVersion引数と version 一致チェック(PreconditionFailedException)を撤去web/js/tasks.js:86の「If-Match 対象外」コメントと status の 412 ハンドリングを整理(status では 412 を期待しない)/status: If-Match/412 が無いことを確認し、「楽観ロック対象外」を明示記述ADR-0012 に追記する基準
受入条件
/statusが If-Match 無しで 200 を返す(統合 IT)関連
#663(blocks)/ ADR-0012 /
/visibility付与は別 Issue