@smthrs/engine-store 1.0.0-rc.5 → 1.0.0-rc.6

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 (257) hide show
  1. package/README.md +5 -5
  2. package/dist/cjs/ArtifactGc.d.ts +1 -1
  3. package/dist/cjs/ArtifactGc.d.ts.map +1 -1
  4. package/dist/cjs/ArtifactGc.js +1 -1
  5. package/dist/cjs/ArtifactGc.js.map +1 -1
  6. package/dist/cjs/DeferredClockFold.d.ts +4 -4
  7. package/dist/cjs/DeferredClockFold.d.ts.map +1 -1
  8. package/dist/cjs/DeferredClockFold.js +1 -1
  9. package/dist/cjs/DeferredClockFold.js.map +1 -1
  10. package/dist/cjs/DisasterRecovery.d.ts +1 -1
  11. package/dist/cjs/DisasterRecovery.d.ts.map +1 -1
  12. package/dist/cjs/DisasterRecovery.js +1 -1
  13. package/dist/cjs/DisasterRecovery.js.map +1 -1
  14. package/dist/cjs/DurableEngineState.d.ts +1 -1
  15. package/dist/cjs/DurableEngineState.d.ts.map +1 -1
  16. package/dist/cjs/DurableEngineState.js +1 -1
  17. package/dist/cjs/DurableEngineState.js.map +1 -1
  18. package/dist/cjs/ExecutionSnapshot.d.ts +2 -2
  19. package/dist/cjs/ExecutionSnapshot.d.ts.map +1 -1
  20. package/dist/cjs/ExecutionSnapshot.js +1 -1
  21. package/dist/cjs/ExecutionSnapshot.js.map +1 -1
  22. package/dist/cjs/Migrations.d.ts +2 -2
  23. package/dist/cjs/Migrations.d.ts.map +1 -1
  24. package/dist/cjs/OwnerIdentity.js +1 -1
  25. package/dist/cjs/OwnerIdentity.js.map +2 -2
  26. package/dist/cjs/PlanInputStore.d.ts +1 -1
  27. package/dist/cjs/PlanInputStore.d.ts.map +1 -1
  28. package/dist/cjs/PlanInputStore.js +1 -1
  29. package/dist/cjs/PlanInputStore.js.map +1 -1
  30. package/dist/cjs/PlanMergeStore.d.ts +1 -1
  31. package/dist/cjs/PlanMergeStore.d.ts.map +1 -1
  32. package/dist/cjs/PlanMergeStore.js +1 -1
  33. package/dist/cjs/PlanMergeStore.js.map +1 -1
  34. package/dist/cjs/Retention.d.ts +2 -2
  35. package/dist/cjs/Retention.d.ts.map +1 -1
  36. package/dist/cjs/Retention.js +1 -1
  37. package/dist/cjs/Retention.js.map +1 -1
  38. package/dist/cjs/RunChangeFeed.d.ts +1 -1
  39. package/dist/cjs/RunChangeFeed.d.ts.map +1 -1
  40. package/dist/cjs/RunChangeFeed.js +1 -1
  41. package/dist/cjs/RunChangeFeed.js.map +1 -1
  42. package/dist/cjs/SelectionStore.d.ts +1 -1
  43. package/dist/cjs/SelectionStore.d.ts.map +1 -1
  44. package/dist/cjs/SelectionStore.js +1 -1
  45. package/dist/cjs/SelectionStore.js.map +1 -1
  46. package/dist/cjs/StepBoundary.js +3 -3
  47. package/dist/cjs/StepBoundary.js.map +2 -2
  48. package/dist/cjs/StepCacheFold.d.ts +2 -2
  49. package/dist/cjs/StepCacheFold.d.ts.map +1 -1
  50. package/dist/cjs/StepCacheFold.js +1 -1
  51. package/dist/cjs/StepCacheFold.js.map +1 -1
  52. package/dist/cjs/internal/EngineStateSchema.d.ts +1 -1
  53. package/dist/cjs/internal/EngineStateSchema.d.ts.map +1 -1
  54. package/dist/cjs/internal/EngineStateSchema.js.map +1 -1
  55. package/dist/cjs/internal/ExecutionSnapshotRead.d.ts +3 -3
  56. package/dist/cjs/internal/ExecutionSnapshotRead.d.ts.map +1 -1
  57. package/dist/cjs/internal/ExecutionSnapshotRead.js.map +1 -1
  58. package/dist/cjs/internal/ExecutionSnapshotRelations.d.ts +1 -1
  59. package/dist/cjs/internal/ExecutionSnapshotRelations.d.ts.map +1 -1
  60. package/dist/cjs/internal/ExecutionSnapshotRelations.js.map +1 -1
  61. package/dist/cjs/internal/RetentionOps.d.ts +2 -2
  62. package/dist/cjs/internal/RetentionOps.d.ts.map +1 -1
  63. package/dist/cjs/internal/RetentionOps.js +1 -1
  64. package/dist/cjs/internal/RetentionOps.js.map +1 -1
  65. package/dist/cjs/internal/RunCatalogOps.d.ts +1 -1
  66. package/dist/cjs/internal/RunCatalogOps.d.ts.map +1 -1
  67. package/dist/cjs/internal/RunCatalogOps.js +1 -1
  68. package/dist/cjs/internal/RunCatalogOps.js.map +1 -1
  69. package/dist/cjs/internal/RunListing.d.ts +1 -1
  70. package/dist/cjs/internal/RunListing.d.ts.map +1 -1
  71. package/dist/cjs/internal/RunListing.js +1 -1
  72. package/dist/cjs/internal/RunListing.js.map +1 -1
  73. package/dist/cjs/migrations/0001_initial.d.ts +1 -1
  74. package/dist/cjs/migrations/0001_initial.d.ts.map +1 -1
  75. package/dist/cjs/migrations/0001_initial.js +1 -1
  76. package/dist/cjs/migrations/0001_initial.js.map +1 -1
  77. package/dist/cjs/migrations/0002_selection_store.d.ts +1 -1
  78. package/dist/cjs/migrations/0002_selection_store.d.ts.map +1 -1
  79. package/dist/cjs/migrations/0002_selection_store.js +1 -1
  80. package/dist/cjs/migrations/0002_selection_store.js.map +1 -1
  81. package/dist/cjs/migrations/0003_plan_inputs.d.ts +1 -1
  82. package/dist/cjs/migrations/0003_plan_inputs.d.ts.map +1 -1
  83. package/dist/cjs/migrations/0003_plan_inputs.js +1 -1
  84. package/dist/cjs/migrations/0003_plan_inputs.js.map +1 -1
  85. package/dist/cjs/migrations/0004_plan_environment.d.ts +1 -1
  86. package/dist/cjs/migrations/0004_plan_environment.d.ts.map +1 -1
  87. package/dist/cjs/migrations/0004_plan_environment.js +1 -1
  88. package/dist/cjs/migrations/0004_plan_environment.js.map +1 -1
  89. package/dist/cjs/migrations/0005_plan_merges.d.ts +1 -1
  90. package/dist/cjs/migrations/0005_plan_merges.d.ts.map +1 -1
  91. package/dist/cjs/migrations/0005_plan_merges.js +1 -1
  92. package/dist/cjs/migrations/0005_plan_merges.js.map +1 -1
  93. package/dist/cjs/migrations/0006_execution_listing.d.ts +1 -1
  94. package/dist/cjs/migrations/0006_execution_listing.d.ts.map +1 -1
  95. package/dist/cjs/migrations/0006_execution_listing.js +1 -1
  96. package/dist/cjs/migrations/0006_execution_listing.js.map +1 -1
  97. package/dist/cjs/migrations/0007_run_parent_sequence.d.ts +1 -1
  98. package/dist/cjs/migrations/0007_run_parent_sequence.d.ts.map +1 -1
  99. package/dist/cjs/migrations/0007_run_parent_sequence.js +1 -1
  100. package/dist/cjs/migrations/0007_run_parent_sequence.js.map +1 -1
  101. package/dist/cjs/migrations/0008_deferred_consumption.d.ts +1 -1
  102. package/dist/cjs/migrations/0008_deferred_consumption.d.ts.map +1 -1
  103. package/dist/cjs/migrations/0008_deferred_consumption.js +1 -1
  104. package/dist/cjs/migrations/0008_deferred_consumption.js.map +1 -1
  105. package/dist/cjs/migrations/0009_deferred_clock_facts.d.ts +1 -1
  106. package/dist/cjs/migrations/0009_deferred_clock_facts.d.ts.map +1 -1
  107. package/dist/cjs/migrations/0009_deferred_clock_facts.js +1 -1
  108. package/dist/cjs/migrations/0009_deferred_clock_facts.js.map +1 -1
  109. package/dist/cjs/migrations/0010_pending_cancellation.d.ts +1 -1
  110. package/dist/cjs/migrations/0010_pending_cancellation.d.ts.map +1 -1
  111. package/dist/cjs/migrations/0010_pending_cancellation.js +1 -1
  112. package/dist/cjs/migrations/0010_pending_cancellation.js.map +1 -1
  113. package/dist/cjs/test/TestStores.d.ts +4 -4
  114. package/dist/cjs/test/TestStores.d.ts.map +1 -1
  115. package/dist/esm/ArtifactGc.d.ts +1 -1
  116. package/dist/esm/ArtifactGc.d.ts.map +1 -1
  117. package/dist/esm/ArtifactGc.js +1 -1
  118. package/dist/esm/ArtifactGc.js.map +1 -1
  119. package/dist/esm/DeferredClockFold.d.ts +4 -4
  120. package/dist/esm/DeferredClockFold.d.ts.map +1 -1
  121. package/dist/esm/DeferredClockFold.js +1 -1
  122. package/dist/esm/DeferredClockFold.js.map +1 -1
  123. package/dist/esm/DisasterRecovery.d.ts +1 -1
  124. package/dist/esm/DisasterRecovery.d.ts.map +1 -1
  125. package/dist/esm/DisasterRecovery.js +1 -1
  126. package/dist/esm/DisasterRecovery.js.map +1 -1
  127. package/dist/esm/DurableEngineState.d.ts +1 -1
  128. package/dist/esm/DurableEngineState.d.ts.map +1 -1
  129. package/dist/esm/DurableEngineState.js +1 -1
  130. package/dist/esm/DurableEngineState.js.map +1 -1
  131. package/dist/esm/ExecutionSnapshot.d.ts +2 -2
  132. package/dist/esm/ExecutionSnapshot.d.ts.map +1 -1
  133. package/dist/esm/ExecutionSnapshot.js +1 -1
  134. package/dist/esm/ExecutionSnapshot.js.map +1 -1
  135. package/dist/esm/Migrations.d.ts +2 -2
  136. package/dist/esm/Migrations.d.ts.map +1 -1
  137. package/dist/esm/OwnerIdentity.js +1 -1
  138. package/dist/esm/OwnerIdentity.js.map +1 -1
  139. package/dist/esm/PlanInputStore.d.ts +1 -1
  140. package/dist/esm/PlanInputStore.d.ts.map +1 -1
  141. package/dist/esm/PlanInputStore.js +1 -1
  142. package/dist/esm/PlanInputStore.js.map +1 -1
  143. package/dist/esm/PlanMergeStore.d.ts +1 -1
  144. package/dist/esm/PlanMergeStore.d.ts.map +1 -1
  145. package/dist/esm/PlanMergeStore.js +1 -1
  146. package/dist/esm/PlanMergeStore.js.map +1 -1
  147. package/dist/esm/Retention.d.ts +2 -2
  148. package/dist/esm/Retention.d.ts.map +1 -1
  149. package/dist/esm/Retention.js +1 -1
  150. package/dist/esm/Retention.js.map +1 -1
  151. package/dist/esm/RunChangeFeed.d.ts +1 -1
  152. package/dist/esm/RunChangeFeed.d.ts.map +1 -1
  153. package/dist/esm/RunChangeFeed.js +1 -1
  154. package/dist/esm/RunChangeFeed.js.map +1 -1
  155. package/dist/esm/SelectionStore.d.ts +1 -1
  156. package/dist/esm/SelectionStore.d.ts.map +1 -1
  157. package/dist/esm/SelectionStore.js +1 -1
  158. package/dist/esm/SelectionStore.js.map +1 -1
  159. package/dist/esm/StepBoundary.js +3 -3
  160. package/dist/esm/StepBoundary.js.map +1 -1
  161. package/dist/esm/StepCacheFold.d.ts +2 -2
  162. package/dist/esm/StepCacheFold.d.ts.map +1 -1
  163. package/dist/esm/StepCacheFold.js +1 -1
  164. package/dist/esm/StepCacheFold.js.map +1 -1
  165. package/dist/esm/internal/EngineStateSchema.d.ts +1 -1
  166. package/dist/esm/internal/EngineStateSchema.d.ts.map +1 -1
  167. package/dist/esm/internal/EngineStateSchema.js.map +1 -1
  168. package/dist/esm/internal/ExecutionSnapshotRead.d.ts +3 -3
  169. package/dist/esm/internal/ExecutionSnapshotRead.d.ts.map +1 -1
  170. package/dist/esm/internal/ExecutionSnapshotRead.js.map +1 -1
  171. package/dist/esm/internal/ExecutionSnapshotRelations.d.ts +1 -1
  172. package/dist/esm/internal/ExecutionSnapshotRelations.d.ts.map +1 -1
  173. package/dist/esm/internal/ExecutionSnapshotRelations.js.map +1 -1
  174. package/dist/esm/internal/RetentionOps.d.ts +2 -2
  175. package/dist/esm/internal/RetentionOps.d.ts.map +1 -1
  176. package/dist/esm/internal/RetentionOps.js +1 -1
  177. package/dist/esm/internal/RetentionOps.js.map +1 -1
  178. package/dist/esm/internal/RunCatalogOps.d.ts +1 -1
  179. package/dist/esm/internal/RunCatalogOps.d.ts.map +1 -1
  180. package/dist/esm/internal/RunCatalogOps.js +1 -1
  181. package/dist/esm/internal/RunCatalogOps.js.map +1 -1
  182. package/dist/esm/internal/RunListing.d.ts +1 -1
  183. package/dist/esm/internal/RunListing.d.ts.map +1 -1
  184. package/dist/esm/internal/RunListing.js +1 -1
  185. package/dist/esm/internal/RunListing.js.map +1 -1
  186. package/dist/esm/migrations/0001_initial.d.ts +1 -1
  187. package/dist/esm/migrations/0001_initial.d.ts.map +1 -1
  188. package/dist/esm/migrations/0001_initial.js +1 -1
  189. package/dist/esm/migrations/0001_initial.js.map +1 -1
  190. package/dist/esm/migrations/0002_selection_store.d.ts +1 -1
  191. package/dist/esm/migrations/0002_selection_store.d.ts.map +1 -1
  192. package/dist/esm/migrations/0002_selection_store.js +1 -1
  193. package/dist/esm/migrations/0002_selection_store.js.map +1 -1
  194. package/dist/esm/migrations/0003_plan_inputs.d.ts +1 -1
  195. package/dist/esm/migrations/0003_plan_inputs.d.ts.map +1 -1
  196. package/dist/esm/migrations/0003_plan_inputs.js +1 -1
  197. package/dist/esm/migrations/0003_plan_inputs.js.map +1 -1
  198. package/dist/esm/migrations/0004_plan_environment.d.ts +1 -1
  199. package/dist/esm/migrations/0004_plan_environment.d.ts.map +1 -1
  200. package/dist/esm/migrations/0004_plan_environment.js +1 -1
  201. package/dist/esm/migrations/0004_plan_environment.js.map +1 -1
  202. package/dist/esm/migrations/0005_plan_merges.d.ts +1 -1
  203. package/dist/esm/migrations/0005_plan_merges.d.ts.map +1 -1
  204. package/dist/esm/migrations/0005_plan_merges.js +1 -1
  205. package/dist/esm/migrations/0005_plan_merges.js.map +1 -1
  206. package/dist/esm/migrations/0006_execution_listing.d.ts +1 -1
  207. package/dist/esm/migrations/0006_execution_listing.d.ts.map +1 -1
  208. package/dist/esm/migrations/0006_execution_listing.js +1 -1
  209. package/dist/esm/migrations/0006_execution_listing.js.map +1 -1
  210. package/dist/esm/migrations/0007_run_parent_sequence.d.ts +1 -1
  211. package/dist/esm/migrations/0007_run_parent_sequence.d.ts.map +1 -1
  212. package/dist/esm/migrations/0007_run_parent_sequence.js +1 -1
  213. package/dist/esm/migrations/0007_run_parent_sequence.js.map +1 -1
  214. package/dist/esm/migrations/0008_deferred_consumption.d.ts +1 -1
  215. package/dist/esm/migrations/0008_deferred_consumption.d.ts.map +1 -1
  216. package/dist/esm/migrations/0008_deferred_consumption.js +1 -1
  217. package/dist/esm/migrations/0008_deferred_consumption.js.map +1 -1
  218. package/dist/esm/migrations/0009_deferred_clock_facts.d.ts +1 -1
  219. package/dist/esm/migrations/0009_deferred_clock_facts.d.ts.map +1 -1
  220. package/dist/esm/migrations/0009_deferred_clock_facts.js +1 -1
  221. package/dist/esm/migrations/0009_deferred_clock_facts.js.map +1 -1
  222. package/dist/esm/migrations/0010_pending_cancellation.d.ts +1 -1
  223. package/dist/esm/migrations/0010_pending_cancellation.d.ts.map +1 -1
  224. package/dist/esm/migrations/0010_pending_cancellation.js +1 -1
  225. package/dist/esm/migrations/0010_pending_cancellation.js.map +1 -1
  226. package/dist/esm/test/TestStores.d.ts +4 -4
  227. package/dist/esm/test/TestStores.d.ts.map +1 -1
  228. package/package.json +22 -22
  229. package/src/ArtifactGc.ts +1 -1
  230. package/src/DeferredClockFold.ts +1 -1
  231. package/src/DisasterRecovery.ts +1 -1
  232. package/src/DurableEngineState.ts +1 -1
  233. package/src/ExecutionSnapshot.ts +1 -1
  234. package/src/OwnerIdentity.ts +1 -1
  235. package/src/PlanInputStore.ts +1 -1
  236. package/src/PlanMergeStore.ts +1 -1
  237. package/src/Retention.ts +2 -2
  238. package/src/RunChangeFeed.ts +1 -1
  239. package/src/SelectionStore.ts +1 -1
  240. package/src/StepBoundary.ts +3 -3
  241. package/src/StepCacheFold.ts +1 -1
  242. package/src/internal/EngineStateSchema.ts +1 -1
  243. package/src/internal/ExecutionSnapshotRead.ts +1 -1
  244. package/src/internal/ExecutionSnapshotRelations.ts +1 -1
  245. package/src/internal/RetentionOps.ts +2 -2
  246. package/src/internal/RunCatalogOps.ts +1 -1
  247. package/src/internal/RunListing.ts +1 -1
  248. package/src/migrations/0001_initial.ts +1 -1
  249. package/src/migrations/0002_selection_store.ts +1 -1
  250. package/src/migrations/0003_plan_inputs.ts +1 -1
  251. package/src/migrations/0004_plan_environment.ts +1 -1
  252. package/src/migrations/0005_plan_merges.ts +1 -1
  253. package/src/migrations/0006_execution_listing.ts +1 -1
  254. package/src/migrations/0007_run_parent_sequence.ts +1 -1
  255. package/src/migrations/0008_deferred_consumption.ts +1 -1
  256. package/src/migrations/0009_deferred_clock_facts.ts +1 -1
  257. package/src/migrations/0010_pending_cancellation.ts +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/SelectionStore.ts"],
