Finalizing a flow makes it permanently read-only and cannot be undone, and it is
not part of the current workflow, so the controls that create that state are now
hidden: the Finalized toggle in the title toolbar and the Finalized entry in the
flows list filter. Gated by FLOW_FINALIZATION_ENABLED, matching SWIMLANES_ENABLED,
so nothing is deleted and re-enabling is one line.
Only the controls are gated. The badge on a flow row, the disabled delete and the
read-only editor stay, because rows finalized before the switch still exist and
hiding the explanation of why such a flow cannot be edited would make the app
inexplicable. For the same reason the toggle reappears for a flow that is already
finalized, so its state is never invisible in the place that owns it.
A persisted FINALIZED list filter now falls back to showing everything: a filter
whose control is hidden would otherwise keep narrowing the list with nothing on
screen to clear it.
The backend is untouched - finalized still gates editing and deletion there.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Projects group the flow list in place: no new page or tab, the existing sidebar
list gains collapsible per-project sections plus a project filter. Everything is
behind PROJECTS_ENABLED in shared/feature-flags, and with no projects the list
renders exactly as it did before - flows-list.spec guards that.
FlowsList stays the single owner of the load, filter, sort and list state; the
grouping is extracted into a pure flow-grouping.ts and a presentational
flows-group component. A wrapper rendering N flows lists would have re-registered
the same list state and re-triggered the load N times. The group chrome mirrors
tasks-executions-list, which already implements collapsible groups, so the two
sidebars read as one product.
toFlowCreateRequest deliberately still carries no project. It builds the
full-replace PUT the editor issues on every save, so a project sent there would
be silently dropped each time; membership changes only through
assignFlowToProject. flow-mapper.spec guards it.
Deleting a project destroys its flows, so it gets its own dialog rather than a
wider ConfirmDialogService: it names the count, lists the flows, says that
finalized flows go too - which the flow list otherwise forbids - and requires the
project name to be typed. Widening the shared confirm service for one destructive
caller would have rippled through every other call site.
Moving a flow between projects is a menu item, not drag-and-drop: the sidebar is
320px with its own scroll, and the whole card is already a click target, so a
drag gesture would fight the open-flow gesture. Flow order inside a project uses
up/down arrows for the same reason.
Shared context is edited in a dialog modelled on the Global Inputs panel, which
is the mental model users already have for ${{global.x}}; the title toolbar shows
the inherited values read-only, because that is where prompts are written.
"Run project" creates and starts a run - creating alone would look like nothing
happened. Runs come back BLOCKED or STOPPED when a step needs inputs or failed,
and the UI says so instead of claiming progress. Project runs surface in /tasks
as sibling groups labelled with a project chip, derived in tasks-executor: no
second nesting level and no backend change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>