@smthrs/run-store 0.0.0-stage → 1.0.0-rc.3

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 (157) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +21 -0
  3. package/README.md +195 -2
  4. package/dist/cjs/AttemptStore.d.ts +359 -0
  5. package/dist/cjs/AttemptStore.d.ts.map +1 -0
  6. package/dist/cjs/AttemptStore.js +731 -0
  7. package/dist/cjs/AttemptStore.js.map +7 -0
  8. package/dist/cjs/Heartbeat.d.ts +46 -0
  9. package/dist/cjs/Heartbeat.d.ts.map +1 -0
  10. package/dist/cjs/Heartbeat.js +28 -0
  11. package/dist/cjs/Heartbeat.js.map +7 -0
  12. package/dist/cjs/Migrations.d.ts +42 -0
  13. package/dist/cjs/Migrations.d.ts.map +1 -0
  14. package/dist/cjs/Migrations.js +55 -0
  15. package/dist/cjs/Migrations.js.map +7 -0
  16. package/dist/cjs/Ownership.d.ts +275 -0
  17. package/dist/cjs/Ownership.d.ts.map +1 -0
  18. package/dist/cjs/Ownership.js +129 -0
  19. package/dist/cjs/Ownership.js.map +7 -0
  20. package/dist/cjs/RunStore.d.ts +571 -0
  21. package/dist/cjs/RunStore.d.ts.map +1 -0
  22. package/dist/cjs/RunStore.js +1079 -0
  23. package/dist/cjs/RunStore.js.map +7 -0
  24. package/dist/cjs/RunStoreMetrics.d.ts +151 -0
  25. package/dist/cjs/RunStoreMetrics.d.ts.map +1 -0
  26. package/dist/cjs/RunStoreMetrics.js +91 -0
  27. package/dist/cjs/RunStoreMetrics.js.map +7 -0
  28. package/dist/cjs/index.d.ts +47 -0
  29. package/dist/cjs/index.d.ts.map +1 -0
  30. package/dist/cjs/index.js +43 -0
  31. package/dist/cjs/index.js.map +7 -0
  32. package/dist/cjs/internal/Boundary.d.ts +80 -0
  33. package/dist/cjs/internal/Boundary.d.ts.map +1 -0
  34. package/dist/cjs/internal/Boundary.js +92 -0
  35. package/dist/cjs/internal/Boundary.js.map +7 -0
  36. package/dist/cjs/internal/Companion.d.ts +44 -0
  37. package/dist/cjs/internal/Companion.d.ts.map +1 -0
  38. package/dist/cjs/internal/Companion.js +50 -0
  39. package/dist/cjs/internal/Companion.js.map +7 -0
  40. package/dist/cjs/internal/SpanOutcome.d.ts +37 -0
  41. package/dist/cjs/internal/SpanOutcome.d.ts.map +1 -0
  42. package/dist/cjs/internal/SpanOutcome.js +62 -0
  43. package/dist/cjs/internal/SpanOutcome.js.map +7 -0
  44. package/dist/cjs/migrations/0001_initial.d.ts +15 -0
  45. package/dist/cjs/migrations/0001_initial.d.ts.map +1 -0
  46. package/dist/cjs/migrations/0001_initial.js +110 -0
  47. package/dist/cjs/migrations/0001_initial.js.map +7 -0
  48. package/dist/cjs/migrations/0002_lineage.d.ts +27 -0
  49. package/dist/cjs/migrations/0002_lineage.d.ts.map +1 -0
  50. package/dist/cjs/migrations/0002_lineage.js +43 -0
  51. package/dist/cjs/migrations/0002_lineage.js.map +7 -0
  52. package/dist/cjs/migrations/0003_execution_revisions.d.ts +15 -0
  53. package/dist/cjs/migrations/0003_execution_revisions.d.ts.map +1 -0
  54. package/dist/cjs/migrations/0003_execution_revisions.js +77 -0
  55. package/dist/cjs/migrations/0003_execution_revisions.js.map +7 -0
  56. package/dist/cjs/migrations/0004_waiting_request.d.ts +32 -0
  57. package/dist/cjs/migrations/0004_waiting_request.d.ts.map +1 -0
  58. package/dist/cjs/migrations/0004_waiting_request.js +42 -0
  59. package/dist/cjs/migrations/0004_waiting_request.js.map +7 -0
  60. package/dist/cjs/package.json +1 -0
  61. package/dist/cjs/test/TestRunStore.d.ts +17 -0
  62. package/dist/cjs/test/TestRunStore.d.ts.map +1 -0
  63. package/dist/cjs/test/TestRunStore.js +42 -0
  64. package/dist/cjs/test/TestRunStore.js.map +7 -0
  65. package/dist/esm/AttemptStore.d.ts +359 -0
  66. package/dist/esm/AttemptStore.d.ts.map +1 -0
  67. package/dist/esm/AttemptStore.js +835 -0
  68. package/dist/esm/AttemptStore.js.map +1 -0
  69. package/dist/esm/Heartbeat.d.ts +46 -0
  70. package/dist/esm/Heartbeat.d.ts.map +1 -0
  71. package/dist/esm/Heartbeat.js +46 -0
  72. package/dist/esm/Heartbeat.js.map +1 -0
  73. package/dist/esm/Migrations.d.ts +42 -0
  74. package/dist/esm/Migrations.d.ts.map +1 -0
  75. package/dist/esm/Migrations.js +56 -0
  76. package/dist/esm/Migrations.js.map +1 -0
  77. package/dist/esm/Ownership.d.ts +275 -0
  78. package/dist/esm/Ownership.d.ts.map +1 -0
  79. package/dist/esm/Ownership.js +299 -0
  80. package/dist/esm/Ownership.js.map +1 -0
  81. package/dist/esm/RunStore.d.ts +571 -0
  82. package/dist/esm/RunStore.d.ts.map +1 -0
  83. package/dist/esm/RunStore.js +1287 -0
  84. package/dist/esm/RunStore.js.map +1 -0
  85. package/dist/esm/RunStoreMetrics.d.ts +151 -0
  86. package/dist/esm/RunStoreMetrics.d.ts.map +1 -0
  87. package/dist/esm/RunStoreMetrics.js +120 -0
  88. package/dist/esm/RunStoreMetrics.js.map +1 -0
  89. package/dist/esm/index.d.ts +47 -0
  90. package/dist/esm/index.d.ts.map +1 -0
  91. package/dist/esm/index.js +47 -0
  92. package/dist/esm/index.js.map +1 -0
  93. package/dist/esm/internal/Boundary.d.ts +80 -0
  94. package/dist/esm/internal/Boundary.d.ts.map +1 -0
  95. package/dist/esm/internal/Boundary.js +109 -0
  96. package/dist/esm/internal/Boundary.js.map +1 -0
  97. package/dist/esm/internal/Companion.d.ts +44 -0
  98. package/dist/esm/internal/Companion.d.ts.map +1 -0
  99. package/dist/esm/internal/Companion.js +53 -0
  100. package/dist/esm/internal/Companion.js.map +1 -0
  101. package/dist/esm/internal/SpanOutcome.d.ts +37 -0
  102. package/dist/esm/internal/SpanOutcome.d.ts.map +1 -0
  103. package/dist/esm/internal/SpanOutcome.js +48 -0
  104. package/dist/esm/internal/SpanOutcome.js.map +1 -0
  105. package/dist/esm/migrations/0001_initial.d.ts +15 -0
  106. package/dist/esm/migrations/0001_initial.d.ts.map +1 -0
  107. package/dist/esm/migrations/0001_initial.js +88 -0
  108. package/dist/esm/migrations/0001_initial.js.map +1 -0
  109. package/dist/esm/migrations/0002_lineage.d.ts +27 -0
  110. package/dist/esm/migrations/0002_lineage.d.ts.map +1 -0
  111. package/dist/esm/migrations/0002_lineage.js +33 -0
  112. package/dist/esm/migrations/0002_lineage.js.map +1 -0
  113. package/dist/esm/migrations/0003_execution_revisions.d.ts +15 -0
  114. package/dist/esm/migrations/0003_execution_revisions.d.ts.map +1 -0
  115. package/dist/esm/migrations/0003_execution_revisions.js +58 -0
  116. package/dist/esm/migrations/0003_execution_revisions.js.map +1 -0
  117. package/dist/esm/migrations/0004_waiting_request.d.ts +32 -0
  118. package/dist/esm/migrations/0004_waiting_request.d.ts.map +1 -0
  119. package/dist/esm/migrations/0004_waiting_request.js +37 -0
  120. package/dist/esm/migrations/0004_waiting_request.js.map +1 -0
  121. package/dist/esm/test/TestRunStore.d.ts +17 -0
  122. package/dist/esm/test/TestRunStore.d.ts.map +1 -0
  123. package/dist/esm/test/TestRunStore.js +19 -0
  124. package/dist/esm/test/TestRunStore.js.map +1 -0
  125. package/docs/README.md +142 -0
  126. package/docs/api.md +901 -0
  127. package/docs/concepts/durable-values.md +140 -0
  128. package/docs/concepts/execution-revisions.md +53 -0
  129. package/docs/concepts/fencing.md +146 -0
  130. package/docs/concepts/leases.md +154 -0
  131. package/docs/concepts/liveness-evidence.md +142 -0
  132. package/docs/guides/cancel-a-run.md +156 -0
  133. package/docs/guides/claim-and-finish-a-run.md +175 -0
  134. package/docs/guides/compose-the-stores.md +142 -0
  135. package/docs/guides/observe-outcomes.md +182 -0
  136. package/docs/guides/record-step-attempts.md +170 -0
  137. package/docs/guides/recover-a-stalled-run.md +173 -0
  138. package/docs/guides/testing.md +141 -0
  139. package/docs/installation.md +109 -0
  140. package/docs/quickstart.md +201 -0
  141. package/docs/troubleshooting.md +255 -0
  142. package/package.json +183 -3
  143. package/src/AttemptStore.ts +1195 -0
  144. package/src/Heartbeat.ts +47 -0
  145. package/src/Migrations.ts +59 -0
  146. package/src/Ownership.ts +407 -0
  147. package/src/RunStore.ts +2063 -0
  148. package/src/RunStoreMetrics.ts +165 -0
  149. package/src/index.ts +51 -0
  150. package/src/internal/Boundary.ts +130 -0
  151. package/src/internal/Companion.ts +64 -0
  152. package/src/internal/SpanOutcome.ts +71 -0
  153. package/src/migrations/0001_initial.ts +114 -0
  154. package/src/migrations/0002_lineage.ts +38 -0
  155. package/src/migrations/0003_execution_revisions.ts +69 -0
  156. package/src/migrations/0004_waiting_request.ts +39 -0
  157. package/src/test/TestRunStore.ts +22 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/RunStore.ts"],