4
- "sourcesContent": ["/**\n * Durable suspected-edge storage for probabilistic selection.\n *\n * The store persists advisory beliefs only: training moves edge confidence and\n * appends evidence, but it never writes scheduler journal records, step keys,\n * or cache rows (law 5). The edge and snapshot schemas are `Selection`'s own \u2014\n * one source of truth, so a stored edge is by construction what\n * `Selection.select` consumes. Evidence entries stay strings: training appends\n * the observation JSON-encoded, so audits can replay the asymmetric rule\n * without the schema forking from `Selection.SuspectedEdge`.\n *\n * Model-backed selection, CLI verbs, approval routing, auto-appending\n * proposals, and scheduled recertification cadence are deliberately out of\n * scope here: `engine-store` must not grow a model dependency, no selection\n * CLI verbs are exposed yet, approval routing belongs to\n * `@smthrs/control`, proposal admission still needs human product review, and\n * cadence belongs to a system-flow/product layer rather than the primitive\n * store.\n *\n * @since 0.1.0\n */\n\nimport { DurableWriter } from \"@smthrs/database/DurableWriter\"\nimport * as Clock from \"effect/Clock\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport * as SqlClient from \"effect/unstable/sql/SqlClient\"\nimport * as Selection from \"./Selection.ts\"\n\n/**\n * Training outcomes for a suspected edge: a `hit` is a recertified deferral\n * that passed, a `miss` is one that failed \u2014 the guess was confidently wrong.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const TrainingOutcome = Schema.Literals([\"hit\", \"miss\"])\n\n/**\n * Training outcomes for a suspected edge.\n *\n * @category models\n * @since 0.1.0\n */\nexport type TrainingOutcome = typeof TrainingOutcome.Type\n\n/**\n * One observed result for a stored suspected edge.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const TrainingObservation = Schema.Struct({\n scope: Schema.NonEmptyString,\n affects: Schema.NonEmptyString,\n outcome: TrainingOutcome\n})\n\n/**\n * One observed result for a stored suspected edge.\n *\n * @category models\n * @since 0.1.0\n */\nexport type TrainingObservation = typeof TrainingObservation.Type\n\n/**\n * Stable selection-store failure codes.\n *\n * @category schemas\n * @since 1.0.0\n */\nexport const SelectionStoreErrorCode = Schema.Literals([\n \"invalid_input\",\n \"decode_failed\",\n \"persistence_failed\"\n])\n\n/**\n * Stable selection-store failure codes.\n *\n * @category models\n * @since 1.0.0\n */\nexport type SelectionStoreErrorCode = typeof SelectionStoreErrorCode.Type\n\n/**\n * A rejected public value, corrupt stored row, or unavailable database.\n *\n * @category errors\n * @since 1.0.0\n */\nexport class SelectionStoreError extends Schema.TaggedError<SelectionStoreError>()(\n \"@smthrs/engine-store/SelectionStoreError\",\n {\n code: SelectionStoreErrorCode,\n message: Schema.String,\n scope: Schema.optional(Schema.String),\n affects: Schema.optional(Schema.String),\n cause: Schema.optional(Schema.Unknown)\n }\n) {}\n\n/**\n * Durable suspected-edge persistence operations. `snapshot` pins the injected\n * clock's now, never `Date.now()`; `train` applies the asymmetric rule \u2014 a\n * hit gains five percent of the remaining headroom, a miss halves \u2014 to\n * matching stored edges only, ignoring unknown pairs rather than creating\n * them. Training retains at most {@link maxEvidenceEntries} newest evidence\n * entries per edge.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Service {\n readonly upsert: (\n edges: ReadonlyArray<Selection.SuspectedEdge>\n ) => Effect.Effect<void, SelectionStoreError>\n readonly list: () => Effect.Effect<ReadonlyArray<Selection.SuspectedEdge>, SelectionStoreError>\n readonly snapshot: () => Effect.Effect<Selection.BeliefSnapshot, SelectionStoreError>\n readonly train: (\n observations: ReadonlyArray<TrainingObservation>\n ) => Effect.Effect<void, SelectionStoreError>\n}\n\n/**\n * Service tag for the durable suspected-edge store.\n *\n * @category services\n * @since 0.1.0\n */\nexport class SelectionStore extends Context.Service<SelectionStore, Service>()(\n \"@smthrs/engine-store/SelectionStore\"\n) {}\n\nconst EvidenceListFromJsonString = Schema.fromJsonString(Schema.Array(Schema.String))\n\nconst EdgeRow = Schema.Struct({\n scope: Schema.NonEmptyString,\n affects: Schema.NonEmptyString,\n confidence: Schema.Number,\n validFromMs: Selection.NonNegativeSafeInt,\n evidenceJson: Schema.String\n})\n\ntype EdgeRow = typeof EdgeRow.Type\n\nconst invalidInput = (message: string, cause: unknown): SelectionStoreError =>\n new SelectionStoreError({ code: \"invalid_input\", message, cause })\n\nconst persistenceFailed = (message: string, cause: unknown): SelectionStoreError =>\n cause instanceof SelectionStoreError\n ? cause\n : new SelectionStoreError({ code: \"persistence_failed\", message, cause })\n\nconst decodeObservation = (input: unknown): Effect.Effect<TrainingObservation, SelectionStoreError> =>\n Schema.decodeUnknownEffect(TrainingObservation)(input).pipe(\n Effect.mapError((cause) => invalidInput(\"a training observation is invalid\", cause))\n )\n\nconst decodeEdge = (input: unknown): Effect.Effect<Selection.SuspectedEdge, SelectionStoreError> =>\n Schema.decodeUnknownEffect(Selection.SuspectedEdge)(input).pipe(\n Effect.mapError((cause) => invalidInput(\"a suspected edge is invalid\", cause))\n )\n\n// `decodeEdge` has already copied and validated this as an array of strings.\n// JSON encoding that closed value cannot fail, so retaining a fictitious\n// schema-error branch here only obscures the actual persistence failures.\nconst encodeEvidence = (evidence: ReadonlyArray<string>): Effect.Effect<string> =>\n Effect.succeed(JSON.stringify(evidence))\n\nconst decodeEvidence = (\n evidenceJson: string,\n row: Pick<EdgeRow, \"scope\" | \"affects\">\n): Effect.Effect<ReadonlyArray<string>, SelectionStoreError> =>\n Schema.decodeUnknownEffect(EvidenceListFromJsonString)(evidenceJson).pipe(\n Effect.mapError((cause) =>\n new SelectionStoreError({\n code: \"decode_failed\",\n message: `stored edge ${row.scope} -> ${row.affects} has invalid evidence`,\n scope: row.scope,\n affects: row.affects,\n cause\n })\n )\n )\n\nconst decodeRow = (input: unknown): Effect.Effect<Selection.SuspectedEdge, SelectionStoreError> =>\n Schema.decodeUnknownEffect(EdgeRow)(input).pipe(\n Effect.mapError((cause) =>\n new SelectionStoreError({\n code: \"decode_failed\",\n message: \"a stored selection edge row is invalid\",\n cause\n })\n ),\n Effect.flatMap((row) =>\n decodeEvidence(row.evidenceJson, row).pipe(\n Effect.flatMap((evidence) =>\n Schema.decodeUnknownEffect(Selection.SuspectedEdge)({\n scope: row.scope,\n affects: row.affects,\n confidence: row.confidence,\n validFromMs: row.validFromMs,\n evidence\n }).pipe(Effect.mapError((cause) =>\n new SelectionStoreError({\n code: \"decode_failed\",\n message: `stored edge ${row.scope} -> ${row.affects} is invalid`,\n scope: row.scope,\n affects: row.affects,\n cause\n })\n ))\n )\n )\n )\n )\n\nconst trainConfidence = (confidence: number, outcome: TrainingOutcome): number =>\n outcome === \"hit\"\n ? confidence + 0.05 * (1 - confidence)\n : confidence * 0.5\n\n/**\n * Maximum evidence entries retained for one suspected edge. Training drops\n * the oldest entries after this bound and preserves the newest entries in\n * observation order.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const maxEvidenceEntries = 128\n\n/**\n * Builds the SQL-backed suspected-edge store.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make: Effect.Effect<Service, never, DurableWriter | SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n const writer = yield* DurableWriter\n\n const selectEdge = (observation: TrainingObservation) =>\n sql<EdgeRow>`\n SELECT\n scope,\n affects,\n confidence,\n valid_from_ms AS \"validFromMs\",\n evidence_json AS \"evidenceJson\"\n FROM flows_selection_suspected_edges\n WHERE scope = ${observation.scope}\n AND affects = ${observation.affects}\n `\n\n const writeEdge = (edge: Selection.SuspectedEdge): Effect.Effect<void, SelectionStoreError> =>\n Effect.gen(function*() {\n const evidenceJson = yield* encodeEvidence(edge.evidence)\n yield* sql`\n INSERT INTO flows_selection_suspected_edges (\n scope, affects, confidence, valid_from_ms, evidence_json\n ) VALUES (\n ${edge.scope}, ${edge.affects}, ${edge.confidence}, ${edge.validFromMs}, ${evidenceJson}\n ) ON CONFLICT (scope, affects) DO UPDATE SET\n confidence = excluded.confidence,\n valid_from_ms = excluded.valid_from_ms,\n evidence_json = excluded.evidence_json\n `.pipe(Effect.mapError((cause) =>\n persistenceFailed(\n `could not persist edge ${edge.scope} -> ${edge.affects}`,\n cause\n )\n ))\n })\n\n const upsert: Service[\"upsert\"] = Effect.fn(\"SelectionStore.upsert\")((edges) =>\n Effect.gen(function*() {\n const decoded = yield* Effect.forEach(edges, decodeEdge)\n yield* writer.write(Effect.forEach(decoded, writeEdge, { discard: true })).pipe(\n Effect.mapError((cause) => persistenceFailed(\"could not persist suspected edges\", cause))\n )\n })\n )\n\n const list: Service[\"list\"] = Effect.fn(\"SelectionStore.list\")(() =>\n Effect.gen(function*() {\n const rows = yield* sql<EdgeRow>`\n SELECT\n scope,\n affects,\n confidence,\n valid_from_ms AS \"validFromMs\",\n evidence_json AS \"evidenceJson\"\n FROM flows_selection_suspected_edges\n ORDER BY scope, affects\n `.pipe(Effect.mapError((cause) => persistenceFailed(\"could not list suspected edges\", cause)))\n return yield* Effect.forEach(rows, decodeRow)\n })\n )\n\n const snapshot: Service[\"snapshot\"] = Effect.fn(\"SelectionStore.snapshot\")(() =>\n Effect.gen(function*() {\n const pinnedAtMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor))\n const edges = yield* list()\n return { pinnedAtMs, edges }\n })\n )\n\n const train: Service[\"train\"] = Effect.fn(\"SelectionStore.train\")((observations) =>\n Effect.gen(function*() {\n const decoded = yield* Effect.forEach(observations, decodeObservation)\n yield* writer.write(Effect.forEach(\n decoded,\n (observation) =>\n Effect.gen(function*() {\n const rows = yield* selectEdge(observation).pipe(\n Effect.mapError((cause) =>\n persistenceFailed(\n `could not read edge ${observation.scope} -> ${observation.affects}`,\n cause\n )\n )\n )\n const row = rows[0]\n if (row === undefined) return\n const edge = yield* decodeRow(row)\n yield* writeEdge({\n ...edge,\n confidence: trainConfidence(edge.confidence, observation.outcome),\n evidence: [...edge.evidence, JSON.stringify(observation)].slice(-maxEvidenceEntries)\n })\n }),\n { discard: true }\n )).pipe(Effect.mapError((cause) => persistenceFailed(\"could not train suspected edges\", cause)))\n })\n )\n\n return { upsert, list, snapshot, train }\n})\n\n/**\n * Provides the SQL-backed suspected-edge store.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<SelectionStore, never, DurableWriter | SqlClient.SqlClient> = Layer.effect(\n SelectionStore\n)(make)\n"],
4
+ "sourcesContent": ["/**\n * Durable suspected-edge storage for probabilistic selection.\n *\n * The store persists advisory beliefs only: training moves edge confidence and\n * appends evidence, but it never writes scheduler journal records, step keys,\n * or cache rows (law 5). The edge and snapshot schemas are `Selection`'s own \u2014\n * one source of truth, so a stored edge is by construction what\n * `Selection.select` consumes. Evidence entries stay strings: training appends\n * the observation JSON-encoded, so audits can replay the asymmetric rule\n * without the schema forking from `Selection.SuspectedEdge`.\n *\n * Model-backed selection, CLI verbs, approval routing, auto-appending\n * proposals, and scheduled recertification cadence are deliberately out of\n * scope here: `engine-store` must not grow a model dependency, no selection\n * CLI verbs are exposed yet, approval routing belongs to\n * `@smthrs/control`, proposal admission still needs human product review, and\n * cadence belongs to a system-flow/product layer rather than the primitive\n * store.\n *\n * @since 0.1.0\n */\n\nimport { DurableWriter } from \"@smthrs/database/DurableWriter\"\nimport * as Clock from \"effect/Clock\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\nimport * as Selection from \"./Selection.ts\"\n\n/**\n * Training outcomes for a suspected edge: a `hit` is a recertified deferral\n * that passed, a `miss` is one that failed \u2014 the guess was confidently wrong.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const TrainingOutcome = Schema.Literals([\"hit\", \"miss\"])\n\n/**\n * Training outcomes for a suspected edge.\n *\n * @category models\n * @since 0.1.0\n */\nexport type TrainingOutcome = typeof TrainingOutcome.Type\n\n/**\n * One observed result for a stored suspected edge.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const TrainingObservation = Schema.Struct({\n scope: Schema.NonEmptyString,\n affects: Schema.NonEmptyString,\n outcome: TrainingOutcome\n})\n\n/**\n * One observed result for a stored suspected edge.\n *\n * @category models\n * @since 0.1.0\n */\nexport type TrainingObservation = typeof TrainingObservation.Type\n\n/**\n * Stable selection-store failure codes.\n *\n * @category schemas\n * @since 1.0.0\n */\nexport const SelectionStoreErrorCode = Schema.Literals([\n \"invalid_input\",\n \"decode_failed\",\n \"persistence_failed\"\n])\n\n/**\n * Stable selection-store failure codes.\n *\n * @category models\n * @since 1.0.0\n */\nexport type SelectionStoreErrorCode = typeof SelectionStoreErrorCode.Type\n\n/**\n * A rejected public value, corrupt stored row, or unavailable database.\n *\n * @category errors\n * @since 1.0.0\n */\nexport class SelectionStoreError extends Schema.TaggedError<SelectionStoreError>()(\n \"@smthrs/engine-store/SelectionStoreError\",\n {\n code: SelectionStoreErrorCode,\n message: Schema.String,\n scope: Schema.optional(Schema.String),\n affects: Schema.optional(Schema.String),\n cause: Schema.optional(Schema.Unknown)\n }\n) {}\n\n/**\n * Durable suspected-edge persistence operations. `snapshot` pins the injected\n * clock's now, never `Date.now()`; `train` applies the asymmetric rule \u2014 a\n * hit gains five percent of the remaining headroom, a miss halves \u2014 to\n * matching stored edges only, ignoring unknown pairs rather than creating\n * them. Training retains at most {@link maxEvidenceEntries} newest evidence\n * entries per edge.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Service {\n readonly upsert: (\n edges: ReadonlyArray<Selection.SuspectedEdge>\n ) => Effect.Effect<void, SelectionStoreError>\n readonly list: () => Effect.Effect<ReadonlyArray<Selection.SuspectedEdge>, SelectionStoreError>\n readonly snapshot: () => Effect.Effect<Selection.BeliefSnapshot, SelectionStoreError>\n readonly train: (\n observations: ReadonlyArray<TrainingObservation>\n ) => Effect.Effect<void, SelectionStoreError>\n}\n\n/**\n * Service tag for the durable suspected-edge store.\n *\n * @category services\n * @since 0.1.0\n */\nexport class SelectionStore extends Context.Service<SelectionStore, Service>()(\n \"@smthrs/engine-store/SelectionStore\"\n) {}\n\nconst EvidenceListFromJsonString = Schema.fromJsonString(Schema.Array(Schema.String))\n\nconst EdgeRow = Schema.Struct({\n scope: Schema.NonEmptyString,\n affects: Schema.NonEmptyString,\n confidence: Schema.Number,\n validFromMs: Selection.NonNegativeSafeInt,\n evidenceJson: Schema.String\n})\n\ntype EdgeRow = typeof EdgeRow.Type\n\nconst invalidInput = (message: string, cause: unknown): SelectionStoreError =>\n new SelectionStoreError({ code: \"invalid_input\", message, cause })\n\nconst persistenceFailed = (message: string, cause: unknown): SelectionStoreError =>\n cause instanceof SelectionStoreError\n ? cause\n : new SelectionStoreError({ code: \"persistence_failed\", message, cause })\n\nconst decodeObservation = (input: unknown): Effect.Effect<TrainingObservation, SelectionStoreError> =>\n Schema.decodeUnknownEffect(TrainingObservation)(input).pipe(\n Effect.mapError((cause) => invalidInput(\"a training observation is invalid\", cause))\n )\n\nconst decodeEdge = (input: unknown): Effect.Effect<Selection.SuspectedEdge, SelectionStoreError> =>\n Schema.decodeUnknownEffect(Selection.SuspectedEdge)(input).pipe(\n Effect.mapError((cause) => invalidInput(\"a suspected edge is invalid\", cause))\n )\n\n// `decodeEdge` has already copied and validated this as an array of strings.\n// JSON encoding that closed value cannot fail, so retaining a fictitious\n// schema-error branch here only obscures the actual persistence failures.\nconst encodeEvidence = (evidence: ReadonlyArray<string>): Effect.Effect<string> =>\n Effect.succeed(JSON.stringify(evidence))\n\nconst decodeEvidence = (\n evidenceJson: string,\n row: Pick<EdgeRow, \"scope\" | \"affects\">\n): Effect.Effect<ReadonlyArray<string>, SelectionStoreError> =>\n Schema.decodeUnknownEffect(EvidenceListFromJsonString)(evidenceJson).pipe(\n Effect.mapError((cause) =>\n new SelectionStoreError({\n code: \"decode_failed\",\n message: `stored edge ${row.scope} -> ${row.affects} has invalid evidence`,\n scope: row.scope,\n affects: row.affects,\n cause\n })\n )\n )\n\nconst decodeRow = (input: unknown): Effect.Effect<Selection.SuspectedEdge, SelectionStoreError> =>\n Schema.decodeUnknownEffect(EdgeRow)(input).pipe(\n Effect.mapError((cause) =>\n new SelectionStoreError({\n code: \"decode_failed\",\n message: \"a stored selection edge row is invalid\",\n cause\n })\n ),\n Effect.flatMap((row) =>\n decodeEvidence(row.evidenceJson, row).pipe(\n Effect.flatMap((evidence) =>\n Schema.decodeUnknownEffect(Selection.SuspectedEdge)({\n scope: row.scope,\n affects: row.affects,\n confidence: row.confidence,\n validFromMs: row.validFromMs,\n evidence\n }).pipe(Effect.mapError((cause) =>\n new SelectionStoreError({\n code: \"decode_failed\",\n message: `stored edge ${row.scope} -> ${row.affects} is invalid`,\n scope: row.scope,\n affects: row.affects,\n cause\n })\n ))\n )\n )\n )\n )\n\nconst trainConfidence = (confidence: number, outcome: TrainingOutcome): number =>\n outcome === \"hit\"\n ? confidence + 0.05 * (1 - confidence)\n : confidence * 0.5\n\n/**\n * Maximum evidence entries retained for one suspected edge. Training drops\n * the oldest entries after this bound and preserves the newest entries in\n * observation order.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const maxEvidenceEntries = 128\n\n/**\n * Builds the SQL-backed suspected-edge store.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make: Effect.Effect<Service, never, DurableWriter | SqlClient.SqlClient> = Effect.gen(function*() {\n const sql = yield* SqlClient.SqlClient\n const writer = yield* DurableWriter\n\n const selectEdge = (observation: TrainingObservation) =>\n sql<EdgeRow>`\n SELECT\n scope,\n affects,\n confidence,\n valid_from_ms AS \"validFromMs\",\n evidence_json AS \"evidenceJson\"\n FROM flows_selection_suspected_edges\n WHERE scope = ${observation.scope}\n AND affects = ${observation.affects}\n `\n\n const writeEdge = (edge: Selection.SuspectedEdge): Effect.Effect<void, SelectionStoreError> =>\n Effect.gen(function*() {\n const evidenceJson = yield* encodeEvidence(edge.evidence)\n yield* sql`\n INSERT INTO flows_selection_suspected_edges (\n scope, affects, confidence, valid_from_ms, evidence_json\n ) VALUES (\n ${edge.scope}, ${edge.affects}, ${edge.confidence}, ${edge.validFromMs}, ${evidenceJson}\n ) ON CONFLICT (scope, affects) DO UPDATE SET\n confidence = excluded.confidence,\n valid_from_ms = excluded.valid_from_ms,\n evidence_json = excluded.evidence_json\n `.pipe(Effect.mapError((cause) =>\n persistenceFailed(\n `could not persist edge ${edge.scope} -> ${edge.affects}`,\n cause\n )\n ))\n })\n\n const upsert: Service[\"upsert\"] = Effect.fn(\"SelectionStore.upsert\")((edges) =>\n Effect.gen(function*() {\n const decoded = yield* Effect.forEach(edges, decodeEdge)\n yield* writer.write(Effect.forEach(decoded, writeEdge, { discard: true })).pipe(\n Effect.mapError((cause) => persistenceFailed(\"could not persist suspected edges\", cause))\n )\n })\n )\n\n const list: Service[\"list\"] = Effect.fn(\"SelectionStore.list\")(() =>\n Effect.gen(function*() {\n const rows = yield* sql<EdgeRow>`\n SELECT\n scope,\n affects,\n confidence,\n valid_from_ms AS \"validFromMs\",\n evidence_json AS \"evidenceJson\"\n FROM flows_selection_suspected_edges\n ORDER BY scope, affects\n `.pipe(Effect.mapError((cause) => persistenceFailed(\"could not list suspected edges\", cause)))\n return yield* Effect.forEach(rows, decodeRow)\n })\n )\n\n const snapshot: Service[\"snapshot\"] = Effect.fn(\"SelectionStore.snapshot\")(() =>\n Effect.gen(function*() {\n const pinnedAtMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor))\n const edges = yield* list()\n return { pinnedAtMs, edges }\n })\n )\n\n const train: Service[\"train\"] = Effect.fn(\"SelectionStore.train\")((observations) =>\n Effect.gen(function*() {\n const decoded = yield* Effect.forEach(observations, decodeObservation)\n yield* writer.write(Effect.forEach(\n decoded,\n (observation) =>\n Effect.gen(function*() {\n const rows = yield* selectEdge(observation).pipe(\n Effect.mapError((cause) =>\n persistenceFailed(\n `could not read edge ${observation.scope} -> ${observation.affects}`,\n cause\n )\n )\n )\n const row = rows[0]\n if (row === undefined) return\n const edge = yield* decodeRow(row)\n yield* writeEdge({\n ...edge,\n confidence: trainConfidence(edge.confidence, observation.outcome),\n evidence: [...edge.evidence, JSON.stringify(observation)].slice(-maxEvidenceEntries)\n })\n }),\n { discard: true }\n )).pipe(Effect.mapError((cause) => persistenceFailed(\"could not train suspected edges\", cause)))\n })\n )\n\n return { upsert, list, snapshot, train }\n})\n\n/**\n * Provides the SQL-backed suspected-edge store.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<SelectionStore, never, DurableWriter | SqlClient.SqlClient> = Layer.effect(\n SelectionStore\n)(make)\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -60,7 +60,7 @@ var import_keys = require("@smthrs/keys");
60
60
  var FileSet = __toESM(require("@smthrs/plan/FileSet"), 1);
61
61
  var Context = __toESM(require("effect/Context"), 1);
62
62
  var Effect = __toESM(require("effect/Effect"), 1);
63
- var Encoding = __toESM(require("effect/Encoding"), 1);
63
+ var Base64 = __toESM(require("effect/encoding/Base64"), 1);
64
64
  var FileSystem = __toESM(require("effect/FileSystem"), 1);
65
65
  var Layer = __toESM(require("effect/Layer"), 1);
66
66
  var Path = __toESM(require("effect/Path"), 1);
@@ -310,7 +310,7 @@ const makeFileSystem = (fs, platformPath, artifacts, options = {}) => {
310
310
  path,
311
311
  digest,
312
312
  sizeBytes: bytes.length,
313
- content: Encoding.encodeBase64(bytes)
313
+ content: Base64.encode(bytes)
314
314
  },
315
315
  inlinedBytes: bytes.length
316
316
  };
