Skip to content

Fix: end-to-end optimistic concurrency for water corrections (codex review of #76) - #77

Merged
mforce merged 1 commit into
mainfrom
fix/water-concurrency-contract
Jul 18, 2026
Merged

mforce merged 1 commit into
mainfrom
fix/water-concurrency-contract

Conversation

@mforce

@mforce mforce commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Follow-up to #76 (merged before codex's review landed — owner asked for a new PR). Full review table below.

The P1

The EF Version token only protected the update handler's internal read→save window. A user's actual read→edit→save cycle had no protection: load record, someone else corrects it, save — the second save re-read the fresh row and silently overwrote the first correction. The parallel-update test only passed by timing luck (codex's run had both PUTs return 204 — flaky).

Fix — end-to-end optimistic contract: version on every list row → PUT requires the base version → mismatch is a deterministic 409 ("changed since you loaded it") → EF token backstops the residual in-request race. The SPA threads the loaded version through edit mode and shows a human conflict message. Race test now sends the same base version from both writers: deterministically one 204, one 409, no timing dependence.

Review table (codex, 8 findings)

Sev Finding Resolution
P1 No client base version → silent stale-form overwrite + flaky race test End-to-end version contract as above; deterministic tests
P2 Update lacked the lifecycle gate — archived flocks' records stayed mutable Gate added; archived history is read-only (test)
P2 Meter readings unvalidated for precision/range → persisted Quantity ≠ End − Start possible after numeric(18,3) rounding 3-decimal + range validation on both readings (test)
P2 Edit mode corrupted subsequent capture state (form left aimed at historical/archived flock) resetForm restores all capture defaults
P3 Numeric enum strings ("1") accepted as source Named values only (test)
P2 Idempotency key rotates only after refresh → edited payload after refresh-failure replays original Declined here: the established app-wide key contract (all screens); changing it is a cross-cutting slice. Can file.
P2 Migration omits farm_id/house_id/created_by (spec §12.5) Declined here: identical to FeedUsage (reviewed twice) — houses are phantom until Phase 2, creator attribution arrives with the audit-log slice.
— Tenant filtering/stamping, foreign-flock rejection, migration ops verified correct —

187 tests green. Live-verified: correct-with-base 204 → stale-base 409 → row at version 1; numeric enum 400.

…eview of #76)

codex P1: the EF Version token only guarded the update handler's own
read->save window — with no client-supplied base version, a stale form
silently overwrote an intervening correction, and the parallel-update test
was timing-flaky (codex's run had both PUTs return 204). The contract is now
end-to-end: Version rides on every list row, PUT requires the base version,
a mismatch is a deterministic 409, and the EF token backstops the residual
in-request window. Race test rewritten to send the same base version from
both writers — deterministic, no timing dependence — plus a sequential
stale-form 409 test.

Also from the review:
- P2: update now enforces the same flock lifecycle gate as recording —
  an archived flock's water history is read-only (test).
- P2: meter readings validated to 3 decimal places and a sane range; extra
  precision could round into Quantity != MeterEnd - MeterStart after
  numeric(18,3) persistence (test).
- P2: cancelling/finishing an edit fully restores capture defaults
  (flock/date/source/unit) — it previously left the form aimed at the
  historical record's flock, possibly archived and absent from the picker.
- P3: water source accepts named values only — numeric enum strings like
  "1" are refused (test).

Declined as established app-wide conventions (noted on the PR): the
idempotency-key rotate-after-refresh contract, and farm_id/house_id/
created_by columns (houses phantom until Phase 2; creator attribution
arrives with the audit-log slice) — both identical to FeedUsage, reviewed
twice there.

187 tests green. Live-verified: correct-with-base 204, stale-base 409,
numeric enum 400, version increments on the row.
@mforce
mforce merged commit 78246f6 into main Jul 18, 2026
3 checks passed
@mforce
mforce deleted the fix/water-concurrency-contract branch July 18, 2026 19:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant