@graphrefly/graphrefly 0.47.1 → 0.48.0
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/base/composition/index.cjs +28 -19
- package/dist/base/composition/index.cjs.map +1 -1
- package/dist/base/composition/index.d.cts +14 -5
- package/dist/base/composition/index.d.ts +14 -5
- package/dist/base/composition/index.js +9 -9
- package/dist/base/index.cjs +294 -164
- package/dist/base/index.cjs.map +1 -1
- package/dist/base/index.d.cts +2 -2
- package/dist/base/index.d.ts +2 -2
- package/dist/base/index.js +77 -72
- package/dist/base/io/index.cjs +145 -85
- package/dist/base/io/index.cjs.map +1 -1
- package/dist/base/io/index.d.cts +32 -5
- package/dist/base/io/index.d.ts +32 -5
- package/dist/base/io/index.js +5 -5
- package/dist/base/mutation/index.cjs +21 -0
- package/dist/base/mutation/index.cjs.map +1 -1
- package/dist/base/mutation/index.d.cts +23 -1
- package/dist/base/mutation/index.d.ts +23 -1
- package/dist/base/mutation/index.js +3 -1
- package/dist/base/sources/browser/index.cjs +18 -12
- package/dist/base/sources/browser/index.cjs.map +1 -1
- package/dist/base/sources/browser/index.d.cts +20 -2
- package/dist/base/sources/browser/index.d.ts +20 -2
- package/dist/base/sources/browser/index.js +18 -12
- package/dist/base/sources/browser/index.js.map +1 -1
- package/dist/base/sources/event/index.cjs +29 -1
- package/dist/base/sources/event/index.cjs.map +1 -1
- package/dist/base/sources/event/index.d.cts +67 -3
- package/dist/base/sources/event/index.d.ts +67 -3
- package/dist/base/sources/event/index.js +5 -2
- package/dist/base/sources/index.cjs +96 -50
- package/dist/base/sources/index.cjs.map +1 -1
- package/dist/base/sources/index.d.cts +1 -1
- package/dist/base/sources/index.d.ts +1 -1
- package/dist/base/sources/index.js +7 -4
- package/dist/base/sources/node/index.cjs +43 -37
- package/dist/base/sources/node/index.cjs.map +1 -1
- package/dist/base/sources/node/index.js +43 -37
- package/dist/base/sources/node/index.js.map +1 -1
- package/dist/{chunk-J5WFUEO4.js → chunk-23MAWVOJ.js} +3 -3
- package/dist/{chunk-YXCPV26R.js → chunk-3REMCHSS.js} +39 -27
- package/dist/chunk-3REMCHSS.js.map +1 -0
- package/dist/{chunk-CEVNQ74M.js → chunk-3YGXPUHW.js} +2 -2
- package/dist/{chunk-CEVNQ74M.js.map → chunk-3YGXPUHW.js.map} +1 -1
- package/dist/{chunk-EVYY4X5A.js → chunk-46X2EFQH.js} +16 -5
- package/dist/chunk-46X2EFQH.js.map +1 -0
- package/dist/{chunk-NY2PYHNC.js → chunk-5UY3PNFY.js} +12 -5
- package/dist/chunk-5UY3PNFY.js.map +1 -0
- package/dist/{chunk-RGMTUZCL.js → chunk-65OM4XLQ.js} +50 -4
- package/dist/chunk-65OM4XLQ.js.map +1 -0
- package/dist/{chunk-3PSLNJDU.js → chunk-6DQYBIHW.js} +314 -49
- package/dist/chunk-6DQYBIHW.js.map +1 -0
- package/dist/{chunk-LDCSZ72P.js → chunk-6YBER5UP.js} +3 -3
- package/dist/{chunk-LDCSZ72P.js.map → chunk-6YBER5UP.js.map} +1 -1
- package/dist/{chunk-7EGRP2VX.js → chunk-7BULJTL6.js} +2 -2
- package/dist/{chunk-7EGRP2VX.js.map → chunk-7BULJTL6.js.map} +1 -1
- package/dist/{chunk-VLAGJZSL.js → chunk-7T7WLEPM.js} +25 -4
- package/dist/chunk-7T7WLEPM.js.map +1 -0
- package/dist/{chunk-PKPO3JTZ.js → chunk-AQAKDE7F.js} +29 -11
- package/dist/chunk-AQAKDE7F.js.map +1 -0
- package/dist/{chunk-2OB3CEJS.js → chunk-B5Y5GPD5.js} +2 -2
- package/dist/{chunk-BXGZFGZ4.js → chunk-C5QD5DQX.js} +22 -1
- package/dist/chunk-C5QD5DQX.js.map +1 -0
- package/dist/{chunk-4XCHZRUJ.js → chunk-D5YGR4TP.js} +58 -7
- package/dist/chunk-D5YGR4TP.js.map +1 -0
- package/dist/{chunk-NPRP3MCV.js → chunk-DHDCOOJU.js} +2 -2
- package/dist/chunk-DHDCOOJU.js.map +1 -0
- package/dist/{chunk-MTTRCEJT.js → chunk-DVTDF5OI.js} +2 -2
- package/dist/{chunk-SOOKUYVM.js → chunk-F7EKHR32.js} +13 -9
- package/dist/chunk-F7EKHR32.js.map +1 -0
- package/dist/{chunk-A7KV5UK4.js → chunk-G7H6PN7P.js} +2 -2
- package/dist/{chunk-OCUDSN63.js → chunk-GGKHHG5Y.js} +110 -64
- package/dist/chunk-GGKHHG5Y.js.map +1 -0
- package/dist/{chunk-RAGGHLCV.js → chunk-GUNIRPEJ.js} +8 -6
- package/dist/{chunk-RAGGHLCV.js.map → chunk-GUNIRPEJ.js.map} +1 -1
- package/dist/{chunk-YJ4U2D2C.js → chunk-J5TBZFBD.js} +9 -7
- package/dist/chunk-J5TBZFBD.js.map +1 -0
- package/dist/{chunk-Y52CS6YA.js → chunk-JA67ZQG2.js} +2 -2
- package/dist/{chunk-Y52CS6YA.js.map → chunk-JA67ZQG2.js.map} +1 -1
- package/dist/{chunk-U225SKB4.js → chunk-K4ZYJ4EM.js} +569 -424
- package/dist/chunk-K4ZYJ4EM.js.map +1 -0
- package/dist/{chunk-Z4YXAUDN.js → chunk-KUFXLAEY.js} +11 -7
- package/dist/{chunk-Z4YXAUDN.js.map → chunk-KUFXLAEY.js.map} +1 -1
- package/dist/{chunk-IHTWQEDR.js → chunk-LTSI7ULC.js} +3 -3
- package/dist/{chunk-IHTWQEDR.js.map → chunk-LTSI7ULC.js.map} +1 -1
- package/dist/{chunk-DKNHAICT.js → chunk-MMHGYX44.js} +25 -9
- package/dist/chunk-MMHGYX44.js.map +1 -0
- package/dist/{chunk-K7PDZYQE.js → chunk-MQMTRKY3.js} +129 -50
- package/dist/chunk-MQMTRKY3.js.map +1 -0
- package/dist/{chunk-42FQ27MQ.js → chunk-MTODGQBR.js} +44 -179
- package/dist/chunk-MTODGQBR.js.map +1 -0
- package/dist/{chunk-O3MT7DYI.js → chunk-N6MNJNHB.js} +2 -2
- package/dist/{chunk-FVINAAKA.js → chunk-NBK6QQMG.js} +14 -13
- package/dist/{chunk-FVINAAKA.js.map → chunk-NBK6QQMG.js.map} +1 -1
- package/dist/{chunk-DM4OMPWK.js → chunk-NSA5K5G2.js} +2 -2
- package/dist/{chunk-MLTPJMH6.js → chunk-QQYULEZL.js} +2 -2
- package/dist/chunk-QSW4DFKE.js +31 -0
- package/dist/chunk-QSW4DFKE.js.map +1 -0
- package/dist/{chunk-PZWISPIQ.js → chunk-S7HN5FHL.js} +17 -11
- package/dist/chunk-S7HN5FHL.js.map +1 -0
- package/dist/{chunk-4S53H2KR.js → chunk-SUNCHMML.js} +2 -2
- package/dist/{chunk-4GYMCUDZ.js → chunk-T2U6N3FV.js} +7 -7
- package/dist/{chunk-RJOG4IJU.js → chunk-T5URUIIY.js} +50 -35
- package/dist/chunk-T5URUIIY.js.map +1 -0
- package/dist/{chunk-B4AKFXGE.js → chunk-TPTZZV25.js} +6 -6
- package/dist/chunk-TPTZZV25.js.map +1 -0
- package/dist/{chunk-BU3SEFA5.js → chunk-V46JWFGV.js} +7 -6
- package/dist/chunk-V46JWFGV.js.map +1 -0
- package/dist/{chunk-IJRR6YAI.js → chunk-VLDRAMP7.js} +18 -12
- package/dist/chunk-VLDRAMP7.js.map +1 -0
- package/dist/{chunk-6XZYT4SW.js → chunk-X6ESZDR6.js} +8 -9
- package/dist/chunk-X6ESZDR6.js.map +1 -0
- package/dist/{chunk-E5OZPDIW.js → chunk-X7BA5PWG.js} +7 -5
- package/dist/chunk-X7BA5PWG.js.map +1 -0
- package/dist/{chunk-CXANAIZU.js → chunk-XEWV254I.js} +3 -3
- package/dist/{chunk-CXANAIZU.js.map → chunk-XEWV254I.js.map} +1 -1
- package/dist/{chunk-V4Y3TM7U.js → chunk-YBJVKMTM.js} +38 -16
- package/dist/chunk-YBJVKMTM.js.map +1 -0
- package/dist/{chunk-7ADWWI2T.js → chunk-ZW32BPXV.js} +17 -6
- package/dist/chunk-ZW32BPXV.js.map +1 -0
- package/dist/compat/index.cjs +52 -5
- package/dist/compat/index.cjs.map +1 -1
- package/dist/compat/index.d.cts +1 -1
- package/dist/compat/index.d.ts +1 -1
- package/dist/compat/index.js +7 -7
- package/dist/compat/nestjs/index.cjs +52 -5
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +1 -1
- package/dist/compat/nestjs/index.d.ts +1 -1
- package/dist/compat/nestjs/index.js +4 -4
- package/dist/{fallback-Bx46zqky.d.cts → fallback-BROR6ZhO.d.cts} +1 -1
- package/dist/{fallback-pIWW8A2d.d.ts → fallback-DO80aM_3.d.ts} +1 -1
- package/dist/{index-B_p8tnvf.d.cts → index-D1z3XcF9.d.cts} +1 -0
- package/dist/{index-_HDSmPyp.d.ts → index-DZ6yua0Q.d.ts} +1 -0
- package/dist/index.cjs +2387 -1707
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +173 -150
- package/dist/index.js.map +1 -1
- package/dist/presets/ai/index.cjs +88 -26
- package/dist/presets/ai/index.cjs.map +1 -1
- package/dist/presets/ai/index.js +14 -14
- package/dist/presets/harness/index.cjs +183 -51
- package/dist/presets/harness/index.cjs.map +1 -1
- package/dist/presets/harness/index.d.cts +15 -5
- package/dist/presets/harness/index.d.ts +15 -5
- package/dist/presets/harness/index.js +26 -26
- package/dist/presets/index.cjs +298 -101
- package/dist/presets/index.cjs.map +1 -1
- package/dist/presets/index.d.cts +2 -2
- package/dist/presets/index.d.ts +2 -2
- package/dist/presets/index.js +49 -49
- package/dist/presets/inspect/index.cjs +63 -14
- package/dist/presets/inspect/index.cjs.map +1 -1
- package/dist/presets/inspect/index.d.cts +1 -1
- package/dist/presets/inspect/index.d.ts +1 -1
- package/dist/presets/inspect/index.js +6 -6
- package/dist/presets/resilience/index.cjs +64 -44
- package/dist/presets/resilience/index.cjs.map +1 -1
- package/dist/presets/resilience/index.d.cts +12 -8
- package/dist/presets/resilience/index.d.ts +12 -8
- package/dist/presets/resilience/index.js +6 -6
- package/dist/{rate-limiter-DpVbSYdH.d.cts → rate-limiter-DC26FM8J.d.cts} +10 -1
- package/dist/{rate-limiter-CEALq4N1.d.ts → rate-limiter-DyWpwpQP.d.ts} +10 -1
- package/dist/{reactive-layout-fswlBUvX.d.ts → reactive-layout-BBBWH0V_.d.cts} +85 -4
- package/dist/{reactive-layout-fswlBUvX.d.cts → reactive-layout-BBBWH0V_.d.ts} +85 -4
- package/dist/solutions/index.cjs +239 -92
- package/dist/solutions/index.cjs.map +1 -1
- package/dist/solutions/index.d.cts +2 -2
- package/dist/solutions/index.d.ts +2 -2
- package/dist/solutions/index.js +32 -32
- package/dist/{spawnable-5mDY501F.d.cts → spawnable-B2IlW60f.d.cts} +23 -2
- package/dist/{spawnable-D3lR0oQu.d.ts → spawnable-tttFz2Nh.d.ts} +23 -2
- package/dist/testing/index.cjs +94 -0
- package/dist/testing/index.cjs.map +1 -0
- package/dist/testing/index.d.cts +59 -0
- package/dist/testing/index.d.ts +59 -0
- package/dist/testing/index.js +73 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/{timeout-U5O4ESK3.js → timeout-BEABACRP.js} +2 -2
- package/dist/utils/ai/browser.cjs.map +1 -1
- package/dist/utils/ai/browser.d.cts +2 -2
- package/dist/utils/ai/browser.d.ts +2 -2
- package/dist/utils/ai/browser.js +10 -10
- package/dist/utils/ai/browser.js.map +1 -1
- package/dist/utils/ai/index.cjs +291 -191
- package/dist/utils/ai/index.cjs.map +1 -1
- package/dist/utils/ai/index.d.cts +108 -12
- package/dist/utils/ai/index.d.ts +108 -12
- package/dist/utils/ai/index.js +23 -21
- package/dist/utils/ai/node.cjs.map +1 -1
- package/dist/utils/ai/node.d.cts +5 -5
- package/dist/utils/ai/node.d.ts +5 -5
- package/dist/utils/ai/node.js +3 -3
- package/dist/utils/ai/node.js.map +1 -1
- package/dist/utils/cqrs/index.cjs +29 -3
- package/dist/utils/cqrs/index.cjs.map +1 -1
- package/dist/utils/cqrs/index.d.cts +12 -7
- package/dist/utils/cqrs/index.d.ts +12 -7
- package/dist/utils/cqrs/index.js +2 -2
- package/dist/utils/demo-shell/index.cjs +45 -19
- package/dist/utils/demo-shell/index.cjs.map +1 -1
- package/dist/utils/demo-shell/index.d.cts +1 -1
- package/dist/utils/demo-shell/index.d.ts +1 -1
- package/dist/utils/demo-shell/index.js +2 -2
- package/dist/utils/domain-templates/index.cjs +1 -1
- package/dist/utils/domain-templates/index.cjs.map +1 -1
- package/dist/utils/domain-templates/index.js +3 -3
- package/dist/utils/graphspec/index.cjs +1 -1
- package/dist/utils/graphspec/index.cjs.map +1 -1
- package/dist/utils/graphspec/index.js +3 -3
- package/dist/utils/harness/index.cjs +16 -10
- package/dist/utils/harness/index.cjs.map +1 -1
- package/dist/utils/harness/index.js +1 -1
- package/dist/utils/index.cjs +1692 -1192
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.cts +7 -7
- package/dist/utils/index.d.ts +7 -7
- package/dist/utils/index.js +77 -59
- package/dist/utils/inspect/index.cjs +52 -4
- package/dist/utils/inspect/index.cjs.map +1 -1
- package/dist/utils/inspect/index.d.cts +32 -3
- package/dist/utils/inspect/index.d.ts +32 -3
- package/dist/utils/inspect/index.js +4 -4
- package/dist/utils/job-queue/index.cjs +46 -9
- package/dist/utils/job-queue/index.cjs.map +1 -1
- package/dist/utils/job-queue/index.d.cts +33 -3
- package/dist/utils/job-queue/index.d.ts +33 -3
- package/dist/utils/job-queue/index.js +2 -2
- package/dist/utils/memory/index.cjs +570 -425
- package/dist/utils/memory/index.cjs.map +1 -1
- package/dist/utils/memory/index.d.cts +261 -33
- package/dist/utils/memory/index.d.ts +261 -33
- package/dist/utils/memory/index.js +10 -2
- package/dist/utils/messaging/index.cjs.map +1 -1
- package/dist/utils/messaging/index.d.cts +4 -3
- package/dist/utils/messaging/index.d.ts +4 -3
- package/dist/utils/messaging/index.js +2 -2
- package/dist/utils/orchestration/index.cjs +14 -3
- package/dist/utils/orchestration/index.cjs.map +1 -1
- package/dist/utils/orchestration/index.js +3 -3
- package/dist/utils/process/index.cjs +32 -2
- package/dist/utils/process/index.cjs.map +1 -1
- package/dist/utils/process/index.d.cts +4 -3
- package/dist/utils/process/index.d.ts +4 -3
- package/dist/utils/process/index.js +3 -3
- package/dist/utils/reactive-layout/index.cjs +184 -55
- package/dist/utils/reactive-layout/index.cjs.map +1 -1
- package/dist/utils/reactive-layout/index.d.cts +128 -3
- package/dist/utils/reactive-layout/index.d.ts +128 -3
- package/dist/utils/reactive-layout/index.js +16 -8
- package/dist/utils/reduction/index.cjs +1 -1
- package/dist/utils/reduction/index.cjs.map +1 -1
- package/dist/utils/reduction/index.js +2 -2
- package/dist/utils/resilience/index.cjs +64 -43
- package/dist/utils/resilience/index.cjs.map +1 -1
- package/dist/utils/resilience/index.d.cts +1 -1
- package/dist/utils/resilience/index.d.ts +1 -1
- package/dist/utils/resilience/index.js +5 -5
- package/dist/utils/surface/index.cjs +1 -1
- package/dist/utils/surface/index.cjs.map +1 -1
- package/dist/utils/surface/index.js +4 -4
- package/package.json +15 -3
- package/dist/chunk-3PSLNJDU.js.map +0 -1
- package/dist/chunk-42FQ27MQ.js.map +0 -1
- package/dist/chunk-4XCHZRUJ.js.map +0 -1
- package/dist/chunk-6XZYT4SW.js.map +0 -1
- package/dist/chunk-7ADWWI2T.js.map +0 -1
- package/dist/chunk-B4AKFXGE.js.map +0 -1
- package/dist/chunk-BU3SEFA5.js.map +0 -1
- package/dist/chunk-BXGZFGZ4.js.map +0 -1
- package/dist/chunk-DKNHAICT.js.map +0 -1
- package/dist/chunk-E5OZPDIW.js.map +0 -1
- package/dist/chunk-EVYY4X5A.js.map +0 -1
- package/dist/chunk-IJRR6YAI.js.map +0 -1
- package/dist/chunk-K7PDZYQE.js.map +0 -1
- package/dist/chunk-NPRP3MCV.js.map +0 -1
- package/dist/chunk-NY2PYHNC.js.map +0 -1
- package/dist/chunk-OCUDSN63.js.map +0 -1
- package/dist/chunk-PKPO3JTZ.js.map +0 -1
- package/dist/chunk-PZWISPIQ.js.map +0 -1
- package/dist/chunk-RGMTUZCL.js.map +0 -1
- package/dist/chunk-RJOG4IJU.js.map +0 -1
- package/dist/chunk-SOOKUYVM.js.map +0 -1
- package/dist/chunk-U225SKB4.js.map +0 -1
- package/dist/chunk-V4Y3TM7U.js.map +0 -1
- package/dist/chunk-VLAGJZSL.js.map +0 -1
- package/dist/chunk-W2BOPXTI.js +0 -1
- package/dist/chunk-YJ4U2D2C.js.map +0 -1
- package/dist/chunk-YXCPV26R.js.map +0 -1
- package/dist/timeout-U5O4ESK3.js.map +0 -1
- /package/dist/{chunk-J5WFUEO4.js.map → chunk-23MAWVOJ.js.map} +0 -0
- /package/dist/{chunk-2OB3CEJS.js.map → chunk-B5Y5GPD5.js.map} +0 -0
- /package/dist/{chunk-MTTRCEJT.js.map → chunk-DVTDF5OI.js.map} +0 -0
- /package/dist/{chunk-A7KV5UK4.js.map → chunk-G7H6PN7P.js.map} +0 -0
- /package/dist/{chunk-O3MT7DYI.js.map → chunk-N6MNJNHB.js.map} +0 -0
- /package/dist/{chunk-DM4OMPWK.js.map → chunk-NSA5K5G2.js.map} +0 -0
- /package/dist/{chunk-MLTPJMH6.js.map → chunk-QQYULEZL.js.map} +0 -0
- /package/dist/{chunk-4S53H2KR.js.map → chunk-SUNCHMML.js.map} +0 -0
- /package/dist/{chunk-4GYMCUDZ.js.map → chunk-T2U6N3FV.js.map} +0 -0
- /package/dist/{chunk-W2BOPXTI.js.map → timeout-BEABACRP.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/resilience/breaker.ts","../src/utils/resilience/budget-gate.ts","../src/utils/resilience/fallback.ts","../src/utils/resilience/rate-limiter.ts","../src/utils/resilience/adaptive-rate-limiter.ts"],"sourcesContent":["/**\n * Circuit breaker — open/half-open/closed state machine + companion bundle.\n *\n * - {@link circuitBreaker} returns a synchronous breaker handle (counters,\n * state machine, optional reactive-options subscription).\n * - {@link withBreaker} wraps a `Node<T>` and surfaces a reactive\n * `Node<CircuitState>` companion (`bundle.breakerState`) for telemetry.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\tfactoryTag,\n\tmonotonicNs,\n\ttype Node,\n\tnode,\n\tRESOLVED,\n} from \"@graphrefly/pure-ts/core\";\nimport {\n\tclampNonNegative,\n\tisNode,\n\tmsgVal,\n\ttype NodeOrValue,\n\toperatorOpts,\n} from \"../../base/resilience/_internal.js\";\nimport { type BackoffStrategy, NS_PER_SEC } from \"../../base/resilience/backoff.js\";\nimport type { GateState } from \"./gate-state.js\";\n\nexport type CircuitState = \"closed\" | \"open\" | \"half-open\";\n\n/**\n * Lifecycle-shaped state companion emitted by {@link withBreaker}\n * (DS-13.5.B, locked 2026-05-01). Pre-1.0 break vs the prior\n * `Node<CircuitState>` (string-only) shape.\n *\n * `status` extends {@link GateState} with `\"half-open\"`. The numeric\n * fields surface the breaker's full internal state for telemetry and\n * `describe()` traversal.\n *\n * @category extra/resilience\n */\nexport interface BreakerState {\n\treadonly status: GateState | \"half-open\";\n\treadonly failureCount: number;\n\treadonly openCycle: number;\n\treadonly lastOpenedAtNs: number;\n\treadonly halfOpenAttempts: number;\n\treadonly lastCooldownNs: number;\n}\n\n/**\n * Thrown when {@link withBreaker} is configured with `onOpen: \"error\"` and the breaker rejects work.\n *\n * @category extra\n */\nexport class CircuitOpenError extends Error {\n\toverride name = \"CircuitOpenError\";\n\tconstructor() {\n\t\tsuper(\"Circuit breaker is open\");\n\t}\n}\n\nexport interface CircuitBreakerOptions {\n\t/** Number of consecutive failures before opening. Default: 5. */\n\tfailureThreshold?: number;\n\t/** Base cooldown in nanoseconds before transitioning to half-open. Default: 30s. */\n\tcooldownNs?: number;\n\t/** Backoff strategy for cooldown escalation across consecutive open cycles. Overrides `cooldownNs` when provided. */\n\tcooldown?: BackoffStrategy;\n\t/** Max trial requests allowed in half-open state. Default: 1. */\n\thalfOpenMax?: number;\n\t/**\n\t * Clock function returning **nanoseconds** with `monotonicNs()` semantics\n\t * (monotonically non-decreasing; suitable for elapsed-time arithmetic — never\n\t * use `Date.now()` because wall-clock skew can flip elapsed math negative).\n\t * Default: `monotonicNs` from `core/clock`. Override for deterministic tests.\n\t */\n\tnow?: () => number;\n}\n\nexport interface CircuitBreaker {\n\t/** Whether a request should be allowed through. Triggers open→half-open transition when cooldown expires. */\n\tcanExecute(): boolean;\n\t/** Record a successful execution. Resets to closed. */\n\trecordSuccess(): void;\n\t/** Record a failed execution. May transition to open. */\n\trecordFailure(error?: unknown): void;\n\t/**\n\t * Current circuit state (read-only, does not trigger transitions).\n\t *\n\t * **Telemetry:** wrap with {@link withBreaker} to surface this as a reactive\n\t * `Node<CircuitState>` companion (`bundle.breakerState`) — every state\n\t * transition (`closed`/`open`/`half-open`) emits to subscribers.\n\t */\n\treadonly state: CircuitState;\n\t/** Number of consecutive failures in the current closed period. */\n\treadonly failureCount: number;\n\t/** Manually reset to closed state, clearing all counters. */\n\treset(): void;\n\t/**\n\t * Release the reactive-options subscription (Tier 6.5 3.2.4, 2026-04-29).\n\t * No-op when constructed with static options. Call when retiring a\n\t * breaker whose options came from a `Node<CircuitBreakerOptions>` to\n\t * avoid leaking the option-Node subscription.\n\t */\n\tdispose(): void;\n}\n\n/**\n * Factory for a synchronous circuit breaker with `closed`, `open`, and `half-open` states.\n *\n * Supports escalating cooldown via an optional {@link BackoffStrategy} — each consecutive\n * open→half-open→open cycle increments the backoff attempt.\n *\n * @param options - Threshold, cooldown, half-open limit, and optional clock\n * override; OR a `Node<CircuitBreakerOptions>` carrying the same shape\n * reactively (Tier 6.5 3.2.4).\n * @returns {@link CircuitBreaker} instance.\n *\n * @remarks\n * **Timing:** Uses `monotonicNs()` by default (nanoseconds). Override `now` for tests.\n *\n * **Reactive options (locked semantics, Tier 6.5 3.2.4, 2026-04-29).**\n * When `options` is a `Node<CircuitBreakerOptions>`, the breaker\n * subscribes at construction and re-reads `failureThreshold` /\n * `cooldownNs` / `cooldown` / `halfOpenMax` / `now` on each DATA. **An\n * option swap RESETS the breaker to `\"closed\"`** with all counters\n * cleared — operators tuning a runaway breaker get a clean baseline.\n * If retaining failure history across re-tunings matters, derive a new\n * breaker per-tuning instead. Call `breaker.dispose()` when retiring to\n * release the option-Node subscription.\n *\n * @example\n * ```ts\n * import { circuitBreaker, exponential, NS_PER_SEC } from \"@graphrefly/graphrefly-ts\";\n *\n * const b = circuitBreaker({\n * failureThreshold: 3,\n * cooldown: exponential({ baseNs: 1 * NS_PER_SEC }),\n * });\n * ```\n *\n * @category extra\n */\nexport function circuitBreaker(options?: NodeOrValue<CircuitBreakerOptions>): CircuitBreaker {\n\tlet threshold = 5;\n\tlet baseCooldownNs = 30 * NS_PER_SEC;\n\tlet cooldownStrategy: BackoffStrategy | null = null;\n\tlet halfOpenMax = 1;\n\tlet now: () => number = monotonicNs;\n\n\tfunction applyOptions(o: CircuitBreakerOptions | undefined): void {\n\t\tthreshold = Math.max(1, o?.failureThreshold ?? 5);\n\t\tbaseCooldownNs = clampNonNegative(o?.cooldownNs ?? 30 * NS_PER_SEC);\n\t\tcooldownStrategy = o?.cooldown ?? null;\n\t\thalfOpenMax = Math.max(1, o?.halfOpenMax ?? 1);\n\t\tnow = o?.now ?? monotonicNs;\n\t}\n\n\tlet _state: CircuitState = \"closed\";\n\tlet _failureCount = 0;\n\tlet _openCycle = 0;\n\tlet _lastOpenedAt = 0;\n\tlet _lastCooldownNs = baseCooldownNs;\n\tlet _halfOpenAttempts = 0;\n\n\t// DS-13.5.B (locked 2026-05-01): reactive option swaps preserve\n\t// internal state — no reset across rebind. `now` is mode-locked at\n\t// construction (clock override is structural); a mid-flight `now`\n\t// change is logged and skipped (the prior `now` is preserved).\n\t// Empty `{}` emits are no-ops.\n\t//\n\t// QA A2 (2026-05-03): bad-`now` mid-flight does NOT throw — sync\n\t// throw inside a subscribe callback corrupts host scheduler state\n\t// (mirrors timeout's `actions.down([[ERROR]])` rationale; sink-side\n\t// throws break the wave's dispatch contract).\n\t//\n\t// QA A8 (2026-05-03): the push-on-subscribe re-delivery of the\n\t// cached opts fires the subscribe callback once at attach time with\n\t// the same value used for the eager `applyOptions(initialOpts)`\n\t// call above. Skip the first cached emit so opts are not re-applied\n\t// twice on construction.\n\tlet initialOpts: CircuitBreakerOptions | undefined;\n\tlet optsUnsub: (() => void) | undefined;\n\tif (isNode(options)) {\n\t\tconst optsNode = options as Node<CircuitBreakerOptions>;\n\t\tinitialOpts = optsNode.cache as CircuitBreakerOptions | undefined;\n\t\tapplyOptions(initialOpts);\n\t\tconst lockedNow = initialOpts?.now;\n\t\tconst hadInitialCache = initialOpts !== undefined;\n\t\tlet firstEmit = hadInitialCache;\n\t\toptsUnsub = optsNode.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] !== DATA) continue;\n\t\t\t\tif (firstEmit) {\n\t\t\t\t\tfirstEmit = false;\n\t\t\t\t\tcontinue; // QA A8: skip push-on-subscribe replay of cached opts\n\t\t\t\t}\n\t\t\t\tconst next = m[1] as CircuitBreakerOptions | undefined;\n\t\t\t\tif (next == null || typeof next !== \"object\") continue;\n\t\t\t\tif (Object.keys(next).length === 0) continue; // empty {} no-op\n\t\t\t\tif (\"now\" in next && next.now !== lockedNow) {\n\t\t\t\t\t// QA A2: log + skip; do NOT throw inside a subscribe\n\t\t\t\t\t// callback — host scheduler corruption hazard.\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"circuitBreaker: ignoring mid-flight `now` change — clock override is mode-locked at construction. Prior `now` preserved.\",\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// State-preserving merge: only re-apply the axes that\n\t\t\t\t// changed; preserve `_state`, `_failureCount`, etc.\n\t\t\t\tconst merged: CircuitBreakerOptions = {\n\t\t\t\t\t...(initialOpts ?? {}),\n\t\t\t\t\t...next,\n\t\t\t\t\t...(lockedNow !== undefined ? { now: lockedNow } : {}),\n\t\t\t\t};\n\t\t\t\tapplyOptions(merged);\n\t\t\t\tinitialOpts = merged;\n\t\t\t}\n\t\t});\n\t} else {\n\t\tapplyOptions(options as CircuitBreakerOptions | undefined);\n\t}\n\t_lastCooldownNs = baseCooldownNs;\n\n\tfunction getCooldownNs(): number {\n\t\tif (!cooldownStrategy) return baseCooldownNs;\n\t\tconst delayNs = cooldownStrategy(_openCycle);\n\t\treturn delayNs !== null ? delayNs : baseCooldownNs;\n\t}\n\n\tfunction transitionToOpen(): void {\n\t\t_state = \"open\";\n\t\t_lastCooldownNs = getCooldownNs();\n\t\t_lastOpenedAt = now();\n\t\t_halfOpenAttempts = 0;\n\t}\n\n\tconst breaker: CircuitBreaker = {\n\t\tcanExecute(): boolean {\n\t\t\tif (_state === \"closed\") return true;\n\n\t\t\tif (_state === \"open\") {\n\t\t\t\tconst elapsed = now() - _lastOpenedAt;\n\t\t\t\tif (elapsed >= _lastCooldownNs) {\n\t\t\t\t\t_state = \"half-open\";\n\t\t\t\t\t_halfOpenAttempts = 1;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (_halfOpenAttempts < halfOpenMax) {\n\t\t\t\t_halfOpenAttempts++;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\n\t\trecordSuccess(): void {\n\t\t\tif (_state === \"half-open\") {\n\t\t\t\t_state = \"closed\";\n\t\t\t\t_failureCount = 0;\n\t\t\t\t_openCycle = 0;\n\t\t\t} else if (_state === \"closed\") {\n\t\t\t\t_failureCount = 0;\n\t\t\t}\n\t\t},\n\n\t\trecordFailure(_error?: unknown): void {\n\t\t\tif (_state === \"half-open\") {\n\t\t\t\t_openCycle++;\n\t\t\t\ttransitionToOpen();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (_state === \"closed\") {\n\t\t\t\t_failureCount++;\n\t\t\t\tif (_failureCount >= threshold) {\n\t\t\t\t\ttransitionToOpen();\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tget state(): CircuitState {\n\t\t\treturn _state;\n\t\t},\n\n\t\tget failureCount(): number {\n\t\t\treturn _failureCount;\n\t\t},\n\n\t\treset(): void {\n\t\t\t_state = \"closed\";\n\t\t\t_failureCount = 0;\n\t\t\t_openCycle = 0;\n\t\t\t_halfOpenAttempts = 0;\n\t\t},\n\n\t\tdispose(): void {\n\t\t\toptsUnsub?.();\n\t\t},\n\n\t\t// Internal accessors used by withBreaker for the BreakerState\n\t\t// companion (DS-13.5.B). Not part of the public CircuitBreaker\n\t\t// interface but exposed for the bundle wiring.\n\t};\n\t(breaker as unknown as { _stateSnapshot: () => BreakerState })._stateSnapshot = () => ({\n\t\tstatus: _state,\n\t\tfailureCount: _failureCount,\n\t\topenCycle: _openCycle,\n\t\tlastOpenedAtNs: _lastOpenedAt,\n\t\thalfOpenAttempts: _halfOpenAttempts,\n\t\tlastCooldownNs: _lastCooldownNs,\n\t});\n\n\treturn breaker;\n}\n\nexport type WithBreakerBundle<T> = {\n\tnode: Node<T>;\n\tbreakerState: Node<BreakerState>;\n};\n\n/**\n * Returns a unary wrapper that gates upstream `DATA` through a {@link CircuitBreaker}.\n *\n * @param breaker - Shared breaker instance (typically one per resource).\n * @param options - `onOpen: \"skip\"` emits `RESOLVED` when open; `\"error\"` emits {@link CircuitOpenError}.\n * @returns Function mapping `Node<T>` to `{ node, breakerState }` companion nodes.\n *\n * @remarks\n * **Success path:** `COMPLETE` calls {@link CircuitBreaker.recordSuccess}. **Failure path:** upstream `ERROR` calls {@link CircuitBreaker.recordFailure} and is forwarded.\n *\n * **State telemetry:** `breakerState: Node<CircuitState>` is a reactive companion that mirrors `breaker.state` — every transition (`closed`/`open`/`half-open`) emits a `DATA`. Also accessible via `node.meta.breakerState` for `describe()` traversal.\n *\n * @example\n * ```ts\n * import { state, withBreaker, circuitBreaker } from \"@graphrefly/graphrefly-ts\";\n *\n * const b = circuitBreaker({ failureThreshold: 2 });\n * const s = state(1);\n * const { node, breakerState } = withBreaker(b)(s);\n * ```\n *\n * @category extra\n */\nexport function withBreaker<T>(\n\tbreaker: CircuitBreaker,\n\toptions?: { onOpen?: \"skip\" | \"error\"; meta?: Record<string, unknown> },\n): (source: Node<T>) => WithBreakerBundle<T> {\n\tconst onOpen = options?.onOpen ?? \"skip\";\n\tconst callerMeta = options?.meta;\n\n\treturn (source: Node<T>): WithBreakerBundle<T> => {\n\t\tconst snapshot = (breaker as unknown as { _stateSnapshot?: () => BreakerState })._stateSnapshot;\n\t\tconst initialSnapshot: BreakerState = snapshot\n\t\t\t? snapshot()\n\t\t\t: {\n\t\t\t\t\tstatus: breaker.state,\n\t\t\t\t\tfailureCount: breaker.failureCount,\n\t\t\t\t\topenCycle: 0,\n\t\t\t\t\tlastOpenedAtNs: 0,\n\t\t\t\t\thalfOpenAttempts: 0,\n\t\t\t\t\tlastCooldownNs: 0,\n\t\t\t\t};\n\t\tconst wrapped = node<T>(\n\t\t\t[],\n\t\t\t(_deps, a) => {\n\t\t\t\tfunction syncState(): void {\n\t\t\t\t\tconst s = snapshot\n\t\t\t\t\t\t? snapshot()\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\tstatus: breaker.state,\n\t\t\t\t\t\t\t\tfailureCount: breaker.failureCount,\n\t\t\t\t\t\t\t\topenCycle: 0,\n\t\t\t\t\t\t\t\tlastOpenedAtNs: 0,\n\t\t\t\t\t\t\t\thalfOpenAttempts: 0,\n\t\t\t\t\t\t\t\tlastCooldownNs: 0,\n\t\t\t\t\t\t\t};\n\t\t\t\t\twrapped.meta.breakerState.down([[DIRTY], [DATA, s]]);\n\t\t\t\t}\n\n\t\t\t\tconst unsub = source.subscribe((msgs) => {\n\t\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\t\tconst t = m[0];\n\t\t\t\t\t\tif (t === DIRTY) a.down([[DIRTY]]);\n\t\t\t\t\t\telse if (t === DATA) {\n\t\t\t\t\t\t\tif (breaker.canExecute()) {\n\t\t\t\t\t\t\t\tsyncState();\n\t\t\t\t\t\t\t\ta.emit(m[1] as T);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsyncState();\n\t\t\t\t\t\t\t\tif (onOpen === \"error\") a.down([[ERROR, new CircuitOpenError()]]);\n\t\t\t\t\t\t\t\telse a.down([[RESOLVED]]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (t === RESOLVED) a.down([[RESOLVED]]);\n\t\t\t\t\t\telse if (t === COMPLETE) {\n\t\t\t\t\t\t\tbreaker.recordSuccess();\n\t\t\t\t\t\t\tsyncState();\n\t\t\t\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t\t\t\t} else if (t === ERROR) {\n\t\t\t\t\t\t\tbreaker.recordFailure(msgVal(m));\n\t\t\t\t\t\t\tsyncState();\n\t\t\t\t\t\t\ta.down([m]);\n\t\t\t\t\t\t} else a.down([m]);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tsyncState();\n\t\t\t\treturn { onDeactivation: unsub };\n\t\t\t},\n\t\t\t{\n\t\t\t\t...operatorOpts(),\n\t\t\t\tmeta: {\n\t\t\t\t\t...(callerMeta ?? {}),\n\t\t\t\t\tbreakerState: initialSnapshot,\n\t\t\t\t\t...factoryTag(\"withBreaker\", { onOpen }),\n\t\t\t\t},\n\t\t\t\tcompleteWhenDepsComplete: false,\n\t\t\t\tinitial: source.cache,\n\t\t\t},\n\t\t);\n\n\t\treturn { node: wrapped, breakerState: wrapped.meta.breakerState as Node<BreakerState> };\n\t};\n}\n","/**\n * `budgetGate` — numeric-constraint flow gate (Tier 2.2 promotion from\n * `patterns/reduction/`).\n *\n * Lives alongside the other `extra/resilience/` flow controls (`retry`,\n * `circuitBreaker`, `rateLimiter`, `tokenBucket`, `fallback`, `withStatus`).\n *\n * @module\n */\n\nimport type { NodeActions } from \"@graphrefly/pure-ts/core\";\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\ttype Message,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\tPAUSE,\n\tRESOLVED,\n\tRESUME,\n} from \"@graphrefly/pure-ts/core\";\nimport { domainMeta } from \"../../base/meta/domain-meta.js\";\nimport type { GateState } from \"./gate-state.js\";\n\n/** A reactive constraint for {@link budgetGate}. */\nexport type BudgetConstraint<T = unknown> = {\n\t/** Constraint node whose value is checked. */\n\tnode: Node<T>;\n\t/** Returns `true` when the constraint is satisfied (budget available). */\n\tcheck: (value: T) => boolean;\n\t/**\n\t * Optional human-readable name for `BudgetGateState.constraintsSnapshot`.\n\t * Defaults to the constraint Node's `.name` (or `\"\"` when unset).\n\t */\n\tname?: string;\n};\n\n/** Options for {@link budgetGate}. */\nexport type BudgetGateOptions = Omit<NodeOptions<unknown>, \"describeKind\" | \"name\" | \"meta\"> & {\n\tmeta?: Record<string, unknown>;\n};\n\n/**\n * Per-constraint snapshot inside {@link BudgetGateState}. The `value` field is\n * typed as `unknown` because constraint values are generic — most callers\n * carry numeric budgets but the gate doesn't enforce that. Cast at the\n * subscriber site if you need a narrower type.\n *\n * @category extra/resilience\n */\nexport interface BudgetConstraintSnapshot {\n\treadonly name: string;\n\treadonly satisfied: boolean;\n\treadonly value: unknown;\n}\n\n/**\n * Lifecycle-shaped state companion emitted by {@link budgetGate} (DS-13.5.B,\n * locked 2026-05-01). `status` is `\"open\"` when every constraint's `check`\n * returns true; `\"closed\"` otherwise. The `constraintsSnapshot` array\n * preserves constraint ordering and reflects the most recent values seen\n * via per-constraint reactive updates.\n *\n * @category extra/resilience\n */\nexport interface BudgetGateState {\n\treadonly status: GateState;\n\treadonly constraintsSnapshot: ReadonlyArray<BudgetConstraintSnapshot>;\n}\n\n/**\n * Bundle returned by {@link budgetGate}: the gated output node and its\n * gate-state companion. Pre-1.0 break vs. the prior `Node<T>` return —\n * unwrap via `.node` for downstream wiring.\n *\n * @category extra/resilience\n */\nexport interface BudgetGateBundle<T> {\n\tnode: Node<T>;\n\tbudgetGateState: Node<BudgetGateState>;\n}\n\n/**\n * Unbounded head-index queue with O(1) `push` and O(1) `shift`.\n *\n * Distinct from {@link RingBuffer} (drop-oldest, fixed capacity) because\n * `budgetGate` MUST NOT silently drop buffered DATA when the gate is closed —\n * upstream is asked to PAUSE and the buffered items are guaranteed to flush\n * once the constraint relaxes (or on terminal force-flush). A drop-oldest\n * eviction would break that contract by losing items between PAUSE and\n * RESUME.\n *\n * Storage grows on demand and is compacted opportunistically: once the head\n * pointer crosses the midpoint of the underlying array, we slice the consumed\n * prefix away. Memory bound is **worst-case ~3× live size** (DF3, 2026-04-29\n * doc tighten — a queue that grows to N, drains to 0.6N, then re-pushes 0.4N\n * retains ~3× live size between compactions). Amortized footprint trends\n * lower under steady-state usage. Trade-off: keeps `push` and `shift` O(1) —\n * replacing the prior `buffer.slice(1)` per drain which was O(N²) over a\n * long-lived bucket (Tier 3.3.1 fix).\n */\nclass HeadIndexQueue<T> {\n\tprivate buf: T[] = [];\n\tprivate head = 0;\n\n\tget size(): number {\n\t\treturn this.buf.length - this.head;\n\t}\n\n\tpush(item: T): void {\n\t\tthis.buf.push(item);\n\t}\n\n\t/** O(1) — removes and returns the oldest item, or `undefined` when empty. */\n\tshift(): T | undefined {\n\t\tif (this.head >= this.buf.length) return undefined;\n\t\tconst item = this.buf[this.head]!;\n\t\t// Release the slot for GC. Cheaper than splice; cost folded into the\n\t\t// periodic compaction below.\n\t\t(this.buf as Array<T | undefined>)[this.head] = undefined;\n\t\tthis.head++;\n\t\t// Compact when more than half the array is consumed prefix.\n\t\tif (this.head > 32 && this.head * 2 > this.buf.length) {\n\t\t\tthis.buf = this.buf.slice(this.head);\n\t\t\tthis.head = 0;\n\t\t}\n\t\treturn item;\n\t}\n\n\tclear(): void {\n\t\tthis.buf = [];\n\t\tthis.head = 0;\n\t}\n}\n\n/**\n * Pass-through that respects reactive constraint nodes.\n *\n * DATA flows through when all constraints are satisfied. When any constraint\n * is exceeded, `PAUSE` is sent upstream and DATA is buffered in a FIFO queue.\n * When constraints relax, the queue drains in arrival order and `RESUME` is\n * sent upstream.\n *\n * ## Invariants (do not refactor without preserving)\n *\n * 1. **Terminal force-flush.** On `COMPLETE` / `ERROR` arriving from `source`,\n * every buffered item is emitted downstream BEFORE the terminal message is\n * forwarded. The constraint is intentionally bypassed for the flush — once\n * upstream is done, the caller must see the buffered work, not lose it.\n * See COMPOSITION-GUIDE §19 (terminal-emission operators).\n *\n * 2. **PAUSE-release ordering.** When a constraint flips from saturated →\n * released, the queue drains in FIFO order downstream BEFORE `RESUME` is\n * sent upstream. Reversing the order (RESUME-then-drain) would let new\n * upstream DATA interleave with the queue tail, breaking arrival-order\n * delivery. See COMPOSITION-GUIDE §9, §9a (diamond + batch coalescing).\n *\n * 3. **Deferred RESOLVED.** A `RESOLVED` from `source` while the queue is\n * non-empty is held until the queue drains, then forwarded — so downstream\n * sees `[buffered DATA…, RESOLVED]` in causal order rather than\n * `[RESOLVED, buffered DATA…]`.\n *\n * **Stall risk (qa D4):** if the constraint never relaxes AND no terminal\n * arrives from `source`, the deferred RESOLVED is held forever. Downstream\n * consumers that depend on `RESOLVED` for an `awaitSettled`-style\n * coordination wait stall in this case. PAUSE is sent upstream so source\n * backpressure stops further DATA, but the gate itself has no escape\n * hatch — by design (the producer-pattern is fire-and-forget; recovery\n * happens at the compositor level via timeout, retry, or cancellation).\n *\n * 4. **Constraint DIRTY suppression.** Constraint-node DIRTY does NOT\n * propagate downstream — only `source`-DIRTY does. The gate's downstream\n * semantics track `source`'s wave, not constraint waves.\n *\n * 5. **Lazy PAUSE (qa D3).** PAUSE is sent upstream ONLY when a `source` DATA\n * arrives that fails the constraint check (the first blocked item). A\n * constraint flipping closed BEFORE any source DATA arrives does NOT emit\n * a preemptive PAUSE — upstream may push DATA freely until the first\n * item is buffered. This matches the producer-pattern lazy-activation\n * philosophy (don't impose backpressure for hypothetical future blocks).\n * For eager-PAUSE semantics, wrap the gate in a compositor that watches\n * constraints + source independently.\n *\n * ## Queue\n *\n * The internal buffer is an unbounded {@link HeadIndexQueue} (O(1) push,\n * O(1) shift, opportunistic compaction). It does NOT use {@link RingBuffer}\n * because RingBuffer's drop-oldest eviction would silently lose buffered\n * items between PAUSE and RESUME. Backpressure (PAUSE) is the upstream\n * contract for bounding the queue, not capacity-driven eviction here.\n *\n * ## Producer-pattern: source edge is invisible to `describe()`\n *\n * `budgetGate` is constructed via `node([], fn)` and subscribes to `source`\n * and the constraint nodes manually inside its activation fn. Because no\n * dep is declared at construction, **`describe()` shows no edge from\n * `source` (or any constraint) into the returned node** — the gate looks\n * like a standalone leaf source. This is intentional (see COMPOSITION-GUIDE\n * §24 \"Edges are derived, not declared\"): if you want the constraint /\n * source dependency to appear in describe output, surface it at the\n * compositor level (e.g. annotate via `meta.ai.upstream`, or wrap the gate\n * in a parent factory that exposes the deps as constructor args).\n *\n * ## Reference equality + Tier 6.5 3.2.5 locked semantics\n *\n * **Constraint VALUES are reactive.** Each `BudgetConstraint.node` is\n * subscribed at activation; per-value changes flip the gate (re-evaluate\n * in the same wave) and trigger PAUSE/RESUME upstream. Per the locked\n * semantic rule for the reactive-options-widening batch (Tier 6.5 3.2.5,\n * 2026-04-29): \"constraints array re-evaluated immediately against\n * current source; adding/removing constraints triggers gate\n * re-evaluation in the same wave\" — the per-value half is shipped via\n * the existing constraint-Node subscription model.\n *\n * **The constraints ARRAY shape is static.** The factory captures the\n * `constraints` array reference and each `check` function at\n * construction; it does NOT diff subsequent arrays. To add or remove\n * constraints reactively, build the swap at the compositor level (a\n * `switchMap` rebuild over a constraint-shape Node), or construct a new\n * gate. Dynamic constraint-array reactivity is intentionally deferred —\n * the subscription churn (resub on every constraint add/remove) and\n * `latestValues` shape mutation overshoot the budget-gate's\n * fire-and-forget ergonomics.\n *\n * @param source - Input node.\n * @param constraints - Reactive constraint checks. MUST be non-empty.\n * @param opts - Optional node options.\n * @returns Gated node.\n *\n * @throws {RangeError} when `constraints.length === 0`. The gate has no\n * meaningful identity without at least one check — degenerate to plain\n * pass-through (e.g. via `derived([source], ([v]) => v)`) instead.\n *\n * @category resilience\n */\nexport function budgetGate<T>(\n\tsource: Node<T>,\n\tconstraints: ReadonlyArray<BudgetConstraint>,\n\topts?: BudgetGateOptions,\n): BudgetGateBundle<T> {\n\tif (constraints.length === 0) throw new RangeError(\"budgetGate requires at least one constraint\");\n\n\tconst constraintNodes = constraints.map((c) => c.node);\n\tconst allDeps = [source as Node, ...constraintNodes] as Node[];\n\n\tconst buffer = new HeadIndexQueue<T>();\n\tlet paused = false;\n\tlet pendingResolved = false;\n\tconst lockId = Symbol(\"budget-gate\");\n\n\t// Latest DATA from each constraint. Seeded at **activation time** (inside the\n\t// producer fn below) — a wiring-time boundary read, not a reactive-callback\n\t// read — so concurrent constraint updates between factory-time and\n\t// activation-time are reflected before `checkBudget()` first runs. The\n\t// subscribe handler updates this array on each constraint DATA message, so\n\t// `checkBudget` never reads `.cache` from inside a reactive callback.\n\tconst latestValues: unknown[] = new Array(constraints.length);\n\n\tfunction checkBudget(): boolean {\n\t\treturn constraints.every((c, i) => c.check(latestValues[i]));\n\t}\n\n\t// DS-13.5.B (locked 2026-05-01): lifecycle-shaped state companion.\n\t// Initialized with `status: \"closed\"` until activation seeds the values\n\t// and the first `checkBudget()` runs.\n\t//\n\t// QA A3 (2026-05-03): equality uses structural compare on\n\t// `(status, name, satisfied, value)` tuples via `Object.is` per\n\t// `value` — NOT `JSON.stringify`. Caller-supplied constraint values\n\t// (`unknown`) can be circular, BigInt, or otherwise non-serializable;\n\t// `JSON.stringify` would throw and corrupt the wave dispatch.\n\tfunction budgetGateStateEqual(a: BudgetGateState, b: BudgetGateState): boolean {\n\t\tif (a === b) return true;\n\t\tif (a.status !== b.status) return false;\n\t\tconst sa = a.constraintsSnapshot;\n\t\tconst sb = b.constraintsSnapshot;\n\t\tif (sa.length !== sb.length) return false;\n\t\tfor (let i = 0; i < sa.length; i++) {\n\t\t\tconst ai = sa[i];\n\t\t\tconst bi = sb[i];\n\t\t\tif (ai === undefined || bi === undefined) return false;\n\t\t\tif (ai.name !== bi.name) return false;\n\t\t\tif (ai.satisfied !== bi.satisfied) return false;\n\t\t\tif (!Object.is(ai.value, bi.value)) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tconst budgetGateState = node<BudgetGateState>([], {\n\t\tname: \"budgetGateState\",\n\t\tdescribeKind: \"state\",\n\t\tinitial: {\n\t\t\tstatus: \"closed\",\n\t\t\tconstraintsSnapshot: constraints.map((c) => ({\n\t\t\t\tname: c.name ?? c.node.name ?? \"\",\n\t\t\t\tsatisfied: false,\n\t\t\t\tvalue: undefined,\n\t\t\t})),\n\t\t},\n\t\tequals: budgetGateStateEqual,\n\t});\n\n\tlet lastEmittedState: BudgetGateState | null = null;\n\n\tfunction publishState(): void {\n\t\tconst snapshot: BudgetConstraintSnapshot[] = constraints.map((c, i) => {\n\t\t\tconst v = latestValues[i];\n\t\t\tlet satisfied = false;\n\t\t\ttry {\n\t\t\t\tsatisfied = c.check(v as never);\n\t\t\t} catch (err) {\n\t\t\t\t// QA A3: log the bug-throw rather than silently mapping to\n\t\t\t\t// `satisfied=false`. The constraint's check function failing\n\t\t\t\t// is a programmer error — at minimum surface it to console.\n\t\t\t\tconsole.error(\n\t\t\t\t\t`budgetGate: constraint \"${c.name ?? c.node.name ?? `[${i}]`}\" check threw; treating as not satisfied.`,\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t\tsatisfied = false;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tname: c.name ?? c.node.name ?? \"\",\n\t\t\t\tsatisfied,\n\t\t\t\tvalue: v,\n\t\t\t};\n\t\t});\n\t\tconst status: GateState = snapshot.every((s) => s.satisfied) ? \"open\" : \"closed\";\n\t\tconst next: BudgetGateState = { status, constraintsSnapshot: snapshot };\n\t\tif (lastEmittedState != null && budgetGateStateEqual(lastEmittedState, next)) {\n\t\t\treturn;\n\t\t}\n\t\tlastEmittedState = next;\n\t\tbudgetGateState.down([[DIRTY], [DATA, next]]);\n\t}\n\n\tfunction flushBuffer(actions: NodeActions): void {\n\t\t// FIFO drain — invariant 2 (PAUSE-release ordering). Stop early if a\n\t\t// later constraint check flips false mid-drain (the queue's tail stays\n\t\t// buffered for the next RESUME).\n\t\twhile (buffer.size > 0 && checkBudget()) {\n\t\t\tconst item = buffer.shift()!;\n\t\t\tactions.emit(item);\n\t\t}\n\t\t// Drain deferred RESOLVED once buffer is empty (invariant 3).\n\t\tif (buffer.size === 0 && pendingResolved) {\n\t\t\tpendingResolved = false;\n\t\t\tactions.down([[RESOLVED]]);\n\t\t}\n\t}\n\n\t// Producer pattern: manually subscribe to all deps for per-message interception.\n\t// Source / constraint edges are intentionally NOT declared as `_deps` — see\n\t// the JSDoc \"Producer-pattern\" section above and COMPOSITION-GUIDE §24.\n\tconst out = node<T>(\n\t\t[],\n\t\t(_data, gateActions) => {\n\t\t\t// Seed `latestValues` at activation (not factory time) so any constraint\n\t\t\t// updates between factory return and first subscribe are captured before\n\t\t\t// source's push-on-subscribe fires `checkBudget()`.\n\t\t\tfor (let i = 0; i < constraints.length; i++) {\n\t\t\t\tlatestValues[i] = constraints[i]!.node.cache;\n\t\t\t}\n\t\t\t// Seed the companion state at activation as well.\n\t\t\tpublishState();\n\t\t\tconst unsubs: Array<() => void> = [];\n\t\t\tfor (let depIdx = 0; depIdx < allDeps.length; depIdx++) {\n\t\t\t\tconst dep = allDeps[depIdx];\n\t\t\t\tunsubs.push(\n\t\t\t\t\tdep.subscribe((msgs) => {\n\t\t\t\t\t\tfor (const msg of msgs) {\n\t\t\t\t\t\t\t_handleBudgetMessage(msg, depIdx, gateActions);\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tonDeactivation: () => {\n\t\t\t\t\tfor (const u of unsubs) u();\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\t{\n\t\t\t...opts,\n\t\t\tdescribeKind: \"derived\",\n\t\t\tmeta: domainMeta(\"resilience\", \"budget_gate\", opts?.meta),\n\t\t} as NodeOptions<T>,\n\t);\n\n\treturn { node: out, budgetGateState };\n\n\tfunction _handleBudgetMessage(msg: Message, depIndex: number, actions: NodeActions): boolean {\n\t\tconst t = msg[0];\n\n\t\t// Source messages (dep 0)\n\t\tif (depIndex === 0) {\n\t\t\tif (t === DATA) {\n\t\t\t\tif (checkBudget() && buffer.size === 0) {\n\t\t\t\t\tactions.emit(msg[1] as T);\n\t\t\t\t} else {\n\t\t\t\t\tbuffer.push(msg[1] as T);\n\t\t\t\t\tif (!paused) {\n\t\t\t\t\t\tpaused = true;\n\t\t\t\t\t\tactions.up([[PAUSE, lockId]]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (t === DIRTY) {\n\t\t\t\tactions.down([[DIRTY]]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (t === RESOLVED) {\n\t\t\t\tif (buffer.size === 0) {\n\t\t\t\t\tactions.down([[RESOLVED]]);\n\t\t\t\t} else {\n\t\t\t\t\t// Buffer non-empty: defer RESOLVED until buffer drains (invariant 3).\n\t\t\t\t\tpendingResolved = true;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (t === COMPLETE || t === ERROR) {\n\t\t\t\t// Invariant 1: terminal force-flush. Drain every buffered item\n\t\t\t\t// downstream BEFORE forwarding the terminal — bypass the constraint\n\t\t\t\t// since \"upstream done\" must not lose buffered work.\n\t\t\t\twhile (buffer.size > 0) {\n\t\t\t\t\tactions.emit(buffer.shift()!);\n\t\t\t\t}\n\t\t\t\tpendingResolved = false;\n\t\t\t\t// Release PAUSE lock before forwarding terminal so upstream sees a\n\t\t\t\t// clean release rather than a still-paused terminal.\n\t\t\t\tif (paused) {\n\t\t\t\t\tpaused = false;\n\t\t\t\t\tactions.up([[RESUME, lockId]]);\n\t\t\t\t}\n\t\t\t\tactions.down([msg]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// Constraint node messages (dep 1+): capture DATA then re-check budget\n\t\tif (t === DATA) {\n\t\t\tlatestValues[depIndex - 1] = msg[1];\n\t\t}\n\t\tif (t === DATA || t === RESOLVED) {\n\t\t\t// qa A2: hoist `checkBudget()` to a local — both branches consult it\n\t\t\t// and `c.check(value)` may be expensive or non-pure (closes over time,\n\t\t\t// counters, etc.); calling it twice was a 2× cost amplifier and an\n\t\t\t// inconsistency risk if the predicate flips between calls.\n\t\t\t//\n\t\t\t// qa A3: each constraint's `c.check(latestValues[i])` runs against\n\t\t\t// the constraint's last cached value. If a constraint's cache is\n\t\t\t// `undefined` (constraint Node hasn't emitted DATA yet OR was\n\t\t\t// activated before any push-on-subscribe), the predicate sees\n\t\t\t// `undefined`. Treat undefined as \"constraint not ready ⇒ closed\"\n\t\t\t// (conservative — don't release the gate on incomplete state).\n\t\t\tconst ok = checkBudget();\n\t\t\tif (ok && buffer.size > 0) {\n\t\t\t\t// Invariant 2: drain FIFO downstream BEFORE releasing PAUSE upstream.\n\t\t\t\tflushBuffer(actions);\n\t\t\t\tif (buffer.size === 0 && paused) {\n\t\t\t\t\tpaused = false;\n\t\t\t\t\tactions.up([[RESUME, lockId]]);\n\t\t\t\t}\n\t\t\t} else if (!ok && !paused && buffer.size > 0) {\n\t\t\t\t// Defensive — buffer.size > 0 implies paused=true under normal flow\n\t\t\t\t// (a buffered source DATA always sets paused). Kept for clarity if\n\t\t\t\t// invariants ever shift.\n\t\t\t\tpaused = true;\n\t\t\t\tactions.up([[PAUSE, lockId]]);\n\t\t\t}\n\t\t\t// DS-13.5.B: re-publish gate state on constraint update.\n\t\t\tif (t === DATA) publishState();\n\t\t\treturn true;\n\t\t}\n\t\tif (t === DIRTY) {\n\t\t\t// Invariant 4: constraint DIRTY does not propagate downstream.\n\t\t\treturn true;\n\t\t}\n\t\tif (t === ERROR) {\n\t\t\t// Constraint error → forward downstream\n\t\t\tactions.down([msg]);\n\t\t\treturn true;\n\t\t}\n\t\tif (t === COMPLETE) {\n\t\t\t// Constraint completed — locked at last value, no-op\n\t\t\treturn true;\n\t\t}\n\t\t// Unknown constraint types → default forwarding\n\t\treturn false;\n\t}\n}\n","/**\n * Fallback — replace upstream ERROR with a static or computed source.\n *\n * Accepts scalar / `Node` / `PromiseLike` / `AsyncIterable` fallbacks; non-Node\n * inputs are routed through `fromAny` so the fallback participates in the\n * reactive protocol uniformly.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\tfactoryTag,\n\ttype Node,\n\tnode,\n\tRESOLVED,\n\tTEARDOWN,\n} from \"@graphrefly/pure-ts/core\";\nimport { fromAny } from \"@graphrefly/pure-ts/extra\";\nimport {\n\tisAsyncIterable,\n\tisNode,\n\tisThenable,\n\toperatorOpts,\n} from \"../../base/resilience/_internal.js\";\n\n/** Inputs accepted by {@link fallback}. */\nexport type FallbackInput<T> = T | Node<T> | PromiseLike<T> | AsyncIterable<T>;\n\n/**\n * On upstream terminal `ERROR`, switch to a fallback source instead of propagating the error.\n *\n * Accepts any of:\n * - **scalar value** — emits `[[DATA, fb], [COMPLETE]]`\n * - **`Node<T>`** — subscribes and forwards all messages (push-on-subscribe delivers current cache)\n * - **`Promise<T>` / thenable** — resolves into a one-shot `DATA` then `COMPLETE` (via {@link fromAny})\n * - **`AsyncIterable<T>`** — streams each yielded value as `DATA`, then `COMPLETE` (via {@link fromAny})\n *\n * Non-`Node` inputs are routed through {@link fromAny} so the fallback participates in the\n * reactive protocol uniformly. Bare strings, arrays, and other synchronous scalars are treated\n * as single values (NOT split into characters / elements) to avoid the `fromAny`-on-string\n * iteration gotcha.\n *\n * Composes naturally with {@link retry}:\n * `pipe(source, retry({count:3}), fallback(\"default\"))`.\n *\n * @param source - Upstream node.\n * @param fb - Fallback value, node, promise, or async iterable.\n * @returns Node that replaces errors with the fallback.\n *\n * @example\n * ```ts\n * import { fallback, throwError } from \"@graphrefly/graphrefly-ts\";\n *\n * const safe = fallback(throwError(new Error(\"boom\")), \"default\");\n * safe.cache; // \"default\" after subscribe\n * ```\n *\n * @category extra\n */\nexport function fallback<T>(\n\tsource: Node<T>,\n\tfb: FallbackInput<T>,\n\toptions?: { meta?: Record<string, unknown> },\n): Node<T> {\n\tconst callerMeta = options?.meta;\n\treturn node<T>(\n\t\t(_data, a) => {\n\t\t\tlet fallbackUnsub: (() => void) | undefined;\n\t\t\tlet sourceUnsub: (() => void) | undefined;\n\n\t\t\tfunction switchToFallback(): void {\n\t\t\t\tsourceUnsub?.();\n\t\t\t\tsourceUnsub = undefined;\n\t\t\t\tif (isNode(fb) || isThenable(fb) || isAsyncIterable(fb)) {\n\t\t\t\t\tconst fbNode = fromAny(fb as Node<T> | PromiseLike<T> | AsyncIterable<T>);\n\t\t\t\t\tfallbackUnsub = fbNode.subscribe((fMsgs) => {\n\t\t\t\t\t\ta.down(fMsgs);\n\t\t\t\t\t\t// qa A14: clear fallbackUnsub on terminal so the teardown\n\t\t\t\t\t\t// closure doesn't double-call it. Idempotency of\n\t\t\t\t\t\t// fromAny's unsub is implementation-defined; explicit\n\t\t\t\t\t\t// self-clear is safer.\n\t\t\t\t\t\tfor (const fm of fMsgs) {\n\t\t\t\t\t\t\tconst ft = fm[0];\n\t\t\t\t\t\t\tif (ft === COMPLETE || ft === ERROR || ft === TEARDOWN) {\n\t\t\t\t\t\t\t\tfallbackUnsub = undefined;\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\ta.emit(fb as T);\n\t\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsourceUnsub = source.subscribe((msgs) => {\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tconst t = m[0];\n\t\t\t\t\tif (t === DIRTY) a.down([[DIRTY]]);\n\t\t\t\t\telse if (t === DATA) a.emit(m[1] as T);\n\t\t\t\t\telse if (t === RESOLVED) a.down([[RESOLVED]]);\n\t\t\t\t\telse if (t === COMPLETE) a.down([[COMPLETE]]);\n\t\t\t\t\telse if (t === ERROR) {\n\t\t\t\t\t\tswitchToFallback();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else if (t === TEARDOWN) {\n\t\t\t\t\t\tfallbackUnsub?.();\n\t\t\t\t\t\ta.down([m]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else a.down([m]);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tonDeactivation: () => {\n\t\t\t\t\tsourceUnsub?.();\n\t\t\t\t\tfallbackUnsub?.();\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\t{\n\t\t\t...operatorOpts(),\n\t\t\tinitial: source.cache,\n\t\t\tmeta: { ...(callerMeta ?? {}), ...factoryTag(\"fallback\") },\n\t\t},\n\t);\n}\n","/**\n * Rate limiters — `tokenBucket` (raw meter), `rateLimiter` (operator with\n * bounded queue + reactive backpressure companions), and the re-export of\n * `adaptiveRateLimiter` from its standalone module.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tDIRTY,\n\tERROR,\n\tfactoryTag,\n\tmonotonicNs,\n\ttype Node,\n\tnode,\n\tRESOLVED,\n\tResettableTimer,\n\tRingBuffer,\n\tTEARDOWN,\n} from \"@graphrefly/pure-ts/core\";\nimport {\n\tisNode,\n\ttype NodeOrValue,\n\toperatorOpts,\n\tresolveReactiveOption,\n} from \"../../base/resilience/_internal.js\";\nimport { NS_PER_MS, NS_PER_SEC } from \"../../base/resilience/backoff.js\";\nimport type { GateState } from \"./gate-state.js\";\n\n// `adaptiveRateLimiter` lives in extra/adaptive-rate-limiter.ts (kept independent\n// because it has its own internal control-loop machinery).\nexport * from \"./adaptive-rate-limiter.js\";\n\nexport interface TokenBucket {\n\t/**\n\t * Number of tokens currently available (after refill).\n\t *\n\t * **Float-valued.** When `refillPerSecond` is fractional (or `capacity` × elapsed-fraction\n\t * yields a non-integer), the bucket accumulates fractional refill credit between\n\t * `tryConsume`s. Consumers should not assume integer tokens — e.g. with\n\t * `tokenBucket(10, 2.5)` after 100ms of elapsed time `available()` may report `0.25`.\n\t */\n\tavailable(): number;\n\t/** Try to consume `cost` tokens. Returns `true` if successful. */\n\ttryConsume(cost?: number): boolean;\n\t/**\n\t * Return `cost` tokens to the bucket (capped at capacity). Used when a\n\t * multi-bucket admission fails partway — e.g., `adaptiveRateLimiter`\n\t * consumes from an rpm bucket, then a tpm bucket; if tpm fails, call\n\t * `rpmBucket.putBack(requestCost)` so the rpm slot isn't wasted.\n\t * No-op for non-positive `cost`.\n\t */\n\tputBack(cost?: number): void;\n}\n\n/** Optional configuration for {@link tokenBucket}. */\nexport interface TokenBucketOptions {\n\t/**\n\t * Clock function returning **nanoseconds** with `monotonicNs()` semantics\n\t * (monotonically non-decreasing). Default: `monotonicNs` from `core/clock`.\n\t * Override for deterministic tests — eliminates the need for `vi.useFakeTimers`\n\t * to drive token-refill scheduling.\n\t */\n\tclock?: () => number;\n}\n\n/**\n * Token-bucket meter (capacity + refill rate per second). Use with {@link rateLimiter} or custom gates.\n *\n * @param capacity - Maximum tokens (must be positive).\n * @param refillPerSecond - Tokens added per elapsed second (non-negative; may be fractional).\n * @param opts - Optional `clock` override for deterministic testing.\n * @returns {@link TokenBucket} instance.\n *\n * @remarks\n * **Float behavior:** the internal token counter is float-valued — fractional refill\n * accumulates between `tryConsume` calls. See {@link TokenBucket.available} for caveats.\n *\n * **Clock injection:** pass `opts.clock` to drive refill scheduling deterministically\n * in tests. The contract matches {@link circuitBreaker}'s `now` option: must return\n * `monotonicNs()`-style nanoseconds, never `Date.now()` (wall-clock skew breaks\n * elapsed math).\n *\n * @example\n * ```ts\n * import { tokenBucket } from \"@graphrefly/graphrefly-ts\";\n *\n * const bucket = tokenBucket(10, 2); // capacity 10, refill 2 tokens/sec\n * bucket.tryConsume(3); // true — 7 tokens remaining\n * bucket.available(); // ~7 (plus any elapsed refill — float-valued)\n *\n * // Deterministic test:\n * let t = 0;\n * const tb = tokenBucket(5, 1, { clock: () => t });\n * tb.tryConsume(5); // exhausts\n * t = 1_000_000_000; // advance 1s → +1 refill\n * tb.tryConsume(1); // true\n * ```\n *\n * @category extra\n */\nexport function tokenBucket(\n\tcapacity: number,\n\trefillPerSecond: number,\n\topts?: TokenBucketOptions,\n): TokenBucket {\n\tif (capacity <= 0) throw new RangeError(\"capacity must be > 0\");\n\tif (refillPerSecond < 0) throw new RangeError(\"refillPerSecond must be >= 0\");\n\n\tconst clock = opts?.clock ?? monotonicNs;\n\n\tlet tokens = capacity;\n\tlet updatedAt = clock();\n\n\tfunction refill(now: number): void {\n\t\tif (refillPerSecond > 0) {\n\t\t\tconst elapsedNs = now - updatedAt;\n\t\t\ttokens = Math.min(capacity, tokens + (elapsedNs / NS_PER_SEC) * refillPerSecond);\n\t\t}\n\t\tupdatedAt = now;\n\t}\n\n\treturn {\n\t\tavailable(): number {\n\t\t\trefill(clock());\n\t\t\treturn tokens;\n\t\t},\n\t\ttryConsume(cost = 1): boolean {\n\t\t\tif (cost <= 0) return true;\n\t\t\tconst now = clock();\n\t\t\trefill(now);\n\t\t\tif (tokens >= cost) {\n\t\t\t\ttokens -= cost;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\tputBack(cost = 1): void {\n\t\t\tif (cost <= 0) return;\n\t\t\trefill(clock());\n\t\t\ttokens = Math.min(capacity, tokens + cost);\n\t\t},\n\t};\n}\n\nexport type RateLimiterOverflowPolicy = \"drop-oldest\" | \"drop-newest\" | \"error\";\n\nexport type RateLimiterOptions = {\n\t/** Maximum `DATA` emissions per window (must be > 0). */\n\tmaxEvents: number;\n\t/** Window length in nanoseconds (must be > 0). */\n\twindowNs: number;\n\t/**\n\t * Cap on items queued while waiting for token refill.\n\t *\n\t * **Required.** Pass a finite positive integer (>= 1) for a bounded queue, OR\n\t * the literal `Infinity` to opt in to an unbounded queue (caller acknowledges\n\t * the unbounded-memory-growth risk on a high-rate source). Omitting this\n\t * throws at construction time — the silent-unbounded-buffer footgun is the\n\t * most common rateLimiter mis-configuration.\n\t */\n\tmaxBuffer: number;\n\t/** Overflow policy when `maxBuffer` is exceeded. Default: `\"drop-newest\"`. */\n\tonOverflow?: RateLimiterOverflowPolicy;\n\t/**\n\t * Caller-supplied metadata merged into the produced node's `meta` (Tier 5.2\n\t * D8 widening). Use {@link domainMeta} to tag the layer for `describe()` /\n\t * mermaid grouping (e.g. `domainMeta(\"resilient\", \"rate-limit\")`). The\n\t * primitive's own `factoryTag(\"rateLimiter\", opts)` and the `droppedCount`\n\t * / `rateLimitState` companion seeds always win against caller-supplied\n\t * keys so the audit trail can't be silently overwritten.\n\t */\n\tmeta?: Record<string, unknown>;\n};\n\n/**\n * Thrown by {@link rateLimiter} when `onOverflow: \"error\"` and the pending buffer is full.\n *\n * @category extra\n */\nexport class RateLimiterOverflowError extends Error {\n\toverride name = \"RateLimiterOverflowError\";\n\tconstructor(maxBuffer: number) {\n\t\tsuper(`rateLimiter buffer overflow (maxBuffer=${maxBuffer})`);\n\t}\n}\n\n/**\n * Combined runtime state surfaced by {@link rateLimiter} alongside `droppedCount`.\n * Tier 5.2 D7 widening — exposes pending-buffer occupancy and a `paused`\n * flag so consumers can render backpressure (UI), feed `lens.health`, or\n * gate downstream effects.\n */\n/**\n * Lifecycle-shaped state companion emitted by {@link rateLimiter}.\n *\n * **DS-13.5.B widening (2026-05-01).** `status` extends {@link GateState}\n * with `\"throttled\"` (= `paused === true`). Pre-1.0 break vs the prior\n * shape (which omitted `status`).\n *\n * - `\"open\"` — passing through (no buffering, no recent overflow drops).\n * - `\"throttled\"` — at least one item queued awaiting a token refill.\n * - `\"closed\"` — reserved for future terminal lifecycle reporting.\n */\nexport type RateLimiterState = {\n\t/** DS-13.5.B status field — `\"open\" | \"closed\" | \"throttled\"`. */\n\tstatus: GateState | \"throttled\";\n\t/** Cumulative `DATA` items dropped due to overflow since this subscription cycle started. */\n\tdroppedCount: number;\n\t/** Items currently buffered awaiting a token refill. `0` when the limiter is passing through. */\n\tpendingCount: number;\n\t/** `true` when at least one item is queued (the limiter is actively throttling). */\n\tpaused: boolean;\n};\n\nfunction rateLimiterStateEqual(a: RateLimiterState, b: RateLimiterState): boolean {\n\treturn (\n\t\ta.status === b.status &&\n\t\ta.droppedCount === b.droppedCount &&\n\t\ta.pendingCount === b.pendingCount &&\n\t\ta.paused === b.paused\n\t);\n}\n\nconst RATE_LIMITER_INITIAL_STATE: RateLimiterState = Object.freeze({\n\tstatus: \"open\" as const,\n\tdroppedCount: 0,\n\tpendingCount: 0,\n\tpaused: false,\n});\n\n/** Bundle returned by {@link rateLimiter}. */\nexport type RateLimiterBundle<T> = {\n\t/** The throttled stream — at most `maxEvents` `DATA` per `windowNs`. */\n\tnode: Node<T>;\n\t/**\n\t * Reactive companion: count of `DATA` items dropped since the producer\n\t * activated.\n\t *\n\t * - Increments on every drop under any overflow policy (`drop-newest`,\n\t * `drop-oldest`). The `error` policy terminates the stream after a single\n\t * overflow, so `droppedCount` increments at most once in that path.\n\t * - **Lifecycle scoping (qa A1 + EC7):** the counter retains its final\n\t * value through terminal (`COMPLETE` / `ERROR` / `TEARDOWN`) so consumers\n\t * see the final drop count, not zero. The closure-held counter resets to\n\t * `0` only when the producer fn re-runs — which only happens on a new\n\t * subscription cycle, and only if the producer was constructed with\n\t * `resubscribable: true`. The default `rateLimiter` producer is NOT\n\t * resubscribable, so a single producer-fn run is the typical lifetime.\n\t * - Producer-pattern note: this companion is invisible to `describe()`\n\t * traversal from `node` (effect-mirror limitation; same shape as\n\t * `withBreaker.breakerState` and `withStatus.status`). Surface it via\n\t * `node.meta.droppedCount` if you need it in topology snapshots.\n\t */\n\tdroppedCount: Node<number>;\n\t/**\n\t * Reactive companion: combined `{droppedCount, pendingCount, paused}` view.\n\t *\n\t * - `pendingCount` reflects the live buffer occupancy and updates on every\n\t * push / shift / overflow drop; `paused` is shorthand for\n\t * `pendingCount > 0`.\n\t * - Equality-deduped — re-emits only when one of the three fields actually\n\t * changes (so a busy steady-state where every DATA passes immediately\n\t * produces one `paused: false` emission, not one per DATA).\n\t * - **Lifecycle scoping (qa EC7):** same contract as `droppedCount` —\n\t * retains its final value through terminal; resets to the initial\n\t * `{droppedCount: 0, pendingCount: 0, paused: false}` only on a new\n\t * producer-fn run (resubscribable upstream required).\n\t * - Same producer-pattern caveat as `droppedCount` re: `describe()` visibility.\n\t */\n\trateLimitState: Node<RateLimiterState>;\n};\n\n/** Shared opts validation gate — used by both the static-form and the reactive-swap paths. */\nfunction validateRateLimiterOpts(o: RateLimiterOptions): void {\n\tif (o.maxEvents <= 0) throw new RangeError(\"maxEvents must be > 0\");\n\tif (o.windowNs <= 0) throw new RangeError(\"windowNs must be > 0\");\n\tif (o.maxBuffer === undefined) {\n\t\tthrow new RangeError(\n\t\t\t\"rateLimiter requires explicit maxBuffer (use Infinity to opt in to unbounded)\",\n\t\t);\n\t}\n\tconst isUnbounded = o.maxBuffer === Infinity;\n\tif (!isUnbounded && (!Number.isInteger(o.maxBuffer) || o.maxBuffer < 1)) {\n\t\tthrow new RangeError(\"maxBuffer must be a positive integer (or Infinity for unbounded)\");\n\t}\n}\n\n/**\n * Resolve the construction-time lock value for reactive (Node-form) opts.\n * Throws (D4) when the opts Node has no cached value — the mode + initial\n * cap are locked here and the swap handler rejects mode toggles, so a silent\n * bounded-`maxBuffer:1` default would be an undebuggable footgun.\n */\nfunction resolveInitialReactiveOpts(optsNode: Node<RateLimiterOptions>): RateLimiterOptions {\n\tconst cached = optsNode.cache as RateLimiterOptions | undefined;\n\tif (cached === undefined) {\n\t\tthrow new RangeError(\n\t\t\t\"rateLimiter: reactive (Node-form) opts must carry a cached value at \" +\n\t\t\t\t\"construction — seed the opts Node with `initial`. Mode (bounded vs \" +\n\t\t\t\t\"unbounded) and the initial cap are LOCKED from `.cache` at construction \" +\n\t\t\t\t\"and the swap handler rejects mode toggles; an un-seeded Node would \" +\n\t\t\t\t\"silently lock bounded `maxBuffer: 1` (D4).\",\n\t\t);\n\t}\n\treturn cached;\n}\n\n/**\n * Token-bucket rate limiter: at most `maxEvents` `DATA` values per `windowNs`.\n *\n * Uses {@link tokenBucket} internally (capacity = `maxEvents`, refill = `maxEvents / windowSeconds`).\n * Excess items are queued FIFO (in a fixed-capacity {@link RingBuffer} for O(1) push/shift)\n * until a token is available. The queue is bounded by the **required** `maxBuffer` option\n * with a configurable overflow policy.\n *\n * @param source - Upstream node.\n * @param opts - Rate + bounded-buffer configuration. `maxBuffer` is required (use `Infinity` to opt in to unbounded).\n * @returns `{ node, droppedCount }` bundle. Subscribe to `node` for the throttled stream and to `droppedCount` for backpressure pressure.\n *\n * @throws {RangeError} when `maxEvents` / `windowNs` is non-positive, when\n * `maxBuffer` is omitted, when `maxBuffer` is a finite value < 1, or when\n * `opts` is a Node (reactive form) with **no cached value at construction**\n * (D4 — the mode + initial cap are locked from `.cache` at construction;\n * seed the opts Node with `initial`).\n *\n * @remarks\n * **Reactive opts (Node form) must be seeded.** Mode (bounded vs unbounded)\n * and the initial cap are read from `opts.cache` at construction and the swap\n * handler rejects mode toggles. An un-seeded opts Node therefore throws at\n * construction rather than silently locking bounded `maxBuffer: 1` (D4).\n *\n * **Terminal:** `COMPLETE` / `ERROR` cancel the refill timer, drop the pending queue,\n * reset `droppedCount` to `0`, and propagate.\n *\n * @example\n * ```ts\n * import { rateLimiter, state, NS_PER_SEC } from \"@graphrefly/graphrefly-ts\";\n *\n * const src = state(0);\n * // Allow at most 5 DATA values per second; queue up to 100 excess items, drop newest beyond.\n * const { node: limited, droppedCount } = rateLimiter(src, {\n * maxEvents: 5,\n * windowNs: NS_PER_SEC,\n * maxBuffer: 100,\n * });\n * droppedCount.subscribe(([m]) => console.log(\"dropped so far:\", m[1]));\n * ```\n *\n * @category extra\n */\nexport function rateLimiter<T>(\n\tsource: Node<T>,\n\topts: NodeOrValue<RateLimiterOptions>,\n): RateLimiterBundle<T> {\n\t// Mode (bounded vs unbounded) + the initial cap are LOCKED at construction\n\t// from the resolved opts (Tier 6.5 3.2.3 swap rule — runtime swaps change\n\t// the cap WITHIN the same mode; toggling bounded↔unbounded requires\n\t// re-mounting). For reactive (Node-form) opts the lock is read from\n\t// `.cache` at construction — so an un-seeded opts Node is a hard error\n\t// (D4, relaxed 2026-05-18): silently defaulting to bounded `maxBuffer: 1`\n\t// and then no-op-ing every mode-changing emit is the worst kind of\n\t// footgun. Fail loud — seed the opts Node with `initial`. The resolved\n\t// value is validated by the same gate static-form opts use; reactive\n\t// *swaps* still re-validate softly in the producer body (a bad runtime\n\t// emit keeps the previous values rather than throwing into the dataplane).\n\tconst isReactive = isNode(opts);\n\tconst initialOpts: RateLimiterOptions = isReactive\n\t\t? resolveInitialReactiveOpts(opts as Node<RateLimiterOptions>)\n\t\t: (opts as RateLimiterOptions);\n\tvalidateRateLimiterOpts(initialOpts);\n\tconst initialMaxBuffer = initialOpts.maxBuffer;\n\tconst isUnbounded = initialMaxBuffer === Infinity;\n\n\tconst out = node<T>(\n\t\t(_data, a) => {\n\t\t\t// Mutable closure-state — replaced on each option swap. `initialOpts`\n\t\t\t// is fully resolved + validated at construction (D4), so these reads\n\t\t\t// are unconditional (no `?? placeholder` fallback).\n\t\t\tlet maxEvents = initialOpts.maxEvents;\n\t\t\tlet windowNs = initialOpts.windowNs;\n\t\t\tlet maxBuffer = initialMaxBuffer;\n\t\t\tlet onOverflow: RateLimiterOverflowPolicy = initialOpts.onOverflow ?? \"drop-newest\";\n\t\t\tlet refillPerSec = (maxEvents * NS_PER_SEC) / windowNs;\n\t\t\tlet tokenTimeNs = NS_PER_SEC / refillPerSec;\n\t\t\tlet bucket = tokenBucket(maxEvents, refillPerSec);\n\n\t\t\t// RingBuffer for O(1) push + shift. Unbounded mode falls back to a plain\n\t\t\t// array (RingBuffer requires a positive integer capacity); the caller\n\t\t\t// explicitly opted in via `maxBuffer: Infinity` and accepts the cost.\n\t\t\t// Bounded mode allocates with the INITIAL `maxBuffer`; runtime cap\n\t\t\t// reductions enforce drop-oldest at push time without resizing the ring.\n\t\t\tconst pending: { push: (v: T) => void; shift: () => T | undefined; size: number } =\n\t\t\t\tisUnbounded ? makeArrayQueue<T>() : ringBufferQueue<T>(Math.max(1, maxBuffer));\n\t\t\tconst timer = new ResettableTimer();\n\t\t\tlet terminated = false;\n\t\t\tlet dropped = 0;\n\n\t\t\t// Mirror the dropped counter + combined state to the meta companions.\n\t\t\t// The `emit` call is the same subscribe-callback effect-mirror\n\t\t\t// pattern used by `withBreaker.breakerState` / `withStatus.status`\n\t\t\t// (sanctioned per audit § F.7).\n\t\t\tconst droppedNode = out.meta.droppedCount;\n\t\t\tconst stateNode = out.meta.rateLimitState;\n\t\t\tlet lastState: RateLimiterState = RATE_LIMITER_INITIAL_STATE;\n\t\t\tfunction syncState(): void {\n\t\t\t\tdroppedNode.emit(dropped);\n\t\t\t\tconst isPaused = pending.size > 0;\n\t\t\t\tconst next: RateLimiterState = {\n\t\t\t\t\tstatus: isPaused ? \"throttled\" : \"open\",\n\t\t\t\t\tdroppedCount: dropped,\n\t\t\t\t\tpendingCount: pending.size,\n\t\t\t\t\tpaused: isPaused,\n\t\t\t\t};\n\t\t\t\t// Equality-dedup at the emit boundary so steady-state pass-through\n\t\t\t\t// (every DATA passes immediately — pendingCount stays 0, paused\n\t\t\t\t// stays false) doesn't generate one state DATA per source DATA.\n\t\t\t\tif (!rateLimiterStateEqual(lastState, next)) {\n\t\t\t\t\tlastState = next;\n\t\t\t\t\tstateNode.emit(next);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Reset for this subscription cycle — `dropped` is the closure\n\t\t\t// variable (already 0 at construction); `pending.size` is also 0\n\t\t\t// (fresh queue per producer activation). The companion Node caches\n\t\t\t// may still hold a prior cycle's terminal values, so re-emit the\n\t\t\t// initial state explicitly.\n\t\t\tlastState = RATE_LIMITER_INITIAL_STATE;\n\t\t\tdroppedNode.emit(0);\n\t\t\tstateNode.emit(RATE_LIMITER_INITIAL_STATE);\n\n\t\t\t// Tier 6.5 3.2.3 (2026-04-29): reactive option swap handler.\n\t\t\t// Locked semantics: `maxEvents`/`windowNs` swap rebuilds the\n\t\t\t// token bucket at the next refill window (tokens reset to new\n\t\t\t// capacity, refill rate updates immediately). `maxBuffer` shrink\n\t\t\t// drops oldest pending entries until size ≤ new cap. `onOverflow`\n\t\t\t// swap takes effect at the next overflow check. Mode toggling\n\t\t\t// (bounded ↔ unbounded) is NOT supported — locked at construction.\n\t\t\tconst optMirror = resolveReactiveOption<RateLimiterOptions>(\n\t\t\t\topts as NodeOrValue<RateLimiterOptions>,\n\t\t\t\t(next) => {\n\t\t\t\t\tif (terminated) return;\n\t\t\t\t\tif (next == null) return;\n\t\t\t\t\t// QA A9 (2026-05-03): explicit empty `{}` short-circuit\n\t\t\t\t\t// for symmetry with timeout / retry / circuitBreaker\n\t\t\t\t\t// (DS-13.5.B locked rule: empty `{}` is a no-op — no\n\t\t\t\t\t// rebind, no companion fire). Pre-fix, empty `{}` was\n\t\t\t\t\t// implicitly a no-op via the validation gate's\n\t\t\t\t\t// `next.maxEvents > 0` check on `undefined`; this\n\t\t\t\t\t// makes the rule explicit and resilient to future\n\t\t\t\t\t// validation refactors.\n\t\t\t\t\tif (typeof next === \"object\" && Object.keys(next).length === 0) return;\n\t\t\t\t\t// Validate; if invalid, keep previous values (no throw into dataplane).\n\t\t\t\t\tif (!(next.maxEvents > 0) || !(next.windowNs > 0)) return;\n\t\t\t\t\tconst nextBuf = next.maxBuffer;\n\t\t\t\t\tif (nextBuf === undefined) return;\n\t\t\t\t\tconst nextUnbounded = nextBuf === Infinity;\n\t\t\t\t\tif (nextUnbounded !== isUnbounded) {\n\t\t\t\t\t\t// Mode toggle not supported — skip silently. Caller using\n\t\t\t\t\t\t// reactive opts must keep maxBuffer in the same mode.\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!nextUnbounded && (!Number.isInteger(nextBuf) || nextBuf < 1)) return;\n\n\t\t\t\t\t// qa F-C (Tier 5 /qa pass, 2026-04-29): reactive `maxBuffer`\n\t\t\t\t\t// is monotonically non-increasing. The pending RingBuffer is\n\t\t\t\t\t// allocated once at construction; growing the cap reactively\n\t\t\t\t\t// would let the overflow check pass more pushes than the\n\t\t\t\t\t// ring's capacity → silent drop-oldest at the substrate level\n\t\t\t\t\t// (RingBuffer.push wraps), bypassing our `dropped` counter\n\t\t\t\t\t// and `onOverflow: \"error\"` arm. Reject grow swaps with a\n\t\t\t\t\t// console.warn and keep the previous cap. Shrink stays\n\t\t\t\t\t// supported (drop-oldest below).\n\t\t\t\t\tif (!nextUnbounded && nextBuf > maxBuffer) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`rateLimiter: reactive maxBuffer grow (${maxBuffer} → ${nextBuf}) ` +\n\t\t\t\t\t\t\t\t\"rejected. The pending ring buffer is allocated at construction; \" +\n\t\t\t\t\t\t\t\t\"reactive maxBuffer is monotonically non-increasing. Recreate \" +\n\t\t\t\t\t\t\t\t\"the rateLimiter with the larger cap if growth is required.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tmaxEvents = next.maxEvents;\n\t\t\t\t\twindowNs = next.windowNs;\n\t\t\t\t\tmaxBuffer = nextBuf;\n\t\t\t\t\tonOverflow = next.onOverflow ?? \"drop-newest\";\n\t\t\t\t\trefillPerSec = (maxEvents * NS_PER_SEC) / windowNs;\n\t\t\t\t\ttokenTimeNs = NS_PER_SEC / refillPerSec;\n\t\t\t\t\t// Rebuild bucket — tokens snap to new capacity. The old refill\n\t\t\t\t\t// timer continues to fire `tryEmit` which will use the new\n\t\t\t\t\t// bucket (same closure variable).\n\t\t\t\t\tbucket = tokenBucket(maxEvents, refillPerSec);\n\n\t\t\t\t\t// Drop-oldest until pending.size <= maxBuffer (bounded only).\n\t\t\t\t\tif (!nextUnbounded) {\n\t\t\t\t\t\twhile (pending.size > maxBuffer) {\n\t\t\t\t\t\t\tpending.shift();\n\t\t\t\t\t\t\tdropped += 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tsyncState();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tfunction tryEmit(): void {\n\t\t\t\twhile (pending.size > 0) {\n\t\t\t\t\tif (bucket.tryConsume(1)) {\n\t\t\t\t\t\ta.emit(pending.shift() as T);\n\t\t\t\t\t\tsyncState();\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Wait one full token-refill interval. Avoids calling bucket.available()\n\t\t\t\t\t\t// which would advance the internal refill clock and steal fractional credit.\n\t\t\t\t\t\t// §5.10: setTimeout (not fromTimer) — refill-delay scheduling needs clearTimeout/setTimeout;\n\t\t\t\t\t\t// fromTimer creates a new Node per reset, adding lifecycle overhead per retry.\n\t\t\t\t\t\ttimer.start(Math.max(1, tokenTimeNs / NS_PER_MS), tryEmit);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction recordDrop(): void {\n\t\t\t\tdropped += 1;\n\t\t\t\tsyncState();\n\t\t\t}\n\n\t\t\tfunction resetForTerminal(): void {\n\t\t\t\tterminated = true;\n\t\t\t\ttimer.cancel();\n\t\t\t\t// RingBuffer.clear-equivalent: drain remaining slots so refs GC.\n\t\t\t\twhile (pending.size > 0) pending.shift();\n\t\t\t\t// qa A1: companions retain their last-emitted DATA value\n\t\t\t\t// through terminal (consumer sees the final drop count, not 0).\n\t\t\t\t// The closure-held `dropped` resets to 0 so a re-subscribe\n\t\t\t\t// cycle starts fresh; the activation block above re-emits\n\t\t\t\t// `RATE_LIMITER_INITIAL_STATE` at that point.\n\t\t\t\tdropped = 0;\n\t\t\t}\n\n\t\t\tconst unsub = source.subscribe((msgs) => {\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (terminated) return;\n\t\t\t\t\tconst t = m[0];\n\t\t\t\t\tif (t === DIRTY) a.down([[DIRTY]]);\n\t\t\t\t\telse if (t === DATA) {\n\t\t\t\t\t\tif (!isUnbounded && pending.size >= maxBuffer) {\n\t\t\t\t\t\t\tif (onOverflow === \"drop-newest\") {\n\t\t\t\t\t\t\t\trecordDrop();\n\t\t\t\t\t\t\t} else if (onOverflow === \"drop-oldest\") {\n\t\t\t\t\t\t\t\tpending.shift();\n\t\t\t\t\t\t\t\tpending.push(m[1] as T);\n\t\t\t\t\t\t\t\trecordDrop();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\trecordDrop();\n\t\t\t\t\t\t\t\tresetForTerminal();\n\t\t\t\t\t\t\t\ta.down([[ERROR, new RateLimiterOverflowError(maxBuffer)]]);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpending.push(m[1] as T);\n\t\t\t\t\t\t\tsyncState();\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttryEmit();\n\t\t\t\t\t} else if (t === RESOLVED) a.down([[RESOLVED]]);\n\t\t\t\t\telse if (t === COMPLETE) {\n\t\t\t\t\t\tresetForTerminal();\n\t\t\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t\t\t} else if (t === ERROR) {\n\t\t\t\t\t\tresetForTerminal();\n\t\t\t\t\t\ta.down([m]);\n\t\t\t\t\t} else if (t === TEARDOWN) {\n\t\t\t\t\t\tresetForTerminal();\n\t\t\t\t\t\ta.down([m]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else a.down([m]);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tonDeactivation: () => {\n\t\t\t\t\tterminated = true;\n\t\t\t\t\ttimer.cancel();\n\t\t\t\t\tunsub();\n\t\t\t\t\toptMirror.unsub();\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\t{\n\t\t\t...operatorOpts(),\n\t\t\tinitial: source.cache,\n\t\t\tmeta: {\n\t\t\t\t// Caller-supplied meta first; companion seeds + factoryTag\n\t\t\t\t// override below so they always win.\n\t\t\t\t...(isReactive ? {} : ((opts as RateLimiterOptions).meta ?? {})),\n\t\t\t\tdroppedCount: 0,\n\t\t\t\trateLimitState: RATE_LIMITER_INITIAL_STATE,\n\t\t\t\t...factoryTag(\"rateLimiter\", isReactive ? { reactiveOpts: true } : opts),\n\t\t\t},\n\t\t},\n\t);\n\n\treturn {\n\t\tnode: out,\n\t\tdroppedCount: out.meta.droppedCount as Node<number>,\n\t\trateLimitState: out.meta.rateLimitState as Node<RateLimiterState>,\n\t};\n}\n\n/**\n * RingBuffer-backed queue adapter — exposes the small `{ push, shift, size }`\n * shape rateLimiter needs without leaking the rest of `RingBuffer`'s API.\n */\nfunction ringBufferQueue<T>(capacity: number): {\n\tpush: (v: T) => void;\n\tshift: () => T | undefined;\n\tsize: number;\n} {\n\tconst buf = new RingBuffer<T>(capacity);\n\treturn {\n\t\tpush: (v: T) => buf.push(v),\n\t\tshift: () => buf.shift(),\n\t\tget size(): number {\n\t\t\treturn buf.size;\n\t\t},\n\t} as { push: (v: T) => void; shift: () => T | undefined; size: number };\n}\n\n/**\n * Plain-array fallback queue for `maxBuffer: Infinity`. Accepts the O(N) shift\n * cost — the caller opted in to unbounded growth.\n */\nfunction makeArrayQueue<T>(): {\n\tpush: (v: T) => void;\n\tshift: () => T | undefined;\n\tsize: number;\n} {\n\tconst arr: T[] = [];\n\treturn {\n\t\tpush: (v: T) => {\n\t\t\tarr.push(v);\n\t\t},\n\t\tshift: () => arr.shift(),\n\t\tget size(): number {\n\t\t\treturn arr.length;\n\t\t},\n\t} as { push: (v: T) => void; shift: () => T | undefined; size: number };\n}\n","/**\n * Adaptive rate limiter — reactive, live-tunable, 429-aware.\n *\n * Wraps two `tokenBucket` instances (requests, tokens) with:\n * - Reactive `rpm` / `tpm` knobs that can be re-tuned at runtime via `NodeInput<number>`.\n * - An adaptation signal input (`Node<RateLimitSignal>`) that feeds back\n * provider 429 / retry-after / x-ratelimit-* headers to tighten limits.\n * - A `clampCooldownMs` TTL on signal-induced caps so a transient 429 doesn't\n * permanently throttle — caps decay back to user-configured values after\n * the cooldown elapses.\n * - TPM-miss recovery: consumed RPM tokens are returned to the request\n * bucket when the TPM admit fails, via `TokenBucket.putBack`.\n * - Imperative `acquire()` for bridging to Promise-based call paths\n * (used by the `withRateLimiter` adapter middleware).\n *\n * **Timer policy:** sleeps use `ResettableTimer` (documented spec §5.10\n * escape hatch in `src/extra/timer.ts`) rather than `fromTimer` to avoid\n * allocating a new Node per acquire cycle.\n *\n * Design lives in `docs/optimizations.md` § \"Reactive adaptive rate limiter\".\n */\n\nimport { DATA, monotonicNs, type Node, node, ResettableTimer } from \"@graphrefly/pure-ts/core\";\nimport { fromAny, type NodeInput } from \"@graphrefly/pure-ts/extra\";\nimport { NS_PER_SEC } from \"../../base/resilience/backoff.js\";\nimport { type TokenBucket, tokenBucket } from \"./rate-limiter.js\";\n\n// ---------------------------------------------------------------------------\n// Signal shape\n// ---------------------------------------------------------------------------\n\n/**\n * Rate-limit signal emitted by an adaptation source (e.g., an HTTP 429 parser).\n *\n * Any subset of fields may be present. The adaptive rate limiter uses:\n * - `retryAfterMs` — blocks acquire() for this duration.\n * - `rpmCap` / `tpmCap` — tightens effective rpm/tpm to this value (decays\n * back to the user-configured cap after `clampCooldownMs`).\n * - `usageHint` — updates the last-known rpm/tpm usage ratio for logging.\n */\nexport interface RateLimitSignal {\n\t/** Throttle duration — pause acquire() for this long. */\n\tretryAfterMs?: number;\n\t/** Hard cap for requests-per-minute; effective rpm = min(current, rpmCap) while clamp is active. */\n\trpmCap?: number;\n\t/** Hard cap for tokens-per-minute; effective tpm = min(current, tpmCap) while clamp is active. */\n\ttpmCap?: number;\n\t/** Observed usage-percentage hint (0..1) — for observability, not gating. */\n\tusageHint?: { rpm?: number; tpm?: number };\n\t/** Free-form provider-specific payload. */\n\tmetadata?: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface AdaptiveRateLimiterOptions {\n\tname?: string;\n\t/** Effective requests-per-minute cap. Reactive — reads push-on-subscribe. */\n\trpm?: NodeInput<number>;\n\t/** Effective tokens-per-minute cap. Reactive. */\n\ttpm?: NodeInput<number>;\n\t/** Source of adaptation signals (429 parser output, etc.). */\n\tadaptation?: NodeInput<RateLimitSignal>;\n\t/**\n\t * How long (ms) a signal-induced `rpmCap` / `tpmCap` stays in effect before\n\t * relaxing back to the user-configured value. Default 60_000 (one minute).\n\t * Set to `Infinity` to make signal caps sticky until manually cleared.\n\t * A fresh signal with the same cap resets the cooldown.\n\t */\n\tclampCooldownMs?: number;\n\t/** Burst capacity overshoot above the steady-state rpm/tpm. Default 1 (no burst). */\n\tburstMultiplier?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Bundle\n// ---------------------------------------------------------------------------\n\nexport interface AdaptiveRateLimiterBundle {\n\t/** Effective requests-per-minute (post-signal-clamp). Reactive. */\n\treadonly effectiveRpm: Node<number>;\n\t/** Effective tokens-per-minute (post-signal-clamp). Reactive. */\n\treadonly effectiveTpm: Node<number>;\n\t/** Last adaptation signal observed. */\n\treadonly lastSignal: Node<RateLimitSignal>;\n\t/** Pending `acquire()` callers waiting for capacity. */\n\treadonly pending: Node<number>;\n\t/** Current request-token-bucket fill (approximate). */\n\treadonly rpmAvailable: Node<number>;\n\t/** Current token-bucket fill (approximate). */\n\treadonly tpmAvailable: Node<number>;\n\t/**\n\t * Imperative bridge: wait until `requestCost` request-tokens and\n\t * `tokenCost` tokens are available, then consume them. Honors the\n\t * most recent `retryAfterMs` from adaptation signals. Rejects with\n\t * an `AbortError`-named error if `signal` aborts while waiting.\n\t * `requestCost` defaults to 1; `tokenCost` defaults to 0 (rpm-only gating).\n\t */\n\tacquire(opts?: { requestCost?: number; tokenCost?: number; signal?: AbortSignal }): Promise<void>;\n\t/**\n\t * Feed back observed token usage (post-call) so the TPM bucket reflects\n\t * real consumption rather than the pre-call estimate. A positive `delta`\n\t * debits additional TPM (undershot estimate); a negative `delta` credits\n\t * back overshoot (`putBack`).\n\t */\n\trecordUsage(delta: number): void;\n\t/** Manually feed an adaptation signal — useful for tests. */\n\trecordSignal(sig: RateLimitSignal): void;\n\t/** Dispose internal subscriptions and pending timers. */\n\tdispose(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Error construction\n// ---------------------------------------------------------------------------\n\nfunction makeAbortError(reason: string): Error {\n\tconst err = new Error(reason) as Error & { name: string };\n\terr.name = \"AbortError\";\n\treturn err;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Create an adaptive rate limiter. Compose with any call source via\n * `await limiter.acquire({ requestCost, tokenCost, signal })`.\n */\nexport function adaptiveRateLimiter(\n\topts: AdaptiveRateLimiterOptions = {},\n): AdaptiveRateLimiterBundle {\n\tconst burst = Math.max(1, opts.burstMultiplier ?? 1);\n\tconst clampCooldownMs = opts.clampCooldownMs ?? 60_000;\n\n\t// Resolve reactive rpm/tpm inputs. Callers may pass `NodeInput` which\n\t// could be a literal number or a Node. `fromAny` normalizes to a Node.\n\tconst rpmInputNode =\n\t\topts.rpm != null\n\t\t\t? fromAny(opts.rpm as NodeInput<number>)\n\t\t\t: node<number>([], { initial: Number.POSITIVE_INFINITY });\n\tconst tpmInputNode =\n\t\topts.tpm != null\n\t\t\t? fromAny(opts.tpm as NodeInput<number>)\n\t\t\t: node<number>([], { initial: Number.POSITIVE_INFINITY });\n\n\t// Signal cap state — updated by recordSignal() / adaptation source.\n\t// The decay timer relaxes the cap back to Infinity after `clampCooldownMs`.\n\tconst signalRpmCap = node<number>([], {\n\t\tinitial: Number.POSITIVE_INFINITY,\n\t\tname: \"adaptiveRateLimiter/signalRpmCap\",\n\t});\n\tconst signalTpmCap = node<number>([], {\n\t\tinitial: Number.POSITIVE_INFINITY,\n\t\tname: \"adaptiveRateLimiter/signalTpmCap\",\n\t});\n\tconst lastSignal = node<RateLimitSignal>([], {\n\t\tinitial: {},\n\t\tname: \"adaptiveRateLimiter/lastSignal\",\n\t});\n\n\t// Compute effective rpm/tpm: min of user-configured cap and signal cap.\n\tconst effectiveRpm = node<number>(\n\t\t[rpmInputNode, signalRpmCap],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tactions.emit(Math.min(Number(data[0] ?? Infinity), Number(data[1] ?? Infinity)));\n\t\t},\n\t\t{ name: \"adaptiveRateLimiter/effectiveRpm\", describeKind: \"derived\" },\n\t);\n\tconst effectiveTpm = node<number>(\n\t\t[tpmInputNode, signalTpmCap],\n\t\t(batchData, actions, ctx) => {\n\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t);\n\t\t\tactions.emit(Math.min(Number(data[0] ?? Infinity), Number(data[1] ?? Infinity)));\n\t\t},\n\t\t{ name: \"adaptiveRateLimiter/effectiveTpm\", describeKind: \"derived\" },\n\t);\n\n\t// Token buckets — rebuilt when effective caps change.\n\tlet rpmBucket: TokenBucket = makeBucket(\n\t\tNumber(rpmInputNode.cache ?? Number.POSITIVE_INFINITY),\n\t\tburst,\n\t);\n\tlet tpmBucket: TokenBucket = makeBucket(\n\t\tNumber(tpmInputNode.cache ?? Number.POSITIVE_INFINITY),\n\t\tburst,\n\t);\n\n\t// A signal `rpmCap`/`tpmCap` of 0 means \"halt admission entirely\" (e.g.,\n\t// some providers emit this during hard quota exhaustion). We honor it by\n\t// marking the bucket as closed via a long throttle-until; the bucket itself\n\t// stays at its previous capacity so decay can relax it naturally.\n\tlet rpmHardStop = false;\n\tlet tpmHardStop = false;\n\n\tconst unsubRpm = effectiveRpm.subscribe((msgs) => {\n\t\tfor (const msg of msgs) {\n\t\t\tif (msg[0] === DATA) {\n\t\t\t\tconst v = Number(msg[1]);\n\t\t\t\tif (Number.isFinite(v) && v > 0) {\n\t\t\t\t\trpmBucket = makeBucket(v, burst);\n\t\t\t\t\trpmHardStop = false;\n\t\t\t\t} else if (v === Infinity) {\n\t\t\t\t\trpmBucket = makeBucket(Infinity, burst);\n\t\t\t\t\trpmHardStop = false;\n\t\t\t\t} else if (v <= 0) {\n\t\t\t\t\t// Hard stop — no admission until cap relaxes.\n\t\t\t\t\trpmHardStop = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\tconst unsubTpm = effectiveTpm.subscribe((msgs) => {\n\t\tfor (const msg of msgs) {\n\t\t\tif (msg[0] === DATA) {\n\t\t\t\tconst v = Number(msg[1]);\n\t\t\t\tif (Number.isFinite(v) && v > 0) {\n\t\t\t\t\ttpmBucket = makeBucket(v, burst);\n\t\t\t\t\ttpmHardStop = false;\n\t\t\t\t} else if (v === Infinity) {\n\t\t\t\t\ttpmBucket = makeBucket(Infinity, burst);\n\t\t\t\t\ttpmHardStop = false;\n\t\t\t\t} else if (v <= 0) {\n\t\t\t\t\ttpmHardStop = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t// Throttle-until: set by retryAfterMs signals.\n\tlet throttleUntilNs = 0;\n\n\t// Clamp-decay timers — when they fire, the signal cap is relaxed back to Infinity.\n\tconst rpmDecayTimer = new ResettableTimer();\n\tconst tpmDecayTimer = new ResettableTimer();\n\n\t// Adaptation source subscription.\n\tlet unsubAdapt: (() => void) | undefined;\n\tif (opts.adaptation != null) {\n\t\tconst adaptNode = fromAny(opts.adaptation as NodeInput<RateLimitSignal>);\n\t\tunsubAdapt = adaptNode.subscribe((msgs) => {\n\t\t\tfor (const msg of msgs) {\n\t\t\t\tif (msg[0] === DATA) applySignal(msg[1] as RateLimitSignal);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction applySignal(sig: RateLimitSignal): void {\n\t\tlastSignal.emit(sig);\n\t\t// Accept `rpmCap`/`tpmCap` of 0 as a valid hard-stop signal. Only\n\t\t// reject non-finite caps (NaN/Infinity).\n\t\tif (sig.rpmCap != null && Number.isFinite(sig.rpmCap) && sig.rpmCap >= 0) {\n\t\t\tsignalRpmCap.emit(sig.rpmCap);\n\t\t\t// Schedule decay. Uses ResettableTimer — each new clamp resets the cooldown.\n\t\t\tif (Number.isFinite(clampCooldownMs) && clampCooldownMs > 0) {\n\t\t\t\trpmDecayTimer.start(clampCooldownMs, () => signalRpmCap.emit(Number.POSITIVE_INFINITY));\n\t\t\t}\n\t\t}\n\t\tif (sig.tpmCap != null && Number.isFinite(sig.tpmCap) && sig.tpmCap >= 0) {\n\t\t\tsignalTpmCap.emit(sig.tpmCap);\n\t\t\tif (Number.isFinite(clampCooldownMs) && clampCooldownMs > 0) {\n\t\t\t\ttpmDecayTimer.start(clampCooldownMs, () => signalTpmCap.emit(Number.POSITIVE_INFINITY));\n\t\t\t}\n\t\t}\n\t\tif (sig.retryAfterMs != null && sig.retryAfterMs > 0) {\n\t\t\tconst resumeAt = monotonicNs() + sig.retryAfterMs * 1_000_000;\n\t\t\tif (resumeAt > throttleUntilNs) throttleUntilNs = resumeAt;\n\t\t}\n\t}\n\n\tconst pending = node<number>([], { initial: 0, name: \"adaptiveRateLimiter/pending\" });\n\tconst rpmAvailableNode = node<number>([], {\n\t\tinitial: Number.POSITIVE_INFINITY,\n\t\tname: \"adaptiveRateLimiter/rpmAvailable\",\n\t});\n\tconst tpmAvailableNode = node<number>([], {\n\t\tinitial: Number.POSITIVE_INFINITY,\n\t\tname: \"adaptiveRateLimiter/tpmAvailable\",\n\t});\n\n\tconst bumpPending = (delta: number): void => {\n\t\tpending.emit((pending.cache ?? 0) + delta);\n\t};\n\tconst refreshAvailable = (): void => {\n\t\trpmAvailableNode.emit(rpmBucket.available());\n\t\ttpmAvailableNode.emit(tpmBucket.available());\n\t};\n\n\tasync function acquire(\n\t\tacquireOpts: { requestCost?: number; tokenCost?: number; signal?: AbortSignal } = {},\n\t): Promise<void> {\n\t\tconst requestCost = acquireOpts.requestCost ?? 1;\n\t\tconst tokenCost = acquireOpts.tokenCost ?? 0;\n\t\tconst abortSignal = acquireOpts.signal;\n\n\t\tbumpPending(1);\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tif (abortSignal?.aborted) throw makeAbortError(\"AdaptiveRateLimiter.acquire aborted\");\n\n\t\t\t\t// Honor retry-after window.\n\t\t\t\tconst now = monotonicNs();\n\t\t\t\tif (throttleUntilNs > now) {\n\t\t\t\t\tconst waitMs = Math.ceil((throttleUntilNs - now) / 1_000_000);\n\t\t\t\t\tawait sleepReactive(waitMs, abortSignal);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Hard-stop (cap=0) → wait for the decay timer to relax.\n\t\t\t\tif ((requestCost > 0 && rpmHardStop) || (tokenCost > 0 && tpmHardStop)) {\n\t\t\t\t\tawait sleepReactive(250, abortSignal);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Capture local refs so a concurrent rpm/tpm cap-change rebuilding\n\t\t\t\t// the bucket doesn't send `putBack` to a different bucket than\n\t\t\t\t// `tryConsume` debited. If the cap relaxes mid-flight, the OLD\n\t\t\t\t// bucket gets the credit (safe — it's closed over a closure the\n\t\t\t\t// new acquires don't see), and new acquires pick up the new\n\t\t\t\t// bucket on their own next iteration.\n\t\t\t\tconst rpmAtAcquire = rpmBucket;\n\t\t\t\tconst tpmAtAcquire = tpmBucket;\n\n\t\t\t\t// Try consume RPM first.\n\t\t\t\tconst gotRpm = rpmAtAcquire.tryConsume(requestCost);\n\t\t\t\tif (!gotRpm) {\n\t\t\t\t\tawait sleepReactive(estimateWaitMs(rpmAtAcquire, requestCost), abortSignal);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Then TPM — if it fails, return the RPM token (no wasted slot).\n\t\t\t\tconst gotTpm = tokenCost > 0 ? tpmAtAcquire.tryConsume(tokenCost) : true;\n\t\t\t\tif (!gotTpm) {\n\t\t\t\t\trpmAtAcquire.putBack(requestCost);\n\t\t\t\t\tawait sleepReactive(estimateWaitMs(tpmAtAcquire, tokenCost), abortSignal);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\trefreshAvailable();\n\t\t\t\treturn;\n\t\t\t}\n\t\t} finally {\n\t\t\tbumpPending(-1);\n\t\t}\n\t}\n\n\tfunction recordUsage(delta: number): void {\n\t\tif (delta > 0) {\n\t\t\t// Undershoot: debit additional tokens. Non-blocking — if it fails, the\n\t\t\t// next acquire will just wait longer.\n\t\t\ttpmBucket.tryConsume(delta);\n\t\t} else if (delta < 0) {\n\t\t\t// Overshoot: credit back.\n\t\t\ttpmBucket.putBack(-delta);\n\t\t}\n\t\trefreshAvailable();\n\t}\n\n\tfunction dispose(): void {\n\t\tunsubRpm();\n\t\tunsubTpm();\n\t\tunsubAdapt?.();\n\t\trpmDecayTimer.cancel();\n\t\ttpmDecayTimer.cancel();\n\t}\n\n\treturn {\n\t\teffectiveRpm,\n\t\teffectiveTpm,\n\t\tlastSignal,\n\t\tpending,\n\t\trpmAvailable: rpmAvailableNode,\n\t\ttpmAvailable: tpmAvailableNode,\n\t\tacquire,\n\t\trecordUsage,\n\t\trecordSignal: applySignal,\n\t\tdispose,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\nfunction makeBucket(perMinute: number, burst: number): TokenBucket {\n\tif (!Number.isFinite(perMinute) || perMinute === Infinity) {\n\t\treturn tokenBucket(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);\n\t}\n\tconst capacity = Math.max(1, perMinute * burst);\n\tconst refillPerSecond = perMinute / 60;\n\treturn tokenBucket(capacity, refillPerSecond);\n}\n\nfunction estimateWaitMs(bucket: TokenBucket, needed: number): number {\n\tconst have = bucket.available();\n\tconst deficit = Math.max(0, needed - have);\n\tif (deficit <= 0) return 25; // retry quickly; primary path already failed so pacing is forced\n\t// Heuristic: wait 100ms per missing unit, clamped.\n\treturn Math.min(5_000, Math.max(50, deficit * 100));\n}\n\n/**\n * Promise-based sleep using `ResettableTimer` (spec §5.10 escape hatch).\n * Cleanly removes abort listener on both the timer-fires and abort paths;\n * no leaked `AbortSignal.addEventListener` registrations.\n */\nfunction sleepReactive(ms: number, signal?: AbortSignal): Promise<void> {\n\tif (ms <= 0) return Promise.resolve();\n\tif (signal?.aborted) return Promise.reject(makeAbortError(\"AdaptiveRateLimiter.acquire aborted\"));\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = new ResettableTimer();\n\t\tlet onAbort: (() => void) | undefined;\n\t\tconst cleanup = (): void => {\n\t\t\ttimer.cancel();\n\t\t\tif (signal && onAbort) signal.removeEventListener(\"abort\", onAbort);\n\t\t};\n\t\ttimer.start(ms, () => {\n\t\t\tcleanup();\n\t\t\tresolve();\n\t\t});\n\t\tif (signal) {\n\t\t\tonAbort = (): void => {\n\t\t\t\tcleanup();\n\t\t\t\treject(makeAbortError(\"AdaptiveRateLimiter.acquire aborted\"));\n\t\t\t};\n\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t}\n\t});\n}\n\nexport { NS_PER_SEC };\n"],"mappings":"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","names":["COMPLETE","DATA","DIRTY","ERROR","node","RESOLVED","node","DIRTY","DATA","RESOLVED","COMPLETE","ERROR","COMPLETE","DATA","DIRTY","ERROR","factoryTag","node","RESOLVED","node","COMPLETE","ERROR","DIRTY","DATA","RESOLVED","factoryTag","COMPLETE","DATA","DIRTY","ERROR","factoryTag","monotonicNs","node","RESOLVED","ResettableTimer","TEARDOWN","DATA","monotonicNs","node","fromAny","fromAny","node","DATA","monotonicNs","monotonicNs","node","ResettableTimer","DIRTY","DATA","ERROR","RESOLVED","COMPLETE","TEARDOWN","factoryTag"]}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
renderContextView,
|
|
3
3
|
taggedContextPool
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DVTDF5OI.js";
|
|
5
5
|
import {
|
|
6
6
|
refineLoop
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YBJVKMTM.js";
|
|
8
8
|
import {
|
|
9
9
|
QUEUE_NAMES
|
|
10
10
|
} from "./chunk-OIWU3NYV.js";
|
|
11
11
|
import {
|
|
12
12
|
topic
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-DHDCOOJU.js";
|
|
14
14
|
|
|
15
15
|
// src/presets/harness/actor-pool.ts
|
|
16
16
|
import { node } from "@graphrefly/pure-ts/core";
|
|
@@ -21,7 +21,7 @@ function actorPool(parent, opts = {}) {
|
|
|
21
21
|
const name = opts.name ?? `actorPool-${++_actorPoolSeq}`;
|
|
22
22
|
const graph = new Graph(name);
|
|
23
23
|
parent.mount(name, graph);
|
|
24
|
-
const depthCap = opts.depthCap ??
|
|
24
|
+
const depthCap = opts.depthCap ?? 8;
|
|
25
25
|
let autoActor = 0;
|
|
26
26
|
const liveHandles = /* @__PURE__ */ new Set();
|
|
27
27
|
const contextPool = taggedContextPool(graph, {
|
|
@@ -122,7 +122,7 @@ import { filter } from "@graphrefly/pure-ts/extra";
|
|
|
122
122
|
function defaultToOutput(result) {
|
|
123
123
|
const { best, score, status } = result;
|
|
124
124
|
const scoreStr = Number.isFinite(score) ? score.toFixed(3) : String(score);
|
|
125
|
-
const artifact = best
|
|
125
|
+
const artifact = best;
|
|
126
126
|
if (status === "converged") {
|
|
127
127
|
return {
|
|
128
128
|
outcome: "success",
|
|
@@ -571,4 +571,4 @@ export {
|
|
|
571
571
|
harnessProfile,
|
|
572
572
|
harnessTrace
|
|
573
573
|
};
|
|
574
|
-
//# sourceMappingURL=chunk-
|
|
574
|
+
//# sourceMappingURL=chunk-TPTZZV25.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presets/harness/actor-pool.ts","../src/presets/harness/eval-verifier.ts","../src/presets/harness/refine-executor.ts","../src/presets/harness/ownership-controller.ts","../src/presets/harness/profile.ts","../src/presets/harness/trace.ts"],"sourcesContent":["/**\n * DS-14.6.A U-B — `actorPool()` (Phase 14.5).\n *\n * The dynamic-track complement to `spawnable()` (SESSION-DS-14.6-A L7/L8 +\n * 9Q walk). An actor is **identity + cursor + tool closure, NOT a subgraph**\n * (D-B1): no per-actor mount, so `describe()` shows only the pool / todo /\n * context-hub collections and the actor count drifts inside a single\n * reactive `active` map node. `depthCap` is enforced via the depth carried\n * on the attach request (D-B2); `release()` cascades teardown to the\n * actor's context view + todo cursor subscriptions (§3i — free).\n *\n * Contrast `spawnable()`: agent IS a subgraph, topology reflects the agent\n * set, `describe()`-visible — use it when agent identities are pre-known.\n * Use `actorPool()` for runtime recursive fan-out where agent count drifts.\n *\n * @module\n */\n\nimport { type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { type ReactiveLogBundle, reactiveLog } from \"@graphrefly/pure-ts/extra\";\nimport { Graph } from \"@graphrefly/pure-ts/graph\";\nimport {\n\ttype ContextEntry,\n\ttype ContextView,\n\ttype RenderedEntry,\n\trenderContextView,\n\ttype TaggedContextPoolBundle,\n\ttaggedContextPool,\n} from \"../ai/context/index.js\";\n\nexport type ActorId = string;\n\nexport interface Todo {\n\treadonly id: string;\n\treadonly assignee?: ActorId;\n\treadonly payload: unknown;\n}\n\nexport type ActorStatus = \"idle\" | \"running\" | \"blocked\" | \"done\";\n\nexport interface ActorState {\n\treadonly id: ActorId;\n\treadonly depth: number;\n\treadonly status: ActorStatus;\n}\n\nexport interface ActorSpec<T> {\n\treadonly id?: ActorId;\n\t/** Recursion depth — gated against `depthCap` (D-B2). Default 0 (root). */\n\treadonly depth?: number;\n\t/** Per-actor compression view over the shared context pool. */\n\treadonly view: Omit<ContextView<T>, \"pressure\"> & { readonly pressure: Node<number> };\n}\n\nexport interface ActorHandle<T> {\n\treadonly id: ActorId;\n\t/** This actor's compressed context slice. */\n\treadonly context: Node<readonly RenderedEntry<T>[]>;\n\t/** Todos currently assigned to this actor (or unassigned). */\n\treadonly todoCursor: Node<readonly Todo[]>;\n\t/** Write an entry into the shared pool, stamped with this actor's id tag. */\n\tpublish(entry: Omit<ContextEntry<T>, \"id\" | \"t_ns\"> & { id?: string }): string;\n\tenqueueTodo(t: Todo): void;\n\treadonly status: Node<ActorStatus>;\n\tsetStatus(s: ActorStatus): void;\n\t/** Idempotent. Tears the actor's subscriptions + removes it from `active`. */\n\trelease(): void;\n}\n\nexport interface ActorPoolOptions<T> {\n\treadonly name?: string;\n\t/**\n\t * Max recursion depth; `attachActor` with `depth > depthCap` throws.\n\t * Default: `8` (F-ACTOR, 2026-05-18 smell sweep — a finite default bounds\n\t * runaway recursive fan-out; previously silently `Infinity`). Pass an\n\t * explicit `Number.POSITIVE_INFINITY` to opt in to unbounded recursion.\n\t */\n\treadonly depthCap?: number;\n\t/** Forwarded to the backing context pool. */\n\treadonly contextTopic?: string;\n\treadonly llmCompress?: TaggedContextPoolBundle<T>[\"_opts\"][\"llmCompress\"];\n}\n\nexport interface ActorPoolBundle<T> {\n\tattachActor(spec: ActorSpec<T>): ActorHandle<T>;\n\treadonly contextPool: TaggedContextPoolBundle<T>;\n\treadonly todos: ReactiveLogBundle<Todo>;\n\t/** Single reactive map of live actors — `describe()`-coherent (D-B1). */\n\treadonly active: Node<ReadonlyMap<ActorId, ActorState>>;\n\treadonly graph: Graph;\n\tdispose(): void;\n}\n\n/** Process-wide sequence for collision-safe default mount names (QA P6). */\nlet _actorPoolSeq = 0;\n\nexport function actorPool<T = unknown>(\n\tparent: Graph,\n\topts: ActorPoolOptions<T> = {},\n): ActorPoolBundle<T> {\n\t// QA P6: collision-safe default — recursive fan-out spins nested pools\n\t// under one parent; static \"actorPool\" would collide on `parent.mount`.\n\tconst name = opts.name ?? `actorPool-${++_actorPoolSeq}`;\n\tconst graph = new Graph(name);\n\tparent.mount(name, graph);\n\tconst depthCap = opts.depthCap ?? 8;\n\t// QA P5: per-pool actor counter (was module-global → test-pollution).\n\tlet autoActor = 0;\n\t// QA P7: track live handles so dispose() can release them all.\n\tconst liveHandles = new Set<ActorHandle<T>>();\n\n\tconst contextPool = taggedContextPool<T>(graph, {\n\t\ttopic: opts.contextTopic ?? \"context\",\n\t\tllmCompress: opts.llmCompress,\n\t\tname: `${name}.ctx`,\n\t});\n\tconst todos: ReactiveLogBundle<Todo> = reactiveLog<Todo>(undefined, { name: `${name}.todos` });\n\n\t// Single reactive `active` map node — actor count drifts inside it; no\n\t// per-actor subgraph mount (D-B1, describe-coherent).\n\tconst stateMap = new Map<ActorId, ActorState>();\n\tconst active = node<ReadonlyMap<ActorId, ActorState>>([], {\n\t\tname: `${name}.active`,\n\t\tinitial: new Map(),\n\t});\n\tfunction pushActive(): void {\n\t\tactive.emit(new Map(stateMap));\n\t}\n\n\tfunction attachActor(spec: ActorSpec<T>): ActorHandle<T> {\n\t\tconst depth = spec.depth ?? 0;\n\t\tif (depth > depthCap) {\n\t\t\tthrow new RangeError(`actorPool: depth ${depth} exceeds depthCap ${depthCap}`);\n\t\t}\n\t\tconst id = spec.id ?? `actor-${++autoActor}`;\n\n\t\tconst context = renderContextView(contextPool, spec.view as ContextView<T>);\n\t\t// Per-actor todo cursor — assigned-to-me or unassigned.\n\t\tconst todoCursor = node<readonly Todo[]>(\n\t\t\t[todos.entries as Node],\n\t\t\t(data, actions, ctx) => {\n\t\t\t\tconst all = (data[0] != null && data[0].length > 0 ? data[0].at(-1) : ctx.prevData[0]) as\n\t\t\t\t\t| readonly Todo[]\n\t\t\t\t\t| undefined;\n\t\t\t\tactions.emit((all ?? []).filter((t) => t.assignee === id || t.assignee === undefined));\n\t\t\t},\n\t\t\t{ describeKind: \"derived\" },\n\t\t);\n\t\tconst status = node<ActorStatus>([], { name: `${name}.${id}.status`, initial: \"idle\" });\n\n\t\tstateMap.set(id, { id, depth, status: \"idle\" });\n\t\tpushActive();\n\n\t\t// Keepalive subs so `.cache` stays warm; torn on release (cascade-cancel).\n\t\tconst subs = [\n\t\t\tcontext.subscribe(() => {}),\n\t\t\ttodoCursor.subscribe(() => {}),\n\t\t\tstatus.subscribe(() => {}),\n\t\t];\n\t\tlet released = false;\n\n\t\tconst handle: ActorHandle<T> = {\n\t\t\tid,\n\t\t\tcontext,\n\t\t\ttodoCursor,\n\t\t\tstatus,\n\t\t\tpublish(entry) {\n\t\t\t\tconst tags = [...(entry.tags ?? []), `actor:${id}`];\n\t\t\t\treturn contextPool.add({ ...entry, tags });\n\t\t\t},\n\t\t\tenqueueTodo(t) {\n\t\t\t\ttodos.append(t);\n\t\t\t},\n\t\t\tsetStatus(s) {\n\t\t\t\tstatus.emit(s);\n\t\t\t\tconst prev = stateMap.get(id);\n\t\t\t\tif (prev) {\n\t\t\t\t\tstateMap.set(id, { ...prev, status: s });\n\t\t\t\t\tpushActive();\n\t\t\t\t}\n\t\t\t},\n\t\t\trelease() {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\t// Cascade-cancel: tearing the keepalive subs deactivates the\n\t\t\t\t// per-actor derived nodes (lazy-deactivation — COMPOSITION-GUIDE\n\t\t\t\t// §1), detaching `context`/`todoCursor`/`status` from the shared\n\t\t\t\t// pool/todos logs once no other subscriber remains.\n\t\t\t\tfor (const u of subs) u();\n\t\t\t\tstateMap.delete(id);\n\t\t\t\tliveHandles.delete(handle);\n\t\t\t\tpushActive();\n\t\t\t},\n\t\t};\n\t\tliveHandles.add(handle);\n\t\treturn handle;\n\t}\n\n\treturn {\n\t\tattachActor,\n\t\tcontextPool,\n\t\ttodos,\n\t\tactive,\n\t\tgraph,\n\t\tdispose(): void {\n\t\t\t// QA P7: release outstanding actors first (tears their keepalive\n\t\t\t// subs / deactivates per-actor derived nodes) before disposing the\n\t\t\t// shared pool + todo log.\n\t\t\tfor (const h of [...liveHandles]) h.release();\n\t\t\tcontextPool.dispose();\n\t\t\ttodos.dispose();\n\t\t},\n\t};\n}\n","/**\n * evalVerifier — re-run the affected eval tasks against the execute-stage\n * artifact instead of asking an LLM to opine on the fix.\n *\n * Pairs naturally with {@link refineExecutor}: refineExecutor emits an\n * `ExecuteOutput<T>.artifact` holding the converged candidate; evalVerifier\n * pulls it out via `extractArtifact` and feeds a single-candidate batch\n * into the same `Evaluator<T>` shape that `refineLoop` used. Consistent\n * scoring between EXECUTE and VERIFY — no \"LLM said it looks fine\" gap.\n *\n * **C2 lifecycle (Tier 6.5).** The work fn is invoked once per claimed\n * verify-stage job. A fresh single-candidate eval subgraph is mounted\n * inside the work fn and tears down when the JobFlow pump ack/unsubs.\n *\n * @module\n */\n\nimport { batch, type Node, node } from \"@graphrefly/pure-ts/core\";\nimport { filter } from \"@graphrefly/pure-ts/extra\";\nimport { Graph } from \"@graphrefly/pure-ts/graph\";\nimport type {\n\tExecuteOutput,\n\tHarnessExecutor,\n\tHarnessJobPayload,\n\tHarnessVerifier,\n\tTriagedItem,\n\tVerifyOutput,\n} from \"../../utils/harness/types.js\";\nimport type { JobEnvelope } from \"../../utils/job-queue/index.js\";\nimport { refineExecutor } from \"./refine-executor.js\";\nimport type {\n\tDatasetItem,\n\tEvalResult,\n\tEvaluator,\n\tRefineLoopOptions,\n\tRefineStrategy,\n} from \"./refine-loop.js\";\n\n/** Summary of the re-eval wave passed to a custom `toOutput` mapper. */\nexport interface EvalVerifierSummary {\n\treadonly scores: readonly EvalResult[];\n\treadonly meanScore: number;\n\treadonly passCount: number;\n\treadonly total: number;\n\treadonly threshold: number;\n\t/**\n\t * True when the EXECUTE stage did not produce an artifact (i.e.\n\t * `extractArtifact` returned `null` / `undefined`). Downstream mappers\n\t * can distinguish this from \"evaluator ran but everything scored zero\".\n\t */\n\treadonly missingArtifact?: boolean;\n}\n\n/** Configuration for {@link evalVerifier}. */\nexport interface EvalVerifierConfig<T> {\n\t/**\n\t * Pull the artifact that should be re-evaluated out of the execute-stage\n\t * output. Default: `(exec) => exec.artifact as T` — works out-of-the-box\n\t * with `refineExecutor` (which populates `artifact` by default).\n\t */\n\textractArtifact?: (exec: ExecuteOutput<T>, item: TriagedItem) => T | null | undefined;\n\n\t/**\n\t * Reactive evaluator — same contract as `refineLoop`'s `Evaluator<T>`.\n\t */\n\tevaluator: Evaluator<T>;\n\n\t/**\n\t * Resolve which dataset rows to score this verification against.\n\t */\n\tdatasetFor: (item: TriagedItem) => readonly DatasetItem[];\n\n\t/** Mean score required to pass verification. Default `0.5`. */\n\tthreshold?: number;\n\n\t/** Optional output mapper — override the default findings / errorClass shape. */\n\ttoOutput?: (summary: EvalVerifierSummary) => VerifyOutput;\n\n\t/** Node name prefix for introspection. */\n\tname?: string;\n\n\t/**\n\t * Optional parent graph on which per-claim eval subgraphs mount at\n\t * `eval/${claimId}` (DS-13.5.D.4, locked 2026-05-01). When provided,\n\t * each claim creates a fresh `Graph(\\`eval_${claimId}\\`)` subgraph\n\t * carrying `candidates` / `dataset` / `output` and mounts it at\n\t * `eval/${claimId}` on this parent — `describe()` walks the parent\n\t * see the per-claim topology while the claim is in flight, and the\n\t * subgraph is removed via `parent.remove(\"eval/${claimId}\")` on the\n\t * output node's `deactivate` cleanup (fires when JobFlow's pump\n\t * unsubscribes after ack/nack).\n\t *\n\t * **claimId source.** `JobEnvelope.id` (assigned at enqueue time);\n\t * unique within a JobFlow lifetime — uniqueness within a single pump\n\t * cycle is the JobFlow's contract.\n\t *\n\t * When omitted, internal nodes float (pre-DS-13.5.D.4 behavior).\n\t */\n\tgraph?: Graph;\n}\n\nfunction meanScore(scores: readonly EvalResult[]): number {\n\tif (scores.length === 0) return Number.NEGATIVE_INFINITY;\n\tlet sum = 0;\n\tfor (const s of scores) sum += s.score;\n\treturn sum / scores.length;\n}\n\nfunction defaultToOutput(summary: EvalVerifierSummary): VerifyOutput {\n\tconst { passCount, total, meanScore: mean, threshold, missingArtifact } = summary;\n\tconst meanStr = Number.isFinite(mean) ? mean.toFixed(3) : String(mean);\n\tconst verified = !missingArtifact && total > 0 && mean >= threshold;\n\tconst findings = missingArtifact\n\t\t? [\"EXECUTE stage did not emit an artifact; cannot verify reactively\"]\n\t\t: verified\n\t\t\t? [`${passCount}/${total} eval tasks passed; mean score ${meanStr} ≥ ${threshold}`]\n\t\t\t: total === 0\n\t\t\t\t? [\"No eval tasks were selected for this item — cannot verify\"]\n\t\t\t\t: [\n\t\t\t\t\t\t`${passCount}/${total} eval tasks passed; mean score ${meanStr} < threshold ${threshold}`,\n\t\t\t\t\t];\n\treturn verified\n\t\t? { verified: true, findings }\n\t\t: { verified: false, findings, errorClass: \"structural\" };\n}\n\nfunction defaultExtractArtifact<T>(exec: ExecuteOutput<T>): T | null | undefined {\n\treturn exec.artifact ?? null;\n}\n\n/**\n * Build a {@link HarnessVerifier} that re-runs the eval suite against the\n * artifact produced by EXECUTE.\n *\n * Reads `job.payload.execution` (filled by the upstream execute work fn)\n * and runs the evaluator against `extractArtifact(execution, item)`.\n * Returns the same payload with `verify` filled in.\n *\n * @example Pair with refineExecutor for end-to-end eval consistency.\n * ```ts\n * const evaluator: Evaluator<CatalogEntry> = (cands, ds) => runEval(cands, ds);\n * const harness = harnessLoop(\"repair\", {\n * adapter,\n * executor: refineExecutor({ ..., evaluator, ...strategyConfig }),\n * verifier: evalVerifier({ evaluator, datasetFor, threshold: 0.8 }),\n * });\n * ```\n */\nexport function evalVerifier<T>(config: EvalVerifierConfig<T>): HarnessVerifier<T> {\n\tconst name = config.name ?? \"eval-verifier\";\n\tconst threshold = config.threshold ?? 0.5;\n\tconst toOutput = config.toOutput ?? defaultToOutput;\n\tconst extract = config.extractArtifact ?? defaultExtractArtifact<T>;\n\tconst parentGraph = config.graph;\n\t// DS-13.5.B QA A7 (2026-05-03): per-claimId collision counter.\n\t// Reingest paths preserve identity per DS-13.5.D.3 so the same\n\t// claimId can land on the verifier while a prior cycle is still\n\t// in-flight. Without disambiguation, `parentGraph.mount(\n\t// \"eval/${id}\", sub)` would either silently overwrite the prior\n\t// mount or throw \"mount already exists\". The counter is keyed by\n\t// claimId so DISTINCT ids start at seq=0 (`eval/${id}`) and only\n\t// REPEAT ids get a `_${seq}` suffix.\n\tconst mountSeqByClaimId = new Map<string, number>();\n\n\treturn (job: JobEnvelope<HarnessJobPayload<T>>) => {\n\t\tconst { item, execution } = job.payload;\n\t\t// Defensive: verify stage should always run AFTER execute stage with\n\t\t// `execution` populated. If it isn't, surface that as a structural\n\t\t// failure so the dispatch effect can route the item.\n\t\tif (execution == null) {\n\t\t\treturn {\n\t\t\t\t...job.payload,\n\t\t\t\tverify: {\n\t\t\t\t\tverified: false,\n\t\t\t\t\tfindings: [\"evalVerifier: prior execute stage produced no execution\"],\n\t\t\t\t\terrorClass: \"structural\" as const,\n\t\t\t\t},\n\t\t\t} satisfies HarnessJobPayload<T>;\n\t\t}\n\t\tconst artifact = extract(execution, item);\n\t\tif (artifact == null) {\n\t\t\treturn {\n\t\t\t\t...job.payload,\n\t\t\t\tverify: toOutput({\n\t\t\t\t\tscores: [],\n\t\t\t\t\tmeanScore: Number.NEGATIVE_INFINITY,\n\t\t\t\t\tpassCount: 0,\n\t\t\t\t\ttotal: 0,\n\t\t\t\t\tthreshold,\n\t\t\t\t\tmissingArtifact: true,\n\t\t\t\t}),\n\t\t\t} satisfies HarnessJobPayload<T>;\n\t\t}\n\n\t\t// Per-claim eval subgraph. State seeds with the single candidate +\n\t\t// resolved dataset; the evaluator returns a Node<readonly EvalResult[]>.\n\t\t// The terminal payload emits when the evaluator settles; intermediate\n\t\t// nulls are filtered.\n\t\t//\n\t\t// **Batch-coalescing for synchronous-emit-during-subscribe evaluators\n\t\t// (COMPOSITION-GUIDE §9a).** This `batch()` wrap is load-bearing for a\n\t\t// SPECIFIC evaluator pattern: evaluators that, during the\n\t\t// `evaluator(candidates, dataset)` constructor call, synchronously\n\t\t// `subscribe()` to BOTH inputs and emit on each subscribe-callback\n\t\t// firing. Each subscribe pushes the cached value to its callback, the\n\t\t// callback runs `out.emit(...)`, and that emit becomes its own wave\n\t\t// when not inside a batch — leaving multiple DATA messages visible to\n\t\t// the downstream `derived` once it activates. The JobFlow pump's\n\t\t// \"first DATA wins\" capture would then fire on the FIRST intermediate\n\t\t// emit (e.g. empty scores from a pre-dataset recompute) instead of\n\t\t// the final settled value.\n\t\t//\n\t\t// Wrapping the constructor call in `batch()` coalesces those internal\n\t\t// emits into one multi-message delivery (§9a); sugar `derived`\n\t\t// auto-unwraps to the LAST value per its snapshot/combine semantics\n\t\t// (sugar.ts), so the downstream sees only the final settled scores.\n\t\t//\n\t\t// **Async evaluators are NOT covered by this fix.** Evaluators that\n\t\t// subscribe via microtask / Promise.then() / setTimeout don't see the\n\t\t// §9a hazard at all — their emits land in separate waves regardless\n\t\t// of whether the constructor call is batched. The fix is strictly\n\t\t// for the synchronous-emit-during-subscribe pattern (today's tests:\n\t\t// `presenceEvaluator` in actuator-executor.test.ts; `keywordEvaluator`\n\t\t// in refine-executor.test.ts uses `derived` and is naturally\n\t\t// single-emit). See `harness-default-bridges.test.ts` regression test\n\t\t// \"evalVerifier coalesces synchronous-emit-during-subscribe\n\t\t// evaluators\" for the locked contract.\n\t\t// DS-13.5.D.4 (locked 2026-05-01): when `parentGraph` is configured,\n\t\t// each claim creates its own subgraph `eval_${claimId}` and mounts\n\t\t// it at `eval/${claimId}` on the parent. Internal node names use\n\t\t// the simple shape (`candidates`, `dataset`, `output`) since each\n\t\t// subgraph is a fresh namespace. Cleanup attaches to `raw`'s\n\t\t// deactivate hook so the segment is removed when JobFlow\n\t\t// unsubscribes after ack/nack (canonical \"last unsubscribe\"\n\t\t// signal via the existing NodeFnCleanup.onDeactivation protocol).\n\t\tconst claimId = job.id;\n\t\t// QA A7: only append a `_${seq}` suffix when this claimId has\n\t\t// already been mounted (collision case). Distinct claimIds\n\t\t// resolve to `eval/${claimId}` as before.\n\t\tconst seq = mountSeqByClaimId.get(claimId) ?? 0;\n\t\tmountSeqByClaimId.set(claimId, seq + 1);\n\t\tconst segmentSuffix = seq === 0 ? \"\" : `_${seq}`;\n\t\tconst segmentPath = `eval/${claimId}${segmentSuffix}`;\n\t\tconst sub = parentGraph != null ? new Graph(`eval_${claimId}${segmentSuffix}`) : null;\n\t\tconst candidatesName = sub != null ? \"candidates\" : `${name}/candidates`;\n\t\tconst datasetName = sub != null ? \"dataset\" : `${name}/dataset`;\n\t\tconst outputName = sub != null ? \"output\" : `${name}/output`;\n\t\tconst gateName = sub != null ? \"gate-out\" : `${name}/gate-out`;\n\n\t\tconst candidates = node<readonly T[]>([], {\n\t\t\tinitial: [artifact as T],\n\t\t\tname: candidatesName,\n\t\t});\n\t\tconst dataset = node<readonly DatasetItem[]>([], {\n\t\t\tinitial: config.datasetFor(item),\n\t\t\tname: datasetName,\n\t\t});\n\t\tif (sub != null) {\n\t\t\tsub.add(candidates, { name: \"candidates\" });\n\t\t\tsub.add(dataset, { name: \"dataset\" });\n\t\t}\n\t\tlet scoresNode!: ReturnType<Evaluator<T>>;\n\t\tbatch(() => {\n\t\t\tscoresNode = config.evaluator(candidates, dataset);\n\t\t});\n\t\t// DS-13.5.B QA A1 (2026-05-03): the deactivate cleanup hook MUST\n\t\t// be returned on every fn run, including the early-return path\n\t\t// where `arr == null` (evaluator emits a placeholder before\n\t\t// settling). Cleanup capture happens at the END of fn execution\n\t\t// (NodeFnCleanup contract), so a fn that returned early without\n\t\t// the cleanup object would leak `eval/${claimId}` if its first\n\t\t// run produced null and the consumer unsubscribed before\n\t\t// scoresNode emits non-null.\n\t\tconst cleanup =\n\t\t\tparentGraph != null\n\t\t\t\t? () => ({\n\t\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\t\t// Auto-unmount on JobFlow's pump unsubscribe after\n\t\t\t\t\t\t\t// ack/nack (DS-13.5.D.4). Idempotent: try/catch\n\t\t\t\t\t\t\t// covers the case where the segment was already\n\t\t\t\t\t\t\t// removed (e.g. parent destroy cascade ran first).\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tparentGraph.remove(segmentPath);\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t/* best-effort cleanup */\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t: () => undefined;\n\t\tconst raw = node<HarnessJobPayload<T> | null>(\n\t\t\t[scoresNode as Node<unknown>],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst arr = data[0] as readonly EvalResult[] | null | undefined;\n\t\t\t\tif (arr == null) {\n\t\t\t\t\tactions.emit(null);\n\t\t\t\t\treturn cleanup();\n\t\t\t\t}\n\t\t\t\tconst mean = meanScore(arr);\n\t\t\t\tconst passCount = arr.filter((s) => s.score >= threshold).length;\n\t\t\t\tactions.emit({\n\t\t\t\t\t...job.payload,\n\t\t\t\t\tverify: toOutput({\n\t\t\t\t\t\tscores: arr,\n\t\t\t\t\t\tmeanScore: mean,\n\t\t\t\t\t\tpassCount,\n\t\t\t\t\t\ttotal: arr.length,\n\t\t\t\t\t\tthreshold,\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t\treturn cleanup();\n\t\t\t},\n\t\t\t{ name: outputName, describeKind: \"derived\" },\n\t\t);\n\t\tconst gateOut = filter(raw, (v) => v != null, { name: gateName }) as ReturnType<\n\t\t\tHarnessVerifier<T>\n\t\t>;\n\t\tif (sub != null) {\n\t\t\tsub.add(raw, { name: \"output\" });\n\t\t\t// QA A6: register the gate-out filter on the per-claim\n\t\t\t// subgraph so describe() walks see the consumer-facing node\n\t\t\t// alongside candidates/dataset/output. Without this, the\n\t\t\t// returned filter floats and the subgraph's external surface\n\t\t\t// is incomplete in topology snapshots.\n\t\t\tsub.add(gateOut as Node<unknown>, { name: \"gate-out\" });\n\t\t\t(parentGraph as Graph).mount(segmentPath, sub);\n\t\t}\n\t\treturn gateOut;\n\t};\n}\n\n/**\n * Config for {@link harnessEvalPair} — the typed bundle that produces a\n * matched `refineExecutor<T>` + `evalVerifier<T>` pair sharing one\n * {@link Evaluator} and one `datasetFor` resolver.\n */\nexport interface HarnessEvalPairConfig<T> {\n\t/** Map a triaged item to the seed candidate. */\n\tseedFrom: (item: TriagedItem) => T;\n\t/** The reactive evaluator used by BOTH executor and verifier. */\n\tevaluator: Evaluator<T>;\n\t/** The refinement strategy (e.g. `errorCritique(teacher)`). */\n\tstrategy: RefineStrategy<T>;\n\t/** Resolve dataset rows per triaged item. */\n\tdatasetFor: (item: TriagedItem) => readonly DatasetItem[];\n\t/** Pass-threshold for the verifier. Default `0.5`. */\n\tthreshold?: number;\n\t/** Convergence / budget options forwarded to each inner `refineLoop`. */\n\trefine?: Omit<RefineLoopOptions, \"dataset\" | \"name\">;\n\t/**\n\t * Shared node-name prefix — the executor becomes `${name}-exec` and the\n\t * verifier `${name}-verify` for distinct but related describe() paths.\n\t * Default `\"harness-pair\"`.\n\t */\n\tname?: string;\n}\n\n/**\n * Typed factory that returns a matched `{ executor, verifier }` pair.\n *\n * Prevents the \"executor wrote `A`, verifier expected `B`\" class of runtime\n * cast errors — `T` is threaded through both sides, so mixing up the\n * configuration is a compile error instead of a silent `as T` in\n * `extractArtifact`. Shares the evaluator so EXECUTE and VERIFY score with\n * identical semantics (the whole point of `evalVerifier`).\n */\nexport function harnessEvalPair<T>(config: HarnessEvalPairConfig<T>): {\n\texecutor: HarnessExecutor<T>;\n\tverifier: HarnessVerifier<T>;\n} {\n\tconst baseName = config.name ?? \"harness-pair\";\n\tconst executor = refineExecutor<T>({\n\t\tname: `${baseName}-exec`,\n\t\tseedFrom: config.seedFrom,\n\t\tevaluator: config.evaluator,\n\t\tstrategy: config.strategy,\n\t\tdatasetFor: config.datasetFor,\n\t\trefine: config.refine,\n\t});\n\tconst verifier = evalVerifier<T>({\n\t\tname: `${baseName}-verify`,\n\t\tevaluator: config.evaluator,\n\t\tdatasetFor: config.datasetFor,\n\t\tthreshold: config.threshold,\n\t});\n\treturn { executor, verifier };\n}\n","/**\n * refineExecutor — bridge a `refineLoop` into the harness EXECUTE work fn.\n *\n * Each claimed job mounts a fresh `refineLoop`; when the loop reaches a\n * terminal status (`converged` / `budget` / `errored`), the work fn emits a\n * single {@link HarnessJobPayload} with `execution` filled in. The JobFlow\n * pump subscribes once, takes the first DATA, then unsubscribes — so the\n * inner loop tears down cleanly when the harness acks the job.\n *\n * **C2 lifecycle (Tier 6.5).** The work fn is invoked once per claim, so\n * no internal `switchMap` is needed (the prior pre-C2 shape used switchMap\n * to handle a stream of items). The pump owns the per-claim lifecycle:\n * activation when the work fn returns, teardown when the result Node is\n * unsubscribed.\n *\n * **Cross-item learning:** a fresh refineLoop per item means\n * `errorCritique`-style failure sampling does NOT accumulate across items\n * sharing a `rootCause`. A persistent-loop + re-seed surface is filed in\n * `docs/optimizations.md` as a long-term follow-up.\n *\n * @module\n */\n\nimport { node } from \"@graphrefly/pure-ts/core\";\nimport { filter } from \"@graphrefly/pure-ts/extra\";\nimport type {\n\tExecuteOutput,\n\tHarnessExecutor,\n\tHarnessJobPayload,\n\tTriagedItem,\n} from \"../../utils/harness/types.js\";\nimport type { JobEnvelope } from \"../../utils/job-queue/index.js\";\nimport {\n\ttype DatasetItem,\n\ttype Evaluator,\n\ttype RefineLoopOptions,\n\ttype RefineStatus,\n\ttype RefineStrategy,\n\trefineLoop,\n} from \"./refine-loop.js\";\n\n/** Terminal-run snapshot passed to a custom `toOutput` mapper. */\nexport interface RefineExecutorResult<T> {\n\t/**\n\t * Best candidate the inner loop converged on. `undefined` (SENTINEL) if the\n\t * inner loop never produced a best — e.g. it `errored` before any iteration\n\t * settled. (`loop.best` is a SENTINEL `Node<T>` post the 2026-05-18\n\t * anti-pattern sweep — no `null` placeholder.)\n\t */\n\treadonly best: T | undefined;\n\t/** Aggregate score at termination. `-Infinity` if the batch was empty. */\n\treadonly score: number;\n\t/** Reason the loop terminated. */\n\treadonly status: RefineStatus;\n}\n\n/** Configuration for {@link refineExecutor}. */\nexport interface RefineExecutorConfig<T> {\n\t/** Map a triaged item to the seed candidate (e.g. a catalog entry, prompt, patch). */\n\tseedFrom: (item: TriagedItem) => T;\n\n\t/** Reactive evaluator — same shape as passed to `refineLoop`. */\n\tevaluator: Evaluator<T>;\n\n\t/** Strategy (e.g. `errorCritique(teacher)`). Applied to every item's inner loop. */\n\tstrategy: RefineStrategy<T>;\n\n\t/** Map a triaged item to the dataset rows the evaluator should score against. */\n\tdatasetFor: (item: TriagedItem) => readonly DatasetItem[];\n\n\t/**\n\t * Optional mapper from the inner loop's terminal snapshot to an\n\t * `ExecuteOutput<T>`. Default: converged→success, budget→partial,\n\t * errored→failure.\n\t */\n\ttoOutput?: (result: RefineExecutorResult<T>) => ExecuteOutput<T>;\n\n\t/** Convergence / budget options forwarded to each inner `refineLoop`. */\n\trefine?: Omit<RefineLoopOptions, \"dataset\" | \"name\">;\n\n\t/** Node name prefix for introspection. Default `\"refine-executor\"`. */\n\tname?: string;\n}\n\nfunction defaultToOutput<T>(result: RefineExecutorResult<T>): ExecuteOutput<T> {\n\tconst { best, score, status } = result;\n\tconst scoreStr = Number.isFinite(score) ? score.toFixed(3) : String(score);\n\tconst artifact: T | undefined = best;\n\tif (status === \"converged\") {\n\t\treturn {\n\t\t\toutcome: \"success\",\n\t\t\tdetail: `refineLoop converged at score ${scoreStr}`,\n\t\t\tartifact,\n\t\t};\n\t}\n\tif (status === \"budget\") {\n\t\treturn {\n\t\t\toutcome: \"partial\",\n\t\t\tdetail: `refineLoop hit budget at score ${scoreStr}`,\n\t\t\tartifact,\n\t\t};\n\t}\n\treturn {\n\t\toutcome: \"failure\",\n\t\tdetail: `refineLoop errored (status=${status})`,\n\t\tartifact,\n\t};\n}\n\n/**\n * Build a {@link HarnessExecutor} backed by a `refineLoop` per claimed\n * job.\n *\n * @example Eval-driven repair loop in the harness EXECUTE slot.\n * ```ts\n * const harness = harnessLoop(\"repair\", {\n * adapter,\n * executor: refineExecutor({\n * seedFrom: (item) => initialCatalogEntry(item),\n * datasetFor: (item) => pickAffectedTasks(item, allTasks),\n * evaluator: (cands, tasks) => runEvalBatch(cands, tasks),\n * strategy: errorCritique({ teacher, width: 3 }),\n * refine: { maxIterations: 5, minScore: 0.9 },\n * }),\n * });\n * ```\n */\nexport function refineExecutor<T>(config: RefineExecutorConfig<T>): HarnessExecutor<T> {\n\tconst name = config.name ?? \"refine-executor\";\n\tconst toOutput = config.toOutput ?? defaultToOutput<T>;\n\n\treturn (job: JobEnvelope<HarnessJobPayload<T>>) => {\n\t\tconst item = job.payload.item;\n\t\tconst loop = refineLoop<T>(config.seedFrom(item), config.evaluator, config.strategy, {\n\t\t\t...config.refine,\n\t\t\tdataset: config.datasetFor(item),\n\t\t\tname: `${name}/inner`,\n\t\t});\n\t\t// Terminal-allowlist guard — emit non-null only on `converged` / `budget` /\n\t\t// `errored`; intermediate `running` waves emit `null` (deduped via the\n\t\t// node's default Object.is). The trailing `filter(v != null)` strips\n\t\t// the null DATA so the JobFlow pump's first-DATA capture sees the\n\t\t// terminal payload, not the intermediate null.\n\t\tconst raw = node<HarnessJobPayload<T> | null>(\n\t\t\t[loop.status, loop.best, loop._score],\n\t\t\t(batchData, actions, ctx) => {\n\t\t\t\tconst data = batchData.map((batch, i) =>\n\t\t\t\t\tbatch != null && batch.length > 0 ? batch.at(-1) : ctx.prevData[i],\n\t\t\t\t);\n\t\t\t\tconst s = data[0] as RefineStatus;\n\t\t\t\tif (s !== \"converged\" && s !== \"budget\" && s !== \"errored\") {\n\t\t\t\t\tactions.emit(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst exec = toOutput({\n\t\t\t\t\tbest: data[1] as T | undefined,\n\t\t\t\t\tscore: data[2] as number,\n\t\t\t\t\tstatus: s,\n\t\t\t\t});\n\t\t\t\tactions.emit({\n\t\t\t\t\t...job.payload,\n\t\t\t\t\texecution: { item, ...exec },\n\t\t\t\t});\n\t\t\t},\n\t\t\t{ name: `${name}/output`, describeKind: \"derived\" },\n\t\t);\n\t\treturn filter(raw, (v) => v != null, { name: `${name}/gate-out` }) as ReturnType<\n\t\t\tHarnessExecutor<T>\n\t\t>;\n\t};\n}\n","/**\n * `ownershipController()` — multi-agent subgraph ownership preset\n * (DS-14.5.A delta #8; L5/L6 + Q1–Q10).\n *\n * **Placement (documented choice).** Lives in `presets/harness/` alongside\n * the other multi-agent coordination presets (`spawnable()`, `actorPool()`).\n * Per the 4-layer rubric this is a ≥3-utils composition (messaging `topic` +\n * `derived` arbitration + Guard ABAC) wiring multi-agent coordination — the\n * same charter as `harness/`'s existing `spawnable`/`actorPool`. A separate\n * `presets/multi-agent/` directory was rejected to avoid fragmenting the\n * multi-agent presets across two folders for a single factory (L6: \"recipe +\n * preset, NO new primitive\").\n *\n * **What it is.** A `Graph` that owns a shared ownership `topic` (Q3 — one\n * topic carries claim / release / override; subscribers narrow by `kind`),\n * a reactive `current` derivation that folds the ownership log applying the\n * L0–L3 staircase, and a `guard` (`policyAllowing` — the Q7 reactive-options\n * Guard widening) the caller mounts on the owned subgraph. It **consumes the\n * existing DS-14 {@link OwnershipChange}** envelope — it does NOT redefine it.\n *\n * **Staircase (Q10 — `level` is a priority axis, NOT a mechanism enum):**\n * - **L1 TTL** — a `claim` carries a level; the controller's `ttl` (ms)\n * bounds the live window. L1 honors TTL strictly (Q4): a crash inside the\n * window does NOT early-release; recommend `ttl ≤ 60s`.\n * - **L2 heartbeat** — `heartbeat?: NodeInput<unknown>` (Q2). Any reactive\n * trigger Node; each emission resets the countdown (\"max tolerance since\n * last sign of life\", unified across L1/L2). No library timer is shipped\n * and no `claim.heartbeat()` method exists (`feedback_no_imperative` +\n * `feedback_no_imperative_wrap_as_primitive`).\n * - **L3 supervisor** — a `kind:\"override\"` change wins by `level` priority\n * regardless of expiry/heartbeat (priority axis independent of the expiry\n * axis). Supervisor publishes to the SAME topic (Q3).\n *\n * **No polling / no timer (spec §5.8/§5.9/§5.10).** Expiry is evaluated\n * reactively: `current` recomputes whenever the ownership topic OR the\n * heartbeat OR the optional `clock` trigger emits, folding the whole log\n * from scratch (idempotent — no carried mutable cursor). Auto-release on\n * wall-clock TTL requires the caller to wire a reactive `clock` tick\n * (`fromTimer({ ms })` or an activity-derived Node) — the library does not\n * own a timer (L6 / Q2). Without `clock`, expiry still resolves at the next\n * topic/heartbeat emission and on any read that recomputes the derivation.\n *\n * @module\n */\n\nimport { type Node, type NodeGuard, policyAllowing, wallClockNs } from \"@graphrefly/pure-ts/core\";\nimport type { NodeInput, OwnershipChange, OwnershipChangePayload } from \"@graphrefly/pure-ts/extra\";\nimport { fromAny } from \"@graphrefly/pure-ts/extra\";\nimport { Graph } from \"@graphrefly/pure-ts/graph\";\nimport { type TopicGraph, topic } from \"../../utils/messaging/index.js\";\n\n/** Ownership level — priority axis (Q10). Higher rank = higher priority. */\nconst LEVEL_RANK = { L0: 0, L1: 1, L2: 2, L3: 3 } as const;\ntype OwnershipLevel = keyof typeof LEVEL_RANK;\n\n/** Options for {@link ownershipController}. */\nexport type OwnershipControllerOptions = {\n\t/**\n\t * TTL (milliseconds) bounding the live window of a claim. Honored\n\t * strictly (Q4) — a crash inside the window does not early-release.\n\t * Recommend ≤ 60_000 for L1 holds; wire `heartbeat` (L2) for longer.\n\t */\n\treadonly ttl: number;\n\t/**\n\t * L2 heartbeat (Q2). Any reactive trigger Node — each emission resets the\n\t * TTL countdown. Simple: `fromTimer({ ms: ttl / 3 })`. Activity-based:\n\t * `derived([toolCalls.events], …)`. Omitted → pure L1 TTL semantics.\n\t */\n\treadonly heartbeat?: NodeInput<unknown>;\n\t/**\n\t * L3 supervisor id. A `kind:\"override\"` change whose `actor` equals this\n\t * id wins by `level` priority regardless of expiry/heartbeat. Override\n\t * delivery is the shared topic with the `kind:\"override\"` discriminant\n\t * (Q3) — not a separate priority topic.\n\t */\n\treadonly supervisor?: string;\n\t/**\n\t * Optional reactive clock trigger used ONLY to re-evaluate TTL expiry\n\t * (e.g. `fromTimer({ ms: 1_000 })`). The library ships no timer (L6/Q2);\n\t * wire this for wall-clock-driven auto-release without an intervening\n\t * claim/heartbeat. Without it, expiry resolves lazily on the next\n\t * topic/heartbeat emission.\n\t */\n\treadonly clock?: NodeInput<unknown>;\n\t/** Bounded retention for the ownership topic (default 256). */\n\treadonly retainedLimit?: number;\n};\n\n/**\n * Resolved ownership state emitted by {@link OwnershipControllerGraph.current}.\n * `owner` is `null` when unclaimed or the live claim has expired.\n */\nexport type OwnershipState = {\n\treadonly owner: string | null;\n\treadonly level: OwnershipLevel | null;\n\t/** Allow-set fed to the Guard. `[]` (deny-all) when `owner === null`. */\n\treadonly allowed: readonly string[];\n\t/**\n\t * Internal (F3/F4) — last sign-of-life (wall-clock ns) for the *current*\n\t * claim: `max(claim.sinceNs, last in-window heartbeat)`. Carried in the\n\t * derivation's OWN emitted state (read back via `ctx.prevData`) so the\n\t * fold is pure — same (folded log, beat-this-wave, now, prevState) →\n\t * same output. NEVER an instance field. `null` when unclaimed. Scoped to\n\t * the active claim: a value older than the active claim's `sinceNs` (a\n\t * prior owner's beat) is discarded so it cannot extend a new owner.\n\t */\n\treadonly signOfLifeNs: number | null;\n};\n\nconst EMPTY_STATE: OwnershipState = {\n\towner: null,\n\tlevel: null,\n\tallowed: [],\n\tsignOfLifeNs: null,\n};\n\ntype ActiveOwner = { owner: string; level: OwnershipLevel; sinceNs: number };\n\n/**\n * Multi-agent subgraph ownership controller. See module docs.\n *\n * Public surface:\n * - `topic` — the shared ownership `TopicGraph<OwnershipChange>` (Q3).\n * Agents publish claim/release/override here (use the `claim`/`release`/\n * `override` helpers — thin reactive wrappers over `topic.publish`, i.e.\n * message flow, NOT imperative triggers).\n * - `current` — `Node<OwnershipState>`: the reactively-resolved owner after\n * applying L1 TTL + L2 heartbeat + L3 supervisor arbitration.\n * - `allowed` — `Node<readonly string[]>`: the Guard allow-set (derived from\n * `current`); re-points on claim/release/override with no rewire.\n * - `guard` — `NodeGuard` from `policyAllowing(this.allowed)`. Mount on the\n * owned subgraph's nodes (`node({ guard })`) for the Q7 hard-block.\n */\nexport class OwnershipControllerGraph extends Graph {\n\treadonly topic: TopicGraph<OwnershipChange>;\n\treadonly current: Node<OwnershipState>;\n\treadonly allowed: Node<readonly string[]>;\n\treadonly guard: NodeGuard;\n\n\tprivate readonly _ttlNs: number;\n\tprivate readonly _supervisor: string | undefined;\n\t/**\n\t * Whether a heartbeat `NodeInput` was supplied at construction (F14 —\n\t * `claim()`'s `level` default is `\"L2\"` when wired, else `\"L1\"`). NOT a\n\t * mutable accumulator — set once in the constructor, read-only after.\n\t */\n\tprivate readonly _hasHeartbeat: boolean;\n\n\tconstructor(name: string, opts: OwnershipControllerOptions) {\n\t\tsuper(name);\n\t\tthis._ttlNs = Math.max(0, opts.ttl) * 1_000_000;\n\t\tthis._supervisor = opts.supervisor;\n\t\tthis._hasHeartbeat = opts.heartbeat != null;\n\n\t\tthis.topic = topic<OwnershipChange>(`${name}__ownership`, {\n\t\t\tretainedLimit: opts.retainedLimit ?? 256,\n\t\t});\n\t\t// The topic is its own TopicGraph; tear it down with this controller.\n\t\t// (Not mounted — `topic.events` already belongs to the TopicGraph; a\n\t\t// re-`add` would violate single-graph node ownership. Consumers\n\t\t// inspect ownership via `current` / `allowed`, not via a mount.)\n\t\tthis.addDisposer(() => {\n\t\t\tthis.topic.destroy();\n\t\t});\n\n\t\t// `current` recomputes whenever the ownership stream changes, the\n\t\t// heartbeat fires, or the optional clock ticks — the only sources\n\t\t// that can change the resolved owner. ALL deps wired BEFORE any claim\n\t\t// can be published (observers before emitters — §47 rule 2).\n\t\tconst deps: Node<unknown>[] = [this.topic.events as Node<unknown>];\n\t\tconst heartbeatIdx = opts.heartbeat != null ? deps.push(fromAny(opts.heartbeat)) - 1 : -1;\n\t\tif (opts.clock != null) deps.push(fromAny(opts.clock) as Node<unknown>);\n\n\t\tthis.current = this.derived<OwnershipState>(\n\t\t\t\"currentOwner\",\n\t\t\tdeps,\n\t\t\t(batchData, ctx) => {\n\t\t\t\t// Wall-clock (F2) — must match `makeChange`'s `t_ns` stamp so\n\t\t\t\t// `nowNs - active.sinceNs` compares like-for-like. Mixing\n\t\t\t\t// monotonic + wall clocks (the prior bug) made TTL math\n\t\t\t\t// nonsense once the two clocks diverged.\n\t\t\t\tconst nowNs = wallClockNs();\n\t\t\t\t// Did the heartbeat dep emit this wave? `batchData[i]` is the\n\t\t\t\t// array of values dep `i` emitted THIS wave; a non-empty\n\t\t\t\t// heartbeat batch is one or more beats.\n\t\t\t\tconst beatThisWave =\n\t\t\t\t\theartbeatIdx >= 0 &&\n\t\t\t\t\t(() => {\n\t\t\t\t\t\tconst hb = batchData[heartbeatIdx] as readonly unknown[] | null | undefined;\n\t\t\t\t\t\treturn hb != null && hb.length > 0;\n\t\t\t\t\t})();\n\n\t\t\t\t// Fold the WHOLE ownership log from scratch (idempotent — the\n\t\t\t\t// topic emits the full retained array, so a cursor would be a\n\t\t\t\t// bug surface; pure reduction is correct and simple, §47).\n\t\t\t\t// `batchData[0]` is `(readonly OwnershipChange[])[]` — the\n\t\t\t\t// snapshots emitted this wave; take the latest (mirrors\n\t\t\t\t// `topic.latest`'s `batch.at(-1)` pattern). On a SENTINEL\n\t\t\t\t// first-activation wave fall back to `ctx.prevData[0]`.\n\t\t\t\tconst topoBatch = batchData[0] as\n\t\t\t\t\t| readonly (readonly OwnershipChange[])[]\n\t\t\t\t\t| null\n\t\t\t\t\t| undefined;\n\t\t\t\tconst log = (\n\t\t\t\t\ttopoBatch != null && topoBatch.length > 0\n\t\t\t\t\t\t? topoBatch[topoBatch.length - 1]\n\t\t\t\t\t\t: (ctx.prevData[0] as readonly OwnershipChange[] | undefined)\n\t\t\t\t) as readonly OwnershipChange[] | undefined;\n\t\t\t\tlet active: ActiveOwner | null = null;\n\t\t\t\tif (log != null) {\n\t\t\t\t\tfor (const ch of log) {\n\t\t\t\t\t\tif (ch?.change == null) continue;\n\t\t\t\t\t\tactive = applyChange(active, ch, this._supervisor);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// F3/F4 — PURE sign-of-life. Read the prior `signOfLifeNs`\n\t\t\t\t// from THIS derivation's own previously-emitted state\n\t\t\t\t// (`ctx.prevData[0]` for the `current` node is unavailable —\n\t\t\t\t// `prevData[0]` is the topic dep — so we read `ctx.cache`,\n\t\t\t\t// the node's own last emit). No instance field anywhere.\n\t\t\t\tconst prevState = (ctx.cache ?? undefined) as OwnershipState | undefined;\n\n\t\t\t\tlet nextActive: ActiveOwner | null = active;\n\t\t\t\tlet signOfLifeNs: number | null = null;\n\n\t\t\t\tif (active != null && this._ttlNs > 0) {\n\t\t\t\t\t// Carry the prior sign-of-life ONLY if it belongs to THIS\n\t\t\t\t\t// claim (>= the active claim's `sinceNs`). A value from a\n\t\t\t\t\t// prior owner (older than `sinceNs`) is discarded so a\n\t\t\t\t\t// stale beat cannot extend a freshly-claimed window. A new\n\t\t\t\t\t// owner therefore starts from its own claim time.\n\t\t\t\t\tconst carried =\n\t\t\t\t\t\tprevState?.signOfLifeNs != null && prevState.signOfLifeNs >= active.sinceNs\n\t\t\t\t\t\t\t? prevState.signOfLifeNs\n\t\t\t\t\t\t\t: active.sinceNs;\n\t\t\t\t\t// Expire FIRST against the carried sign-of-life (Q4 strict —\n\t\t\t\t\t// a late beat must NOT resurrect an already-lapsed claim),\n\t\t\t\t\t// THEN accept a still-timely beat THIS wave. `signOfLifeNs`\n\t\t\t\t\t// only ever advances on an actual beat-this-wave, so a\n\t\t\t\t\t// recompute storm with no beat cannot renew a dead claim\n\t\t\t\t\t// (idempotent re-fold).\n\t\t\t\t\tconst lapsed = nowNs - carried >= this._ttlNs;\n\t\t\t\t\tif (lapsed) {\n\t\t\t\t\t\tnextActive = null;\n\t\t\t\t\t\tsignOfLifeNs = null;\n\t\t\t\t\t} else if (beatThisWave) {\n\t\t\t\t\t\tsignOfLifeNs = nowNs; // timely beat → renew\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsignOfLifeNs = carried; // unchanged — carry forward\n\t\t\t\t\t}\n\t\t\t\t} else if (active != null) {\n\t\t\t\t\t// No TTL configured — never expires; sign-of-life still\n\t\t\t\t\t// tracked (owner-scoped) for completeness/observability.\n\t\t\t\t\tconst carried =\n\t\t\t\t\t\tprevState?.signOfLifeNs != null && prevState.signOfLifeNs >= active.sinceNs\n\t\t\t\t\t\t\t? prevState.signOfLifeNs\n\t\t\t\t\t\t\t: active.sinceNs;\n\t\t\t\t\tsignOfLifeNs = beatThisWave ? nowNs : carried;\n\t\t\t\t}\n\n\t\t\t\treturn [\n\t\t\t\t\tnextActive == null\n\t\t\t\t\t\t? EMPTY_STATE\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\towner: nextActive.owner,\n\t\t\t\t\t\t\t\tlevel: nextActive.level,\n\t\t\t\t\t\t\t\tallowed: [nextActive.owner],\n\t\t\t\t\t\t\t\tsignOfLifeNs,\n\t\t\t\t\t\t\t},\n\t\t\t\t];\n\t\t\t},\n\t\t\t{ keepAlive: true },\n\t\t);\n\n\t\tthis.allowed = this.derived<readonly string[]>(\n\t\t\t\"allowed\",\n\t\t\t[this.current],\n\t\t\t(batchData, ctx) => {\n\t\t\t\tconst batch = batchData[0] as readonly OwnershipState[] | null | undefined;\n\t\t\t\tconst s = (batch != null && batch.length > 0 ? batch[batch.length - 1] : ctx.prevData[0]) as\n\t\t\t\t\t| OwnershipState\n\t\t\t\t\t| undefined;\n\t\t\t\treturn [s?.allowed ?? []];\n\t\t\t},\n\t\t\t{ keepAlive: true },\n\t\t);\n\t\t// F12 — `keepAlive: true` above already installs a self-pruning\n\t\t// keepalive subscription (same as `current`); a second\n\t\t// `keepalive(this.allowed)` disposer was redundant double-subscription.\n\n\t\t// Q7 — the reactive-options Guard. `policyAllowing` reads\n\t\t// `this.allowed.cache` synchronously at write-check time, so\n\t\t// claim/release/override re-point the allow-set with NO rewire.\n\t\tthis.guard = policyAllowing(this.allowed);\n\t}\n\n\t/**\n\t * Publish a `claim`. Thin reactive wrapper over `topic.publish` (message\n\t * flow per §29 — NOT an imperative trigger). `level` defaults to `\"L2\"`\n\t * when this controller has a heartbeat wired, else `\"L1\"`.\n\t */\n\tclaim(actor: string, level?: OwnershipLevel): void {\n\t\t// F14 — documented default (JSDoc + §47): L2 when a heartbeat\n\t\t// NodeInput was wired at construction, else L1.\n\t\tconst lvl = level ?? (this._hasHeartbeat ? \"L2\" : \"L1\");\n\t\tthis.topic.publish(makeChange({ kind: \"claim\", subgraphId: this.name, actor, level: lvl }));\n\t}\n\n\t/** Publish a `release`. Clears ownership iff `actor` is the current owner. */\n\trelease(actor: string): void {\n\t\tthis.topic.publish(makeChange({ kind: \"release\", subgraphId: this.name, actor }));\n\t}\n\n\t/**\n\t * Publish a supervisor `override` (L3). Wins by `level` priority\n\t * regardless of expiry (Q10). `actor` should be this controller's\n\t * `supervisor` id for the override to take precedence.\n\t */\n\toverride(actor: string, previousActor: string, reason: string): void {\n\t\tthis.topic.publish(\n\t\t\tmakeChange({ kind: \"override\", subgraphId: this.name, actor, previousActor, reason }),\n\t\t);\n\t}\n}\n\n/** Wrap an {@link OwnershipChangePayload} in the DS-14 {@link OwnershipChange} envelope. */\nfunction makeChange(payload: OwnershipChangePayload): OwnershipChange {\n\t// F2 — `BaseChange.t_ns` is contractually wall-clock (`wallClockNs()`,\n\t// see change.ts). The fold compares `nowNs (wallClockNs)` against\n\t// `ch.t_ns` for TTL/expiry, so stamp + compare MUST use the same clock.\n\tconst t = wallClockNs();\n\treturn { structure: \"ownership\", version: t, t_ns: t, lifecycle: \"ownership\", change: payload };\n}\n\n/**\n * Fold one ownership change into the resolved-owner state.\n *\n * - `claim` — sets the active owner (records claim time for L1 TTL). A\n * lower-priority claim cannot displace a higher-`level` live owner (Q10 —\n * override arbitration is pure level comparison).\n * - `release` — clears ownership iff the releasing actor is the owner.\n * - `override` — supervisor override: wins by `level` priority. Carries\n * `previousActor` + `reason` per DS-14 (Q3); modeled as an L3 hand-off to\n * `p.actor`.\n */\nfunction applyChange(\n\tactive: ActiveOwner | null,\n\tch: OwnershipChange,\n\tsupervisor: string | undefined,\n): ActiveOwner | null {\n\tconst p = ch.change;\n\t// Use the change's publish timestamp (`t_ns` — wall-clock, stamped in\n\t// `makeChange`) as the claim time — NOT the fold time. The log is re-folded\n\t// from scratch on every recompute (§47), so stamping a fresh clock read\n\t// here would re-baseline the TTL window every recompute and the claim would\n\t// never expire. The fold compares against `wallClockNs()` (F2 — same clock).\n\tif (p.kind === \"claim\") {\n\t\tif (active != null && LEVEL_RANK[active.level] > LEVEL_RANK[p.level]) return active;\n\t\treturn { owner: p.actor, level: p.level, sinceNs: ch.t_ns };\n\t}\n\tif (p.kind === \"release\") {\n\t\tif (active != null && active.owner === p.actor) return null;\n\t\treturn active;\n\t}\n\t// override (F5) — a `kind:\"override\"` only seizes ownership when the\n\t// publishing actor IS the configured supervisor. The prior disjunction\n\t// `|| LEVEL_RANK.L3 >= LEVEL_RANK[active.level]` was a tautology (L3 is the\n\t// max rank ⇒ always true), so ANY actor's override took over. If no\n\t// supervisor is configured, overrides are explicitly disabled (a non-null\n\t// `supervisor` is the gate, not an accidental fall-through).\n\tconst isSupervisor = supervisor != null && p.actor === supervisor;\n\tif (isSupervisor) {\n\t\treturn { owner: p.actor, level: \"L3\", sinceNs: ch.t_ns };\n\t}\n\treturn active;\n}\n\n/**\n * Create a multi-agent subgraph ownership controller (DS-14.5.A #8).\n *\n * @example\n * ```ts\n * const oc = ownershipController(\"payments\", { ttl: 30_000, supervisor: \"lead\" });\n * // Mount the Guard on the owned subgraph:\n * const n = node([], { initial: 0, guard: oc.guard });\n * oc.claim(\"agent-a\"); // agent-a now owns; non-owner writes throw\n * oc.override(\"lead\", \"agent-a\", \"rebalance\"); // supervisor takes over\n * ```\n */\nexport function ownershipController(\n\tname: string,\n\topts: OwnershipControllerOptions,\n): OwnershipControllerGraph {\n\treturn new OwnershipControllerGraph(name, opts);\n}\n","/**\n * Harness-specific graph profiling (roadmap §9.0).\n *\n * Extends {@link graphProfile} with harness domain counters:\n * queue depths, strategy entries, retry/reingestion tracker sizes.\n *\n * @module\n */\n\nimport {\n\ttype GraphProfileOptions,\n\ttype GraphProfileResult,\n\tgraphProfile,\n} from \"@graphrefly/pure-ts/graph\";\nimport { QUEUE_NAMES } from \"../../utils/harness/defaults.js\";\nimport type { QueueRoute, TriagedItem } from \"../../utils/harness/types.js\";\nimport type { HarnessGraph } from \"./harness-loop.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Harness-specific profile extending the base graph profile. */\nexport interface HarnessProfileResult extends GraphProfileResult {\n\t/** Per-queue retained item counts. */\n\tqueueDepths: Record<QueueRoute, number>;\n\t/** Number of rootCause→intervention entries in the strategy model. */\n\tstrategyEntries: number;\n\t/** Global retry count across all items. */\n\ttotalRetries: number;\n\t/** Global reingestion count across all items. */\n\ttotalReingestions: number;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Profile a harness graph with domain-specific counters.\n *\n * **Snapshot caveat (Unit 22 B).** Reads `.cache` values from the\n * strategy / retry / reingestion nodes + each queue topic's `.retained()`\n * view. These are point-in-time reads and are not transactional — if you\n * invoke this during an in-flight reactive wave the values may reflect\n * a partially-settled frame. For end-of-wave accuracy, call from outside\n * any batch boundary.\n *\n * @param harness - The HarnessGraph to profile.\n * @param opts - Optional base profile options.\n * @returns Harness profile with queue depths, strategy stats, and tracker sizes.\n */\nexport function harnessProfile(\n\tharness: HarnessGraph,\n\topts?: GraphProfileOptions,\n): HarnessProfileResult {\n\tconst base = graphProfile(harness, opts);\n\n\t// Unit 22 B: iterate the hub's topic registry instead of a raw Map so\n\t// queue topics added post-construction (dead-letter `__unrouted`, etc.)\n\t// don't get silently ignored.\n\tconst queueDepths: Record<string, number> = {};\n\tfor (const route of QUEUE_NAMES) {\n\t\tconst t = harness.queues.has(route) ? harness.queues.topic<TriagedItem>(route) : null;\n\t\tqueueDepths[route] = t?.retained().length ?? 0;\n\t}\n\n\treturn {\n\t\t...base,\n\t\tqueueDepths: queueDepths as Record<QueueRoute, number>,\n\t\tstrategyEntries: harness.strategy.entries.cache?.size ?? 0,\n\t\ttotalRetries: harness.totalRetries.cache ?? 0,\n\t\ttotalReingestions: harness.totalReingestions.cache ?? 0,\n\t};\n}\n","/**\n * Harness pipeline trace — thin sugar over `graph.observe({ format: \"stage-log\" })`.\n *\n * Since 2026-04-22 (D2), stage-labeled tracing is a first-class observe format\n * on {@link Graph}. `harnessTrace` wires that format over the 7 pipeline stages\n * (INTAKE → TRIAGE → QUEUE → GATE → EXECUTE → VERIFY → STRATEGY) with sensible\n * defaults so harness consumers don't need to restate the stage map.\n *\n * For non-harness graphs, call `graph.observe({ format: \"stage-log\", stageLabels })`\n * directly — the format is domain-agnostic.\n *\n * @module\n */\n\nimport { monotonicNs } from \"@graphrefly/pure-ts/core\";\nimport type { ObserveEvent, ObserveResult } from \"@graphrefly/pure-ts/graph\";\nimport type { HarnessGraph } from \"./harness-loop.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Event type captured by structured trace. */\nexport type TraceEventType = \"data\" | \"error\" | \"complete\";\n\n/** A single structured trace event. */\nexport interface TraceEvent {\n\t/** Elapsed seconds since trace was created. */\n\telapsed: number;\n\t/** Pipeline stage label (INTAKE, TRIAGE, QUEUE, GATE, EXECUTE, VERIFY, STRATEGY). */\n\tstage: string;\n\t/** Event type. */\n\ttype: TraceEventType;\n\t/** Data payload (present for \"data\" and \"error\" events). Omitted at \"summary\" detail. */\n\tdata?: unknown;\n\t/** Human-readable summary of the data. Present at \"standard\" and \"full\" detail. */\n\tsummary?: string;\n}\n\n/** Detail level for trace output. */\nexport type TraceDetail =\n\t/** Stage + elapsed only. No data preview. Lowest overhead. */\n\t| \"summary\"\n\t/** Stage + elapsed + truncated data preview. Default. */\n\t| \"standard\"\n\t/** Stage + elapsed + full raw data. Use for debugging, not production. */\n\t| \"full\";\n\n/** Handle returned by {@link harnessTrace}. Call `dispose()` to stop tracing. */\nexport interface HarnessTraceHandle {\n\t/** Stop tracing and detach all observers. Safe to call multiple times. */\n\tdispose(): void;\n\t/**\n\t * Structured trace events collected since creation. Plain array — no\n\t * subscription needed (COMPOSITION-GUIDE §1: avoid lazy-activation\n\t * friction for inspection tools). Populated reactively via observe().\n\t */\n\treadonly events: readonly TraceEvent[];\n}\n\n/** Options for {@link harnessTrace}. */\nexport interface HarnessTraceOptions {\n\t/** Sink for rendered trace lines. Default: `console.log`. Pass `null` for structured-only. */\n\tlogger?: ((line: string) => void) | null;\n\t/** Detail level for both string and structured output. Default: `\"summary\"`. */\n\tdetail?: TraceDetail;\n}\n\n// ---------------------------------------------------------------------------\n// Stage labels\n// ---------------------------------------------------------------------------\n\n/**\n * Observe paths → stage labels for the 7 harness stages. Path set is\n * sourced from {@link HarnessGraph.stageNodes} so inspection tools stay\n * decoupled from mount-structure changes (Unit 22 C).\n */\nfunction buildStageLabels(harness: HarnessGraph): Record<string, string> {\n\tconst labels: Record<string, string> = {};\n\tfor (const { label, paths } of harness.stageNodes()) {\n\t\tfor (const p of paths) labels[p] = label;\n\t}\n\treturn labels;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Attach a stage-log trace over the harness pipeline. Delegates to\n * `harness.observe({ format: \"stage-log\", ... })` for each stage path —\n * every event is captured in `handle.events` (structured) AND rendered via\n * the `logger` (string output).\n *\n * **Detail levels:**\n * - `\"summary\"` — stage + elapsed only. Minimal overhead.\n * - `\"standard\"` (default) — stage + elapsed + truncated data preview.\n * - `\"full\"` — stage + elapsed + full raw data object in events.\n *\n * Elapsed timestamps are relative to the `harnessTrace()` invocation time,\n * not the first event.\n */\nexport function harnessTrace(\n\tharness: HarnessGraph,\n\topts?: HarnessTraceOptions,\n): HarnessTraceHandle {\n\tconst logger: ((line: string) => void) | null =\n\t\topts?.logger === null ? null : (opts?.logger ?? console.log);\n\tconst detail: TraceDetail = opts?.detail ?? \"summary\";\n\tconst startNs = monotonicNs();\n\tconst observations: ObserveResult[] = [];\n\tconst events: TraceEvent[] = [];\n\tconst stageLabels = buildStageLabels(harness);\n\n\tfunction elapsedSecs(): number {\n\t\treturn (monotonicNs() - startNs) / 1e9;\n\t}\n\n\tfunction recordEvent(stage: string, type: TraceEventType, rawData: unknown): void {\n\t\tconst ev: TraceEvent = { elapsed: elapsedSecs(), stage, type };\n\t\tif (detail !== \"summary\") ev.summary = summarize(rawData);\n\t\tif (detail === \"full\") ev.data = rawData;\n\t\tevents.push(ev);\n\t}\n\n\t// One observe call per path — keeps per-stage elapsed offsets anchored to\n\t// this invocation (the shared stage-log format uses its own elapsed clock\n\t// per observation, which matches the legacy behavior). We also intercept\n\t// each event through `onEvent` so structured `events[]` stays populated\n\t// regardless of `logger`.\n\tfor (const [path, stage] of Object.entries(stageLabels)) {\n\t\ttry {\n\t\t\tconst obs = harness.observe(path, {\n\t\t\t\tformat: \"stage-log\",\n\t\t\t\tstageLabels,\n\t\t\t\tlogger: logger ? (line: string) => logger(line) : () => {},\n\t\t\t\tincludeTypes: [\"data\", \"error\", \"complete\"],\n\t\t\t});\n\t\t\tobs.onEvent((event: ObserveEvent) => {\n\t\t\t\tif (event.type === \"data\") recordEvent(stage, \"data\", (event as { data: unknown }).data);\n\t\t\t\telse if (event.type === \"error\")\n\t\t\t\t\trecordEvent(stage, \"error\", (event as { data: unknown }).data);\n\t\t\t\telse if (event.type === \"complete\") recordEvent(stage, \"complete\", undefined);\n\t\t\t});\n\t\t\tobservations.push(obs);\n\t\t} catch (err) {\n\t\t\t// Node may not exist yet (e.g., a gated-queue route that hasn't been\n\t\t\t// mounted on this harness). Record a synthetic error trace event so\n\t\t\t// consumers see WHICH stage dropped out and why — silent swallow\n\t\t\t// breaks dry-run equivalence (a regression in stage wiring would\n\t\t\t// not surface in the trace).\n\t\t\tconst msg = err instanceof Error ? err.message : String(err);\n\t\t\trecordEvent(stage, \"error\", `observe-unavailable: ${path} — ${msg}`);\n\t\t\tif (logger) {\n\t\t\t\tlogger(`[${elapsedSecs().toFixed(3)}s] ${stage.padEnd(9)} ✗ observe-unavailable: ${msg}`);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tget events(): readonly TraceEvent[] {\n\t\t\treturn events;\n\t\t},\n\t\tdispose() {\n\t\t\tfor (const obs of observations) obs.dispose();\n\t\t\tobservations.length = 0;\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — kept here because `observe({ format: \"stage-log\" })` emits short\n// one-line previews; the structured `events[]` is free to carry richer\n// summaries with different truncation bounds (120 for JSON, 80 for strings).\n// ---------------------------------------------------------------------------\n\nfunction summarize(value: unknown): string {\n\tif (value == null) return \"null\";\n\tif (typeof value === \"string\") return truncate(value, 80);\n\tif (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n\tif (typeof value === \"bigint\") return String(value);\n\ttry {\n\t\tconst json = JSON.stringify(value);\n\t\treturn truncate(json, 120);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n\nfunction truncate(s: string, max: number): string {\n\treturn s.length > max ? `${s.slice(0, max - 1)}…` : s;\n}\n"],"mappings":"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","names":["node","filter","Graph","node","node","batch","defaultToOutput","Graph","node","batch","filter","Graph","Graph","batch"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
firstValueFrom
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-N6MNJNHB.js";
|
|
4
4
|
|
|
5
5
|
// src/base/composition/single-from-any.ts
|
|
6
|
-
import { COMPLETE, ERROR } from "@graphrefly/pure-ts/core";
|
|
6
|
+
import { COMPLETE, ERROR, TEARDOWN } from "@graphrefly/pure-ts/core";
|
|
7
7
|
import { fromAny } from "@graphrefly/pure-ts/extra";
|
|
8
8
|
function singleFromAny(factory, opts = {}) {
|
|
9
9
|
const keyFn = opts.keyFn ?? ((k) => String(k));
|
|
@@ -70,11 +70,12 @@ function singleNodeFromAny(factory, opts = {}) {
|
|
|
70
70
|
if (existing) return existing;
|
|
71
71
|
const node = fromAny(factory(key));
|
|
72
72
|
inFlight.set(k, node);
|
|
73
|
-
|
|
73
|
+
let unsub;
|
|
74
|
+
unsub = node.subscribe((msgs) => {
|
|
74
75
|
for (const m of msgs) {
|
|
75
|
-
if (m[0] === ERROR || m[0] === COMPLETE) {
|
|
76
|
+
if (m[0] === ERROR || m[0] === COMPLETE || m[0] === TEARDOWN) {
|
|
76
77
|
if (inFlight.get(k) === node) inFlight.delete(k);
|
|
77
|
-
unsub();
|
|
78
|
+
unsub?.();
|
|
78
79
|
return;
|
|
79
80
|
}
|
|
80
81
|
}
|
|
@@ -87,4 +88,4 @@ export {
|
|
|
87
88
|
singleFromAny,
|
|
88
89
|
singleNodeFromAny
|
|
89
90
|
};
|
|
90
|
-
//# sourceMappingURL=chunk-
|
|
91
|
+
//# sourceMappingURL=chunk-V46JWFGV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/base/composition/single-from-any.ts"],"sourcesContent":["/**\n * `singleFromAny` — keyed promise/Node de-duplication (\"singleflight\").\n *\n * Given a `factory: (key) => NodeInput<T>`, returns a callable that dedupes\n * concurrent invocations sharing the same key — all callers with the same\n * key while a request is in-flight receive the same `Promise<T>`. Once the\n * underlying source settles (DATA, ERROR, or COMPLETE), the cache entry is\n * cleared so the next call re-invokes the factory.\n *\n * This is the classic \"singleflight\" pattern from Go, generalised over the\n * library's `NodeInput<T>` bridge so callers can pass Promise-returning\n * factories, Node-returning factories, or plain value factories with\n * identical semantics.\n *\n * Use cases:\n * - `withReplayCache` cache-miss thundering-herd dedup\n * - Shared HTTP fetches keyed by URL\n * - Expensive compute keyed by request fingerprint\n *\n * @example\n * ```ts\n * const fetchUser = singleFromAny<string, User>((id) => fetch(`/users/${id}`).then(r => r.json()));\n * // Two concurrent callers with id=\"42\" → one underlying fetch, two Promises resolving to the same User.\n * const [a, b] = await Promise.all([fetchUser(\"42\"), fetchUser(\"42\")]);\n * ```\n *\n * @category extra\n */\n\nimport type { Node } from \"@graphrefly/pure-ts/core\";\nimport { COMPLETE, ERROR, TEARDOWN } from \"@graphrefly/pure-ts/core\";\n// Import directly from the source sub-files (rather than the `./sources.js`\n// barrel) so the `single-from-any` module is NOT part of any cycle that runs\n// through `extra/sources/index.ts` — eager re-exports through the barrel were\n// observed to leave `firstValueFrom` / `keepalive` unresolved during nested\n// import chains under vite-node.\nimport { fromAny, type NodeInput } from \"@graphrefly/pure-ts/extra\";\nimport { firstValueFrom } from \"../sources/settled.js\";\n\nexport interface SingleFromAnyOptions<K> {\n\t/**\n\t * Convert a typed key into a cache-string. Defaults to `String(key)`, which\n\t * works for primitive keys; callers with object keys should provide a\n\t * stable serializer (e.g., canonical JSON).\n\t */\n\tkeyFn?: (key: K) => string;\n}\n\n/**\n * Dedupe concurrent `factory(key)` invocations. Returns a bound callable.\n *\n * @param factory - Produces a `NodeInput<T>` for each unique key.\n * @param opts - Optional key-stringification.\n * @returns A function `(key: K) => Promise<T>` whose inflight results are shared per key.\n */\nexport function singleFromAny<K, T>(\n\tfactory: (key: K) => NodeInput<T>,\n\topts: SingleFromAnyOptions<K> = {},\n): (key: K) => Promise<T> {\n\tconst keyFn = opts.keyFn ?? ((k: K) => String(k));\n\tconst inFlight = new Map<string, Promise<T>>();\n\n\treturn (key: K): Promise<T> => {\n\t\tconst k = keyFn(key);\n\t\tconst existing = inFlight.get(k);\n\t\tif (existing) return existing;\n\n\t\tconst input = factory(key);\n\n\t\t// Resolve the NodeInput to a Promise<T>. Different input shapes need\n\t\t// different bridges — Promise/Node/AsyncIterable/Iterable/plain value.\n\t\tlet rawPromise: Promise<T>;\n\t\tif (input != null && typeof (input as PromiseLike<T>).then === \"function\") {\n\t\t\trawPromise = Promise.resolve(input as PromiseLike<T>);\n\t\t} else if (\n\t\t\tinput != null &&\n\t\t\ttypeof input === \"object\" &&\n\t\t\t\"subscribe\" in (input as object) &&\n\t\t\t\"cache\" in (input as object)\n\t\t) {\n\t\t\t// Node: bridge via firstValueFrom.\n\t\t\trawPromise = firstValueFrom(input as Node<T>);\n\t\t} else if (\n\t\t\tinput != null &&\n\t\t\ttypeof input === \"object\" &&\n\t\t\tSymbol.asyncIterator in (input as object)\n\t\t) {\n\t\t\t// AsyncIterable — take the first value, then close the iterator so\n\t\t\t// any owned resources (HTTP body, subscription, timer) are released.\n\t\t\trawPromise = (async () => {\n\t\t\t\tconst iter = (input as AsyncIterable<T>)[Symbol.asyncIterator]();\n\t\t\t\ttry {\n\t\t\t\t\tconst { value, done } = await iter.next();\n\t\t\t\t\tif (done) throw new Error(\"singleFromAny: factory returned empty async iterable\");\n\t\t\t\t\treturn value as T;\n\t\t\t\t} finally {\n\t\t\t\t\tawait iter.return?.();\n\t\t\t\t}\n\t\t\t})();\n\t\t} else if (input != null && typeof input === \"object\" && Symbol.iterator in (input as object)) {\n\t\t\t// Iterable — take the first value, close the iterator.\n\t\t\trawPromise = (async () => {\n\t\t\t\tconst iter = (input as Iterable<T>)[Symbol.iterator]();\n\t\t\t\ttry {\n\t\t\t\t\tconst { value, done } = iter.next();\n\t\t\t\t\tif (done) throw new Error(\"singleFromAny: factory returned empty iterable\");\n\t\t\t\t\treturn value as T;\n\t\t\t\t} finally {\n\t\t\t\t\titer.return?.();\n\t\t\t\t}\n\t\t\t})();\n\t\t} else {\n\t\t\t// Plain value.\n\t\t\trawPromise = Promise.resolve(input as T);\n\t\t}\n\n\t\t// Install the cache entry BEFORE attaching `.finally`. Otherwise a\n\t\t// sync-resolved Promise's finally microtask could run before the\n\t\t// `inFlight.set` below, leaving a stale entry installed afterwards.\n\t\t// We wrap in a holder whose reference we capture *before* chaining.\n\t\tlet tracked!: Promise<T>;\n\t\tconst cleanup = (): void => {\n\t\t\tif (inFlight.get(k) === tracked) inFlight.delete(k);\n\t\t};\n\t\ttracked = rawPromise.then(\n\t\t\t(v) => {\n\t\t\t\tcleanup();\n\t\t\t\treturn v;\n\t\t\t},\n\t\t\t(e) => {\n\t\t\t\tcleanup();\n\t\t\t\tthrow e;\n\t\t\t},\n\t\t);\n\t\tinFlight.set(k, tracked);\n\t\treturn tracked;\n\t};\n}\n\n/**\n * Reactive variant: returns a bound callable that hands out `Node<T>` values.\n * All concurrent callers with the same key during an in-flight source share\n * the same Node. The cache entry is evicted (so the next call re-invokes\n * `factory`) when the underlying source either:\n *\n * - **terminally settles** — `ERROR` or `COMPLETE`; or\n * - **tears down** — `TEARDOWN` (M8 fix). A DATA-only source (e.g. a\n * long-lived `state(...)`) never emits `ERROR`/`COMPLETE`, so without\n * the TEARDOWN arm a destroyed shared Node — plus this watcher\n * subscription — would be pinned in the `inFlight` Map forever. Evicting\n * on TEARDOWN bounds the entry's lifetime to the Node's own lifetime.\n *\n * DATA is NOT an eviction trigger — callers subscribing after the first\n * DATA still receive the shared Node (and push-on-subscribe per the spec's\n * cached-DATA contract). The Node stays shared while alive (the dedup\n * contract); only its death (terminal or teardown) releases the entry.\n *\n * Use when downstream wants reactive subscription (not a one-shot Promise).\n *\n * @category extra\n */\nexport function singleNodeFromAny<K, T>(\n\tfactory: (key: K) => NodeInput<T>,\n\topts: SingleFromAnyOptions<K> = {},\n): (key: K) => Node<T> {\n\tconst keyFn = opts.keyFn ?? ((k: K) => String(k));\n\tconst inFlight = new Map<string, Node<T>>();\n\n\treturn (key: K): Node<T> => {\n\t\tconst k = keyFn(key);\n\t\tconst existing = inFlight.get(k);\n\t\tif (existing) return existing;\n\n\t\tconst node = fromAny(factory(key));\n\t\tinFlight.set(k, node);\n\n\t\t// Evict on Node death — terminal settle (ERROR / COMPLETE) OR\n\t\t// TEARDOWN (M8). DATA is a value emission, not a lifecycle\n\t\t// transition; multi-emitting Nodes continue to share across\n\t\t// subscribers after the first value. The TEARDOWN arm is what\n\t\t// releases DATA-only `state(...)` sources, which never terminate.\n\t\t// `unsub` is hoisted (mirrors `singleFromAny`'s `let tracked!`): the\n\t\t// sink closes over it, and a `fromAny` source that synchronously\n\t\t// replays a cached terminal/TEARDOWN on subscribe would otherwise hit\n\t\t// `unsub` in its TDZ. In that sync-terminal case `unsub` is still\n\t\t// `undefined` when the sink runs — `unsub?.()` no-ops, but the\n\t\t// load-bearing `inFlight.delete(k)` eviction still runs and an\n\t\t// already-terminal node won't re-emit (the dangling sub is inert).\n\t\tlet unsub: (() => void) | undefined;\n\t\tunsub = node.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (m[0] === ERROR || m[0] === COMPLETE || m[0] === TEARDOWN) {\n\t\t\t\t\tif (inFlight.get(k) === node) inFlight.delete(k);\n\t\t\t\t\tunsub?.();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn node;\n\t};\n}\n"],"mappings":";;;;;AA8BA,SAAS,UAAU,OAAO,gBAAgB;AAM1C,SAAS,eAA+B;AAmBjC,SAAS,cACf,SACA,OAAgC,CAAC,GACR;AACzB,QAAM,QAAQ,KAAK,UAAU,CAAC,MAAS,OAAO,CAAC;AAC/C,QAAM,WAAW,oBAAI,IAAwB;AAE7C,SAAO,CAAC,QAAuB;AAC9B,UAAM,IAAI,MAAM,GAAG;AACnB,UAAM,WAAW,SAAS,IAAI,CAAC;AAC/B,QAAI,SAAU,QAAO;AAErB,UAAM,QAAQ,QAAQ,GAAG;AAIzB,QAAI;AACJ,QAAI,SAAS,QAAQ,OAAQ,MAAyB,SAAS,YAAY;AAC1E,mBAAa,QAAQ,QAAQ,KAAuB;AAAA,IACrD,WACC,SAAS,QACT,OAAO,UAAU,YACjB,eAAgB,SAChB,WAAY,OACX;AAED,mBAAa,eAAe,KAAgB;AAAA,IAC7C,WACC,SAAS,QACT,OAAO,UAAU,YACjB,OAAO,iBAAkB,OACxB;AAGD,oBAAc,YAAY;AACzB,cAAM,OAAQ,MAA2B,OAAO,aAAa,EAAE;AAC/D,YAAI;AACH,gBAAM,EAAE,OAAO,KAAK,IAAI,MAAM,KAAK,KAAK;AACxC,cAAI,KAAM,OAAM,IAAI,MAAM,sDAAsD;AAChF,iBAAO;AAAA,QACR,UAAE;AACD,gBAAM,KAAK,SAAS;AAAA,QACrB;AAAA,MACD,GAAG;AAAA,IACJ,WAAW,SAAS,QAAQ,OAAO,UAAU,YAAY,OAAO,YAAa,OAAkB;AAE9F,oBAAc,YAAY;AACzB,cAAM,OAAQ,MAAsB,OAAO,QAAQ,EAAE;AACrD,YAAI;AACH,gBAAM,EAAE,OAAO,KAAK,IAAI,KAAK,KAAK;AAClC,cAAI,KAAM,OAAM,IAAI,MAAM,gDAAgD;AAC1E,iBAAO;AAAA,QACR,UAAE;AACD,eAAK,SAAS;AAAA,QACf;AAAA,MACD,GAAG;AAAA,IACJ,OAAO;AAEN,mBAAa,QAAQ,QAAQ,KAAU;AAAA,IACxC;AAMA,QAAI;AACJ,UAAM,UAAU,MAAY;AAC3B,UAAI,SAAS,IAAI,CAAC,MAAM,QAAS,UAAS,OAAO,CAAC;AAAA,IACnD;AACA,cAAU,WAAW;AAAA,MACpB,CAAC,MAAM;AACN,gBAAQ;AACR,eAAO;AAAA,MACR;AAAA,MACA,CAAC,MAAM;AACN,gBAAQ;AACR,cAAM;AAAA,MACP;AAAA,IACD;AACA,aAAS,IAAI,GAAG,OAAO;AACvB,WAAO;AAAA,EACR;AACD;AAwBO,SAAS,kBACf,SACA,OAAgC,CAAC,GACX;AACtB,QAAM,QAAQ,KAAK,UAAU,CAAC,MAAS,OAAO,CAAC;AAC/C,QAAM,WAAW,oBAAI,IAAqB;AAE1C,SAAO,CAAC,QAAoB;AAC3B,UAAM,IAAI,MAAM,GAAG;AACnB,UAAM,WAAW,SAAS,IAAI,CAAC;AAC/B,QAAI,SAAU,QAAO;AAErB,UAAM,OAAO,QAAQ,QAAQ,GAAG,CAAC;AACjC,aAAS,IAAI,GAAG,IAAI;AAcpB,QAAI;AACJ,YAAQ,KAAK,UAAU,CAAC,SAAS;AAChC,iBAAW,KAAK,MAAM;AACrB,YAAI,EAAE,CAAC,MAAM,SAAS,EAAE,CAAC,MAAM,YAAY,EAAE,CAAC,MAAM,UAAU;AAC7D,cAAI,SAAS,IAAI,CAAC,MAAM,KAAM,UAAS,OAAO,CAAC;AAC/C,kBAAQ;AACR;AAAA,QACD;AAAA,MACD;AAAA,IACD,CAAC;AACD,WAAO;AAAA,EACR;AACD;","names":[]}
|
|
@@ -34,15 +34,19 @@ function externalProducer(register, opts) {
|
|
|
34
34
|
cleanup = typeof ret === "function" ? ret : void 0;
|
|
35
35
|
} catch (err) {
|
|
36
36
|
triad.error(err);
|
|
37
|
-
return
|
|
38
|
-
|
|
37
|
+
return {
|
|
38
|
+
onDeactivation: () => {
|
|
39
|
+
active = false;
|
|
40
|
+
}
|
|
39
41
|
};
|
|
40
42
|
}
|
|
41
|
-
return
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
return {
|
|
44
|
+
onDeactivation: () => {
|
|
45
|
+
active = false;
|
|
46
|
+
try {
|
|
47
|
+
cleanup?.();
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
46
50
|
}
|
|
47
51
|
};
|
|
48
52
|
}, sourceOpts(opts));
|
|
@@ -68,10 +72,12 @@ function externalBundle(register, channels, opts) {
|
|
|
68
72
|
const n = node(
|
|
69
73
|
(_data, _a) => {
|
|
70
74
|
activatedCount++;
|
|
71
|
-
return
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
return {
|
|
76
|
+
onDeactivation: () => {
|
|
77
|
+
teardownCount++;
|
|
78
|
+
if (activatedCount > 0 && teardownCount >= activatedCount && teardownCount >= channels.length) {
|
|
79
|
+
finishCleanup();
|
|
80
|
+
}
|
|
75
81
|
}
|
|
76
82
|
};
|
|
77
83
|
},
|
|
@@ -125,4 +131,4 @@ export {
|
|
|
125
131
|
externalProducer,
|
|
126
132
|
externalBundle
|
|
127
133
|
};
|
|
128
|
-
//# sourceMappingURL=chunk-
|
|
134
|
+
//# sourceMappingURL=chunk-VLDRAMP7.js.map
|