You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Browse filesBrowse the repository at this point in the historyBrowse files
authored and
Jammy2211
committed
fix: grade an absent rehearsal as STALE, not RED (#144)
Heart reported RED `release validation FAILED` against a Release Integrate
run that was entirely green: 661 passed, 0 failed, `failures: []`.
The tick's auto-ingest (`release_run.py`) folds the latest release-integrate
run's stage report on its own, with no merge base. That artifact is
integrate-only — `to_stage_report()` emits a single top-level `stage` — so it
can never carry a `rehearse` stage, and `_Accumulator.release_ready()` requires
one. `release_ready` was therefore false by construction on this path however
green the run was, and `readiness.py` mapped every false to the RED axis.
One boolean cannot answer both "did anything fail?" and "is the evidence
complete?". Add `validation_outcome: pass|fail|incomplete` and read that for
severity; `release_ready` is unchanged beside it for compatibility.
- validate.py: emit `validation_outcome`. `fail` covers any adverse evidence —
a failed stage, positive `totals.failed`/`totals.timeout`, or a non-empty
`failures` list — not just a stage literally marked "fail", which
`release_ready()` alone tested. Also apply `_norm_status` in `add_report`,
which previously copied stage statuses verbatim, so a merged report's
"failure" never normalised and read as not-a-failure.
- readiness.py: `fail` -> RED (unchanged), `incomplete` -> STALE. A report
predating the field falls back to `release_ready` and stays RED — fail closed.
The STALE text keeps the literal "release validation" so the Health Agent
classifier routes it to the `validate` capability instead of `unknown`.
- dashboard.py: render `incomplete` as WARN, not a FAIL row beside a stale
header; add `stale_reasons` to `to_dict()`, the surface the Health Agent and
mobile read, so a reason moved onto the stale axis is re-reported rather than
disappearing.
- release_run.py: stop calling every non-true ingest "FAILED", and re-fold once
when the stored report predates the discriminator — otherwise the RED would
persist behind the run-id cache. That migration is skipped when the report
already holds a `rehearse` stage, which this check's artifact cannot
reproduce, so a release drive's manual multi-stage ingest is never regressed.
Verified end-to-end against the real state and the real artifact, with no CI
re-dispatched: RED 45 / `release validation FAILED` -> YELLOW 70 /
`release validation incomplete: no rehearsal for current source`, red_reasons
empty. The remaining yellows are the pre-existing workspace-validation and
manifest-drift ones.
Regression tests pin the fail-closed cases a naive "no stage says fail" test
would have softened: positive failed counts, positive timeout counts, a
non-empty failures list, a "failure" status synonym arriving via `add_report`,
and a legacy report with no discriminator. The pre-existing
`test_validation_failed_is_red` is unmodified and serves as the control.
Diagnosis and plan were cross-reviewed by Codex, which rejected an earlier
readiness-only fix; its load-bearing findings were re-verified against the code
before being adopted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ENfMeYpRfAifDQ63uheMbV
0 commit comments