You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Recording a real gap. Deliberately not implementing yet — the reasoning for deferring is below, and it is the substance of this issue.
Upstream: objectstack-ai/objectstack#4354 tracks the platform-side fix. This issue stays open as the hotcrm-side record of the gap and of the decision not to work around it locally.
The gap
A scheduled sweep that selects records and then acts on none is indistinguishable, from outside, from a sweep that had no work to do. Both:
report success: true
log nothing
run on time, every time
write zero records
This is what let #567 / objectstack#4347 live. Three flows (opportunity_stagnation, contract_renewal, campaign_enrollment) ran daily and produced nothing, for as long as they had existed. Nobody noticed, because there was nothing to notice.
It was caught by the runtime tests added in #563/#566 — not by anything in production. Had those flows not been in the tested set, the bug would still be live.
Runtime coverage now stands at 24/24 hooks and 17/20 flows, and test/runtime-coverage.test.ts fails when a new hook or flow arrives untested. That is the actual defense, and it exists.
Blast radius is already bounded: every sweep's query carries limit: 500 (campaign_enrollment 1000), so even a fully-drained backlog is capped per run.
Why an app-level detector was considered and rejected
The obvious app-side fix is a counter: assignment nodes tallying selected-vs-acted inside the loop, a decision comparing them, and a notify when a sweep selects > 0 and acts on 0.
It was rejected, for now, on these grounds:
The detector would be built from the primitives that just proved able to fail silently. A silent-no-op detector implemented with decision gates inside a loop body has exactly the failure mode it is meant to catch. When it breaks, it breaks quietly, and we are back where we started with more code.
script nodes cannot help. The built-in runtime treats inline config.script as a no-op and logs no server-side JS sandbox — this node is a no-op. Emitting a run summary from a flow would require registering real functions, which is a larger commitment than the problem currently justifies.
The engine already has the data.AutomationEngine carries recordLog / executionLog / listRuns internally. The information exists; it is simply not surfaced. Building a parallel app-level mechanism now means maintaining it forever and discarding it when the platform exposes the real thing.
Whether the counter approach even works is unverified. Whether an assignment inside a loop body accumulates across iterations has not been tested. Proposing it as the plan of record without that check would repeat the mistake this whole thread is about.
The actual fix
Platform-side, tracked in objectstack-ai/objectstack#4354: surface flow run summaries — records selected, acted on, skipped — in the console and make them queryable. Then "selected 30, acted 0" is visible on day one, for every flow, with no per-flow instrumentation and nothing for an app to maintain.
Revisit when
objectstack#4354 ships → adopt it, close this; or
a second silent-no-op incident occurs that the test suite did not catch → the cost/benefit changes and the app-level detector becomes worth its risks
Not to be confused with
The one-time backlog burst on first run after #566 deploys — every currently-stalled deal gets nudged, every contract already inside its notice window gets a renewal task. That is a migration consequence of fixing the bug, not a defect, and it is bounded by the existing limit: 500.
Recording a real gap. Deliberately not implementing yet — the reasoning for deferring is below, and it is the substance of this issue.
The gap
A scheduled sweep that selects records and then acts on none is indistinguishable, from outside, from a sweep that had no work to do. Both:
success: trueThis is what let #567 / objectstack#4347 live. Three flows (
opportunity_stagnation,contract_renewal,campaign_enrollment) ran daily and produced nothing, for as long as they had existed. Nobody noticed, because there was nothing to notice.It was caught by the runtime tests added in #563/#566 — not by anything in production. Had those flows not been in the tested set, the bug would still be live.
Why this is not urgent
test/flow-scheduled.test.tsthat walks every registered flow and fails if any loop body reintroduces a bare string condition.test/runtime-coverage.test.tsfails when a new hook or flow arrives untested. That is the actual defense, and it exists.limit: 500(campaign_enrollment1000), so even a fully-drained backlog is capped per run.Why an app-level detector was considered and rejected
The obvious app-side fix is a counter:
assignmentnodes tallying selected-vs-acted inside the loop, adecisioncomparing them, and anotifywhen a sweep selects> 0and acts on0.It was rejected, for now, on these grounds:
decisiongates inside aloopbody has exactly the failure mode it is meant to catch. When it breaks, it breaks quietly, and we are back where we started with more code.scriptnodes cannot help. The built-in runtime treats inlineconfig.scriptas a no-op and logsno server-side JS sandbox — this node is a no-op. Emitting a run summary from a flow would require registering realfunctions, which is a larger commitment than the problem currently justifies.AutomationEnginecarriesrecordLog/executionLog/listRunsinternally. The information exists; it is simply not surfaced. Building a parallel app-level mechanism now means maintaining it forever and discarding it when the platform exposes the real thing.assignmentinside a loop body accumulates across iterations has not been tested. Proposing it as the plan of record without that check would repeat the mistake this whole thread is about.The actual fix
Platform-side, tracked in objectstack-ai/objectstack#4354: surface flow run summaries — records selected, acted on, skipped — in the console and make them queryable. Then "selected 30, acted 0" is visible on day one, for every flow, with no per-flow instrumentation and nothing for an app to maintain.
Revisit when
Not to be confused with
The one-time backlog burst on first run after #566 deploys — every currently-stalled deal gets nudged, every contract already inside its notice window gets a renewal task. That is a migration consequence of fixing the bug, not a defect, and it is bounded by the existing
limit: 500.Related: #495, #563, #566, #567, objectstack-ai/objectstack#4347, objectstack-ai/objectstack#4354.