fix(occupancy): PR #70 review follow-ups — live staff-baseline floor, operator communication, dwell edge cases #77

Closed
opened 2026-09-24 22:31:48 +00:00 by gabogg · 1 comment
Owner

Findings from the round-3 review of PR #70 (comment 1727). Includes one accepted P2 that needs a decision, hence needs-triage.

Accepted P2 (needs a decision)

  1. The live two-phase model still floors occupancy at the staff baseline (n_staff_target; with k = 1.35 and in = out = 1000 during retail hours the live tile shows 180), and cuts off the negative tail in the live tile and hourly series. The proportional model and its aggregates have a zero floor; ADR 0006 was corrected to say so. Decide: keep the staff baseline as a deliberate live-presentation choice (and label it), or move the live tile to the zero-floor model as #32 intended.

Behaviour (P3)

  1. /dwell/dayparts defaults to facility_now().date(): between midnight and the reset it returns the calendar day that hasn't opened, not the business day (use #69's business-day resolution).
  2. For N = 2–13 trusted cycles, the empirical variance clamp [0.00005, 0.005] can still draw a narrow band while the state is UNCALIBRATED. Consider keeping the guardrail-derived variance until the maturity threshold.
  3. occupancy_definition_cutover_at: dwell is recomputed, not stored, so past days are recomputed under the new definition. A "don't compare across the cutover" annotation would hide valid comparisons. Clarify the column's meaning (and ADR 0006's "rewrite historical aggregates: rejected").
  4. The dwell label reads 0 min · 10:00–22:00 before opening and on closed days.

Operator-facing (P3)

  1. No in-app explanation of the definition change: on deploy day, night occupancy drops from 12 (guard floor) to 0. occupancy_definition_cutover_at, dwell_kpi_label and is_uncalibrated aren't surfaced anywhere yet (the Statistics Deck, PR #20, is the intended consumer).
  2. No UI to set initial_exit_multiplier.

Code quality (P3)

  1. Feature envy: analytics_service.get_dwell_dayparts_async re-implements Little's Law dwell math that lives in the occupancy service (deferred since round 2).
  2. An f-string cache key packs the cycle, the start epoch and a closed flag (occupancy_service.py ~1699).

Acceptance criteria

  • Item 1 decided and implemented.
  • Items 2–9 fixed or explicitly declined.
  • Full suite green.

Related: #36, #32, ADR 0006, PR #70, PR #20, #73, #74.

Triage decisions (2026-09-24)

  • Item 1: move the live tile and hourly series to the zero-floor model (ADR 0006, #32). If staff should be visible, show them separately ("+N staff on site"), never inside the people-inside count.
  • Item 2 is also required by #88; do it first.
  • Items 3–9: fix or explicitly decline, as listed.
> Findings from the round-3 review of PR #70 (comment 1727). Includes one **accepted P2** that needs a decision, hence `needs-triage`. ## Accepted P2 (needs a decision) 1. **The live two-phase model still floors occupancy at the staff baseline** (`n_staff_target`; with `k = 1.35` and in = out = 1000 during retail hours the live tile shows **180**), and cuts off the negative tail in the live tile and hourly series. The proportional model and its aggregates have a zero floor; ADR 0006 was corrected to say so. **Decide:** keep the staff baseline as a deliberate live-presentation choice (and label it), or move the live tile to the zero-floor model as #32 intended. ## Behaviour (P3) 2. **`/dwell/dayparts` defaults to `facility_now().date()`**: between midnight and the reset it returns the calendar day that hasn't opened, not the business day (use #69's business-day resolution). 3. **For N = 2–13 trusted cycles**, the empirical variance clamp `[0.00005, 0.005]` can still draw a narrow band while the state is `UNCALIBRATED`. Consider keeping the guardrail-derived variance until the maturity threshold. 4. **`occupancy_definition_cutover_at`:** dwell is recomputed, not stored, so past days are recomputed under the new definition. A "don't compare across the cutover" annotation would hide valid comparisons. Clarify the column's meaning (and ADR 0006's "rewrite historical aggregates: rejected"). 5. **The dwell label reads `0 min · 10:00–22:00`** before opening and on closed days. ## Operator-facing (P3) 6. **No in-app explanation** of the definition change: on deploy day, night occupancy drops from 12 (guard floor) to 0. `occupancy_definition_cutover_at`, `dwell_kpi_label` and `is_uncalibrated` aren't surfaced anywhere yet (the Statistics Deck, PR #20, is the intended consumer). 7. **No UI to set `initial_exit_multiplier`.** ## Code quality (P3) 8. **Feature envy:** `analytics_service.get_dwell_dayparts_async` re-implements Little's Law dwell math that lives in the occupancy service (deferred since round 2). 9. **An f-string cache key** packs the cycle, the start epoch and a closed flag (`occupancy_service.py` ~1699). ## Acceptance criteria - [ ] Item 1 decided and implemented. - [ ] Items 2–9 fixed or explicitly declined. - [ ] Full suite green. Related: #36, #32, ADR 0006, PR #70, PR #20, #73, #74. ## Triage decisions (2026-09-24) - **Item 1:** move the live tile and hourly series to the zero-floor model (ADR 0006, #32). If staff should be visible, show them separately ("+N staff on site"), never inside the people-inside count. - Item 2 is also required by #88; do it first. - Items 3–9: fix or explicitly decline, as listed.
Author
Owner

Note (2026-09-24): PR #20 is closed because the statistics deck is being redesigned from scratch. References to PR #20 / RFC-ARCH-2026-004 here now mean the future deck. The deck-side items stay valid, but the redesign will decide where they are shown. The backend contracts a deck should consume are listed in PR #20, comment 1835.

Note (2026-09-24): **PR #20 is closed** because the statistics deck is being redesigned from scratch. References to PR #20 / RFC-ARCH-2026-004 here now mean *the future deck*. The deck-side items stay valid, but the redesign will decide where they are shown. The backend contracts a deck should consume are listed in PR #20, comment 1835.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gabogg/hikcentral#77
No description provided.