Filed by the repo:hotcrm PM seat on behalf of a dev that measured this while adopting #4354 downstream (hotcrm#568, PR hotcrm#1355). Unassigned, awaiting triage — I do not produce domain:* or type from a sister-repo seat. Dedupe search run: no existing card.
Follow-on to #4354, which shipped the measurement. This is about the alert predicate the field documentation recommends for reading it, not about the counters, which work.
The claim
sys_automation_run.acted_count's field description states the broken-sweep detector as:
selected_count > 0 AND acted_count = 0 AND unmeasured_count = 0
with no qualifier. Any flow that re-selects the same records each run and gates each one on "was this already handled" satisfies that predicate in its healthy steady state — for as long as the prior work remains outstanding. That is not an exotic shape; it is the ordinary way to write an idempotent sweep, and both of hotcrm's daily sweeps are written that way.
Measured, not argued
Driven through the real AutomationEngine against opportunity_stagnation, reading result.summary off terminal runs. Pinned downstream as an executable pair in test/flow-run-summary.test.ts (hotcrm PR #1355):
| run | totals | run-level predicate | find_existing_task.selected | gate skips |
|---|
| healthy steady state — every stalled deal already nudged | selected 4, acted 0 | FIRES | 2 | 2 |
| dead gate — the #4347 shape, loop-body edge condition forced false | selected 2, acted 0 | FIRES | 0 | 2 |
| genuinely idle — nothing stalled | selected 0, acted 0 | quiet | — | 0 |
⇒ The predicate cannot separate rows 1 and 2, which is precisely the discrimination it is advertised to make.
⚠️The "over N consecutive runs" qualifier does not fix this. A healthy idempotent sweep trips the predicate on every consecutive run while the outstanding work stands — the steady state is persistent, not transient. Consecutiveness filters flapping, not this.
The data to disambiguate is already shipped — that is what makes this cheap
The per-node fold in summary_json separates the two cleanly:
- healthy — the idempotency lookup accounts for the gate skips (it found an open task for each record the gate skipped)
- broken — the same gate skips with nothing found to justify any of them
skipped_count's own description already gestures at this ("Many skips with no writes names the gate as the suspect"). So this reads as a precision gap in the recommended predicate, not a missing capability — most likely a documentation fix, possibly a second documented predicate for the idempotent-sweep case.
Why it matters more than a doc nit
The audience for this predicate is an operator wiring an alert they will then trust for months. A detector that fires during normal operation gets muted, and a muted broken-sweep detector returns everyone to the situation #4354 was opened to end — with the added cost that it now looks monitored. ⭐ The failure mode of a false-positive alert is the same silence the original bug had.
⚠️ There is a downstream pin on the current behaviour — please read before "fixing" it
hotcrm's test/flow-run-summary.test.ts asserts that both shapes currently trip the predicate:
expect(trips(healthy),'a correctly idempotent run no longer trips the predicate — re-check the qualifier in the admin docs').toBe(true);expect(trips(broken),'the broken sweep stopped tripping the predicate — the detector has regressed').toBe(true);
If this card is resolved by tightening the predicate's semantics, that first assertion goes red on a correctly fixed platform, and hotcrm's admin docs carry a qualifier that would then need removing. The assertion message says so, so the red is self-explaining — but registering it here so the fix is not surprised by it. If the resolution is documentation-only, nothing downstream moves.
Not worked around downstream
hotcrm documented the qualifier for its operators and changed no behaviour. ⛔ No app-side detector was built — that route is permanently rejected there on #4354's own reasoning.
Related: #4354 (shipped the summary), #4347 (the original silent no-op), hotcrm#568, hotcrm PR #1355.
Filed by the
repo:hotcrmPM seat on behalf of a dev that measured this while adopting #4354 downstream (hotcrm#568, PR hotcrm#1355). Unassigned, awaiting triage — I do not producedomain:*ortypefrom a sister-repo seat. Dedupe search run: no existing card.Follow-on to #4354, which shipped the measurement. This is about the alert predicate the field documentation recommends for reading it, not about the counters, which work.
The claim
sys_automation_run.acted_count's field description states the broken-sweep detector as:with no qualifier. Any flow that re-selects the same records each run and gates each one on "was this already handled" satisfies that predicate in its healthy steady state — for as long as the prior work remains outstanding. That is not an exotic shape; it is the ordinary way to write an idempotent sweep, and both of hotcrm's daily sweeps are written that way.
Measured, not argued
Driven through the real
AutomationEngineagainstopportunity_stagnation, readingresult.summaryoff terminal runs. Pinned downstream as an executable pair intest/flow-run-summary.test.ts(hotcrm PR #1355):find_existing_task.selected⇒ The predicate cannot separate rows 1 and 2, which is precisely the discrimination it is advertised to make.
The data to disambiguate is already shipped — that is what makes this cheap
The per-node fold in
summary_jsonseparates the two cleanly:skipped_count's own description already gestures at this ("Many skips with no writes names the gate as the suspect"). So this reads as a precision gap in the recommended predicate, not a missing capability — most likely a documentation fix, possibly a second documented predicate for the idempotent-sweep case.Why it matters more than a doc nit
The audience for this predicate is an operator wiring an alert they will then trust for months. A detector that fires during normal operation gets muted, and a muted broken-sweep detector returns everyone to the situation #4354 was opened to end — with the added cost that it now looks monitored. ⭐ The failure mode of a false-positive alert is the same silence the original bug had.
hotcrm's
test/flow-run-summary.test.tsasserts that both shapes currently trip the predicate:If this card is resolved by tightening the predicate's semantics, that first assertion goes red on a correctly fixed platform, and hotcrm's admin docs carry a qualifier that would then need removing. The assertion message says so, so the red is self-explaining — but registering it here so the fix is not surprised by it. If the resolution is documentation-only, nothing downstream moves.
Not worked around downstream
hotcrm documented the qualifier for its operators and changed no behaviour. ⛔ No app-side detector was built — that route is permanently rejected there on #4354's own reasoning.
Related: #4354 (shipped the summary), #4347 (the original silent no-op), hotcrm#568, hotcrm PR #1355.