Skip to content

F16: Water usage tracking (#67) - #76

Merged
mforce merged 3 commits into
mainfrom
feat/f16-water-usage
Jul 18, 2026
Merged

mforce merged 3 commits into
mainfrom
feat/f16-water-usage

Conversation

@mforce

@mforce mforce commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Closes #67 (epic #14, Phase 1.1). Spec §12.5.

Design decision (was open on the issue)

Water has no lots/ledger behind it — nothing downstream depends on a record — so records are editable (Version-token guarded updates), unlike feed usage's compensating-row model. Flock and date are identity-like and stay fixed; everything else is correctable. Per the #73 decisions, water corrections join the admin-gated surface when roles land.

What

  • WaterUsage — flock, date, quantity (L/gal), source (Well/Municipal/Tank/Other), optional meter start/end: quantity may be omitted and derives from the meter delta; if both are given they must agree. CreatedAtUtc for same-day ordering; DailyEntryId reserved with a real FK.
  • Lifecycle gate identical to production and feed: depleted flocks accept backfill up to depletion, archived never; future dates refused.
  • API — POST / PUT /{id} / GET on /api/v1/water-usage (flock/date filters, paged).
  • SPA — Water page: capture form with a meters toggle, depleted flocks labeled backfill-only (consistent with PR Fixes from the PR #74 accuracy reviews: depleted backfill, draft-grade preservation, help/glossary corrections, a11y #75's daily-entry fix), records list with filters and inline correct.

Tests (8 new, 186 green)

  • Domain: meter pair/ordering/delta-consistency rules, unit whitelist, update keeps flock/date + Version bump.
  • Integration: direct + meter-derived recording (85.25 from 1000→1085.25); guard matrix (qty≠delta 400, meters-alone-backwards 400, unknown source/unit 400, future/archived 422); correction round-trip; parallel-update Version race — one 204, one 409, delta 1, no blended payload.

Runtime verified

API scenario (meter derivation, correction) + Playwright UI (direct record, metered record showing 500 → 562.5, inline correction) — zero console errors.

Docs

Help section + GLOSSARY entry land as a follow-up commit on this PR once #75 merges — both PRs edit those files, and #75 carries the accuracy-review corrections they must build on (docs-sync rule).

mforce added 3 commits July 18, 2026 11:42
Spec §12.5. Unlike feed there is no inventory behind water, so records are
EDITABLE (Version token) instead of corrected via compensating rows — the
design decision #67 left open, resolved and documented in the domain comment;
corrections become admin-gated with #73.

- WaterUsage: flock + date (fixed after creation — identity-like), quantity
  in L/gal, source (Well/Municipal/Tank/Other), optional meter start/end.
  Quantity may be omitted when meters are given (derived as the delta);
  when both are present they must agree. CreatedAtUtc for same-day ordering;
  DailyEntryId reserved with a real FK.
- Record/Update handlers: same flock lifecycle gate as production and feed
  (CanRecordProductionOn — depleted backfill allowed, archived never),
  future dates refused. Update keeps flock/date fixed; parallel corrections
  race on the Version token (loser 409).
- API: POST/PUT/GET /api/v1/water-usage (flock/date filters, paged).
- SPA: Water page — capture form with a meters toggle, depleted flocks
  labeled backfill-only, record list with filters and inline "correct".

Tests: 8 new (domain meter rules/unit guard/update semantics; integration
direct + meter-derived recording, guard matrix incl. qty-vs-delta mismatch,
archived/future 422s, update keeps flock/date, parallel-update Version race
with no blended payload). 186 green.

Runtime-verified: API scenario (derive 85.25 from meters, correction round
trip) + Playwright UI flow (direct + metered records, inline correction).

Help/GLOSSARY sections follow in this PR once #75 (which edits the same
files) merges — docs-sync rule.

Closes #67
Docs-sync rule: Help page gains a Water section (direct vs meter-derived
amounts, in-place correction, lifecycle rule), a "Wrong water record" row in
the corrections map, and a glossary entry; GLOSSARY.md gains the Water usage
entry noting it is the one editable record type (no inventory behind it).
Written against #75's corrected text — held until that merged.

local-pi review (scoped 96k run, 0 P1 / 2 P2 / 2 P3, all applied):
- Equal meter readings now refuse with a meter-specific message instead of
  surfacing as a confusing "quantity must be positive" (domain + validators;
  equal-readings domain test added).
- Source validator gains NotEmpty for a clear missing-field message.
- Meter-pair rule's When guard rewritten to state its intent.
- SPA maps 409 to a human concurrent-edit message.
@mforce
mforce merged commit 89ba9eb into main Jul 18, 2026
3 checks passed
@mforce
mforce deleted the feat/f16-water-usage branch July 18, 2026 18:49
@mforce

mforce commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

codex review landed post-merge: 1 P1 (the Version token didn't cover the user's read→edit→save cycle — a stale form could silently overwrite an intervening correction, and the race test was timing-flaky) plus 4 applied P2/P3s and 2 declined-as-convention items. All addressed in PR #77 (owner asked for a follow-up PR): end-to-end version contract (list rows carry version, PUT requires it, mismatch → deterministic 409), archived-flock records read-only, meter precision/range validation, edit-mode state restoration, named-enum-only sources. Full table on #77.

mforce added a commit that referenced this pull request Jul 18, 2026
…eview of #76) (#77)

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.

Co-authored-by: mforce <>
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.

F16: Water usage tracking

1 participant