New run: start a run from the app, on the working copy
The site promises simulated inputs and mocked sensor values, and nothing in the app was that. A run already is: the values are the caller's, the state is the run's own namespace, and nothing it computes reaches the live flow. What was missing was a screen to do it from, and the draft flag being honoured. `/runs/new` is a flow, a field per declared input, a seed and Run; `/runs` stays the log. A comma-separated list in a number field expands into the grid `fluksio sweep --param` builds and goes to the sweep route, so launching one no longer needs a terminal. Only numbers split: a comma in a string is content, and one in JSON is syntax. `RunCreate.draft` was validated at submit and dropped before the run executed, so "try the working copy" ran the published one. `Run.draft` is a column now, the driver reads the same copy the submit checked, and a retry carries it. `FlowSummary.mode` came with it so the rail can say which flows are batch before one is picked. Also here: a Retry button on a finished run, which the route has always had and the UI never did, and parameter cells truncated to their column with the full value on hover. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013TTfoK82awm8wvxXhHz3XF
This commit is contained in:
@@ -906,6 +906,7 @@ class RunService:
|
||||
group_id=group_id,
|
||||
cause=cause,
|
||||
no_cache=no_cache,
|
||||
draft=draft,
|
||||
status="queued",
|
||||
labels=required_labels(flow),
|
||||
needs=required_resources(flow),
|
||||
@@ -973,6 +974,10 @@ class RunService:
|
||||
inputs, the same group, so a sweep is completed rather than repeated.
|
||||
The stage cache is what makes it cheap — the nodes that finished are
|
||||
restored rather than run again.
|
||||
|
||||
A run of the working copy retries as one, which means whatever the
|
||||
draft is now rather than what it was — there is no older draft to go
|
||||
back to, and the point of retrying a draft run is the code on disk.
|
||||
"""
|
||||
with Session(db_engine) as session:
|
||||
run = session.get(Run, run_id)
|
||||
@@ -989,6 +994,7 @@ class RunService:
|
||||
cause="retry",
|
||||
actor=actor,
|
||||
no_cache=source.no_cache,
|
||||
draft=source.draft,
|
||||
parent_id=source.id,
|
||||
)
|
||||
|
||||
@@ -1203,17 +1209,20 @@ class RunService:
|
||||
|
||||
sink = MetricSink(run_id)
|
||||
try:
|
||||
flow = self.controller.store.read_flow(run.flow)
|
||||
flow = self.controller.store.read_flow(run.flow, draft=run.draft)
|
||||
# Read again, now that it is this run's turn: a sweep queues every
|
||||
# run at once, and the code on disk is free to move in the hours
|
||||
# before the last of them starts. What the record must name is the
|
||||
# state that ran, not the state that was submitted.
|
||||
# state that ran, not the state that was submitted. A draft that
|
||||
# was published meanwhile reads as the published copy, which is
|
||||
# the same document under a different name.
|
||||
digests = node_digests(flow)
|
||||
run.code_digest = self._restamp(run, run_digest(flow))
|
||||
state = self._state_factory(f"{RUN_NAMESPACE}:{run_id}")
|
||||
pipeline = self.controller.build_run_pipeline(
|
||||
flow,
|
||||
state=state,
|
||||
draft=run.draft,
|
||||
observer=observe,
|
||||
emission_observer=sink.handle,
|
||||
run=RunContext(
|
||||
|
||||
@@ -355,6 +355,8 @@ class MessageHistory(BaseModel):
|
||||
class FlowSummary(BaseModel):
|
||||
name: str
|
||||
title: str = ""
|
||||
#: Whether running it means one finite execution or leaving it running.
|
||||
mode: Literal["live", "batch"] = "live"
|
||||
node_count: int = 0
|
||||
error_count: int = 0
|
||||
has_draft: bool = False
|
||||
|
||||
Reference in New Issue
Block a user