The categorical fix for the recurring "assistant produced a flow I can't
save" problem. Until now, POST/PUT /flows rejected (400) on ANY
@ValidFlowStructure violation, conflating two very different things:
genuinely corrupt/inconsistent data, and a flow that is merely not
runnable yet. The user's model - and how workflow editors normally
behave - is that an incomplete flow must be savable as a DRAFT and only
gated at execution time.
Key realisation: FlowExecutionValidator.collectErrors already runs the
same @ValidFlowStructure bean validation, so DRAFT vs EXECUTABLE status
(toView -> isExecutable) already reflects every structural/executability
problem, and ExecutionsService.startExecution independently calls
flowExecutionValidator.validate() - so a non-executable draft can never
actually run. The hard save-gate was therefore redundant for the
executability class; only data-integrity needed to keep blocking.
FlowService.validateFlow now partitions violations by code:
- SAVE_BLOCKING_CODES (integrity: type/inputs/outputs mismatch, missing
config, duplicate/missing node ids, unknown node type, nested
containers, lane integrity, global-input integrity, bias-annotation
integrity, and non-decodable request-level constraints like a null
flow) still reject with 400, re-encoded via ValidationErrorCodec so
the structured errors[] contract is unchanged.
- everything else (dangling/absent connections, container subflow not
yet exposing its handles, exclusive-branch merges, branch-rejoin/end
gaps, dependencies, deadlocks, shared-session ordering, ...) no longer
blocks: the flow saves as DRAFT and the issue is surfaced by
GET /flows/{id}/validation.
This closes the whole class of "structurally sane but not runnable ->
can't save" failures once and for all, instead of chasing each variant.
Tests: dangling connection now saves as DRAFT (was 400); the two
exclusive-branch-merge tests updated from "rejected" to "saved as draft,
reported by the execution validator"; factory-tampering and
bias-integrity rejections still 400 unchanged. 443/443.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>