4
+ "sourcesContent": ["/**\n * Fenced run persistence and ownership compare-and-swap operations.\n *\n * Operations that accept or verify `LivenessEvidence` (`claim`,\n * `claimAndOwn`, `steal`, `heartbeat`, `requestCancel`, and `recoverClaim`)\n * take the caller's `nowMs` and judge it literally, including the lease cutoff\n * and exact `evidence.checkedAtMs === nowMs` rule. The lease operations\n * (`claim`, `claimAndOwn`, `steal`, `heartbeat`, and `recoverClaim`) also\n * refuse a `nowMs` that runs ahead of the injected Effect `Clock` by more than\n * `heartbeatSkewAllowance`: no composition produces one honestly, because the\n * caller and the store share a process, and such a reading is the one lever\n * that steals a fresh owner or pins a lease past the cutoff. A reading behind\n * the clock is admitted, since it only makes staleness judgments more\n * conservative and the monotonic heartbeat absorbs it. `requestCancel` keeps\n * the literal reading because its timestamp is request data, never a lease\n * predicate. Lifecycle stamps use the Effect `Clock`: `create` writes\n * `created_at_ms`, `activate` writes `started_at_ms` and `heartbeat_at_ms`,\n * and `transitionOwned` writes `finished_at_ms`. A row can therefore carry\n * readings from two clocks. Within the allowance the store trusts the caller's\n * clock, which is right for an in-process library over local SQLite and must\n * not cross a trust boundary.\n *\n * Arbitration itself \u2014 who holds the claim, who holds the lease, whether the\n * fence still stands \u2014 is delegated to `@smthrs/journal`'s injected\n * `Consensus` strategy. The run row mirrors the strategy's answer in the same\n * write transaction, and every ownership transition the strategy grants is\n * appended to the run's `run-store` companion journal stream in that\n * transaction (see {@link OwnershipTransition}); heartbeats renew the lease\n * and never enter the journal.\n *\n * @since 0.1.0\n */\n\nimport * as Dialect from \"@smthrs/database/Dialect\"\nimport { afterCommit, DatabaseError, DurableWriter, fromSqlError } from \"@smthrs/database/DurableWriter\"\nimport {\n type ClaimOutcome as LeaseClaimOutcome,\n Consensus,\n type ConsensusError,\n type LivenessEvidence,\n matchesEvidence,\n sameOwner\n} from \"@smthrs/journal/Consensus\"\nimport type { JournalError } from \"@smthrs/journal/Journal\"\nimport { isCompanionRunId } from \"@smthrs/journal/JournalEvent\"\nimport { OwnerId } from \"@smthrs/journal/OwnerId\"\nimport * as SqlConsensus from \"@smthrs/journal/SqlConsensus\"\nimport * as ObservabilityMetric from \"@smthrs/observability/Metric\"\nimport { Clock, Context, Duration, Effect, Layer, Metric, Schema } from \"effect\"\nimport * as SqlClient from \"effect/unstable/sql/SqlClient\"\nimport type * as SqlError from \"effect/unstable/sql/SqlError\"\nimport { heartbeatSkewAllowance, heartbeatStaleAfter } from \"./Heartbeat.ts\"\nimport * as Boundary from \"./internal/Boundary.ts\"\nimport * as Companion from \"./internal/Companion.ts\"\nimport { observeExit, observeOutcome } from \"./internal/SpanOutcome.ts\"\nimport * as RunStoreMetrics from \"./RunStoreMetrics.ts\"\n\n/** JSON text carrying an arbitrary decoded value. */\nconst UnknownFromJsonString = Schema.fromJsonString(Schema.Unknown)\n\n/**\n * A UTF-16 surrogate without its partner. The journal uses the same ES2022\n * compatible check because `String.prototype.isWellFormed` needs ES2024.\n */\nconst loneSurrogate = /[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]/\n\n/** A non-empty identifier whose UTF-16 encoding survives SQLite unchanged. */\nconst wellFormedIdentifier = (value: string): boolean => value.length > 0 && !loneSurrogate.test(value)\n\nconst NonNegativeSafeInt = Schema.Int.check(\n Schema.isGreaterThanOrEqualTo(0),\n Schema.isLessThanOrEqualTo(Number.MAX_SAFE_INTEGER)\n)\n\nconst durableIdentifier = Schema.NonEmptyString.check(\n Schema.isMaxLength(Boundary.maximumIdentifierLength),\n Schema.makeFilter(\n (value: string) => wellFormedIdentifier(value) && Boundary.isDurableText(value),\n { title: \"durableIdentifier\" }\n )\n)\n\n/**\n * Maximum nesting admitted for executable run state.\n *\n * @since 1.0.0-rc.0\n * @category constants\n */\nexport const maximumRunJsonDepth = 128\n\n/**\n * Maximum values and members admitted for executable run state.\n *\n * @since 1.0.0-rc.0\n * @category constants\n */\nexport const maximumRunJsonNodes = 100_000\n\nconst runJsonLimits: Boundary.JsonLimits = {\n maxDepth: maximumRunJsonDepth,\n maxMembers: maximumRunJsonNodes,\n maxNodes: maximumRunJsonNodes\n}\n\n/**\n * Stable run states understood by the durability layer.\n *\n * @since 0.1.0\n * @category models\n */\nexport const RunStatus = Schema.Literals([\n \"pending\",\n \"running\",\n \"suspended\",\n \"completed\",\n \"failed\",\n \"cancelled\"\n])\n\n/**\n * A stable run state.\n *\n * @since 0.1.0\n * @category models\n */\nexport type RunStatus = typeof RunStatus.Type\n\n/**\n * The run states a run never leaves.\n *\n * Named because two rules are written against exactly this set: a settled run\n * refuses new cancellation intent ({@link RequestCancelOutcome}), and a\n * settled row is never claimed, activated, or swept.\n *\n * @since 0.1.0\n * @category models\n */\nexport const TerminalRunStatus = Schema.Literals([\"completed\", \"failed\", \"cancelled\"])\n\n/**\n * The type of {@link TerminalRunStatus}.\n *\n * @since 0.1.0\n * @category models\n */\nexport type TerminalRunStatus = typeof TerminalRunStatus.Type\n\n/**\n * Whether a status is one a run never leaves.\n *\n * @since 0.1.0\n * @category models\n */\nexport const isTerminalRunStatus = (status: RunStatus): status is TerminalRunStatus =>\n status === \"completed\" || status === \"failed\" || status === \"cancelled\"\n\n/**\n * Stable failure codes surfaced by `RunStore`.\n *\n * @since 0.1.0\n * @category errors\n */\nexport const RunStoreErrorCode = Schema.Literals([\n \"invalid_run\",\n \"not_found_row\",\n \"constraint\",\n \"decode_failed\",\n \"persistence_failed\"\n])\n\n/**\n * A stable `RunStore` failure code.\n *\n * @since 0.1.0\n * @category errors\n */\nexport type RunStoreErrorCode = typeof RunStoreErrorCode.Type\n\n/**\n * A normalized run persistence failure.\n *\n * Compare-and-swap competition is represented by successful outcome values,\n * never by this error channel.\n *\n * The identity string equals the defining module path, which is how every\n * tagged identity in this package is named.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class RunStoreError extends Schema.TaggedError<RunStoreError>()(\"@smthrs/run-store/RunStoreError\", {\n code: RunStoreErrorCode,\n method: Schema.String,\n message: Schema.String,\n cause: Schema.Unknown\n}) {}\n\n/**\n * The exact persisted fields guarded by a claim and its later activation.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface RunSnapshot {\n readonly status: RunStatus\n readonly owner: OwnerId | null\n readonly heartbeatAtMs: number | null\n}\n\n/**\n * A decoded row in `flows_runs`.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface RunRow extends RunSnapshot {\n readonly runId: string\n readonly createdAtMs: number\n readonly startedAtMs: number | null\n readonly finishedAtMs: number | null\n readonly claim: OwnerId | null\n readonly claimedAtMs: number | null\n readonly parentRunId: string | null\n readonly cancelRequestedAtMs: number | null\n /**\n * The trampoline lineage this run is a round of, and which round it is.\n * `null` on every run that is not part of a lineage, which reads as round 0\n * of a lineage of one.\n *\n * Optional on the interface so a caller that builds a `RunRow` by hand \u2014\n * a time-travel fixture, a recovery double \u2014 keeps compiling: the pair was\n * added by an append-only migration, and every reader must already tolerate\n * a row written before it.\n */\n readonly lineageId?: string | null | undefined\n readonly roundOrdinal?: number | null | undefined\n readonly stateJson: string\n}\n\n/**\n * Metadata recorded when a run row is created.\n *\n * `parentRunId` is the ancestry edge a fork, rewind, child, or later\n * trampoline round carries. It is a column rather than a `state_json` field\n * because ancestry is walked in SQL.\n *\n * `lineageId` and `roundOrdinal` are the trampoline pair: which lineage a run\n * is a round of, and which round. Both absent \u2014 the ordinary case \u2014 means the\n * run is a lineage of one, and is read back as round 0 of itself.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface CreateOptions {\n readonly parentRunId?: string | undefined\n readonly lineageId?: string | undefined\n readonly roundOrdinal?: number | undefined\n}\n\n/**\n * An extra compare-and-swap predicate over first-class run metadata.\n *\n * A guard is the seam for lifecycle rules a harness must enforce atomically \u2014\n * `{ cancelRequested: \"absent\" }` is the \"do not finalize a run someone asked\n * to cancel\" rule, expressed as SQL rather than as a read-then-write race.\n *\n * @since 0.1.0\n * @category models\n */\nexport const TransitionGuard = Schema.Struct({\n cancelRequested: Schema.optional(Schema.Literals([\"absent\", \"present\"] as const))\n})\n\n/**\n * An extra compare-and-swap predicate over first-class run metadata.\n *\n * @since 0.1.0\n * @category models\n */\nexport type TransitionGuard = typeof TransitionGuard.Type\n\n/**\n * Result of recording a cancellation request.\n *\n * The request is deliberately unfenced: any observer may ask, and the owner\n * decides. `requestedAtMs` is the winning request, so a repeat request reports\n * the original time rather than overwriting it.\n *\n * @since 0.1.0\n * @category models\n */\nexport type RequestCancelOutcome =\n | { readonly _tag: \"CancelRequested\"; readonly requestedAtMs: number }\n | { readonly _tag: \"AlreadyRequested\"; readonly requestedAtMs: number }\n | { readonly _tag: \"NotFound\" }\n /**\n * The run had already settled, so nothing was recorded. A terminal run has\n * no owner and no drive to observe a request, so writing one would leave\n * intent nothing ever acts on \u2014 and a reader that takes the column as live\n * intent (`RunDriver.inheritParentCancellation`) would cancel children a\n * `completed` parent had finished with. The status says which ending the\n * request lost to.\n */\n | { readonly _tag: \"Terminal\"; readonly status: TerminalRunStatus }\n\n/**\n * Result of acquiring claim columns for a later activation.\n *\n * @since 0.1.0\n * @category models\n */\nexport type ClaimOutcome =\n | { readonly _tag: \"Claimed\"; readonly claimedAtMs: number }\n | { readonly _tag: \"NotFound\" }\n | { readonly _tag: \"AlreadyClaimed\" }\n | { readonly _tag: \"HeartbeatFresh\" }\n | { readonly _tag: \"SnapshotChanged\" }\n\n/**\n * Result of stealing an exact run snapshot after liveness evidence is checked.\n *\n * `LivenessUnconfirmed` means the supplied evidence did not match the\n * snapshot's owner, the host relation its `kind` requires, or `nowMs`, so the\n * steal was refused before any compare-and-swap ran. The other tags have the\n * same meaning as {@link ClaimOutcome} after the comparison runs.\n *\n * @since 1.0.0-rc.0\n * @category models\n */\nexport type StealOutcome = ClaimOutcome | { readonly _tag: \"LivenessUnconfirmed\" }\n\n/**\n * Result of claiming and activating ownership in one compare-and-swap.\n *\n * @since 0.1.0\n * @category models\n */\nexport type ClaimAndOwnOutcome =\n | { readonly _tag: \"Activated\" }\n | { readonly _tag: \"NotFound\" }\n | { readonly _tag: \"AlreadyClaimed\" }\n | { readonly _tag: \"HeartbeatFresh\" }\n | { readonly _tag: \"SnapshotChanged\" }\n /**\n * The caller's exact snapshot is still current, its different owner has a\n * stale heartbeat, and no matching `LivenessEvidence` was supplied, so no\n * compare-and-swap ran. Re-reading and retrying cannot make progress; supply\n * evidence, or use `steal`. A current fresh heartbeat instead reports\n * `HeartbeatFresh`, because evidence cannot satisfy the stale-lease predicate.\n */\n | { readonly _tag: \"EvidenceRequired\" }\n\ntype ClaimLossOutcome = Exclude<ClaimOutcome, { readonly _tag: \"Claimed\" }>\n\n/**\n * Result of activating a held claim.\n *\n * @since 0.1.0\n * @category models\n */\nexport type ActivateOutcome =\n | { readonly _tag: \"Activated\" }\n | { readonly _tag: \"ClaimLost\" }\n | { readonly _tag: \"SnapshotChanged\" }\n\n/**\n * Result of clearing a held claim.\n *\n * @since 0.1.0\n * @category models\n */\nexport type AbandonClaimOutcome =\n | { readonly _tag: \"Abandoned\" }\n | { readonly _tag: \"ClaimLost\" }\n\n/**\n * Result of clearing an exact stale claim after its claimant was proven dead.\n *\n * @since 0.1.0\n * @category models\n */\nexport type RecoverClaimOutcome =\n | { readonly _tag: \"Recovered\" }\n | { readonly _tag: \"NotFound\" }\n | { readonly _tag: \"ClaimFresh\" }\n | { readonly _tag: \"ClaimChanged\" }\n | { readonly _tag: \"LivenessUnconfirmed\" }\n\n/**\n * Result of a fenced ownership heartbeat.\n *\n * @since 0.1.0\n * @category models\n */\nexport type HeartbeatOutcome =\n | { readonly _tag: \"Updated\" }\n | { readonly _tag: \"FenceLost\" }\n | { readonly _tag: \"NotFound\" }\n\n/**\n * Result of a fenced owned transition.\n *\n * @since 0.1.0\n * @category models\n */\nexport type TransitionOutcome =\n | { readonly _tag: \"Transitioned\" }\n | { readonly _tag: \"FenceLost\" }\n | { readonly _tag: \"NotFound\" }\n | { readonly _tag: \"GuardFailed\" }\n\n/**\n * An ownership transition the consensus strategy granted on a run.\n *\n * `claimed` and `stolen` reserve the run for a claimant, `activated` promotes\n * a claim to ownership, `released` gives a claim or ownership back, and\n * `expired` clears a dead claimant's stale claim. Each is appended as a\n * `flows.consensus.<transition>` fact, with payload `{ runId, owner,\n * grantedAtMs }`, to the run's companion stream\n * `JournalEvent.companionRunId(\"run-store\", runId)` in the transaction that\n * made it; a rolled-back transition leaves no fact. Heartbeats are not\n * transitions and never enter the journal.\n *\n * The facts live beside the run's own stream rather than in it, so a consumer\n * that reads the run's stream by position never sees one, and a rewind or\n * compaction of that stream never truncates one. They are recorded when the\n * caller runs with a `Journal` in its context.\n *\n * @since 1.0.0\n * @category models\n */\nexport const OwnershipTransition = Schema.Literals([\"claimed\", \"activated\", \"released\", \"stolen\", \"expired\"])\n\n/**\n * The type of {@link OwnershipTransition}.\n *\n * @since 1.0.0\n * @category models\n */\nexport type OwnershipTransition = typeof OwnershipTransition.Type\n\n/**\n * The name of the run store's journal companion stream: every fact the store\n * records about a run goes to `JournalEvent.companionRunId(companionStream,\n * runId)`.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const companionStream: string = Companion.stream\n\n/**\n * The journal event type an ownership transition is appended under.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ownershipEventType = (transition: OwnershipTransition): string => `flows.consensus.${transition}`\n\n/**\n * Fenced persistence operations for durable runs.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface Service {\n readonly create: (\n runId: string,\n stateJson: string,\n options?: CreateOptions | undefined\n ) => Effect.Effect<void, RunStoreError>\n readonly get: (runId: string) => Effect.Effect<RunRow, RunStoreError>\n /**\n * Reads every persisted round of this run's trampoline lineage, in ordinal\n * order, from any member execution ID. An older root with null lineage\n * columns is included as round zero. Missing IDs return an empty array.\n * Fork ancestry alone does not join lineages. This is a snapshot read;\n * callers making lifecycle decisions must hold their shared write transaction.\n */\n readonly lineage: (runId: string) => Effect.Effect<ReadonlyArray<RunRow>, RunStoreError>\n /** Reads only the highest persisted round; fails with not_found_row for an unknown ID. */\n readonly latestRound: (runId: string) => Effect.Effect<RunRow, RunStoreError>\n /** Records an unfenced cancellation request that later guarded transitions observe. */\n readonly requestCancel: (runId: string, nowMs: number) => Effect.Effect<RequestCancelOutcome, RunStoreError>\n /**\n * Records the first owner observation of durable intent. Returns false if\n * intent is absent or the running owner fence no longer matches.\n */\n readonly acknowledgeCancel: (runId: string, owner: OwnerId, nowMs: number) => Effect.Effect<boolean, RunStoreError>\n /**\n * Resolves and requests every non-terminal round in one write transaction.\n * Terminal is returned only when the entire logical run has settled; its\n * status is the last round's status. Child-DAG cascading belongs to the\n * engine. Ordinary requestCancel remains the explicitly round-scoped port.\n */\n readonly requestCancelLineage: (runId: string, nowMs: number) => Effect.Effect<RequestCancelOutcome, RunStoreError>\n /**\n * Claims an exact pending or suspended snapshot for a later `activate`.\n * `nowMs` becomes `claimed_at_ms`, so it must not run ahead of the Effect\n * `Clock` by more than `heartbeatSkewAllowance`; a reading that does fails\n * with `invalid_run`.\n */\n readonly claim: (\n runId: string,\n expected: RunSnapshot,\n claimant: OwnerId,\n nowMs: number\n ) => Effect.Effect<ClaimOutcome, RunStoreError>\n /**\n * Claims and activates an exact snapshot atomically under the supplied owner.\n * Replacing a different running owner also requires matching liveness evidence.\n * A composition builds that `LivenessEvidence` with a `LivenessProbe`.\n * `evidence.checkedAtMs` must equal `nowMs` exactly, so evidence probed at T\n * is refused when this operation is called at T+1. `nowMs` is the lease\n * cutoff and the new heartbeat, so it must not run ahead of the Effect\n * `Clock` by more than `heartbeatSkewAllowance`; a reading that does fails\n * with `invalid_run`.\n */\n readonly claimAndOwn: (\n runId: string,\n expected: RunSnapshot,\n owner: OwnerId,\n nowMs: number,\n evidence?: LivenessEvidence | undefined\n ) => Effect.Effect<ClaimAndOwnOutcome, RunStoreError>\n readonly activate: (\n runId: string,\n claimant: OwnerId,\n claimedAtMs: number,\n expected: RunSnapshot\n ) => Effect.Effect<ActivateOutcome, RunStoreError>\n readonly abandonClaim: (\n runId: string,\n claimant: OwnerId,\n claimedAtMs: number\n ) => Effect.Effect<AbandonClaimOutcome, RunStoreError>\n /**\n * Recovers an exact stale claim after matching its claimant and liveness\n * evidence. A composition builds `LivenessEvidence` with a `LivenessProbe`.\n * `evidence.checkedAtMs` must equal `nowMs` exactly, so evidence probed at T\n * is refused when this operation is called at T+1. `nowMs` is the staleness\n * cutoff, so it must not run ahead of the Effect `Clock` by more than\n * `heartbeatSkewAllowance`; a reading that does fails with `invalid_run`.\n * `claimedAtMs` is the fence token compared against the row and carries no\n * such bound.\n */\n readonly recoverClaim: (\n runId: string,\n staleClaimant: OwnerId,\n claimedAtMs: number,\n observer: OwnerId,\n nowMs: number,\n evidence: LivenessEvidence\n ) => Effect.Effect<RecoverClaimOutcome, RunStoreError>\n /**\n * Renews the owner's lease. The write is monotonic, so a reading behind the\n * persisted heartbeat still reports `Updated` without moving it back. `nowMs`\n * must not run ahead of the Effect `Clock` by more than\n * `heartbeatSkewAllowance`; a reading that does fails with `invalid_run`\n * instead of pinning the lease past the cutoff.\n */\n readonly heartbeat: (\n runId: string,\n owner: OwnerId,\n nowMs: number\n ) => Effect.Effect<HeartbeatOutcome, RunStoreError>\n /**\n * Reconfirms an exclusively owned lease after a missed heartbeat budget.\n * Uses the same validation and transactional mirror as `heartbeat`, but\n * refuses a pending takeover claim even before the claimant activates.\n */\n readonly reconfirm: (\n runId: string,\n owner: OwnerId,\n nowMs: number\n ) => Effect.Effect<HeartbeatOutcome, RunStoreError>\n readonly transitionOwned: (\n runId: string,\n owner: OwnerId,\n toStatus: RunStatus,\n stateJson?: string | undefined,\n guard?: TransitionGuard | undefined\n ) => Effect.Effect<TransitionOutcome, RunStoreError>\n /**\n * Steals an exact run snapshot after matching liveness evidence for its\n * recorded owner. A composition builds `LivenessEvidence` with a\n * `LivenessProbe`. `evidence.checkedAtMs` must equal `nowMs` exactly, so\n * evidence probed at T is refused when this operation is called at T+1.\n * `LivenessUnconfirmed` means the evidence did not match and no\n * compare-and-swap ran. `nowMs` is the staleness cutoff, so it must not run\n * ahead of the Effect `Clock` by more than `heartbeatSkewAllowance`; a\n * reading that does fails with `invalid_run` before any comparison.\n */\n readonly steal: (\n runId: string,\n expected: RunSnapshot,\n claimant: OwnerId,\n nowMs: number,\n evidence: LivenessEvidence\n ) => Effect.Effect<StealOutcome, RunStoreError>\n}\n\n/**\n * Service tag for fenced run persistence.\n *\n * The identity string equals the defining module path, which is how every\n * service in this package is named.\n *\n * @since 0.1.0\n * @category services\n */\nexport class RunStore extends Context.Service<RunStore, Service>()(\"@smthrs/run-store/RunStore\") {}\n\nconst claimed = (claimedAtMs: number): ClaimOutcome => ({ _tag: \"Claimed\", claimedAtMs })\nconst notFound = { _tag: \"NotFound\" } as const\nconst alreadyClaimed = { _tag: \"AlreadyClaimed\" } as const\nconst heartbeatFresh = { _tag: \"HeartbeatFresh\" } as const\nconst snapshotChanged = { _tag: \"SnapshotChanged\" } as const\nconst evidenceRequired = { _tag: \"EvidenceRequired\" } as const\nconst activated = { _tag: \"Activated\" } as const\nconst claimLost = { _tag: \"ClaimLost\" } as const\nconst abandoned = { _tag: \"Abandoned\" } as const\nconst recovered = { _tag: \"Recovered\" } as const\nconst claimFresh = { _tag: \"ClaimFresh\" } as const\nconst claimChanged = { _tag: \"ClaimChanged\" } as const\nconst livenessUnconfirmed = { _tag: \"LivenessUnconfirmed\" } as const\nconst updated = { _tag: \"Updated\" } as const\nconst fenceLost = { _tag: \"FenceLost\" } as const\nconst transitioned = { _tag: \"Transitioned\" } as const\nconst guardFailed = { _tag: \"GuardFailed\" } as const\n\n/**\n * The staleness cutoff in milliseconds, derived from the one definition rather\n * than restated. `Ownership` imports `RunStore`, so the shared constants live\n * in the `Heartbeat` leaf both can reach.\n */\nconst heartbeatStaleAfterMs = Duration.toMillis(heartbeatStaleAfter)\nconst heartbeatSkewAllowanceMs = Duration.toMillis(heartbeatSkewAllowance)\nconst terminalStatuses: ReadonlySet<RunStatus> = new Set([\"completed\", \"failed\", \"cancelled\"])\n\nconst DatabaseRunRow = Schema.Struct({\n runId: durableIdentifier,\n status: RunStatus,\n createdAtMs: NonNegativeSafeInt,\n startedAtMs: Schema.NullOr(NonNegativeSafeInt),\n finishedAtMs: Schema.NullOr(NonNegativeSafeInt),\n ownerHostId: Schema.NullOr(durableIdentifier),\n ownerPid: Schema.NullOr(NonNegativeSafeInt),\n ownerNonce: Schema.NullOr(durableIdentifier),\n heartbeatAtMs: Schema.NullOr(NonNegativeSafeInt),\n claimHostId: Schema.NullOr(durableIdentifier),\n claimPid: Schema.NullOr(NonNegativeSafeInt),\n claimNonce: Schema.NullOr(durableIdentifier),\n claimedAtMs: Schema.NullOr(NonNegativeSafeInt),\n parentRunId: Schema.NullOr(durableIdentifier),\n cancelRequestedAtMs: Schema.NullOr(NonNegativeSafeInt),\n lineageId: Schema.NullOr(durableIdentifier),\n roundOrdinal: Schema.NullOr(NonNegativeSafeInt),\n stateJson: Schema.String\n})\n\ntype DatabaseRunRow = typeof DatabaseRunRow.Type\n\nconst runStoreError = (\n method: string,\n code: RunStoreErrorCode,\n message: string,\n cause: unknown\n): RunStoreError =>\n new RunStoreError({\n code,\n method,\n message: `${code}: RunStore.${method}: ${message}`,\n cause\n })\n\n/**\n * The normalized database failure behind a strategy or journal failure, found\n * by following `cause` chains the way the durable writer's retry\n * classification does. A wrapper that carries no database failure at all \u2014\n * a closed journal, say \u2014 has no category to report.\n */\nconst databaseFailure = (cause: unknown): DatabaseError | undefined => {\n let current: unknown = cause\n for (let depth = 0; depth < 8 && typeof current === \"object\" && current !== null; depth++) {\n if (current instanceof DatabaseError) return current\n current = (current as { readonly cause?: unknown }).cause\n }\n return undefined\n}\n\nconst persistenceError = (\n method: string,\n cause: RunStoreError | DatabaseError | ConsensusError | JournalError\n): RunStoreError => {\n if (Schema.is(RunStoreError)(cause)) return cause\n const database = cause instanceof DatabaseError ? cause : databaseFailure(cause.cause)\n const code = database?.code === \"constraint\"\n ? \"constraint\"\n : \"persistence_failed\"\n return runStoreError(method, code, \"database operation failed\", {\n category: code,\n // Keep the classification for outer transaction retries, never driver\n // causes that may contain executable state or bound SQL values.\n cause: new DatabaseError({ code: database?.code ?? \"unknown\" })\n })\n}\n\nconst invalidRunError = (\n method: string,\n causeOrField: unknown,\n detail = \"violates the durable contract\"\n): RunStoreError =>\n runStoreError(\n method,\n \"invalid_run\",\n \"run input is invalid\",\n typeof causeOrField === \"string\" ? { field: causeOrField, detail } : causeOrField\n )\n\nconst stateAdmission = (value: unknown) => Boundary.admitJsonText(value, runJsonLimits)\n\nconst isJsonString = (value: unknown): value is string =>\n Schema.decodeUnknownResult(UnknownFromJsonString)(value)._tag === \"Success\"\n\nconst validOwner = (value: unknown): value is OwnerId =>\n Schema.is(OwnerId)(value) &&\n Boundary.isDurableText(value.hostId) &&\n Boundary.isDurableText(value.nonce) &&\n Number.isSafeInteger(value.pid) &&\n value.pid >= 0\n\n/** Caller timestamps are SQLite-safe, non-negative integer millisecond readings. */\nconst validTimestamp = (value: number): boolean => Number.isSafeInteger(value) && value >= 0\n\nconst inertRecord = (input: unknown, allowed?: ReadonlySet<string>): Record<string, unknown> | undefined => {\n if (typeof input !== \"object\" || input === null) return undefined\n try {\n const prototype = Object.getPrototypeOf(input)\n if (prototype !== Object.prototype && prototype !== null) return undefined\n const fields: Record<string, unknown> = Object.create(null)\n for (const key of Reflect.ownKeys(input)) {\n const descriptor = Object.getOwnPropertyDescriptor(input, key)\n if (descriptor === undefined || !descriptor.enumerable) continue\n if (!(\"value\" in descriptor) || (allowed !== undefined && (typeof key !== \"string\" || !allowed.has(key)))) {\n return undefined\n }\n if (typeof key === \"string\") fields[key] = descriptor.value\n }\n return fields\n } catch {\n return undefined\n }\n}\n\nconst snapshotOwner = (method: string, field: string, input: unknown): Effect.Effect<OwnerId, RunStoreError> =>\n Effect.suspend(() => {\n const fields = inertRecord(input, new Set([\"hostId\", \"pid\", \"nonce\"]))\n if (fields === undefined) {\n return Effect.fail(invalidRunError(method, field, \"must be an inert owner record\"))\n }\n const owner = Object.freeze({\n hostId: fields.hostId,\n pid: fields.pid,\n nonce: fields.nonce\n })\n return validOwner(owner)\n ? Effect.succeed(owner)\n : Effect.fail(invalidRunError(method, field, \"must be a valid owner identity\"))\n })\n\nconst snapshotRunId = (method: string, input: unknown): Effect.Effect<string, RunStoreError> =>\n typeof input === \"string\" && wellFormedIdentifier(input) && Boundary.isDurableText(input)\n ? Effect.succeed(input)\n : Effect.fail(invalidRunError(method, \"runId\", \"must be well-formed durable text\"))\n\nconst snapshotTimestamp = (\n method: string,\n field: string,\n input: unknown\n): Effect.Effect<number, RunStoreError> =>\n typeof input === \"number\" && validTimestamp(input)\n ? Effect.succeed(input)\n : Effect.fail(\n invalidRunError(method, field, \"must be a non-negative safe integer\")\n )\n\n/**\n * Admits a caller's lease reading. Once admitted the reading is authoritative:\n * it is the cutoff the lease predicate compares against and the stamp the row\n * keeps. The store refuses only a reading that runs ahead of its own `Clock`\n * by more than `heartbeatSkewAllowance`, because no composition produces one\n * honestly (the caller and the store share a process) and it is the one lever\n * that steals a fresh owner or pins a lease past the cutoff. A reading behind\n * the clock is admitted: it makes every staleness judgment more conservative,\n * and `heartbeat`'s monotonic write absorbs it.\n */\nconst snapshotLeaseReading = (\n method: string,\n field: string,\n input: unknown,\n cause: { readonly runId: string; readonly claimedAtMs?: number }\n): Effect.Effect<number, RunStoreError> =>\n Effect.gen(function*() {\n const nowMs = yield* snapshotTimestamp(method, field, input)\n const clockMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor))\n if (nowMs > clockMs + heartbeatSkewAllowanceMs) {\n return yield* Effect.fail(\n invalidRunError(method, {\n ...cause,\n field,\n nowMs,\n clockMs,\n detail: \"runs ahead of the store clock by more than the heartbeat skew allowance\"\n })\n )\n }\n return nowMs\n })\n\nconst snapshotState = (\n method: string,\n input: unknown,\n cause: unknown\n): Effect.Effect<string, RunStoreError> => {\n const admitted = stateAdmission(input)\n return admitted.ok\n ? Effect.succeed(admitted.value)\n : Effect.fail(\n invalidRunError(\n method,\n cause,\n admitted.complaint\n )\n )\n}\n\nconst snapshotCreateOptions = (\n input: unknown\n): Effect.Effect<\n Readonly<{ parentRunId: string | null; lineageId: string | null; roundOrdinal: number | null }>,\n RunStoreError\n> =>\n Effect.suspend(() => {\n const value = input ?? {}\n const fields = inertRecord(value, new Set([\"parentRunId\", \"lineageId\", \"roundOrdinal\"]))\n if (fields === undefined) {\n return Effect.fail(invalidRunError(\"create\", \"options\", \"must be an inert data record\"))\n }\n const rawParent = fields.parentRunId\n const rawLineage = fields.lineageId\n const rawRound = fields.roundOrdinal\n const parentRunId = rawParent === undefined ? null : rawParent\n const lineageId = rawLineage === undefined ? null : rawLineage\n const roundOrdinal = rawRound === undefined ? null : rawRound\n if (\n (parentRunId !== null &&\n (typeof parentRunId !== \"string\" ||\n !wellFormedIdentifier(parentRunId) ||\n !Boundary.isDurableText(parentRunId))) ||\n (lineageId !== null &&\n (typeof lineageId !== \"string\" ||\n !wellFormedIdentifier(lineageId) ||\n !Boundary.isDurableText(lineageId))) ||\n (roundOrdinal !== null && (typeof roundOrdinal !== \"number\" || !validTimestamp(roundOrdinal))) ||\n (lineageId === null) !== (roundOrdinal === null)\n ) return Effect.fail(invalidRunError(\"create\", \"options\", \"contains invalid ancestry or lineage\"))\n return Effect.succeed(Object.freeze({ parentRunId, lineageId, roundOrdinal }))\n })\n\n/**\n * Structural admission for a value typed as a required-field record. `RunRow`\n * extends `RunSnapshot`, so a row from `get` must pass wherever an expected\n * snapshot is typed: extra own data fields are ignored, while a non-plain\n * prototype, any enumerable accessor, or a missing or inherited required field\n * is still refused. The required fields come from one guarded own-descriptor snapshot.\n */\nconst inertFields = (input: unknown, required: ReadonlyArray<string>): Record<string, unknown> | undefined => {\n const fields = inertRecord(input)\n return fields !== undefined && required.every((key) => Object.hasOwn(fields, key)) ? fields : undefined\n}\n\n// Admission ties ownership to status; internal consumers never see the\n// impossible combinations permitted by the public input record.\ntype RunningSnapshot = {\n readonly status: \"running\"\n readonly owner: OwnerId\n readonly heartbeatAtMs: number\n}\n\ntype AdmittedSnapshot = RunningSnapshot | {\n readonly status: Exclude<RunStatus, \"running\">\n readonly owner: null\n readonly heartbeatAtMs: null\n}\n\nconst snapshotExpected = (\n method: string,\n input: unknown\n): Effect.Effect<AdmittedSnapshot, RunStoreError> =>\n Effect.gen(function*() {\n const fields = inertFields(input, [\"status\", \"owner\", \"heartbeatAtMs\"])\n if (fields === undefined) {\n return yield* Effect.fail(invalidRunError(method, \"expected\", \"must be an inert snapshot record\"))\n }\n const status = fields.status\n const rawOwner = fields.owner\n const heartbeatAtMs = fields.heartbeatAtMs\n if (\n !Schema.is(RunStatus)(status) ||\n (heartbeatAtMs !== null && (typeof heartbeatAtMs !== \"number\" || !validTimestamp(heartbeatAtMs)))\n ) {\n return yield* Effect.fail(invalidRunError(method, \"expected\", \"contains an invalid status or heartbeat\"))\n }\n const owner = rawOwner === null ? null : yield* snapshotOwner(method, \"expected.owner\", rawOwner)\n if (status === \"running\") {\n if (owner === null || heartbeatAtMs === null) {\n return yield* Effect.fail(invalidRunError(method, \"expected\", \"violates ownership invariants\"))\n }\n return Object.freeze({ status, owner, heartbeatAtMs })\n } else {\n if (owner !== null || heartbeatAtMs !== null) {\n return yield* Effect.fail(invalidRunError(method, \"expected\", \"violates ownership invariants\"))\n }\n return Object.freeze({ status, owner, heartbeatAtMs })\n }\n })\n\nconst snapshotEvidence = (\n method: string,\n input: unknown\n): Effect.Effect<LivenessEvidence, RunStoreError> =>\n Effect.gen(function*() {\n const fields = inertRecord(input, new Set([\"expectedOwner\", \"checkedAtMs\", \"kind\"]))\n if (fields === undefined) {\n return yield* Effect.fail(invalidRunError(method, \"evidence\", \"must be an inert evidence record\"))\n }\n const expectedOwner = yield* snapshotOwner(method, \"evidence.expectedOwner\", fields.expectedOwner)\n const checkedAtMs = yield* snapshotTimestamp(method, \"evidence.checkedAtMs\", fields.checkedAtMs)\n const kind = fields.kind\n if (\n kind !== \"same-host-pid-dead\" &&\n kind !== \"cross-host-unreachable-stale\" &&\n kind !== \"lease-expired\"\n ) return yield* Effect.fail(invalidRunError(method, \"evidence.kind\"))\n return Object.freeze({ expectedOwner, checkedAtMs, kind })\n })\n\nconst snapshotGuard = (\n input: unknown\n): Effect.Effect<TransitionGuard | undefined, RunStoreError> =>\n Effect.suspend(() => {\n if (input === undefined) return Effect.succeed(undefined)\n const fields = inertRecord(input, new Set([\"cancelRequested\"]))\n if (fields === undefined) {\n return Effect.fail(invalidRunError(\"transitionOwned\", \"guard\", \"must be an inert exact guard\"))\n }\n const cancelRequested = fields.cancelRequested\n if (cancelRequested !== undefined && cancelRequested !== \"absent\" && cancelRequested !== \"present\") {\n return Effect.fail(invalidRunError(\"transitionOwned\", \"guard.cancelRequested\"))\n }\n return Effect.succeed(Object.freeze(cancelRequested === undefined ? {} : { cancelRequested }))\n })\n\nconst ownerFromColumns = (\n hostId: string | null,\n pid: number | null,\n nonce: string | null\n): OwnerId | null | undefined => {\n if (hostId === null && pid === null && nonce === null) return null\n if (hostId !== null && pid !== null && nonce !== null) {\n // DatabaseRunRow already admitted all three columns through the stricter\n // durable identifier and safe-integer schemas.\n return Object.freeze({ hostId, pid, nonce })\n }\n return undefined\n}\n\nconst rowMatchesClaim = (row: DatabaseRunRow, claimant: OwnerId, claimedAtMs: number): boolean =>\n row.claimHostId === claimant.hostId &&\n row.claimPid === claimant.pid &&\n row.claimNonce === claimant.nonce &&\n row.claimedAtMs === claimedAtMs\n\n/**\n * The exact predicate the ownership compare-and-swap used to compile into its\n * UPDATE, evaluated in the serialized write transaction instead: the row must\n * still match the caller's expected snapshot.\n */\nconst rowMatchesSnapshot = (row: DatabaseRunRow, expected: AdmittedSnapshot): boolean =>\n row.status === expected.status &&\n row.ownerHostId === (expected.owner?.hostId ?? null) &&\n row.ownerPid === (expected.owner?.pid ?? null) &&\n row.ownerNonce === (expected.owner?.nonce ?? null) &&\n row.heartbeatAtMs === expected.heartbeatAtMs\n\nconst rowOwnedBy = (row: DatabaseRunRow, owner: OwnerId): boolean =>\n row.status === \"running\" &&\n row.ownerHostId === owner.hostId &&\n row.ownerPid === owner.pid &&\n row.ownerNonce === owner.nonce\n\nconst claimColumnsFree = (row: DatabaseRunRow): boolean =>\n row.claimHostId === null && row.claimPid === null && row.claimNonce === null && row.claimedAtMs === null\n\nconst decodeRunRow = (method: string, runId: string, input: unknown): Effect.Effect<RunRow, RunStoreError> =>\n Schema.decodeUnknownEffect(DatabaseRunRow)(input).pipe(\n Effect.mapError(() =>\n runStoreError(method, \"decode_failed\", \"could not decode flows_runs row\", { runId, stage: \"row-schema\" })\n ),\n Effect.flatMap((row) => {\n const owner = ownerFromColumns(row.ownerHostId, row.ownerPid, row.ownerNonce)\n const claim = ownerFromColumns(row.claimHostId, row.claimPid, row.claimNonce)\n const admittedState = stateAdmission(row.stateJson)\n const stateJsonValid = isJsonString(row.stateJson)\n const invalidOwner = owner === undefined ||\n (owner === null && row.heartbeatAtMs !== null) ||\n (owner !== null && row.heartbeatAtMs === null) ||\n (row.status === \"running\" ? owner === null : owner !== null)\n const invalidClaim = claim === undefined ||\n (claim === null && row.claimedAtMs !== null) ||\n (claim !== null && row.claimedAtMs === null)\n if (invalidOwner || invalidClaim || !admittedState.ok) {\n // The cause is published to logs, spans, and telemetry. Executable\n // state may carry credentials, so report only the invariants read here.\n return Effect.fail(\n runStoreError(method, \"decode_failed\", \"flows_runs row violates durable invariants\", {\n runId,\n status: row.status,\n heartbeatAtMs: row.heartbeatAtMs,\n claimedAtMs: row.claimedAtMs,\n hasClaimColumns: row.claimHostId !== null || row.claimPid !== null || row.claimNonce !== null,\n hasOwnerColumns: row.ownerHostId !== null || row.ownerPid !== null || row.ownerNonce !== null,\n stateJsonValid\n })\n )\n }\n return Effect.succeed(Object.freeze({\n runId: row.runId,\n status: row.status,\n createdAtMs: row.createdAtMs,\n startedAtMs: row.startedAtMs,\n finishedAtMs: row.finishedAtMs,\n owner,\n heartbeatAtMs: row.heartbeatAtMs,\n claim,\n claimedAtMs: row.claimedAtMs,\n parentRunId: row.parentRunId,\n cancelRequestedAtMs: row.cancelRequestedAtMs,\n lineageId: row.lineageId,\n roundOrdinal: row.roundOrdinal,\n stateJson: row.stateJson\n }))\n })\n )\n\n/**\n * Rows that belong to the logical run containing `runId`: every round that\n * names the resolved lineage, plus the root itself when its own lineage\n * columns are null or name that lineage. A same-named run in an independent\n * lineage is excluded. Shared by every lineage read and write so membership\n * cannot drift between them.\n */\nconst lineageMembership = (sql: SqlClient.SqlClient, runId: string) =>\n sql`(\n lineage_id = (SELECT COALESCE(lineage_id, run_id) FROM flows_runs WHERE run_id = ${runId})\n OR (\n run_id = (SELECT COALESCE(lineage_id, run_id) FROM flows_runs WHERE run_id = ${runId})\n AND (\n lineage_id IS NULL\n OR lineage_id = (SELECT COALESCE(lineage_id, run_id) FROM flows_runs WHERE run_id = ${runId})\n )\n )\n )`\n\n/** The status and request columns a cancellation miss is classified from. */\ninterface CancellationRow {\n readonly requestedAtMs: number | null\n readonly status: string\n}\n\n/**\n * Names the outcome of a guarded cancellation UPDATE that matched nothing.\n *\n * `rows` are the lineage members in round order; an absent set is `NotFound`.\n * Status is read before the request column, so a settled run reports how it\n * ended even when its own closing request is still on the row. A live\n * unrequested row cannot survive the UPDATE inside one serialized writer\n * transaction, so it is a persistence invariant failure rather than a retry.\n */\nconst classifyCancellationMiss = (\n method: string,\n runId: string,\n rows: ReadonlyArray<CancellationRow>\n): Effect.Effect<RequestCancelOutcome, RunStoreError> =>\n Effect.gen(function*() {\n let outcome: RequestCancelOutcome = notFound\n for (const row of rows) {\n const status = yield* Schema.decodeUnknownEffect(RunStatus)(row.status).pipe(\n Effect.mapError((cause) => runStoreError(method, \"decode_failed\", \"could not decode flows_runs status\", cause))\n )\n if (isTerminalRunStatus(status)) {\n // A terminal predecessor never hides a live round's request.\n if (outcome._tag !== \"AlreadyRequested\") outcome = { _tag: \"Terminal\", status }\n continue\n }\n if (row.requestedAtMs === null) {\n return yield* Effect.fail(\n runStoreError(method, \"persistence_failed\", \"guarded cancellation update missed a live unrequested row\", {\n runId,\n stage: \"write-invariant\"\n })\n )\n }\n outcome = { _tag: \"AlreadyRequested\", requestedAtMs: Number(row.requestedAtMs) }\n }\n return outcome\n })\n\nconst selectRun = (sql: SqlClient.SqlClient, runId: string, mode: \"single\" | \"lineage\" | \"latest\" = \"single\") =>\n sql<DatabaseRunRow>`\n SELECT\n run_id AS \"runId\",\n status AS \"status\",\n created_at_ms AS \"createdAtMs\",\n started_at_ms AS \"startedAtMs\",\n finished_at_ms AS \"finishedAtMs\",\n owner_host_id AS \"ownerHostId\",\n owner_pid AS \"ownerPid\",\n owner_nonce AS \"ownerNonce\",\n heartbeat_at_ms AS \"heartbeatAtMs\",\n claim_host_id AS \"claimHostId\",\n claim_pid AS \"claimPid\",\n claim_nonce AS \"claimNonce\",\n claimed_at_ms AS \"claimedAtMs\",\n parent_run_id AS \"parentRunId\",\n cancel_requested_at_ms AS \"cancelRequestedAtMs\",\n lineage_id AS \"lineageId\",\n round_ordinal AS \"roundOrdinal\",\n state_json AS \"stateJson\"\n FROM flows_runs\n WHERE ${\n mode === \"latest\"\n ? sql`run_id = COALESCE(\n (SELECT run_id FROM flows_runs\n WHERE lineage_id = (SELECT COALESCE(lineage_id, run_id) FROM flows_runs WHERE run_id = ${runId})\n ORDER BY round_ordinal DESC LIMIT 1),\n (SELECT run_id FROM flows_runs WHERE run_id = ${runId})\n )`\n : mode === \"lineage\"\n ? lineageMembership(sql, runId)\n : sql`run_id = ${runId}`\n }\n ORDER BY COALESCE(round_ordinal, 0), run_id\n `\n\nconst classifyClaimLoss = (\n row: DatabaseRunRow | undefined,\n nowMs: number\n): ClaimLossOutcome => {\n if (row === undefined) return notFound\n if (row.claimHostId !== null) return alreadyClaimed\n if (\n row.status === \"running\" &&\n row.heartbeatAtMs !== null &&\n row.heartbeatAtMs >= nowMs - heartbeatStaleAfterMs\n ) {\n return heartbeatFresh\n }\n return snapshotChanged\n}\n\nconst evidenceMatches = (\n expected: RunSnapshot,\n claimant: OwnerId,\n checkedAtMs: number,\n evidence: LivenessEvidence\n): boolean =>\n expected.status === \"running\" &&\n expected.owner !== null &&\n matchesEvidence(expected.owner, claimant, checkedAtMs, evidence)\n\n/**\n * Constructs the production `RunStore` implementation.\n *\n * `state_json` is executable state: it is decoded and re-entered on every\n * resume, so it is persisted and returned byte-for-byte. Nothing rewrites it\n * on the way through \u2014 a redactor here would silently change what the flow\n * re-reads (issue #72). `Schema.Redacted` hides inspection but allows schema\n * JSON encoding by default. Use `Schema.Redacted(Schema.String, {\n * disallowJsonEncode: true })` to refuse JSON encoding, or exclude credentials\n * from the persistence schema and store a secret reference resolved from a\n * secret provider on resume. Publication hygiene belongs on journal-event\n * and export surfaces.\n *\n * `state_json` and `flows_attempts.outcome_json` therefore hold unredacted\n * run inputs and results, so the run's SQLite file (`.flows/*.db`) must be\n * handled as a secret store.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const make: Effect.Effect<Service, never, Consensus | DurableWriter | SqlClient.SqlClient> = Effect.gen(\n function*() {\n const sql = yield* Effect.service(SqlClient.SqlClient)\n const writer = yield* DurableWriter\n const consensus = yield* Consensus\n\n const write = <A, R>(\n method: string,\n effect: Effect.Effect<A, SqlError.SqlError | RunStoreError | ConsensusError | JournalError, R>\n ): Effect.Effect<A, RunStoreError, R> =>\n writer.write(effect).pipe(Effect.mapError((cause) => persistenceError(method, cause)))\n\n /** Appends an ownership transition to the run's companion stream (see {@link OwnershipTransition}). */\n const recordTransition = (\n runId: string,\n transition: OwnershipTransition,\n owner: OwnerId,\n grantedAtMs: number | null\n ): Effect.Effect<void, JournalError> =>\n Companion.append(runId, ownershipEventType(transition), {\n owner: { hostId: owner.hostId, pid: owner.pid, nonce: owner.nonce },\n grantedAtMs\n })\n\n // A bare SELECT needs no write transaction and no replay; only the error\n // vocabulary stays shared with `write`.\n const read = <A, R>(\n method: string,\n effect: Effect.Effect<A, SqlError.SqlError, R>\n ): Effect.Effect<A, RunStoreError, R> =>\n effect.pipe(Effect.mapError((cause) => persistenceError(method, fromSqlError(cause))))\n\n const create = Effect.fn(\"RunStore.create\")(\n (\n runIdInput: string,\n stateInput: string,\n optionsInput?: CreateOptions | undefined\n ): Effect.Effect<void, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"create\", runIdInput)\n if (isCompanionRunId(runId)) {\n return yield* Effect.fail(invalidRunError(\"create\", \"runId\", \"names a reserved journal companion stream\"))\n }\n const { lineageId, parentRunId, roundOrdinal } = yield* snapshotCreateOptions(optionsInput)\n // This cause is published to logs, spans, and telemetry. Executable\n // state may carry credentials, so include its shape but never its text.\n const stateJson = yield* snapshotState(\"create\", stateInput, {\n runId,\n parentRunId,\n lineageId,\n roundOrdinal,\n stateJsonLength: typeof stateInput === \"string\" ? stateInput.length : null,\n stateJsonValid: isJsonString(stateInput)\n })\n yield* Effect.annotateCurrentSpan({ runId })\n const createdAtMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor))\n yield* write(\n \"create\",\n sql`\n INSERT INTO flows_runs (\n run_id,\n status,\n created_at_ms,\n started_at_ms,\n finished_at_ms,\n owner_host_id,\n owner_pid,\n owner_nonce,\n heartbeat_at_ms,\n claim_host_id,\n claim_pid,\n claim_nonce,\n claimed_at_ms,\n parent_run_id,\n lineage_id,\n round_ordinal,\n state_json\n ) VALUES (\n ${runId},\n 'pending',\n ${createdAtMs},\n NULL,\n NULL,\n NULL,\n NULL,\n NULL,\n NULL,\n NULL,\n NULL,\n NULL,\n NULL,\n ${parentRunId},\n ${lineageId},\n ${roundOrdinal},\n ${stateJson}\n )\n `.pipe(Effect.asVoid)\n )\n }).pipe(observeExit)\n )\n\n const get = Effect.fn(\"RunStore.get\")((runIdInput: string): Effect.Effect<RunRow, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"get\", runIdInput)\n yield* Effect.annotateCurrentSpan({ runId })\n const rows = yield* read(\"get\", selectRun(sql, runId))\n return rows[0] === undefined\n ? yield* Effect.fail(\n runStoreError(\"get\", \"not_found_row\", `run ${runId} was not found`, { runId })\n )\n : yield* decodeRunRow(\"get\", runId, rows[0])\n }).pipe(observeExit)\n )\n\n const lineage = Effect.fn(\"RunStore.lineage\")((runIdInput: string) =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"lineage\", runIdInput)\n yield* Effect.annotateCurrentSpan({ runId })\n const rows = yield* read(\"lineage\", selectRun(sql, runId, \"lineage\"))\n return yield* Effect.forEach(rows, (row) => decodeRunRow(\"lineage\", row.runId, row))\n }).pipe(observeExit)\n )\n\n const latestRound = Effect.fn(\"RunStore.latestRound\")((runIdInput: string) =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"latestRound\", runIdInput)\n yield* Effect.annotateCurrentSpan({ runId })\n const rows = yield* read(\"latestRound\", selectRun(sql, runId, \"latest\"))\n return rows[0] === undefined\n ? yield* Effect.fail(runStoreError(\"latestRound\", \"not_found_row\", `run ${runId} was not found`, { runId }))\n : yield* decodeRunRow(\"latestRound\", rows[0].runId, rows[0])\n }).pipe(observeExit)\n )\n\n const requestCancel = Effect.fn(\"RunStore.requestCancel\")((\n runIdInput: string,\n nowMsInput: number\n ): Effect.Effect<RequestCancelOutcome, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"requestCancel\", runIdInput)\n const nowMs = yield* snapshotTimestamp(\"requestCancel\", \"nowMs\", nowMsInput)\n yield* Effect.annotateCurrentSpan({ runId })\n return yield* write(\n \"requestCancel\",\n Effect.gen(function*() {\n // The status predicate is part of the compare-and-swap rather than\n // a read-then-write check: a run that settles between a caller's\n // read and this UPDATE must lose the write, not race it. The writer\n // serializes the whole transaction, so a miss is classified by one\n // read of the same columns and never retried.\n const rows = yield* sql<{ readonly requestedAtMs: number }>`\n UPDATE flows_runs\n SET cancel_requested_at_ms = ${nowMs}\n WHERE run_id = ${runId}\n AND cancel_requested_at_ms IS NULL\n AND status NOT IN ('completed', 'failed', 'cancelled')\n RETURNING cancel_requested_at_ms AS \"requestedAtMs\"\n `\n if (rows[0] !== undefined) {\n return { _tag: \"CancelRequested\", requestedAtMs: Number(rows[0].requestedAtMs) } as const\n }\n const current = yield* sql<CancellationRow>`\n SELECT cancel_requested_at_ms AS \"requestedAtMs\", status AS \"status\"\n FROM flows_runs WHERE run_id = ${runId}\n `\n return yield* classifyCancellationMiss(\"requestCancel\", runId, current)\n })\n )\n }).pipe(observeOutcome<RequestCancelOutcome>())\n )\n\n const requestCancelLineage = Effect.fn(\"RunStore.requestCancelLineage\")((runIdInput: string, nowMsInput: number) =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"requestCancelLineage\", runIdInput)\n const nowMs = yield* snapshotTimestamp(\"requestCancelLineage\", \"nowMs\", nowMsInput)\n yield* Effect.annotateCurrentSpan({ runId })\n return yield* write(\n \"requestCancelLineage\",\n Effect.gen(function*() {\n // One set-based guarded UPDATE over the lineage: cost follows the\n // members' metadata, never their state payloads, and a settled\n // history of any length adds no statements.\n const rows = yield* sql<{ readonly requestedAtMs: number }>`\n UPDATE flows_runs\n SET cancel_requested_at_ms = ${nowMs}\n WHERE ${lineageMembership(sql, runId)}\n AND cancel_requested_at_ms IS NULL\n AND status NOT IN ('completed', 'failed', 'cancelled')\n RETURNING cancel_requested_at_ms AS \"requestedAtMs\"\n `\n if (rows[0] !== undefined) {\n return { _tag: \"CancelRequested\", requestedAtMs: Number(rows[0].requestedAtMs) } as const\n }\n // A previous request on any live round dominates a terminal\n // predecessor; otherwise the latest round's ending is reported.\n const members = yield* sql<CancellationRow>`\n SELECT cancel_requested_at_ms AS \"requestedAtMs\", status AS \"status\"\n FROM flows_runs\n WHERE ${lineageMembership(sql, runId)}\n ORDER BY COALESCE(round_ordinal, 0), run_id\n `\n return yield* classifyCancellationMiss(\"requestCancelLineage\", runId, members)\n })\n )\n }).pipe(observeOutcome<RequestCancelOutcome>())\n )\n\n const claim = Effect.fn(\"RunStore.claim\")((\n runIdInput: string,\n expectedInput: RunSnapshot,\n claimantInput: OwnerId,\n nowMsInput: number\n ): Effect.Effect<ClaimOutcome, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"claim\", runIdInput)\n const expected = yield* snapshotExpected(\"claim\", expectedInput)\n const claimant = yield* snapshotOwner(\"claim\", \"claimant\", claimantInput)\n const nowMs = yield* snapshotLeaseReading(\n \"claim\",\n \"nowMs\",\n nowMsInput,\n { runId }\n )\n yield* Effect.annotateCurrentSpan({ runId, claimantHostId: claimant.hostId })\n return yield* write(\n \"claim\",\n Effect.gen(function*() {\n // `claim` never admits a running run, so it needs no staleness\n // test: `pending` and `suspended` exclude it. `claimAndOwn` is the\n // method that genuinely needs one, because it does admit `running`.\n // The snapshot predicate is evaluated inside the serialized write\n // transaction, so it is exactly the compare-and-swap it replaces.\n const row = (yield* selectRun(sql, runId))[0]\n const admissible = row !== undefined &&\n (row.status === \"pending\" || row.status === \"suspended\") &&\n rowMatchesSnapshot(row, expected) &&\n claimColumnsFree(row)\n if (!admissible) return classifyClaimLoss(row, nowMs)\n const grant = yield* consensus.claim(runId, claimant, nowMs)\n if (grant._tag === \"Rejected\") {\n // The strategy is authoritative: the row admitted what the lease\n // refused, and the loss is classified from the row exactly as a\n // failed compare-and-swap always was.\n return classifyClaimLoss(row, nowMs)\n }\n yield* sql`\n UPDATE flows_runs\n SET\n claim_host_id = ${claimant.hostId},\n claim_pid = ${claimant.pid},\n claim_nonce = ${claimant.nonce},\n claimed_at_ms = ${grant.grantedAtMs}\n WHERE run_id = ${runId}\n `\n yield* recordTransition(runId, \"claimed\", claimant, grant.grantedAtMs)\n return claimed(grant.grantedAtMs)\n })\n )\n }).pipe(observeOutcome(\n (outcome) => RunStoreMetrics.claim[outcome._tag],\n Metric.withAttributes(RunStoreMetrics.claims, { op: \"claim\" })\n ))\n )\n\n const claimAndOwn = Effect.fn(\"RunStore.claimAndOwn\")((\n runIdInput: string,\n expectedInput: RunSnapshot,\n ownerInput: OwnerId,\n nowMsInput: number,\n evidenceInput?: LivenessEvidence | undefined\n ): Effect.Effect<ClaimAndOwnOutcome, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"claimAndOwn\", runIdInput)\n const expected = yield* snapshotExpected(\"claimAndOwn\", expectedInput)\n const owner = yield* snapshotOwner(\"claimAndOwn\", \"owner\", ownerInput)\n const nowMs = yield* snapshotLeaseReading(\n \"claimAndOwn\",\n \"nowMs\",\n nowMsInput,\n { runId }\n )\n const evidence = evidenceInput === undefined\n ? undefined\n : yield* snapshotEvidence(\"claimAndOwn\", evidenceInput)\n yield* Effect.annotateCurrentSpan({ runId, ownerHostId: owner.hostId })\n return yield* Effect.suspend((): Effect.Effect<ClaimAndOwnOutcome, RunStoreError> => {\n if (\n expected.status === \"running\" &&\n !sameOwner(expected.owner, owner) &&\n !(evidence !== undefined && evidenceMatches(expected, owner, nowMs, evidence))\n ) {\n return read(\"claimAndOwn\", selectRun(sql, runId)).pipe(\n Effect.map((current) => {\n const row = current[0]\n // Classify the lease before upgrading an unchanged stale\n // snapshot. Evidence cannot displace a fresh heartbeat, so\n // naming evidence first would advise a retry the SQL must refuse.\n const loss = classifyClaimLoss(row, nowMs)\n return loss === snapshotChanged && row !== undefined && rowMatchesSnapshot(row, expected)\n ? evidenceRequired\n : loss\n })\n )\n }\n\n return write(\n \"claimAndOwn\",\n Effect.gen(function*() {\n const row = (yield* selectRun(sql, runId))[0]\n const admissible = row !== undefined &&\n (row.status === \"pending\" || row.status === \"suspended\" || row.status === \"running\") &&\n rowMatchesSnapshot(row, expected) &&\n claimColumnsFree(row) &&\n (row.status !== \"running\" ||\n row.heartbeatAtMs === null ||\n row.heartbeatAtMs < nowMs - heartbeatStaleAfterMs)\n if (!admissible) return classifyClaimLoss(row, nowMs)\n const expectedOwner = expected.owner\n let grant: LeaseClaimOutcome\n let transition: OwnershipTransition = \"claimed\"\n if (expectedOwner === null) {\n grant = yield* consensus.claim(runId, owner, nowMs)\n } else if (sameOwner(expectedOwner, owner)) {\n // Re-owning one's own stale run: the stale lease is released\n // and re-granted under a fresh generation (R4).\n yield* consensus.release(runId, owner)\n yield* recordTransition(runId, \"released\", owner, null)\n grant = yield* consensus.claim(runId, owner, nowMs)\n } else {\n // Admission above proved the evidence matches, so the steal is\n // the strategy's evidence-gated claim.\n grant = yield* consensus.steal(runId, owner, nowMs, evidence!)\n transition = \"stolen\"\n }\n if (grant._tag === \"Rejected\") return classifyClaimLoss(row, nowMs)\n const activation = yield* consensus.activate(runId, owner, grant.grantedAtMs, nowMs)\n if (activation._tag === \"Lost\") {\n // The strategy revoked the grant it just made \u2014 its lease moved\n // on under the transaction \u2014 so the fresh grant is released and\n // the loss classified from the row.\n yield* consensus.release(runId, owner)\n return classifyClaimLoss(row, nowMs)\n }\n yield* recordTransition(runId, transition, owner, grant.grantedAtMs)\n yield* recordTransition(runId, \"activated\", owner, grant.grantedAtMs)\n yield* sql`\n UPDATE flows_runs\n SET\n status = 'running',\n started_at_ms = COALESCE(started_at_ms, ${nowMs}),\n finished_at_ms = NULL,\n owner_host_id = ${owner.hostId},\n owner_pid = ${owner.pid},\n owner_nonce = ${owner.nonce},\n heartbeat_at_ms = ${nowMs}\n WHERE run_id = ${runId}\n `\n return activated\n })\n )\n })\n }).pipe(observeOutcome(\n (outcome) => RunStoreMetrics.claimAndOwn[outcome._tag],\n Metric.withAttributes(RunStoreMetrics.claims, { op: \"claim_and_own\" })\n ))\n )\n\n const activate = Effect.fn(\"RunStore.activate\")((\n runIdInput: string,\n claimantInput: OwnerId,\n claimedAtMsInput: number,\n expectedInput: RunSnapshot\n ): Effect.Effect<ActivateOutcome, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"activate\", runIdInput)\n const claimant = yield* snapshotOwner(\"activate\", \"claimant\", claimantInput)\n const claimedAtMs = yield* snapshotTimestamp(\n \"activate\",\n \"claimedAtMs\",\n claimedAtMsInput\n )\n const expected = yield* snapshotExpected(\"activate\", expectedInput)\n yield* Effect.annotateCurrentSpan({ runId, claimantHostId: claimant.hostId })\n const activatedAtMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor))\n return yield* write(\n \"activate\",\n Effect.gen(function*() {\n const row = (yield* selectRun(sql, runId))[0]\n if (row === undefined || !rowMatchesClaim(row, claimant, claimedAtMs)) return claimLost\n\n const clearClaim = sql`\n UPDATE flows_runs\n SET\n claim_host_id = NULL,\n claim_pid = NULL,\n claim_nonce = NULL,\n claimed_at_ms = NULL\n WHERE run_id = ${runId}\n AND claim_host_id = ${claimant.hostId}\n AND claim_pid = ${claimant.pid}\n AND claim_nonce = ${claimant.nonce}\n AND claimed_at_ms = ${claimedAtMs}\n `\n if (!rowMatchesSnapshot(row, expected)) {\n // The claim is intact but the guarded snapshot moved on: clear\n // the claim, exactly as the compare-and-swap always did, and\n // release the strategy's matching claim so the two stay aligned.\n yield* consensus.release(runId, claimant)\n yield* recordTransition(runId, \"released\", claimant, claimedAtMs)\n yield* clearClaim\n return snapshotChanged\n }\n const activation = yield* consensus.activate(runId, claimant, claimedAtMs, activatedAtMs)\n if (activation._tag === \"Lost\") {\n // The strategy no longer holds this claim though the row still\n // shows it; the strategy is authoritative, so the stale claim\n // columns are cleared and the claim reported lost.\n yield* clearClaim\n return claimLost\n }\n yield* sql`\n UPDATE flows_runs\n SET\n status = 'running',\n started_at_ms = COALESCE(started_at_ms, ${activatedAtMs}),\n finished_at_ms = NULL,\n owner_host_id = ${claimant.hostId},\n owner_pid = ${claimant.pid},\n owner_nonce = ${claimant.nonce},\n heartbeat_at_ms = ${activatedAtMs},\n claim_host_id = NULL,\n claim_pid = NULL,\n claim_nonce = NULL,\n claimed_at_ms = NULL\n WHERE run_id = ${runId}\n `\n yield* recordTransition(runId, \"activated\", claimant, claimedAtMs)\n return activated\n })\n )\n }).pipe(observeOutcome(\n (outcome) => RunStoreMetrics.activate[outcome._tag],\n Metric.withAttributes(RunStoreMetrics.claims, { op: \"activate\" })\n ))\n )\n\n const abandonClaim = Effect.fn(\"RunStore.abandonClaim\")((\n runIdInput: string,\n claimantInput: OwnerId,\n claimedAtMsInput: number\n ): Effect.Effect<AbandonClaimOutcome, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"abandonClaim\", runIdInput)\n const claimant = yield* snapshotOwner(\"abandonClaim\", \"claimant\", claimantInput)\n const claimedAtMs = yield* snapshotTimestamp(\"abandonClaim\", \"claimedAtMs\", claimedAtMsInput)\n yield* Effect.annotateCurrentSpan({ runId, claimantHostId: claimant.hostId })\n return yield* write(\n \"abandonClaim\",\n Effect.gen(function*() {\n const rows = yield* sql<{ readonly runId: string }>`\n UPDATE flows_runs\n SET\n claim_host_id = NULL,\n claim_pid = NULL,\n claim_nonce = NULL,\n claimed_at_ms = NULL\n WHERE run_id = ${runId}\n AND claim_host_id = ${claimant.hostId}\n AND claim_pid = ${claimant.pid}\n AND claim_nonce = ${claimant.nonce}\n AND claimed_at_ms = ${claimedAtMs}\n RETURNING run_id AS \"runId\"\n `\n if (rows.length === 0) return claimLost\n yield* consensus.release(runId, claimant)\n yield* recordTransition(runId, \"released\", claimant, claimedAtMs)\n return abandoned\n })\n )\n }).pipe(observeOutcome(\n (outcome) => RunStoreMetrics.abandonClaim[outcome._tag],\n Metric.withAttributes(RunStoreMetrics.claims, { op: \"abandon_claim\" })\n ))\n )\n\n const recoverClaim = Effect.fn(\"RunStore.recoverClaim\")((\n runIdInput: string,\n staleClaimantInput: OwnerId,\n claimedAtMsInput: number,\n observerInput: OwnerId,\n nowMsInput: number,\n evidenceInput: LivenessEvidence\n ): Effect.Effect<RecoverClaimOutcome, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"recoverClaim\", runIdInput)\n const staleClaimant = yield* snapshotOwner(\"recoverClaim\", \"staleClaimant\", staleClaimantInput)\n const claimedAtMs = yield* snapshotTimestamp(\n \"recoverClaim\",\n \"claimedAtMs\",\n claimedAtMsInput\n )\n const observer = yield* snapshotOwner(\"recoverClaim\", \"observer\", observerInput)\n const nowMs = yield* snapshotLeaseReading(\"recoverClaim\", \"nowMs\", nowMsInput, { runId, claimedAtMs })\n const evidence = yield* snapshotEvidence(\"recoverClaim\", evidenceInput)\n yield* Effect.annotateCurrentSpan({ runId, observerHostId: observer.hostId })\n return yield* Effect.suspend((): Effect.Effect<RecoverClaimOutcome, RunStoreError> => {\n if (!matchesEvidence(staleClaimant, observer, nowMs, evidence)) {\n return Effect.succeed(livenessUnconfirmed)\n }\n return write(\n \"recoverClaim\",\n Effect.gen(function*() {\n const row = (yield* selectRun(sql, runId))[0]\n if (row === undefined) return notFound\n if (!rowMatchesClaim(row, staleClaimant, claimedAtMs)) return claimChanged\n if (claimedAtMs >= nowMs - heartbeatStaleAfterMs) return claimFresh\n const outcome = yield* consensus.recover(runId, staleClaimant, claimedAtMs, observer, nowMs, evidence)\n if (outcome._tag === \"Rejected\") {\n // The strategy refused what the row admitted: as the strategy\n // knows it, the claim has moved on.\n return claimChanged\n }\n yield* sql`\n UPDATE flows_runs\n SET\n claim_host_id = NULL,\n claim_pid = NULL,\n claim_nonce = NULL,\n claimed_at_ms = NULL\n WHERE run_id = ${runId}\n `\n yield* recordTransition(runId, \"expired\", staleClaimant, claimedAtMs)\n return recovered\n })\n )\n })\n }).pipe(observeOutcome(\n (outcome) => RunStoreMetrics.recoverClaim[outcome._tag],\n Metric.withAttributes(RunStoreMetrics.claims, { op: \"recover_claim\" })\n ))\n )\n\n const renewLease = (method: \"heartbeat\" | \"reconfirm\") =>\n Effect.fn(`RunStore.${method}`)((\n runIdInput: string,\n ownerInput: OwnerId,\n nowMsInput: number\n ): Effect.Effect<HeartbeatOutcome, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(method, runIdInput)\n const owner = yield* snapshotOwner(method, \"owner\", ownerInput)\n const nowMs = yield* snapshotLeaseReading(\n method,\n \"nowMs\",\n nowMsInput,\n { runId }\n )\n yield* Effect.annotateCurrentSpan({ runId, ownerHostId: owner.hostId })\n return yield* write(\n method,\n Effect.gen(function*() {\n // The pulse drives the strategy's lease renewal; the row mirrors\n // the stamp the strategy recorded. The lease timestamp is\n // monotonic: a heartbeat that arrives late \u2014 delayed past a newer\n // one from the same owner \u2014 never moves it backwards and never\n // makes a live run look stale to `claimAndOwn`/`steal`'s cutoff. A\n // late clock reading still reports `Updated`: the fence held, and\n // the write proves liveness regardless of which caller clock\n // reading it carried. Prior art: Temporal's shard `rangeID` only\n // ever advances (`reference/temporal/service/history/shard/context_impl.go`,\n // `renewRangeLocked`). Heartbeats never enter the journal.\n const outcome = yield* consensus[method](runId, owner, nowMs)\n if (outcome._tag === \"Lost\") {\n const current = yield* selectRun(sql, runId)\n return current.length === 0 ? notFound : fenceLost\n }\n // The mirror write is verified: a renewed lease over a row this\n // owner no longer holds is a lease/row disagreement, and a success\n // that wrote nothing would hide it. RETURNING keeps the old row\n // answer \u2014 `FenceLost` when the row moved on, `NotFound` when it\n // is gone.\n const rows = yield* sql<{ readonly runId: string }>`\n UPDATE flows_runs\n SET heartbeat_at_ms = ${Dialect.greatest(sql)}(heartbeat_at_ms, ${outcome.heartbeatAtMs})\n WHERE run_id = ${runId}\n AND status = 'running'\n AND owner_host_id = ${owner.hostId}\n AND owner_pid = ${owner.pid}\n AND owner_nonce = ${owner.nonce}\n RETURNING run_id AS \"runId\"\n `\n if (rows.length > 0) return updated\n const current = yield* selectRun(sql, runId)\n return current.length === 0 ? notFound : fenceLost\n })\n )\n }).pipe(observeOutcome((outcome) => RunStoreMetrics.heartbeat[outcome._tag], RunStoreMetrics.heartbeats))\n )\n\n const heartbeat = renewLease(\"heartbeat\")\n const reconfirm = renewLease(\"reconfirm\")\n\n const transitionOwned = Effect.fn(\"RunStore.transitionOwned\")((\n runIdInput: string,\n ownerInput: OwnerId,\n toStatusInput: RunStatus,\n stateInput?: string | undefined,\n guardInput?: TransitionGuard | undefined\n ): Effect.Effect<TransitionOutcome, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"transitionOwned\", runIdInput)\n const owner = yield* snapshotOwner(\"transitionOwned\", \"owner\", ownerInput)\n if (!Schema.is(RunStatus)(toStatusInput) || toStatusInput === \"pending\") {\n return yield* Effect.fail(invalidRunError(\"transitionOwned\", \"toStatus\"))\n }\n const toStatus = toStatusInput\n const state = stateInput === undefined\n ? null\n : yield* snapshotState(\"transitionOwned\", stateInput, {\n runId,\n stateJsonLength: typeof stateInput === \"string\" ? stateInput.length : null,\n stateJsonValid: isJsonString(stateInput)\n })\n const guard = yield* snapshotGuard(guardInput)\n yield* Effect.annotateCurrentSpan({ runId, ownerHostId: owner.hostId, to: toStatus })\n const transitionedAtMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor))\n const outcome = yield* write(\n \"transitionOwned\",\n Effect.gen(function*() {\n // The fence check is the strategy's guard (R3); the row check keeps\n // the mirror honest. Both run in the serialized write transaction,\n // so a concurrent cancellation request or reclaim can never slip\n // between check and write.\n const held = yield* consensus.guard(runId, owner).pipe(\n Effect.as(true),\n Effect.catch((cause) => cause.code === \"fence_lost\" ? Effect.succeed(false) : Effect.fail(cause))\n )\n const row = (yield* selectRun(sql, runId))[0]\n if (row === undefined) return notFound\n if (!held || !rowOwnedBy(row, owner)) return fenceLost\n const guardBlocked = (guard?.cancelRequested === \"absent\" && row.cancelRequestedAtMs !== null) ||\n (guard?.cancelRequested === \"present\" && row.cancelRequestedAtMs === null)\n if (guardBlocked) return guardFailed\n if (toStatus === \"running\") {\n yield* sql`\n UPDATE flows_runs\n SET\n status = 'running',\n finished_at_ms = NULL,\n state_json = COALESCE(${state}, state_json)\n WHERE run_id = ${runId}\n `\n return transitioned\n }\n yield* sql`\n UPDATE flows_runs\n SET\n status = ${toStatus},\n finished_at_ms = ${terminalStatuses.has(toStatus) ? transitionedAtMs : null},\n owner_host_id = NULL,\n owner_pid = NULL,\n owner_nonce = NULL,\n heartbeat_at_ms = NULL,\n claim_host_id = NULL,\n claim_pid = NULL,\n claim_nonce = NULL,\n claimed_at_ms = NULL,\n state_json = COALESCE(${state}, state_json)\n WHERE run_id = ${runId}\n `\n yield* consensus.release(runId, owner)\n yield* recordTransition(runId, \"released\", owner, null)\n if (terminalStatuses.has(toStatus)) {\n yield* afterCommit(Metric.update(ObservabilityMetric.runThroughput, 1), sql)\n }\n return transitioned\n })\n )\n return outcome\n }).pipe(\n observeOutcome((outcome) =>\n Metric.withAttributes(RunStoreMetrics.transition[outcome._tag], {\n to: toStatusInput\n }), Metric.withAttributes(RunStoreMetrics.transitions, { to: toStatusInput }))\n )\n )\n\n const steal = Effect.fn(\"RunStore.steal\")((\n runIdInput: string,\n expectedInput: RunSnapshot,\n claimantInput: OwnerId,\n nowMsInput: number,\n evidenceInput: LivenessEvidence\n ): Effect.Effect<StealOutcome, RunStoreError> =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"steal\", runIdInput)\n const expected = yield* snapshotExpected(\"steal\", expectedInput)\n const claimant = yield* snapshotOwner(\"steal\", \"claimant\", claimantInput)\n const nowMs = yield* snapshotLeaseReading(\n \"steal\",\n \"nowMs\",\n nowMsInput,\n { runId }\n )\n const evidence = yield* snapshotEvidence(\"steal\", evidenceInput)\n yield* Effect.annotateCurrentSpan({ runId, claimantHostId: claimant.hostId })\n return yield* Effect.suspend((): Effect.Effect<StealOutcome, RunStoreError> => {\n if (!evidenceMatches(expected, claimant, nowMs, evidence)) {\n return Effect.succeed(livenessUnconfirmed)\n }\n return write(\n \"steal\",\n Effect.gen(function*() {\n const row = (yield* selectRun(sql, runId))[0]\n const admissible = row !== undefined &&\n rowMatchesSnapshot(row, expected) &&\n claimColumnsFree(row) &&\n row.heartbeatAtMs !== null &&\n row.heartbeatAtMs < nowMs - heartbeatStaleAfterMs\n if (!admissible) return classifyClaimLoss(row, nowMs)\n const grant = yield* consensus.steal(runId, claimant, nowMs, evidence)\n if (grant._tag === \"Rejected\") {\n // The strategy refused what the row admitted \u2014 for example its\n // lease still records a fresh heartbeat \u2014 and is authoritative.\n return classifyClaimLoss(row, nowMs)\n }\n yield* sql`\n UPDATE flows_runs\n SET\n claim_host_id = ${claimant.hostId},\n claim_pid = ${claimant.pid},\n claim_nonce = ${claimant.nonce},\n claimed_at_ms = ${grant.grantedAtMs}\n WHERE run_id = ${runId}\n `\n yield* recordTransition(runId, \"stolen\", claimant, grant.grantedAtMs)\n return claimed(grant.grantedAtMs)\n })\n )\n })\n }).pipe(observeOutcome(\n (outcome) => RunStoreMetrics.steal[outcome._tag],\n Metric.withAttributes(RunStoreMetrics.claims, { op: \"steal\" })\n ))\n )\n\n const acknowledgeCancel: Service[\"acknowledgeCancel\"] = Effect.fn(\"RunStore.acknowledgeCancel\")((\n id,\n ownerInput,\n time\n ) =>\n Effect.gen(function*() {\n const runId = yield* snapshotRunId(\"acknowledgeCancel\", id)\n const owner = yield* snapshotOwner(\"acknowledgeCancel\", \"owner\", ownerInput)\n const observedAtMs = yield* snapshotTimestamp(\"acknowledgeCancel\", \"nowMs\", time)\n yield* Effect.annotateCurrentSpan({ runId, ownerHostId: owner.hostId })\n const acknowledgement = JSON.stringify({ observedAtMs, owner })\n return yield* write(\n \"acknowledgeCancel\",\n sql`\n UPDATE flows_runs\n SET cancel_acknowledgement_json = COALESCE(cancel_acknowledgement_json, ${acknowledgement})\n WHERE run_id = ${runId} AND status = 'running' AND cancel_requested_at_ms IS NOT NULL\n AND owner_host_id = ${owner.hostId} AND owner_pid = ${owner.pid} AND owner_nonce = ${owner.nonce}\n RETURNING run_id\n `.pipe(Effect.map((rows) => rows.length > 0))\n )\n }).pipe(observeExit)\n )\n\n return RunStore.of({\n create,\n get,\n lineage,\n latestRound,\n requestCancel,\n acknowledgeCancel,\n requestCancelLineage,\n claim,\n claimAndOwn,\n activate,\n abandonClaim,\n recoverClaim,\n heartbeat,\n reconfirm,\n transitionOwned,\n steal\n })\n }\n)\n\n/**\n * Constructs a stub `RunStore` whose direct operations fail and whose\n * compare-and-swap operations report typed losses until overridden.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const makeNoop = (overrides: Partial<Service> = {}): Service => {\n const unavailable = (method: string) =>\n Effect.fail(runStoreError(method, \"persistence_failed\", \"no run store in this environment\", method))\n return RunStore.of({\n create: Effect.fn(\"RunStore.create\")(() => unavailable(\"create\")),\n get: Effect.fn(\"RunStore.get\")(() => unavailable(\"get\")),\n lineage: Effect.fn(\"RunStore.lineage\")(() => unavailable(\"lineage\")),\n latestRound: Effect.fn(\"RunStore.latestRound\")(() => unavailable(\"latestRound\")),\n requestCancel: Effect.fn(\"RunStore.requestCancel\")(() => Effect.succeed(notFound)),\n acknowledgeCancel: Effect.fn(\"RunStore.acknowledgeCancel\")(() => Effect.succeed(false)),\n requestCancelLineage: Effect.fn(\"RunStore.requestCancelLineage\")(() => unavailable(\"requestCancelLineage\")),\n claim: Effect.fn(\"RunStore.claim\")(() => Effect.succeed(notFound)),\n claimAndOwn: Effect.fn(\"RunStore.claimAndOwn\")(() => Effect.succeed(notFound)),\n activate: Effect.fn(\"RunStore.activate\")(() => Effect.succeed(claimLost)),\n abandonClaim: Effect.fn(\"RunStore.abandonClaim\")(() => Effect.succeed(claimLost)),\n recoverClaim: Effect.fn(\"RunStore.recoverClaim\")(() => Effect.succeed(notFound)),\n heartbeat: Effect.fn(\"RunStore.heartbeat\")(() => Effect.succeed(notFound)),\n reconfirm: Effect.fn(\"RunStore.reconfirm\")(() => Effect.succeed(notFound)),\n transitionOwned: Effect.fn(\"RunStore.transitionOwned\")(() => Effect.succeed(notFound)),\n steal: Effect.fn(\"RunStore.steal\")(() => Effect.succeed(notFound)),\n ...overrides\n })\n}\n\n/**\n * Provides a stub `RunStore`.\n *\n * @since 0.1.0\n * @category layers\n */\nexport const layerNoop = (overrides: Partial<Service> = {}): Layer.Layer<RunStore> =>\n Layer.succeed(RunStore)(makeNoop(overrides))\n\n/**\n * Provides the database-backed `RunStore` over an explicitly injected\n * consensus strategy.\n *\n * Arbitration \u2014 claims, activation, steals, heartbeats, and fence checks \u2014 is\n * delegated to the `Consensus` service in context; the run row mirrors the\n * outcome in the same transaction. Provide the SAME strategy instance the\n * journal fences through (`SqlJournal.layerWith`), because\n * `Consensus.layerLocal` keeps its leases per layer build.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerWith: Layer.Layer<RunStore, never, Consensus | DurableWriter | SqlClient.SqlClient> = Layer.effect(\n RunStore,\n make\n)\n\n/**\n * Provides the database-backed `RunStore` arbitrated by the database-backed\n * strategy, `SqlConsensus`, over the same client \u2014 the strategy\n * `SqlJournal.layer` fences through. Use {@link layerWith} to inject a\n * different strategy.\n *\n * @since 0.1.0\n * @category layers\n */\nexport const layer: Layer.Layer<RunStore, never, DurableWriter | SqlClient.SqlClient> = layerWith.pipe(\n Layer.provide(SqlConsensus.layer)\n)\n"],
5
+ "mappings": "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",
6
+ "names": ["current"]
7
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Standard metric definitions for fenced run persistence.
3
+ *
4
+ * This module only defines the metric handles, following the shape of Effect's
5
+ * `ClusterMetrics`. `RunStore` updates them as compare-and-swap outcomes are
6
+ * decided, so every driver that persists through the store — the engine, a
7
+ * sweeper, a foreign process — lands in the same counters. No exporter ships
8
+ * in this package; provide one — for example `@smthrs/observability` —
9
+ * and these counters appear in it.
10
+ *
11
+ * Each exported record maps an outcome's `_tag` to an attributed view of the
12
+ * counter, so an update is a lookup rather than a branch:
13
+ * `Metric.update(RunStoreMetrics.claim[outcome._tag], 1)`.
14
+ *
15
+ * @since 0.1.0
16
+ */
17
+ import * as Metric from "effect/Metric";
18
+ /**
19
+ * Counter over ownership claim compare-and-swaps, dimensioned by `op`
20
+ * (`claim`, `claim_and_own`, `activate`, `abandon_claim`, `recover_claim`, or
21
+ * `steal`) and `outcome` (the
22
+ * operation's result tag in snake case, or `failure` or `interrupt` when the
23
+ * call did not return one).
24
+ *
25
+ * @category metrics
26
+ * @since 0.1.0
27
+ */
28
+ export declare const claims: Metric.Counter<number>;
29
+ /**
30
+ * Counter over fenced run state transitions, dimensioned by `outcome`
31
+ * (`transitioned`, `fence_lost`, `not_found`, `guard_failed`, `failure`, or
32
+ * `interrupt`) and `to` (the requested target status).
33
+ *
34
+ * @category metrics
35
+ * @since 0.1.0
36
+ */
37
+ export declare const transitions: Metric.Counter<number>;
38
+ /**
39
+ * Counter over fenced ownership heartbeats, dimensioned by `outcome`
40
+ * (`updated`, `fence_lost`, `not_found`, `failure`, or `interrupt`). A
41
+ * `fence_lost` heartbeat is the fencing event: the writer discovered another
42
+ * owner holds the run.
43
+ *
44
+ * @category metrics
45
+ * @since 0.1.0
46
+ */
47
+ export declare const heartbeats: Metric.Counter<number>;
48
+ /**
49
+ * `claims` views for `RunStore.claim`, keyed by `ClaimOutcome` tag.
50
+ *
51
+ * @category metrics
52
+ * @since 0.1.0
53
+ */
54
+ export declare const claim: {
55
+ readonly NotFound: Metric.Metric<number, Metric.CounterState<number>>;
56
+ readonly Claimed: Metric.Metric<number, Metric.CounterState<number>>;
57
+ readonly AlreadyClaimed: Metric.Metric<number, Metric.CounterState<number>>;
58
+ readonly HeartbeatFresh: Metric.Metric<number, Metric.CounterState<number>>;
59
+ readonly SnapshotChanged: Metric.Metric<number, Metric.CounterState<number>>;
60
+ };
61
+ /**
62
+ * `claims` views for `RunStore.claimAndOwn`, keyed by `ClaimAndOwnOutcome`
63
+ * tag.
64
+ *
65
+ * @category metrics
66
+ * @since 0.1.0
67
+ */
68
+ export declare const claimAndOwn: {
69
+ readonly NotFound: Metric.Metric<number, Metric.CounterState<number>>;
70
+ readonly Activated: Metric.Metric<number, Metric.CounterState<number>>;
71
+ readonly AlreadyClaimed: Metric.Metric<number, Metric.CounterState<number>>;
72
+ readonly HeartbeatFresh: Metric.Metric<number, Metric.CounterState<number>>;
73
+ readonly SnapshotChanged: Metric.Metric<number, Metric.CounterState<number>>;
74
+ readonly EvidenceRequired: Metric.Metric<number, Metric.CounterState<number>>;
75
+ };
76
+ /**
77
+ * `claims` views for `RunStore.activate`, keyed by `ActivateOutcome` tag. A
78
+ * `claim_lost` activation is a fencing event: the held claim was recovered or
79
+ * replaced before the activation ran.
80
+ *
81
+ * @category metrics
82
+ * @since 0.1.0
83
+ */
84
+ export declare const activate: {
85
+ readonly Activated: Metric.Metric<number, Metric.CounterState<number>>;
86
+ readonly SnapshotChanged: Metric.Metric<number, Metric.CounterState<number>>;
87
+ readonly ClaimLost: Metric.Metric<number, Metric.CounterState<number>>;
88
+ };
89
+ /**
90
+ * `claims` views for `RunStore.abandonClaim`, keyed by outcome tag.
91
+ *
92
+ * @category metrics
93
+ * @since 1.0.0-rc.0
94
+ */
95
+ export declare const abandonClaim: {
96
+ readonly ClaimLost: Metric.Metric<number, Metric.CounterState<number>>;
97
+ readonly Abandoned: Metric.Metric<number, Metric.CounterState<number>>;
98
+ };
99
+ /**
100
+ * `claims` views for `RunStore.recoverClaim`, keyed by outcome tag.
101
+ *
102
+ * @category metrics
103
+ * @since 1.0.0-rc.0
104
+ */
105
+ export declare const recoverClaim: {
106
+ readonly NotFound: Metric.Metric<number, Metric.CounterState<number>>;
107
+ readonly Recovered: Metric.Metric<number, Metric.CounterState<number>>;
108
+ readonly ClaimFresh: Metric.Metric<number, Metric.CounterState<number>>;
109
+ readonly ClaimChanged: Metric.Metric<number, Metric.CounterState<number>>;
110
+ readonly LivenessUnconfirmed: Metric.Metric<number, Metric.CounterState<number>>;
111
+ };
112
+ /**
113
+ * `claims` views for `RunStore.steal`, keyed by `StealOutcome` tag.
114
+ *
115
+ * @category metrics
116
+ * @since 0.1.0
117
+ */
118
+ export declare const steal: {
119
+ readonly NotFound: Metric.Metric<number, Metric.CounterState<number>>;
120
+ readonly Claimed: Metric.Metric<number, Metric.CounterState<number>>;
121
+ readonly AlreadyClaimed: Metric.Metric<number, Metric.CounterState<number>>;
122
+ readonly HeartbeatFresh: Metric.Metric<number, Metric.CounterState<number>>;
123
+ readonly SnapshotChanged: Metric.Metric<number, Metric.CounterState<number>>;
124
+ readonly LivenessUnconfirmed: Metric.Metric<number, Metric.CounterState<number>>;
125
+ };
126
+ /**
127
+ * `heartbeats` views keyed by `HeartbeatOutcome` tag.
128
+ *
129
+ * @category metrics
130
+ * @since 0.1.0
131
+ */
132
+ export declare const heartbeat: {
133
+ readonly FenceLost: Metric.Metric<number, Metric.CounterState<number>>;
134
+ readonly NotFound: Metric.Metric<number, Metric.CounterState<number>>;
135
+ readonly Updated: Metric.Metric<number, Metric.CounterState<number>>;
136
+ };
137
+ /**
138
+ * `transitions` views keyed by `TransitionOutcome` tag. `RunStore` adds the
139
+ * `to` attribute at the update site, because the target status is call input
140
+ * rather than a closed set this module should restate.
141
+ *
142
+ * @category metrics
143
+ * @since 0.1.0
144
+ */
145
+ export declare const transition: {
146
+ readonly FenceLost: Metric.Metric<number, Metric.CounterState<number>>;
147
+ readonly NotFound: Metric.Metric<number, Metric.CounterState<number>>;
148
+ readonly Transitioned: Metric.Metric<number, Metric.CounterState<number>>;
149
+ readonly GuardFailed: Metric.Metric<number, Metric.CounterState<number>>;
150
+ };
151
+ //# sourceMappingURL=RunStoreMetrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RunStoreMetrics.d.ts","sourceRoot":"","sources":["../../src/RunStoreMetrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC;;;;;;;;;GASG;AACH,eAAO,MAAM,MAAM,wBAEjB,CAAA;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,wBAEtB,CAAA;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,wBAErB,CAAA;AAiBF;;;;;GAKG;AACH,eAAO,MAAM,KAAK;;;;;;CAAgD,CAAA;AAElE;;;;;;GAMG;AACH,eAAO,MAAM,WAAW;;;;;;;CAIvB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;;;;CAIpB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;CAIxB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;;CAIxB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,KAAK;;;;;;;CAIjB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,SAAS;;;;CAA2E,CAAA;AAEjG;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU;;;;;CAItB,CAAA"}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var RunStoreMetrics_exports = {};
30
+ __export(RunStoreMetrics_exports, {
31
+ abandonClaim: () => abandonClaim,
32
+ activate: () => activate,
33
+ claim: () => claim,
34
+ claimAndOwn: () => claimAndOwn,
35
+ claims: () => claims,
36
+ heartbeat: () => heartbeat,
37
+ heartbeats: () => heartbeats,
38
+ recoverClaim: () => recoverClaim,
39
+ steal: () => steal,
40
+ transition: () => transition,
41
+ transitions: () => transitions
42
+ });
43
+ module.exports = __toCommonJS(RunStoreMetrics_exports);
44
+ var Metric = __toESM(require("effect/Metric"), 1);
45
+ const claims = Metric.counter("flows_run_claims", {
46
+ description: "Ownership claim compare-and-swaps by operation and outcome"
47
+ });
48
+ const transitions = Metric.counter("flows_run_transitions", {
49
+ description: "Fenced run state transitions by outcome and target status"
50
+ });
51
+ const heartbeats = Metric.counter("flows_run_heartbeats", {
52
+ description: "Fenced ownership heartbeats by outcome"
53
+ });
54
+ const attributeValue = (tag) => tag.replace(/(?<=[a-z0-9])(?=[A-Z])/g, "_").toLowerCase();
55
+ const byOutcome = (metric, attributes, tags) => Object.fromEntries(
56
+ tags.map((tag) => [tag, Metric.withAttributes(metric, { ...attributes, outcome: attributeValue(tag) })])
57
+ );
58
+ const claimTags = ["Claimed", "NotFound", "AlreadyClaimed", "HeartbeatFresh", "SnapshotChanged"];
59
+ const claim = byOutcome(claims, { op: "claim" }, claimTags);
60
+ const claimAndOwn = byOutcome(
61
+ claims,
62
+ { op: "claim_and_own" },
63
+ ["Activated", "NotFound", "AlreadyClaimed", "HeartbeatFresh", "SnapshotChanged", "EvidenceRequired"]
64
+ );
65
+ const activate = byOutcome(
66
+ claims,
67
+ { op: "activate" },
68
+ ["Activated", "ClaimLost", "SnapshotChanged"]
69
+ );
70
+ const abandonClaim = byOutcome(
71
+ claims,
72
+ { op: "abandon_claim" },
73
+ ["Abandoned", "ClaimLost"]
74
+ );
75
+ const recoverClaim = byOutcome(
76
+ claims,
77
+ { op: "recover_claim" },
78
+ ["Recovered", "NotFound", "ClaimFresh", "ClaimChanged", "LivenessUnconfirmed"]
79
+ );
80
+ const steal = byOutcome(
81
+ claims,
82
+ { op: "steal" },
83
+ ["Claimed", "NotFound", "AlreadyClaimed", "HeartbeatFresh", "SnapshotChanged", "LivenessUnconfirmed"]
84
+ );
85
+ const heartbeat = byOutcome(heartbeats, {}, ["Updated", "FenceLost", "NotFound"]);
86
+ const transition = byOutcome(
87
+ transitions,
88
+ {},
89
+ ["Transitioned", "FenceLost", "NotFound", "GuardFailed"]
90
+ );
91
+ //# sourceMappingURL=RunStoreMetrics.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/RunStoreMetrics.ts"],
4
+ "sourcesContent": ["/**\n * Standard metric definitions for fenced run persistence.\n *\n * This module only defines the metric handles, following the shape of Effect's\n * `ClusterMetrics`. `RunStore` updates them as compare-and-swap outcomes are\n * decided, so every driver that persists through the store \u2014 the engine, a\n * sweeper, a foreign process \u2014 lands in the same counters. No exporter ships\n * in this package; provide one \u2014 for example `@smthrs/observability` \u2014\n * and these counters appear in it.\n *\n * Each exported record maps an outcome's `_tag` to an attributed view of the\n * counter, so an update is a lookup rather than a branch:\n * `Metric.update(RunStoreMetrics.claim[outcome._tag], 1)`.\n *\n * @since 0.1.0\n */\n\nimport * as Metric from \"effect/Metric\"\n\n/**\n * Counter over ownership claim compare-and-swaps, dimensioned by `op`\n * (`claim`, `claim_and_own`, `activate`, `abandon_claim`, `recover_claim`, or\n * `steal`) and `outcome` (the\n * operation's result tag in snake case, or `failure` or `interrupt` when the\n * call did not return one).\n *\n * @category metrics\n * @since 0.1.0\n */\nexport const claims = Metric.counter(\"flows_run_claims\", {\n description: \"Ownership claim compare-and-swaps by operation and outcome\"\n})\n\n/**\n * Counter over fenced run state transitions, dimensioned by `outcome`\n * (`transitioned`, `fence_lost`, `not_found`, `guard_failed`, `failure`, or\n * `interrupt`) and `to` (the requested target status).\n *\n * @category metrics\n * @since 0.1.0\n */\nexport const transitions = Metric.counter(\"flows_run_transitions\", {\n description: \"Fenced run state transitions by outcome and target status\"\n})\n\n/**\n * Counter over fenced ownership heartbeats, dimensioned by `outcome`\n * (`updated`, `fence_lost`, `not_found`, `failure`, or `interrupt`). A\n * `fence_lost` heartbeat is the fencing event: the writer discovered another\n * owner holds the run.\n *\n * @category metrics\n * @since 0.1.0\n */\nexport const heartbeats = Metric.counter(\"flows_run_heartbeats\", {\n description: \"Fenced ownership heartbeats by outcome\"\n})\n\n/** Rewrites an outcome tag (`AlreadyClaimed`) as an attribute value (`already_claimed`). */\nconst attributeValue = (tag: string): string => tag.replace(/(?<=[a-z0-9])(?=[A-Z])/g, \"_\").toLowerCase()\n\n/** Builds the `_tag`-keyed record of attributed counter views for one operation. */\nconst byOutcome = <const Tags extends ReadonlyArray<string>>(\n metric: Metric.Metric<number, Metric.CounterState<number>>,\n attributes: Readonly<Record<string, string>>,\n tags: Tags\n): { readonly [Tag in Tags[number]]: Metric.Metric<number, Metric.CounterState<number>> } =>\n Object.fromEntries(\n tags.map((tag) => [tag, Metric.withAttributes(metric, { ...attributes, outcome: attributeValue(tag) })])\n ) as { [Tag in Tags[number]]: Metric.Metric<number, Metric.CounterState<number>> }\n\nconst claimTags = [\"Claimed\", \"NotFound\", \"AlreadyClaimed\", \"HeartbeatFresh\", \"SnapshotChanged\"] as const\n\n/**\n * `claims` views for `RunStore.claim`, keyed by `ClaimOutcome` tag.\n *\n * @category metrics\n * @since 0.1.0\n */\nexport const claim = byOutcome(claims, { op: \"claim\" }, claimTags)\n\n/**\n * `claims` views for `RunStore.claimAndOwn`, keyed by `ClaimAndOwnOutcome`\n * tag.\n *\n * @category metrics\n * @since 0.1.0\n */\nexport const claimAndOwn = byOutcome(\n claims,\n { op: \"claim_and_own\" },\n [\"Activated\", \"NotFound\", \"AlreadyClaimed\", \"HeartbeatFresh\", \"SnapshotChanged\", \"EvidenceRequired\"] as const\n)\n\n/**\n * `claims` views for `RunStore.activate`, keyed by `ActivateOutcome` tag. A\n * `claim_lost` activation is a fencing event: the held claim was recovered or\n * replaced before the activation ran.\n *\n * @category metrics\n * @since 0.1.0\n */\nexport const activate = byOutcome(\n claims,\n { op: \"activate\" },\n [\"Activated\", \"ClaimLost\", \"SnapshotChanged\"] as const\n)\n\n/**\n * `claims` views for `RunStore.abandonClaim`, keyed by outcome tag.\n *\n * @category metrics\n * @since 1.0.0-rc.0\n */\nexport const abandonClaim = byOutcome(\n claims,\n { op: \"abandon_claim\" },\n [\"Abandoned\", \"ClaimLost\"] as const\n)\n\n/**\n * `claims` views for `RunStore.recoverClaim`, keyed by outcome tag.\n *\n * @category metrics\n * @since 1.0.0-rc.0\n */\nexport const recoverClaim = byOutcome(\n claims,\n { op: \"recover_claim\" },\n [\"Recovered\", \"NotFound\", \"ClaimFresh\", \"ClaimChanged\", \"LivenessUnconfirmed\"] as const\n)\n\n/**\n * `claims` views for `RunStore.steal`, keyed by `StealOutcome` tag.\n *\n * @category metrics\n * @since 0.1.0\n */\nexport const steal = byOutcome(\n claims,\n { op: \"steal\" },\n [\"Claimed\", \"NotFound\", \"AlreadyClaimed\", \"HeartbeatFresh\", \"SnapshotChanged\", \"LivenessUnconfirmed\"] as const\n)\n\n/**\n * `heartbeats` views keyed by `HeartbeatOutcome` tag.\n *\n * @category metrics\n * @since 0.1.0\n */\nexport const heartbeat = byOutcome(heartbeats, {}, [\"Updated\", \"FenceLost\", \"NotFound\"] as const)\n\n/**\n * `transitions` views keyed by `TransitionOutcome` tag. `RunStore` adds the\n * `to` attribute at the update site, because the target status is call input\n * rather than a closed set this module should restate.\n *\n * @category metrics\n * @since 0.1.0\n */\nexport const transition = byOutcome(\n transitions,\n {},\n [\"Transitioned\", \"FenceLost\", \"NotFound\", \"GuardFailed\"] as const\n)\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBA,aAAwB;AAYjB,MAAM,SAAS,OAAO,QAAQ,oBAAoB;AAAA,EACvD,aAAa;AACf,CAAC;AAUM,MAAM,cAAc,OAAO,QAAQ,yBAAyB;AAAA,EACjE,aAAa;AACf,CAAC;AAWM,MAAM,aAAa,OAAO,QAAQ,wBAAwB;AAAA,EAC/D,aAAa;AACf,CAAC;AAGD,MAAM,iBAAiB,CAAC,QAAwB,IAAI,QAAQ,2BAA2B,GAAG,EAAE,YAAY;AAGxG,MAAM,YAAY,CAChB,QACA,YACA,SAEA,OAAO;AAAA,EACL,KAAK,IAAI,CAAC,QAAQ,CAAC,KAAK,OAAO,eAAe,QAAQ,EAAE,GAAG,YAAY,SAAS,eAAe,GAAG,EAAE,CAAC,CAAC,CAAC;AACzG;AAEF,MAAM,YAAY,CAAC,WAAW,YAAY,kBAAkB,kBAAkB,iBAAiB;AAQxF,MAAM,QAAQ,UAAU,QAAQ,EAAE,IAAI,QAAQ,GAAG,SAAS;AAS1D,MAAM,cAAc;AAAA,EACzB;AAAA,EACA,EAAE,IAAI,gBAAgB;AAAA,EACtB,CAAC,aAAa,YAAY,kBAAkB,kBAAkB,mBAAmB,kBAAkB;AACrG;AAUO,MAAM,WAAW;AAAA,EACtB;AAAA,EACA,EAAE,IAAI,WAAW;AAAA,EACjB,CAAC,aAAa,aAAa,iBAAiB;AAC9C;AAQO,MAAM,eAAe;AAAA,EAC1B;AAAA,EACA,EAAE,IAAI,gBAAgB;AAAA,EACtB,CAAC,aAAa,WAAW;AAC3B;AAQO,MAAM,eAAe;AAAA,EAC1B;AAAA,EACA,EAAE,IAAI,gBAAgB;AAAA,EACtB,CAAC,aAAa,YAAY,cAAc,gBAAgB,qBAAqB;AAC/E;AAQO,MAAM,QAAQ;AAAA,EACnB;AAAA,EACA,EAAE,IAAI,QAAQ;AAAA,EACd,CAAC,WAAW,YAAY,kBAAkB,kBAAkB,mBAAmB,qBAAqB;AACtG;AAQO,MAAM,YAAY,UAAU,YAAY,CAAC,GAAG,CAAC,WAAW,aAAa,UAAU,CAAU;AAUzF,MAAM,aAAa;AAAA,EACxB;AAAA,EACA,CAAC;AAAA,EACD,CAAC,gBAAgB,aAAa,YAAY,aAAa;AACzD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Durable run state: what is running now, and who owns it.
3
+ *
4
+ * `RunStore` and `AttemptStore` hold the executable authoritative state that
5
+ * recovery reads; `Ownership` supplies the liveness evidence and heartbeat
6
+ * supervision that arbitrate it. The fencing token they trade in, `OwnerId`,
7
+ * is defined by `@smthrs/journal`, because the journal is what it fences, and
8
+ * is re-exported from `Ownership` here, so importing it needs no second
9
+ * package.
10
+ *
11
+ * This entry point is browser-bundleable: every service here is written
12
+ * against the driver-neutral `@smthrs/database` contract. The test double,
13
+ * which binds a Node SQLite database, lives under an explicit subpath:
14
+ *
15
+ * ```ts
16
+ * import { AttemptStore, Ownership, RunStore } from "@smthrs/run-store"
17
+ * import * as TestRunStore from "@smthrs/run-store/test/TestRunStore"
18
+ * ```
19
+ *
20
+ * @since 0.1.0
21
+ */
22
+ /**
23
+ * @category services
24
+ * @since 0.1.0
25
+ */
26
+ export * as RunStore from "./RunStore.ts";
27
+ /**
28
+ * @category metrics
29
+ * @since 0.1.0
30
+ */
31
+ export * as RunStoreMetrics from "./RunStoreMetrics.ts";
32
+ /**
33
+ * @category ownership
34
+ * @since 0.1.0
35
+ */
36
+ export * as Ownership from "./Ownership.ts";
37
+ /**
38
+ * @category services
39
+ * @since 0.1.0
40
+ */
41
+ export * as AttemptStore from "./AttemptStore.ts";
42
+ /**
43
+ * @category migrations
44
+ * @since 0.1.0
45
+ */
46
+ export * as Migrations from "./Migrations.ts";
47
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;GAGG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;;;GAGG;AACH,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AAEvD;;;GAGG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C;;;GAGG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AAEjD;;;GAGG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA"}