footprintjs 10.0.1 → 10.2.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.
Files changed (368) hide show
  1. package/AGENTS.md +2 -1
  2. package/CLAUDE.md +61 -7
  3. package/README.md +136 -312
  4. package/dist/esm/index.d.ts +62 -2
  5. package/dist/esm/index.js +13 -6
  6. package/dist/esm/lib/attempt/anySignal.d.ts +19 -0
  7. package/dist/esm/lib/attempt/anySignal.js +39 -0
  8. package/dist/esm/lib/attempt/call.d.ts +88 -0
  9. package/dist/esm/lib/attempt/call.js +129 -0
  10. package/dist/esm/lib/attempt/category.d.ts +37 -0
  11. package/dist/esm/lib/attempt/category.js +131 -0
  12. package/dist/esm/lib/attempt/correlation.d.ts +95 -0
  13. package/dist/esm/lib/attempt/correlation.js +178 -0
  14. package/dist/esm/lib/attempt/deadline.d.ts +44 -0
  15. package/dist/esm/lib/attempt/deadline.js +101 -0
  16. package/dist/esm/lib/attempt/decision.d.ts +52 -0
  17. package/dist/esm/lib/attempt/decision.js +134 -0
  18. package/dist/esm/lib/attempt/effect.d.ts +76 -0
  19. package/dist/esm/lib/attempt/effect.js +160 -0
  20. package/dist/esm/lib/attempt/errors.d.ts +41 -0
  21. package/dist/esm/lib/attempt/errors.js +44 -0
  22. package/dist/esm/lib/attempt/index.d.ts +18 -0
  23. package/dist/esm/lib/attempt/index.js +15 -0
  24. package/dist/esm/lib/attempt/name.d.ts +21 -0
  25. package/dist/esm/lib/attempt/name.js +43 -0
  26. package/dist/esm/lib/attempt/observed.d.ts +66 -0
  27. package/dist/esm/lib/attempt/observed.js +171 -0
  28. package/dist/esm/lib/attempt/policy.d.ts +45 -0
  29. package/dist/esm/lib/attempt/policy.js +83 -0
  30. package/dist/esm/lib/attempt/record.d.ts +11 -0
  31. package/dist/esm/lib/attempt/record.js +24 -0
  32. package/dist/esm/lib/attempt/retryLoopContext.d.ts +18 -0
  33. package/dist/esm/lib/attempt/retryLoopContext.js +24 -0
  34. package/dist/esm/lib/attempt/retryLoopTypes.d.ts +18 -0
  35. package/dist/esm/lib/attempt/retryLoopTypes.js +2 -0
  36. package/dist/esm/lib/attempt/runner.d.ts +157 -0
  37. package/dist/esm/lib/attempt/runner.js +523 -0
  38. package/dist/esm/lib/attempt/standalone.d.ts +47 -0
  39. package/dist/esm/lib/attempt/standalone.js +126 -0
  40. package/dist/esm/lib/attempt/types.d.ts +453 -0
  41. package/dist/esm/lib/attempt/types.js +16 -0
  42. package/dist/esm/lib/budget/index.d.ts +9 -0
  43. package/dist/esm/lib/budget/index.js +9 -0
  44. package/dist/esm/lib/budget/timeBudget.d.ts +136 -0
  45. package/dist/esm/lib/budget/timeBudget.js +666 -0
  46. package/dist/esm/lib/builder/FlowChartBuilder.d.ts +88 -3
  47. package/dist/esm/lib/builder/FlowChartBuilder.js +199 -9
  48. package/dist/esm/lib/builder/retryLoop.d.ts +56 -0
  49. package/dist/esm/lib/builder/retryLoop.js +283 -0
  50. package/dist/esm/lib/builder/types.d.ts +35 -1
  51. package/dist/esm/lib/builder/types.js +1 -1
  52. package/dist/esm/lib/decide/decide.js +2 -2
  53. package/dist/esm/lib/decide/index.d.ts +1 -1
  54. package/dist/esm/lib/decide/index.js +2 -2
  55. package/dist/esm/lib/decide/types.d.ts +3 -0
  56. package/dist/esm/lib/decide/types.js +1 -8
  57. package/dist/esm/lib/engine/graph/StageNode.d.ts +11 -1
  58. package/dist/esm/lib/engine/graph/StageNode.js +1 -1
  59. package/dist/esm/lib/engine/graph/prefixNodeTree.js +5 -1
  60. package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +36 -6
  61. package/dist/esm/lib/engine/handlers/DeciderHandler.js +9 -5
  62. package/dist/esm/lib/engine/handlers/ResumeEntry.js +3 -1
  63. package/dist/esm/lib/engine/handlers/SelectorHandler.js +6 -5
  64. package/dist/esm/lib/engine/handlers/StageRunner.d.ts +8 -1
  65. package/dist/esm/lib/engine/handlers/StageRunner.js +36 -9
  66. package/dist/esm/lib/engine/handlers/SubflowExecutor.js +24 -5
  67. package/dist/esm/lib/engine/handlers/commitStage.d.ts +7 -0
  68. package/dist/esm/lib/engine/handlers/commitStage.js +22 -4
  69. package/dist/esm/lib/engine/handlers/stageError.d.ts +9 -0
  70. package/dist/esm/lib/engine/handlers/stageError.js +23 -1
  71. package/dist/esm/lib/engine/index.d.ts +2 -2
  72. package/dist/esm/lib/engine/index.js +1 -1
  73. package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +27 -13
  74. package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +68 -40
  75. package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.d.ts +4 -1
  76. package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +19 -2
  77. package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.d.ts +22 -3
  78. package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.js +61 -9
  79. package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +1 -0
  80. package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +2 -1
  81. package/dist/esm/lib/engine/narrative/formatting/sentences.d.ts +23 -0
  82. package/dist/esm/lib/engine/narrative/formatting/sentences.js +127 -1
  83. package/dist/esm/lib/engine/narrative/index.d.ts +2 -2
  84. package/dist/esm/lib/engine/narrative/index.js +1 -1
  85. package/dist/esm/lib/engine/narrative/narrativeTypes.d.ts +27 -21
  86. package/dist/esm/lib/engine/narrative/narrativeTypes.js +1 -1
  87. package/dist/esm/lib/engine/narrative/recorders/ManifestFlowRecorder.d.ts +45 -2
  88. package/dist/esm/lib/engine/narrative/recorders/ManifestFlowRecorder.js +55 -2
  89. package/dist/esm/lib/engine/narrative/recorders/index.d.ts +1 -1
  90. package/dist/esm/lib/engine/narrative/recorders/index.js +1 -1
  91. package/dist/esm/lib/engine/narrative/types.d.ts +74 -5
  92. package/dist/esm/lib/engine/narrative/types.js +1 -1
  93. package/dist/esm/lib/engine/traversal/FlowchartTraverser.d.ts +59 -19
  94. package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +194 -118
  95. package/dist/esm/lib/engine/traversalContext.d.ts +7 -2
  96. package/dist/esm/lib/engine/traversalContext.js +6 -3
  97. package/dist/esm/lib/engine/types.d.ts +52 -57
  98. package/dist/esm/lib/engine/types.js +1 -1
  99. package/dist/esm/lib/errors/terminalFailure.d.ts +29 -0
  100. package/dist/esm/lib/errors/terminalFailure.js +60 -0
  101. package/dist/esm/lib/{decide → filter}/evaluator.d.ts +7 -2
  102. package/dist/esm/lib/filter/evaluator.js +135 -0
  103. package/dist/esm/lib/ids/idempotencyKey.d.ts +14 -0
  104. package/dist/esm/lib/ids/idempotencyKey.js +24 -0
  105. package/dist/esm/lib/memory/StageContext.d.ts +123 -3
  106. package/dist/esm/lib/memory/StageContext.js +245 -4
  107. package/dist/esm/lib/memory/attention.d.ts +26 -0
  108. package/dist/esm/lib/memory/attention.js +171 -0
  109. package/dist/esm/lib/memory/borrowedMutation.d.ts +8 -0
  110. package/dist/esm/lib/memory/borrowedMutation.js +14 -1
  111. package/dist/esm/lib/memory/frameTypes.d.ts +38 -0
  112. package/dist/esm/lib/memory/frameTypes.js +3 -1
  113. package/dist/esm/lib/memory/redaction.d.ts +12 -0
  114. package/dist/esm/lib/memory/redaction.js +54 -5
  115. package/dist/esm/lib/pause/record.js +10 -1
  116. package/dist/esm/lib/pause/types.d.ts +19 -0
  117. package/dist/esm/lib/pause/types.js +17 -1
  118. package/dist/esm/lib/reactive/createTypedScope.js +3 -1
  119. package/dist/esm/lib/reactive/types.d.ts +36 -4
  120. package/dist/esm/lib/reactive/types.js +3 -1
  121. package/dist/esm/lib/recorder/AttemptRecorder.d.ts +141 -0
  122. package/dist/esm/lib/recorder/AttemptRecorder.js +412 -0
  123. package/dist/esm/lib/recorder/ControlDepRecorder.d.ts +36 -2
  124. package/dist/esm/lib/recorder/ControlDepRecorder.js +50 -2
  125. package/dist/esm/lib/recorder/EmitRecorder.d.ts +3 -0
  126. package/dist/esm/lib/recorder/EmitRecorder.js +1 -1
  127. package/dist/esm/lib/recorder/InOutRecorder.d.ts +37 -1
  128. package/dist/esm/lib/recorder/InOutRecorder.js +75 -6
  129. package/dist/esm/lib/recorder/QualityRecorder.d.ts +32 -5
  130. package/dist/esm/lib/recorder/QualityRecorder.js +87 -29
  131. package/dist/esm/lib/recorder/TopologyRecorder.d.ts +35 -5
  132. package/dist/esm/lib/recorder/TopologyRecorder.js +76 -2
  133. package/dist/esm/lib/recorder/attemptMetricRows.d.ts +11 -0
  134. package/dist/esm/lib/recorder/attemptMetricRows.js +202 -0
  135. package/dist/esm/lib/recorder/attemptMetrics.d.ts +7 -0
  136. package/dist/esm/lib/recorder/attemptMetrics.js +165 -0
  137. package/dist/esm/lib/recorder/attemptMetricsTypes.d.ts +114 -0
  138. package/dist/esm/lib/recorder/attemptMetricsTypes.js +2 -0
  139. package/dist/esm/lib/recorder/attemptOTel.d.ts +78 -0
  140. package/dist/esm/lib/recorder/attemptOTel.js +107 -0
  141. package/dist/esm/lib/recorder/attemptSeries.d.ts +81 -0
  142. package/dist/esm/lib/recorder/attemptSeries.js +103 -0
  143. package/dist/esm/lib/recorder/hooks.d.ts +9 -0
  144. package/dist/esm/lib/recorder/hooks.js +4 -1
  145. package/dist/esm/lib/runner/DeferredObserverTier.js +13 -1
  146. package/dist/esm/lib/runner/ExecutionRuntime.d.ts +54 -6
  147. package/dist/esm/lib/runner/ExecutionRuntime.js +57 -5
  148. package/dist/esm/lib/runner/FlowChartExecutor.d.ts +46 -2
  149. package/dist/esm/lib/runner/FlowChartExecutor.js +330 -101
  150. package/dist/esm/lib/runner/RunnableChart.d.ts +6 -0
  151. package/dist/esm/lib/runner/RunnableChart.js +5 -1
  152. package/dist/esm/lib/runner/attach.d.ts +2 -1
  153. package/dist/esm/lib/runner/attach.js +8 -2
  154. package/dist/esm/lib/runner/checkpoint.js +3 -1
  155. package/dist/esm/lib/runner/deadline.d.ts +42 -0
  156. package/dist/esm/lib/runner/deadline.js +90 -0
  157. package/dist/esm/lib/runner/index.d.ts +3 -1
  158. package/dist/esm/lib/runner/index.js +2 -1
  159. package/dist/esm/lib/runner/options.d.ts +17 -0
  160. package/dist/esm/lib/runner/options.js +3 -1
  161. package/dist/esm/lib/runner/recordSink.d.ts +128 -0
  162. package/dist/esm/lib/runner/recordSink.js +296 -0
  163. package/dist/esm/lib/runner/resume.d.ts +3 -0
  164. package/dist/esm/lib/runner/resume.js +22 -2
  165. package/dist/esm/lib/runner/snapshot.d.ts +2 -2
  166. package/dist/esm/lib/runner/snapshot.js +6 -2
  167. package/dist/esm/lib/scope/ScopeFacade.d.ts +118 -4
  168. package/dist/esm/lib/scope/ScopeFacade.js +304 -7
  169. package/dist/esm/lib/scope/recorders/DebugRecorder.d.ts +17 -3
  170. package/dist/esm/lib/scope/recorders/DebugRecorder.js +79 -13
  171. package/dist/esm/lib/scope/recorders/MetricRecorder.d.ts +10 -0
  172. package/dist/esm/lib/scope/recorders/MetricRecorder.js +15 -2
  173. package/dist/esm/lib/scope/runtime.d.ts +3 -0
  174. package/dist/esm/lib/scope/runtime.js +1 -1
  175. package/dist/esm/lib/scope/types.d.ts +6 -0
  176. package/dist/esm/lib/scope/types.js +1 -1
  177. package/dist/esm/lib/sinks/fileSink.d.ts +135 -0
  178. package/dist/esm/lib/sinks/fileSink.js +387 -0
  179. package/dist/esm/recorders.d.ts +8 -2
  180. package/dist/esm/recorders.js +7 -1
  181. package/dist/esm/sinks.d.ts +23 -0
  182. package/dist/esm/sinks.js +23 -0
  183. package/dist/esm/trace.d.ts +2 -2
  184. package/dist/esm/trace.js +1 -1
  185. package/dist/index.js +77 -60
  186. package/dist/lib/attempt/anySignal.js +43 -0
  187. package/dist/lib/attempt/call.js +134 -0
  188. package/dist/lib/attempt/category.js +136 -0
  189. package/dist/lib/attempt/correlation.js +186 -0
  190. package/dist/lib/attempt/deadline.js +112 -0
  191. package/dist/lib/attempt/decision.js +139 -0
  192. package/dist/lib/attempt/effect.js +174 -0
  193. package/dist/lib/attempt/errors.js +49 -0
  194. package/dist/lib/attempt/index.js +26 -0
  195. package/dist/lib/attempt/name.js +48 -0
  196. package/dist/lib/attempt/observed.js +177 -0
  197. package/dist/lib/attempt/policy.js +92 -0
  198. package/dist/lib/attempt/record.js +29 -0
  199. package/dist/lib/attempt/retryLoopContext.js +31 -0
  200. package/dist/lib/attempt/retryLoopTypes.js +3 -0
  201. package/dist/lib/attempt/runner.js +530 -0
  202. package/dist/lib/attempt/standalone.js +130 -0
  203. package/dist/lib/attempt/types.js +17 -0
  204. package/dist/lib/budget/index.js +17 -0
  205. package/dist/lib/budget/timeBudget.js +674 -0
  206. package/dist/lib/builder/FlowChartBuilder.js +199 -9
  207. package/dist/lib/builder/retryLoop.js +287 -0
  208. package/dist/lib/builder/types.js +1 -1
  209. package/dist/lib/decide/decide.js +2 -2
  210. package/dist/lib/decide/index.js +2 -2
  211. package/dist/lib/decide/types.js +1 -8
  212. package/dist/lib/engine/graph/StageNode.js +1 -1
  213. package/dist/lib/engine/graph/prefixNodeTree.js +5 -1
  214. package/dist/lib/engine/handlers/ChildrenExecutor.js +36 -6
  215. package/dist/lib/engine/handlers/DeciderHandler.js +7 -3
  216. package/dist/lib/engine/handlers/ResumeEntry.js +3 -1
  217. package/dist/lib/engine/handlers/SelectorHandler.js +4 -3
  218. package/dist/lib/engine/handlers/StageRunner.js +36 -9
  219. package/dist/lib/engine/handlers/SubflowExecutor.js +24 -5
  220. package/dist/lib/engine/handlers/commitStage.js +24 -5
  221. package/dist/lib/engine/handlers/stageError.js +26 -2
  222. package/dist/lib/engine/index.js +1 -1
  223. package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +67 -39
  224. package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +19 -2
  225. package/dist/lib/engine/narrative/NarrativeFlowRecorder.js +60 -8
  226. package/dist/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +2 -1
  227. package/dist/lib/engine/narrative/formatting/sentences.js +131 -2
  228. package/dist/lib/engine/narrative/index.js +1 -1
  229. package/dist/lib/engine/narrative/narrativeTypes.js +1 -1
  230. package/dist/lib/engine/narrative/recorders/ManifestFlowRecorder.js +55 -2
  231. package/dist/lib/engine/narrative/recorders/index.js +1 -1
  232. package/dist/lib/engine/narrative/types.js +1 -1
  233. package/dist/lib/engine/traversal/FlowchartTraverser.js +192 -116
  234. package/dist/lib/engine/traversalContext.js +6 -3
  235. package/dist/lib/engine/types.js +1 -1
  236. package/dist/lib/errors/terminalFailure.js +64 -0
  237. package/dist/lib/filter/evaluator.js +139 -0
  238. package/dist/lib/ids/idempotencyKey.js +28 -0
  239. package/dist/lib/memory/StageContext.js +246 -5
  240. package/dist/lib/memory/attention.js +175 -0
  241. package/dist/lib/memory/borrowedMutation.js +16 -2
  242. package/dist/lib/memory/frameTypes.js +3 -1
  243. package/dist/lib/memory/redaction.js +54 -5
  244. package/dist/lib/pause/record.js +10 -1
  245. package/dist/lib/pause/types.js +17 -1
  246. package/dist/lib/reactive/createTypedScope.js +3 -1
  247. package/dist/lib/reactive/types.js +3 -1
  248. package/dist/lib/recorder/AttemptRecorder.js +417 -0
  249. package/dist/lib/recorder/ControlDepRecorder.js +50 -2
  250. package/dist/lib/recorder/EmitRecorder.js +1 -1
  251. package/dist/lib/recorder/InOutRecorder.js +75 -6
  252. package/dist/lib/recorder/QualityRecorder.js +87 -29
  253. package/dist/lib/recorder/TopologyRecorder.js +76 -2
  254. package/dist/lib/recorder/attemptMetricRows.js +209 -0
  255. package/dist/lib/recorder/attemptMetrics.js +174 -0
  256. package/dist/lib/recorder/attemptMetricsTypes.js +3 -0
  257. package/dist/lib/recorder/attemptOTel.js +111 -0
  258. package/dist/lib/recorder/attemptSeries.js +113 -0
  259. package/dist/lib/recorder/hooks.js +4 -1
  260. package/dist/lib/runner/DeferredObserverTier.js +13 -1
  261. package/dist/lib/runner/ExecutionRuntime.js +57 -5
  262. package/dist/lib/runner/FlowChartExecutor.js +330 -101
  263. package/dist/lib/runner/RunnableChart.js +5 -1
  264. package/dist/lib/runner/attach.js +8 -2
  265. package/dist/lib/runner/checkpoint.js +3 -1
  266. package/dist/lib/runner/deadline.js +94 -0
  267. package/dist/lib/runner/index.js +4 -2
  268. package/dist/lib/runner/options.js +3 -1
  269. package/dist/lib/runner/recordSink.js +302 -0
  270. package/dist/lib/runner/resume.js +22 -2
  271. package/dist/lib/runner/snapshot.js +6 -2
  272. package/dist/lib/scope/ScopeFacade.js +303 -6
  273. package/dist/lib/scope/recorders/DebugRecorder.js +79 -13
  274. package/dist/lib/scope/recorders/MetricRecorder.js +15 -2
  275. package/dist/lib/scope/runtime.js +1 -1
  276. package/dist/lib/scope/types.js +1 -1
  277. package/dist/lib/sinks/fileSink.js +392 -0
  278. package/dist/recorders.js +10 -2
  279. package/dist/sinks.js +32 -0
  280. package/dist/trace.js +1 -1
  281. package/dist/types/index.d.ts +62 -2
  282. package/dist/types/lib/attempt/anySignal.d.ts +19 -0
  283. package/dist/types/lib/attempt/call.d.ts +88 -0
  284. package/dist/types/lib/attempt/category.d.ts +37 -0
  285. package/dist/types/lib/attempt/correlation.d.ts +95 -0
  286. package/dist/types/lib/attempt/deadline.d.ts +44 -0
  287. package/dist/types/lib/attempt/decision.d.ts +52 -0
  288. package/dist/types/lib/attempt/effect.d.ts +76 -0
  289. package/dist/types/lib/attempt/errors.d.ts +41 -0
  290. package/dist/types/lib/attempt/index.d.ts +18 -0
  291. package/dist/types/lib/attempt/name.d.ts +21 -0
  292. package/dist/types/lib/attempt/observed.d.ts +66 -0
  293. package/dist/types/lib/attempt/policy.d.ts +45 -0
  294. package/dist/types/lib/attempt/record.d.ts +11 -0
  295. package/dist/types/lib/attempt/retryLoopContext.d.ts +18 -0
  296. package/dist/types/lib/attempt/retryLoopTypes.d.ts +18 -0
  297. package/dist/types/lib/attempt/runner.d.ts +157 -0
  298. package/dist/types/lib/attempt/standalone.d.ts +47 -0
  299. package/dist/types/lib/attempt/types.d.ts +453 -0
  300. package/dist/types/lib/budget/index.d.ts +9 -0
  301. package/dist/types/lib/budget/timeBudget.d.ts +136 -0
  302. package/dist/types/lib/builder/FlowChartBuilder.d.ts +88 -3
  303. package/dist/types/lib/builder/retryLoop.d.ts +56 -0
  304. package/dist/types/lib/builder/types.d.ts +35 -1
  305. package/dist/types/lib/decide/index.d.ts +1 -1
  306. package/dist/types/lib/decide/types.d.ts +3 -0
  307. package/dist/types/lib/engine/graph/StageNode.d.ts +11 -1
  308. package/dist/types/lib/engine/handlers/StageRunner.d.ts +8 -1
  309. package/dist/types/lib/engine/handlers/commitStage.d.ts +7 -0
  310. package/dist/types/lib/engine/handlers/stageError.d.ts +9 -0
  311. package/dist/types/lib/engine/index.d.ts +2 -2
  312. package/dist/types/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +27 -13
  313. package/dist/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +4 -1
  314. package/dist/types/lib/engine/narrative/NarrativeFlowRecorder.d.ts +22 -3
  315. package/dist/types/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +1 -0
  316. package/dist/types/lib/engine/narrative/formatting/sentences.d.ts +23 -0
  317. package/dist/types/lib/engine/narrative/index.d.ts +2 -2
  318. package/dist/types/lib/engine/narrative/narrativeTypes.d.ts +27 -21
  319. package/dist/types/lib/engine/narrative/recorders/ManifestFlowRecorder.d.ts +45 -2
  320. package/dist/types/lib/engine/narrative/recorders/index.d.ts +1 -1
  321. package/dist/types/lib/engine/narrative/types.d.ts +74 -5
  322. package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +59 -19
  323. package/dist/types/lib/engine/traversalContext.d.ts +7 -2
  324. package/dist/types/lib/engine/types.d.ts +52 -57
  325. package/dist/types/lib/errors/terminalFailure.d.ts +29 -0
  326. package/dist/types/lib/{decide → filter}/evaluator.d.ts +7 -2
  327. package/dist/types/lib/ids/idempotencyKey.d.ts +14 -0
  328. package/dist/types/lib/memory/StageContext.d.ts +123 -3
  329. package/dist/types/lib/memory/attention.d.ts +26 -0
  330. package/dist/types/lib/memory/borrowedMutation.d.ts +8 -0
  331. package/dist/types/lib/memory/frameTypes.d.ts +38 -0
  332. package/dist/types/lib/memory/redaction.d.ts +12 -0
  333. package/dist/types/lib/pause/types.d.ts +19 -0
  334. package/dist/types/lib/reactive/types.d.ts +36 -4
  335. package/dist/types/lib/recorder/AttemptRecorder.d.ts +141 -0
  336. package/dist/types/lib/recorder/ControlDepRecorder.d.ts +36 -2
  337. package/dist/types/lib/recorder/EmitRecorder.d.ts +3 -0
  338. package/dist/types/lib/recorder/InOutRecorder.d.ts +37 -1
  339. package/dist/types/lib/recorder/QualityRecorder.d.ts +32 -5
  340. package/dist/types/lib/recorder/TopologyRecorder.d.ts +35 -5
  341. package/dist/types/lib/recorder/attemptMetricRows.d.ts +11 -0
  342. package/dist/types/lib/recorder/attemptMetrics.d.ts +7 -0
  343. package/dist/types/lib/recorder/attemptMetricsTypes.d.ts +114 -0
  344. package/dist/types/lib/recorder/attemptOTel.d.ts +78 -0
  345. package/dist/types/lib/recorder/attemptSeries.d.ts +81 -0
  346. package/dist/types/lib/recorder/hooks.d.ts +9 -0
  347. package/dist/types/lib/runner/ExecutionRuntime.d.ts +54 -6
  348. package/dist/types/lib/runner/FlowChartExecutor.d.ts +46 -2
  349. package/dist/types/lib/runner/RunnableChart.d.ts +6 -0
  350. package/dist/types/lib/runner/attach.d.ts +2 -1
  351. package/dist/types/lib/runner/deadline.d.ts +42 -0
  352. package/dist/types/lib/runner/index.d.ts +3 -1
  353. package/dist/types/lib/runner/options.d.ts +17 -0
  354. package/dist/types/lib/runner/recordSink.d.ts +128 -0
  355. package/dist/types/lib/runner/resume.d.ts +3 -0
  356. package/dist/types/lib/runner/snapshot.d.ts +2 -2
  357. package/dist/types/lib/scope/ScopeFacade.d.ts +118 -4
  358. package/dist/types/lib/scope/recorders/DebugRecorder.d.ts +17 -3
  359. package/dist/types/lib/scope/recorders/MetricRecorder.d.ts +10 -0
  360. package/dist/types/lib/scope/runtime.d.ts +3 -0
  361. package/dist/types/lib/scope/types.d.ts +6 -0
  362. package/dist/types/lib/sinks/fileSink.d.ts +135 -0
  363. package/dist/types/recorders.d.ts +8 -2
  364. package/dist/types/sinks.d.ts +23 -0
  365. package/dist/types/trace.d.ts +2 -2
  366. package/package.json +20 -6
  367. package/dist/esm/lib/decide/evaluator.js +0 -130
  368. package/dist/lib/decide/evaluator.js +0 -134
