- **P0: None found. P1 findings are present.** - **P1 — The 5-event/70% gate can be satisfied by repeatedly counting the same follow-up measurement, and then exposes summaries based on only one observation for an outcome.** Each event independently selects the first point in its window and appends it without preventing reuse; `result_value_count` is pooled across both outcomes, while eligibility is global. Consequently, five same-day events plus one next-day point per metric produce `event_count=5`, `coverage=100%`, `result_value_count=10`, and `evaluable=true`. Likewise, five values for one outcome plus only one for the second still render a `follow_median` for the second. This defeats the intended minimum-data safeguard and labels a single value as a “typical follow-up value.” `scripts/health/dashboard_v5/observation_engine.py:403-426, 479-503, 530-550, 562-579` `scripts/health/assets/health-assets/dashboard-v5-observations.js:160-166` - **P1 — Completely unmapped histamine days disappear instead of preserving missingness or blocking the summary.** `_histamine_events` drops a nutrition day when `mapped <= 0`; `evaluate_plan` only checks `review_status` on the returned events. Thus, an all-unknown nutrition day is invisible, while five other fully mapped days can still make the plan evaluable. This is less conservative than the partially mapped case and silently excludes the strongest missingness condition. `scripts/health/dashboard_v5/comparison_contract.py:247-257, 265-280` `scripts/health/dashboard_v5/observation_engine.py:537-550` - **P1 — Observation creation and result recalculation are not idempotent across worker replay.** Observation payloads contain no idempotency key; a create action with an empty `observation_id` generates a fresh ID on every application. The worker commits before the queue file is removed, so a crash after commit and before unlink causes the same queue file to create another plan on restart. `observation_result_snapshot` replay similarly inserts another immutable result version. `scripts/health/dashboard_v5/observation_contract.py:151-205` `scripts/health/health_dashboard_action_worker.py:1485-1511, 1580-1588, 2156-2168` - **P1 — Legacy V5 observation plans no longer render in the replacement UI.** The API deliberately falls back to `evaluate_observation` for older influence/outcome configurations, whose response contains `phases` but none of the new event-centric fields. The frontend nevertheless unconditionally reads `event_count`, `limitations`, `comparison`, and `observations.length`; opening such a persisted plan throws during rendering. This breaks backward compatibility despite retaining the legacy backend evaluator and requiring no migration. `scripts/health/dashboard_v5/read_api.py:4326-4336` `scripts/health/dashboard_v5/observation_engine.py:344-350` `scripts/health/assets/health-assets/dashboard-v5-observations.js:149-191` - **P1 — Follow-ups after the plan’s final event are never loaded.** Outcome series are requested only through `end_date`, although non-symptom outcomes are associated starting on the following day. An event on `end_date` is retained, but its following-night/day measurement at `end_date + 1` is necessarily reported missing. The comparison handoff uses the same unextended range, so it also cannot show that follow-up. `scripts/health/dashboard_v5/observation_engine.py:452-468, 481-501, 607-612, 628-633` `scripts/health/dashboard_v5/comparison_contract.py:504-516, 544-560` - **P2 — Histamine mapping coverage is averaged per day rather than calculated from documented entries.** The source events provide `mapped_entries` and `documented_entries`, but the plan averages daily percentages. A 1/1 day plus a 1/100 day is displayed as roughly 50.5% rather than roughly 2%, materially overstating mapping completeness. `scripts/health/dashboard_v5/comparison_contract.py:250-279` `scripts/health/dashboard_v5/observation_engine.py:537-541, 622-625` - **P2 — The Today endpoint is now permanently silent rather than conditionally integrated.** It returns `task: null` without examining active plans, dates, phases, or existing documentation. Therefore, even an unambiguous missing planned entry can never generate the permitted Today hint. `scripts/health/dashboard_v5/read_api.py:4266-4272` - **P2 — Existing `/api/v1/observations*` response envelopes were replaced rather than extended.** The previous numeric `version: 1` field was removed from templates, list, and active-today responses and replaced by `contract_version`. Clients using the existing versioned endpoints and numeric envelope field receive a breaking response-shape change; backward compatibility would require retaining `version` additively. `scripts/health/dashboard_v5/read_api.py:4248-4272`