fix(automation): bind a flow published while the server runs, without a restart#2576
Merged
Conversation
… a restart Follow-up to #2560 (cold-boot flow binding). A flow published while the server runs — author a record-triggered automation in the Studio, publish it, immediately update a matching record — did not fire; its trigger only bound on the next restart. Two gaps, both fixed: 1. The publish path fired no rebind signal. The runtime dispatcher now announces 'metadata:reloaded' after a successful POST /packages/:id/publish-drafts — the same signal a dev artifact reload fires — so boot-cached consumers re-sync. 2. The runtime re-sync read the wrong source. The automation service's 'metadata:reloaded' re-sync pulled metadata.list('flow'), which returns 0 in a real running server; it now reads protocol.getMetaItems({type:'flow'}) — the same flattened view #2560's cold-boot bind uses — keeping teardown of removed flows. A failed/unavailable protocol read is a no-op and never tears down live flows. Verified end-to-end on a clean instance: authored a record-triggered flow in a writable package, published it without restarting, then updated a matching record and observed the flow fire (before the fix it did not). New regression tests: flow-publish-rebind.test.ts (re-sync binds a post-boot flow, tears down a removed one, reads the protocol not metadata.list, and never tears down on a failed read) plus two http-dispatcher.test.ts cases (publish fires / does not fire the hook). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 21 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Jul 4, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #2560 (cold-boot flow binding). A flow published while the server is running — the Studio online-authoring journey (author a record-triggered automation, publish it, immediately update a matching record) — did not fire. Its trigger only bound on the next process restart.
Two gaps, both fixed:
The publish path fired no rebind signal.
POST /packages/:id/publish-drafts→protocol.publishPackageDraftspromotes drafts to active but emitted no event the automation service listens to. The runtime dispatcher now announcesmetadata:reloadedafter a successful publish — the same signal a dev artifact reload fires (MetadataPlugin._reloadAndAnnounce) — so boot-cached consumers re-sync without a restart. Best-effort: a subscriber failure never fails the publish (drafts are already live); it rides the response asrebindError. Fires only when something was published/unhidden.The runtime re-sync read the wrong source. The automation service's
metadata:reloadedre-sync pulledmetadata.list('flow'), which returns 0 in a real running server (it does not surface inline app flows), so even when the hook fired it bound nothing. It now readsprotocol.getMetaItems({ type: 'flow' })— the same flattened flow view fix(automation): bind flow triggers on cold boot, not just after HMR reload #2560's cold-boot bind andGET /meta/flowuse — via a sharedreadFlowDefsFromProtocolhelper, while keeping the teardown of flows removed from the artifact. A failed or unavailable protocol read returnsnulland is a no-op — it never tears down live flows. Thekernel:readycold-boot bind stays additive (no teardown), reusing the same helper.Only one
metadata:reloadedsubscriber exists (the automation service), so firing it on publish has no collateral effect. Production is largely unaffected — a deploy reboots the process, so #2560's cold-boot bind covers it; this closes the gap for dev and single-instance Studio authoring.Changes
packages/services/service-automation/src/plugin.ts— extractreadFlowDefsFromProtocol;metadata:reloadedre-sync (resyncFlowsFromMetadata→resyncFlowsFromProtocol) reads the protocol and keeps teardown;syncFlowsFromProtocol(kernel:ready) reuses the helper, stays additive.packages/runtime/src/http-dispatcher.ts— announcemetadata:reloadedafter a successfulpublish-drafts.Tests
flow-publish-rebind.test.ts(4 cases): binds a flow that appears in the protocol after boot; tears down a flow the protocol no longer serves; reads the protocol, notmetadata.list(the exact bug); does not tear down live flows on a failed protocol read. The first/third fail on the pre-fix code.http-dispatcher.test.tscases: publish-drafts announcesmetadata:reloaded/ does not announce when nothing was published.flow-hot-reload.test.tsfake service (metadata → protocol source).@objectstack/service-automation231/231,@objectstack/runtime447/447; eslint clean.Verification (end-to-end, clean instance, no restart)
On a fresh showcase (
:3210):showcase_pubtestas a draft in a writable base package.showcase_task→ flow did not fire (negative control — still a draft).POST /packages/:id/publish-draftswithout restarting → flow became active; response carried norebindError.process.stderr.writeprobe inAutomationEngine.execute, sincelogger.infois suppressed andsys_automation_runonly records suspended runs). Probe removed before commit.🤖 Generated with Claude Code