@@ -1,13 +1,18 @@
1
1
  /**
2
- * decide/evaluator -- Prisma-style filter evaluator for decision rules.
2
+ * filter/evaluator -- Prisma-style filter evaluator for decision rules.
3
3
  *
4
4
  * Pure function. Takes a WhereFilter, a value getter, and a redaction checker.
5
5
  * Evaluates each condition, records the result, returns matched/conditions.
6
6
  *
7
7
  * All keys in the filter are ANDed (all must match for the rule to match).
8
8
  * Decoupled from ScopeFacade — receives callbacks, not scope.
9
+ *
10
+ * A LEAF module of its own (P2, 10.1): `decide()` (decide/) and the attempt rule table (attempt/decision.ts) share
11
+ * this one law, and neither may reach the other's dependencies through it. In decide/ it made `attempt/` reach
12
+ * `scope/` (decide.ts imports the scope runtime), so a `scope → attempt` edge — `scope.$call` — closed a module
13
+ * cycle.
9
14
  */
10
- import type { FilterCondition, WhereFilter } from './types.js';
15
+ import type { FilterCondition, WhereFilter } from '../decide/types.js';
11
16
  /**
12
17
  * Evaluates a Prisma-style filter against scope values.
13
18
  *
@@ -0,0 +1,14 @@
1
+ /**
2
+ * ids/idempotencyKey.ts — the ONE format rule for an idempotency key (design §3.3, packet P3). A leaf: the attempt
3
+ * law (`attempt/effect.ts · admitIdempotencyKey`, a key a stage's rule or a `$call` gives) and the checkpoint decoder
4
+ * (`pause/record.ts`, a key a paused execution carries to its resume) both ask it, so a key a stored checkpoint
5
+ * smuggles in is held to the same rule as one the code made.
6
+ *
7
+ * 1–255 printable ASCII characters (`\x21`–`\x7e`), no space: a key travels in a request header
8
+ * (`Idempotency-Key`), so a CR/LF could forge one, and 255 is Stripe's limit, inside every header limit. The
9
+ * problem names the rule, never the value.
10
+ */
11
+ /** The longest idempotency key. */
12
+ export declare const MAX_IDEMPOTENCY_KEY_LENGTH = 255;
13
+ /** Why `value` is not an idempotency key, or `undefined` when it is one. */
14
+ export declare function idempotencyKeyProblem(value: unknown): string | undefined;
@@ -13,12 +13,28 @@
13
13
  */
