Skip to content

Commit f585d9e

Browse files
alex-strukclaude
andcommitted
docs(workflow-builder): record D-17 — cancel-on-new-Try never fires
9.7 fails, and the diagnosis is precise. Against a purpose-built 15s dynamic node, a second Try leaves the first running: two concurrent 'running' runs, neither cancelled, both completing. The backend is correct and complete. startRun calls cancelInFlightTriesForLineage before starting, and G-021 deliberately narrows it so only runs stamped RunTrigger='try' are cancelled — production runs must complete regardless, or feeding 240 documents through would have document #2 cancel document #1. The frontend never stamps one. The drawer's Try and the Run tab both call useStartWorkflowRun -> POST /workflows/:id/runs, and that endpoint hard-codes the new run as 'api'. So the 'try' category has no producer at all, and the cancel set is always empty. G-021's narrowing is inert because the thing it protects is never created. Every editor Try therefore behaves as a production run: two Trys race, canvas badges follow whichever statuses land last, and on an OCR or LLM graph the abandoned run keeps spending. Two fix shapes, both with tradeoffs (a public-API 'trigger' field that callers could assert, vs a dedicated /tries route), so this needs Alex — and should likely be decided together with D-16, the other Try-path gap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 121c564 commit f585d9e

1 file changed

Lines changed: 51 additions & 0 deletions

File tree

feature-docs/20260724-workflow-builder-spec-completion/WALKTHROUGH_PARTS_2_14.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,54 @@ Two defensible fixes, and the choice is a product decision:
432432
the run" invariant and leaving version semantics alone.
433433

434434
Not implemented unilaterally. Needs Alex.
435+
436+
## D-17 — no editor run is ever stamped "try", so cancel-on-new-Try never fires (open)
437+
438+
9.7 asks: start a Try, Try again mid-run → *"prior run cancelled server-side
439+
(shows cancelled in Run history); **exactly one active run**."*
440+
441+
Measured against a purpose-built 15-second dynamic node (`walk-slow-node`, so
442+
there is a real window to cancel in):
443+
444+
| | |
445+
|---|---|
446+
| after first Try | 1 run `running` |
447+
| after second Try, mid-flight | **2 runs `running`** |
448+
| at completion | both `succeeded`, **none cancelled** |
449+
450+
The backend machinery exists and is correct. `startRun` calls
451+
`cancelInFlightTriesForLineage(id)` before starting, and G-021 deliberately
452+
narrows it: *"only runs stamped `RunTrigger = "try"` are cancelled — production
453+
runs started through this endpoint run to completion regardless"*, so that
454+
feeding 240 documents through does not have document #2 cancel document #1.
455+
456+
**The frontend never stamps one.** The drawer's Try and the Run tab both call
457+
`useStartWorkflowRun``POST /workflows/:id/runs`, and that endpoint hard-codes
458+
the new run as `"api"`:
459+
460+
```ts
461+
// G-021: this is the public run API — a production run, not an editor
462+
// preview. Marking it `"api"` keeps it out of every later cancel set.
463+
"api",
464+
```
465+
466+
So the `"try"` side of the distinction has no producer. `cancelInFlightTriesForLineage`
467+
runs on every start and always finds an empty set. G-021's careful narrowing is
468+
inert because the category it protects never gets created.
469+
470+
Consequences: every editor Try is treated as a production run, two Trys race
471+
each other with the canvas badges fed by whichever statuses land last, and on an
472+
expensive graph (OCR, LLM) the abandoned run keeps spending.
473+
474+
Two shapes of fix, and the choice is a product decision:
475+
476+
1. **Add `trigger` to `StartRunRequestDto`** and have the drawer send `"try"`.
477+
Smallest change, but it puts a field on the *public* run API that lets a
478+
caller opt their own runs into being cancelled by editor activity.
479+
2. **Give Try its own endpoint** (`POST /workflows/:id/tries`) that stamps
480+
`"try"` server-side. No public-API surface change and the trigger stops being
481+
client-assertable, at the cost of a second route.
482+
483+
Not implemented unilaterally — this is the third Try-path item (with **D-16**)
484+
where the documented behaviour and the shipped behaviour differ, and the two
485+
should probably be decided together.

0 commit comments

Comments
 (0)