Publish, delete and module installs rebuild only what changed

Publishing or deleting a flow now splices that one flow into the running
graph instead of reconnecting every node in the installation, saving a
shared node's source rebuilds the flows using it, and installing modules
rebuilds only the flows holding a node that would not load.

Renaming stays on the full rebuild — it rewrites message references in
every other flow's document — and so does startup, which has no graph to
splice into.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StpRc2C6au1WJ1EUU7fsfu
This commit is contained in:
2026-08-23 18:32:51 +02:00
co-authored by Claude Opus 5
parent d553fbad80
commit cba8ca2c54
3 changed files with 16 additions and 4 deletions
+6
View File
@@ -10,6 +10,12 @@ Deferring because out of scope is fine, but don't mention deferring than.
## Open
### Dashboard UI rework (dedicated session)
Rework the dashboard UI to make it more flexible in terms of the look and feel.
I consider following design options a) overall appearance: material look or liquid glass (einUI https://github.com/einui/einui) b) color palette
### To be sorted
- FEAT/UI: we promise testing, but currently don't provide an UI for testing e.g. mock values or probing edge cases of a flow. This should be resolved (in a dedicated session); I'm thinking of a "Labs" page, which allows simulating an installation with all the flows (using their draft states) and which allows injecting values or mocking values based on events in the past
+3 -3
View File
@@ -425,7 +425,7 @@ async def publish_flow(
detail={"message": str(exc), "current_version": exc.current},
)
_audit("published", name, user)
await controller.reload()
await controller.reload_flow(name)
return _detail(controller, published)
@@ -475,7 +475,7 @@ async def delete_flow(
# Its files are gone; its values and queued work would otherwise linger.
await run_in_threadpool(controller.forget_flow, name)
await run_in_threadpool(_forget_runs, session, name)
await controller.reload()
await controller.reload_flow(name)
return Message(message=f"Deleted flow '{name}'")
@@ -556,7 +556,7 @@ async def save_node_source(
controller.store.write_lib_source, ref, source.code
)
if changed:
await controller.reload()
await controller.reload_lib_users(ref)
else:
await run_in_threadpool(
controller.store.write_node_source, name, node_id, source.code, True
+7 -1
View File
@@ -44,6 +44,12 @@ async def apply_modules(
A manifest that does not resolve changes nothing: the venv is left as it
was and the stored manifest is only written once the install succeeded.
Only the flows already holding a node that would not load are rebuilt,
because those are the ones an install is called to fix. A flow that this
install *breaks* — a package taken back out from under it — is still green
and fails at call time with the node author's own import error, until
something rebuilds it.
"""
ok, output = await run_in_threadpool(modules.sync, body.requirements)
if not ok:
@@ -65,5 +71,5 @@ async def apply_modules(
# packages that were just installed — a node that could not import one is
# the reason this was called, and it stays red until it is built again.
pool.respawn_all()
await controller.reload()
await controller.reload_failed_flows()
return ApplyResult(ok=True, output=output)