Record batch runs beside cascades so Home lists them

A batch run opens no cascade, and FlowRun was written only from
cascade_started — so `fluksio run` showed on /runs and in `fluksio status` and
was simply absent from Home. The collector now folds the run_started and
run_finished events RunService already published. Such a record is exempt from
the staleness sweep in both places: a training step of an hour is a normal one,
and only run_finished ends it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 15:54:15 +02:00
co-authored by Claude Opus 5
parent 6bc71ddaf3
commit 121cb2e8f0
3 changed files with 89 additions and 12 deletions
+46 -2
View File
@@ -226,11 +226,55 @@ def test_a_traceback_no_failure_ever_claims_is_dropped() -> None:
assert collector._tracebacks == {}
def test_a_batch_run_is_recorded_beside_the_cascades(db: Session) -> None:
"""`fluksio run` showed on /runs and nowhere on Home.
It opens no cascade, so the collector saw its nodes and its failures with
no record to fold them into. The start is aged past the staleness cutoff on
purpose: a training step of an hour is a normal one, and only
`run_finished` may end a batch run.
"""
collector = MetricsCollector(EventBus())
started = time.time() - RUN_STALE_S - 60
collector.handle(
{"type": "run_started", "run": "run-batch", "flow": FLOW, "ts": started}
)
collector.handle(
{
"type": "node_executed",
"flow": FLOW,
"node": NODE,
"run": "run-batch",
"duration_ms": 5.0,
"ts": started,
}
)
asyncio.run(collector.flush())
row = db.exec(select(FlowRun).where(FlowRun.id == "run-batch")).one()
assert (row.status, row.source, row.nodes) == ("running", "run", 1)
collector.handle(
{
"type": "run_finished",
"run": "run-batch",
"flow": FLOW,
"status": "ok",
"ts": started + 30,
}
)
asyncio.run(collector.flush())
db.expire_all()
row = db.exec(select(FlowRun).where(FlowRun.id == "run-batch")).one()
assert (row.status, row.duration_ms) == ("ok", 30_000.0)
def test_a_failure_keeps_the_run_it_happened_in(db: Session) -> None:
"""The payload always carried it; the row used to drop it.
A batch run publishes no `cascade_started`, so there is no `FlowRun` beside
this — which is exactly the case that had no way of being asked about.
Nothing told this collector the run had started, so there is no `FlowRun`
beside it — the failure still has to name what it happened in.
"""
collector = MetricsCollector(EventBus())
ts = datetime.now(UTC).replace(second=0, microsecond=0).timestamp()