@@ -338,7 +338,7 @@ const makeFileSystem = (fs, platformPath, artifacts, options = {}) => {
338
338
  if (current) return;
339
339
  let bytes;
340
340
  if (output.content !== void 0) {
341
- const decoded = Encoding.decodeBase64(output.content);
341
+ const decoded = Base64.decode(output.content);
342
342
  if (Result.isFailure(decoded)) {
343
343
  return yield* Effect.fail(inlineCorruption(output.path, `${output.digest}`));
344
344
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/StepBoundary.ts"],
4
- "sourcesContent": ["/**\n * Hermetic step boundary contracts, the filesystem-backed production\n * implementation, and the deterministic test implementation.\n *\n * @since 0.1.0\n */\n\nimport * as ArtifactStore from \"@smthrs/artifacts/ArtifactStore\"\nimport { Sha256 } from \"@smthrs/crypto\"\nimport { FileBoundary } from \"@smthrs/flow/FileBoundary\"\nimport { FileInput } from \"@smthrs/flow/FileInput\"\nimport * as KernelFileSystem from \"@smthrs/kernel/FileSystem\"\nimport { Workspace as KernelWorkspace } from \"@smthrs/kernel/Workspace\"\nimport { DerivedKey } from \"@smthrs/keys\"\nimport * as FileSet from \"@smthrs/plan/FileSet\"\nimport * as Context from \"effect/Context\"\nimport type * as Crypto from \"effect/Crypto\"\nimport * as Effect from \"effect/Effect\"\nimport * as Encoding from \"effect/Encoding\"\nimport * as FileSystem from \"effect/FileSystem\"\nimport * as Layer from \"effect/Layer\"\nimport * as Path from \"effect/Path\"\nimport * as Result from \"effect/Result\"\nimport * as Schema from \"effect/Schema\"\nimport * as FileBoundarySnapshot from \"./internal/FileBoundarySnapshot.ts\"\nimport * as FileEnumeration from \"./internal/FileEnumeration.ts\"\nimport { compareText } from \"./internal/Ordering.ts\"\n\n/**\n * A file boundary that has been measured but not yet run: the caller's\n * declaration, paired with what the host actually found on disk for it.\n *\n * `prepare` produces one of these before a step executes and `settle` consumes\n * it afterwards, which is what lets the boundary say whether the world moved\n * underneath the declaration. Check it with {@link readSetMatches}.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const PreparedBoundary = Schema.Struct({\n descriptor: FileBoundary,\n /**\n * What the host actually measured for the declared read set at prepare\n * time. The declared digests are caller metadata folded into the step key;\n * this is the evidence that they still describe reality, and a sealed\n * cache hit is refused when the two disagree (issue #90).\n */\n readSnapshot: Schema.Array(FileInput)\n})\n\n/**\n * The value form of {@link PreparedBoundary}.\n *\n * @since 0.1.0\n * @category models\n */\nexport type PreparedBoundary = typeof PreparedBoundary.Type\n\n/**\n * Exact read inputs from a boundary after ignoring declarations that still need expansion.\n *\n * @category accessors\n * @since 0.1.0\n */\nexport const exactReads = (descriptor: FileBoundary): ReadonlyArray<FileInput> =>\n descriptor.readSet.filter((entry): entry is FileInput => !FileSet.isGlob(entry))\n\n/**\n * Whether every declared read still matches what the host measured.\n *\n * Reads the host reports but the declaration never claimed are ignored: the\n * declaration is the dependency edge set, and an incidental extra read is not\n * one of its edges. A declared path missing from the measurement counts as a\n * mismatch \u2014 its value is unknown, so reuse cannot be justified.\n *\n * @since 0.1.0\n * @category predicates\n */\nexport const readSetMatches = (prepared: PreparedBoundary): boolean => {\n const digestsByPath = new Map<string, Set<string>>()\n for (const measured of prepared.readSnapshot) {\n const digests = digestsByPath.get(measured.path)\n if (digests === undefined) digestsByPath.set(measured.path, new Set([measured.digest]))\n else digests.add(measured.digest)\n }\n return prepared.descriptor.readSet.every((entry) =>\n !FileSet.isGlob(entry) &&\n digestsByPath.get(entry.path)?.has(entry.digest) === true\n )\n}\n\n/**\n * The paths a step wrote that its `expected` write set did not name, together\n * with the diff identity they were observed under.\n *\n * An expected-mode boundary reports these rather than failing: the declaration\n * is a prediction, and a wrong prediction is evidence to journal, not an\n * error. A hard-mode boundary raises {@link UndeclaredWrite} for the same\n * observation.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const ExpectedSetDeviation = Schema.TaggedStruct(\"ExpectedSetDeviation\", {\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n})\n\n/**\n * Declared writes an expected-mode step did not produce, and did not declare\n * as removals.\n *\n * The absence itself is the defect: recording it as valid evidence caches the\n * claim \"this file should not exist\", which `replayOutputs` then acts on by\n * deleting the path on a workspace that never ran the step. A hard-mode\n * boundary raises {@link MissingDeclaredOutput} for the same observation.\n *\n * Expected mode records it rather than failing because the declaration there is\n * a prediction \u2014 and because a deviation of any variant already bars the\n * evidence from the shared cache (`ActionPersistence` gates `recordCache` on\n * `deviation === undefined`), so an unexplained absence never reaches another\n * host either way.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const MissingOutputDeviation = Schema.TaggedStruct(\"MissingDeclaredOutput\", {\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n})\n\n/**\n * Declared removals an expected-mode step left in place.\n *\n * The dual of {@link MissingOutputDeviation}: a removal is a promise about the\n * post-state exactly as a write is. A path that was promised absent but is\n * still present \u2014 possibly mutated \u2014 must not settle as valid evidence, or\n * the mutation is cached under a declaration that disclaimed it and replay\n * materializes it everywhere. A hard-mode boundary raises\n * {@link SurvivingDeclaredRemoval} for the same observation.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const SurvivingRemovalDeviation = Schema.TaggedStruct(\"SurvivingDeclaredRemoval\", {\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n})\n\n/**\n * What a settled boundary observed that its declaration did not predict.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const BoundaryDeviation = Schema.Union([ExpectedSetDeviation, MissingOutputDeviation, SurvivingRemovalDeviation])\n\n/**\n * The value form of {@link BoundaryDeviation}.\n *\n * @since 0.1.0\n * @category models\n */\nexport type BoundaryDeviation = typeof BoundaryDeviation.Type\n\n/**\n * What a settled boundary proved about a step's writes \u2014 the record the\n * journal keeps and a later cache hit is justified against.\n *\n * `diffIdentity` names the post-state the step produced; `declaredOutputs`\n * carries whatever the implementation needs to reproduce it on a workspace\n * that never ran the step. The two optional fields are the honest gaps:\n * `wholeTreeWritesVerified` is present only when the boundary could observe\n * the entire execution tree, and `deviation` only when it saw writes the\n * declaration did not predict.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const BoundaryEvidence = Schema.Struct({\n declaredOutputs: Schema.Unknown,\n diffIdentity: Schema.NonEmptyString,\n /**\n * The boundary observed the whole execution tree and proved that every\n * write was covered by the descriptor's write set. Only evidence carrying\n * this explicit proof may enter the cross-run cache. Older evidence and\n * boundaries that can inspect declared paths only omit it.\n */\n wholeTreeWritesVerified: Schema.optional(Schema.Literal(true)),\n /** The body could observe only declared filesystem inputs. */\n hermeticReadsVerified: Schema.optional(Schema.Literal(true)),\n deviation: Schema.optional(BoundaryDeviation)\n})\n\n/**\n * The value form of {@link BoundaryEvidence}.\n *\n * @since 0.1.0\n * @category models\n */\nexport type BoundaryEvidence = typeof BoundaryEvidence.Type\n\n/**\n * A hard-mode step wrote outside its declared write set.\n *\n * Hard mode treats the declaration as a contract, so this is a refusal, not a\n * report: the step's evidence is never journaled and never becomes a cache\n * entry. An expected-mode boundary records the same observation as a\n * {@link BoundaryDeviation} instead.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class UndeclaredWrite extends Schema.TaggedError<UndeclaredWrite>()(\n \"@smthrs/engine-store/UndeclaredWrite\",\n {\n code: Schema.Literal(\"undeclared_write\"),\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n }\n) {}\n\n/**\n * A step finished without producing a path its write set declared, and did not\n * declare that path as a removal.\n *\n * Bazel hard-fails the same observation \u2014 `SkyframeActionExecutor.checkOutputs`\n * reports \"output was not created\" \u2014 and for the same reason: the alternative\n * is to record `digest: null` as valid evidence, which caches the claim \"this\n * file should not exist\" and makes every later replay delete the path. A step\n * that crashed after declaring its writes would poison the cache with an\n * eraser.\n *\n * Declaring the path in {@link FileBoundary}'s `removes` is how a deliberate\n * deletion says so, and it is the only thing that makes the absence legitimate.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class MissingDeclaredOutput extends Schema.TaggedError<MissingDeclaredOutput>()(\n \"@smthrs/engine-store/MissingDeclaredOutput\",\n {\n code: Schema.Literal(\"missing_declared_output\"),\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n }\n) {}\n\n/**\n * A hard-mode step left a declared removal in place.\n *\n * The dual of {@link MissingDeclaredOutput}: `removes` promises the path is\n * absent afterwards, and a path that survived \u2014 or was quietly rewritten \u2014\n * is a post-state the declaration disclaimed. Settling it as evidence would\n * cache the surviving bytes under a removal and hand them to every replay.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class SurvivingDeclaredRemoval extends Schema.TaggedError<SurvivingDeclaredRemoval>()(\n \"@smthrs/engine-store/SurvivingDeclaredRemoval\",\n {\n code: Schema.Literal(\"surviving_declared_removal\"),\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n }\n) {}\n\n/**\n * The host could not honour the boundary at all \u2014 a filesystem that cannot be\n * measured, a path that cannot be read, a transient I/O failure.\n *\n * It is the catch-all host refusal, deliberately kept distinct from the two\n * refusals a caller can act on: {@link BoundaryCorruption} (the bytes changed\n * under a recorded digest) and {@link MissingArtifact} (the bytes might be\n * fetchable from a shared tier).\n *\n * @since 0.1.0\n * @category errors\n */\nexport class UnsupportedBoundary extends Schema.TaggedError<UnsupportedBoundary>()(\n \"@smthrs/engine-store/UnsupportedBoundary\",\n {\n code: Schema.Literal(\"unsupported_boundary\"),\n message: Schema.String,\n /**\n * The refusing host or artifact-store failure, carried whole rather than\n * flattened into the message (`PlatformError`'s own convention), so the\n * journal record and a live debugger both see the original error.\n */\n cause: Schema.optional(Schema.Defect())\n }\n) {}\n\n/**\n * Recorded boundary evidence whose bytes no longer match their recorded\n * digest \u2014 an integrity violation of the store's strongest invariant, as\n * opposed to a transient host failure (issue #150). The two classes were\n * previously conflated under one `UnsupportedBoundary` tag, so a failing\n * disk corrupting many blobs journalled identically to a one-off EIO.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class BoundaryCorruption extends Schema.TaggedError<BoundaryCorruption>()(\n \"@smthrs/engine-store/BoundaryCorruption\",\n {\n code: Schema.Literal(\"boundary_corruption\"),\n path: Schema.String,\n recordedDigest: Schema.String,\n measuredDigest: Schema.String\n }\n) {}\n\n/**\n * Recorded evidence references an artifact this host's store does not hold\n * (issue #172). Distinct from {@link UnsupportedBoundary} because it is the one\n * replay refusal a *shared artifact tier* can repair: the caller fetches the\n * blob from the remote store, writes it back locally, and retries the replay\n * once before falling through to a real execution. Every other host refusal\n * says nothing about where the bytes might be.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class MissingArtifact extends Schema.TaggedError<MissingArtifact>()(\n \"@smthrs/engine-store/MissingArtifact\",\n {\n code: Schema.Literal(\"missing_artifact\"),\n path: Schema.String,\n digest: Schema.String\n }\n) {}\n\n/**\n * The three-call lifecycle of a hermetic step boundary.\n *\n * `prepare` measures the declared read set before the step runs; `settle`\n * measures the writes afterwards and turns them into {@link BoundaryEvidence};\n * `replayOutputs` reproduces a previous step's outputs from that evidence, so\n * a cache hit can materialize results on a workspace that never executed\n * anything.\n *\n * @since 0.1.0\n * @category services\n */\nexport interface Service {\n readonly prepare: (\n descriptor: FileBoundary\n ) => Effect.Effect<PreparedBoundary, UnsupportedBoundary, Crypto.Crypto>\n readonly settle: (\n prepared: PreparedBoundary\n ) => Effect.Effect<\n BoundaryEvidence,\n UndeclaredWrite | MissingDeclaredOutput | SurvivingDeclaredRemoval | UnsupportedBoundary | BoundaryCorruption,\n Crypto.Crypto\n >\n readonly replayOutputs: (\n evidence: BoundaryEvidence\n ) => Effect.Effect<void, UnsupportedBoundary | BoundaryCorruption | MissingArtifact, Crypto.Crypto>\n}\n\n/**\n * The service key for a {@link Service}. Provide it with `layer` for\n * a real workspace or `layerTest` for a deterministic in-memory one.\n *\n * @since 0.1.0\n * @category services\n */\nexport const StepBoundary: Context.Service<Service, Service> = Context.Service<Service>(\n \"@smthrs/engine-store/StepBoundary\"\n)\n\n/**\n * Brands a {@link Service} implementation as a `StepBoundary`, so a new\n * boundary backend is type-checked where it is written rather than where it is\n * provided.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const make = (service: Service): Service => StepBoundary.of(service)\n\n/**\n * The digest a measurement reports for a declared path that does not exist.\n * Declared digests are hex SHA-256 strings, so the sentinel can never\n * collide with a real measurement \u2014 a declaration naming a vanished file\n * always mismatches and the cache hit is refused.\n */\nconst absentDigest = \"absent\"\n\n/**\n * The persisted shape of the production layer's `declaredOutputs`: the\n * declared write set's materialized post-state, so a cache-hit replay can\n * reproduce the outputs on a workspace that never ran the step.\n *\n * Outputs are recorded by content digest, never inlined unbounded\n * (issue #113): a `null` digest records that the path did not exist at\n * settle time; `content` (base64) is present only when the payload fits the\n * configured inline bound, otherwise the bytes live in the host's\n * content-addressed object directory under the digest and the row carries\n * only the reference. Temporal's blob-size limits are the prior art \u2014 the\n * evidence is persisted into every attempt row and cache entry.\n *\n * The digest is decoded against the store's strict address schema\n * (`ArtifactStore.Digest`), so a persisted row carrying a malformed address\n * fails the decode here instead of reaching the store or the sync RPCs.\n */\nconst DigestReferencedOutput = Schema.Struct({\n path: Schema.String,\n digest: Schema.NullOr(ArtifactStore.Digest),\n sizeBytes: Schema.optional(Schema.Number),\n content: Schema.optional(Schema.String)\n})\n\nconst MaterializedOutputs = Schema.Struct({\n outputs: Schema.Array(DigestReferencedOutput),\n trees: Schema.optional(Schema.Array(Schema.Struct({\n path: Schema.String,\n identity: Schema.String\n })))\n})\n\n/**\n * The directory portion of a slash-separated path, or `undefined` for a\n * bare filename (nothing to create). Boundary paths are workspace-relative\n * and slash-separated \u2014 the same normalization the kernel `FileSystem`\n * layer applies.\n */\nconst parentDirectory = (path: string): string | undefined => {\n const index = path.lastIndexOf(\"/\")\n return index <= 0 ? undefined : path.slice(0, index)\n}\n\nconst hostFailure = (cause: unknown): UnsupportedBoundary =>\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message: \"the host filesystem could not enforce the step boundary\",\n cause\n })\n\n/**\n * An artifact store that refused the operation outright \u2014 a failing disk, an\n * unreachable shared tier, an address that is not a content address. Unlike a\n * miss or a corruption it says nothing about the artifact, so it stays the\n * ordinary retryable host refusal the issue-#107 call sites already handle.\n */\nconst artifactFailure = (cause: ArtifactStore.ArtifactStoreError): UnsupportedBoundary =>\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message: `the artifact store could not serve the step boundary: ${cause.message}`,\n cause\n })\n\n/**\n * Corruption of an *inline* evidence payload (issue #159): the recorded\n * base64 no longer decodes, so there are no bytes to measure a digest of \u2014\n * the `invalid_base64` sentinel records that the measurement itself was\n * impossible, which is still cache-origin corruption and never a transient\n * host refusal.\n */\nconst inlineCorruption = (path: string, recordedDigest: string): BoundaryCorruption =>\n new BoundaryCorruption({\n code: \"boundary_corruption\",\n path,\n recordedDigest,\n measuredDigest: \"invalid_base64\"\n })\n\n/**\n * The two size bounds that decide when a settled output is inlined into\n * boundary evidence and when it is spilled to the `ArtifactStore` by digest.\n *\n * Both default to values that keep an evidence row small enough to journal;\n * raise them only when the workspace's outputs are known to be small and the\n * extra artifact round-trip is measurably costing something.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface FileSystemOptions {\n /**\n * The largest output (in bytes) inlined into the boundary evidence\n * itself. Anything larger is handed to the `ArtifactStore` and recorded by\n * digest reference only (issue #113). Defaults to 1 MiB.\n */\n readonly maxInlineBytes?: number | undefined\n /**\n * The largest *aggregate* inline payload (in bytes) a single settle may\n * fold into its boundary evidence (issue #122). A per-output bound alone\n * still let a wide write set multiply many individually-small payloads\n * into an unbounded evidence row; once an output would push the running\n * total past this bound it is spilled to the `ArtifactStore` and\n * recorded by digest reference only, even though it individually fits\n * {@link maxInlineBytes}. Defaults to 8 MiB.\n */\n readonly maxTotalInlineBytes?: number | undefined\n /**\n * The workspace root cache replay confines its writes, removals, and prune\n * walk to. A filesystem already confined to a root (the kernel-guarded\n * layer's service, or a `confined` view) keeps its own root and ignores\n * this. Otherwise it defaults to the process working directory, the root a\n * path-based host resolves `prepare` and `settle` against. {@link layer}\n * passes the kernel `Workspace` root when one is in context.\n */\n readonly root?: string | undefined\n}\n\nconst defaultMaxInlineBytes = 1024 * 1024\nconst defaultMaxTotalInlineBytes = 8 * 1024 * 1024\ninterface MeasuredDigest {\n readonly digest: ArtifactStore.Digest\n readonly bytes: Uint8Array\n}\n\ninterface BatchedDigest {\n readonly digest: ArtifactStore.Digest\n readonly sizeBytes: number\n readonly bytes?: Uint8Array\n}\n\ntype MaterializedOutput = typeof DigestReferencedOutput.Type\n\n/**\n * The digests this evidence references rather than inlines \u2014 the set that must\n * be durable in a shared artifact tier before the evidence's cache entry\n * becomes observable there, and the set a replay on a fresh host has to fetch.\n *\n * Evidence recorded by a foreign boundary implementation references nothing\n * this store can name, so it yields the empty list rather than failing: the\n * caller's replay path already refuses such evidence with\n * {@link UnsupportedBoundary}.\n *\n * @since 0.1.0\n * @category accessors\n */\nexport const referencedDigests = (evidence: BoundaryEvidence): ReadonlyArray<ArtifactStore.Digest> => {\n const decoded = Schema.decodeUnknownResult(MaterializedOutputs)(evidence.declaredOutputs)\n if (decoded._tag === \"Failure\") return []\n const digests: Array<ArtifactStore.Digest> = []\n for (const output of decoded.success.outputs) {\n if (output.digest === null || output.content !== undefined) continue\n digests.push(output.digest)\n }\n return digests\n}\n\n/**\n * Builds the filesystem-backed boundary service.\n *\n * The blob mechanics \u2014 content addressing, atomic publication, digest\n * verification, dedupe \u2014 belong to `artifacts` and were extracted into\n * `@smthrs/artifacts` (`packages/smithers/flows/artifacts/docs/api.md`). What stays here\n * is the *policy* that decides which outputs become blobs at all: the\n * inline-versus-spill budgets are a property of how large an evidence row may\n * get, not of how bytes are stored.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const makeFileSystem = (\n fs: FileSystem.FileSystem,\n platformPath: Path.Path,\n artifacts: ArtifactStore.Service,\n options: FileSystemOptions = {}\n): Service => {\n const maxInlineBytes = options.maxInlineBytes ?? defaultMaxInlineBytes\n const maxTotalInlineBytes = options.maxTotalInlineBytes ?? defaultMaxTotalInlineBytes\n // Stat tuples are not content identities: a same-size rewrite can preserve\n // mtime, device, and inode on every supported filesystem.\n const readDigestFrom = Effect.fn(\"StepBoundary.readDigest\")(function*(host: FileSystem.FileSystem, path: string) {\n const bytes = yield* host.readFile(path)\n const digest = yield* Schema.decodeUnknownEffect(Sha256)(bytes).pipe(Effect.orDie)\n return { digest, bytes } satisfies MeasuredDigest\n })\n const readDigest = (path: string) => readDigestFrom(fs, path)\n const batch = KernelFileSystem.batch(fs)\n const measurements = Effect.fn(\"StepBoundary.measurements\")(\n function*(paths: ReadonlyArray<string>, content: boolean) {\n if (batch === undefined) {\n return yield* Effect.forEach(paths, (path) =>\n readDigest(path).pipe(\n Effect.map((value): BatchedDigest => ({\n digest: value.digest,\n sizeBytes: value.bytes.length,\n ...(content ? { bytes: value.bytes } : {})\n })),\n Effect.catchReason(\"PlatformError\", \"NotFound\", () => Effect.succeed(undefined)),\n Effect.mapError(hostFailure)\n ), { concurrency: KernelFileSystem.fallbackConcurrency })\n }\n const values: Array<BatchedDigest | undefined> = []\n for (let offset = 0; offset < paths.length; offset += batch.maxSize) {\n const response = yield* batch.execute(\n paths.slice(offset, offset + batch.maxSize).map((path) => ({\n operation: \"digest\",\n path,\n content\n }))\n ).pipe(Effect.mapError(hostFailure))\n for (const entry of [...response.entries].sort((a, b) => a.index - b.index)) {\n const measured = yield* Effect.fromResult(entry.result).pipe(\n Effect.catchReason(\"PlatformError\", \"NotFound\", () => Effect.succeed(undefined)),\n Effect.mapError(hostFailure)\n )\n if (measured === undefined) values.push(undefined)\n else if (measured.operation !== \"digest\") {\n return yield* Effect.fail(hostFailure(new Error(\"host returned a non-digest batch result\")))\n } else values.push({ ...measured, digest: measured.digest as ArtifactStore.Digest })\n }\n }\n return values\n }\n )\n /**\n * One host call, never two. An `exists` probe ahead of the read asked\n * exactly what the read itself answers \u2014 a path that is not there refuses\n * with `NotFound` \u2014 and every declared read was therefore measured twice.\n * On the confined host that is two CPython forks per path\n * (`@smthrs/platform-node/AtomicFileSystem` spawns one interpreter per\n * operation, ~130 ms each), so the probe doubled the cost of every\n * `prepare`, every post-body change check, and every materialization.\n */\n const measure = (\n path: string,\n host: FileSystem.FileSystem = fs\n ): Effect.Effect<string, UnsupportedBoundary, Crypto.Crypto> =>\n readDigestFrom(host, path).pipe(\n Effect.map((measured) => measured.digest),\n Effect.catchReason(\"PlatformError\", \"NotFound\", () => Effect.succeed(absentDigest)),\n Effect.mapError(hostFailure)\n )\n // Both expansions enumerate through `FileEnumeration`, never the host\n // `glob`: host results are absolute under the kernel FileSystem and skip\n // dotfiles under Node's matcher, so trusting them silently emptied every\n // workspace-relative expansion and let a tree replay delete dotfiles the\n // producer wrote.\n const expandGlob = Effect.fn(\"StepBoundary.expandGlob\")(function*(glob: FileSet.Glob) {\n return yield* FileEnumeration.expandGlob(fs, glob).pipe(Effect.mapError(hostFailure))\n })\n const treeEntries = Effect.fn(\"StepBoundary.treeEntries\")(function*(host: FileSystem.FileSystem, path: string) {\n return yield* FileEnumeration.entriesUnder(host, path).pipe(Effect.mapError(hostFailure))\n })\n // Replay writes and deletes what evidence names, so the lexical check alone\n // is not a boundary: a symlink planted on a lexically valid path would carry\n // the mutation outside the workspace. Every replay mutation, its probes, and\n // its prune walk go through a view pinned to the root, where each call is\n // one descriptor-relative, no-follow request and a symlink anywhere on the\n // path is refused rather than followed. `prepare` and `settle` only read,\n // and keep the caller's filesystem.\n const replayRoot = KernelFileSystem.confinedRoot(fs) ?? options.root ?? \".\"\n const replayHost: Effect.Effect<FileSystem.FileSystem, UnsupportedBoundary> = KernelFileSystem.isConfinable(fs)\n ? KernelFileSystem.confined(fs, replayRoot).pipe(\n Effect.provideService(Path.Path, platformPath),\n Effect.mapError(hostFailure)\n )\n : Effect.fail(\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message:\n \"cache replay needs a confined host filesystem; the host carries no descriptor-relative executor or isolation attestation\"\n })\n )\n const capture = Effect.fn(\"StepBoundary.capture\")(\n function*(path: string, inlineBudget: number, measured: BatchedDigest | undefined) {\n yield* Effect.annotateCurrentSpan({ path })\n // As in `measure`: the read reports absence itself, so the output is\n // captured in one host call rather than an `exists` probe plus a read.\n if (measured === undefined) {\n return { output: { path, digest: null } satisfies MaterializedOutput, inlinedBytes: 0 }\n }\n const bytes = measured.bytes\n if (bytes === undefined) return yield* Effect.fail(hostFailure(new Error(\"host omitted requested batch content\")))\n const digest = measured.digest\n // Inline only while both bounds hold (issue #122): the per-output bound\n // and the settle-wide aggregate budget the caller threads through.\n if (bytes.length <= maxInlineBytes && bytes.length <= inlineBudget) {\n return {\n output: {\n path,\n digest,\n sizeBytes: bytes.length,\n content: Encoding.encodeBase64(bytes)\n } satisfies MaterializedOutput,\n inlinedBytes: bytes.length\n }\n }\n // Over the inline bound: the payload goes to the content-addressed\n // artifact store and the persisted row carries only the reference. Every\n // property that used to live here \u2014 atomic publication, verify-once\n // dedupe, healing rewrite of a corrupt address \u2014 is now the store's, and\n // is tested there (`@smthrs/artifacts`).\n const stored = yield* artifacts.put(bytes).pipe(Effect.mapError(artifactFailure))\n if (stored !== digest) {\n return yield* Effect.fail(\n new BoundaryCorruption({\n code: \"boundary_corruption\",\n path,\n recordedDigest: `${stored}`,\n measuredDigest: digest\n })\n )\n }\n return { output: { path, digest, sizeBytes: bytes.length } satisfies MaterializedOutput, inlinedBytes: 0 }\n }\n )\n const materialize = Effect.fn(\"StepBoundary.materialize\")(function*(\n host: FileSystem.FileSystem,\n output: MaterializedOutput\n ) {\n yield* Effect.annotateCurrentSpan({ path: output.path })\n // The filesystem is the cheapest source of truth for a warm workspace.\n // Probe before decoding inline bytes or consulting the artifact store; a\n // transient probe refusal merely forfeits the optimization and the\n // ordinary materialization path retains its existing typed failures.\n const current = yield* measure(output.path, host).pipe(\n Effect.map((digest) => digest !== absentDigest && digest === output.digest),\n Effect.catch(() => Effect.succeed(false))\n )\n if (current) return\n let bytes: Uint8Array\n if (output.content !== undefined) {\n const decoded = Encoding.decodeBase64(output.content)\n if (Result.isFailure(decoded)) {\n // Inline content is cache-origin bytes: an undecodable payload is a\n // tampered durable row, classified exactly like a digest mismatch so\n // the caller routes it to the Inconsistency receiver instead of\n // retrying it as a transient host refusal (issue #159).\n return yield* Effect.fail(inlineCorruption(output.path, `${output.digest}`))\n }\n bytes = decoded.success\n // Inline evidence is verified here because nothing else can: the bytes\n // travel inside the row rather than at a content address. A referenced\n // artifact is verified by the store on the way out, so the check below\n // would be a second read+hash of the same bytes.\n const measured = yield* Schema.decodeUnknownEffect(Sha256)(bytes).pipe(Effect.orDie)\n if (measured !== output.digest) {\n return yield* Effect.fail(\n new BoundaryCorruption({\n code: \"boundary_corruption\",\n path: output.path,\n recordedDigest: `${output.digest}`,\n measuredDigest: measured\n })\n )\n }\n } else {\n bytes = yield* artifacts.get(`${output.digest}`).pipe(\n Effect.catchTags({\n // A reference this host cannot resolve is not a host refusal: a\n // shared artifact tier may still hold it, so the caller gets a\n // typed, repairable failure and fetches before falling back to a\n // real execution (issue #172).\n \"@smthrs/artifacts/ArtifactMissing\": (missing) =>\n Effect.fail(\n new MissingArtifact({ code: \"missing_artifact\", path: output.path, digest: missing.digest })\n ),\n // Corruption is a distinct typed failure from a transient host\n // error (issue #150): the caller routes it to the Inconsistency\n // receiver instead of treating it as an ordinary retryable refusal.\n \"@smthrs/artifacts/ArtifactCorruption\": (corrupt) =>\n Effect.fail(\n new BoundaryCorruption({\n code: \"boundary_corruption\",\n path: output.path,\n recordedDigest: corrupt.recordedDigest,\n measuredDigest: corrupt.measuredDigest\n })\n ),\n \"@smthrs/artifacts/ArtifactStoreError\": (failure) => Effect.fail(artifactFailure(failure))\n })\n )\n }\n // The original body may have created the output's directory itself; a\n // fresh workspace replaying the evidence has no such directory and\n // `writeFile` does not create parents (issue #107).\n const parent = parentDirectory(output.path)\n if (parent !== undefined) {\n yield* host.makeDirectory(parent, { recursive: true }).pipe(Effect.mapError(hostFailure))\n }\n yield* host.writeFile(output.path, bytes).pipe(Effect.mapError(hostFailure))\n })\n return make({\n prepare: Effect.fn(\"StepBoundary.prepare\")(function*(descriptor) {\n const boundary = FileBoundarySnapshot.make(descriptor)\n yield* Effect.annotateCurrentSpan({\n reads: boundary.readSet.length,\n boundaryMode: boundary.boundaryMode\n })\n // The dirty check's evidence (issue #90): what the host actually\n // measured for every declared read, never the declaration itself \u2014\n // defaulting the snapshot to the declaration made `readSetMatches`\n // compare the declaration against itself and pass unconditionally\n // (issue #104).\n const readSnapshot: Array<FileInput> = []\n const paths: Array<string> = []\n for (const entry of boundary.readSet) {\n if (FileSet.isGlob(entry)) {\n paths.push(...yield* expandGlob(entry))\n } else paths.push(entry.path)\n }\n const measured = yield* measurements(paths, false)\n for (const [index, path] of paths.entries()) {\n readSnapshot.push({ path, digest: measured[index]?.digest ?? absentDigest })\n }\n readSnapshot.sort((left, right) => compareText(left.path, right.path))\n return Object.freeze({\n descriptor: boundary,\n readSnapshot: Object.freeze(readSnapshot.map((entry) => Object.freeze({ ...entry })))\n })\n }),\n settle: Effect.fn(\"StepBoundary.settle\")(function*(prepared) {\n yield* Effect.annotateCurrentSpan({\n writes: prepared.descriptor.writeSet.length,\n boundaryMode: prepared.descriptor.boundaryMode\n })\n // Undeclared-write detection is scoped to the declared read set: a\n // declared read whose content moved during the body and is not also a\n // declared write was mutated outside the contract. Whole-tree change\n // detection needs the jj diff surface and stays a documented\n // limitation of this layer rather than a silent pass \u2014 the read set\n // is exactly the material cacheability rests on.\n const removes = prepared.descriptor.removes ?? []\n const declaredWrites = [...prepared.descriptor.writeSet, ...removes]\n const declaredCovers = (path: string) =>\n declaredWrites.some((entry) =>\n typeof entry === \"string\"\n ? entry === path\n : entry._tag === \"TreeArtifact\"\n ? path === entry.path || path.startsWith(`${entry.path}/`)\n : FileSet.matchesGlob(entry, path)\n )\n const undeclared: Array<string> = []\n const checkedReads = prepared.readSnapshot.filter((entry) => !declaredCovers(entry.path))\n const currentReads = yield* measurements(checkedReads.map((entry) => entry.path), false)\n for (const [index, entry] of checkedReads.entries()) {\n if ((currentReads[index]?.digest ?? absentDigest) !== entry.digest) undeclared.push(entry.path)\n }\n const outputs: Array<MaterializedOutput> = []\n // Declared removals are captured on the same path and under the same\n // `digest: null` semantics they have always had \u2014 the difference is that\n // here the absence was declared, so it is evidence rather than a defect.\n const missing: Array<string> = []\n const surviving: Array<string> = []\n let inlinedBytes = 0\n const outputPaths: Array<string> = []\n const treeMembers: Array<{ readonly path: string; readonly files: ReadonlyArray<string> }> = []\n for (const entry of prepared.descriptor.writeSet) {\n if (typeof entry === \"string\") outputPaths.push(entry)\n else if (entry._tag === \"Glob\") outputPaths.push(...yield* expandGlob(entry))\n else {\n const files = (yield* treeEntries(fs, entry.path)).files\n outputPaths.push(...files)\n treeMembers.push({ path: entry.path, files })\n }\n }\n outputPaths.push(...removes)\n const captured = new Map<string, MaterializedOutput>()\n const sortedPaths = [...new Set(outputPaths)].sort(compareText)\n // Digest-only preflight bounds content batches by bytes as well as path\n // count. Both measurements share the guarded host's pinned root identity;\n // a rewrite between them is a refusal, never mismatched cached content.\n const sizes = batch === undefined ? undefined : yield* measurements(sortedPaths, false)\n for (let offset = 0; offset < sortedPaths.length;) {\n let end = offset\n let responseBytes = 256\n const size = batch?.maxSize ?? KernelFileSystem.fallbackConcurrency\n while (end < sortedPaths.length && end - offset < size) {\n const estimate = 4096 + new TextEncoder().encode(sortedPaths[end]).length * 6 +\n Math.ceil((sizes?.[end]?.sizeBytes ?? 0) / 3) * 4\n if (end > offset && batch !== undefined && responseBytes + estimate > batch.maxResponseBytes) break\n responseBytes += estimate\n end++\n }\n const group = sortedPaths.slice(offset, end)\n const contents = yield* measurements(group, true)\n for (const [index, path] of group.entries()) {\n const measured = contents[index]\n if (sizes !== undefined && measured?.digest !== sizes[offset + index]?.digest) {\n return yield* Effect.fail(hostFailure(new Error(`output changed during measurement: ${path}`)))\n }\n const output = yield* capture(path, maxTotalInlineBytes - inlinedBytes, measured)\n // `capture` reports the bytes it actually inlined (zero for a\n // digest-only reference), so the aggregate budget never has to\n // re-derive them from the row it just built.\n inlinedBytes += output.inlinedBytes\n outputs.push(output.output)\n captured.set(path, output.output)\n if (\n output.output.digest === null &&\n !removes.includes(path) &&\n prepared.descriptor.writeSet.some((entry) => typeof entry === \"string\" && entry === path)\n ) missing.push(path)\n // The dual check: a removal promised the path absent, and it is still\n // here \u2014 possibly rewritten. Settling it would cache the surviving\n // bytes under a declaration that disclaimed them.\n if (output.output.digest !== null && removes.includes(path)) surviving.push(path)\n }\n offset = end\n }\n // A tree member is already one of the captured outputs, so its identity\n // is folded from the digest that capture measured rather than from a\n // second read of the same bytes. `capture` spells an absent member\n // `null` where `measure` spells it `absentDigest`; the identity keeps\n // the `measure` spelling, so a tree recorded before this and one\n // recorded after hash identically.\n const trees: Array<{ readonly path: string; readonly identity: string }> = []\n for (const tree of treeMembers) {\n const pairs = tree.files.map((path) =>\n [path.slice(tree.path.length + 1), captured.get(path)!.digest ?? absentDigest] as const\n )\n trees.push({\n path: tree.path,\n identity: yield* Schema.decodeUnknownEffect(DerivedKey)({ kind: \"tree-artifact\", files: pairs }).pipe(\n Effect.orDie\n )\n })\n }\n // Through `DerivedKey` \u2014 the repo's one hashing chokepoint \u2014 so the identity is\n // a digest of the RFC 8785 canonical form rather than of whatever\n // `JSON.stringify` happened to emit for this shape.\n const diffIdentity = yield* Schema.decodeUnknownEffect(DerivedKey)({\n kind: \"diff-identity\",\n outputs: outputs.map((output) => [output.path, output.digest]),\n trees\n }).pipe(Effect.orDie)\n yield* Effect.annotateCurrentSpan({ diffIdentity })\n if (prepared.descriptor.boundaryMode === \"hard\") {\n if (undeclared.length > 0) {\n return yield* Effect.fail(\n new UndeclaredWrite({ code: \"undeclared_write\", paths: undeclared, diffIdentity })\n )\n }\n // A declared output that was never produced is a defect, not evidence\n // (Bazel's `checkOutputs`). Recording it would cache `digest: null`,\n // which every later `replayOutputs` reads as \"delete this path\".\n if (missing.length > 0) {\n return yield* Effect.fail(\n new MissingDeclaredOutput({ code: \"missing_declared_output\", paths: missing, diffIdentity })\n )\n }\n if (surviving.length > 0) {\n return yield* Effect.fail(\n new SurvivingDeclaredRemoval({ code: \"surviving_declared_removal\", paths: surviving, diffIdentity })\n )\n }\n }\n return {\n declaredOutputs: { outputs, ...(trees.length === 0 ? {} : { trees }) },\n diffIdentity,\n // This filesystem-only boundary cannot observe writes elsewhere in\n // the tree. Omission is deliberate: ActionPersistence treats the\n // result as run-local and will not publish it to the shared cache.\n //\n // An undeclared write is reported ahead of a missing output, and a\n // missing output ahead of a surviving removal: each is the stronger\n // claim about the same execution, and every variant bars the evidence\n // from the shared cache identically.\n ...(undeclared.length > 0\n ? { deviation: { _tag: \"ExpectedSetDeviation\" as const, paths: undeclared, diffIdentity } }\n : missing.length > 0\n ? { deviation: { _tag: \"MissingDeclaredOutput\" as const, paths: missing, diffIdentity } }\n : surviving.length > 0\n ? { deviation: { _tag: \"SurvivingDeclaredRemoval\" as const, paths: surviving, diffIdentity } }\n : {})\n }\n }),\n replayOutputs: Effect.fn(\"StepBoundary.replayOutputs\")(function*(evidence) {\n yield* Effect.annotateCurrentSpan({ diffIdentity: evidence.diffIdentity })\n const decoded = Schema.decodeUnknownResult(MaterializedOutputs)(evidence.declaredOutputs)\n if (decoded._tag === \"Failure\") {\n // Evidence recorded by a different boundary implementation carries\n // no materializable outputs; refusing is honest \u2014 the caller's\n // dispatch path falls back to a real execution or fails visibly.\n return yield* Effect.fail(\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message: \"the recorded boundary evidence carries no materializable outputs\"\n })\n )\n }\n // Replay writes and DELETES whatever the evidence names, so a path is\n // honored only in the coordinate system declarations are written in.\n // Evidence can arrive from a foreign producer through cache sync; an\n // absolute or upward spelling is an eraser aimed outside the workspace,\n // and refusing it here is what keeps that a refusal instead of a wipe.\n const foreign = [\n ...(decoded.success.trees ?? []).map((tree) => tree.path),\n ...decoded.success.outputs.map((output) => output.path)\n ].filter((path) => !FileSet.workspaceRelative(path))\n if (foreign.length > 0) {\n return yield* Effect.fail(\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message: `the recorded boundary evidence names paths outside the workspace: ${foreign.join(\", \")}`\n })\n )\n }\n // Before any mutation: a host that cannot be confined refuses the whole\n // replay, and the caller falls back to a real execution.\n const host = yield* replayHost\n const emptyDirectoryCandidates = new Set<string>()\n for (const tree of decoded.success.trees ?? []) {\n const prefix = `${tree.path}/`.replace(/\\/{2,}$/g, \"/\")\n const recorded = new Set(\n decoded.success.outputs\n .map((output) => output.path)\n .filter((path) => path.startsWith(prefix))\n )\n const entries = yield* treeEntries(host, tree.path)\n for (const path of entries.files) {\n if (!recorded.has(path)) yield* host.remove(path).pipe(Effect.mapError(hostFailure))\n }\n for (const directory of entries.directories) emptyDirectoryCandidates.add(directory)\n }\n for (const output of decoded.success.outputs) {\n if (output.digest === null) {\n // `force` IS the probe: the evidence says this path must not exist,\n // and a removal that finds it already gone has done its job. The\n // `exists` call that used to precede it was a second host call \u2014 a\n // second process on the confined host \u2014 for the same answer.\n yield* host.remove(output.path, { force: true }).pipe(Effect.mapError(hostFailure))\n } else {\n yield* materialize(host, output)\n }\n }\n // Remove only directories proven empty after pruning and\n // materialization. Deepest-first preserves every directory that still\n // contains a recorded child while clearing stale empty scaffolding.\n const directories = [...emptyDirectoryCandidates].sort((left, right) =>\n right.length - left.length || compareText(left, right)\n )\n for (const directory of directories) {\n const entries = yield* host.readDirectory(directory).pipe(Effect.mapError(hostFailure))\n if (entries.length === 0) yield* host.remove(directory).pipe(Effect.mapError(hostFailure))\n }\n })\n })\n}\n\n/**\n * Provides the filesystem-backed production boundary: `prepare` measures the\n * declared read set for real, `settle` detects reads mutated outside the\n * declared write set and captures the write set's post-state, and\n * `replayOutputs` re-materializes those outputs on cache-hit replay.\n *\n * Host access arrives through Effect's `FileSystem` tag, which the capability\n * kernel decorates in place \u2014 the same seam every host implementation (node,\n * bun, browser, sandbox) already provides. Blob storage arrives through\n * `@smthrs/artifacts`, so the same boundary runs over a purely local store or\n * over a local-plus-shared composition without knowing which it got.\n *\n * Replay confines its own mutations rather than trusting the composition:\n * they run through `@smthrs/kernel/FileSystem`'s `confined` view, rooted at\n * the kernel `Workspace` when one is in context. A symlink on a replayed path\n * (a parent, the final component, or a member of a replayed tree) and a host\n * with no descriptor-relative executor or isolation attestation both refuse\n * with {@link UnsupportedBoundary}, which the caller journals as\n * `replay_failed` before executing the step for real.\n *\n * @since 0.1.0\n * @category layers\n */\nexport const layer: Layer.Layer<\n Service,\n never,\n FileSystem.FileSystem | Path.Path | ArtifactStore.ArtifactStore\n> = Layer.effect(\n StepBoundary,\n Effect.gen(function*() {\n const fs = yield* FileSystem.FileSystem\n const platformPath = yield* Path.Path\n const artifacts = yield* ArtifactStore.ArtifactStore\n const workspace = yield* Effect.serviceOption(KernelWorkspace)\n return makeFileSystem(fs, platformPath, artifacts, workspace._tag === \"Some\" ? { root: workspace.value.root } : {})\n })\n)\n\n/**\n * What the deterministic test boundary should pretend the host observed.\n *\n * Every field is a fixture, not a classification rule: a test supplies settlement\n * evidence or failure, the read snapshot, and whether the host claims\n * to support the boundary at all \u2014 then asserts on the resulting evidence or\n * refusal. Defaults describe a well-behaved, fully-supported host.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface TestOptions {\n /** A settlement failure supplied verbatim by an integration fixture. */\n readonly failure?: UndeclaredWrite | MissingDeclaredOutput | SurvivingDeclaredRemoval | undefined\n /** Settlement evidence supplied verbatim; classification belongs to the real layer. */\n readonly deviation?: BoundaryDeviation | undefined\n /**\n * What `prepare` reports as measured for the declared read set. Defaults\n * to the declaration itself; a test supplies a different snapshot to stand\n * for a file whose content moved out from under a stale declaration\n * (issue #90).\n */\n readonly readSnapshot?: ReadonlyArray<FileInput> | undefined\n /** The opaque replay payload recorded in the evidence. */\n readonly declaredOutputs?: unknown\n /** The post-state identity the evidence names. Defaults to a fixed string. */\n readonly diffIdentity?: string | undefined\n /** When false, every call fails with {@link UnsupportedBoundary}. Defaults to true. */\n readonly supported?: boolean | undefined\n /** Whether the fixture attests a whole-tree observation. Defaults to true. */\n readonly wholeTreeWriteDetection?: boolean | undefined\n /** Whether the fixture models an isolated read surface. Defaults to true. */\n readonly hermeticReadDetection?: boolean | undefined\n /** Observes each `replayOutputs` call, so a test can assert replay happened. */\n readonly onReplay?: (evidence: BoundaryEvidence) => void\n}\n\nconst unsupported = (): UnsupportedBoundary =>\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message: \"the host cannot enforce the declared step boundary\"\n })\n\n/**\n * Deterministic in-memory boundary suitable only for tests. Production\n * wiring uses {@link layer}, the filesystem-backed implementation.\n *\n * Whole-tree change detection is supplied by sandboxed settlement, not by any\n * boundary layer: an isolated execution's transaction IS the tree, so\n * `ActionPersistence` compares its diff against the declared sets and sets\n * `wholeTreeWritesVerified` structurally (the retired piece-6 limitation).\n * {@link layer} still detects mutations within the declared read set only,\n * which is why unsandboxed evidence stays run-local.\n *\n * @since 0.1.0\n * @category layers\n */\nexport const layerTest = (options: TestOptions = {}): Layer.Layer<Service> => {\n const diffIdentity = options.diffIdentity ?? \"test-diff\"\n const service = make({\n prepare: Effect.fn(\"StepBoundary.prepare\")(function*(descriptor) {\n if (options.supported === false) return yield* Effect.fail(unsupported())\n return {\n descriptor,\n readSnapshot: options.readSnapshot ?? exactReads(descriptor)\n }\n }),\n settle: Effect.fn(\"StepBoundary.settle\")(function*(prepared) {\n if (options.supported === false) return yield* Effect.fail(unsupported())\n if (options.failure !== undefined) return yield* Effect.fail(options.failure)\n return {\n declaredOutputs: options.declaredOutputs ?? { paths: prepared.descriptor.writeSet },\n diffIdentity,\n ...(options.wholeTreeWriteDetection === false ? {} : { wholeTreeWritesVerified: true as const }),\n ...(options.hermeticReadDetection === false ? {} : { hermeticReadsVerified: true as const }),\n ...(options.deviation === undefined ? {} : { deviation: options.deviation })\n }\n }),\n replayOutputs: Effect.fn(\"StepBoundary.replayOutputs\")(function*(evidence) {\n if (options.supported === false) return yield* Effect.fail(unsupported())\n yield* Effect.sync(() => {\n options.onReplay?.(evidence)\n })\n })\n })\n return Layer.succeed(StepBoundary, service)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Hermetic step boundary contracts, the filesystem-backed production\n * implementation, and the deterministic test implementation.\n *\n * @since 0.1.0\n */\n\nimport * as ArtifactStore from \"@smthrs/artifacts/ArtifactStore\"\nimport { Sha256 } from \"@smthrs/crypto\"\nimport { FileBoundary } from \"@smthrs/flow/FileBoundary\"\nimport { FileInput } from \"@smthrs/flow/FileInput\"\nimport * as KernelFileSystem from \"@smthrs/kernel/FileSystem\"\nimport { Workspace as KernelWorkspace } from \"@smthrs/kernel/Workspace\"\nimport { DerivedKey } from \"@smthrs/keys\"\nimport * as FileSet from \"@smthrs/plan/FileSet\"\nimport * as Context from \"effect/Context\"\nimport type * as Crypto from \"effect/Crypto\"\nimport * as Effect from \"effect/Effect\"\nimport * as Base64 from \"effect/encoding/Base64\"\nimport * as FileSystem from \"effect/FileSystem\"\nimport * as Layer from \"effect/Layer\"\nimport * as Path from \"effect/Path\"\nimport * as Result from \"effect/Result\"\nimport * as Schema from \"effect/Schema\"\nimport * as FileBoundarySnapshot from \"./internal/FileBoundarySnapshot.ts\"\nimport * as FileEnumeration from \"./internal/FileEnumeration.ts\"\nimport { compareText } from \"./internal/Ordering.ts\"\n\n/**\n * A file boundary that has been measured but not yet run: the caller's\n * declaration, paired with what the host actually found on disk for it.\n *\n * `prepare` produces one of these before a step executes and `settle` consumes\n * it afterwards, which is what lets the boundary say whether the world moved\n * underneath the declaration. Check it with {@link readSetMatches}.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const PreparedBoundary = Schema.Struct({\n descriptor: FileBoundary,\n /**\n * What the host actually measured for the declared read set at prepare\n * time. The declared digests are caller metadata folded into the step key;\n * this is the evidence that they still describe reality, and a sealed\n * cache hit is refused when the two disagree (issue #90).\n */\n readSnapshot: Schema.Array(FileInput)\n})\n\n/**\n * The value form of {@link PreparedBoundary}.\n *\n * @since 0.1.0\n * @category models\n */\nexport type PreparedBoundary = typeof PreparedBoundary.Type\n\n/**\n * Exact read inputs from a boundary after ignoring declarations that still need expansion.\n *\n * @category accessors\n * @since 0.1.0\n */\nexport const exactReads = (descriptor: FileBoundary): ReadonlyArray<FileInput> =>\n descriptor.readSet.filter((entry): entry is FileInput => !FileSet.isGlob(entry))\n\n/**\n * Whether every declared read still matches what the host measured.\n *\n * Reads the host reports but the declaration never claimed are ignored: the\n * declaration is the dependency edge set, and an incidental extra read is not\n * one of its edges. A declared path missing from the measurement counts as a\n * mismatch \u2014 its value is unknown, so reuse cannot be justified.\n *\n * @since 0.1.0\n * @category predicates\n */\nexport const readSetMatches = (prepared: PreparedBoundary): boolean => {\n const digestsByPath = new Map<string, Set<string>>()\n for (const measured of prepared.readSnapshot) {\n const digests = digestsByPath.get(measured.path)\n if (digests === undefined) digestsByPath.set(measured.path, new Set([measured.digest]))\n else digests.add(measured.digest)\n }\n return prepared.descriptor.readSet.every((entry) =>\n !FileSet.isGlob(entry) &&\n digestsByPath.get(entry.path)?.has(entry.digest) === true\n )\n}\n\n/**\n * The paths a step wrote that its `expected` write set did not name, together\n * with the diff identity they were observed under.\n *\n * An expected-mode boundary reports these rather than failing: the declaration\n * is a prediction, and a wrong prediction is evidence to journal, not an\n * error. A hard-mode boundary raises {@link UndeclaredWrite} for the same\n * observation.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const ExpectedSetDeviation = Schema.TaggedStruct(\"ExpectedSetDeviation\", {\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n})\n\n/**\n * Declared writes an expected-mode step did not produce, and did not declare\n * as removals.\n *\n * The absence itself is the defect: recording it as valid evidence caches the\n * claim \"this file should not exist\", which `replayOutputs` then acts on by\n * deleting the path on a workspace that never ran the step. A hard-mode\n * boundary raises {@link MissingDeclaredOutput} for the same observation.\n *\n * Expected mode records it rather than failing because the declaration there is\n * a prediction \u2014 and because a deviation of any variant already bars the\n * evidence from the shared cache (`ActionPersistence` gates `recordCache` on\n * `deviation === undefined`), so an unexplained absence never reaches another\n * host either way.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const MissingOutputDeviation = Schema.TaggedStruct(\"MissingDeclaredOutput\", {\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n})\n\n/**\n * Declared removals an expected-mode step left in place.\n *\n * The dual of {@link MissingOutputDeviation}: a removal is a promise about the\n * post-state exactly as a write is. A path that was promised absent but is\n * still present \u2014 possibly mutated \u2014 must not settle as valid evidence, or\n * the mutation is cached under a declaration that disclaimed it and replay\n * materializes it everywhere. A hard-mode boundary raises\n * {@link SurvivingDeclaredRemoval} for the same observation.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const SurvivingRemovalDeviation = Schema.TaggedStruct(\"SurvivingDeclaredRemoval\", {\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n})\n\n/**\n * What a settled boundary observed that its declaration did not predict.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const BoundaryDeviation = Schema.Union([ExpectedSetDeviation, MissingOutputDeviation, SurvivingRemovalDeviation])\n\n/**\n * The value form of {@link BoundaryDeviation}.\n *\n * @since 0.1.0\n * @category models\n */\nexport type BoundaryDeviation = typeof BoundaryDeviation.Type\n\n/**\n * What a settled boundary proved about a step's writes \u2014 the record the\n * journal keeps and a later cache hit is justified against.\n *\n * `diffIdentity` names the post-state the step produced; `declaredOutputs`\n * carries whatever the implementation needs to reproduce it on a workspace\n * that never ran the step. The two optional fields are the honest gaps:\n * `wholeTreeWritesVerified` is present only when the boundary could observe\n * the entire execution tree, and `deviation` only when it saw writes the\n * declaration did not predict.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const BoundaryEvidence = Schema.Struct({\n declaredOutputs: Schema.Unknown,\n diffIdentity: Schema.NonEmptyString,\n /**\n * The boundary observed the whole execution tree and proved that every\n * write was covered by the descriptor's write set. Only evidence carrying\n * this explicit proof may enter the cross-run cache. Older evidence and\n * boundaries that can inspect declared paths only omit it.\n */\n wholeTreeWritesVerified: Schema.optional(Schema.Literal(true)),\n /** The body could observe only declared filesystem inputs. */\n hermeticReadsVerified: Schema.optional(Schema.Literal(true)),\n deviation: Schema.optional(BoundaryDeviation)\n})\n\n/**\n * The value form of {@link BoundaryEvidence}.\n *\n * @since 0.1.0\n * @category models\n */\nexport type BoundaryEvidence = typeof BoundaryEvidence.Type\n\n/**\n * A hard-mode step wrote outside its declared write set.\n *\n * Hard mode treats the declaration as a contract, so this is a refusal, not a\n * report: the step's evidence is never journaled and never becomes a cache\n * entry. An expected-mode boundary records the same observation as a\n * {@link BoundaryDeviation} instead.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class UndeclaredWrite extends Schema.TaggedError<UndeclaredWrite>()(\n \"@smthrs/engine-store/UndeclaredWrite\",\n {\n code: Schema.Literal(\"undeclared_write\"),\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n }\n) {}\n\n/**\n * A step finished without producing a path its write set declared, and did not\n * declare that path as a removal.\n *\n * Bazel hard-fails the same observation \u2014 `SkyframeActionExecutor.checkOutputs`\n * reports \"output was not created\" \u2014 and for the same reason: the alternative\n * is to record `digest: null` as valid evidence, which caches the claim \"this\n * file should not exist\" and makes every later replay delete the path. A step\n * that crashed after declaring its writes would poison the cache with an\n * eraser.\n *\n * Declaring the path in {@link FileBoundary}'s `removes` is how a deliberate\n * deletion says so, and it is the only thing that makes the absence legitimate.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class MissingDeclaredOutput extends Schema.TaggedError<MissingDeclaredOutput>()(\n \"@smthrs/engine-store/MissingDeclaredOutput\",\n {\n code: Schema.Literal(\"missing_declared_output\"),\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n }\n) {}\n\n/**\n * A hard-mode step left a declared removal in place.\n *\n * The dual of {@link MissingDeclaredOutput}: `removes` promises the path is\n * absent afterwards, and a path that survived \u2014 or was quietly rewritten \u2014\n * is a post-state the declaration disclaimed. Settling it as evidence would\n * cache the surviving bytes under a removal and hand them to every replay.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class SurvivingDeclaredRemoval extends Schema.TaggedError<SurvivingDeclaredRemoval>()(\n \"@smthrs/engine-store/SurvivingDeclaredRemoval\",\n {\n code: Schema.Literal(\"surviving_declared_removal\"),\n paths: Schema.Array(Schema.String),\n diffIdentity: Schema.NonEmptyString\n }\n) {}\n\n/**\n * The host could not honour the boundary at all \u2014 a filesystem that cannot be\n * measured, a path that cannot be read, a transient I/O failure.\n *\n * It is the catch-all host refusal, deliberately kept distinct from the two\n * refusals a caller can act on: {@link BoundaryCorruption} (the bytes changed\n * under a recorded digest) and {@link MissingArtifact} (the bytes might be\n * fetchable from a shared tier).\n *\n * @since 0.1.0\n * @category errors\n */\nexport class UnsupportedBoundary extends Schema.TaggedError<UnsupportedBoundary>()(\n \"@smthrs/engine-store/UnsupportedBoundary\",\n {\n code: Schema.Literal(\"unsupported_boundary\"),\n message: Schema.String,\n /**\n * The refusing host or artifact-store failure, carried whole rather than\n * flattened into the message (`PlatformError`'s own convention), so the\n * journal record and a live debugger both see the original error.\n */\n cause: Schema.optional(Schema.Defect())\n }\n) {}\n\n/**\n * Recorded boundary evidence whose bytes no longer match their recorded\n * digest \u2014 an integrity violation of the store's strongest invariant, as\n * opposed to a transient host failure (issue #150). The two classes were\n * previously conflated under one `UnsupportedBoundary` tag, so a failing\n * disk corrupting many blobs journalled identically to a one-off EIO.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class BoundaryCorruption extends Schema.TaggedError<BoundaryCorruption>()(\n \"@smthrs/engine-store/BoundaryCorruption\",\n {\n code: Schema.Literal(\"boundary_corruption\"),\n path: Schema.String,\n recordedDigest: Schema.String,\n measuredDigest: Schema.String\n }\n) {}\n\n/**\n * Recorded evidence references an artifact this host's store does not hold\n * (issue #172). Distinct from {@link UnsupportedBoundary} because it is the one\n * replay refusal a *shared artifact tier* can repair: the caller fetches the\n * blob from the remote store, writes it back locally, and retries the replay\n * once before falling through to a real execution. Every other host refusal\n * says nothing about where the bytes might be.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class MissingArtifact extends Schema.TaggedError<MissingArtifact>()(\n \"@smthrs/engine-store/MissingArtifact\",\n {\n code: Schema.Literal(\"missing_artifact\"),\n path: Schema.String,\n digest: Schema.String\n }\n) {}\n\n/**\n * The three-call lifecycle of a hermetic step boundary.\n *\n * `prepare` measures the declared read set before the step runs; `settle`\n * measures the writes afterwards and turns them into {@link BoundaryEvidence};\n * `replayOutputs` reproduces a previous step's outputs from that evidence, so\n * a cache hit can materialize results on a workspace that never executed\n * anything.\n *\n * @since 0.1.0\n * @category services\n */\nexport interface Service {\n readonly prepare: (\n descriptor: FileBoundary\n ) => Effect.Effect<PreparedBoundary, UnsupportedBoundary, Crypto.Crypto>\n readonly settle: (\n prepared: PreparedBoundary\n ) => Effect.Effect<\n BoundaryEvidence,\n UndeclaredWrite | MissingDeclaredOutput | SurvivingDeclaredRemoval | UnsupportedBoundary | BoundaryCorruption,\n Crypto.Crypto\n >\n readonly replayOutputs: (\n evidence: BoundaryEvidence\n ) => Effect.Effect<void, UnsupportedBoundary | BoundaryCorruption | MissingArtifact, Crypto.Crypto>\n}\n\n/**\n * The service key for a {@link Service}. Provide it with `layer` for\n * a real workspace or `layerTest` for a deterministic in-memory one.\n *\n * @since 0.1.0\n * @category services\n */\nexport const StepBoundary: Context.Service<Service, Service> = Context.Service<Service>(\n \"@smthrs/engine-store/StepBoundary\"\n)\n\n/**\n * Brands a {@link Service} implementation as a `StepBoundary`, so a new\n * boundary backend is type-checked where it is written rather than where it is\n * provided.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const make = (service: Service): Service => StepBoundary.of(service)\n\n/**\n * The digest a measurement reports for a declared path that does not exist.\n * Declared digests are hex SHA-256 strings, so the sentinel can never\n * collide with a real measurement \u2014 a declaration naming a vanished file\n * always mismatches and the cache hit is refused.\n */\nconst absentDigest = \"absent\"\n\n/**\n * The persisted shape of the production layer's `declaredOutputs`: the\n * declared write set's materialized post-state, so a cache-hit replay can\n * reproduce the outputs on a workspace that never ran the step.\n *\n * Outputs are recorded by content digest, never inlined unbounded\n * (issue #113): a `null` digest records that the path did not exist at\n * settle time; `content` (base64) is present only when the payload fits the\n * configured inline bound, otherwise the bytes live in the host's\n * content-addressed object directory under the digest and the row carries\n * only the reference. Temporal's blob-size limits are the prior art \u2014 the\n * evidence is persisted into every attempt row and cache entry.\n *\n * The digest is decoded against the store's strict address schema\n * (`ArtifactStore.Digest`), so a persisted row carrying a malformed address\n * fails the decode here instead of reaching the store or the sync RPCs.\n */\nconst DigestReferencedOutput = Schema.Struct({\n path: Schema.String,\n digest: Schema.NullOr(ArtifactStore.Digest),\n sizeBytes: Schema.optional(Schema.Number),\n content: Schema.optional(Schema.String)\n})\n\nconst MaterializedOutputs = Schema.Struct({\n outputs: Schema.Array(DigestReferencedOutput),\n trees: Schema.optional(Schema.Array(Schema.Struct({\n path: Schema.String,\n identity: Schema.String\n })))\n})\n\n/**\n * The directory portion of a slash-separated path, or `undefined` for a\n * bare filename (nothing to create). Boundary paths are workspace-relative\n * and slash-separated \u2014 the same normalization the kernel `FileSystem`\n * layer applies.\n */\nconst parentDirectory = (path: string): string | undefined => {\n const index = path.lastIndexOf(\"/\")\n return index <= 0 ? undefined : path.slice(0, index)\n}\n\nconst hostFailure = (cause: unknown): UnsupportedBoundary =>\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message: \"the host filesystem could not enforce the step boundary\",\n cause\n })\n\n/**\n * An artifact store that refused the operation outright \u2014 a failing disk, an\n * unreachable shared tier, an address that is not a content address. Unlike a\n * miss or a corruption it says nothing about the artifact, so it stays the\n * ordinary retryable host refusal the issue-#107 call sites already handle.\n */\nconst artifactFailure = (cause: ArtifactStore.ArtifactStoreError): UnsupportedBoundary =>\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message: `the artifact store could not serve the step boundary: ${cause.message}`,\n cause\n })\n\n/**\n * Corruption of an *inline* evidence payload (issue #159): the recorded\n * base64 no longer decodes, so there are no bytes to measure a digest of \u2014\n * the `invalid_base64` sentinel records that the measurement itself was\n * impossible, which is still cache-origin corruption and never a transient\n * host refusal.\n */\nconst inlineCorruption = (path: string, recordedDigest: string): BoundaryCorruption =>\n new BoundaryCorruption({\n code: \"boundary_corruption\",\n path,\n recordedDigest,\n measuredDigest: \"invalid_base64\"\n })\n\n/**\n * The two size bounds that decide when a settled output is inlined into\n * boundary evidence and when it is spilled to the `ArtifactStore` by digest.\n *\n * Both default to values that keep an evidence row small enough to journal;\n * raise them only when the workspace's outputs are known to be small and the\n * extra artifact round-trip is measurably costing something.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface FileSystemOptions {\n /**\n * The largest output (in bytes) inlined into the boundary evidence\n * itself. Anything larger is handed to the `ArtifactStore` and recorded by\n * digest reference only (issue #113). Defaults to 1 MiB.\n */\n readonly maxInlineBytes?: number | undefined\n /**\n * The largest *aggregate* inline payload (in bytes) a single settle may\n * fold into its boundary evidence (issue #122). A per-output bound alone\n * still let a wide write set multiply many individually-small payloads\n * into an unbounded evidence row; once an output would push the running\n * total past this bound it is spilled to the `ArtifactStore` and\n * recorded by digest reference only, even though it individually fits\n * {@link maxInlineBytes}. Defaults to 8 MiB.\n */\n readonly maxTotalInlineBytes?: number | undefined\n /**\n * The workspace root cache replay confines its writes, removals, and prune\n * walk to. A filesystem already confined to a root (the kernel-guarded\n * layer's service, or a `confined` view) keeps its own root and ignores\n * this. Otherwise it defaults to the process working directory, the root a\n * path-based host resolves `prepare` and `settle` against. {@link layer}\n * passes the kernel `Workspace` root when one is in context.\n */\n readonly root?: string | undefined\n}\n\nconst defaultMaxInlineBytes = 1024 * 1024\nconst defaultMaxTotalInlineBytes = 8 * 1024 * 1024\ninterface MeasuredDigest {\n readonly digest: ArtifactStore.Digest\n readonly bytes: Uint8Array\n}\n\ninterface BatchedDigest {\n readonly digest: ArtifactStore.Digest\n readonly sizeBytes: number\n readonly bytes?: Uint8Array\n}\n\ntype MaterializedOutput = typeof DigestReferencedOutput.Type\n\n/**\n * The digests this evidence references rather than inlines \u2014 the set that must\n * be durable in a shared artifact tier before the evidence's cache entry\n * becomes observable there, and the set a replay on a fresh host has to fetch.\n *\n * Evidence recorded by a foreign boundary implementation references nothing\n * this store can name, so it yields the empty list rather than failing: the\n * caller's replay path already refuses such evidence with\n * {@link UnsupportedBoundary}.\n *\n * @since 0.1.0\n * @category accessors\n */\nexport const referencedDigests = (evidence: BoundaryEvidence): ReadonlyArray<ArtifactStore.Digest> => {\n const decoded = Schema.decodeUnknownResult(MaterializedOutputs)(evidence.declaredOutputs)\n if (decoded._tag === \"Failure\") return []\n const digests: Array<ArtifactStore.Digest> = []\n for (const output of decoded.success.outputs) {\n if (output.digest === null || output.content !== undefined) continue\n digests.push(output.digest)\n }\n return digests\n}\n\n/**\n * Builds the filesystem-backed boundary service.\n *\n * The blob mechanics \u2014 content addressing, atomic publication, digest\n * verification, dedupe \u2014 belong to `artifacts` and were extracted into\n * `@smthrs/artifacts` (`packages/smithers/flows/artifacts/docs/api.md`). What stays here\n * is the *policy* that decides which outputs become blobs at all: the\n * inline-versus-spill budgets are a property of how large an evidence row may\n * get, not of how bytes are stored.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const makeFileSystem = (\n fs: FileSystem.FileSystem,\n platformPath: Path.Path,\n artifacts: ArtifactStore.Service,\n options: FileSystemOptions = {}\n): Service => {\n const maxInlineBytes = options.maxInlineBytes ?? defaultMaxInlineBytes\n const maxTotalInlineBytes = options.maxTotalInlineBytes ?? defaultMaxTotalInlineBytes\n // Stat tuples are not content identities: a same-size rewrite can preserve\n // mtime, device, and inode on every supported filesystem.\n const readDigestFrom = Effect.fn(\"StepBoundary.readDigest\")(function*(host: FileSystem.FileSystem, path: string) {\n const bytes = yield* host.readFile(path)\n const digest = yield* Schema.decodeUnknownEffect(Sha256)(bytes).pipe(Effect.orDie)\n return { digest, bytes } satisfies MeasuredDigest\n })\n const readDigest = (path: string) => readDigestFrom(fs, path)\n const batch = KernelFileSystem.batch(fs)\n const measurements = Effect.fn(\"StepBoundary.measurements\")(\n function*(paths: ReadonlyArray<string>, content: boolean) {\n if (batch === undefined) {\n return yield* Effect.forEach(paths, (path) =>\n readDigest(path).pipe(\n Effect.map((value): BatchedDigest => ({\n digest: value.digest,\n sizeBytes: value.bytes.length,\n ...(content ? { bytes: value.bytes } : {})\n })),\n Effect.catchReason(\"PlatformError\", \"NotFound\", () => Effect.succeed(undefined)),\n Effect.mapError(hostFailure)\n ), { concurrency: KernelFileSystem.fallbackConcurrency })\n }\n const values: Array<BatchedDigest | undefined> = []\n for (let offset = 0; offset < paths.length; offset += batch.maxSize) {\n const response = yield* batch.execute(\n paths.slice(offset, offset + batch.maxSize).map((path) => ({\n operation: \"digest\",\n path,\n content\n }))\n ).pipe(Effect.mapError(hostFailure))\n for (const entry of [...response.entries].sort((a, b) => a.index - b.index)) {\n const measured = yield* Effect.fromResult(entry.result).pipe(\n Effect.catchReason(\"PlatformError\", \"NotFound\", () => Effect.succeed(undefined)),\n Effect.mapError(hostFailure)\n )\n if (measured === undefined) values.push(undefined)\n else if (measured.operation !== \"digest\") {\n return yield* Effect.fail(hostFailure(new Error(\"host returned a non-digest batch result\")))\n } else values.push({ ...measured, digest: measured.digest as ArtifactStore.Digest })\n }\n }\n return values\n }\n )\n /**\n * One host call, never two. An `exists` probe ahead of the read asked\n * exactly what the read itself answers \u2014 a path that is not there refuses\n * with `NotFound` \u2014 and every declared read was therefore measured twice.\n * On the confined host that is two CPython forks per path\n * (`@smthrs/platform-node/AtomicFileSystem` spawns one interpreter per\n * operation, ~130 ms each), so the probe doubled the cost of every\n * `prepare`, every post-body change check, and every materialization.\n */\n const measure = (\n path: string,\n host: FileSystem.FileSystem = fs\n ): Effect.Effect<string, UnsupportedBoundary, Crypto.Crypto> =>\n readDigestFrom(host, path).pipe(\n Effect.map((measured) => measured.digest),\n Effect.catchReason(\"PlatformError\", \"NotFound\", () => Effect.succeed(absentDigest)),\n Effect.mapError(hostFailure)\n )\n // Both expansions enumerate through `FileEnumeration`, never the host\n // `glob`: host results are absolute under the kernel FileSystem and skip\n // dotfiles under Node's matcher, so trusting them silently emptied every\n // workspace-relative expansion and let a tree replay delete dotfiles the\n // producer wrote.\n const expandGlob = Effect.fn(\"StepBoundary.expandGlob\")(function*(glob: FileSet.Glob) {\n return yield* FileEnumeration.expandGlob(fs, glob).pipe(Effect.mapError(hostFailure))\n })\n const treeEntries = Effect.fn(\"StepBoundary.treeEntries\")(function*(host: FileSystem.FileSystem, path: string) {\n return yield* FileEnumeration.entriesUnder(host, path).pipe(Effect.mapError(hostFailure))\n })\n // Replay writes and deletes what evidence names, so the lexical check alone\n // is not a boundary: a symlink planted on a lexically valid path would carry\n // the mutation outside the workspace. Every replay mutation, its probes, and\n // its prune walk go through a view pinned to the root, where each call is\n // one descriptor-relative, no-follow request and a symlink anywhere on the\n // path is refused rather than followed. `prepare` and `settle` only read,\n // and keep the caller's filesystem.\n const replayRoot = KernelFileSystem.confinedRoot(fs) ?? options.root ?? \".\"\n const replayHost: Effect.Effect<FileSystem.FileSystem, UnsupportedBoundary> = KernelFileSystem.isConfinable(fs)\n ? KernelFileSystem.confined(fs, replayRoot).pipe(\n Effect.provideService(Path.Path, platformPath),\n Effect.mapError(hostFailure)\n )\n : Effect.fail(\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message:\n \"cache replay needs a confined host filesystem; the host carries no descriptor-relative executor or isolation attestation\"\n })\n )\n const capture = Effect.fn(\"StepBoundary.capture\")(\n function*(path: string, inlineBudget: number, measured: BatchedDigest | undefined) {\n yield* Effect.annotateCurrentSpan({ path })\n // As in `measure`: the read reports absence itself, so the output is\n // captured in one host call rather than an `exists` probe plus a read.\n if (measured === undefined) {\n return { output: { path, digest: null } satisfies MaterializedOutput, inlinedBytes: 0 }\n }\n const bytes = measured.bytes\n if (bytes === undefined) return yield* Effect.fail(hostFailure(new Error(\"host omitted requested batch content\")))\n const digest = measured.digest\n // Inline only while both bounds hold (issue #122): the per-output bound\n // and the settle-wide aggregate budget the caller threads through.\n if (bytes.length <= maxInlineBytes && bytes.length <= inlineBudget) {\n return {\n output: {\n path,\n digest,\n sizeBytes: bytes.length,\n content: Base64.encode(bytes)\n } satisfies MaterializedOutput,\n inlinedBytes: bytes.length\n }\n }\n // Over the inline bound: the payload goes to the content-addressed\n // artifact store and the persisted row carries only the reference. Every\n // property that used to live here \u2014 atomic publication, verify-once\n // dedupe, healing rewrite of a corrupt address \u2014 is now the store's, and\n // is tested there (`@smthrs/artifacts`).\n const stored = yield* artifacts.put(bytes).pipe(Effect.mapError(artifactFailure))\n if (stored !== digest) {\n return yield* Effect.fail(\n new BoundaryCorruption({\n code: \"boundary_corruption\",\n path,\n recordedDigest: `${stored}`,\n measuredDigest: digest\n })\n )\n }\n return { output: { path, digest, sizeBytes: bytes.length } satisfies MaterializedOutput, inlinedBytes: 0 }\n }\n )\n const materialize = Effect.fn(\"StepBoundary.materialize\")(function*(\n host: FileSystem.FileSystem,\n output: MaterializedOutput\n ) {\n yield* Effect.annotateCurrentSpan({ path: output.path })\n // The filesystem is the cheapest source of truth for a warm workspace.\n // Probe before decoding inline bytes or consulting the artifact store; a\n // transient probe refusal merely forfeits the optimization and the\n // ordinary materialization path retains its existing typed failures.\n const current = yield* measure(output.path, host).pipe(\n Effect.map((digest) => digest !== absentDigest && digest === output.digest),\n Effect.catch(() => Effect.succeed(false))\n )\n if (current) return\n let bytes: Uint8Array\n if (output.content !== undefined) {\n const decoded = Base64.decode(output.content)\n if (Result.isFailure(decoded)) {\n // Inline content is cache-origin bytes: an undecodable payload is a\n // tampered durable row, classified exactly like a digest mismatch so\n // the caller routes it to the Inconsistency receiver instead of\n // retrying it as a transient host refusal (issue #159).\n return yield* Effect.fail(inlineCorruption(output.path, `${output.digest}`))\n }\n bytes = decoded.success\n // Inline evidence is verified here because nothing else can: the bytes\n // travel inside the row rather than at a content address. A referenced\n // artifact is verified by the store on the way out, so the check below\n // would be a second read+hash of the same bytes.\n const measured = yield* Schema.decodeUnknownEffect(Sha256)(bytes).pipe(Effect.orDie)\n if (measured !== output.digest) {\n return yield* Effect.fail(\n new BoundaryCorruption({\n code: \"boundary_corruption\",\n path: output.path,\n recordedDigest: `${output.digest}`,\n measuredDigest: measured\n })\n )\n }\n } else {\n bytes = yield* artifacts.get(`${output.digest}`).pipe(\n Effect.catchTags({\n // A reference this host cannot resolve is not a host refusal: a\n // shared artifact tier may still hold it, so the caller gets a\n // typed, repairable failure and fetches before falling back to a\n // real execution (issue #172).\n \"@smthrs/artifacts/ArtifactMissing\": (missing) =>\n Effect.fail(\n new MissingArtifact({ code: \"missing_artifact\", path: output.path, digest: missing.digest })\n ),\n // Corruption is a distinct typed failure from a transient host\n // error (issue #150): the caller routes it to the Inconsistency\n // receiver instead of treating it as an ordinary retryable refusal.\n \"@smthrs/artifacts/ArtifactCorruption\": (corrupt) =>\n Effect.fail(\n new BoundaryCorruption({\n code: \"boundary_corruption\",\n path: output.path,\n recordedDigest: corrupt.recordedDigest,\n measuredDigest: corrupt.measuredDigest\n })\n ),\n \"@smthrs/artifacts/ArtifactStoreError\": (failure) => Effect.fail(artifactFailure(failure))\n })\n )\n }\n // The original body may have created the output's directory itself; a\n // fresh workspace replaying the evidence has no such directory and\n // `writeFile` does not create parents (issue #107).\n const parent = parentDirectory(output.path)\n if (parent !== undefined) {\n yield* host.makeDirectory(parent, { recursive: true }).pipe(Effect.mapError(hostFailure))\n }\n yield* host.writeFile(output.path, bytes).pipe(Effect.mapError(hostFailure))\n })\n return make({\n prepare: Effect.fn(\"StepBoundary.prepare\")(function*(descriptor) {\n const boundary = FileBoundarySnapshot.make(descriptor)\n yield* Effect.annotateCurrentSpan({\n reads: boundary.readSet.length,\n boundaryMode: boundary.boundaryMode\n })\n // The dirty check's evidence (issue #90): what the host actually\n // measured for every declared read, never the declaration itself \u2014\n // defaulting the snapshot to the declaration made `readSetMatches`\n // compare the declaration against itself and pass unconditionally\n // (issue #104).\n const readSnapshot: Array<FileInput> = []\n const paths: Array<string> = []\n for (const entry of boundary.readSet) {\n if (FileSet.isGlob(entry)) {\n paths.push(...yield* expandGlob(entry))\n } else paths.push(entry.path)\n }\n const measured = yield* measurements(paths, false)\n for (const [index, path] of paths.entries()) {\n readSnapshot.push({ path, digest: measured[index]?.digest ?? absentDigest })\n }\n readSnapshot.sort((left, right) => compareText(left.path, right.path))\n return Object.freeze({\n descriptor: boundary,\n readSnapshot: Object.freeze(readSnapshot.map((entry) => Object.freeze({ ...entry })))\n })\n }),\n settle: Effect.fn(\"StepBoundary.settle\")(function*(prepared) {\n yield* Effect.annotateCurrentSpan({\n writes: prepared.descriptor.writeSet.length,\n boundaryMode: prepared.descriptor.boundaryMode\n })\n // Undeclared-write detection is scoped to the declared read set: a\n // declared read whose content moved during the body and is not also a\n // declared write was mutated outside the contract. Whole-tree change\n // detection needs the jj diff surface and stays a documented\n // limitation of this layer rather than a silent pass \u2014 the read set\n // is exactly the material cacheability rests on.\n const removes = prepared.descriptor.removes ?? []\n const declaredWrites = [...prepared.descriptor.writeSet, ...removes]\n const declaredCovers = (path: string) =>\n declaredWrites.some((entry) =>\n typeof entry === \"string\"\n ? entry === path\n : entry._tag === \"TreeArtifact\"\n ? path === entry.path || path.startsWith(`${entry.path}/`)\n : FileSet.matchesGlob(entry, path)\n )\n const undeclared: Array<string> = []\n const checkedReads = prepared.readSnapshot.filter((entry) => !declaredCovers(entry.path))\n const currentReads = yield* measurements(checkedReads.map((entry) => entry.path), false)\n for (const [index, entry] of checkedReads.entries()) {\n if ((currentReads[index]?.digest ?? absentDigest) !== entry.digest) undeclared.push(entry.path)\n }\n const outputs: Array<MaterializedOutput> = []\n // Declared removals are captured on the same path and under the same\n // `digest: null` semantics they have always had \u2014 the difference is that\n // here the absence was declared, so it is evidence rather than a defect.\n const missing: Array<string> = []\n const surviving: Array<string> = []\n let inlinedBytes = 0\n const outputPaths: Array<string> = []\n const treeMembers: Array<{ readonly path: string; readonly files: ReadonlyArray<string> }> = []\n for (const entry of prepared.descriptor.writeSet) {\n if (typeof entry === \"string\") outputPaths.push(entry)\n else if (entry._tag === \"Glob\") outputPaths.push(...yield* expandGlob(entry))\n else {\n const files = (yield* treeEntries(fs, entry.path)).files\n outputPaths.push(...files)\n treeMembers.push({ path: entry.path, files })\n }\n }\n outputPaths.push(...removes)\n const captured = new Map<string, MaterializedOutput>()\n const sortedPaths = [...new Set(outputPaths)].sort(compareText)\n // Digest-only preflight bounds content batches by bytes as well as path\n // count. Both measurements share the guarded host's pinned root identity;\n // a rewrite between them is a refusal, never mismatched cached content.\n const sizes = batch === undefined ? undefined : yield* measurements(sortedPaths, false)\n for (let offset = 0; offset < sortedPaths.length;) {\n let end = offset\n let responseBytes = 256\n const size = batch?.maxSize ?? KernelFileSystem.fallbackConcurrency\n while (end < sortedPaths.length && end - offset < size) {\n const estimate = 4096 + new TextEncoder().encode(sortedPaths[end]).length * 6 +\n Math.ceil((sizes?.[end]?.sizeBytes ?? 0) / 3) * 4\n if (end > offset && batch !== undefined && responseBytes + estimate > batch.maxResponseBytes) break\n responseBytes += estimate\n end++\n }\n const group = sortedPaths.slice(offset, end)\n const contents = yield* measurements(group, true)\n for (const [index, path] of group.entries()) {\n const measured = contents[index]\n if (sizes !== undefined && measured?.digest !== sizes[offset + index]?.digest) {\n return yield* Effect.fail(hostFailure(new Error(`output changed during measurement: ${path}`)))\n }\n const output = yield* capture(path, maxTotalInlineBytes - inlinedBytes, measured)\n // `capture` reports the bytes it actually inlined (zero for a\n // digest-only reference), so the aggregate budget never has to\n // re-derive them from the row it just built.\n inlinedBytes += output.inlinedBytes\n outputs.push(output.output)\n captured.set(path, output.output)\n if (\n output.output.digest === null &&\n !removes.includes(path) &&\n prepared.descriptor.writeSet.some((entry) => typeof entry === \"string\" && entry === path)\n ) missing.push(path)\n // The dual check: a removal promised the path absent, and it is still\n // here \u2014 possibly rewritten. Settling it would cache the surviving\n // bytes under a declaration that disclaimed them.\n if (output.output.digest !== null && removes.includes(path)) surviving.push(path)\n }\n offset = end\n }\n // A tree member is already one of the captured outputs, so its identity\n // is folded from the digest that capture measured rather than from a\n // second read of the same bytes. `capture` spells an absent member\n // `null` where `measure` spells it `absentDigest`; the identity keeps\n // the `measure` spelling, so a tree recorded before this and one\n // recorded after hash identically.\n const trees: Array<{ readonly path: string; readonly identity: string }> = []\n for (const tree of treeMembers) {\n const pairs = tree.files.map((path) =>\n [path.slice(tree.path.length + 1), captured.get(path)!.digest ?? absentDigest] as const\n )\n trees.push({\n path: tree.path,\n identity: yield* Schema.decodeUnknownEffect(DerivedKey)({ kind: \"tree-artifact\", files: pairs }).pipe(\n Effect.orDie\n )\n })\n }\n // Through `DerivedKey` \u2014 the repo's one hashing chokepoint \u2014 so the identity is\n // a digest of the RFC 8785 canonical form rather than of whatever\n // `JSON.stringify` happened to emit for this shape.\n const diffIdentity = yield* Schema.decodeUnknownEffect(DerivedKey)({\n kind: \"diff-identity\",\n outputs: outputs.map((output) => [output.path, output.digest]),\n trees\n }).pipe(Effect.orDie)\n yield* Effect.annotateCurrentSpan({ diffIdentity })\n if (prepared.descriptor.boundaryMode === \"hard\") {\n if (undeclared.length > 0) {\n return yield* Effect.fail(\n new UndeclaredWrite({ code: \"undeclared_write\", paths: undeclared, diffIdentity })\n )\n }\n // A declared output that was never produced is a defect, not evidence\n // (Bazel's `checkOutputs`). Recording it would cache `digest: null`,\n // which every later `replayOutputs` reads as \"delete this path\".\n if (missing.length > 0) {\n return yield* Effect.fail(\n new MissingDeclaredOutput({ code: \"missing_declared_output\", paths: missing, diffIdentity })\n )\n }\n if (surviving.length > 0) {\n return yield* Effect.fail(\n new SurvivingDeclaredRemoval({ code: \"surviving_declared_removal\", paths: surviving, diffIdentity })\n )\n }\n }\n return {\n declaredOutputs: { outputs, ...(trees.length === 0 ? {} : { trees }) },\n diffIdentity,\n // This filesystem-only boundary cannot observe writes elsewhere in\n // the tree. Omission is deliberate: ActionPersistence treats the\n // result as run-local and will not publish it to the shared cache.\n //\n // An undeclared write is reported ahead of a missing output, and a\n // missing output ahead of a surviving removal: each is the stronger\n // claim about the same execution, and every variant bars the evidence\n // from the shared cache identically.\n ...(undeclared.length > 0\n ? { deviation: { _tag: \"ExpectedSetDeviation\" as const, paths: undeclared, diffIdentity } }\n : missing.length > 0\n ? { deviation: { _tag: \"MissingDeclaredOutput\" as const, paths: missing, diffIdentity } }\n : surviving.length > 0\n ? { deviation: { _tag: \"SurvivingDeclaredRemoval\" as const, paths: surviving, diffIdentity } }\n : {})\n }\n }),\n replayOutputs: Effect.fn(\"StepBoundary.replayOutputs\")(function*(evidence) {\n yield* Effect.annotateCurrentSpan({ diffIdentity: evidence.diffIdentity })\n const decoded = Schema.decodeUnknownResult(MaterializedOutputs)(evidence.declaredOutputs)\n if (decoded._tag === \"Failure\") {\n // Evidence recorded by a different boundary implementation carries\n // no materializable outputs; refusing is honest \u2014 the caller's\n // dispatch path falls back to a real execution or fails visibly.\n return yield* Effect.fail(\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message: \"the recorded boundary evidence carries no materializable outputs\"\n })\n )\n }\n // Replay writes and DELETES whatever the evidence names, so a path is\n // honored only in the coordinate system declarations are written in.\n // Evidence can arrive from a foreign producer through cache sync; an\n // absolute or upward spelling is an eraser aimed outside the workspace,\n // and refusing it here is what keeps that a refusal instead of a wipe.\n const foreign = [\n ...(decoded.success.trees ?? []).map((tree) => tree.path),\n ...decoded.success.outputs.map((output) => output.path)\n ].filter((path) => !FileSet.workspaceRelative(path))\n if (foreign.length > 0) {\n return yield* Effect.fail(\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message: `the recorded boundary evidence names paths outside the workspace: ${foreign.join(\", \")}`\n })\n )\n }\n // Before any mutation: a host that cannot be confined refuses the whole\n // replay, and the caller falls back to a real execution.\n const host = yield* replayHost\n const emptyDirectoryCandidates = new Set<string>()\n for (const tree of decoded.success.trees ?? []) {\n const prefix = `${tree.path}/`.replace(/\\/{2,}$/g, \"/\")\n const recorded = new Set(\n decoded.success.outputs\n .map((output) => output.path)\n .filter((path) => path.startsWith(prefix))\n )\n const entries = yield* treeEntries(host, tree.path)\n for (const path of entries.files) {\n if (!recorded.has(path)) yield* host.remove(path).pipe(Effect.mapError(hostFailure))\n }\n for (const directory of entries.directories) emptyDirectoryCandidates.add(directory)\n }\n for (const output of decoded.success.outputs) {\n if (output.digest === null) {\n // `force` IS the probe: the evidence says this path must not exist,\n // and a removal that finds it already gone has done its job. The\n // `exists` call that used to precede it was a second host call \u2014 a\n // second process on the confined host \u2014 for the same answer.\n yield* host.remove(output.path, { force: true }).pipe(Effect.mapError(hostFailure))\n } else {\n yield* materialize(host, output)\n }\n }\n // Remove only directories proven empty after pruning and\n // materialization. Deepest-first preserves every directory that still\n // contains a recorded child while clearing stale empty scaffolding.\n const directories = [...emptyDirectoryCandidates].sort((left, right) =>\n right.length - left.length || compareText(left, right)\n )\n for (const directory of directories) {\n const entries = yield* host.readDirectory(directory).pipe(Effect.mapError(hostFailure))\n if (entries.length === 0) yield* host.remove(directory).pipe(Effect.mapError(hostFailure))\n }\n })\n })\n}\n\n/**\n * Provides the filesystem-backed production boundary: `prepare` measures the\n * declared read set for real, `settle` detects reads mutated outside the\n * declared write set and captures the write set's post-state, and\n * `replayOutputs` re-materializes those outputs on cache-hit replay.\n *\n * Host access arrives through Effect's `FileSystem` tag, which the capability\n * kernel decorates in place \u2014 the same seam every host implementation (node,\n * bun, browser, sandbox) already provides. Blob storage arrives through\n * `@smthrs/artifacts`, so the same boundary runs over a purely local store or\n * over a local-plus-shared composition without knowing which it got.\n *\n * Replay confines its own mutations rather than trusting the composition:\n * they run through `@smthrs/kernel/FileSystem`'s `confined` view, rooted at\n * the kernel `Workspace` when one is in context. A symlink on a replayed path\n * (a parent, the final component, or a member of a replayed tree) and a host\n * with no descriptor-relative executor or isolation attestation both refuse\n * with {@link UnsupportedBoundary}, which the caller journals as\n * `replay_failed` before executing the step for real.\n *\n * @since 0.1.0\n * @category layers\n */\nexport const layer: Layer.Layer<\n Service,\n never,\n FileSystem.FileSystem | Path.Path | ArtifactStore.ArtifactStore\n> = Layer.effect(\n StepBoundary,\n Effect.gen(function*() {\n const fs = yield* FileSystem.FileSystem\n const platformPath = yield* Path.Path\n const artifacts = yield* ArtifactStore.ArtifactStore\n const workspace = yield* Effect.serviceOption(KernelWorkspace)\n return makeFileSystem(fs, platformPath, artifacts, workspace._tag === \"Some\" ? { root: workspace.value.root } : {})\n })\n)\n\n/**\n * What the deterministic test boundary should pretend the host observed.\n *\n * Every field is a fixture, not a classification rule: a test supplies settlement\n * evidence or failure, the read snapshot, and whether the host claims\n * to support the boundary at all \u2014 then asserts on the resulting evidence or\n * refusal. Defaults describe a well-behaved, fully-supported host.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface TestOptions {\n /** A settlement failure supplied verbatim by an integration fixture. */\n readonly failure?: UndeclaredWrite | MissingDeclaredOutput | SurvivingDeclaredRemoval | undefined\n /** Settlement evidence supplied verbatim; classification belongs to the real layer. */\n readonly deviation?: BoundaryDeviation | undefined\n /**\n * What `prepare` reports as measured for the declared read set. Defaults\n * to the declaration itself; a test supplies a different snapshot to stand\n * for a file whose content moved out from under a stale declaration\n * (issue #90).\n */\n readonly readSnapshot?: ReadonlyArray<FileInput> | undefined\n /** The opaque replay payload recorded in the evidence. */\n readonly declaredOutputs?: unknown\n /** The post-state identity the evidence names. Defaults to a fixed string. */\n readonly diffIdentity?: string | undefined\n /** When false, every call fails with {@link UnsupportedBoundary}. Defaults to true. */\n readonly supported?: boolean | undefined\n /** Whether the fixture attests a whole-tree observation. Defaults to true. */\n readonly wholeTreeWriteDetection?: boolean | undefined\n /** Whether the fixture models an isolated read surface. Defaults to true. */\n readonly hermeticReadDetection?: boolean | undefined\n /** Observes each `replayOutputs` call, so a test can assert replay happened. */\n readonly onReplay?: (evidence: BoundaryEvidence) => void\n}\n\nconst unsupported = (): UnsupportedBoundary =>\n new UnsupportedBoundary({\n code: \"unsupported_boundary\",\n message: \"the host cannot enforce the declared step boundary\"\n })\n\n/**\n * Deterministic in-memory boundary suitable only for tests. Production\n * wiring uses {@link layer}, the filesystem-backed implementation.\n *\n * Whole-tree change detection is supplied by sandboxed settlement, not by any\n * boundary layer: an isolated execution's transaction IS the tree, so\n * `ActionPersistence` compares its diff against the declared sets and sets\n * `wholeTreeWritesVerified` structurally (the retired piece-6 limitation).\n * {@link layer} still detects mutations within the declared read set only,\n * which is why unsandboxed evidence stays run-local.\n *\n * @since 0.1.0\n * @category layers\n */\nexport const layerTest = (options: TestOptions = {}): Layer.Layer<Service> => {\n const diffIdentity = options.diffIdentity ?? \"test-diff\"\n const service = make({\n prepare: Effect.fn(\"StepBoundary.prepare\")(function*(descriptor) {\n if (options.supported === false) return yield* Effect.fail(unsupported())\n return {\n descriptor,\n readSnapshot: options.readSnapshot ?? exactReads(descriptor)\n }\n }),\n settle: Effect.fn(\"StepBoundary.settle\")(function*(prepared) {\n if (options.supported === false) return yield* Effect.fail(unsupported())\n if (options.failure !== undefined) return yield* Effect.fail(options.failure)\n return {\n declaredOutputs: options.declaredOutputs ?? { paths: prepared.descriptor.writeSet },\n diffIdentity,\n ...(options.wholeTreeWriteDetection === false ? {} : { wholeTreeWritesVerified: true as const }),\n ...(options.hermeticReadDetection === false ? {} : { hermeticReadsVerified: true as const }),\n ...(options.deviation === undefined ? {} : { deviation: options.deviation })\n }\n }),\n replayOutputs: Effect.fn(\"StepBoundary.replayOutputs\")(function*(evidence) {\n if (options.supported === false) return yield* Effect.fail(unsupported())\n yield* Effect.sync(() => {\n options.onReplay?.(evidence)\n })\n })\n })\n return Layer.succeed(StepBoundary, service)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["KernelWorkspace"]
7
7
  }
@@ -41,7 +41,7 @@ import { CacheStore } from "@smthrs/step-cache";
41
41
  import * as Effect from "effect/Effect";
42
42
  import * as HashMap from "effect/HashMap";
43
43
  import * as HashSet from "effect/HashSet";
44
- import * as SqlClient from "effect/unstable/sql/SqlClient";
44
+ import * as SqlClient from "effect/sql/SqlClient";
45
45
  /**
46
46
  * The journal event type the fold reads.
47
47
  *
@@ -144,5 +144,5 @@ export declare const rebuild: () => Effect.Effect<{
144
144
  admitted: number;
145
145
  retired: number;
146
146
  heads: number;
147
- }, import("effect/unstable/sql/SqlError").SqlError | CacheStore.CacheStoreError | Journal.JournalError, SqlClient.SqlClient | import("@smthrs/database/DurableWriter").DurableWriter | Journal.Journal>;
147
+ }, import("effect/sql/SqlError").SqlError | CacheStore.CacheStoreError | Journal.JournalError, SqlClient.SqlClient | import("@smthrs/database/DurableWriter").DurableWriter | Journal.Journal>;
148
148
  //# sourceMappingURL=StepCacheFold.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StepCacheFold.d.ts","sourceRoot":"","sources":["../../src/StepCacheFold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,SAAS,MAAM,+BAA+B,CAAA;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,SAAS,kCAAkC,CAAA;AAExD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,MAAM,CAAkD,CAAA;AAEpG;;;;;GAKG;AACH,MAAM,WAAW,KAAK;IACpB,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,CAAA;IAClE,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;CAC1C;AAED;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,UAAU,CAAC,WAAW,KAAG,MACsB,CAAA;AAqB1E;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,EAAE,OAAO,YAAY,CAAC,KAAK,KAAG,KAMhE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,OAAO,EAAE,KAA+D,CAAA;AAErF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,CAIlD,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,KAAK,KAAG,aAAa,CAAC,UAAU,CAAC,WAAW,CAS7E,CAAA;AAED;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAwBD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,OAAO;;;;;;;uMAgClB,CAAA"}
1
+ {"version":3,"file":"StepCacheFold.d.ts","sourceRoot":"","sources":["../../src/StepCacheFold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,SAAS,kCAAkC,CAAA;AAExD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,MAAM,CAAkD,CAAA;AAEpG;;;;;GAKG;AACH,MAAM,WAAW,KAAK;IACpB,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,CAAA;IAClE,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;CAC1C;AAED;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,UAAU,CAAC,WAAW,KAAG,MACsB,CAAA;AAqB1E;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,EAAE,OAAO,YAAY,CAAC,KAAK,KAAG,KAMhE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,OAAO,EAAE,KAA+D,CAAA;AAErF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,CAIlD,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,KAAK,KAAG,aAAa,CAAC,UAAU,CAAC,WAAW,CAS7E,CAAA;AAED;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAwBD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,OAAO;;;;;;;8LAgClB,CAAA"}
@@ -43,7 +43,7 @@ var import_step_cache = require("@smthrs/step-cache");
43
43
  var Effect = __toESM(require("effect/Effect"), 1);
44
44
  var HashMap = __toESM(require("effect/HashMap"), 1);
45
45
  var HashSet = __toESM(require("effect/HashSet"), 1);
46
- var SqlClient = __toESM(require("effect/unstable/sql/SqlClient"), 1);
46
+ var SqlClient = __toESM(require("effect/sql/SqlClient"), 1);
47
47
  const eventType = "flows.engine.cache-provenance";
48
48
  const retiringActions = ["expired", "stale_read_set", "replay_failed"];
49
49
  const refId = (ref) => JSON.stringify([ref.keyDigest, ref.recordedRunId, ref.recordedEventSeq]);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/StepCacheFold.ts"],
4
- "sourcesContent": ["/**\n * The step-cache fold: `flows_step_cache` heads rebuilt from the journal.\n *\n * A head exists only because a `CacheStore.put` inserted it, and\n * `ActionPersistence` journals exactly that insertion as a\n * `flows.engine.cache-provenance` entry with action `admitted`, in the same\n * `journal.transact` as the `put`. A duplicate or a losing first-writer race\n * writes a ledger row but no head, so it journals no `admitted` entry. Every\n * engine eviction is journalled before the fenced `CacheStore.evict` that\n * performs it: `expired`, `stale_read_set`, and `replay_failed` with reason\n * `corruption`, each naming the row it retires. The fold retires a row on any\n * `replay_failed` that names it: a producer slot keeps the first reason it\n * journalled, so a corruption can hide behind an earlier host failure. A row\n * whose replay failed only on the host is then a miss.\n *\n * The fold keeps the admitted rows no eviction retired. A key with one such\n * row gets it back as its head. A key with several lost at least one of them\n * without a journal entry (`sweepExpired`, retention, an unfenced `evict`), and\n * the journal cannot say which one the store still held, so it gets no head.\n *\n * Cache results stay in the append-only `flows_step_cache_recorded` ledger\n * that the `admitted` entry addresses. The journal is redacted and replayed to\n * sync subscribers, so it carries the decision and the provenance, never the\n * executable result (issue #72).\n *\n * A rebuild writes a subset of the live heads, apart from rows removed without\n * a journal entry, whose restored hits still pass the engine's age, read-set,\n * and digest checks. Shared-tier write-backs have no local entry and are not\n * rebuilt. Compaction and rewind delete eviction entries, so a store holding\n * a compacted run, or one where any run was ever rewound, gets no heads at all.\n * Retention is not yet covered: it deletes a run's entries, and with them the\n * compaction floor and any eviction that run journalled of another run's row,\n * so a rebuild after retention can restore a row the engine evicted (issue\n * #2053). A convergence re-record that reuses an evicted row's provenance stays\n * retired, so that key misses.\n *\n * @since 1.0.0\n */\n\nimport { Journal, type JournalEvent, Projection } from \"@smthrs/journal\"\nimport { CacheStore } from \"@smthrs/step-cache\"\nimport * as Effect from \"effect/Effect\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as SqlClient from \"effect/unstable/sql/SqlClient\"\n\n/**\n * The journal event type the fold reads.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const eventType = \"flows.engine.cache-provenance\"\n\n/**\n * Provenance actions that retire the row they name.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const retiringActions: ReadonlyArray<string> = [\"expired\", \"stale_read_set\", \"replay_failed\"]\n\n/**\n * Admitted rows folded so far, and the ones the journal retired.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface State {\n /** Every row a `put` inserted as a head, by {@link refId}. */\n readonly admitted: HashMap.HashMap<string, CacheStore.RecordedRef>\n /** Rows an engine eviction retired, by {@link refId}. */\n readonly retired: HashSet.HashSet<string>\n}\n\n/**\n * The stable identity of a recorded row.\n *\n * @category models\n * @since 1.0.0\n */\nexport const refId = (ref: CacheStore.RecordedRef): string =>\n JSON.stringify([ref.keyDigest, ref.recordedRunId, ref.recordedEventSeq])\n\nconst record = (value: unknown): Readonly<Record<string, unknown>> | undefined =>\n typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? value as Readonly<Record<string, unknown>>\n : undefined\n\n/** The row a payload names by `keyDigest`/`recordedRunId`/`recordedEventSeq`, if it names one. */\nconst named = (payload: Readonly<Record<string, unknown>>): CacheStore.RecordedRef | undefined =>\n typeof payload.keyDigest === \"string\" && typeof payload.recordedRunId === \"string\" &&\n typeof payload.recordedEventSeq === \"number\"\n ? {\n keyDigest: payload.keyDigest,\n recordedRunId: payload.recordedRunId,\n recordedEventSeq: payload.recordedEventSeq\n }\n : undefined\n\nconst retires = (payload: Readonly<Record<string, unknown>>): boolean =>\n retiringActions.includes(payload.action as string)\n\n/**\n * Folds one committed journal entry.\n *\n * @category folds\n * @since 1.0.0\n */\nexport const reduce = (state: State, entry: JournalEvent.Entry): State => {\n const payload = record(entry.payload)\n const ref = entry.eventType === eventType && payload !== undefined ? named(payload) : undefined\n if (ref === undefined) return state\n if (payload!.action === \"admitted\") return { ...state, admitted: HashMap.set(state.admitted, refId(ref), ref) }\n return retires(payload!) ? { ...state, retired: HashSet.add(state.retired, refId(ref)) } : state\n}\n\n/**\n * The empty fold.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const initial: State = { admitted: HashMap.empty(), retired: HashSet.empty() }\n\n/**\n * The fold as a journal projection, for `Journal.project` over one run.\n *\n * @category folds\n * @since 1.0.0\n */\nexport const projection: Projection.Projection<State> = Projection.make({\n name: \"flows.engine.step-cache\",\n initial,\n reduce: (state, entry) => Effect.succeed(reduce(state, entry))\n})\n\n/**\n * The head each key may be rebuilt with: its one admitted row no eviction\n * retired. A key with more than one such row gets none.\n *\n * @category folds\n * @since 1.0.0\n */\nexport const candidates = (state: State): ReadonlyArray<CacheStore.RecordedRef> => {\n const byKey = new Map<string, Array<CacheStore.RecordedRef>>()\n for (const [id, ref] of state.admitted) {\n if (HashSet.has(state.retired, id)) continue\n const refs = byKey.get(ref.keyDigest)\n if (refs === undefined) byKey.set(ref.keyDigest, [ref])\n else refs.push(ref)\n }\n return [...byKey.values()].flatMap((refs) => refs.length === 1 ? refs : [])\n}\n\n/**\n * What a {@link rebuild} read and wrote.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Rebuilt {\n /** Runs with cache provenance entries. */\n readonly runs: number\n /** Runs with a compaction floor. Any makes the rebuild write no heads. */\n readonly compacted: number\n /** Runs a rewind truncated. Any makes the rebuild write no heads. */\n readonly rewound: number\n readonly admitted: number\n readonly retired: number\n readonly heads: number\n}\n\nconst pageSize = 512\n\n/** Folds one run's cache provenance entries into `state`, in sequence order. */\nconst foldRun = (journal: Journal.Service, runId: string, start: State) =>\n Effect.gen(function*() {\n let state = start\n let after: JournalEvent.Seq | undefined\n while (true) {\n const page = yield* journal.entries({\n runId: runId as JournalEvent.RunId,\n eventTypes: [eventType],\n limit: pageSize,\n ...(after === undefined ? {} : { after })\n })\n for (const entry of page.entries) {\n state = reduce(state, entry)\n after = entry.seq\n }\n if (!page.hasMore) return state\n }\n })\n\n/**\n * Rebuilds every `flows_step_cache` head from the journal.\n *\n * Reads each run's cache provenance entries in sequence order, folds them,\n * and replaces the heads through `CacheStore.rebuildHeads` in one writer\n * transaction. A store with a compacted or rewound run clears the heads\n * instead: the eviction that retired a row may be gone. A journal read failure, including\n * a compaction that lands during the read, leaves the heads untouched.\n *\n * Run it on a quiescent store, as a restore does: a cache write that commits\n * between the journal read and the rewrite is dropped from the heads (a miss,\n * never a wrong hit).\n *\n * @category operations\n * @since 1.0.0\n */\nexport const rebuild = Effect.fn(\"StepCacheFold.rebuild\")(function*() {\n const journal = yield* Journal.Journal\n const sql = yield* SqlClient.SqlClient\n const floors = yield* sql<{ readonly compacted: number | string }>`\n SELECT COUNT(DISTINCT run_id) AS compacted FROM flows_journal_checkpoints\n WHERE compacted_at_ms IS NOT NULL\n `\n const compacted = Number(floors[0]!.compacted)\n const generations = yield* sql<{ readonly rewound: number | string }>`\n SELECT COUNT(*) AS rewound FROM flows_journal_generations\n `\n const rewound = Number(generations[0]!.rewound)\n const runs = yield* sql<{ readonly run_id: string }>`\n SELECT DISTINCT run_id FROM flows_journal_events\n WHERE event_type = ${eventType}\n ORDER BY run_id\n `\n let state = initial\n if (compacted === 0 && rewound === 0) {\n for (const { run_id } of runs) {\n state = yield* foldRun(journal, run_id, state)\n }\n }\n const heads = yield* CacheStore.rebuildHeads(candidates(state))\n return {\n runs: runs.length,\n compacted,\n rewound,\n admitted: HashMap.size(state.admitted),\n retired: HashSet.size(state.retired),\n heads\n } satisfies Rebuilt\n})\n"],
4
+ "sourcesContent": ["/**\n * The step-cache fold: `flows_step_cache` heads rebuilt from the journal.\n *\n * A head exists only because a `CacheStore.put` inserted it, and\n * `ActionPersistence` journals exactly that insertion as a\n * `flows.engine.cache-provenance` entry with action `admitted`, in the same\n * `journal.transact` as the `put`. A duplicate or a losing first-writer race\n * writes a ledger row but no head, so it journals no `admitted` entry. Every\n * engine eviction is journalled before the fenced `CacheStore.evict` that\n * performs it: `expired`, `stale_read_set`, and `replay_failed` with reason\n * `corruption`, each naming the row it retires. The fold retires a row on any\n * `replay_failed` that names it: a producer slot keeps the first reason it\n * journalled, so a corruption can hide behind an earlier host failure. A row\n * whose replay failed only on the host is then a miss.\n *\n * The fold keeps the admitted rows no eviction retired. A key with one such\n * row gets it back as its head. A key with several lost at least one of them\n * without a journal entry (`sweepExpired`, retention, an unfenced `evict`), and\n * the journal cannot say which one the store still held, so it gets no head.\n *\n * Cache results stay in the append-only `flows_step_cache_recorded` ledger\n * that the `admitted` entry addresses. The journal is redacted and replayed to\n * sync subscribers, so it carries the decision and the provenance, never the\n * executable result (issue #72).\n *\n * A rebuild writes a subset of the live heads, apart from rows removed without\n * a journal entry, whose restored hits still pass the engine's age, read-set,\n * and digest checks. Shared-tier write-backs have no local entry and are not\n * rebuilt. Compaction and rewind delete eviction entries, so a store holding\n * a compacted run, or one where any run was ever rewound, gets no heads at all.\n * Retention is not yet covered: it deletes a run's entries, and with them the\n * compaction floor and any eviction that run journalled of another run's row,\n * so a rebuild after retention can restore a row the engine evicted (issue\n * #2053). A convergence re-record that reuses an evicted row's provenance stays\n * retired, so that key misses.\n *\n * @since 1.0.0\n */\n\nimport { Journal, type JournalEvent, Projection } from \"@smthrs/journal\"\nimport { CacheStore } from \"@smthrs/step-cache\"\nimport * as Effect from \"effect/Effect\"\nimport * as HashMap from \"effect/HashMap\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\n\n/**\n * The journal event type the fold reads.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const eventType = \"flows.engine.cache-provenance\"\n\n/**\n * Provenance actions that retire the row they name.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const retiringActions: ReadonlyArray<string> = [\"expired\", \"stale_read_set\", \"replay_failed\"]\n\n/**\n * Admitted rows folded so far, and the ones the journal retired.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface State {\n /** Every row a `put` inserted as a head, by {@link refId}. */\n readonly admitted: HashMap.HashMap<string, CacheStore.RecordedRef>\n /** Rows an engine eviction retired, by {@link refId}. */\n readonly retired: HashSet.HashSet<string>\n}\n\n/**\n * The stable identity of a recorded row.\n *\n * @category models\n * @since 1.0.0\n */\nexport const refId = (ref: CacheStore.RecordedRef): string =>\n JSON.stringify([ref.keyDigest, ref.recordedRunId, ref.recordedEventSeq])\n\nconst record = (value: unknown): Readonly<Record<string, unknown>> | undefined =>\n typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? value as Readonly<Record<string, unknown>>\n : undefined\n\n/** The row a payload names by `keyDigest`/`recordedRunId`/`recordedEventSeq`, if it names one. */\nconst named = (payload: Readonly<Record<string, unknown>>): CacheStore.RecordedRef | undefined =>\n typeof payload.keyDigest === \"string\" && typeof payload.recordedRunId === \"string\" &&\n typeof payload.recordedEventSeq === \"number\"\n ? {\n keyDigest: payload.keyDigest,\n recordedRunId: payload.recordedRunId,\n recordedEventSeq: payload.recordedEventSeq\n }\n : undefined\n\nconst retires = (payload: Readonly<Record<string, unknown>>): boolean =>\n retiringActions.includes(payload.action as string)\n\n/**\n * Folds one committed journal entry.\n *\n * @category folds\n * @since 1.0.0\n */\nexport const reduce = (state: State, entry: JournalEvent.Entry): State => {\n const payload = record(entry.payload)\n const ref = entry.eventType === eventType && payload !== undefined ? named(payload) : undefined\n if (ref === undefined) return state\n if (payload!.action === \"admitted\") return { ...state, admitted: HashMap.set(state.admitted, refId(ref), ref) }\n return retires(payload!) ? { ...state, retired: HashSet.add(state.retired, refId(ref)) } : state\n}\n\n/**\n * The empty fold.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const initial: State = { admitted: HashMap.empty(), retired: HashSet.empty() }\n\n/**\n * The fold as a journal projection, for `Journal.project` over one run.\n *\n * @category folds\n * @since 1.0.0\n */\nexport const projection: Projection.Projection<State> = Projection.make({\n name: \"flows.engine.step-cache\",\n initial,\n reduce: (state, entry) => Effect.succeed(reduce(state, entry))\n})\n\n/**\n * The head each key may be rebuilt with: its one admitted row no eviction\n * retired. A key with more than one such row gets none.\n *\n * @category folds\n * @since 1.0.0\n */\nexport const candidates = (state: State): ReadonlyArray<CacheStore.RecordedRef> => {\n const byKey = new Map<string, Array<CacheStore.RecordedRef>>()\n for (const [id, ref] of state.admitted) {\n if (HashSet.has(state.retired, id)) continue\n const refs = byKey.get(ref.keyDigest)\n if (refs === undefined) byKey.set(ref.keyDigest, [ref])\n else refs.push(ref)\n }\n return [...byKey.values()].flatMap((refs) => refs.length === 1 ? refs : [])\n}\n\n/**\n * What a {@link rebuild} read and wrote.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Rebuilt {\n /** Runs with cache provenance entries. */\n readonly runs: number\n /** Runs with a compaction floor. Any makes the rebuild write no heads. */\n readonly compacted: number\n /** Runs a rewind truncated. Any makes the rebuild write no heads. */\n readonly rewound: number\n readonly admitted: number\n readonly retired: number\n readonly heads: number\n}\n\nconst pageSize = 512\n\n/** Folds one run's cache provenance entries into `state`, in sequence order. */\nconst foldRun = (journal: Journal.Service, runId: string, start: State) =>\n Effect.gen(function*() {\n let state = start\n let after: JournalEvent.Seq | undefined\n while (true) {\n const page = yield* journal.entries({\n runId: runId as JournalEvent.RunId,\n eventTypes: [eventType],\n limit: pageSize,\n ...(after === undefined ? {} : { after })\n })\n for (const entry of page.entries) {\n state = reduce(state, entry)\n after = entry.seq\n }\n if (!page.hasMore) return state\n }\n })\n\n/**\n * Rebuilds every `flows_step_cache` head from the journal.\n *\n * Reads each run's cache provenance entries in sequence order, folds them,\n * and replaces the heads through `CacheStore.rebuildHeads` in one writer\n * transaction. A store with a compacted or rewound run clears the heads\n * instead: the eviction that retired a row may be gone. A journal read failure, including\n * a compaction that lands during the read, leaves the heads untouched.\n *\n * Run it on a quiescent store, as a restore does: a cache write that commits\n * between the journal read and the rewrite is dropped from the heads (a miss,\n * never a wrong hit).\n *\n * @category operations\n * @since 1.0.0\n */\nexport const rebuild = Effect.fn(\"StepCacheFold.rebuild\")(function*() {\n const journal = yield* Journal.Journal\n const sql = yield* SqlClient.SqlClient\n const floors = yield* sql<{ readonly compacted: number | string }>`\n SELECT COUNT(DISTINCT run_id) AS compacted FROM flows_journal_checkpoints\n WHERE compacted_at_ms IS NOT NULL\n `\n const compacted = Number(floors[0]!.compacted)\n const generations = yield* sql<{ readonly rewound: number | string }>`\n SELECT COUNT(*) AS rewound FROM flows_journal_generations\n `\n const rewound = Number(generations[0]!.rewound)\n const runs = yield* sql<{ readonly run_id: string }>`\n SELECT DISTINCT run_id FROM flows_journal_events\n WHERE event_type = ${eventType}\n ORDER BY run_id\n `\n let state = initial\n if (compacted === 0 && rewound === 0) {\n for (const { run_id } of runs) {\n state = yield* foldRun(journal, run_id, state)\n }\n }\n const heads = yield* CacheStore.rebuildHeads(candidates(state))\n return {\n runs: runs.length,\n compacted,\n rewound,\n admitted: HashMap.size(state.admitted),\n retired: HashSet.size(state.retired),\n heads\n } satisfies Rebuilt\n})\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import type { Service as WriterService } from "@smthrs/database/DurableWriter";
20
20
  import * as Effect from "effect/Effect";
21
- import type * as SqlClient from "effect/unstable/sql/SqlClient";
21
+ import type * as SqlClient from "effect/sql/SqlClient";
22
22
  /**
23
23
  * SQL dialects a statement is known to be accepted by, verbatim.
24
24
  *
@@ -1 +1 @@
1
- {"version":3,"file":"EngineStateSchema.d.ts","sourceRoot":"","sources":["../../../src/internal/EngineStateSchema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAC9E,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,SAAS,MAAM,+BAA+B,CAAA;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,aAAa,CAAC,SAAS,CAa/C,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,SAAS,CAAC,SAAS,EAAE,QAAQ,aAAa,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAMvF,CAAA"}
1
+ {"version":3,"file":"EngineStateSchema.d.ts","sourceRoot":"","sources":["../../../src/internal/EngineStateSchema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAC9E,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,aAAa,CAAC,SAAS,CAa/C,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,SAAS,CAAC,SAAS,EAAE,QAAQ,aAAa,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAMvF,CAAA"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/internal/EngineStateSchema.ts"],
4
- "sourcesContent": ["/**\n * The schema objects `DurableEngineState` creates outside the journal\n * migration, and the inventory of what porting them to another\n * dialect costs.\n *\n * Each durable package owns its migration ladder. Spawn edges and their GC\n * trigger moved into engine-store migration 0006. The remaining\n * statements are engine-store-owned storage created idempotently at\n * construction instead (issues #40/#41/#79/#81). That is a deliberate lane\n * boundary, not an oversight \u2014 but it left the statements invisible to the\n * pg-porting plan in the database roadmap, which\n * scoped only the journal migration's SQLite-specific DDL. Declaring them here\n * makes the inventory machine-readable: a test diffs the database's schema\n * objects across `make` against this list, so no engine-owned statement can\n * be added without appearing in the porting inventory (issue #92).\n *\n * @since 0.1.0\n */\n\nimport { isReadOnly } from \"@smthrs/database/Dialect\"\nimport type { Service as WriterService } from \"@smthrs/database/DurableWriter\"\nimport * as Effect from \"effect/Effect\"\nimport type * as SqlClient from \"effect/unstable/sql/SqlClient\"\n\n/**\n * SQL dialects a statement is known to be accepted by, verbatim.\n *\n * @since 0.1.0\n * @category models\n */\nexport type Dialect = \"sqlite\" | \"postgres\"\n\n/**\n * One engine-store-owned schema object.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface Statement {\n /** The schema object's name, as it appears in the catalog. */\n readonly name: string\n /** Why the object exists, by issue. */\n readonly reason: string\n /**\n * Dialects that accept this statement unchanged. A statement listing only\n * `\"sqlite\"` must be rewritten before a Postgres/PGlite `Database` layer\n * can construct this service \u2014 `make` pipes every one of them through\n * `Effect.orDie`, so an unported statement is a construction-time defect,\n * not a recoverable error.\n */\n readonly dialects: ReadonlyArray<Dialect>\n readonly ddl: string\n}\n\n/**\n * The porting inventory: every engine-store-owned statement, in creation order.\n *\n * @since 0.1.0\n * @category constants\n */\nexport const statements: ReadonlyArray<Statement> = [\n {\n name: \"flows_runs_stale_running_idx\",\n reason:\n \"Serves the stale-running sweep's per-tick predicate (issue #79): `status = 'running' AND heartbeat_at_ms < cutoff`, ordered by heartbeat. A partial index keeps it tiny \u2014 only live running rows appear.\",\n // Partial indexes are valid Postgres since 9.5, so this one ports as-is.\n dialects: [\"sqlite\", \"postgres\"],\n ddl: `\n CREATE INDEX IF NOT EXISTS flows_runs_stale_running_idx\n ON flows_runs (heartbeat_at_ms)\n WHERE status = 'running'\n `\n }\n]\n\n/**\n * Creates the engine-store-owned objects, idempotently, in inventory order.\n *\n * `flows_runs` must already exist \u2014 every query in this service assumes a\n * migrated database, so `make` composes over one by construction. A read-only\n * client installs nothing.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const apply = (sql: SqlClient.SqlClient, writer: WriterService): Effect.Effect<void> =>\n // A read-only observer reads the schema it finds and installs nothing.\n isReadOnly(sql) ? Effect.void : Effect.forEach(\n statements,\n (statement) => writer.write(sql.unsafe(statement.ddl)).pipe(Effect.orDie),\n { discard: true }\n )\n"],
4
+ "sourcesContent": ["/**\n * The schema objects `DurableEngineState` creates outside the journal\n * migration, and the inventory of what porting them to another\n * dialect costs.\n *\n * Each durable package owns its migration ladder. Spawn edges and their GC\n * trigger moved into engine-store migration 0006. The remaining\n * statements are engine-store-owned storage created idempotently at\n * construction instead (issues #40/#41/#79/#81). That is a deliberate lane\n * boundary, not an oversight \u2014 but it left the statements invisible to the\n * pg-porting plan in the database roadmap, which\n * scoped only the journal migration's SQLite-specific DDL. Declaring them here\n * makes the inventory machine-readable: a test diffs the database's schema\n * objects across `make` against this list, so no engine-owned statement can\n * be added without appearing in the porting inventory (issue #92).\n *\n * @since 0.1.0\n */\n\nimport { isReadOnly } from \"@smthrs/database/Dialect\"\nimport type { Service as WriterService } from \"@smthrs/database/DurableWriter\"\nimport * as Effect from \"effect/Effect\"\nimport type * as SqlClient from \"effect/sql/SqlClient\"\n\n/**\n * SQL dialects a statement is known to be accepted by, verbatim.\n *\n * @since 0.1.0\n * @category models\n */\nexport type Dialect = \"sqlite\" | \"postgres\"\n\n/**\n * One engine-store-owned schema object.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface Statement {\n /** The schema object's name, as it appears in the catalog. */\n readonly name: string\n /** Why the object exists, by issue. */\n readonly reason: string\n /**\n * Dialects that accept this statement unchanged. A statement listing only\n * `\"sqlite\"` must be rewritten before a Postgres/PGlite `Database` layer\n * can construct this service \u2014 `make` pipes every one of them through\n * `Effect.orDie`, so an unported statement is a construction-time defect,\n * not a recoverable error.\n */\n readonly dialects: ReadonlyArray<Dialect>\n readonly ddl: string\n}\n\n/**\n * The porting inventory: every engine-store-owned statement, in creation order.\n *\n * @since 0.1.0\n * @category constants\n */\nexport const statements: ReadonlyArray<Statement> = [\n {\n name: \"flows_runs_stale_running_idx\",\n reason:\n \"Serves the stale-running sweep's per-tick predicate (issue #79): `status = 'running' AND heartbeat_at_ms < cutoff`, ordered by heartbeat. A partial index keeps it tiny \u2014 only live running rows appear.\",\n // Partial indexes are valid Postgres since 9.5, so this one ports as-is.\n dialects: [\"sqlite\", \"postgres\"],\n ddl: `\n CREATE INDEX IF NOT EXISTS flows_runs_stale_running_idx\n ON flows_runs (heartbeat_at_ms)\n WHERE status = 'running'\n `\n }\n]\n\n/**\n * Creates the engine-store-owned objects, idempotently, in inventory order.\n *\n * `flows_runs` must already exist \u2014 every query in this service assumes a\n * migrated database, so `make` composes over one by construction. A read-only\n * client installs nothing.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const apply = (sql: SqlClient.SqlClient, writer: WriterService): Effect.Effect<void> =>\n // A read-only observer reads the schema it finds and installs nothing.\n isReadOnly(sql) ? Effect.void : Effect.forEach(\n statements,\n (statement) => writer.write(sql.unsafe(statement.ddl)).pipe(Effect.orDie),\n { discard: true }\n )\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBA,qBAA2B;AAE3B,aAAwB;AAuCjB,MAAM,aAAuC;AAAA,EAClD;AAAA,IACE,MAAM;AAAA,IACN,QACE;AAAA;AAAA,IAEF,UAAU,CAAC,UAAU,UAAU;AAAA,IAC/B,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,EAKP;AACF;AAYO,MAAM,QAAQ,CAAC,KAA0B;AAAA;AAAA,MAE9C,2BAAW,GAAG,IAAI,OAAO,OAAO,OAAO;AAAA,IACrC;AAAA,IACA,CAAC,cAAc,OAAO,MAAM,IAAI,OAAO,UAAU,GAAG,CAAC,EAAE,KAAK,OAAO,KAAK;AAAA,IACxE,EAAE,SAAS,KAAK;AAAA,EAClB;AAAA;",
6
6
  "names": []
7
7
  }