Skip to content

Production report cannot distinguish a day with no entry from a day that produced zero eggs #780

Description

@mforce

Split out of #777 (decision A), which could not fix this client-side.

What

ProductionDay cannot say whether a day was entered. ReportQueries.cs:157 walks

for (var d = from; d <= to; d = d.AddDays(1))

and emits a row for every calendar day in the window, with total = row?.Total ?? 0. FromCounts and Deaths default to 0 the same way, and HenDayPct is about birds rather than entries. So a day nobody recorded and a day that genuinely produced zero eggs arrive at the SPA identical, and no client can tell them apart.

The series being dense is correct and worth keeping — it is what makes the Dashboard's day strip a true calendar. The gap is only that a zero carries no provenance.

Why it matters now

#777 replaced the Dashboard's 14-day line with a strip of day slots, partly because a line drew a segment straight through unrecorded days and rendered them as a plateau of real production. The strip is honest about the shape but still asserts those days produced zero eggs, because that is all the API says. Two things are blocked on this:

  • the strip cannot distinguish "no entry yet" from "a zero day", though entryFor in web/src/lib/dashboard.ts already treats that as a distinct state for the capture tiles and renders it as an alarm;
  • the strip deliberately draws no average reference line. Averaging the recorded days asserts the flat days are absences; averaging the whole window asserts they are real zeros. A reference line that quietly picks a side is worse than no line, so Dashboard: Recent sales rows do not form columns, and the trend and stock charts are hard to read #777 shipped without one.

Shape of the fix

A presence signal on ProductionDay — an entry count for the day, or a flag. An entry count is probably better: it also distinguishes "some houses recorded" from "every house recorded", which is the same question the Today panel answers for the current day.

Decide before writing code:

  • The report record is a public contract. ProductionDay reaches the Reports page, the CSV exports and anything else reading /reports/production. Adding a field changes export columns; check every caller per AGENTS.md · A write-contract change must update its non-CI callers (Daily entry: require grading to reconcile sellable eggs before submit #394), and say what the export does with it.
  • Voided entries vacate their day (F17 follow-up: voided daily entry permanently blocks re-entering that day #82). A day whose only entries are voided is "no entry", not "entered with zero". entryFor already encodes that rule for the SPA; the server figure has to agree with it or the two surfaces will disagree about the same day.
  • Do not re-derive it in the SPA. The lib/dashboard.ts header note holds: pages never sum report rows, so this is a server figure or it is nothing.

Out of scope

No change to what the report measures, and no change to the strip's appearance beyond what the new signal makes possible.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:apiAPI/endpoint layerbugSomething isn't workingseverity:p3Defect: degraded or partial behaviour

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions