Keep a flow's runs when the flow goes
A run record is a record of what ran, so deleting a flow no longer sweeps its
history: `_forget_runs` is gone from the flow-delete path, and `DELETE /runs/{id}`
is the only thing that removes a run one at a time. The in-flight guard stays —
that is about work, not history.
`DELETE /runs?flow=` is the counterpart to the list's flow filter and what a
reseed needs, sharing the four statements with the single-run delete and
refusing the same way while a run of that flow is still going.
A run whose flow is gone reads as one: `useFlowInputs` reports the 404 rather
than an empty declaration set, so the run page says "flow deleted", explains it,
and disables Retry, which the route would refuse anyway.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CL9zvnnvcp1mvA8o7impxk
This commit is contained in:
@@ -774,6 +774,49 @@ def test_a_running_run_is_refused_rather_than_raced(client, superuser_token_head
|
||||
session.commit()
|
||||
|
||||
|
||||
def test_deleting_a_flows_runs_clears_that_flow_only(client, superuser_token_headers):
|
||||
"""The counterpart to the list's `flow` filter — what a reseed needs.
|
||||
|
||||
Deleting the flow itself leaves the history standing, so this is the tool
|
||||
that clears one. It refuses while a run of that flow is still going, for
|
||||
the same reason deleting a single run does.
|
||||
"""
|
||||
made = datetime.now(UTC)
|
||||
url = f"{settings.API_V1_STR}/runs"
|
||||
with Session(db_engine) as session:
|
||||
session.add(Run(id="bulk-1", flow="swept", status="ok", created_at=made))
|
||||
session.add(RunNode(run_id="bulk-1", node="swept.a", status="ok"))
|
||||
session.add(RunMetric(run_id="bulk-1", name="swept.loss", step=0, value=1.0))
|
||||
session.add(Run(id="bulk-2", flow="swept", status="running", created_at=made))
|
||||
session.add(Run(id="keep-1", flow="other", status="ok", created_at=made))
|
||||
session.commit()
|
||||
|
||||
busy = client.delete(url, params={"flow": "swept"}, headers=superuser_token_headers)
|
||||
assert busy.status_code == 409
|
||||
assert "bulk-2" in busy.json()["detail"]
|
||||
|
||||
with Session(db_engine) as session:
|
||||
session.get(Run, "bulk-2").status = "cancelled"
|
||||
session.commit()
|
||||
|
||||
answer = client.delete(
|
||||
url, params={"flow": "swept"}, headers=superuser_token_headers
|
||||
)
|
||||
assert answer.status_code == 200
|
||||
assert "2" in answer.json()["message"]
|
||||
|
||||
with Session(db_engine) as session:
|
||||
assert session.exec(select(Run).where(col(Run.flow) == "swept")).all() == []
|
||||
for table in (RunNode, RunMetric):
|
||||
assert (
|
||||
session.exec(select(table).where(col(table.run_id) == "bulk-1")).all()
|
||||
== []
|
||||
)
|
||||
assert session.get(Run, "keep-1") is not None
|
||||
session.delete(session.get(Run, "keep-1"))
|
||||
session.commit()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# A cached node's curve
|
||||
#
|
||||
@@ -847,7 +890,7 @@ def test_a_cached_node_answers_with_the_curve_it_was_restored_from(
|
||||
def test_a_curve_whose_run_is_gone_is_empty_rather_than_an_error(
|
||||
client, superuser_token_headers
|
||||
):
|
||||
"""Deleting a flow deletes its runs; what pointed at one is left holding it."""
|
||||
"""Delete the run a cache hit points at and the reusing run holds nothing."""
|
||||
with Session(db_engine) as session:
|
||||
session.delete(session.get(Run, "src-1"))
|
||||
session.commit()
|
||||
|
||||
Reference in New Issue
Block a user