Let a sweep run more than four at a time, and name the run a failure was in
Docs / docs (push) Successful in 29s
Playwright Tests / test-playwright (1, 2) (push) Successful in 3m33s
Playwright Tests / test-playwright (2, 2) (push) Successful in 2m3s
pre-commit / pre-commit (push) Failing after 3m9s
Test Backend / test-backend (push) Successful in 2m46s
Compose Smoke Test / test-compose (push) Successful in 39s
Playwright Tests / merge-reports (push) Successful in 1m47s

Concurrent runs sat at 4 whatever FLOW_MAX_CASCADES said: that setting bounds
cascades, and the run drivers read a hardcoded MAX_PARALLEL nobody could reach.
FLOW_MAX_RUNS is the knob they read now, --max-runs/--max-cascades/--max-workers
are the same three as flags on serve, and the engine says which numbers it
started with — which is the only way to tell that a settings file was read.

Events keep the run they happened in. The payload always carried it and the
persist path dropped it, so reading one run's failures meant filtering the
engine-wide list; a batch run's id reaches those events now too, since a run
has no journaled item to name itself by.

Also: a provisioner's 0 means "no deadline" rather than "cancel on the next
reconcile", and a command that reaches no engine says how to start one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sbYeYaVgYQqm1sbx7wPdL
This commit is contained in:
2026-08-27 14:17:51 +02:00
co-authored by Claude Opus 5
parent c3675688c8
commit 37a7df9d24
23 changed files with 288 additions and 18 deletions
+7 -2
View File
@@ -59,7 +59,12 @@ def _fail(message: str) -> int:
return 1
def _unreachable(exc: Exception, note: str = "") -> int:
#: What to try when nothing answered at all. By far the commonest reason is
#: that no engine is running, and the message said only that it was not.
NO_ENGINE = "Is one running? `fluksio serve` starts one."
def _unreachable(exc: Exception, note: str = NO_ENGINE) -> int:
"""The engine did not answer. Say so as a sentence, not a traceback."""
return _fail(
f"engine not answering ({type(exc).__name__}: {exc})"
@@ -514,7 +519,7 @@ def cmd_run(args: argparse.Namespace, rest: list[str]) -> int:
# The run is the engine's, not this command's: it carries on, and its
# id is how to find it again.
return _unreachable(
exc, f"Run {handle.id} is still on the engine." if handle else ""
exc, f"Run {handle.id} is still on the engine." if handle else NO_ENGINE
)
+7 -3
View File
@@ -299,18 +299,22 @@ class Client:
kind: str = "failure",
limit: int = 10,
flow: str = "",
run: str = "",
since: Any = None,
until: Any = None,
) -> list[dict[str, Any]]:
"""What went wrong, or who changed what. Newest first.
Engine-wide, and narrowed by flow or by time. What *one run* did is a
question about that run: :attr:`RunHandle.failures` answers it from the
run's own node rows, which carry the traceback anyway.
Engine-wide, and narrowed by flow, by run or by time. ``run`` takes a
run id and answers what the engine recorded during it. For the failure
that ended a run, :attr:`RunHandle.failures` is still the shorter road:
it reads the run's own node rows, which carry the traceback too.
"""
query: dict[str, Any] = {"kind": kind, "limit": limit}
if flow:
query["flow"] = flow
if run:
query["run"] = run
for name, value in (("since", since), ("until", until)):
if value is not None:
query[name] = (