- Ya
- 12 Agosti 2026, 20:46 UTC
- Mwandishi
- Kamo
- Ahadi ya
- 7c55054
The wrong-path bug is fixed and the response shape is verified, but the CLASS of failure it belonged to was still open: if TimecardService renames `blocking`, drops it, or reshapes it into an object, the relay still answers 200, nothing throws, and the count silently reads 0 — "Every timecard is clear." from a body nobody could read. awaitingApproval is now `Integer = null` assigned ONCE at the end, instead of an int seeded at 0 and incremented in place. That inversion is the actual fix: the old shape relied on every failure path remembering to overwrite a confident zero, which is exactly how a 404 rendered as good news for as long as it did. A number is produced only on the path that genuinely walked a JSON array; a 200 in an unrecognised shape WARNs with the path and what actually arrived, leaves the figure unknown, and emits no APPROVAL rows. `get("blocking")` replaces `path("blocking")` because a MissingNode iterates as empty and cannot tell "absent" from "[]" — which is the whole distinction. An empty array is still an honest 0, pinned by its own test. The DTO also described two time windows with nothing saying so: the hours and member rows cover the requested from..to, the approval figure covers the CURRENT PAY PERIOD. awaitingApproval moves inside PeriodDto, beside the two dates that define it. With no pay period there is no window for the number to belong to and no close-readiness question was ever asked, so the figure is absent rather than a windowless 0 — the client already reads an absent period as "approvals unavailable", which is what it is. WIRE CHANGE: `awaitingApproval` -> **************** The client follows in a separate pass. Also: - Each attention kind is capped at 6 on its own. The exception loop measured against the COMBINED list, so the ledger ran to twelve exception rows whenever the approval queue happened to be empty: its length depended on a queue it was not showing. - A null member id is a JSON null, not the four-character string "null", which reached the ledger as a member NAMED null. The row stays — a blocking exception with no member is still blocking the close — and the client's own nameOf already renders a null id as an em dash. - trackedHeadcount is a count query, not a scan of every employment row. - The Spring-managed ObjectMapper, not a private one. - Deliberately NOT @Transactional, and the javadoc says why: no figure here is derived from two reads (the exception figures all come from one list), so a snapshot buys nothing observable, while a transaction would pin a Hikari connection across two blocking HTTP relays in the service with this cluster's documented pool-liveness incident. - daysRemaining is EXCLUSIVE: ChronoUnit.DAYS.between counts elapsed days, so a period ending today reads 0, not 1.