Skip to content

Commit e1f577c

Browse files
authored
refactor(stage-gate): use format_gate_rates instead of duplicating it (#1432)
Third review round on #1340. `format_gate_rates` had no production caller — only its own unit tests — while `evolution_metrics.format_health` rendered the same data inline in a different shape. Two renderings of one thing, one of them dead, in a repo whose integration skill runs an explicit dead-code check on every PR. `format_gate_rates` now emits exactly what the health line body needs and `format_health` calls it, so the shape lives next to the data that defines it. Lazily imported for the same reason `compute_health` imports the module lazily: the evolution package is not always available, and the health line must still render without it. The output is byte-identical to before — verified end to end rather than by reading: healthy ...effort_budget=3.0 gate[local_triage]=refine 50%/restart 0% (n=4) | healthy -> check_health: 0 alerts mis-tuned ...gate[local_triage]=refine 0%/restart 75% (n=4) | HIGH_STAGE_RESTART_RATE:local_triage=75% -> check_health: 1 alert Added a test asserting the rendered segment contains no `|`, since the health line's flags must remain the last pipe-separated segment for `evolution_watchdog.check_health` — that constraint was previously only implied by a comment. 52 passing in test_stage_gate.py + test_evolution_metrics.py; ruff clean. Refs #1340
1 parent 15858f8 commit e1f577c

3 files changed

Lines changed: 38 additions & 14 deletions

File tree

evolution/lib/stage_gate.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,21 @@ def gate_flags(
290290

291291

292292
def format_gate_rates(rates: dict[str, dict[str, Any]]) -> str:
293-
"""One-line summary per boundary for the evolution-health sidecar."""
293+
"""Per-boundary rates as they appear in the evolution-health line body.
294+
295+
Emitted WITHOUT a leading marker and with no trailing ``|``: the health
296+
line's flags must stay the last segment after the final pipe, because
297+
``evolution_watchdog.check_health`` keys on the line ending in
298+
``| healthy``. This belongs beside ``effort_budget`` in the body, under the
299+
same constraint.
300+
301+
Returns ``""`` for an empty mapping so the caller can concatenate it
302+
unconditionally.
303+
"""
294304
if not rates:
295305
return ""
296-
parts = [
297-
f"{stage}(n={b['total']} refine={b['stage_refine_rate']:.0%} "
298-
f"restart={b['stage_restart_rate']:.0%})"
306+
return " ".join(
307+
f"gate[{stage}]=refine {b['stage_refine_rate']:.0%}/"
308+
f"restart {b['stage_restart_rate']:.0%} (n={b['total']})"
299309
for stage, b in sorted(rates.items())
300-
]
301-
return "[stage-gate] " + " ".join(parts)
310+
)

evolution/tests/test_stage_gate.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,5 +224,15 @@ def test_format_names_each_boundary(self):
224224
from evolution.lib.stage_gate import compute_gate_rates, format_gate_rates
225225

226226
out = format_gate_rates(compute_gate_rates(self._recs(ACCEPT, REFINE)))
227-
assert "[stage-gate]" in out
228-
assert "local_triage" in out
227+
assert "gate[local_triage]" in out
228+
assert "refine 50%" in out
229+
assert "restart 0%" in out
230+
assert "(n=2)" in out
231+
232+
def test_format_carries_no_pipe(self):
233+
"""The health line's flags must remain the last `|`-separated segment —
234+
`evolution_watchdog.check_health` keys on it ending in `| healthy`."""
235+
from evolution.lib.stage_gate import compute_gate_rates, format_gate_rates
236+
237+
out = format_gate_rates(compute_gate_rates(self._recs(ACCEPT, RESTART)))
238+
assert "|" not in out

scripts/evolution_metrics.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,17 @@ def format_health(h: Dict[str, Any]) -> str:
209209
gate = ""
210210
rates = h.get("stage_gate_rates") or {}
211211
if rates:
212-
# Same rule as effort_budget: body only, never the tail (#1340).
213-
gate = " " + " ".join(
214-
f"gate[{stage}]=refine {b['stage_refine_rate']:.0%}/"
215-
f"restart {b['stage_restart_rate']:.0%} (n={b['total']})"
216-
for stage, b in sorted(rates.items())
217-
)
212+
# Rendered by stage_gate's own formatter so the shape lives next to the
213+
# data that defines it. Same rule as effort_budget: body, never the
214+
# tail (#1340). Lazily imported for the same reason compute_health
215+
# imports it lazily — the evolution package is not always available.
216+
try:
217+
from evolution.lib.stage_gate import format_gate_rates
218+
219+
rendered = format_gate_rates(rates)
220+
gate = f" {rendered}" if rendered else ""
221+
except ImportError:
222+
gate = ""
218223
return (
219224
f"[evolution-metrics] {h['cycles_active']}/{h['cycles_total']} active cycles: "
220225
f"success={_pct(h['cycle_success_rate'])} "

0 commit comments

Comments
 (0)