14
14
  import type { CommitPhase, EmitSourcePosition, UntrackedSource } from 'foottrace';
15
15
  import type { EventLog, SharedMemory } from 'foottrace/write';
16
+ import type { AttemptContext, AttemptRecord, AttemptSummary, LateWrite } from '../attempt/types.js';
17
+ import type { StructuredErrorInfo } from '../errors/errorInfo.js';
18
+ import { type TerminalFailureContext, NativeFailureState } from '../errors/terminalFailure.js';
16
19
  import { type DiagnosticChannel, DiagnosticCollector } from './DiagnosticCollector.js';
17
20
  import type { FlowControlType, StageSnapshot } from './frameTypes.js';
18
21
  import type { RedactionVerdict } from './redaction.js';
19
22
  import { RedactionRule } from './redaction.js';
20
23
  import type { RunPolicy } from './runPolicy.js';
24
+ /**
25
+ * How many call and observed attempt records one stage execution keeps (P2) — a stage that calls in a loop must
26
+ * not grow the execution tree without bound. The rest are counted (`StageSnapshot.attemptsDropped`) and still
27
+ * fired on `onAttempt`. A stage's OWN attempt records are never displaced: its policy's ceiling bounds them.
28
+ */
29
+ export declare const MAX_CALL_RECORDS = 1000;
21
30
  export declare class StageContext {
31
+ #private;
32
+ /** @internal Ownership of an escaping failure. Shared only by sequential frames, never serialized. */
33
+ failureState: NativeFailureState;
34
+ /** @internal The native frame stamp, independent of mutable recorder event context. */
35
+ get failureIdentity(): NonNullable<TerminalFailureContext['stage']> | undefined;
36
+ /** @internal Stamped before observers; never inherited by another frame or persisted. */
37
+ bindFailureIdentity(runId: string, runtimeStageId: string): void;
22
38
  /**
23
39
  * The record half of this frame (`RecordFrame.ts`, L3, C3): the heap and,
24
40
  * when the run keeps them, the redacted mirror and the log; the address;
@@ -138,6 +154,41 @@ export declare class StageContext {
138
154
  * stages that never touch an untracked path pay nothing.
139
155
  */
140
156
  private _untrackedSources?;
157
+ /**
158
+ * The attempts of this frame's execution (10.1) — its own under the attempt runner, appended as each attempt ends
159
+ * (`FlowchartTraverser · executeStage`), and those of the calls it made (P2, {@link reportCallAttempt}) — read
160
+ * by {@link snapshotSelf}. Append-only
161
+ * evidence like `debug`: NOT cleared by {@link discardStaged} (the earlier attempts are the point) and never
162
+ * part of a commit bundle. Lazily allocated: a stage without a policy pays nothing.
163
+ */
164
+ private _attempts?;
165
+ /** The attempt series at a glance, stamped once when it ends. */
166
+ private _attemptSummary?;
167
+ /** Facts collected for this execution only; retry discards deliberately keep them. */
168
+ private _attention?;
169
+ /**
170
+ * The attempt now running (P1b) — set by `StageRunner` before the scope is built, so the facade can hand it out
171
+ * (`scope.$attempt()`) and fence its own attempt. Absent for a stage the attempt runner does not run.
172
+ */
173
+ private _attempt?;
174
+ /** The run's signal, for the attempt a stage outside the runner reports (attempt 1 of 1). */
175
+ private _runSignal?;
176
+ /** Every attempt up to this number has ENDED without an answer — its scope is fenced (0 = none). */
177
+ private _endedThrough;
178
+ /** Writes an attempt tried after it ended — dropped at the scope, kept here as names. Lazily allocated. */
179
+ private _lateWrites?;
180
+ /**
181
+ * Where a call's attempt records are reported (P2) — the engine's `onAttempt` for this stage execution, set by
182
+ * `FlowchartTraverser · executeStage` with the stage's own traversal context (and its correlation).
183
+ */
184
+ private _attemptReporter?;
185
+ /** Calls made so far, per stage attempt — the `k` of a call's series. Lazily allocated. */
186
+ private _callOrdinals?;
187
+ /** Per stage attempt, the signal its calls stop by (`callsEndedSignal`). Lazily allocated. */
188
+ private _callsEnded?;
189
+ /** Call and observed records kept on the frame, and those past {@link MAX_CALL_RECORDS}. */
190
+ private _callRecords;
191
+ private _attemptsDropped;
141
192
  /** Observer called after commit() — used by ScopeFacade to fire ScopeRecorder.onCommit. */
142
193
  private _commitObserver?;
143
194
  constructor(runId: string, name: string, stageId: string, sharedMemory: SharedMemory, branchId?: string, eventLog?: EventLog, isDecider?: boolean);
@@ -404,6 +455,8 @@ export declare class StageContext {
404
455
  * reads them post-run for the execution-tree snapshot.
405
456
  */
406
457
  commit(phase?: CommitPhase): void;
458
+ /** Whether this frame has committed — a call's record arriving after it is late (P2, `ScopeFacade · call`). */
459
+ get committed(): boolean;
407
460
  /**
408
461
  * Throw away everything this stage has STAGED, without committing any of it.
409
462
  *
@@ -429,14 +482,81 @@ export declare class StageContext {
429
482
  * holds bare references — dropping it un-clones nothing);
430
483
  * - `_untrackedSources` — the D2 honesty markers, released with the rest.
431
484
  *
432
- * What is deliberately KEPT: `debug` (logs, metrics, errors, flow messages).
433
- * Diagnostics are the append-only record of what every attempt did, and the
434
- * retry feature's whole point is that the earlier attempts stay visible.
485
+ * What is deliberately KEPT: `debug` (logs, metrics, errors, flow messages)
486
+ * and the attempt records (10.1). Both are the append-only record of what
487
+ * every attempt did, and the retry feature's whole point is that the earlier
488
+ * attempts stay visible.
435
489
  *
436
490
  * No commit is recorded and no observer fires — a discarded attempt is not a
437
491
  * cursor stop, because from shared state's point of view it never happened.
438
492
  */
439
493
  discardStaged(): void;
494
+ /** Append one attempt's record — the attempt runner calls this as each attempt ends. */
495
+ recordAttempt(record: AttemptRecord): void;
496
+ /** Stamp the series' summary — once, when the runner ends the series. */
497
+ endAttempts(summary: AttemptSummary): void;
498
+ /** Reset only at the node's execution stamp, never between attempts. */
499
+ beginAttention(): void;
500
+ /** Delegate the explicit retained-error fact; attention owns validation of its proof. */
501
+ markStageFailure(): void;
502
+ /**
503
+ * The crash-safe record (10.1, P6): an attempt that ended is noted in the run's sink AT ONCE, beside this frame's
504
+ * log — so a process killed before the stage commits (mid-retry) still leaves every attempt that ended. The
505
+ * record is the one the tree keeps: frozen, `structuredClone`-safe, its error the SERVED form (no `raw`), so the
506
+ * note needs no second redaction pass. Without a sink the log does nothing (`EventLog · note`).
507
+ */
508
+ private noteAttempt;
509
+ /** The attempts of this execution so far, in order — its own and its calls'; `undefined` when there are none. */
510
+ get attempts(): readonly AttemptRecord[] | undefined;
511
+ /** The ended series' summary; `undefined` while it runs and without a policy. */
512
+ get attemptSummary(): AttemptSummary | undefined;
513
+ /** The attempt about to run (and the run's signal) — `StageRunner` sets both before it builds the scope. */
514
+ useAttempt(attempt: AttemptContext | undefined, runSignal: AbortSignal | undefined): void;
515
+ /** The attempt now running; `undefined` for a stage the attempt runner does not run. */
516
+ get attempt(): AttemptContext | undefined;
517
+ /** The run's signal, as `StageRunner` last saw it. */
518
+ get runSignal(): AbortSignal | undefined;
519
+ /**
520
+ * Attempt `attempt` ended without an answer: from now on its scope is fenced — a write, an emit or a read
521
+ * through it is refused (`ScopeFacade`). The runner calls this the moment the attempt's signal aborts.
522
+ */
523
+ endAttempt(attempt: number): void;
524
+ /** Whether attempt `attempt` has ended — its scope may no longer touch the stage. */
525
+ hasAttemptEnded(attempt: number): boolean;
526
+ /** Record a write an ended attempt tried — a name, never the value. Append-only evidence like the attempts. */
527
+ recordLateWrite(write: LateWrite): void;
528
+ /** This execution's own idempotency key may be made of a selected value (or was carried by a checkpoint). */
529
+ private _effectKeyTainted;
530
+ /** A touching `$call` with NO key started in this execution — the stage may not be re-run by a resume. */
531
+ private _keylessEffect;
532
+ /**
533
+ * Start an execution's side-effect bookkeeping (`FlowchartTraverser · executeStage`, before attempt 1) — reset,
534
+ * because a frame can be re-used by a later execution. `keyTainted`: the stage's own key may be made of a selected
535
+ * value, so a key a `$call` passes on (`$attempt().idempotencyKey`) is served the same way.
536
+ */
537
+ beginEffects(keyTainted: boolean): void;
538
+ get effectKeyTainted(): boolean;
539
+ /** A keyless touching `$call` started: an `interrupt()` after it would re-send it on resume (`StageRunner`). */
540
+ markKeylessEffect(): void;
541
+ get hasKeylessEffect(): boolean;
542
+ /** Where this execution's call records are reported — the engine sets it before the stage runs. */
543
+ useAttemptReporter(report: ((record: AttemptRecord, structuredError: StructuredErrorInfo | undefined) => void) | undefined): void;
544
+ /**
545
+ * The signal the calls of stage attempt `stageAttempt` run under besides its own (P2): aborted when that attempt is
546
+ * over — however it ended — and when the frame commits, so no call outlives the attempt that made it (no
547
+ * background retry, no wait, no listener left on the run's signal). Already aborted after the commit.
548
+ */
549
+ callsEndedSignal(stageAttempt: number): AbortSignal;
550
+ /** End the calls of `stageAttempt` (every attempt's when `undefined`) — `reason` when the attempt's signal gave one. */
551
+ private endCalls;
552
+ /** The next call's ordinal within stage attempt `stageAttempt`, 1-based (`attempt/call.ts · callSeries`). */
553
+ nextCallOrdinal(stageAttempt: number): number;
554
+ /**
555
+ * A call's (or an observed) attempt record: kept beside the stage's own attempts — the first
556
+ * {@link MAX_CALL_RECORDS}, the rest counted — and reported to the run's `onAttempt` either way. Like the stage's
557
+ * records, NOT cleared by {@link discardStaged}: a failed attempt's calls are evidence too.
558
+ */
559
+ reportCallAttempt(record: AttemptRecord, structuredError: StructuredErrorInfo | undefined): void;
440
560
  /**
441
561
  * Create (or return) this context's linked successor.
442
562
  *
@@ -0,0 +1,26 @@
1
+ /** Engine-owned attention is collected with retained facts; snapshots only copy this bounded projection. */
2
+ import type { Attention } from 'foottrace';
3
+ import type { AttemptRecord, AttemptSummary } from '../attempt/types.js';
4
+ /** One execution's ownership, pending summary, marks and omission accounting stay in this helper. */
5
+ export declare class StageAttention {
6
+ private marks?;
7
+ private omitted;
8
+ private retryPossible;
9
+ private summary?;
10
+ private terminal?;
11
+ /** A new execution starts fresh; an attempt discard never calls this. No collection is reconstructed. */
12
+ begin(): void;
13
+ /** Called only after the frame retained this attempt. Raw error, target and decision payloads are never read. */
14
+ recordAttempt(record: AttemptRecord, owner: string): void;
15
+ recordSummary(summary: AttemptSummary): void;
16
+ /** A summary-only cut wait has no phantom final attempt; use its actual retained summary. */
17
+ private projectSummary;
18
+ /** Only the explicit retained stage-error path calls this; a diagnostic value never classifies itself. */
19
+ stageFailed(errors: unknown): void;
20
+ attemptDropped(): void;
21
+ /** Keep only bounded exact references; one slot is reserved to disclose omissions. */
22
+ append(attention: Attention): void;
23
+ missing(): void;
24
+ /** Copy already collected bounded marks, never an attempt array, diagnostic bag or execution tree. */
25
+ snapshot(): readonly Attention[];
26
+ }
@@ -47,6 +47,14 @@ export declare function firstDifferingPath(before: unknown, after: unknown, pref
47
47
  * path names it); this text names the stage the handle came from.
48
48
  */
49
49
  export declare function deadFrameMessage(stageName: string, runtimeStageId: string | undefined, key: string): string;
50
+ /**
51
+ * The refusal raised in an attempt that has ENDED (P1b) — timed out, aborted or
52
+ * failed — when the function behind it touches its scope anyway. The engine
53
+ * stopped waiting for it; whatever it does now would land beside a later
54
+ * attempt (or after the stage's commit), so it is refused. A write or emit is
55
+ * recorded on the stage (`StageSnapshot.lateWrites`) before this is thrown.
56
+ */
57
+ export declare function lateAttemptMessage(stageName: string, series: string, attempt: number, key: string): string;
50
58
  /**
51
59
  * The warning text for a key the stage WROTE after changing, in place, the
52
60
  * committed value it had read before its first write (copy-on-write, 9.29.0).
@@ -7,9 +7,13 @@
7
7
  * in the record's `types.ts` until C6; the record names nothing outside itself now
8
8
  * (`RECORD_FILES`, scripts/layering.config.cjs), so they sit beside the frame that writes them.
9
9
  *
10
+ * A retried stage's node also carries its attempts (`attempts`, `attemptSummary` — the attempt leaf's types, L0).
11
+ *
10
12
  * A `StageSnapshot` is an `ExecutionTree` (`types.ts`), the shape the record's readers walk:
11
13
  * `commitStops`, `tagStops` and `keysReadFromExecutionTree` take one.
12
14
  */
15
+ import type { Attention } from 'foottrace';
16
+ import type { AttemptRecord, AttemptSummary, LateWrite } from '../attempt/types.js';
13
17
  import type { RetentionPolicy } from '../capture/policies.js';
14
18
  /** Types of control flow decisions captured by the execution engine. */
15
19
  export type FlowControlType = 'next' | 'branch' | 'children' | 'selected' | 'subflow' | 'loop';
@@ -93,6 +97,40 @@ export type StageSnapshot = {
93
97
  metrics: Record<string, unknown>;
94
98
  evals: Record<string, unknown>;
95
99
  flowMessages?: FlowMessage[];
100
+ /**
101
+ * Every attempt of this execution, in order — when it started, how long it took, how it ended, and the
102
+ * retry-or-stop decision that followed (`AttemptRecord`): the stage's own under its retry policy, a `timeoutMs`
103
+ * or a run deadline (10.1, `kind: 'stage'`), and those of the calls it made through `scope.$call` and the
104
+ * outside attempts it reported through `observed()` (P2, `kind: 'call'` / `'observed'`, the first 1000 —
105
+ * `StageContext · MAX_CALL_RECORDS`). Absent when there are none. The commit log never carries attempts: a
106
+ * retried stage is still ONE bundle.
107
+ */
108
+ attempts?: readonly AttemptRecord[];
109
+ /**
110
+ * Call and observed attempt records this execution made past the first 1000 — counted, not kept (each still
111
+ * fired `onAttempt`). Absent when none were dropped.
112
+ */
113
+ attemptsDropped?: number;
114
+ /**
115
+ * The series at a glance — counts per outcome, and how it ended. Stamped when the series ends: absent while it
116
+ * is still running (a snapshot taken mid-stage) and without a policy.
117
+ */
118
+ attemptSummary?: AttemptSummary;
119
+ /**
120
+ * Writes (and emits) an attempt tried AFTER it had ended — timed out, aborted or failed — in the order they came
121
+ * (P1b). Each was dropped at the scope, so none reached the stage's buffer, its commit or a recorder; this is
122
+ * the record that they were tried — the first 100 (`MAX_LATE_WRITES`). An abandoned function can keep running
123
+ * after the run ended, so the list can grow after a snapshot was taken. Names only, never values. Absent when
124
+ * there were none.
125
+ */
126
+ lateWrites?: readonly LateWrite[];
127
+ /**
128
+ * Fixed-reason marks collected as this execution runs. Absent when no fact needs attention.
129
+ * Attempt evidence uses canonical Foottrace attempt IDs. Summary-only and stage-error proof resolves within THIS node
130
+ * through attemptSummary, errors:stageExecutionError or attemptsDropped. Quality marks are read-time recorder work.
131
+ * Marks retain no error text, values, targets or idempotency keys; omitted proof is disclosed as not-collected.
132
+ */
133
+ attention?: readonly Attention[];
96
134
  next?: StageSnapshot;
97
135
  children?: StageSnapshot[];
98
136
  };
@@ -224,6 +224,18 @@ export declare class RedactionRule {
224
224
  * channel) applies on top. Clear values keep their identity.
225
225
  */
226
226
  retainDiagnostic(address: readonly string[], value: unknown): unknown;
227
+ /**
228
+ * An idempotency key as records serve it (P3, design §6). The key is a VALUE, and the documented way to make one
229
+ * is from the input or the state (`order-${args.orderId}`) — so it is selected by its NAME (the diagnostic entry
230
+ * `{ idempotencyKey }`: a state rule of that name, or `policy.diagnostics` at `logs.idempotencyKey`) AND by its
231
+ * SOURCES, conservatively, the subflow-mapper taint law's way: when the input it may be derived from holds a
232
+ * selected key at any depth, or the stage read a selected value before making it (`selectedReads`), it is served
233
+ * whole as the placeholder. Never value equality. An inert rule never reads it.
234
+ */
235
+ retainIdempotencyKey(key: string, sources: {
236
+ readonly input?: unknown;
237
+ readonly selectedReads?: number;
238
+ }): string;
227
239
  /**
228
240
  * Retain flow-message text (`flowMessages.description` / `.rationale`): text
229
241
  * the engine composes has no name of its own, so no state selector reaches
@@ -114,6 +114,8 @@ export declare class PauseSignal extends Error {
114
114
  * (`completeExecution`), or both carried over for a queued sibling.
115
115
  */
116
116
  private _pausedExecution?;
117
+ /** The paused execution's idempotency key (P3) — a touching stage's, stamped once at the stage boundary. */
118
+ private _idempotencyKey?;
117
119
  constructor(data: unknown, stageId: string, pausedBy?: 'interrupt');
118
120
  get subflowPath(): readonly string[];
119
121
  /** The paused execution (its run and `[path/]stageId#N`), once both are known. */
@@ -125,6 +127,13 @@ export declare class PauseSignal extends Error {
125
127
  * carries both (it paused in an EARLIER run).
126
128
  */
127
129
  stampExecution(runtimeStageId: string, runId?: string): void;
130
+ /**
131
+ * The idempotency key the paused execution ran under (P3) — a touching stage's. It rides the checkpoint so the
132
+ * resume re-runs the stage (or its `resumeFn`) under the SAME key: the input its rule read does not cross a
133
+ * resume. First stamp wins.
134
+ */
135
+ get idempotencyKey(): string | undefined;
136
+ stampIdempotencyKey(key: string | undefined): void;
128
137
  /**
129
138
  * Name the run for every pause on this signal that paused in it — this one
130
139
  * and its queued siblings that do not already carry a run (a sibling raised
@@ -213,6 +222,8 @@ export interface PendingPause {
213
222
  readonly pausedBy?: 'interrupt';
214
223
  /** Which execution the sibling paused in (as `pausedExecution`, 9.37.0) — kept so its own resume links to it. */
215
224
  readonly pausedExecution?: PausedExecution;
225
+ /** The idempotency key the sibling's execution ran under (as `idempotencyKey`, 10.1). */
226
+ readonly idempotencyKey?: string;
216
227
  }
217
228
  /**
218
229
  * Returned by a pausable stage's execute/resume function to signal pause.
@@ -277,6 +288,14 @@ export interface FlowchartCheckpoint {
277
288
  * which then resumes without a link.
278
289
  */
279
290
  readonly pausedExecution?: PausedExecution;
291
+ /**
292
+ * The idempotency key the paused execution ran under (10.1) — present only when the paused stage `touches` an
293
+ * outside system with a key rule. `resume()` re-runs the stage (or runs its `resumeFn`) under THIS key, never a
294
+ * recomputed one: the input the rule read does not cross a resume, and a different key on the re-run would make
295
+ * the outside system apply the effect twice. A real value, like `sharedState` — no redaction policy touches the
296
+ * checkpoint. A malformed one is refused at `resume()`.
297
+ */
298
+ readonly idempotencyKey?: string;
280
299
  /** Path through subflows to the paused stage (e.g., ['sf-payment', 'sf-validation']).
281
300
  * Empty array when paused at the top level. */
282
301
  readonly subflowPath: readonly string[];
@@ -7,6 +7,9 @@
7
7
  *
8
8
  * Dependency: type-only imports from engine/ and scope/ (zero runtime cost).
9
9
  */
10
+ import type { AttemptContext, CallOptions, RetryPolicy } from '../attempt/types.js';
11
+ import type { FlowChart } from '../builder/types.js';
12
+ import type { DetachDriver, DetachHandle } from '../detach/types.js';
10
13
  import type { ExecutionEnv } from '../engine/types.js';
11
14
  import type { ScopeRecorder } from '../scope/types.js';
12
15
  export interface ReactiveTarget {
@@ -27,6 +30,10 @@ export interface ReactiveTarget {
27
30
  getValueSilent?(key?: string): unknown;
28
31
  getArgs<T = Record<string, unknown>>(): T;
29
32
  getEnv(): Readonly<ExecutionEnv>;
33
+ /** The attempt the stage function runs in — its signal, its cap and the time left (P1b). */
34
+ getAttempt(): AttemptContext;
35
+ /** A call to an outside system on the attempt runner, recorded under this stage (P2). */
36
+ call<R>(target: string, fn: (attempt: AttemptContext) => Promise<R> | R, policy?: RetryPolicy, options?: CallOptions): Promise<R>;
30
37
  attachScopeRecorder(recorder: ScopeRecorder): void;
31
38
  detachScopeRecorder(recorderId: string): void;
32
39
  getScopeRecorders(): ScopeRecorder[];
@@ -36,8 +43,8 @@ export interface ReactiveTarget {
36
43
  addMetric(name: string, value: unknown): void;
37
44
  addEval(name: string, value: unknown): void;
38
45
  emitEvent(name: string, payload?: unknown): void;
39
- detachAndJoinLater(driver: import('../detach/types.js').DetachDriver, child: import('../builder/types.js').FlowChart, input?: unknown): import('../detach/types.js').DetachHandle;
40
- detachAndForget(driver: import('../detach/types.js').DetachDriver, child: import('../builder/types.js').FlowChart, input?: unknown): void;
46
+ detachAndJoinLater(driver: DetachDriver, child: FlowChart, input?: unknown): DetachHandle;
47
+ detachAndForget(driver: DetachDriver, child: FlowChart, input?: unknown): void;
41
48
  }
42
49
  export interface ScopeMethods {
43
50
  $getValue(key: string): unknown;
@@ -48,6 +55,31 @@ export interface ScopeMethods {
48
55
  $read(dotPath: string): unknown;
49
56
  $getArgs<T = Record<string, unknown>>(): T;
50
57
  $getEnv(): Readonly<ExecutionEnv>;
58
+ /**
59
+ * The attempt this stage function runs in (P1b): `attempt` / `of`, the `signal` to pass to `fetch` and SDK
60
+ * calls (it aborts at the attempt's cap, the run's deadline, or a cancellation), and `timeLeftMs()` — the
61
+ * milliseconds before the run's deadline (`Infinity` without one; reading it with a deadline marks the stage's
62
+ * commit `untrackedSources: ['env']`). A stage with no retry policy and no deadline is attempt 1 of 1.
63
+ */
64
+ $attempt(): AttemptContext;
65
+ /**
66
+ * A call to an outside system, made inside this stage (P2): `fn` runs on the attempt runner under `policy`
67
+ * (none = ONE attempt — still recorded), and every attempt is an `AttemptRecord` of kind `'call'` on this
68
+ * stage's node and on `onAttempt`, with its timing, outcome, category and decision. It runs under this stage
69
+ * attempt's time and signal — pass `t.signal` on. `target` is a NAME from code (1–128 characters, no control
70
+ * character), never a value. A malformed call (target, `fn`, policy) or one from an attempt that already ended
71
+ * THROWS at the call, synchronously, like every scope refusal — it is the stage's code breaking its contract; a
72
+ * call that runs and fails REJECTS with its last attempt's own error. A call that changes an outside system
73
+ * declares it in `options` (`{ touches, idempotencyKey }`, P3): a touching call that may retry needs a key, every
74
+ * record carries its `effect`, and a call that ends without an answer after any attempt of unknown effect rejects
75
+ * with `UnknownEffectError`.
76
+ *
77
+ * ```typescript
78
+ * const quote = await scope.$call('stripe.quotes.create', (t) => stripe.quotes.create(body, { signal: t.signal }),
79
+ * { attempts: 3, timeoutMs: 10_000 });
80
+ * ```
81
+ */
82
+ $call<R>(target: string, fn: (attempt: AttemptContext) => Promise<R> | R, policy?: RetryPolicy, options?: CallOptions): Promise<R>;
51
83
  $debug(key: string, value: unknown): void;
52
84
  $log(value: unknown): void;
53
85
  $error(key: string, value: unknown): void;
@@ -129,8 +161,8 @@ export interface ScopeMethods {
129
161
  * ```
130
162
  */
131
163
  $emit(name: string, payload?: unknown): void;
132
- $detachAndJoinLater(driver: import('../detach/types.js').DetachDriver, child: import('../builder/types.js').FlowChart, input?: unknown): import('../detach/types.js').DetachHandle;
133
- $detachAndForget(driver: import('../detach/types.js').DetachDriver, child: import('../builder/types.js').FlowChart, input?: unknown): void;
164
+ $detachAndJoinLater(driver: DetachDriver, child: FlowChart, input?: unknown): DetachHandle;
165
+ $detachAndForget(driver: DetachDriver, child: FlowChart, input?: unknown): void;
134
166
  /**
135
167
  * Stop the current execution context.
136
168
  *
@@ -0,0 +1,141 @@
1
+ import type { FlowAttemptEvent, FlowDecisionEvent, FlowPauseEvent, FlowRecorder, FlowRunEvent, FlowRunFailedEvent } from '../engine/narrative/types.js';
2
+ import type { AttemptAggregate, AttemptDecisionRow, AttemptQuery, AttemptRecorderOptions, AttemptStorageStats, AttemptStore, PhysicalAttemptRow } from './attemptMetricsTypes.js';
3
+ export declare class AttemptRecorder implements FlowRecorder {
4
+ readonly id: string;
5
+ private readonly rows;
6
+ private readonly logical;
7
+ private readonly starts;
8
+ private reads;
9
+ private readonly pending;
10
+ private readonly exemplars;
11
+ private readonly maxRows;
12
+ private readonly maxPending;
13
+ private readonly timeoutMs;
14
+ private readonly store?;
15
+ private failed;
16
+ private dropped;
17
+ private omittedRows;
18
+ constructor(options?: AttemptRecorderOptions);
19
+ onRunStart(event: FlowRunEvent): void;
20
+ onRunEnd(event: FlowRunEvent): void;
21
+ onPause(event: FlowPauseEvent): void;
22
+ onRunFailed(event: FlowRunFailedEvent): void;
23
+ private end;
24
+ onAttempt(event: FlowAttemptEvent): void;
25
+ onDecision(event: FlowDecisionEvent): void;
26
+ private collect;
27
+ storage(): AttemptStorageStats;
28
+ flush(): Promise<AttemptStorageStats>;
29
+ byTarget(): AttemptAggregate['byTarget'];
30
+ byRun(): AttemptAggregate['byRun'];
31
+ slowest(target: string, limit?: number): import("./attemptMetricsTypes.js").AttemptMetricRef[];
32
+ series(series: string, runId?: string): {
33
+ attempts: PhysicalAttemptRow[];
34
+ decisions: AttemptDecisionRow[];
35
+ };
36
+ aggregate(supplied?: AttemptQuery): Promise<AttemptAggregate>;
37
+ private readStored;
38
+ toSnapshot(): {
39
+ name: string;
40
+ data: {
41
+ byTarget: Map<string, import("./attemptMetricsTypes.js").AttemptStats>;
42
+ byRun: Map<string, import("./attemptMetricsTypes.js").AttemptRunStats>;
43
+ };
44
+ meta: {
45
+ projection: string;
46
+ exemplars: number;
47
+ rows: number;
48
+ maxRows: number;
49
+ storage: AttemptStorageStats;
50
+ workTime: string;
51
+ };
52
+ };
53
+ toOTel(): {
54
+ resourceMetrics: {
55
+ resource: {
56
+ attributes: never[];
57
+ };
58
+ scopeMetrics: {
59
+ scope: {
60
+ name: string;
61
+ };
62
+ metrics: ({
63
+ name: string;
64
+ unit: string;
65
+ description: string;
66
+ histogram: {
67
+ aggregationTemporality: number;
68
+ dataPoints: {
69
+ attributes: {
70
+ key: string;
71
+ value: {
72
+ stringValue: string;
73
+ };
74
+ }[];
75
+ startTimeUnixNano: string;
76
+ timeUnixNano: string;
77
+ count: string;
78
+ sum: number;
79
+ explicitBounds: number[];
80
+ bucketCounts: string[];
81
+ exemplars: {
82
+ traceId?: string | undefined;
83
+ asDouble: number;
84
+ timeUnixNano: string;
85
+ filteredAttributes: {
86
+ key: string;
87
+ value: {
88
+ stringValue: string;
89
+ };
90
+ }[];
91
+ }[];
92
+ }[];
93
+ };
94
+ sum?: undefined;
95
+ } | {
96
+ name: string;
97
+ unit: string;
98
+ sum: {
99
+ aggregationTemporality: number;
100
+ isMonotonic: boolean;
101
+ dataPoints: {
102
+ attributes: {
103
+ key: string;
104
+ value: {
105
+ stringValue: string;
106
+ };
107
+ }[];
108
+ startTimeUnixNano: string;
109
+ timeUnixNano: string;
110
+ asDouble: number;
111
+ exemplars: {
112
+ asDouble: number;
113
+ traceId?: string | undefined;
114
+ timeUnixNano: string;
115
+ filteredAttributes: {
116
+ key: string;
117
+ value: {
118
+ stringValue: string;
119
+ };
120
+ }[];
121
+ }[];
122
+ }[];
123
+ };
124
+ description?: undefined;
125
+ histogram?: undefined;
126
+ })[];
127
+ }[];
128
+ }[];
129
+ };
130
+ /** Clear completed local projections; active/paused evidence and bounded ownership survive executor auto-clear. */
131
+ clear(): void;
132
+ }
133
+ /** Bounded reference adapter for tests/local use; apps supply their own database adapter. */
134
+ export declare function memoryStore(options?: {
135
+ readonly maxRows?: number;
136
+ }): AttemptStore & {
137
+ stats(): {
138
+ rows: number;
139
+ omittedRows: number;
140
+ };
141
+ };
@@ -27,6 +27,17 @@
27
27
  * completing consumes its slot via `onError`, so the post-fan-out
28
28
  * convergence stage is never misattributed as a branch entry.
29
29
  *
30
+ * 3. attempt stops (10.1) — `onAttempt`: a stage whose retry series STOPPED
31
+ * (it gave up, or a later attempt paused or was aborted; the shared
32
+ * predicate `recorder/attemptSeries.ts · isRetryEvidence`) made a decision of its
33
+ * own — the retry-or-stop decision, in the shape `decide()` produces. It
34
+ * is recorded beside the deciders as `kind: 'attempt-stop'`
35
+ * (`getDecisions()`, `getAttemptStop(id)`), `chosen: 'stop'`, the stop
36
+ * reason as its `ruleLabel`. It is NOT a branch entry: a control edge in
37
+ * `causalChain` links a stage to ANOTHER execution that governed it, and
38
+ * the stop governs the stage itself — so `lookup` is unchanged, and a
39
+ * reader walking a failed stage reads the stop off its decision record.
40
+ *
30
41
  * `lookup(runtimeStageId)` then walks the ancestor chain from the stage
31
42
  * upward; the first ancestor (or the stage itself) registered as a
32
43
  * branch entry yields its governing decider — the NEAREST decision.
@@ -55,9 +66,15 @@ import { causalChain } from 'foottrace';
55
66
  */
56
67
  import type { ControlDependency, ControlDepLookup } from 'foottrace';
57
68
  import type { DecisionEvidence, SelectionEvidence } from '../decide/types.js';
58
- import type { FlowDecisionEvent, FlowErrorEvent, FlowRecorder, FlowSelectedEvent, FlowStageEvent } from '../engine/narrative/types.js';
69
+ import type { FlowAttemptEvent, FlowDecisionEvent, FlowErrorEvent, FlowRecorder, FlowSelectedEvent, FlowStageEvent } from '../engine/narrative/types.js';
59
70
  /** One recorded decision/selection event (RFC-003 D5). */
60
71
  export interface ControlDecisionRecord {
72
+ /**
73
+ * `'attempt-stop'` for a stage's own retry-or-stop decision that STOPPED its series (10.1) — then
74
+ * `deciderRuntimeStageId` is the stage's execution, `chosen` is `'stop'`, `evidence` the attempt's decision and
75
+ * `ruleLabel` the stop reason. Absent for a decider / selector.
76
+ */
77
+ kind?: 'attempt-stop';
61
78
  /** runtimeStageId of the decider/selector execution step. */
62
79
  deciderRuntimeStageId: string;
63
80
  /** Chosen branch display name (decider) or selected names (selector). */
@@ -100,6 +117,7 @@ export declare class ControlDepRecorder implements FlowRecorder {
100
117
  private lastRunId?;
101
118
  constructor(options?: ControlDepRecorderOptions);
102
119
  onDecision(event: FlowDecisionEvent): void;
120
+ onAttempt(event: FlowAttemptEvent): void;
103
121
  onSelected(event: FlowSelectedEvent): void;
104
122
  onStageExecuted(event: FlowStageEvent): void;
105
123
  /**
@@ -118,8 +136,24 @@ export declare class ControlDepRecorder implements FlowRecorder {
118
136
  lookup(runtimeStageId: string): ControlDependency | undefined;
119
137
  /** The lookup as a bare function — plug into `causalChain(..., { controlDeps })`. */
120
138
  asLookup(): ControlDepLookup;
121
- /** All decisions recorded for the current run, in event order. */
139
+ /** The attempt-stop decision of a stage execution whose retry series stopped, if it did. */
140
+ getAttemptStop(runtimeStageId: string): ControlDecisionRecord | undefined;
141
+ /** All decisions recorded for the current run, in event order — deciders, selectors and attempt stops. */
122
142
  getDecisions(): ControlDecisionRecord[];
143
+ /**
144
+ * Current-run control evidence for the native snapshot. Project only the
145
+ * stage identities collected during traversal; never walk the execution tree.
146
+ * Parentless roots cannot have a governing control edge.
147
+ */
148
+ toSnapshot(): {
149
+ name: string;
150
+ data: {
151
+ decisions: ControlDecisionRecord[];
152
+ controlDeps: {
153
+ [k: string]: ControlDependency;
154
+ };
155
+ };
156
+ };
123
157
  /** Reset all state (also happens automatically on runId change). */
124
158
  clear(): void;
125
159
  /** Convention 4: a new runId means a new run — reset transient state. */