@rotorsoft/act 1.23.18 → 1.23.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1566,6 +1566,15 @@ var CorrelateCycle = class {
1566
1566
  get checkpoint() {
1567
1567
  return this._checkpoint;
1568
1568
  }
1569
+ /**
1570
+ * Whether any reaction uses a dynamic resolver — i.e. whether `correlate`
1571
+ * performs a store scan rather than the static no-op early-return. The
1572
+ * settle loop reads this to avoid recording a fictitious circuit-breaker
1573
+ * success on a static app whose correlate never touches the store (#1329).
1574
+ */
1575
+ get has_dynamic_resolvers() {
1576
+ return this._has_dynamic_resolvers;
1577
+ }
1569
1578
  /**
1570
1579
  * Initialize correlation state on first call.
1571
1580
  * - Reads max(at) from store, then floors the cold-start checkpoint at
@@ -2860,7 +2869,7 @@ var SettleLoop = class {
2860
2869
  ...correlate_query,
2861
2870
  after: after_before
2862
2871
  });
2863
- this._deps.breaker.passed();
2872
+ if (this._deps.correlate_probes_store) this._deps.breaker.passed();
2864
2873
  last_drain = await this._deps.drain(drain_options);
2865
2874
  const made_progress = subscribed > 0 || last_drain.acked.length > 0 || last_drain.blocked.length > 0 || last_id > after_before;
2866
2875
  if (!made_progress) break;
@@ -3125,13 +3134,14 @@ var Act = class {
3125
3134
  on_error: (error, circuit) => this._emit_error(error, circuit),
3126
3135
  // Re-probe the store when the cooldown elapses, so recovery is
3127
3136
  // automatic even on the default lane (which has no periodic poller).
3128
- // Any polling loop can trip the shared breaker — but a bare `drain()`
3129
- // only touches the store when the controller is armed, so a trip
3130
- // from a correlate failure could go unprobed. `settle()` always runs
3131
- // a correlate (a store query) before draining, so it re-probes
3132
- // regardless of which loop opened the breaker: one success closes it
3133
- // and every loop resumes on its own cadence. A failed probe re-opens
3134
- // the breaker and reschedules the wake.
3137
+ // The wake fires `settle()`, which (in half-open) runs a real store
3138
+ // probe: for a dynamic-resolver app the probe is settle's correlate
3139
+ // (a store scan); for a static-reaction app correlate is a no-op that
3140
+ // records no health, so the probe is settle's DRAIN claim — either
3141
+ // way one success closes the breaker and every loop resumes, a
3142
+ // failure re-opens it and reschedules the wake. Settle does NOT close
3143
+ // the breaker off a no-op correlate (#1329) — only a real store op
3144
+ // (correlate scan or drain claim) records `passed()`.
3135
3145
  on_retry: () => {
3136
3146
  this.settle({ debounceMs: 0 });
3137
3147
  }
@@ -3308,7 +3318,11 @@ var Act = class {
3308
3318
  correlate: (q) => this.correlate(q),
3309
3319
  drain: (o) => this.drain(o),
3310
3320
  on_settled: (drain) => this.emit("settled", drain),
3311
- breaker: this._breaker
3321
+ breaker: this._breaker,
3322
+ // Static-reaction apps' correlate is a no-op (no store call), so a
3323
+ // settle pass carries no store-health signal — don't let it record a
3324
+ // breaker success (#1329).
3325
+ correlate_probes_store: this._correlate.has_dynamic_resolvers
3312
3326
  },
3313
3327
  options.settleDebounceMs ?? DEFAULT_SETTLE_DEBOUNCE_MS
3314
3328
  );