@smthrs/engine-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.
- package/CHANGELOG.md +247 -0
- package/LICENSE +21 -0
- package/README.md +146 -2
- package/dist/cjs/ArtifactGc.d.ts +235 -0
- package/dist/cjs/ArtifactGc.d.ts.map +1 -0
- package/dist/cjs/ArtifactGc.js +183 -0
- package/dist/cjs/ArtifactGc.js.map +7 -0
- package/dist/cjs/ArtifactSync.d.ts +164 -0
- package/dist/cjs/ArtifactSync.d.ts.map +1 -0
- package/dist/cjs/ArtifactSync.js +144 -0
- package/dist/cjs/ArtifactSync.js.map +7 -0
- package/dist/cjs/CacheSync.d.ts +112 -0
- package/dist/cjs/CacheSync.d.ts.map +1 -0
- package/dist/cjs/CacheSync.js +64 -0
- package/dist/cjs/CacheSync.js.map +7 -0
- package/dist/cjs/DeferredClockFold.d.ts +354 -0
- package/dist/cjs/DeferredClockFold.d.ts.map +1 -0
- package/dist/cjs/DeferredClockFold.js +335 -0
- package/dist/cjs/DeferredClockFold.js.map +7 -0
- package/dist/cjs/DisasterRecovery.d.ts +431 -0
- package/dist/cjs/DisasterRecovery.d.ts.map +1 -0
- package/dist/cjs/DisasterRecovery.js +453 -0
- package/dist/cjs/DisasterRecovery.js.map +7 -0
- package/dist/cjs/DurableEngineState.d.ts +776 -0
- package/dist/cjs/DurableEngineState.d.ts.map +1 -0
- package/dist/cjs/DurableEngineState.js +1536 -0
- package/dist/cjs/DurableEngineState.js.map +7 -0
- package/dist/cjs/EngineStore.d.ts +158 -0
- package/dist/cjs/EngineStore.d.ts.map +1 -0
- package/dist/cjs/EngineStore.js +398 -0
- package/dist/cjs/EngineStore.js.map +7 -0
- package/dist/cjs/EngineStoreMetrics.d.ts +276 -0
- package/dist/cjs/EngineStoreMetrics.d.ts.map +1 -0
- package/dist/cjs/EngineStoreMetrics.js +135 -0
- package/dist/cjs/EngineStoreMetrics.js.map +7 -0
- package/dist/cjs/Errors.d.ts +22 -0
- package/dist/cjs/Errors.d.ts.map +1 -0
- package/dist/cjs/Errors.js +39 -0
- package/dist/cjs/Errors.js.map +7 -0
- package/dist/cjs/EventTypes.d.ts +27 -0
- package/dist/cjs/EventTypes.d.ts.map +1 -0
- package/dist/cjs/EventTypes.js +37 -0
- package/dist/cjs/EventTypes.js.map +7 -0
- package/dist/cjs/ExecutionFacts.d.ts +59 -0
- package/dist/cjs/ExecutionFacts.d.ts.map +1 -0
- package/dist/cjs/ExecutionFacts.js +123 -0
- package/dist/cjs/ExecutionFacts.js.map +7 -0
- package/dist/cjs/ExecutionSnapshot.d.ts +117 -0
- package/dist/cjs/ExecutionSnapshot.d.ts.map +1 -0
- package/dist/cjs/ExecutionSnapshot.js +111 -0
- package/dist/cjs/ExecutionSnapshot.js.map +7 -0
- package/dist/cjs/Inconsistency.d.ts +154 -0
- package/dist/cjs/Inconsistency.d.ts.map +1 -0
- package/dist/cjs/Inconsistency.js +135 -0
- package/dist/cjs/Inconsistency.js.map +7 -0
- package/dist/cjs/Migrations.d.ts +56 -0
- package/dist/cjs/Migrations.d.ts.map +1 -0
- package/dist/cjs/Migrations.js +78 -0
- package/dist/cjs/Migrations.js.map +7 -0
- package/dist/cjs/OwnerIdentity.d.ts +91 -0
- package/dist/cjs/OwnerIdentity.d.ts.map +1 -0
- package/dist/cjs/OwnerIdentity.js +65 -0
- package/dist/cjs/OwnerIdentity.js.map +7 -0
- package/dist/cjs/PlanInputStore.d.ts +110 -0
- package/dist/cjs/PlanInputStore.d.ts.map +1 -0
- package/dist/cjs/PlanInputStore.js +224 -0
- package/dist/cjs/PlanInputStore.js.map +7 -0
- package/dist/cjs/PlanMergeStore.d.ts +113 -0
- package/dist/cjs/PlanMergeStore.d.ts.map +1 -0
- package/dist/cjs/PlanMergeStore.js +255 -0
- package/dist/cjs/PlanMergeStore.js.map +7 -0
- package/dist/cjs/PlanScheduler.d.ts +363 -0
- package/dist/cjs/PlanScheduler.d.ts.map +1 -0
- package/dist/cjs/PlanScheduler.js +1090 -0
- package/dist/cjs/PlanScheduler.js.map +7 -0
- package/dist/cjs/Reconciliation.d.ts +175 -0
- package/dist/cjs/Reconciliation.d.ts.map +1 -0
- package/dist/cjs/Reconciliation.js +119 -0
- package/dist/cjs/Reconciliation.js.map +7 -0
- package/dist/cjs/ReplayOnly.d.ts +91 -0
- package/dist/cjs/ReplayOnly.d.ts.map +1 -0
- package/dist/cjs/ReplayOnly.js +52 -0
- package/dist/cjs/ReplayOnly.js.map +7 -0
- package/dist/cjs/Retention.d.ts +113 -0
- package/dist/cjs/Retention.d.ts.map +1 -0
- package/dist/cjs/Retention.js +99 -0
- package/dist/cjs/Retention.js.map +7 -0
- package/dist/cjs/RunCatalogRead.d.ts +17 -0
- package/dist/cjs/RunCatalogRead.d.ts.map +1 -0
- package/dist/cjs/RunCatalogRead.js +33 -0
- package/dist/cjs/RunCatalogRead.js.map +7 -0
- package/dist/cjs/RunChangeFeed.d.ts +75 -0
- package/dist/cjs/RunChangeFeed.d.ts.map +1 -0
- package/dist/cjs/RunChangeFeed.js +96 -0
- package/dist/cjs/RunChangeFeed.js.map +7 -0
- package/dist/cjs/RunState.d.ts +116 -0
- package/dist/cjs/RunState.d.ts.map +1 -0
- package/dist/cjs/RunState.js +76 -0
- package/dist/cjs/RunState.js.map +7 -0
- package/dist/cjs/Selection.d.ts +495 -0
- package/dist/cjs/Selection.d.ts.map +1 -0
- package/dist/cjs/Selection.js +288 -0
- package/dist/cjs/Selection.js.map +7 -0
- package/dist/cjs/SelectionStore.d.ts +141 -0
- package/dist/cjs/SelectionStore.d.ts.map +1 -0
- package/dist/cjs/SelectionStore.js +229 -0
- package/dist/cjs/SelectionStore.js.map +7 -0
- package/dist/cjs/StepBoundary.d.ts +501 -0
- package/dist/cjs/StepBoundary.d.ts.map +1 -0
- package/dist/cjs/StepBoundary.js +619 -0
- package/dist/cjs/StepBoundary.js.map +7 -0
- package/dist/cjs/StepCacheFold.d.ts +148 -0
- package/dist/cjs/StepCacheFold.d.ts.map +1 -0
- package/dist/cjs/StepCacheFold.js +131 -0
- package/dist/cjs/StepCacheFold.js.map +7 -0
- package/dist/cjs/StepSandbox.d.ts +78 -0
- package/dist/cjs/StepSandbox.d.ts.map +1 -0
- package/dist/cjs/StepSandbox.js +75 -0
- package/dist/cjs/StepSandbox.js.map +7 -0
- package/dist/cjs/WakeBus.d.ts +116 -0
- package/dist/cjs/WakeBus.d.ts.map +1 -0
- package/dist/cjs/WakeBus.js +89 -0
- package/dist/cjs/WakeBus.js.map +7 -0
- package/dist/cjs/WorkspaceSandbox.d.ts +619 -0
- package/dist/cjs/WorkspaceSandbox.d.ts.map +1 -0
- package/dist/cjs/WorkspaceSandbox.js +711 -0
- package/dist/cjs/WorkspaceSandbox.js.map +7 -0
- package/dist/cjs/index.d.ts +157 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +95 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/ActionPersistence.d.ts +276 -0
- package/dist/cjs/internal/ActionPersistence.d.ts.map +1 -0
- package/dist/cjs/internal/ActionPersistence.js +1395 -0
- package/dist/cjs/internal/ActionPersistence.js.map +7 -0
- package/dist/cjs/internal/ArtifactRoots.d.ts +38 -0
- package/dist/cjs/internal/ArtifactRoots.d.ts.map +1 -0
- package/dist/cjs/internal/ArtifactRoots.js +89 -0
- package/dist/cjs/internal/ArtifactRoots.js.map +7 -0
- package/dist/cjs/internal/AttemptAdmission.d.ts +53 -0
- package/dist/cjs/internal/AttemptAdmission.d.ts.map +1 -0
- package/dist/cjs/internal/AttemptAdmission.js +52 -0
- package/dist/cjs/internal/AttemptAdmission.js.map +7 -0
- package/dist/cjs/internal/AttemptLifecycle.d.ts +245 -0
- package/dist/cjs/internal/AttemptLifecycle.d.ts.map +1 -0
- package/dist/cjs/internal/AttemptLifecycle.js +112 -0
- package/dist/cjs/internal/AttemptLifecycle.js.map +7 -0
- package/dist/cjs/internal/AttemptProbe.d.ts +53 -0
- package/dist/cjs/internal/AttemptProbe.d.ts.map +1 -0
- package/dist/cjs/internal/AttemptProbe.js +66 -0
- package/dist/cjs/internal/AttemptProbe.js.map +7 -0
- package/dist/cjs/internal/CacheAdmission.d.ts +79 -0
- package/dist/cjs/internal/CacheAdmission.d.ts.map +1 -0
- package/dist/cjs/internal/CacheAdmission.js +71 -0
- package/dist/cjs/internal/CacheAdmission.js.map +7 -0
- package/dist/cjs/internal/CacheAgeHistory.d.ts +27 -0
- package/dist/cjs/internal/CacheAgeHistory.d.ts.map +1 -0
- package/dist/cjs/internal/CacheAgeHistory.js +61 -0
- package/dist/cjs/internal/CacheAgeHistory.js.map +7 -0
- package/dist/cjs/internal/CacheAgeVerdicts.d.ts +15 -0
- package/dist/cjs/internal/CacheAgeVerdicts.d.ts.map +1 -0
- package/dist/cjs/internal/CacheAgeVerdicts.js +73 -0
- package/dist/cjs/internal/CacheAgeVerdicts.js.map +7 -0
- package/dist/cjs/internal/CacheOutputPolicy.d.ts +24 -0
- package/dist/cjs/internal/CacheOutputPolicy.d.ts.map +1 -0
- package/dist/cjs/internal/CacheOutputPolicy.js +75 -0
- package/dist/cjs/internal/CacheOutputPolicy.js.map +7 -0
- package/dist/cjs/internal/CachePublication.d.ts +97 -0
- package/dist/cjs/internal/CachePublication.d.ts.map +1 -0
- package/dist/cjs/internal/CachePublication.js +80 -0
- package/dist/cjs/internal/CachePublication.js.map +7 -0
- package/dist/cjs/internal/CallFacts.d.ts +15 -0
- package/dist/cjs/internal/CallFacts.d.ts.map +1 -0
- package/dist/cjs/internal/CallFacts.js +97 -0
- package/dist/cjs/internal/CallFacts.js.map +7 -0
- package/dist/cjs/internal/CopiedRecord.d.ts +61 -0
- package/dist/cjs/internal/CopiedRecord.d.ts.map +1 -0
- package/dist/cjs/internal/CopiedRecord.js +134 -0
- package/dist/cjs/internal/CopiedRecord.js.map +7 -0
- package/dist/cjs/internal/DeferredPersistence.d.ts +108 -0
- package/dist/cjs/internal/DeferredPersistence.d.ts.map +1 -0
- package/dist/cjs/internal/DeferredPersistence.js +270 -0
- package/dist/cjs/internal/DeferredPersistence.js.map +7 -0
- package/dist/cjs/internal/EffectRecords.d.ts +92 -0
- package/dist/cjs/internal/EffectRecords.d.ts.map +1 -0
- package/dist/cjs/internal/EffectRecords.js +63 -0
- package/dist/cjs/internal/EffectRecords.js.map +7 -0
- package/dist/cjs/internal/EngineJj.d.ts +30 -0
- package/dist/cjs/internal/EngineJj.d.ts.map +1 -0
- package/dist/cjs/internal/EngineJj.js +42 -0
- package/dist/cjs/internal/EngineJj.js.map +7 -0
- package/dist/cjs/internal/EngineStateSchema.d.ts +68 -0
- package/dist/cjs/internal/EngineStateSchema.d.ts.map +1 -0
- package/dist/cjs/internal/EngineStateSchema.js +58 -0
- package/dist/cjs/internal/EngineStateSchema.js.map +7 -0
- package/dist/cjs/internal/ExecutionSnapshotRead.d.ts +162 -0
- package/dist/cjs/internal/ExecutionSnapshotRead.d.ts.map +1 -0
- package/dist/cjs/internal/ExecutionSnapshotRead.js +199 -0
- package/dist/cjs/internal/ExecutionSnapshotRead.js.map +7 -0
- package/dist/cjs/internal/ExecutionSnapshotRelations.d.ts +39 -0
- package/dist/cjs/internal/ExecutionSnapshotRelations.d.ts.map +1 -0
- package/dist/cjs/internal/ExecutionSnapshotRelations.js +105 -0
- package/dist/cjs/internal/ExecutionSnapshotRelations.js.map +7 -0
- package/dist/cjs/internal/ExitEncoding.d.ts +180 -0
- package/dist/cjs/internal/ExitEncoding.d.ts.map +1 -0
- package/dist/cjs/internal/ExitEncoding.js +236 -0
- package/dist/cjs/internal/ExitEncoding.js.map +7 -0
- package/dist/cjs/internal/FileBoundarySnapshot.d.ts +14 -0
- package/dist/cjs/internal/FileBoundarySnapshot.d.ts.map +1 -0
- package/dist/cjs/internal/FileBoundarySnapshot.js +51 -0
- package/dist/cjs/internal/FileBoundarySnapshot.js.map +7 -0
- package/dist/cjs/internal/FileEnumeration.d.ts +127 -0
- package/dist/cjs/internal/FileEnumeration.d.ts.map +1 -0
- package/dist/cjs/internal/FileEnumeration.js +194 -0
- package/dist/cjs/internal/FileEnumeration.js.map +7 -0
- package/dist/cjs/internal/HostReflection.d.ts +80 -0
- package/dist/cjs/internal/HostReflection.d.ts.map +1 -0
- package/dist/cjs/internal/HostReflection.js +43 -0
- package/dist/cjs/internal/HostReflection.js.map +7 -0
- package/dist/cjs/internal/JournalRecords.d.ts +325 -0
- package/dist/cjs/internal/JournalRecords.d.ts.map +1 -0
- package/dist/cjs/internal/JournalRecords.js +122 -0
- package/dist/cjs/internal/JournalRecords.js.map +7 -0
- package/dist/cjs/internal/NodeJournal.d.ts +157 -0
- package/dist/cjs/internal/NodeJournal.d.ts.map +1 -0
- package/dist/cjs/internal/NodeJournal.js +192 -0
- package/dist/cjs/internal/NodeJournal.js.map +7 -0
- package/dist/cjs/internal/Ordering.d.ts +14 -0
- package/dist/cjs/internal/Ordering.d.ts.map +1 -0
- package/dist/cjs/internal/Ordering.js +25 -0
- package/dist/cjs/internal/Ordering.js.map +7 -0
- package/dist/cjs/internal/PlanInputs.d.ts +14 -0
- package/dist/cjs/internal/PlanInputs.d.ts.map +1 -0
- package/dist/cjs/internal/PlanInputs.js +78 -0
- package/dist/cjs/internal/PlanInputs.js.map +7 -0
- package/dist/cjs/internal/PlanMerges.d.ts +178 -0
- package/dist/cjs/internal/PlanMerges.d.ts.map +1 -0
- package/dist/cjs/internal/PlanMerges.js +125 -0
- package/dist/cjs/internal/PlanMerges.js.map +7 -0
- package/dist/cjs/internal/ProvenanceSlot.d.ts +30 -0
- package/dist/cjs/internal/ProvenanceSlot.d.ts.map +1 -0
- package/dist/cjs/internal/ProvenanceSlot.js +53 -0
- package/dist/cjs/internal/ProvenanceSlot.js.map +7 -0
- package/dist/cjs/internal/ResultEnvelope.d.ts +88 -0
- package/dist/cjs/internal/ResultEnvelope.d.ts.map +1 -0
- package/dist/cjs/internal/ResultEnvelope.js +90 -0
- package/dist/cjs/internal/ResultEnvelope.js.map +7 -0
- package/dist/cjs/internal/RetentionOps.d.ts +414 -0
- package/dist/cjs/internal/RetentionOps.d.ts.map +1 -0
- package/dist/cjs/internal/RetentionOps.js +354 -0
- package/dist/cjs/internal/RetentionOps.js.map +7 -0
- package/dist/cjs/internal/RunCatalogOps.d.ts +118 -0
- package/dist/cjs/internal/RunCatalogOps.d.ts.map +1 -0
- package/dist/cjs/internal/RunCatalogOps.js +85 -0
- package/dist/cjs/internal/RunCatalogOps.js.map +7 -0
- package/dist/cjs/internal/RunCoordinator.d.ts +78 -0
- package/dist/cjs/internal/RunCoordinator.d.ts.map +1 -0
- package/dist/cjs/internal/RunCoordinator.js +123 -0
- package/dist/cjs/internal/RunCoordinator.js.map +7 -0
- package/dist/cjs/internal/RunDriver.d.ts +192 -0
- package/dist/cjs/internal/RunDriver.d.ts.map +1 -0
- package/dist/cjs/internal/RunDriver.js +1584 -0
- package/dist/cjs/internal/RunDriver.js.map +7 -0
- package/dist/cjs/internal/RunLifecycle.d.ts +22 -0
- package/dist/cjs/internal/RunLifecycle.d.ts.map +1 -0
- package/dist/cjs/internal/RunLifecycle.js +63 -0
- package/dist/cjs/internal/RunLifecycle.js.map +7 -0
- package/dist/cjs/internal/RunListing.d.ts +78 -0
- package/dist/cjs/internal/RunListing.d.ts.map +1 -0
- package/dist/cjs/internal/RunListing.js +171 -0
- package/dist/cjs/internal/RunListing.js.map +7 -0
- package/dist/cjs/internal/RunListingError.d.ts +35 -0
- package/dist/cjs/internal/RunListingError.d.ts.map +1 -0
- package/dist/cjs/internal/RunListingError.js +47 -0
- package/dist/cjs/internal/RunListingError.js.map +7 -0
- package/dist/cjs/internal/RuntimeGraph.d.ts +73 -0
- package/dist/cjs/internal/RuntimeGraph.d.ts.map +1 -0
- package/dist/cjs/internal/RuntimeGraph.js +180 -0
- package/dist/cjs/internal/RuntimeGraph.js.map +7 -0
- package/dist/cjs/internal/SandboxedExecution.d.ts +80 -0
- package/dist/cjs/internal/SandboxedExecution.d.ts.map +1 -0
- package/dist/cjs/internal/SandboxedExecution.js +104 -0
- package/dist/cjs/internal/SandboxedExecution.js.map +7 -0
- package/dist/cjs/internal/StateTransaction.d.ts +44 -0
- package/dist/cjs/internal/StateTransaction.d.ts.map +1 -0
- package/dist/cjs/internal/StateTransaction.js +52 -0
- package/dist/cjs/internal/StateTransaction.js.map +7 -0
- package/dist/cjs/internal/StepFacts.d.ts +14 -0
- package/dist/cjs/internal/StepFacts.d.ts.map +1 -0
- package/dist/cjs/internal/StepFacts.js +58 -0
- package/dist/cjs/internal/StepFacts.js.map +7 -0
- package/dist/cjs/internal/TypedEvents.d.ts +14 -0
- package/dist/cjs/internal/TypedEvents.d.ts.map +1 -0
- package/dist/cjs/internal/TypedEvents.js +37 -0
- package/dist/cjs/internal/TypedEvents.js.map +7 -0
- package/dist/cjs/migrations/0001_initial.d.ts +17 -0
- package/dist/cjs/migrations/0001_initial.d.ts.map +1 -0
- package/dist/cjs/migrations/0001_initial.js +61 -0
- package/dist/cjs/migrations/0001_initial.js.map +7 -0
- package/dist/cjs/migrations/0002_selection_store.d.ts +15 -0
- package/dist/cjs/migrations/0002_selection_store.d.ts.map +1 -0
- package/dist/cjs/migrations/0002_selection_store.js +56 -0
- package/dist/cjs/migrations/0002_selection_store.js.map +7 -0
- package/dist/cjs/migrations/0003_plan_inputs.d.ts +13 -0
- package/dist/cjs/migrations/0003_plan_inputs.d.ts.map +1 -0
- package/dist/cjs/migrations/0003_plan_inputs.js +90 -0
- package/dist/cjs/migrations/0003_plan_inputs.js.map +7 -0
- package/dist/cjs/migrations/0004_plan_environment.d.ts +14 -0
- package/dist/cjs/migrations/0004_plan_environment.d.ts.map +1 -0
- package/dist/cjs/migrations/0004_plan_environment.js +56 -0
- package/dist/cjs/migrations/0004_plan_environment.js.map +7 -0
- package/dist/cjs/migrations/0005_plan_merges.d.ts +14 -0
- package/dist/cjs/migrations/0005_plan_merges.d.ts.map +1 -0
- package/dist/cjs/migrations/0005_plan_merges.js +82 -0
- package/dist/cjs/migrations/0005_plan_merges.js.map +7 -0
- package/dist/cjs/migrations/0006_execution_listing.d.ts +15 -0
- package/dist/cjs/migrations/0006_execution_listing.d.ts.map +1 -0
- package/dist/cjs/migrations/0006_execution_listing.js +101 -0
- package/dist/cjs/migrations/0006_execution_listing.js.map +7 -0
- package/dist/cjs/migrations/0007_run_parent_sequence.d.ts +15 -0
- package/dist/cjs/migrations/0007_run_parent_sequence.d.ts.map +1 -0
- package/dist/cjs/migrations/0007_run_parent_sequence.js +40 -0
- package/dist/cjs/migrations/0007_run_parent_sequence.js.map +7 -0
- package/dist/cjs/migrations/0008_deferred_consumption.d.ts +16 -0
- package/dist/cjs/migrations/0008_deferred_consumption.d.ts.map +1 -0
- package/dist/cjs/migrations/0008_deferred_consumption.js +45 -0
- package/dist/cjs/migrations/0008_deferred_consumption.js.map +7 -0
- package/dist/cjs/migrations/0009_deferred_clock_facts.d.ts +24 -0
- package/dist/cjs/migrations/0009_deferred_clock_facts.d.ts.map +1 -0
- package/dist/cjs/migrations/0009_deferred_clock_facts.js +141 -0
- package/dist/cjs/migrations/0009_deferred_clock_facts.js.map +7 -0
- package/dist/cjs/migrations/0010_pending_cancellation.d.ts +15 -0
- package/dist/cjs/migrations/0010_pending_cancellation.d.ts.map +1 -0
- package/dist/cjs/migrations/0010_pending_cancellation.js +41 -0
- package/dist/cjs/migrations/0010_pending_cancellation.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/TestStores.d.ts +89 -0
- package/dist/cjs/test/TestStores.d.ts.map +1 -0
- package/dist/cjs/test/TestStores.js +98 -0
- package/dist/cjs/test/TestStores.js.map +7 -0
- package/dist/esm/ArtifactGc.d.ts +235 -0
- package/dist/esm/ArtifactGc.d.ts.map +1 -0
- package/dist/esm/ArtifactGc.js +268 -0
- package/dist/esm/ArtifactGc.js.map +1 -0
- package/dist/esm/ArtifactSync.d.ts +164 -0
- package/dist/esm/ArtifactSync.d.ts.map +1 -0
- package/dist/esm/ArtifactSync.js +176 -0
- package/dist/esm/ArtifactSync.js.map +1 -0
- package/dist/esm/CacheSync.d.ts +112 -0
- package/dist/esm/CacheSync.d.ts.map +1 -0
- package/dist/esm/CacheSync.js +94 -0
- package/dist/esm/CacheSync.js.map +1 -0
- package/dist/esm/DeferredClockFold.d.ts +354 -0
- package/dist/esm/DeferredClockFold.d.ts.map +1 -0
- package/dist/esm/DeferredClockFold.js +472 -0
- package/dist/esm/DeferredClockFold.js.map +1 -0
- package/dist/esm/DisasterRecovery.d.ts +431 -0
- package/dist/esm/DisasterRecovery.d.ts.map +1 -0
- package/dist/esm/DisasterRecovery.js +523 -0
- package/dist/esm/DisasterRecovery.js.map +1 -0
- package/dist/esm/DurableEngineState.d.ts +776 -0
- package/dist/esm/DurableEngineState.d.ts.map +1 -0
- package/dist/esm/DurableEngineState.js +1648 -0
- package/dist/esm/DurableEngineState.js.map +1 -0
- package/dist/esm/EngineStore.d.ts +158 -0
- package/dist/esm/EngineStore.d.ts.map +1 -0
- package/dist/esm/EngineStore.js +403 -0
- package/dist/esm/EngineStore.js.map +1 -0
- package/dist/esm/EngineStoreMetrics.d.ts +276 -0
- package/dist/esm/EngineStoreMetrics.d.ts.map +1 -0
- package/dist/esm/EngineStoreMetrics.js +260 -0
- package/dist/esm/EngineStoreMetrics.js.map +1 -0
- package/dist/esm/Errors.d.ts +22 -0
- package/dist/esm/Errors.d.ts.map +1 -0
- package/dist/esm/Errors.js +22 -0
- package/dist/esm/Errors.js.map +1 -0
- package/dist/esm/EventTypes.d.ts +27 -0
- package/dist/esm/EventTypes.d.ts.map +1 -0
- package/dist/esm/EventTypes.js +27 -0
- package/dist/esm/EventTypes.js.map +1 -0
- package/dist/esm/ExecutionFacts.d.ts +59 -0
- package/dist/esm/ExecutionFacts.d.ts.map +1 -0
- package/dist/esm/ExecutionFacts.js +96 -0
- package/dist/esm/ExecutionFacts.js.map +1 -0
- package/dist/esm/ExecutionSnapshot.d.ts +117 -0
- package/dist/esm/ExecutionSnapshot.d.ts.map +1 -0
- package/dist/esm/ExecutionSnapshot.js +95 -0
- package/dist/esm/ExecutionSnapshot.js.map +1 -0
- package/dist/esm/Inconsistency.d.ts +154 -0
- package/dist/esm/Inconsistency.d.ts.map +1 -0
- package/dist/esm/Inconsistency.js +133 -0
- package/dist/esm/Inconsistency.js.map +1 -0
- package/dist/esm/Migrations.d.ts +56 -0
- package/dist/esm/Migrations.d.ts.map +1 -0
- package/dist/esm/Migrations.js +91 -0
- package/dist/esm/Migrations.js.map +1 -0
- package/dist/esm/OwnerIdentity.d.ts +91 -0
- package/dist/esm/OwnerIdentity.d.ts.map +1 -0
- package/dist/esm/OwnerIdentity.js +99 -0
- package/dist/esm/OwnerIdentity.js.map +1 -0
- package/dist/esm/PlanInputStore.d.ts +110 -0
- package/dist/esm/PlanInputStore.d.ts.map +1 -0
- package/dist/esm/PlanInputStore.js +207 -0
- package/dist/esm/PlanInputStore.js.map +1 -0
- package/dist/esm/PlanMergeStore.d.ts +113 -0
- package/dist/esm/PlanMergeStore.d.ts.map +1 -0
- package/dist/esm/PlanMergeStore.js +244 -0
- package/dist/esm/PlanMergeStore.js.map +1 -0
- package/dist/esm/PlanScheduler.d.ts +363 -0
- package/dist/esm/PlanScheduler.d.ts.map +1 -0
- package/dist/esm/PlanScheduler.js +1321 -0
- package/dist/esm/PlanScheduler.js.map +1 -0
- package/dist/esm/Reconciliation.d.ts +175 -0
- package/dist/esm/Reconciliation.d.ts.map +1 -0
- package/dist/esm/Reconciliation.js +175 -0
- package/dist/esm/Reconciliation.js.map +1 -0
- package/dist/esm/ReplayOnly.d.ts +91 -0
- package/dist/esm/ReplayOnly.d.ts.map +1 -0
- package/dist/esm/ReplayOnly.js +59 -0
- package/dist/esm/ReplayOnly.js.map +1 -0
- package/dist/esm/Retention.d.ts +113 -0
- package/dist/esm/Retention.d.ts.map +1 -0
- package/dist/esm/Retention.js +139 -0
- package/dist/esm/Retention.js.map +1 -0
- package/dist/esm/RunCatalogRead.d.ts +17 -0
- package/dist/esm/RunCatalogRead.d.ts.map +1 -0
- package/dist/esm/RunCatalogRead.js +17 -0
- package/dist/esm/RunCatalogRead.js.map +1 -0
- package/dist/esm/RunChangeFeed.d.ts +75 -0
- package/dist/esm/RunChangeFeed.d.ts.map +1 -0
- package/dist/esm/RunChangeFeed.js +81 -0
- package/dist/esm/RunChangeFeed.js.map +1 -0
- package/dist/esm/RunState.d.ts +116 -0
- package/dist/esm/RunState.d.ts.map +1 -0
- package/dist/esm/RunState.js +109 -0
- package/dist/esm/RunState.js.map +1 -0
- package/dist/esm/Selection.d.ts +495 -0
- package/dist/esm/Selection.d.ts.map +1 -0
- package/dist/esm/Selection.js +482 -0
- package/dist/esm/Selection.js.map +1 -0
- package/dist/esm/SelectionStore.d.ts +141 -0
- package/dist/esm/SelectionStore.d.ts.map +1 -0
- package/dist/esm/SelectionStore.js +215 -0
- package/dist/esm/SelectionStore.js.map +1 -0
- package/dist/esm/StepBoundary.d.ts +501 -0
- package/dist/esm/StepBoundary.d.ts.map +1 -0
- package/dist/esm/StepBoundary.js +919 -0
- package/dist/esm/StepBoundary.js.map +1 -0
- package/dist/esm/StepCacheFold.d.ts +148 -0
- package/dist/esm/StepCacheFold.d.ts.map +1 -0
- package/dist/esm/StepCacheFold.js +201 -0
- package/dist/esm/StepCacheFold.js.map +1 -0
- package/dist/esm/StepSandbox.d.ts +78 -0
- package/dist/esm/StepSandbox.d.ts.map +1 -0
- package/dist/esm/StepSandbox.js +70 -0
- package/dist/esm/StepSandbox.js.map +1 -0
- package/dist/esm/WakeBus.d.ts +116 -0
- package/dist/esm/WakeBus.d.ts.map +1 -0
- package/dist/esm/WakeBus.js +121 -0
- package/dist/esm/WakeBus.js.map +1 -0
- package/dist/esm/WorkspaceSandbox.d.ts +619 -0
- package/dist/esm/WorkspaceSandbox.d.ts.map +1 -0
- package/dist/esm/WorkspaceSandbox.js +985 -0
- package/dist/esm/WorkspaceSandbox.js.map +1 -0
- package/dist/esm/index.d.ts +157 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +157 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/ActionPersistence.d.ts +276 -0
- package/dist/esm/internal/ActionPersistence.d.ts.map +1 -0
- package/dist/esm/internal/ActionPersistence.js +2306 -0
- package/dist/esm/internal/ActionPersistence.js.map +1 -0
- package/dist/esm/internal/ArtifactRoots.d.ts +38 -0
- package/dist/esm/internal/ArtifactRoots.d.ts.map +1 -0
- package/dist/esm/internal/ArtifactRoots.js +76 -0
- package/dist/esm/internal/ArtifactRoots.js.map +1 -0
- package/dist/esm/internal/AttemptAdmission.d.ts +53 -0
- package/dist/esm/internal/AttemptAdmission.d.ts.map +1 -0
- package/dist/esm/internal/AttemptAdmission.js +52 -0
- package/dist/esm/internal/AttemptAdmission.js.map +1 -0
- package/dist/esm/internal/AttemptLifecycle.d.ts +245 -0
- package/dist/esm/internal/AttemptLifecycle.d.ts.map +1 -0
- package/dist/esm/internal/AttemptLifecycle.js +104 -0
- package/dist/esm/internal/AttemptLifecycle.js.map +1 -0
- package/dist/esm/internal/AttemptProbe.d.ts +53 -0
- package/dist/esm/internal/AttemptProbe.d.ts.map +1 -0
- package/dist/esm/internal/AttemptProbe.js +83 -0
- package/dist/esm/internal/AttemptProbe.js.map +1 -0
- package/dist/esm/internal/CacheAdmission.d.ts +79 -0
- package/dist/esm/internal/CacheAdmission.d.ts.map +1 -0
- package/dist/esm/internal/CacheAdmission.js +72 -0
- package/dist/esm/internal/CacheAdmission.js.map +1 -0
- package/dist/esm/internal/CacheAgeHistory.d.ts +27 -0
- package/dist/esm/internal/CacheAgeHistory.d.ts.map +1 -0
- package/dist/esm/internal/CacheAgeHistory.js +49 -0
- package/dist/esm/internal/CacheAgeHistory.js.map +1 -0
- package/dist/esm/internal/CacheAgeVerdicts.d.ts +15 -0
- package/dist/esm/internal/CacheAgeVerdicts.d.ts.map +1 -0
- package/dist/esm/internal/CacheAgeVerdicts.js +54 -0
- package/dist/esm/internal/CacheAgeVerdicts.js.map +1 -0
- package/dist/esm/internal/CacheOutputPolicy.d.ts +24 -0
- package/dist/esm/internal/CacheOutputPolicy.d.ts.map +1 -0
- package/dist/esm/internal/CacheOutputPolicy.js +73 -0
- package/dist/esm/internal/CacheOutputPolicy.js.map +1 -0
- package/dist/esm/internal/CachePublication.d.ts +97 -0
- package/dist/esm/internal/CachePublication.d.ts.map +1 -0
- package/dist/esm/internal/CachePublication.js +116 -0
- package/dist/esm/internal/CachePublication.js.map +1 -0
- package/dist/esm/internal/CallFacts.d.ts +15 -0
- package/dist/esm/internal/CallFacts.d.ts.map +1 -0
- package/dist/esm/internal/CallFacts.js +83 -0
- package/dist/esm/internal/CallFacts.js.map +1 -0
- package/dist/esm/internal/CopiedRecord.d.ts +61 -0
- package/dist/esm/internal/CopiedRecord.d.ts.map +1 -0
- package/dist/esm/internal/CopiedRecord.js +165 -0
- package/dist/esm/internal/CopiedRecord.js.map +1 -0
- package/dist/esm/internal/DeferredPersistence.d.ts +108 -0
- package/dist/esm/internal/DeferredPersistence.d.ts.map +1 -0
- package/dist/esm/internal/DeferredPersistence.js +240 -0
- package/dist/esm/internal/DeferredPersistence.js.map +1 -0
- package/dist/esm/internal/EffectRecords.d.ts +92 -0
- package/dist/esm/internal/EffectRecords.d.ts.map +1 -0
- package/dist/esm/internal/EffectRecords.js +94 -0
- package/dist/esm/internal/EffectRecords.js.map +1 -0
- package/dist/esm/internal/EngineJj.d.ts +30 -0
- package/dist/esm/internal/EngineJj.d.ts.map +1 -0
- package/dist/esm/internal/EngineJj.js +29 -0
- package/dist/esm/internal/EngineJj.js.map +1 -0
- package/dist/esm/internal/EngineStateSchema.d.ts +68 -0
- package/dist/esm/internal/EngineStateSchema.d.ts.map +1 -0
- package/dist/esm/internal/EngineStateSchema.js +53 -0
- package/dist/esm/internal/EngineStateSchema.js.map +1 -0
- package/dist/esm/internal/ExecutionSnapshotRead.d.ts +162 -0
- package/dist/esm/internal/ExecutionSnapshotRead.d.ts.map +1 -0
- package/dist/esm/internal/ExecutionSnapshotRead.js +218 -0
- package/dist/esm/internal/ExecutionSnapshotRead.js.map +1 -0
- package/dist/esm/internal/ExecutionSnapshotRelations.d.ts +39 -0
- package/dist/esm/internal/ExecutionSnapshotRelations.d.ts.map +1 -0
- package/dist/esm/internal/ExecutionSnapshotRelations.js +81 -0
- package/dist/esm/internal/ExecutionSnapshotRelations.js.map +1 -0
- package/dist/esm/internal/ExitEncoding.d.ts +180 -0
- package/dist/esm/internal/ExitEncoding.d.ts.map +1 -0
- package/dist/esm/internal/ExitEncoding.js +361 -0
- package/dist/esm/internal/ExitEncoding.js.map +1 -0
- package/dist/esm/internal/FileBoundarySnapshot.d.ts +14 -0
- package/dist/esm/internal/FileBoundarySnapshot.d.ts.map +1 -0
- package/dist/esm/internal/FileBoundarySnapshot.js +29 -0
- package/dist/esm/internal/FileBoundarySnapshot.js.map +1 -0
- package/dist/esm/internal/FileEnumeration.d.ts +127 -0
- package/dist/esm/internal/FileEnumeration.d.ts.map +1 -0
- package/dist/esm/internal/FileEnumeration.js +236 -0
- package/dist/esm/internal/FileEnumeration.js.map +1 -0
- package/dist/esm/internal/HostReflection.d.ts +80 -0
- package/dist/esm/internal/HostReflection.d.ts.map +1 -0
- package/dist/esm/internal/HostReflection.js +98 -0
- package/dist/esm/internal/HostReflection.js.map +1 -0
- package/dist/esm/internal/JournalRecords.d.ts +325 -0
- package/dist/esm/internal/JournalRecords.d.ts.map +1 -0
- package/dist/esm/internal/JournalRecords.js +306 -0
- package/dist/esm/internal/JournalRecords.js.map +1 -0
- package/dist/esm/internal/NodeJournal.d.ts +157 -0
- package/dist/esm/internal/NodeJournal.d.ts.map +1 -0
- package/dist/esm/internal/NodeJournal.js +298 -0
- package/dist/esm/internal/NodeJournal.js.map +1 -0
- package/dist/esm/internal/Ordering.d.ts +14 -0
- package/dist/esm/internal/Ordering.d.ts.map +1 -0
- package/dist/esm/internal/Ordering.js +14 -0
- package/dist/esm/internal/Ordering.js.map +1 -0
- package/dist/esm/internal/PlanInputs.d.ts +14 -0
- package/dist/esm/internal/PlanInputs.d.ts.map +1 -0
- package/dist/esm/internal/PlanInputs.js +57 -0
- package/dist/esm/internal/PlanInputs.js.map +1 -0
- package/dist/esm/internal/PlanMerges.d.ts +178 -0
- package/dist/esm/internal/PlanMerges.d.ts.map +1 -0
- package/dist/esm/internal/PlanMerges.js +113 -0
- package/dist/esm/internal/PlanMerges.js.map +1 -0
- package/dist/esm/internal/ProvenanceSlot.d.ts +30 -0
- package/dist/esm/internal/ProvenanceSlot.d.ts.map +1 -0
- package/dist/esm/internal/ProvenanceSlot.js +39 -0
- package/dist/esm/internal/ProvenanceSlot.js.map +1 -0
- package/dist/esm/internal/ResultEnvelope.d.ts +88 -0
- package/dist/esm/internal/ResultEnvelope.d.ts.map +1 -0
- package/dist/esm/internal/ResultEnvelope.js +83 -0
- package/dist/esm/internal/ResultEnvelope.js.map +1 -0
- package/dist/esm/internal/RetentionOps.d.ts +414 -0
- package/dist/esm/internal/RetentionOps.d.ts.map +1 -0
- package/dist/esm/internal/RetentionOps.js +576 -0
- package/dist/esm/internal/RetentionOps.js.map +1 -0
- package/dist/esm/internal/RunCatalogOps.d.ts +118 -0
- package/dist/esm/internal/RunCatalogOps.d.ts.map +1 -0
- package/dist/esm/internal/RunCatalogOps.js +120 -0
- package/dist/esm/internal/RunCatalogOps.js.map +1 -0
- package/dist/esm/internal/RunCoordinator.d.ts +78 -0
- package/dist/esm/internal/RunCoordinator.d.ts.map +1 -0
- package/dist/esm/internal/RunCoordinator.js +115 -0
- package/dist/esm/internal/RunCoordinator.js.map +1 -0
- package/dist/esm/internal/RunDriver.d.ts +192 -0
- package/dist/esm/internal/RunDriver.d.ts.map +1 -0
- package/dist/esm/internal/RunDriver.js +2334 -0
- package/dist/esm/internal/RunDriver.js.map +1 -0
- package/dist/esm/internal/RunLifecycle.d.ts +22 -0
- package/dist/esm/internal/RunLifecycle.d.ts.map +1 -0
- package/dist/esm/internal/RunLifecycle.js +49 -0
- package/dist/esm/internal/RunLifecycle.js.map +1 -0
- package/dist/esm/internal/RunListing.d.ts +78 -0
- package/dist/esm/internal/RunListing.d.ts.map +1 -0
- package/dist/esm/internal/RunListing.js +146 -0
- package/dist/esm/internal/RunListing.js.map +1 -0
- package/dist/esm/internal/RunListingError.d.ts +35 -0
- package/dist/esm/internal/RunListingError.d.ts.map +1 -0
- package/dist/esm/internal/RunListingError.js +27 -0
- package/dist/esm/internal/RunListingError.js.map +1 -0
- package/dist/esm/internal/RuntimeGraph.d.ts +73 -0
- package/dist/esm/internal/RuntimeGraph.d.ts.map +1 -0
- package/dist/esm/internal/RuntimeGraph.js +182 -0
- package/dist/esm/internal/RuntimeGraph.js.map +1 -0
- package/dist/esm/internal/SandboxedExecution.d.ts +80 -0
- package/dist/esm/internal/SandboxedExecution.d.ts.map +1 -0
- package/dist/esm/internal/SandboxedExecution.js +107 -0
- package/dist/esm/internal/SandboxedExecution.js.map +1 -0
- package/dist/esm/internal/StateTransaction.d.ts +44 -0
- package/dist/esm/internal/StateTransaction.d.ts.map +1 -0
- package/dist/esm/internal/StateTransaction.js +54 -0
- package/dist/esm/internal/StateTransaction.js.map +1 -0
- package/dist/esm/internal/StepFacts.d.ts +14 -0
- package/dist/esm/internal/StepFacts.d.ts.map +1 -0
- package/dist/esm/internal/StepFacts.js +35 -0
- package/dist/esm/internal/StepFacts.js.map +1 -0
- package/dist/esm/internal/TypedEvents.d.ts +14 -0
- package/dist/esm/internal/TypedEvents.d.ts.map +1 -0
- package/dist/esm/internal/TypedEvents.js +14 -0
- package/dist/esm/internal/TypedEvents.js.map +1 -0
- package/dist/esm/migrations/0001_initial.d.ts +17 -0
- package/dist/esm/migrations/0001_initial.d.ts.map +1 -0
- package/dist/esm/migrations/0001_initial.js +41 -0
- package/dist/esm/migrations/0001_initial.js.map +1 -0
- package/dist/esm/migrations/0002_selection_store.d.ts +15 -0
- package/dist/esm/migrations/0002_selection_store.d.ts.map +1 -0
- package/dist/esm/migrations/0002_selection_store.js +34 -0
- package/dist/esm/migrations/0002_selection_store.js.map +1 -0
- package/dist/esm/migrations/0003_plan_inputs.d.ts +13 -0
- package/dist/esm/migrations/0003_plan_inputs.d.ts.map +1 -0
- package/dist/esm/migrations/0003_plan_inputs.js +70 -0
- package/dist/esm/migrations/0003_plan_inputs.js.map +1 -0
- package/dist/esm/migrations/0004_plan_environment.d.ts +14 -0
- package/dist/esm/migrations/0004_plan_environment.d.ts.map +1 -0
- package/dist/esm/migrations/0004_plan_environment.js +33 -0
- package/dist/esm/migrations/0004_plan_environment.js.map +1 -0
- package/dist/esm/migrations/0005_plan_merges.d.ts +14 -0
- package/dist/esm/migrations/0005_plan_merges.d.ts.map +1 -0
- package/dist/esm/migrations/0005_plan_merges.js +59 -0
- package/dist/esm/migrations/0005_plan_merges.js.map +1 -0
- package/dist/esm/migrations/0006_execution_listing.d.ts +15 -0
- package/dist/esm/migrations/0006_execution_listing.d.ts.map +1 -0
- package/dist/esm/migrations/0006_execution_listing.js +81 -0
- package/dist/esm/migrations/0006_execution_listing.js.map +1 -0
- package/dist/esm/migrations/0007_run_parent_sequence.d.ts +15 -0
- package/dist/esm/migrations/0007_run_parent_sequence.d.ts.map +1 -0
- package/dist/esm/migrations/0007_run_parent_sequence.js +18 -0
- package/dist/esm/migrations/0007_run_parent_sequence.js.map +1 -0
- package/dist/esm/migrations/0008_deferred_consumption.d.ts +16 -0
- package/dist/esm/migrations/0008_deferred_consumption.d.ts.map +1 -0
- package/dist/esm/migrations/0008_deferred_consumption.js +24 -0
- package/dist/esm/migrations/0008_deferred_consumption.js.map +1 -0
- package/dist/esm/migrations/0009_deferred_clock_facts.d.ts +24 -0
- package/dist/esm/migrations/0009_deferred_clock_facts.d.ts.map +1 -0
- package/dist/esm/migrations/0009_deferred_clock_facts.js +127 -0
- package/dist/esm/migrations/0009_deferred_clock_facts.js.map +1 -0
- package/dist/esm/migrations/0010_pending_cancellation.d.ts +15 -0
- package/dist/esm/migrations/0010_pending_cancellation.d.ts.map +1 -0
- package/dist/esm/migrations/0010_pending_cancellation.js +19 -0
- package/dist/esm/migrations/0010_pending_cancellation.js.map +1 -0
- package/dist/esm/test/TestStores.d.ts +89 -0
- package/dist/esm/test/TestStores.d.ts.map +1 -0
- package/dist/esm/test/TestStores.js +103 -0
- package/dist/esm/test/TestStores.js.map +1 -0
- package/docs/README.md +113 -0
- package/docs/api.md +1494 -0
- package/docs/concepts/attempts-and-replay.md +149 -0
- package/docs/concepts/cache-admission.md +265 -0
- package/docs/concepts/call-facts.md +86 -0
- package/docs/concepts/durable-waits.md +175 -0
- package/docs/concepts/execution-facts.md +90 -0
- package/docs/concepts/ownership-and-fencing.md +128 -0
- package/docs/concepts/step-boundaries.md +148 -0
- package/docs/concepts/workspace-transactions.md +199 -0
- package/docs/event-types.md +14 -0
- package/docs/guides/back-up-and-restore.md +164 -0
- package/docs/guides/collect-unreferenced-artifacts.md +128 -0
- package/docs/guides/compose-a-durable-engine.md +146 -0
- package/docs/guides/coordinate-two-processes.md +120 -0
- package/docs/guides/defer-work-with-selection.md +171 -0
- package/docs/guides/delete-old-run-history.md +154 -0
- package/docs/guides/drive-a-plan.md +314 -0
- package/docs/guides/observe-engine-metrics.md +98 -0
- package/docs/guides/observe-executions.md +187 -0
- package/docs/guides/reclaim-runs-from-a-dead-host.md +133 -0
- package/docs/guides/share-a-cache-across-machines.md +134 -0
- package/docs/guides/testing.md +123 -0
- package/docs/installation.md +102 -0
- package/docs/quickstart.md +215 -0
- package/docs/troubleshooting.md +347 -0
- package/package.json +438 -3
- package/src/ArtifactGc.ts +408 -0
- package/src/ArtifactSync.ts +278 -0
- package/src/CacheSync.ts +131 -0
- package/src/DeferredClockFold.ts +586 -0
- package/src/DisasterRecovery.ts +888 -0
- package/src/DurableEngineState.ts +2656 -0
- package/src/EngineStore.ts +656 -0
- package/src/EngineStoreMetrics.ts +331 -0
- package/src/Errors.ts +28 -0
- package/src/EventTypes.ts +27 -0
- package/src/ExecutionFacts.ts +125 -0
- package/src/ExecutionSnapshot.ts +186 -0
- package/src/Inconsistency.ts +253 -0
- package/src/Migrations.ts +95 -0
- package/src/OwnerIdentity.ts +125 -0
- package/src/PlanInputStore.ts +292 -0
- package/src/PlanMergeStore.ts +330 -0
- package/src/PlanScheduler.ts +1806 -0
- package/src/Reconciliation.ts +224 -0
- package/src/ReplayOnly.ts +95 -0
- package/src/Retention.ts +214 -0
- package/src/RunCatalogRead.ts +27 -0
- package/src/RunChangeFeed.ts +125 -0
- package/src/RunState.ts +137 -0
- package/src/Selection.ts +683 -0
- package/src/SelectionStore.ts +353 -0
- package/src/StepBoundary.ts +1165 -0
- package/src/StepCacheFold.ts +244 -0
- package/src/StepSandbox.ts +110 -0
- package/src/WakeBus.ts +167 -0
- package/src/WorkspaceSandbox.ts +1604 -0
- package/src/index.ts +185 -0
- package/src/internal/ActionPersistence.ts +2687 -0
- package/src/internal/ArtifactRoots.ts +92 -0
- package/src/internal/AttemptAdmission.ts +76 -0
- package/src/internal/AttemptLifecycle.ts +163 -0
- package/src/internal/AttemptProbe.ts +90 -0
- package/src/internal/CacheAdmission.ts +133 -0
- package/src/internal/CacheAgeHistory.ts +58 -0
- package/src/internal/CacheAgeVerdicts.ts +61 -0
- package/src/internal/CacheOutputPolicy.ts +82 -0
- package/src/internal/CachePublication.ts +153 -0
- package/src/internal/CallFacts.ts +87 -0
- package/src/internal/CopiedRecord.ts +201 -0
- package/src/internal/DeferredPersistence.ts +459 -0
- package/src/internal/EffectRecords.ts +136 -0
- package/src/internal/EngineJj.ts +30 -0
- package/src/internal/EngineStateSchema.ts +92 -0
- package/src/internal/ExecutionSnapshotRead.ts +298 -0
- package/src/internal/ExecutionSnapshotRelations.ts +121 -0
- package/src/internal/ExitEncoding.ts +453 -0
- package/src/internal/FileBoundarySnapshot.ts +39 -0
- package/src/internal/FileEnumeration.ts +334 -0
- package/src/internal/HostReflection.ts +128 -0
- package/src/internal/JournalRecords.ts +396 -0
- package/src/internal/NodeJournal.ts +340 -0
- package/src/internal/Ordering.ts +14 -0
- package/src/internal/PlanInputs.ts +62 -0
- package/src/internal/PlanMerges.ts +123 -0
- package/src/internal/ProvenanceSlot.ts +52 -0
- package/src/internal/ResultEnvelope.ts +118 -0
- package/src/internal/RetentionOps.ts +865 -0
- package/src/internal/RunCatalogOps.ts +174 -0
- package/src/internal/RunCoordinator.ts +215 -0
- package/src/internal/RunDriver.ts +3152 -0
- package/src/internal/RunLifecycle.ts +53 -0
- package/src/internal/RunListing.ts +210 -0
- package/src/internal/RunListingError.ts +37 -0
- package/src/internal/RuntimeGraph.ts +253 -0
- package/src/internal/SandboxedExecution.ts +165 -0
- package/src/internal/StateTransaction.ts +71 -0
- package/src/internal/StepFacts.ts +36 -0
- package/src/internal/TypedEvents.ts +29 -0
- package/src/migrations/0001_initial.ts +51 -0
- package/src/migrations/0002_selection_store.ts +38 -0
- package/src/migrations/0003_plan_inputs.ts +75 -0
- package/src/migrations/0004_plan_environment.ts +34 -0
- package/src/migrations/0005_plan_merges.ts +60 -0
- package/src/migrations/0006_execution_listing.ts +86 -0
- package/src/migrations/0007_run_parent_sequence.ts +19 -0
- package/src/migrations/0008_deferred_consumption.ts +27 -0
- package/src/migrations/0009_deferred_clock_facts.ts +163 -0
- package/src/migrations/0010_pending_cancellation.ts +20 -0
- package/src/test/TestStores.ts +145 -0
package/docs/api.md
ADDED
|
@@ -0,0 +1,1494 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "API reference"
|
|
3
|
+
description: "Every public export of @smthrs/engine-store, grouped by namespace: services, layers, constructors, options, schemas, constants, and the stable error codes."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The durable `FlowEngine`. It claims a run before driving it, fences every write
|
|
7
|
+
against the current owner, and persists attempts, waits, and terminal results
|
|
8
|
+
through [`@smthrs/journal`](/api/journal),
|
|
9
|
+
[`@smthrs/run-store`](/api/run-store), and
|
|
10
|
+
[`@smthrs/step-cache`](/api/step-cache). It owns the durable deferred and clock
|
|
11
|
+
tables and composes every package's migration set.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { EngineStore, StepBoundary } from "@smthrs/engine-store"
|
|
15
|
+
|
|
16
|
+
const engine = EngineStore.layer({
|
|
17
|
+
owner: { hostId: "worker-a" },
|
|
18
|
+
journalSource: "worker-a"
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Entry point
|
|
23
|
+
|
|
24
|
+
| Import | Source | Platform |
|
|
25
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------- |
|
|
26
|
+
| `@smthrs/engine-store` | [src/index.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/index.ts) | Node and browser |
|
|
27
|
+
|
|
28
|
+
Every namespace below is also importable from `@smthrs/engine-store/<Module>`.
|
|
29
|
+
`@smthrs/engine-store/internal/*`, `@smthrs/engine-store/migrations/*`, and
|
|
30
|
+
`@smthrs/engine-store/*/index` are blocked in the export map.
|
|
31
|
+
|
|
32
|
+
Shared versioned event schemas are public at `@smthrs/journal/EngineEvent`; see
|
|
33
|
+
[state and event authority](https://journal.smithers.sh/concepts/state-event-authority/).
|
|
34
|
+
Engine adapters `TypedEvents`, `AttemptLifecycle`, and `ResultEnvelope` remain
|
|
35
|
+
internal. They are additive contracts: current writers retain their bytes, and
|
|
36
|
+
the disclosed attempt projection does not replace executable recovery stores.
|
|
37
|
+
|
|
38
|
+
### Bundling is not running
|
|
39
|
+
|
|
40
|
+
This entry point bundles for a browser. The two host reads it once made
|
|
41
|
+
directly, `process.pid` and `randomUUID` from `node:crypto`, enter through the
|
|
42
|
+
injectable [`OwnerIdentity`](#owneridentity) service, and everything it
|
|
43
|
+
composes, `@smthrs/crypto`, `@smthrs/flow`, `@smthrs/journal`,
|
|
44
|
+
`@smthrs/run-store`, `@smthrs/step-cache`, `@smthrs/database`,
|
|
45
|
+
`@smthrs/kernel`, and `@smthrs/engine`, is browser-bundleable too. A release
|
|
46
|
+
that broke the bundle would fail the build before it shipped.
|
|
47
|
+
|
|
48
|
+
The durable stores accept an injected SQL client and `DurableWriter`. Native
|
|
49
|
+
Node.js and Bun compositions use SQLite or the shipped PostgreSQL adapter;
|
|
50
|
+
`NodeDatabase.layer` selects PostgreSQL for a PostgreSQL URL, and
|
|
51
|
+
`@smthrs/database/postgres/PostgresDatabase` supports direct injection. Browser
|
|
52
|
+
bundling does not supply a durable browser driver or qualify browser execution.
|
|
53
|
+
PostgreSQL storage alone does not qualify a canonical backend deployment:
|
|
54
|
+
artifacts, workspaces, and native process state still need durable host storage.
|
|
55
|
+
See [database configuration](https://database.smithers.sh/concepts/sqlite-only/)
|
|
56
|
+
and [platform support](/docs/reference/api/#platform-support).
|
|
57
|
+
|
|
58
|
+
## EngineStore
|
|
59
|
+
|
|
60
|
+
[src/EngineStore.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/EngineStore.ts)
|
|
61
|
+
|
|
62
|
+
The production durable composition.
|
|
63
|
+
|
|
64
|
+
`flow.start`, `flow.ensure`, and `execute` with `discard: true` durably admit
|
|
65
|
+
an execution and schedule its drive before returning its id. They do not wait
|
|
66
|
+
for the child's action, durable wait, or settlement, including when launched
|
|
67
|
+
from inside another action. The driver owns the scheduled drive in its engine
|
|
68
|
+
scope; ending the caller's action or round does not interrupt it. A discarded
|
|
69
|
+
child records `onParentExit: "detach"`, so normal parent completion leaves it
|
|
70
|
+
running. Explicit parent cancellation still cascades to linked children.
|
|
71
|
+
|
|
72
|
+
Observe progress with `poll(id)`, or await the result with
|
|
73
|
+
`execute(payload, { executionId: id })`. Callers that need a durable waiting
|
|
74
|
+
row or attempt receipt must await that evidence explicitly. Closing the engine
|
|
75
|
+
scope releases ownership and retains durable admission and progress for
|
|
76
|
+
recovery by a replacement host. The heartbeat sweep recovers pending admissions
|
|
77
|
+
through the same coordinator and fenced claim/activate path;
|
|
78
|
+
unregistered flows wait for a host that registers them. Pending recovery scans
|
|
79
|
+
at most 64 rows per sweep and rotates registered flows. Each scan cycle has a
|
|
80
|
+
fixed creation-time/id upper bound, so new admissions cannot postpone retrying
|
|
81
|
+
refused work indefinitely. Recovery only selects admissions older than the stale
|
|
82
|
+
heartbeat interval, giving a live admitting host time to claim its own work. Storage
|
|
83
|
+
failures remain visible and are retried by the heartbeat sweep.
|
|
84
|
+
|
|
85
|
+
Cancellation is logical-run scoped: `Flow.interrupt(id)` accepts any round ID
|
|
86
|
+
and records intent across its trampoline lineage and linked child lineages in
|
|
87
|
+
one transaction. Children attached to an earlier parent round remain included;
|
|
88
|
+
time-travel fork ancestry alone is not child ownership. Handoff admission checks
|
|
89
|
+
cancellation in that same transaction domain, so a successor cannot escape a
|
|
90
|
+
request by racing its creation.
|
|
91
|
+
|
|
92
|
+
The request commits before local interrupt delivery. Returning from `interrupt`
|
|
93
|
+
acknowledges intent, not finished user cleanup or a remote owner's settlement.
|
|
94
|
+
Owners on other connections observe durable intent through their cancellation
|
|
95
|
+
polls. Completed predecessor history remains unchanged. On normal parent exit,
|
|
96
|
+
`onParentExit` belongs to the child's creating round and is retained through its
|
|
97
|
+
handoffs; explicit cancellation still cascades to linked children.
|
|
98
|
+
|
|
99
|
+
| Export | Signature | Meaning |
|
|
100
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
101
|
+
| `Options` | interface | Construction options. See below. |
|
|
102
|
+
| `make` | `(options: Options) => Effect<FlowRuntime["Service"], never, Requirements>` | Builds the `FlowRuntime` service the port in [`@smthrs/flow`](/api/flow) declares. |
|
|
103
|
+
| `layer` | `(options: Options) => Layer<SnapshotBoundary \| FlowRuntime, never, Requirements>` | Provides both, using the ambient `Jj` for action bodies and engine bookkeeping. |
|
|
104
|
+
| `layerWithPrivilegedJj` | `(options: Options, privilegedJj: Layer<Jj, E, R>) => Layer<SnapshotBoundary \| FlowRuntime, E, Requirements \| R>` | The same, with a private repository for engine bookkeeping while ambient `Jj` stays for action bodies. |
|
|
105
|
+
|
|
106
|
+
### EngineStore.Options
|
|
107
|
+
|
|
108
|
+
| Field | Type | Meaning |
|
|
109
|
+
| ---------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
110
|
+
| `owner.hostId` | `string` | This engine's stable host identity. Which host a store speaks for is a composition decision, not a host fact. |
|
|
111
|
+
| `journalSource` | `string` | The source id every record this engine writes carries. |
|
|
112
|
+
| `isAlive` | `Ownership.LivenessCheck` (optional) | Liveness arbitration consulted before stealing a run whose lease expired. Answering `true` refuses the takeover. Defaults to `Ownership.leaseLiveness(Ownership.heartbeatStaleAfter)`. |
|
|
113
|
+
| `canExecute` | `(row: RunStore.RunRow) => Effect<boolean>` (optional) | Host admission before claiming, including automatic wakes. |
|
|
114
|
+
| `canActivate` | `(row: RunStore.RunRow) => Effect<boolean>` (optional) | Read-only admission fence rechecked inside the native activation transaction. Use the same consent and ownership policy as preflight; keep module loading and external I/O in `canExecute`. A refusal abandons the claim without dispatching the body. |
|
|
115
|
+
| `clockFireRetryPolicy` | `Schedule<unknown, unknown>` (optional) | Redispatch policy for a durable clock whose fire failed. Defaults to exponential from 100ms capped at 30s, forever, the same option shape as the engine's `suspendedRetryPolicy`. |
|
|
116
|
+
| `requestResume` | `(executionId, reason) => Effect<void>` (optional) | Called for every wake this engine initiates against a PARKED execution: `"deferred"` (a durable deferred completed), `"clock"` (a durable clock fired), `"parent"` (a child settled under a parent parked on it). An operator's own resume is not reported. A host that refuses to re-enter a parked run nobody asked for reads it to tell an engine wake from its own heartbeat sweep. |
|
|
117
|
+
|
|
118
|
+
### Required services
|
|
119
|
+
|
|
120
|
+
`Journal`, `DurableWriter`, `RunStore`, `AttemptStore`, `CacheStore`, Effect's `Crypto`,
|
|
121
|
+
`DurableEngineState`, kernel `Jj`, `StepBoundary`, `OwnerIdentity`, and a
|
|
122
|
+
`Scope`. A composition failure is an unmet `Requirements` type at compile time,
|
|
123
|
+
so there is no run-time composition error to handle.
|
|
124
|
+
|
|
125
|
+
[`WorkspaceSandbox`](#workspacesandbox) and its `EffectDispatcher` are optional
|
|
126
|
+
and are resolved at construction, because `actionExecute` runs on the engine's
|
|
127
|
+
own fiber, which does not carry the store's layer context. `StepSandbox`,
|
|
128
|
+
`WakeBus`, `ArtifactSync`, `CacheSync`, `Inconsistency`, `Reconciliation`,
|
|
129
|
+
[`ReplayOnly`](#replayonly), and `Selection` are optional in the same way.
|
|
130
|
+
|
|
131
|
+
### Behavior
|
|
132
|
+
|
|
133
|
+
The engine stores a versioned state envelope in each run row, fences run and
|
|
134
|
+
attempt ownership, replays encoded exits, and writes engine decisions to the
|
|
135
|
+
journal. Cache addresses are the injected `Sha256` transformation of the step
|
|
136
|
+
key, not the raw `key1_...` value.
|
|
137
|
+
|
|
138
|
+
An action's `CachePolicy.ttlMs` records one age-admission decision per run,
|
|
139
|
+
step key, and cached provenance. Resuming that decision preserves its verdict
|
|
140
|
+
even when the clock crosses the bound in either direction. A conflicting
|
|
141
|
+
journal record proves the opposite verdict only when an exact retry of that
|
|
142
|
+
opposite record returns `Duplicate`. An incompatible TTL or copied history
|
|
143
|
+
with different lineage metadata instead fails with `JournalError` code
|
|
144
|
+
`idempotency_conflict`, before output replay, cache eviction, or body dispatch.
|
|
145
|
+
Changing TTL before a decision exists remains allowed. Omitting `ttlMs` is
|
|
146
|
+
unbounded only before a durable age verdict exists for this run and step.
|
|
147
|
+
Removing `ttlMs` after a recorded verdict fails with `idempotency_conflict`,
|
|
148
|
+
even if the cache head was evicted or replaced. Recovery must retain the
|
|
149
|
+
original TTL and history identity, or use a new action or run identity as
|
|
150
|
+
appropriate. No persisted keys or journal producer identities are rewritten.
|
|
151
|
+
|
|
152
|
+
Durable cancellation is observed, not just recorded: while a run executes, the
|
|
153
|
+
driver polls `cancel_requested_at_ms` on the heartbeat cadence and cancels the
|
|
154
|
+
run when another process has called `RunStore.requestCancel`. Terminal
|
|
155
|
+
transitions are additionally guarded with `{ cancelRequested: "absent" }` inside
|
|
156
|
+
the ownership compare-and-swap.
|
|
157
|
+
|
|
158
|
+
The driver's periodic sweep re-drives durable shapes on the heartbeat
|
|
159
|
+
cadence: pending admissions, parked runs whose cancellation was durably requested,
|
|
160
|
+
runs parked with reason `released`, and stale `running` rows left by a hard-killed
|
|
161
|
+
owner. Each
|
|
162
|
+
re-enters the ordinary claim, steal, and activate path. A wake for a flow the
|
|
163
|
+
sweeping process has not registered logs a once-per-run structured warning and
|
|
164
|
+
leaves the durable waiting row parked.
|
|
165
|
+
|
|
166
|
+
Every lifecycle journal write, run decisions, attempt started and finished, hard
|
|
167
|
+
violations, snapshot identity, cache provenance, deferred completions, clock
|
|
168
|
+
schedules, interruption records, and the `Inconsistency` cache-conflict record,
|
|
169
|
+
takes the journal's durable channel (`emitDurable`), so a saturated lossy queue
|
|
170
|
+
can never drop one. Attempt lifecycle writes additionally pass the owner: a
|
|
171
|
+
reclaimed owner fails with `fence_lost` and self-interrupts instead of
|
|
172
|
+
appending.
|
|
173
|
+
|
|
174
|
+
Full model: [Ownership and fencing](./concepts/ownership-and-fencing.md) and
|
|
175
|
+
[Attempts and replay](./concepts/attempts-and-replay.md).
|
|
176
|
+
|
|
177
|
+
## DurableEngineState
|
|
178
|
+
|
|
179
|
+
`PendingRunCursor` contains `{ createdAtMs: number; runId: string }`. Pending
|
|
180
|
+
queries return rows after `after` and at or before `through`, ordered by those
|
|
181
|
+
fields. An optional `eligibleBeforeMs` limits results to older admissions.
|
|
182
|
+
`pendingRunTail` captures the upper bound of a recovery scan cycle.
|
|
183
|
+
`pendingCancellationRuns` and `pendingCancellationTail` use the same cursors
|
|
184
|
+
for explicitly requested cancellations, independent of registered handlers.
|
|
185
|
+
Their partial index excludes settled and unrequested admissions; cleanup has
|
|
186
|
+
no admission grace and still requires workspace admission and ownership fencing.
|
|
187
|
+
`limit` must be zero or a positive safe integer; other values defect with `RangeError`.
|
|
188
|
+
|
|
189
|
+
[src/DurableEngineState.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/DurableEngineState.ts)
|
|
190
|
+
|
|
191
|
+
Durable deferreds, clocks, waiting rows, and the run parent DAG. This package
|
|
192
|
+
owns deferred and clock state for 1.0. A successful mutation means the row is
|
|
193
|
+
durable, so callers may journal and schedule a wake only after the mutation
|
|
194
|
+
returns.
|
|
195
|
+
|
|
196
|
+
Work that needs both this state and the journal opens `transaction` OUTSIDE
|
|
197
|
+
`Journal.transact`, never the other way around. The SQL implementation shares
|
|
198
|
+
the journal's writer and cannot tell the difference, but `makeMemory` has a
|
|
199
|
+
permit of its own, and taking the journal's writer first while waiting for that
|
|
200
|
+
permit deadlocks against a fiber doing the opposite.
|
|
201
|
+
|
|
202
|
+
| Export | Signature | Meaning |
|
|
203
|
+
| -------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
204
|
+
| `DurableEngineState` | `Context.Service<Service>` | Service tag. |
|
|
205
|
+
| `Service` | interface | The operations below. |
|
|
206
|
+
| `make` | `Effect<Service, never, DurableWriter \| SqlClient>` | The SQL implementation. |
|
|
207
|
+
| `layer` | `Layer<DurableEngineState, never, DurableWriter \| SqlClient>` | Provides it. |
|
|
208
|
+
| `makeMemory` | `(options?: MemoryOptions) => Service` | Deterministic in-memory implementation. |
|
|
209
|
+
| `layerMemory` | `Layer<DurableEngineState>` | Provides it. |
|
|
210
|
+
| `MemoryOptions`, `MemoryRunView` | interfaces | Optional `runs` lookup so the memory twin enforces the same ownership fences. |
|
|
211
|
+
|
|
212
|
+
### Service members
|
|
213
|
+
|
|
214
|
+
| Member | Signature |
|
|
215
|
+
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
216
|
+
| `deferred` | `(address: DeferredAddress) => Effect<Option<DeferredRow>>` |
|
|
217
|
+
| `consumeDeferred` | `(address: DeferredAddress, consumedAtMs: number) => Effect<void>` |
|
|
218
|
+
| `completeDeferred` | `(row: DeferredRow) => Effect<CompleteDeferredOutcome>` |
|
|
219
|
+
| `clock` | `(address: ClockAddress) => Effect<Option<ClockRow>>` |
|
|
220
|
+
| `scheduleClock` | `(row: ClockRow, owner: OwnerId) => Effect<ScheduleClockOutcome>` |
|
|
221
|
+
| `completeClock` | `(address: ClockAddress, completedAtMs: number) => Effect<CompleteClockOutcome>` |
|
|
222
|
+
| `dueClocks` | `(nowMs: number) => Effect<ReadonlyArray<ClockRow>>` |
|
|
223
|
+
| `completeRunClocks` | `(executionId: string, completedAtMs: number) => Effect<void>` |
|
|
224
|
+
| `pendingClocks` | `(scope: { executionId?: string; flowName?: string }) => Effect<ReadonlyArray<ClockRow>>` |
|
|
225
|
+
| `completedDeferreds` | `(flowName: string) => Effect<ReadonlyArray<DeferredAddress>>` |
|
|
226
|
+
| `park` | `(runId: string, waiting: Waiting, owner: OwnerId) => Effect<ParkOutcome>` |
|
|
227
|
+
| `wake` | `(runId: string) => Effect<WakeOutcome>` |
|
|
228
|
+
| `waiting` | `(runId: string) => Effect<Option<WaitingRow>>` |
|
|
229
|
+
| `waitingRuns` | `(filter?: WaitingRunsFilter) => Effect<ReadonlyArray<WaitingRow>>` |
|
|
230
|
+
| `waitingTree` | `(runId: string, options?: WaitingTreeOptions) => Effect<ReadonlyArray<WaitingRow>>` |
|
|
231
|
+
| `pendingRuns` | `(flowName: string, limit?: number, after?: PendingRunCursor, through?: PendingRunCursor, eligibleBeforeMs?: number) => Effect<ReadonlyArray<PendingRunCursor>>` |
|
|
232
|
+
| `pendingRunTail` | `(flowName: string, eligibleBeforeMs?: number) => Effect<Option<PendingRunCursor>>` |
|
|
233
|
+
| `pendingCancellationRuns` | `(limit: number, after?: PendingRunCursor, through?: PendingRunCursor) => Effect<ReadonlyArray<PendingRunCursor>>` |
|
|
234
|
+
| `pendingCancellationTail` | `() => Effect<Option<PendingRunCursor>>` |
|
|
235
|
+
| `staleRunningRuns` | `(staleBeforeMs: number, limit?: number) => Effect<ReadonlyArray<StaleRunningRow>>` |
|
|
236
|
+
| `attemptSurvivors` | `((runId: string, stepKeyDigest: string) => Effect<Option<AttemptSurvivors>>)` (optional) |
|
|
237
|
+
| `recordRunParent` | `(childId: string, parentId: string) => Effect<RecordRunParentOutcome, RunParentCycleError>` |
|
|
238
|
+
| `removeRunParentsForRun` | `(runId: string) => Effect<void>` |
|
|
239
|
+
| `runParents` | `(childId: string) => Effect<ReadonlyArray<RunParentEdge>>` |
|
|
240
|
+
| `runChildren` | `(parentId: string) => Effect<ReadonlyArray<RunParentEdge>>` |
|
|
241
|
+
| `transaction` | `<A, E, R>(effect: Effect<A, E, R>) => Effect<A, E, R>` |
|
|
242
|
+
|
|
243
|
+
`scheduleClock` requires the execution's ownership token. Creation succeeds
|
|
244
|
+
only while the run is running under that owner. An existing clock wins unchanged,
|
|
245
|
+
even after ownership is lost. If no clock exists and the ownership fence fails,
|
|
246
|
+
the effect self-interrupts.
|
|
247
|
+
|
|
248
|
+
`recordRunParent` allocates `MAX(seq) + 1` in the serialized write transaction.
|
|
249
|
+
Migration 0007 indexes `seq` so allocation does not scan retained parent history.
|
|
250
|
+
|
|
251
|
+
`completeRunClocks` closes every uncompleted clock row of one run in a single
|
|
252
|
+
statement, which a terminal transition does in its own transaction.
|
|
253
|
+
`pendingClocks` and `completedDeferreds` never list a row whose run has settled,
|
|
254
|
+
so a registration sweep re-arms timers and replays completions for runs that can
|
|
255
|
+
still make progress and for no others. `completedDeferreds` also excludes
|
|
256
|
+
completions marked observed by `consumeDeferred`. The engine marks a completion
|
|
257
|
+
before `deferredResult` serves it; `deferred` remains a read-only lookup and keeps
|
|
258
|
+
returning the original result for replay. Duplicate completion writes do not
|
|
259
|
+
clear the marker. Migration 0008 leaves existing completions unconsumed until
|
|
260
|
+
the upgraded engine first serves them.
|
|
261
|
+
|
|
262
|
+
`attemptSurvivors` is optional because
|
|
263
|
+
only storage that can range-scan `flows_attempts` implements it; when absent the
|
|
264
|
+
engine falls back to per-attempt point reads against `AttemptStore`.
|
|
265
|
+
|
|
266
|
+
Every list read (`dueClocks`, `pendingClocks`, `completedDeferreds`,
|
|
267
|
+
`waitingRuns`, `runParents`, and `runChildren`) skips a row that will not decode
|
|
268
|
+
and logs a storage-integrity warning naming its primary key, so one corrupt row
|
|
269
|
+
costs its own row and nothing else. The point reads `deferred`, `clock`, and
|
|
270
|
+
`waiting` still die on such a row: reporting "no row" for a completion or
|
|
271
|
+
deadline that is durably recorded but unreadable would re-run work whose side
|
|
272
|
+
effects already ran.
|
|
273
|
+
|
|
274
|
+
`recordRunParent` performs its cycle check inside the same write transaction as
|
|
275
|
+
the insert, so a rejected edge leaves no durable trace and, of two concurrent
|
|
276
|
+
writers whose edges jointly close a cycle, exactly one fails. `transaction`
|
|
277
|
+
makes several store operations atomic; nested store writes become savepoints.
|
|
278
|
+
Serialized write transactions are a documented requirement of the
|
|
279
|
+
`DurableWriter.write` contract, not a SQLite artifact. The shipped PostgreSQL
|
|
280
|
+
adapter uses READ COMMITTED transactions with a transaction-scoped advisory lock
|
|
281
|
+
keyed by schema, acquired before domain reads to serialize writes across pools
|
|
282
|
+
and processes; nested writes use savepoints. A bare PostgreSQL READ COMMITTED
|
|
283
|
+
client without equivalent serialization is insufficient. The in-memory twin
|
|
284
|
+
serializes every store operation behind one permit, snapshots its state on
|
|
285
|
+
entry, rolls back to that snapshot when the effect fails or dies, and treats a
|
|
286
|
+
nested call on the same fiber as a savepoint. It is not durable: a process crash loses the whole
|
|
287
|
+
in-memory state, not only the uncommitted part.
|
|
288
|
+
|
|
289
|
+
When engine state and the journal participate together, `StateTransaction`
|
|
290
|
+
takes the engine-state boundary first and the journal writer inside it. Native
|
|
291
|
+
lifecycle admission, direct cancellation, deferred completion and clock
|
|
292
|
+
scheduling share this order. A refused park or owner transition rolls both
|
|
293
|
+
stores back, including a preexisting memory waiting marker. The memory commit
|
|
294
|
+
callback releases its gate at SQL commit, before post-commit subscribers or
|
|
295
|
+
compaction capture can read it. Failure after publication starts cannot undo
|
|
296
|
+
committed state.
|
|
297
|
+
|
|
298
|
+
`ExecutionFacts` captures the semantic native lifecycle inside the same write.
|
|
299
|
+
Its additive `treeVersion: 1` metadata includes parent attachment policy and a
|
|
300
|
+
redacted waiting question plus its named point. Opaque wake tokens remain in
|
|
301
|
+
the operational waiting row; the fact carries only their digest. The native
|
|
302
|
+
fact fold derives attached descendant human waits and compares them with a
|
|
303
|
+
coherent executor observation. Old v1 facts without the tree extension retain
|
|
304
|
+
legacy tree-wait provenance. Raw waiting request JSON is available through the
|
|
305
|
+
protected execution snapshot reader, independently of its redacted event view.
|
|
306
|
+
|
|
307
|
+
### Addresses, rows, and outcomes
|
|
308
|
+
|
|
309
|
+
| Export | Shape |
|
|
310
|
+
| ------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
311
|
+
| `DeferredAddress` | `{ flowName, executionId, deferredName }` |
|
|
312
|
+
| `DeferredRow` | the address plus `exit: unknown` and optional `metadata` |
|
|
313
|
+
| `ClockAddress` | `{ flowName, executionId, clockName }` |
|
|
314
|
+
| `ClockRow` | the address plus `deferredName`, an absolute due time, and `completedAtMs: number \| null` |
|
|
315
|
+
| `Waiting` | `{ reason, wakeAt?, token?, request?: string }` |
|
|
316
|
+
| `WaitingRow` | `{ runId, reason, wakeAt: number \| null, token: string \| null, request?: Json }` |
|
|
317
|
+
| `StaleRunningRow` | `{ runId, heartbeatAtMs }`: a stale `running` row and the lease heartbeat it froze on |
|
|
318
|
+
| `WaitingReason` | a non-empty string; the open taxonomy the driver writes (`timer`, `event`, `released`, `quarantine`) |
|
|
319
|
+
| `WaitingRunsFilter` | `{ reason?, dueBeforeMs?, cancelRequested? }` |
|
|
320
|
+
| `RunParentEdge` | `{ childId, parentId, seq }`, where `seq` is ordering only |
|
|
321
|
+
| `AttemptSurvivors` | `{ earliestAttempt, earliestStartedAtMs, latest }` |
|
|
322
|
+
| `CompleteDeferredOutcome` | `Completed` or `Existing` |
|
|
323
|
+
| `ScheduleClockOutcome` | `Scheduled` or `Existing` |
|
|
324
|
+
| `CompleteClockOutcome` | `Completed`, `AlreadyCompleted`, or `NotFound` |
|
|
325
|
+
| `ParkOutcome` | `Parked` or `NotFound` |
|
|
326
|
+
| `WakeOutcome` | `Woken`, `NotWaiting`, or `NotFound` |
|
|
327
|
+
| `RecordRunParentOutcome` | `Recorded` or `Existing` |
|
|
328
|
+
| `RunParentCycleError` | tagged error carrying `path`, the execution ids from the child back to itself |
|
|
329
|
+
| `EngineStateFault` | defect for a broken invariant: `reason` names it, `field` names an unwritable or unreadable column |
|
|
330
|
+
|
|
331
|
+
Both implementations answer one behavior contract, so a test written against
|
|
332
|
+
the memory twin holds for the SQL one. See
|
|
333
|
+
[Test against a durable store](./guides/testing.md).
|
|
334
|
+
|
|
335
|
+
Full model: [Durable waits](./concepts/durable-waits.md).
|
|
336
|
+
|
|
337
|
+
### EngineStateFault
|
|
338
|
+
|
|
339
|
+
`DurableEngineState.EngineStateFault` is a tagged defect with optional `field`
|
|
340
|
+
and `cause`. JSON encoding and decoding failures use `value_not_serializable`
|
|
341
|
+
and `value_not_decodable`; `field` identifies the value or column.
|
|
342
|
+
`deferred_completion_missing` and `run_parent_edge_missing` identify missing
|
|
343
|
+
first-writer transaction rows. The tag is
|
|
344
|
+
`@smthrs/engine-store/EngineStateFault`; these remain defects, not recoverable
|
|
345
|
+
failures in the service error channel.
|
|
346
|
+
|
|
347
|
+
## StepBoundary
|
|
348
|
+
|
|
349
|
+
[src/StepBoundary.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/StepBoundary.ts)
|
|
350
|
+
|
|
351
|
+
The declared read and write sets of a step, measured before and after it runs.
|
|
352
|
+
The declaration itself, `FileBoundary` with `readSet`, `writeSet`, `removes`,
|
|
353
|
+
and `boundaryMode` of `hard` or `expected`, lives in
|
|
354
|
+
[`@smthrs/flow`](/api/flow)'s `Action` namespace.
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
interface Service {
|
|
358
|
+
readonly prepare: (descriptor: FileBoundary) => Effect<PreparedBoundary, UnsupportedBoundary, Crypto>
|
|
359
|
+
readonly settle: (prepared: PreparedBoundary) => Effect<
|
|
360
|
+
BoundaryEvidence,
|
|
361
|
+
UndeclaredWrite | MissingDeclaredOutput | SurvivingDeclaredRemoval | UnsupportedBoundary | BoundaryCorruption,
|
|
362
|
+
Crypto
|
|
363
|
+
>
|
|
364
|
+
readonly replayOutputs: (evidence: BoundaryEvidence) => Effect<
|
|
365
|
+
void,
|
|
366
|
+
UnsupportedBoundary | BoundaryCorruption | MissingArtifact,
|
|
367
|
+
Crypto
|
|
368
|
+
>
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
| Export | Signature | Meaning |
|
|
373
|
+
| ------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
374
|
+
| `StepBoundary` | `Context.Service<Service>` | Service tag. |
|
|
375
|
+
| `make` | `(service: Service) => Service` | Brands an implementation, so a wrong shape is reported where it is written. |
|
|
376
|
+
| `makeFileSystem` | `(fs: FileSystem, path: Path, artifacts: ArtifactStore.Service, options?: FileSystemOptions) => Service` | The production boundary. |
|
|
377
|
+
| `layer` | `Layer<Service, never, FileSystem \| Path \| ArtifactStore>` | Provides it, confining replay to the kernel `Workspace` root when one is in context. Resolves that root with the host `Path`. |
|
|
378
|
+
| `layerTest` | `(options?: TestOptions) => Layer<Service>` | Deterministic in-memory boundary. |
|
|
379
|
+
| `exactReads` | `(descriptor: FileBoundary) => ReadonlyArray<FileInput>` | Exact read inputs, ignoring declarations that still need expansion. |
|
|
380
|
+
| `readSetMatches` | `(prepared: PreparedBoundary) => boolean` | Whether the measured snapshot still matches the declaration. |
|
|
381
|
+
| `referencedDigests` | `(evidence: BoundaryEvidence) => ReadonlyArray<ArtifactStore.Digest>` | The digests the evidence references rather than inlines. Evidence from a foreign implementation yields the empty list. |
|
|
382
|
+
|
|
383
|
+
### Schemas
|
|
384
|
+
|
|
385
|
+
| Export | Shape |
|
|
386
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
387
|
+
| `PreparedBoundary` | `{ descriptor, readSnapshot }`. The snapshot is what the host actually measured, and is the evidence the declared digests still describe reality. |
|
|
388
|
+
| `BoundaryEvidence` | `{ declaredOutputs, diffIdentity, wholeTreeWritesVerified?, hermeticReadsVerified?, deviation? }` |
|
|
389
|
+
| `ExpectedSetDeviation` | `{ paths, diffIdentity }`, writes outside the declared write set |
|
|
390
|
+
| `MissingOutputDeviation` | `{ paths, diffIdentity }`, declared writes never produced |
|
|
391
|
+
| `SurvivingRemovalDeviation` | `{ paths, diffIdentity }`, declared removals left in place |
|
|
392
|
+
| `BoundaryDeviation` | the union of the three |
|
|
393
|
+
|
|
394
|
+
### FileSystemOptions
|
|
395
|
+
|
|
396
|
+
| Field | Default | Meaning |
|
|
397
|
+
| --------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
398
|
+
| `maxInlineBytes` | 1 MiB | The largest single output inlined into the evidence. Anything larger is stored by digest reference. |
|
|
399
|
+
| `maxTotalInlineBytes` | 8 MiB | The largest aggregate inline payload one settle may fold in. Past it, an output is spilled even though it individually fits. |
|
|
400
|
+
| `root` | cwd | The root replay confines writes and removals to. A filesystem already confined to a root keeps its own. |
|
|
401
|
+
|
|
402
|
+
`replayOutputs` writes and deletes what the evidence names, so it confines its
|
|
403
|
+
own mutations instead of trusting the composition. Every write, removal, probe,
|
|
404
|
+
and tree prune goes through `@smthrs/kernel/FileSystem`'s `confined` view:
|
|
405
|
+
one descriptor-relative, no-follow request per call against the pinned root. A
|
|
406
|
+
symlink on a replayed path (a parent, the final component, or a member of a
|
|
407
|
+
replayed tree) refuses with `UnsupportedBoundary`, and so does a host with no
|
|
408
|
+
descriptor-relative executor or isolation attestation, before any host call.
|
|
409
|
+
`prepare` and `settle` only read and keep the caller's filesystem.
|
|
410
|
+
|
|
411
|
+
### TestOptions
|
|
412
|
+
|
|
413
|
+
`failure`, `deviation`, `readSnapshot`,
|
|
414
|
+
`declaredOutputs`, `diffIdentity`, `supported`, `wholeTreeWriteDetection`,
|
|
415
|
+
`hermeticReadDetection`, and `onReplay`. Defaults describe a well-behaved,
|
|
416
|
+
fully-supported host. Failures and deviations are returned verbatim; use the real
|
|
417
|
+
filesystem layer to test classification. `wholeTreeWriteDetection` defaults to `true`, which is a
|
|
418
|
+
fixture claim rather than a proof: the real whole-tree observation comes from
|
|
419
|
+
[`WorkspaceSandbox`](#workspacesandbox).
|
|
420
|
+
|
|
421
|
+
### Errors
|
|
422
|
+
|
|
423
|
+
| Error | `code` | Fields |
|
|
424
|
+
| -------------------------- | ---------------------------- | ------------------------------------------ |
|
|
425
|
+
| `UndeclaredWrite` | `undeclared_write` | `paths`, `diffIdentity` |
|
|
426
|
+
| `MissingDeclaredOutput` | `missing_declared_output` | `paths`, `diffIdentity` |
|
|
427
|
+
| `SurvivingDeclaredRemoval` | `surviving_declared_removal` | `paths`, `diffIdentity` |
|
|
428
|
+
| `UnsupportedBoundary` | `unsupported_boundary` | `message`, optional `cause` carried whole |
|
|
429
|
+
| `BoundaryCorruption` | `boundary_corruption` | `path`, `recordedDigest`, `measuredDigest` |
|
|
430
|
+
| `MissingArtifact` | `missing_artifact` | `path`, `digest` |
|
|
431
|
+
|
|
432
|
+
Full model: [Step boundaries](./concepts/step-boundaries.md).
|
|
433
|
+
|
|
434
|
+
## StepSandbox
|
|
435
|
+
|
|
436
|
+
[src/StepSandbox.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/StepSandbox.ts)
|
|
437
|
+
|
|
438
|
+
Scope-safe acquisition of one isolated workspace per step.
|
|
439
|
+
|
|
440
|
+
| Export | Signature | Meaning |
|
|
441
|
+
| ---------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
442
|
+
| `StepSandbox` | `Context.Service<Service>` | Service tag. |
|
|
443
|
+
| `Service` | `{ open: Effect<WorkspaceSandbox.Service, UnsupportedBoundary> }` | Acquires the workspace. |
|
|
444
|
+
| `make` | `(workspace: WorkspaceSandbox.Service) => Service` | Wraps a transaction backend. |
|
|
445
|
+
| `layer` | `Layer<Service, WorkspaceError, FileSystem \| Path \| ArtifactStore \| Workspace>` | The filesystem-backed sandbox. Refuses a path-based host at build. |
|
|
446
|
+
| `layerTest` | `(initialFiles?: InitialFiles) => Layer<Service, WorkspaceError, Crypto>` | Deterministic in-memory sandbox. |
|
|
447
|
+
| `layerNoop` | `Layer<Service>` | Fails closed with `UnsupportedBoundary`, for a host that cannot sandbox. |
|
|
448
|
+
| `UndeclaredRead` | tagged error, `code: "undeclared_read"`, fields `paths` and `diffIdentity` | A hermetic body read outside its declared read set. |
|
|
449
|
+
|
|
450
|
+
## WorkspaceSandbox
|
|
451
|
+
|
|
452
|
+
[src/WorkspaceSandbox.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/WorkspaceSandbox.ts)
|
|
453
|
+
|
|
454
|
+
The functional workspace transaction: a sealed action's body runs in an isolated
|
|
455
|
+
workspace and returns its writes rather than performing them.
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
interface Service {
|
|
459
|
+
readonly execute: <Output, Error>(
|
|
460
|
+
execution: Execution<Output, Error>
|
|
461
|
+
) => Effect<ExecutionResult<Output>, Error | WorkspaceError, Crypto>
|
|
462
|
+
readonly materialize: <Output>(
|
|
463
|
+
accepted: Accepted<Output>
|
|
464
|
+
) => Effect<void, MaterializationConflict | WorkspaceError, Crypto>
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
`materialize` checks digests and retained bytes before changing files. Every
|
|
469
|
+
copy-back host call is one descriptor-relative, no-follow request against the
|
|
470
|
+
root pinned for that commit (`@smthrs/kernel/FileSystem`'s `confined`), so a
|
|
471
|
+
symlink on a change's path, including one swapped in mid-commit, fails with
|
|
472
|
+
`path_escapes_workspace` and is never followed. The host must carry the
|
|
473
|
+
kernel's atomic executor (`@smthrs/platform-node`'s `AtomicFileSystem`), an
|
|
474
|
+
isolation attestation, or be the kernel-guarded filesystem for the same root. The filesystem host serializes cooperating commits through preflight,
|
|
475
|
+
apply, and rollback, using a workspace-root semaphore and the
|
|
476
|
+
`.smithers-workspace-lock` lease file (`@smthrs/artifacts/FileLease`: heartbeat
|
|
477
|
+
every 10 seconds, stale after 60 seconds, acquisition deadline 2 minutes). The
|
|
478
|
+
lock and its children are reserved, as are the `.flows` engine state directory
|
|
479
|
+
and every `reservedPaths` entry, under any write set or boundary mode. A change
|
|
480
|
+
that targets or lies beneath a reserved path fails with `host_unavailable`.
|
|
481
|
+
Reserved names match after Unicode NFKC normalization and case folding, so
|
|
482
|
+
`.FLOWS` or `.flows`, which open `.flows` on default APFS and NTFS volumes, are
|
|
483
|
+
refused on every volume. An NTFS 8.3 short-name shape, such as `FLOWS~1`, is
|
|
484
|
+
refused wherever a reserved segment stands, because an NTFS volume with short
|
|
485
|
+
names on opens the long name through it, including from WSL or SMB.
|
|
486
|
+
Hosts must support exclusive `wx` file creation, `stat` with an mtime,
|
|
487
|
+
`utimes`, `rename`, and `remove`; writers that ignore the lock are outside this
|
|
488
|
+
guarantee.
|
|
489
|
+
|
|
490
|
+
Rollback uses in-memory file pre-images. Copy-back is not crash-atomic, and
|
|
491
|
+
rollback can fail. A compound cause preserves the apply failure plus a
|
|
492
|
+
`WorkspaceError` (`host_unavailable`) carrying the rollback cause. The caller
|
|
493
|
+
must reconcile host files after a crash or failed rollback before resuming.
|
|
494
|
+
A crashed holder's lock is reclaimed once its heartbeat is 60 seconds old. A
|
|
495
|
+
commit that cannot take the lock within 2 minutes fails with
|
|
496
|
+
`commit_lock_timeout` and changes nothing. Lock waits are interruptible.
|
|
497
|
+
|
|
498
|
+
| Export | Signature | Meaning |
|
|
499
|
+
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
500
|
+
| `WorkspaceSandbox` | `Context.Service<Service>` | Service tag. |
|
|
501
|
+
| `make` | `(service: Service) => Service` | Brands an implementation. |
|
|
502
|
+
| `layer` | `(service: Service) => Layer<Service>` | Provides one. |
|
|
503
|
+
| `makeHosted` | `(host: Host) => Service` | The transaction itself, over a `Host`. |
|
|
504
|
+
| `makeMemory` | `(initialFiles?: InitialFiles) => Effect<MemorySandbox, WorkspaceError, Crypto>` | Deterministic, browser-safe, and the conformance implementation. Seeds the whole tree, so an undeclared read is observable. |
|
|
505
|
+
| `makeFileSystem` | `(fs: FileSystem, path: Path, artifacts: ArtifactStore.Service, workspaceRoot: string, options?: FileSystemOptions) => Service` | The production host. |
|
|
506
|
+
| `layerFileSystem` | `(options?: FileSystemOptions) => Layer<Service, WorkspaceError, FileSystem \| Path \| ArtifactStore \| Workspace>` | Provides it, taking the root from the kernel `Workspace` and resolving it with the host `Path`. Fails with `host_unavailable` over a path-based host. |
|
|
507
|
+
| `Workspace` | `Context.Service<Workspace>` | The in-transaction filesystem and effect outbox, available only inside `execute`. |
|
|
508
|
+
| `EffectDispatcher` | `Context.Service<Dispatcher>` | The post-copy-back dispatch stage. Optional. |
|
|
509
|
+
| `layerDispatcher` | `(dispatcher: Dispatcher) => Layer<Dispatcher>` | Provides one. |
|
|
510
|
+
| `violations` | `(descriptor: FileBoundary, base: ReadonlyMap<string, Uint8Array>, provenance: Provenance) => ReadonlyArray<DeclarationViolation>` | Everything the declaration failed to predict, deduplicated. |
|
|
511
|
+
| `isMaterializationConflict` | `(error: unknown) => boolean` | Recognizes both the live class and the persisted schema form. |
|
|
512
|
+
|
|
513
|
+
### Models
|
|
514
|
+
|
|
515
|
+
| Export | Shape |
|
|
516
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
517
|
+
| `Resource` | `{ kind, id }`. This implementation records `kind: "file"`. |
|
|
518
|
+
| `InputObservation` | `{ resource, digest }` |
|
|
519
|
+
| `OutputObservation` | `{ resource, operation: "write" \| "remove", digest }` |
|
|
520
|
+
| `Provenance` | `{ baseRevision, inputs, outputs }` |
|
|
521
|
+
| `FileChange` | `{ path, beforeDigest, afterDigest, after? }`. `beforeDigest` is a materialization precondition, not a hint. |
|
|
522
|
+
| `QueuedEffect` | `{ protocol, idempotencyKey, payload }` |
|
|
523
|
+
| `WorkflowResult<Output>` | `{ output, files, provenance, effects }` |
|
|
524
|
+
| `Execution<Output, Error>` | `{ descriptor, cacheKey?, workflow }` |
|
|
525
|
+
| `DeclarationViolation` | `{ kind: "undeclared-read" \| "undeclared-write", resource }` |
|
|
526
|
+
| `CacheOutcome` | `disabled`, `miss`, or `hit`. A run-local memo, not the cross-run cache. |
|
|
527
|
+
| `Accepted<Output>` | `{ _tag: "Accepted", result, cache, violations }` |
|
|
528
|
+
| `Invalidated` | `{ _tag: "Invalidated", provenance, violations }`. No accessor for the candidate output, files, or queued effects. |
|
|
529
|
+
| `ExecutionResult<Output>` | `Accepted<Output> \| Invalidated` |
|
|
530
|
+
| `Host` | `{ snapshot, baseline, retain, commit, root }` |
|
|
531
|
+
| `MemorySandbox` | `{ service, files }`, where `files` observes host state, changed only by `materialize` |
|
|
532
|
+
| `HostFile` | `{ path, content }` |
|
|
533
|
+
| `InitialFiles` | `Readonly<Record<string, string \| Uint8Array>>` |
|
|
534
|
+
| `FileSystemOptions` | `{ maxInlineBytes?, reservedPaths? }`. `maxInlineBytes` defaults to 1 MiB, matching `StepBoundary`'s evidence bound. `reservedPaths` lists root-relative engine state paths copy-back refuses, beyond `.smithers-workspace-lock` and `.flows`. |
|
|
535
|
+
|
|
536
|
+
### Errors
|
|
537
|
+
|
|
538
|
+
| Error | Fields |
|
|
539
|
+
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
540
|
+
| `WorkspaceError` | `code` of `invalid_path`, `not_found`, `host_unavailable`, `path_escapes_workspace`, or `commit_lock_timeout`; `message`; optional `cause` carried whole |
|
|
541
|
+
| `MaterializationConflict` | `paths` (at most 1,024, each at most 4,096 characters) and `message` |
|
|
542
|
+
|
|
543
|
+
It is a deterministic transaction model, not a security boundary. A body
|
|
544
|
+
reaching the host through a service the transaction does not seed is outside it;
|
|
545
|
+
denying that ambient access is the VM and `SandboxProvider` story in
|
|
546
|
+
[`@smthrs/sandbox`](/api/sandbox). The human diff-review gate is not implemented
|
|
547
|
+
and a settled bundle is applied without it, a known limitation of this release,
|
|
548
|
+
and the transaction's `FileSystem` surface is deliberately partial.
|
|
549
|
+
|
|
550
|
+
Full model: [Workspace transactions](./concepts/workspace-transactions.md).
|
|
551
|
+
|
|
552
|
+
## PlanScheduler
|
|
553
|
+
|
|
554
|
+
Every declared effect tier is schedulable. Sealed dispatches retain their
|
|
555
|
+
content-derived keys and cache-admission requirements. Compensable and
|
|
556
|
+
irreversible dispatches use run-local plan/node/declaration scopes, retain
|
|
557
|
+
their tier in attempt metadata, and never publish shared-cache results. A
|
|
558
|
+
`clean` settlement means no executor ran: it includes both same-run durable
|
|
559
|
+
replay and eligible shared-cache hits. Compensable retries restore the supplied
|
|
560
|
+
snapshot; the generic executor has no irreversible idempotency contract and
|
|
561
|
+
therefore refuses uncertain recovery or retry of those effects. Irreversible
|
|
562
|
+
conflicts fail without automatic rebase or merge elaboration.
|
|
563
|
+
|
|
564
|
+
[src/PlanScheduler.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/PlanScheduler.ts)
|
|
565
|
+
|
|
566
|
+
Drives a persisted [`@smthrs/plan`](/api/plan) plan to completion.
|
|
567
|
+
|
|
568
|
+
```ts
|
|
569
|
+
interface Service {
|
|
570
|
+
readonly record: (plan: Plan) => Effect<PlanStore.RecordResult, SchedulerError, PlanStore | Journal | Crypto>
|
|
571
|
+
readonly append: (plan: Plan) => Effect<void, SchedulerError, PlanStore | Journal | Crypto>
|
|
572
|
+
readonly run: (plan: Plan) => Effect<Report, SchedulerError, Requirements>
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
| Export | Signature | Meaning |
|
|
577
|
+
| --------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
578
|
+
| `PlanScheduler` | `Context.Service<Service>` | Service tag. |
|
|
579
|
+
| `make` | `(options: Options) => Service` | Builds a scheduler bound to one run. |
|
|
580
|
+
| `layer` | `(options: Options) => Layer<PlanScheduler>` | Provides it. |
|
|
581
|
+
| `NodeExecutor` | `Context.Service<Executor>` | The DI seam that turns a node into work. |
|
|
582
|
+
| `layerExecutor` | `(executor: Executor) => Layer<NodeExecutor>` | Provides one. |
|
|
583
|
+
| `recertify` | `(input: { plan, deferringRunId, options }) => Effect<RecertifyResult, ...>` | Re-drives a plan guess-free under a fresh run, then reports remaining debt. |
|
|
584
|
+
| `Requirements` | type | `AttemptStore`, `CacheStore`, `Crypto`, `Jj`, `Journal`, `NodeExecutor`, `PlanStore`, `PlanInputStore`, `PlanMergeStore`, `RunStore`, and `StepBoundary.Service`. |
|
|
585
|
+
|
|
586
|
+
### Options
|
|
587
|
+
|
|
588
|
+
| Field | Meaning |
|
|
589
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
590
|
+
| `runId` | The run this scheduler drives. |
|
|
591
|
+
| `owner` | The `OwnerId` its writes are fenced with. |
|
|
592
|
+
| `sourceId` | The journal source id. |
|
|
593
|
+
| `environment` | Runtime identity copied at construction and bound durably before dispatch; changing it requires a new run. Omission preserves action keys but is a distinct environment binding. |
|
|
594
|
+
| `concurrency.steps` | Caps leaf execution. Defaults to unbounded; zero is refused. |
|
|
595
|
+
| `concurrency.agents` | Caps the agent subset within it. Same defaults. |
|
|
596
|
+
| `rebaseLimit` | How many times a `delay-rebase` node may re-measure and re-key before reconciliation is asked. |
|
|
597
|
+
| `selection.changed` | Changed paths the belief edges are matched against. |
|
|
598
|
+
| `selection.beliefs` | The `BeliefSnapshot` pinned before planning. |
|
|
599
|
+
| `selection.policy` | Deferral policy; `deferBelow` defaults to zero, which defers nothing. |
|
|
600
|
+
| `selection.full` | Treat every verdict as `Admit`, journaled. |
|
|
601
|
+
|
|
602
|
+
Both concurrency caps must be positive safe integers and `rebaseLimit` a
|
|
603
|
+
non-negative one; invalid bounds are rejected at construction.
|
|
604
|
+
|
|
605
|
+
### Models
|
|
606
|
+
|
|
607
|
+
| Export | Shape |
|
|
608
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
609
|
+
| `Outcome` | `built`, `clean`, `failed`, `skipped`, or `deferred` |
|
|
610
|
+
| `ResolvedInput` | `{ from, path, value }`, the settled output of `from` projected along `path` |
|
|
611
|
+
| `NodeInput` | `{ node, attempt, boundary, inputs }` |
|
|
612
|
+
| `Executor` | `{ execute: (input: NodeInput) => Effect<unknown, unknown> }` |
|
|
613
|
+
| `Settlement` | `{ nodeId, planKey, dispatchKey, outcome, attempts, rebases }` |
|
|
614
|
+
| `Report` | `{ planId, digest, settlements, results, verdicts, appended }` |
|
|
615
|
+
| `RecertifyResult` | `{ runId, report, remaining }` |
|
|
616
|
+
| `SchedulerError` | `code` of `invalid_plan`, `boundary_unavailable`, `key_uncomputable`, `elaboration_failed`, or `store_failed` |
|
|
617
|
+
|
|
618
|
+
Ready nodes dispatch through the same persistence path every action uses, so
|
|
619
|
+
the shared step cache,
|
|
620
|
+
[`WorkspaceSandbox`](#workspacesandbox)'s execute-then-materialize transaction,
|
|
621
|
+
attempt rows, and the fenced journal all apply unchanged. A sealed dispatch key
|
|
622
|
+
folds the node's own declaration, measured file digests and projected material
|
|
623
|
+
inputs. It does not fold upstream plan keys, so unchanged consumed content can
|
|
624
|
+
reuse a result even when an upstream declaration changed.
|
|
625
|
+
|
|
626
|
+
Skyframe's `AbstractParallelEvaluator` is the prior art. A private, rebuildable
|
|
627
|
+
`RuntimeGraph` maintains forward/reverse adjacency, dependency counts and ready
|
|
628
|
+
frontiers. Reverse indexes propagate settled outcomes and readiness only;
|
|
629
|
+
content addressing still governs reuse, with no cache-invalidation visitor.
|
|
630
|
+
The coordinator reconciles each completion before admitting dependents, while
|
|
631
|
+
unrelated work can remain in flight. Retry/rebase attempts are not additional
|
|
632
|
+
node settlements. Appended indexes become visible after durable admission.
|
|
633
|
+
|
|
634
|
+
The existing scheduling policy ranks the current ready frontier by priority
|
|
635
|
+
plus capacity-constrained admission passes waited, with exact arithmetic and
|
|
636
|
+
compiled plan order for ties. It retains independent step and agent caps.
|
|
637
|
+
Readiness no longer scans the entire plan after each settlement; admission
|
|
638
|
+
still considers and re-ranks all ready candidates to preserve aging.
|
|
639
|
+
|
|
640
|
+
Read versions are relative to each reader's predecessors. A path with no
|
|
641
|
+
preceding writer is a source input, even when that reader or a later node writes
|
|
642
|
+
it. Source paths and source-glob membership are pinned before the first dispatch
|
|
643
|
+
(new generations are observed at append time); preceding producers' outputs are
|
|
644
|
+
measured after settlement. Later writers do not widen a read glob's membership.
|
|
645
|
+
A rebase re-observes preceding outputs, not source inputs.
|
|
646
|
+
|
|
647
|
+
`PlanInputStore` records those observations before dispatch and restores them on
|
|
648
|
+
reopen, without re-enumerating source globs or measuring source files again.
|
|
649
|
+
Each run binds one plan ID and approved base digest; a different approved
|
|
650
|
+
graph or plan ID needs a new run. New runs observe the current world independently and may
|
|
651
|
+
reuse eligible shared-cache results. These rules do not freeze external state
|
|
652
|
+
or replace boundary enforcement when an unfinished action actually executes.
|
|
653
|
+
|
|
654
|
+
`Options.environment` is copied at scheduler construction and bound durably
|
|
655
|
+
before source observation or dispatch. Recovery requires the same normalized
|
|
656
|
+
identity for all compiled effect tiers. Layer order and duplicates matter;
|
|
657
|
+
capability-pattern order and duplicates do not. Omission, declared-empty, and
|
|
658
|
+
undeclared/run-scoped identities are distinct. A changed environment returns
|
|
659
|
+
`store_failed` with a typed input/merge-store cause of `incompatible_state`, without
|
|
660
|
+
executing or replacing the old binding. Recover with the original environment,
|
|
661
|
+
or explicitly reconcile unfinished effects before starting a new run. This
|
|
662
|
+
checks supplied identities, not undeclared changes in implementation behavior.
|
|
663
|
+
|
|
664
|
+
Ready work is ordered by declared
|
|
665
|
+
`priority` plus one point per round waited, so priority changes latency without
|
|
666
|
+
permitting starvation.
|
|
667
|
+
|
|
668
|
+
Full guide: [Drive a plan to completion](./guides/drive-a-plan.md).
|
|
669
|
+
|
|
670
|
+
`stop-merge` records its stopped-attempt decision in `PlanMergeStore`. Recovery
|
|
671
|
+
preserves `skipped` without repeating that attempt, and commits each generated
|
|
672
|
+
merge with its plan extension, source observations, and durable append event.
|
|
673
|
+
Generated IDs avoid occupied user names. `skipped` does not mean no executor
|
|
674
|
+
ever ran: stopped attempts retain their attempt count. `Report.appended` lists
|
|
675
|
+
new appends by this invocation, not merges recovered from prior invocations.
|
|
676
|
+
|
|
677
|
+
## PlanMergeStore
|
|
678
|
+
|
|
679
|
+
Authoritative scheduler decisions, separate from action failure evidence and
|
|
680
|
+
redacted journal projections. Compose `layer` over the same `SqlClient` and
|
|
681
|
+
`DurableWriter` as `PlanInputStore`, `PlanStore`, and `Journal`, with `Crypto`;
|
|
682
|
+
run migrations first. `TestStores.layer` and `layerAt` include it.
|
|
683
|
+
|
|
684
|
+
| Export | Contract |
|
|
685
|
+
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
686
|
+
| `Identity` | `{ runId, planId, baseDigest, environmentDigest }`. |
|
|
687
|
+
| `Intent` | Version 1; stopped node/key, dispatch key, attempts/rebases, and exact eligible peers. |
|
|
688
|
+
| `Completion` | Version 1; appended generation, parent/result digests, merge ID/key, and winning peers. |
|
|
689
|
+
| `Decision` | Intent and optional committed completion. |
|
|
690
|
+
| `list(identity, owner)` | Committed decisions; refuses enclosing SQL transactions. |
|
|
691
|
+
| `intend(identity, intent, owner)` | First writer wins; joins an outer transaction when provided. Await its commit before exposing the stopped decision. |
|
|
692
|
+
| `complete(identity, nodeId, completion, owner)` | Requires the caller's plan/input append transaction. Repeated identical completion converges; a different completion is refused. |
|
|
693
|
+
| `make`, `layer`, `PlanMergeStore` | SQL constructor, layer, and service tag. |
|
|
694
|
+
| `maximumDecisionCharacters` | 16 Mi JavaScript string code units per encoded decision. |
|
|
695
|
+
|
|
696
|
+
All operations check current ownership, cancellation, and the admitted plan and
|
|
697
|
+
environment. Rows are versioned, checksummed, immutable, and retained until run
|
|
698
|
+
collection; checksums detect corruption, not malicious database writes. The
|
|
699
|
+
scheduler additionally reconstructs approved generation digests and validates
|
|
700
|
+
stopped-node policy before dispatch. It can recover recorded merge-only
|
|
701
|
+
extensions from the original base, but requires supplied approved intervening
|
|
702
|
+
manual generations rather than inventing missing work.
|
|
703
|
+
|
|
704
|
+
`PlanMergeError.code` distinguishes `invalid_input`, `corrupt_state`,
|
|
705
|
+
`incompatible_state`, `fence_lost`, `transaction_open`, `transaction_required`,
|
|
706
|
+
and `persistence_failed`. The scheduler interrupts on fence loss and otherwise
|
|
707
|
+
reports `SchedulerError` with `store_failed` and the typed cause. Migration
|
|
708
|
+
`3005` leaves existing observation heads' merge-state version unknown and
|
|
709
|
+
refuses recovery under this runtime. Finish on the prior runtime or reconcile
|
|
710
|
+
unfinished effects before starting a new run; never infer authority from
|
|
711
|
+
merge-like names/bodies or backfill an unknown version by guessing.
|
|
712
|
+
|
|
713
|
+
## PlanInputStore
|
|
714
|
+
|
|
715
|
+
Authoritative, append-only source observations for compiled plan execution.
|
|
716
|
+
Compose `PlanInputStore.layer` over the **same** `SqlClient` and `DurableWriter`
|
|
717
|
+
as the run, attempt, plan, and journal stores, with `Crypto`. The layer does not
|
|
718
|
+
run migrations. `TestStores.layer` and `layerAt` include it.
|
|
719
|
+
|
|
720
|
+
| Export | Contract |
|
|
721
|
+
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
722
|
+
| `Address` | `{ runId, planId, baseDigest, environmentDigest, generation }`; derive the environment digest with `StepKey.environmentIdentity(environment)`. |
|
|
723
|
+
| `Snapshot` | Version 1; generation; node IDs/keys and each read declaration's frozen source paths; newly pinned `{ path, digest }` entries. |
|
|
724
|
+
| `get(address, owner)` | Returns the recorded snapshot or `None` for the next generation. Refuses enclosing SQL transactions so execution cannot consume speculative state. |
|
|
725
|
+
| `record(address, snapshot, owner)` | First-writer-wins; returns the winning snapshot. Consecutive generations only. Can join a transaction, whose outer commit the caller must await before dispatch. |
|
|
726
|
+
| `make`, `layer`, `PlanInputStore` | SQL constructor, layer, and service tag. No memory/cache fallback. |
|
|
727
|
+
| `maximumSnapshotCharacters` | 16 Mi JavaScript string code units per encoded generation, checked before persisted JSON is decoded. |
|
|
728
|
+
|
|
729
|
+
Reads and writes validate ownership and absent cancellation within the owning
|
|
730
|
+
write transaction. Snapshot rows are checksummed, versioned, schema-validated,
|
|
731
|
+
and immutable; the scheduler additionally checks node declarations, file-version
|
|
732
|
+
selection, and pin consistency before using them. Checksums detect corruption,
|
|
733
|
+
not unauthorized database modification. Rows are retained until their owning
|
|
734
|
+
run is collected; they are not subject to cache eviction or journal redaction.
|
|
735
|
+
|
|
736
|
+
`PlanInputError.code` distinguishes `invalid_input`, `corrupt_state`,
|
|
737
|
+
`incompatible_state`, `fence_lost`, `transaction_open`, and `persistence_failed`.
|
|
738
|
+
The scheduler self-interrupts on fence loss and otherwise exposes the typed
|
|
739
|
+
cause through `SchedulerError` with `store_failed`.
|
|
740
|
+
|
|
741
|
+
Migration `engine-store/0003_plan_inputs` marks runs that already had attempts
|
|
742
|
+
before durable observations existed. Such runs cannot safely enter the new
|
|
743
|
+
compiled scheduler: their original sources may have been overwritten. Finish
|
|
744
|
+
them on the previous runtime before upgrading, or make an explicit operator
|
|
745
|
+
recovery decision and start new work under a new run ID. Do not delete the
|
|
746
|
+
legacy marker or retry an uncertain irreversible effect blindly.
|
|
747
|
+
|
|
748
|
+
Migration `engine-store/0004_plan_environment` adds the environment binding.
|
|
749
|
+
Existing observation heads keep an unknown (`NULL`) environment and are refused;
|
|
750
|
+
the migration never guesses from the current process. New heads require a
|
|
751
|
+
non-empty, immutable fingerprint. Earlier snapshots and attempts remain intact.
|
|
752
|
+
|
|
753
|
+
## Reconciliation
|
|
754
|
+
|
|
755
|
+
[src/Reconciliation.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/Reconciliation.ts)
|
|
756
|
+
|
|
757
|
+
The pluggable seam for when the world disagrees with the declaration. It is the
|
|
758
|
+
first consumer `flows.engine.expected-set-deviation` has had.
|
|
759
|
+
|
|
760
|
+
```ts
|
|
761
|
+
interface Service {
|
|
762
|
+
readonly onDeviation: (deviation: Deviation) => Effect<Verdict>
|
|
763
|
+
readonly onConflict: (conflict: Conflict) => Effect<Verdict>
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
| Export | Signature | Meaning |
|
|
768
|
+
| ----------------------- | --------------------------------------------- | ----------------------------------- |
|
|
769
|
+
| `Reconciliation` | `Context.Service<Service>` | Service tag. |
|
|
770
|
+
| `make` | `(service: Service) => Service` | Names an implementation. |
|
|
771
|
+
| `layer` | `(service: Service) => Layer<Reconciliation>` | Installs one. |
|
|
772
|
+
| `makeDefault` | `() => Service` | The deterministic verdict function. |
|
|
773
|
+
| `layerDefault` | `Layer<Reconciliation>` | Installs it. |
|
|
774
|
+
| `Deviation`, `Conflict` | schemas | What the scheduler reports. |
|
|
775
|
+
| `Verdict` | schema | `Fail`, `Reorder`, or `FactorOut`. |
|
|
776
|
+
|
|
777
|
+
`layerDefault` answers in one order of preference: `Reorder` when every
|
|
778
|
+
undeclared path is one another plan node declares it writes, `FactorOut` when
|
|
779
|
+
another node in the same run deviated on exactly the same paths, and `Fail`
|
|
780
|
+
otherwise. A conflict the runtime strategy could not absorb always fails, because
|
|
781
|
+
choosing a winner between two landings is a semantic judgement this default does
|
|
782
|
+
not have the material to make.
|
|
783
|
+
|
|
784
|
+
The scheduler attributes every deviation on a journal page before judging any of
|
|
785
|
+
it, so two steps that produced the same undeclared paths both see each other.
|
|
786
|
+
Pluggability is dependency injection at the owning seam; there is no hook
|
|
787
|
+
kernel. A model-backed reconciler is a different `Layer` and lives in the agent
|
|
788
|
+
packages: this package has no model dependency and must not grow one.
|
|
789
|
+
|
|
790
|
+
## Selection
|
|
791
|
+
|
|
792
|
+
[src/Selection.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/Selection.ts)
|
|
793
|
+
|
|
794
|
+
The advisory scheduler seam. It may return `Admit`, `Defer`, or `Propose`
|
|
795
|
+
verdicts for sink candidates and missing flows, and never changes a step key, a
|
|
796
|
+
cache row, or a correctness decision.
|
|
797
|
+
|
|
798
|
+
| Export | Signature | Meaning |
|
|
799
|
+
| ---------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------- |
|
|
800
|
+
| `Selection` | `Context.Service<Service>` | Service tag. |
|
|
801
|
+
| `Service` | `{ select: (input: Input) => Effect<ReadonlyArray<Selected>> }` | One verdict per candidate. |
|
|
802
|
+
| `make` | `(service: Service) => Service` | Names an implementation. |
|
|
803
|
+
| `layer` | `(service: Service) => Layer<Selection>` | Installs one. |
|
|
804
|
+
| `makeNoop` / `layerNoop` | | Admits everything. The behavioral default. |
|
|
805
|
+
| `makeHeuristic` / `layerHeuristic` | | Pure glob matching over live edges. |
|
|
806
|
+
| `debt` | `(runId: string, options?: DebtOptions) => Effect<ReadonlyArray<DebtEntry>, ..., Journal>` | Folds the journal for unpaid deferrals. |
|
|
807
|
+
| `risk` | `(input: { changed, beliefs }) => Risk` | A pure annotation, never a gate. |
|
|
808
|
+
| `card` | `(input: CardInput) => ReadonlyArray<string>` | Renders the plan card, one string per line. |
|
|
809
|
+
| `proposeReadSet` | `(input: { beliefs, flow, paths }) => ReadonlyArray<string>` | The `boundaryMode: "expected"` feeder. |
|
|
810
|
+
|
|
811
|
+
### Schemas and models
|
|
812
|
+
|
|
813
|
+
| Export | Shape |
|
|
814
|
+
| -------------------- | ----------------------------------------------------------------------------------------- |
|
|
815
|
+
| `SuspectedEdge` | `{ scope, affects, confidence, validFromMs, evidence }` |
|
|
816
|
+
| `BeliefSnapshot` | `{ pinnedAtMs, edges }` |
|
|
817
|
+
| `Verdict` | `Admit`, `Defer { edge, likelihood }`, or `Propose { flow, edge, confidence }` |
|
|
818
|
+
| `Policy` | `{ deferBelow }`. Zero defers nothing; a likelihood exactly at the threshold is admitted. |
|
|
819
|
+
| `Candidate` | `{ nodeId, planKey, stats? }` |
|
|
820
|
+
| `Input` | `{ changed, sinks, present, beliefs, policy }` |
|
|
821
|
+
| `Selected` | `{ nodeId, verdict }` |
|
|
822
|
+
| `DebtEntry` | `{ planId, nodeId, planKey, edge, likelihood, seq }` |
|
|
823
|
+
| `DebtOptions` | `{ repaidBy? }` |
|
|
824
|
+
| `Risk` | `{ level: "low" \| "medium" \| "high", reasons }` |
|
|
825
|
+
| `CardInput` | `{ settlements, deferrals, proposals, cadence, risk? }` |
|
|
826
|
+
| `NonNegativeSafeInt` | the epoch and counter schema both public schemas and SQLite accept |
|
|
827
|
+
|
|
828
|
+
`layerHeuristic` matches live edges (`validFromMs <= pinnedAtMs`): a matching
|
|
829
|
+
edge supplies the likelihood, a sink can defer only when a live edge names it,
|
|
830
|
+
and a `Candidate.stats` failure ratio raises the likelihood so flaky sinks stay
|
|
831
|
+
inline. Stats alone never defer. A model-backed layer is out of scope, because
|
|
832
|
+
`engine-store` must not depend on a model.
|
|
833
|
+
|
|
834
|
+
`risk` returns `high` at confidence at or above 0.7, `medium` at or above 0.4,
|
|
835
|
+
otherwise `low`, with each reason named `<scope> -> <affects> (<confidence>)`.
|
|
836
|
+
`card` row strings are stable: `clean` renders as `cached` and `built` as
|
|
837
|
+
`run`.
|
|
838
|
+
|
|
839
|
+
Out of scope here: CLI verbs, approval routing, auto-appending proposals, and
|
|
840
|
+
the recertification cadence, which is a product concern rather than a store
|
|
841
|
+
primitive.
|
|
842
|
+
|
|
843
|
+
Full guide: [Defer work with selection beliefs](./guides/defer-work-with-selection.md).
|
|
844
|
+
|
|
845
|
+
## SelectionStore
|
|
846
|
+
|
|
847
|
+
[src/SelectionStore.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/SelectionStore.ts)
|
|
848
|
+
|
|
849
|
+
The durable suspected-edge store, tagged `@smthrs/engine-store/SelectionStore`.
|
|
850
|
+
|
|
851
|
+
| Export | Signature | Meaning |
|
|
852
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------- |
|
|
853
|
+
| `SelectionStore` | `Context.Service<Service>` | Service tag. |
|
|
854
|
+
| `make` | `Effect<Service, never, DurableWriter \| SqlClient>` | Builds it through this package's migration set. |
|
|
855
|
+
| `layer` | `Layer<SelectionStore, never, DurableWriter \| SqlClient>` | Provides it. |
|
|
856
|
+
| `maxEvidenceEntries` | `128` | Newest evidence entries retained per edge. |
|
|
857
|
+
| `TrainingOutcome` | `"hit" \| "miss"` | |
|
|
858
|
+
| `TrainingObservation` | `{ scope, affects, outcome }` | |
|
|
859
|
+
| `SelectionStoreError` | `code` of `invalid_input`, `decode_failed`, or `persistence_failed`, plus optional `scope`, `affects`, and `cause` | |
|
|
860
|
+
|
|
861
|
+
`upsert(edges)` inserts or replaces by `(scope, affects)`. `list()` returns
|
|
862
|
+
every edge. `snapshot()` pins the injected clock's current time, never
|
|
863
|
+
`Date.now()`. `train(observations)` updates only matching edges in one
|
|
864
|
+
transaction, ignores unknown pairs, appends every observation to evidence, and
|
|
865
|
+
applies the asymmetric rule: a hit becomes `confidence + 0.05 * (1 -
|
|
866
|
+
confidence)`, a miss becomes `confidence * 0.5`. Training never creates edges
|
|
867
|
+
and never writes journal records.
|
|
868
|
+
|
|
869
|
+
## ArtifactSync
|
|
870
|
+
|
|
871
|
+
[src/ArtifactSync.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/ArtifactSync.ts)
|
|
872
|
+
|
|
873
|
+
The two-tier artifact protocol, and the seam a shared-cache composition injects
|
|
874
|
+
into.
|
|
875
|
+
|
|
876
|
+
| Export | Signature | Meaning |
|
|
877
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
878
|
+
| `ArtifactSync` | `Context.Service<Service>` | Service tag. |
|
|
879
|
+
| `makeLocal` | `() => Service` | Publish is a no-op; hydrate reports nothing arrived. The default when the tag is absent. |
|
|
880
|
+
| `layerLocal` | `Layer<ArtifactSync>` | Provides it. |
|
|
881
|
+
| `make` | `(options: { local, remote, downloadPolicy? }) => Service` | The two-tier protocol. |
|
|
882
|
+
| `layer` | `(remote: Effect<ArtifactStore.Service, E, R>, options?) => Layer<ArtifactSync, E, ArtifactStore \| R>` | Takes the local tier from the `ArtifactStore` tag and the shared tier from the effect. |
|
|
883
|
+
| `DownloadPolicy` | `RemoteArtifacts.DownloadPolicy` | `all`, `toplevel`, or `minimal`. |
|
|
884
|
+
| `ArtifactPublicationFailed` | `code: "artifact_publication_failed"`, fields `digests`, `message`, optional `cause` | |
|
|
885
|
+
|
|
886
|
+
`publish(digests)` runs `findMissing` on the shared tier, uploads what is
|
|
887
|
+
missing, and re-probes to confirm. The engine calls it immediately before the
|
|
888
|
+
transaction that records the cache entry, and never inside it: this
|
|
889
|
+
is Bazel's REAPI ordering constraint, because a result accessed before its blobs
|
|
890
|
+
are present cannot be validated. A publication that cannot make the artifacts
|
|
891
|
+
durable fails and the shared entry is withheld.
|
|
892
|
+
|
|
893
|
+
`hydrate(digests)` establishes that this host can resolve every referenced
|
|
894
|
+
artifact and reports whether the replay is now worth retrying. It never fails a
|
|
895
|
+
run: a shared tier that is down must not stop work that can simply be done.
|
|
896
|
+
|
|
897
|
+
The two tiers cannot both be layers, because they inhabit the same tag and
|
|
898
|
+
composing them would shadow one with the other.
|
|
899
|
+
|
|
900
|
+
### DownloadPolicy
|
|
901
|
+
|
|
902
|
+
Declared on the shared tier as `RemoteArtifacts.Options.downloadPolicy` and read
|
|
903
|
+
from the store `make` was handed, so one deployment setting reaches both seams.
|
|
904
|
+
An explicit `downloadPolicy` on `make` or `layer` overrides it.
|
|
905
|
+
|
|
906
|
+
| Policy | `hydrate` behavior |
|
|
907
|
+
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
908
|
+
| `all` (default) | Downloads every referenced artifact into this host's store while admitting the replay. Every later read is local, and a shared tier that goes down afterwards costs nothing. |
|
|
909
|
+
| `toplevel` | Downloads nothing. One batched `findMissing` establishes that the shared tier can serve what is missing, and `CombinedArtifacts.get` fetches and writes back the blobs a reader actually reads. |
|
|
910
|
+
| `minimal` | The same probe and the same zero downloads; `CombinedArtifacts.get` then serves without writing back, so this host never accumulates other machines' artifacts. |
|
|
911
|
+
|
|
912
|
+
The two lazy policies are sound only when the store the replay reads through can
|
|
913
|
+
reach the shared tier, which means `CombinedArtifacts` with the same remote
|
|
914
|
+
tier. A tier that refuses the probe is indistinguishable from one that holds
|
|
915
|
+
nothing, so the replay is refused either way and the step executes.
|
|
916
|
+
|
|
917
|
+
## CacheSync
|
|
918
|
+
|
|
919
|
+
[src/CacheSync.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/CacheSync.ts)
|
|
920
|
+
|
|
921
|
+
The second half of the ordering constraint: the shared step-result tier's `put`,
|
|
922
|
+
run after the transaction that made the local row durable.
|
|
923
|
+
|
|
924
|
+
| Export | Signature | Meaning |
|
|
925
|
+
| ------------ | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
|
|
926
|
+
| `CacheSync` | `Context.Service<Service>` | Service tag. |
|
|
927
|
+
| `Service` | `{ publishEntry: (entry: CacheStore.CacheEntry) => Effect<Option<CacheStore.CacheStoreError>> }` | `none` means shared, `some(error)` means not, and why. |
|
|
928
|
+
| `makeLocal` | `() => Service` | The single-tier implementation. The default when the tag is absent. |
|
|
929
|
+
| `layerLocal` | `Layer<CacheSync>` | Provides it. |
|
|
930
|
+
| `make` | `(options: { remote: CacheStore.Service }) => Service` | Publishes to a shared store, typically `RemoteCacheStore`. |
|
|
931
|
+
| `layer` | `(remote: Effect<CacheStore.Service, E, R>) => Layer<CacheSync, E, R>` | Provides it. |
|
|
932
|
+
|
|
933
|
+
It is a separate seam from the `CacheStore` tag because of where the local row
|
|
934
|
+
is written. The engine commits the cache row and the journal record explaining
|
|
935
|
+
it in one `DurableWriter` transaction, and nothing that is not
|
|
936
|
+
storage work may be held across one: a `CacheStore` whose `put` also wrote a
|
|
937
|
+
shared HTTP tier would put a network round trip inside that transaction,
|
|
938
|
+
blocking every other writer for its duration and rolling the local row back
|
|
939
|
+
whenever a shared cache was unreachable. Compose it with `CombinedCacheStore` in
|
|
940
|
+
`"deferred"` publication mode; lookups stay read-through either way.
|
|
941
|
+
|
|
942
|
+
A `Conflict` from the shared tier is not reported: it means another machine
|
|
943
|
+
recorded the key first, which is the first-writer-wins outcome the shared tier
|
|
944
|
+
exists to arbitrate.
|
|
945
|
+
|
|
946
|
+
Neither publication step can fail a run. Both run after `attempts.finish`, so
|
|
947
|
+
the result is already durably recorded on this host. A refusal withholds the
|
|
948
|
+
shared copy, never the local row, and journals a `cache-provenance` record with
|
|
949
|
+
`action: "unpublished"` carrying the stage (`artifacts` or `entry`) and the
|
|
950
|
+
reason.
|
|
951
|
+
|
|
952
|
+
## ArtifactGc
|
|
953
|
+
|
|
954
|
+
[src/ArtifactGc.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/ArtifactGc.ts)
|
|
955
|
+
|
|
956
|
+
Explicit mark and sweep collection of unreferenced blobs. Collection never runs
|
|
957
|
+
automatically; [`smthrs gc`](/cli/gc) runs it after retention.
|
|
958
|
+
|
|
959
|
+
| Export | Signature | Meaning |
|
|
960
|
+
| ------------------ | --------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
961
|
+
| `ArtifactGc` | `Context.Service<Service>` | Service tag. |
|
|
962
|
+
| `Service` | `{ gc: (options?: GcOptions) => Effect<GcReport, ArtifactGcError> }` | Marks the live set from the durable roots, then sweeps. |
|
|
963
|
+
| `make` | `(options?: MakeOptions) => Effect<Service, never, SqlClient \| ArtifactSweep>` | Builds the collector. |
|
|
964
|
+
| `layer` | `(options?: MakeOptions) => Layer<ArtifactGc, never, SqlClient \| ArtifactSweep>` | Provides it. |
|
|
965
|
+
| `layerFileSystem` | `(options?: MakeOptions & SweepOptions) => Layer<ArtifactGc, never, SqlClient \| FileSystem>` | Provides it over a filesystem objects directory. |
|
|
966
|
+
| `ArtifactGcPolicy` | `Context.Service<Policy>` | The opt-in collection policy. |
|
|
967
|
+
| `layerPolicy` | `(policy: Policy) => Layer<ArtifactGcPolicy>` | Installs it. |
|
|
968
|
+
| `defaultGraceMs` | 14 days | Git's `gc.pruneExpire` default. |
|
|
969
|
+
| `ArtifactGcError` | `code` of `invalid_options`, `mark_failed`, or `sweep_failed` | |
|
|
970
|
+
|
|
971
|
+
| Type | Fields |
|
|
972
|
+
| ------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
973
|
+
| `MakeOptions` | `pageSize`, rows per mark-phase page. Defaults to 500. |
|
|
974
|
+
| `Policy` | `graceMs?`, and `pins?` as an `Effect<ReadonlyArray<string>>` resolved fresh on every collection. |
|
|
975
|
+
| `GcOptions` | `graceMs?`, `pins?`, `dryRun?`. Explicit options override the installed policy; the policy overrides the defaults. |
|
|
976
|
+
| `GcReport` | `scannedBlobs`, `liveDigests`, `sweptDigests`, `reclaimedBytes`, `keptByGrace`, `dryRun` |
|
|
977
|
+
|
|
978
|
+
The mark is fail-safe: a root row carrying boundary evidence this build cannot
|
|
979
|
+
decode aborts the collection rather than contributing nothing. Attempt
|
|
980
|
+
checkpoints are also live roots, with digest-shaped strings retained
|
|
981
|
+
conservatively. The live set is computed before the inventory, so a root
|
|
982
|
+
recorded during the sweep can only be missed, never half seen, and the blob such
|
|
983
|
+
a root references is protected by the mtime fence anyway.
|
|
984
|
+
|
|
985
|
+
Full guide: [Collect unreferenced artifacts](./guides/collect-unreferenced-artifacts.md).
|
|
986
|
+
|
|
987
|
+
## StepCacheFold
|
|
988
|
+
|
|
989
|
+
[src/StepCacheFold.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/StepCacheFold.ts)
|
|
990
|
+
|
|
991
|
+
`flows_step_cache` heads as a fold of the journal. Drop the heads and
|
|
992
|
+
`rebuild` writes them back from the retained `cache-provenance` entries.
|
|
993
|
+
|
|
994
|
+
| Export | Signature | Meaning |
|
|
995
|
+
| ----------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
996
|
+
| `rebuild` | `() => Effect<Rebuilt, JournalError \| CacheStoreError \| SqlError, Journal \| SqlClient \| DurableWriter>` | Folds every run's retained entries and replaces the heads. |
|
|
997
|
+
| `reduce` | `(state: State, entry: JournalEvent.Entry) => State` | Folds one entry. |
|
|
998
|
+
| `initial` | `State` | The empty fold. |
|
|
999
|
+
| `projection` | `Projection<State>` | The same fold for `Journal.project` over one run. |
|
|
1000
|
+
| `candidates` | `(state: State) => ReadonlyArray<CacheStore.RecordedRef>` | The head each key may be rebuilt with. |
|
|
1001
|
+
| `refId` | `(ref: CacheStore.RecordedRef) => string` | A recorded row's stable identity. |
|
|
1002
|
+
| `eventType` | `"flows.engine.cache-provenance"` | The entries the fold reads. |
|
|
1003
|
+
| `retiringActions` | `["expired", "stale_read_set", "replay_failed"]` | Provenance actions that retire the row they name. |
|
|
1004
|
+
|
|
1005
|
+
| Type | Fields |
|
|
1006
|
+
| --------- | ------------------------------------------------------------------- |
|
|
1007
|
+
| `State` | `admitted` and `retired`: the rows `put` inserted, the ones evicted |
|
|
1008
|
+
| `Rebuilt` | `runs`, `compacted`, `rewound`, `admitted`, `retired`, `heads` |
|
|
1009
|
+
|
|
1010
|
+
A `put` that inserts a head journals an `admitted` entry naming its row in the
|
|
1011
|
+
same transaction. A duplicate or a losing conflict writes a ledger row but no
|
|
1012
|
+
`admitted` entry. These entries retire the row they name:
|
|
1013
|
+
|
|
1014
|
+
| Entry | Why the row cannot serve |
|
|
1015
|
+
| --------------------------- | -------------------------------------------------- |
|
|
1016
|
+
| `expired`, `stale_read_set` | The engine evicted it. |
|
|
1017
|
+
| `replay_failed` | The engine may have quarantined it for corruption. |
|
|
1018
|
+
|
|
1019
|
+
A producer slot keeps the first `replay_failed` reason it journalled, so a
|
|
1020
|
+
corruption can hide behind an earlier host failure; the fold retires the row on
|
|
1021
|
+
any reason, and a row whose replay failed only on the host is a miss.
|
|
1022
|
+
|
|
1023
|
+
A key with one admitted row that no entry retired gets that row back as its
|
|
1024
|
+
head. A key with several gets no head: one of them left without a journal
|
|
1025
|
+
entry, such as by `sweepExpired`, and the journal cannot say which one the
|
|
1026
|
+
store held. The journal holds provenance, not results: the result stays in the
|
|
1027
|
+
ledger row the entry addresses, because the journal is redacted and replayed
|
|
1028
|
+
to sync subscribers.
|
|
1029
|
+
|
|
1030
|
+
A rebuild writes a subset of the live heads. It drops heads with no local
|
|
1031
|
+
entry, such as shared-tier write-backs. A store with a compacted run, or one where
|
|
1032
|
+
any run was ever rewound, gets no heads, because compaction and rewind may have
|
|
1033
|
+
deleted the eviction that retired a row. Retention is not yet covered: it
|
|
1034
|
+
deletes a run's entries and compaction floor, so a rebuild after retention can
|
|
1035
|
+
restore a row a deleted run evicted. A single admitted row that `sweepExpired`
|
|
1036
|
+
or an operator `evict` removed also returns; every restored hit still passes
|
|
1037
|
+
the engine's age, read-set, and digest checks. Run it on a
|
|
1038
|
+
quiescent store: a write between the journal read and the rewrite is missed.
|
|
1039
|
+
|
|
1040
|
+
## Retention
|
|
1041
|
+
|
|
1042
|
+
[src/Retention.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/Retention.ts)
|
|
1043
|
+
|
|
1044
|
+
Explicit deletion of finished run state. Nothing schedules any of it.
|
|
1045
|
+
|
|
1046
|
+
| Export | Signature | Meaning |
|
|
1047
|
+
| ------------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
1048
|
+
| `Retention` | `Context.Service<Service>` | Service tag. |
|
|
1049
|
+
| `Service` | `{ retain: (options: RetainOptions) => Effect<RetainReport, RetentionError \| JournalError> }` | One bounded pass inside one `journal.transact`. |
|
|
1050
|
+
| `make` | `Effect<Service, never, SqlClient \| Journal>` | Builds it. |
|
|
1051
|
+
| `layer` | `Layer<Retention, never, SqlClient \| Journal>` | Provides it. |
|
|
1052
|
+
| `collect` | `(options: Options) => Effect<Report, SqlError \| RetentionError, SqlClient>` | The host-facing pass, over one database file. |
|
|
1053
|
+
| `eligible` | `(olderThanMs: number, limit?: number) => Effect<ReadonlyArray<string>, SqlError, SqlClient>` | The candidate run ids, oldest first. |
|
|
1054
|
+
| `defaultLimit` | `1000` | Runs deleted by one pass when the caller names no bound. |
|
|
1055
|
+
| `terminalStatuses` | `["completed", "failed", "cancelled"]` | The statuses a run can never leave. |
|
|
1056
|
+
| `runScopedTables` | `ReadonlyArray<RunScopedTable>` | Every table a deleted run leaves rows in, and the column naming the run. |
|
|
1057
|
+
| `RetentionError` | `code` of `scan_failed` or `delete_failed` | |
|
|
1058
|
+
|
|
1059
|
+
| Type | Fields |
|
|
1060
|
+
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1061
|
+
| `RetainOptions` | `olderThanMs` (a duration: how long a run must have been finished), `limit?`, `dryRun?`. A negative `olderThanMs` is read as zero; a `limit` that is not a non-negative safe integer is read as zero. |
|
|
1062
|
+
| `RetainReport` | `cutoffMs`, `runIds`, `retainedForLiveDescendants`, `retainedForLiveAncestors`, `runs`, `attempts`, `clockDeadlines`, `deferredCompletions`, `journalEntries`, `journalCheckpoints`, `journalIdentities`, `archiveEntries`, `timeTravelReceipts`, `dryRun` |
|
|
1063
|
+
| `Options` (for `collect`) | `olderThanMs` (an absolute epoch millisecond threshold), `dryRun?`, `database?`, `limit?`, `pins?` |
|
|
1064
|
+
| `Report` (from `collect`) | `database`, `olderThanMs`, `runs`, `deleted` (empty under a dry run), `dryRun` |
|
|
1065
|
+
|
|
1066
|
+
A run is a candidate only when its status is terminal and it finished before the
|
|
1067
|
+
cutoff, and it is retained whenever a live run stands above or below it in the
|
|
1068
|
+
lineage, over both the `flows_run_parents` edge a spawned child records and the
|
|
1069
|
+
`parent_run_id` column a trampoline lineage is chained through. `collect` runs
|
|
1070
|
+
the same deletion and the same guard, and drops the edge-table half of the walk
|
|
1071
|
+
for a database that does not carry it, such as the control plane's.
|
|
1072
|
+
|
|
1073
|
+
Both passes select children before parents, so a trampoline lineage longer
|
|
1074
|
+
than `limit` still loses at least one run every pass.
|
|
1075
|
+
|
|
1076
|
+
`collect` is the pass [`smthrs gc`](/cli/gc) runs over one database file.
|
|
1077
|
+
|
|
1078
|
+
Full guide: [Delete old run history](./guides/delete-old-run-history.md).
|
|
1079
|
+
|
|
1080
|
+
The host-facing `collect` also accepts `pins`, a list of run IDs required by
|
|
1081
|
+
live ancestry in another store. Pinned runs and their continuation ancestors
|
|
1082
|
+
are excluded before the bounded candidate window, so protected history cannot
|
|
1083
|
+
starve collection of unrelated terminal runs.
|
|
1084
|
+
|
|
1085
|
+
## ExecutionSnapshot
|
|
1086
|
+
|
|
1087
|
+
Execution snapshot reads validate the batch source and safe non-negative revisions,
|
|
1088
|
+
return exactly one row per requested ID in request order (including duplicates),
|
|
1089
|
+
and require every row to share the batch source and have a revision no greater
|
|
1090
|
+
than the batch watermark. Incoherent batches fail with `RunStoreError` and
|
|
1091
|
+
`persistence_failed`; malformed stored fields retain `decode_failed`.
|
|
1092
|
+
|
|
1093
|
+
The engine-owned read port, exported from the root and
|
|
1094
|
+
`@smthrs/engine-store/ExecutionSnapshot`. Its `make()` and `layer` require
|
|
1095
|
+
`SqlClient` with the engine migrations applied.
|
|
1096
|
+
|
|
1097
|
+
| Export | Contract |
|
|
1098
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1099
|
+
| `ExecutionSnapshot`, `Service` | Service tag and its `read` and `related` operations. |
|
|
1100
|
+
| `Service.read(runIds)` | Coherent `Batch` for up to `maximumBatchSize` (200) IDs, preserving request order and duplicates. |
|
|
1101
|
+
| `withReadTransaction(sql, effect)` | Reserves a read transaction without writer admission: deferred on SQLite and repeatable-read, read-only on PostgreSQL. An existing transaction retains its caller-selected isolation. |
|
|
1102
|
+
| `Service.related(options)` | Bounded `RelatedPage` of direct durable children or lineage rounds, with the requested anchor. |
|
|
1103
|
+
| `Position` | Durable database `source` and monotonic `revision`. |
|
|
1104
|
+
| `Snapshot`, `Observed`, `Missing` | Explicit observed execution or absence, including deletion evidence. |
|
|
1105
|
+
| `Waiting` | Timer, signal, approval, quota, human, or other reason, with wake time and SHA-256 `tokenDigest`. |
|
|
1106
|
+
| `Batch`, `RelatedOptions`, `RelatedPage` | Source watermark, scoped results, relation selector, and optional continuation. |
|
|
1107
|
+
| `isNewer(incoming, stored)` | True only for the same source with a greater revision. |
|
|
1108
|
+
|
|
1109
|
+
The requested run's lifecycle remains round scoped. Intent and the first owner's
|
|
1110
|
+
cancellation acknowledgement are separate fields. Observation failures use
|
|
1111
|
+
`RunStoreError` with causes preserved. `withReadTransaction` also preserves SQL
|
|
1112
|
+
transaction admission errors; interruption releases the read transaction.
|
|
1113
|
+
|
|
1114
|
+
## RunChangeFeed
|
|
1115
|
+
|
|
1116
|
+
Exported from the root and `@smthrs/engine-store/RunChangeFeed`. `make()` and
|
|
1117
|
+
`layer` require a migrated `SqlClient`; `RunChangeFeed` is the service tag.
|
|
1118
|
+
|
|
1119
|
+
`Service.current` reads a `Position`.
|
|
1120
|
+
`Service.changesSince({ source, revision, limit })` returns a `Page` containing
|
|
1121
|
+
`Change` entries, the observed source watermark, `nextRevision`, and `hasMore`.
|
|
1122
|
+
The allowed limit is 1 through `maximumPageSize` (1,000). Changes are coalesced
|
|
1123
|
+
per run ID and ordered by revision. Tombstones persist indefinitely, including
|
|
1124
|
+
after ordinary run retention, so an offline consumer can resume its checkpoint.
|
|
1125
|
+
Source replacement or an ahead-of-source checkpoint fails with `invalid_run`.
|
|
1126
|
+
|
|
1127
|
+
## RunCatalogRead
|
|
1128
|
+
|
|
1129
|
+
[src/RunCatalogRead.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/RunCatalogRead.ts)
|
|
1130
|
+
|
|
1131
|
+
Bounded execution pages and the preserved polling run set for a
|
|
1132
|
+
[`@smthrs/sync`](/api/smithers-sync) `RunCatalog`.
|
|
1133
|
+
|
|
1134
|
+
| Export | Signature | Meaning |
|
|
1135
|
+
| ---------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
|
|
1136
|
+
| `RunCatalogRead` | `Context.Service<Service>` | Service tag. |
|
|
1137
|
+
| `Service` | `listRunIds(options?)`, `listRuns(options?)` | Polling IDs or a coherent filtered execution page. |
|
|
1138
|
+
| `make` | `() => Effect<Service, never, SqlClient>` | Builds it over `flows_runs`. |
|
|
1139
|
+
| `layer` | `Layer<RunCatalogRead, never, SqlClient>` | Provides it. |
|
|
1140
|
+
| `ListOptions` | `{ limit? }` | Defaults to `defaultLimit`. |
|
|
1141
|
+
| `defaultLimit` | `10000` | |
|
|
1142
|
+
| `Filters` | Schema and type for status, flow, effective parent, lineage/root, waiting reason, and inclusive creation range. | All predicates run in SQL before pagination. |
|
|
1143
|
+
| `ListRunsOptions`, `RunPage` | `{ filters?, cursor?, limit? }`, page results and source watermark | Versioned source-bound keyset continuation. |
|
|
1144
|
+
| `maximumPageSize` | `200` | `listRuns` defaults to 100, without a total count. |
|
|
1145
|
+
| `RunCatalogError` | `invalid_options`, `invalid_cursor`, `source_changed`, `list_failed` | Typed failures preserve causes. |
|
|
1146
|
+
|
|
1147
|
+
`listRunIds` reads a set rather than a cursor tail, so retention of a run removes it from
|
|
1148
|
+
a follower's view. Rows come back newest first by rowid and are returned oldest
|
|
1149
|
+
first. Nothing here polls: the interval belongs to `RunCatalog.makePolling`.
|
|
1150
|
+
|
|
1151
|
+
`listRuns` orders by immutable creation time plus run ID. It decodes only the
|
|
1152
|
+
selected page plus one lookahead, with indexed ancillary reads scoped to those
|
|
1153
|
+
rows. Numeric offsets are rejected. Successive pages use live keyset semantics;
|
|
1154
|
+
each response is coherent at its own engine revision. Control-only admissions
|
|
1155
|
+
and the control projection are a separate integration.
|
|
1156
|
+
|
|
1157
|
+
Full observation, listing, lag, retention, and cursor contract:
|
|
1158
|
+
[Observe executions and page runs](./guides/observe-executions.md).
|
|
1159
|
+
|
|
1160
|
+
## DisasterRecovery
|
|
1161
|
+
|
|
1162
|
+
[src/DisasterRecovery.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/DisasterRecovery.ts)
|
|
1163
|
+
|
|
1164
|
+
Hot backup, verification, restore, and restore-time fencing.
|
|
1165
|
+
|
|
1166
|
+
| Export | Signature |
|
|
1167
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
1168
|
+
| `backup` | `(options: BackupOptions<R, E>) => Effect<BackupManifest, DisasterRecoveryError \| E, SqlClient \| FileSystem \| Crypto \| R>` |
|
|
1169
|
+
| `verify` | `(backupDirectory: string, options?: FileSizeOptions) => Effect<BackupManifest, DisasterRecoveryError, FileSystem \| Crypto>` |
|
|
1170
|
+
| `restore` | `(options: RestoreOptions) => Effect<RestoredStore, DisasterRecoveryError, FileSystem \| Crypto>` |
|
|
1171
|
+
| `fence` | `(manifest: BackupManifest) => Effect<FenceSummary, DisasterRecoveryError, SqlClient \| DurableWriter>` |
|
|
1172
|
+
| `restoreAndFence` | `(options: RestoreAndFenceOptions<R, E>) => Effect<FencedRestoredStore, DisasterRecoveryError \| E, FileSystem \| Crypto \| R>` |
|
|
1173
|
+
|
|
1174
|
+
| Constant | Value |
|
|
1175
|
+
| ------------------------- | ---------------------------------------------------------------------- |
|
|
1176
|
+
| `databaseFileName` | `store.sqlite3` |
|
|
1177
|
+
| `manifestFileName` | `manifest.json`, written last, so its presence marks a complete backup |
|
|
1178
|
+
| `objectsDirectoryName` | `objects` |
|
|
1179
|
+
| `restoredMarkerFileName` | `restored.json` |
|
|
1180
|
+
| `defaultMaxFileSizeBytes` | 512 MiB |
|
|
1181
|
+
|
|
1182
|
+
| Type | Fields |
|
|
1183
|
+
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1184
|
+
| `FileSizeOptions` | `maxFileSizeBytes?`, a finite non-negative safe integer |
|
|
1185
|
+
| `BackupOptions<R, E>` | `directory`, `objectsDirectory?`, `snapshotDatabaseLayer`, plus `FileSizeOptions` |
|
|
1186
|
+
| `RestoreOptions` | `backupDirectory`, `targetDirectory`, plus `FileSizeOptions` |
|
|
1187
|
+
| `RestoreAndFenceOptions<R, E>` | `RestoreOptions` plus `databaseLayer` |
|
|
1188
|
+
| `RestoredStore` | `{ databaseFile, objectsDirectory, manifest }` |
|
|
1189
|
+
| `FencedRestoredStore` | `RestoredStore` plus `fence: FenceSummary` |
|
|
1190
|
+
| `FenceSummary` | `{ clearedClaims, suspendedRuns }` |
|
|
1191
|
+
| `BackupManifest` | `{ formatVersion: 1, createdAtMs, database: { file, sha256, sizeBytes, migrations }, artifacts }` |
|
|
1192
|
+
| `AppliedMigration` | `{ migrationId, name }` |
|
|
1193
|
+
| `ArtifactEntry` | `{ digest, sizeBytes }` |
|
|
1194
|
+
| `DisasterRecoveryError` | `code` of `invalid_options`, `not_empty`, `invalid_manifest`, `missing_file`, `digest_mismatch`, `artifact_corruption`, `snapshot_incomplete`, `schema_mismatch`, `io`, or `sql`, plus the `method` that raised it |
|
|
1195
|
+
|
|
1196
|
+
The database is snapshotted with `VACUUM INTO`, a read transaction under WAL, so
|
|
1197
|
+
live writers are never blocked. The artifact walk runs after it, because results
|
|
1198
|
+
reference artifacts only after publication. With an `objectsDirectory` the whole
|
|
1199
|
+
capture holds the cross-process artifact backup lease; without one no lease is
|
|
1200
|
+
taken, and a frozen snapshot that references any artifact digest fails with
|
|
1201
|
+
`snapshot_incomplete` instead of reporting success.
|
|
1202
|
+
|
|
1203
|
+
Host access arrives through Effect's `FileSystem` and `SqlClient` tags and
|
|
1204
|
+
hashing through the injected `Crypto` service, so the module carries no platform
|
|
1205
|
+
binding. The SQL dialect is SQLite; a non-SQLite backend fails with the `sql`
|
|
1206
|
+
code rather than pretending to snapshot.
|
|
1207
|
+
|
|
1208
|
+
Full guide: [Back up and restore the store](./guides/back-up-and-restore.md).
|
|
1209
|
+
|
|
1210
|
+
## Inconsistency
|
|
1211
|
+
|
|
1212
|
+
[src/Inconsistency.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/Inconsistency.ts)
|
|
1213
|
+
|
|
1214
|
+
The receiver for cache conflicts and corrupt evidence.
|
|
1215
|
+
|
|
1216
|
+
```ts
|
|
1217
|
+
interface Service {
|
|
1218
|
+
readonly note: (event: CacheConflict) => Effect<InconsistencyVerdict, JournalError>
|
|
1219
|
+
readonly noteCorruption: (event: BlobCorruption) => Effect<InconsistencyVerdict, JournalError>
|
|
1220
|
+
}
|
|
1221
|
+
```
|
|
1222
|
+
|
|
1223
|
+
| Export | Signature | Meaning |
|
|
1224
|
+
| --------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
1225
|
+
| `Inconsistency` | `Context.Service<Service>` | Service tag. |
|
|
1226
|
+
| `make` | `(options: MakeOptions) => Service` | Journals every conflict under the run that attempted the write and returns a fixed verdict. |
|
|
1227
|
+
| `makeNoop` | `(overrides?: Partial<Service>) => Service` | Notes nothing and tolerates. |
|
|
1228
|
+
| `layerNoop` | `(overrides?: Partial<Service>) => Layer<Inconsistency>` | Provides it. |
|
|
1229
|
+
| `layerStrict` | `(owner: Ownership.OwnerId) => Layer<Inconsistency, never, Journal>` | Journals and fails. The default for engine wiring. |
|
|
1230
|
+
| `layerTolerant` | `(owner: Ownership.OwnerId) => Layer<Inconsistency, never, Journal>` | Journals and continues, preserving the first-recorded row. |
|
|
1231
|
+
|
|
1232
|
+
| Type | Fields |
|
|
1233
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
1234
|
+
| `InconsistencyVerdict` | `"fail"` or `"tolerate"` |
|
|
1235
|
+
| `MakeOptions` | `{ journal, verdict, owner }`. The owner is required: every composer of this receiver is a run's owner. |
|
|
1236
|
+
| `CacheConflict` | `{ key, existing, attempted }` |
|
|
1237
|
+
| `BlobCorruption` | `{ runId, keyDigest, path, recordedDigest, measuredDigest, recordedRunId?, recordedEventSeq? }` |
|
|
1238
|
+
|
|
1239
|
+
The record goes through the journal's durable channel, so a `tolerate` verdict
|
|
1240
|
+
cannot silently drop its only record. For `noteCorruption`, `"fail"` fails the
|
|
1241
|
+
dispatch and `"tolerate"` lets it fall back to a real execution, which
|
|
1242
|
+
re-captures and heals the corrupt address.
|
|
1243
|
+
|
|
1244
|
+
## ReplayOnly
|
|
1245
|
+
|
|
1246
|
+
[src/ReplayOnly.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/ReplayOnly.ts)
|
|
1247
|
+
|
|
1248
|
+
An engine composed with this service replays recorded work and executes none.
|
|
1249
|
+
`smthrs runs verify` drives a copy of a run's store with it to report which
|
|
1250
|
+
recorded steps the current code would replay and which step it would execute
|
|
1251
|
+
again.
|
|
1252
|
+
|
|
1253
|
+
```ts
|
|
1254
|
+
interface Service {
|
|
1255
|
+
readonly observe: (dispatch: Dispatch) => Effect<void>
|
|
1256
|
+
}
|
|
1257
|
+
```
|
|
1258
|
+
|
|
1259
|
+
| Export | Signature | Meaning |
|
|
1260
|
+
| -------------- | ---------------------------------------------------------- | ------------------------------------------------------------ |
|
|
1261
|
+
| `ReplayOnly` | `Context.Service<Service>` | Service tag, resolved when the engine is composed. |
|
|
1262
|
+
| `layer` | `(observe: Service["observe"]) => Layer<ReplayOnly>` | Provides it. |
|
|
1263
|
+
| `WouldExecute` | `TaggedError<{ runId, stepKeyDigest, attempt, action }>` | The defect a dispatch dies with instead of running a body. |
|
|
1264
|
+
| `Dispatch` | `{ runId, stepKeyDigest, attempt, action, tier, outcome }` | `outcome` is `"replayed"`, `"resumes"` or `"would-execute"`. |
|
|
1265
|
+
|
|
1266
|
+
A dispatch served from a durable record, a recorded failure included, reports
|
|
1267
|
+
`replayed`. The first dispatch that would run a body dies before its attempt
|
|
1268
|
+
row, snapshot, boundary, or body: it reports `resumes` when it would re-enter a
|
|
1269
|
+
recorded attempt that never finished, such as a step parked inside its body,
|
|
1270
|
+
and `would-execute` when no record matches its key. The defect is never
|
|
1271
|
+
retried, so the run settles `failed` instead of spinning in its retry policy.
|
|
1272
|
+
Replays still write the journal rows a resume converges on, so drive a copy of
|
|
1273
|
+
the store, never the original.
|
|
1274
|
+
|
|
1275
|
+
## OwnerIdentity
|
|
1276
|
+
|
|
1277
|
+
[src/OwnerIdentity.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/OwnerIdentity.ts)
|
|
1278
|
+
|
|
1279
|
+
Mints the `OwnerId` an incarnation fences its writes with.
|
|
1280
|
+
|
|
1281
|
+
| Export | Signature | Meaning |
|
|
1282
|
+
| ----------------- | ------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
1283
|
+
| `OwnerIdentity` | `Context.Service<Service>` | Service tag. |
|
|
1284
|
+
| `Service` | `{ ownerId: (hostId: string) => Effect<OwnerId> }` | The host supplies the incarnation; the caller supplies the host id. |
|
|
1285
|
+
| `make` | `(service: Service) => Service` | Brands an implementation. |
|
|
1286
|
+
| `makeIncarnation` | `(pid: number \| undefined, crypto: Crypto) => Service` | The standard source over an explicitly supplied process id. |
|
|
1287
|
+
| `layer` | `Layer<OwnerIdentity, never, Crypto>` | The platform default. |
|
|
1288
|
+
| `layerConstant` | `(owner: OwnerId) => Layer<OwnerIdentity>` | Pins the whole token. |
|
|
1289
|
+
|
|
1290
|
+
The process id is read off `globalThis` rather than through a bare `process`
|
|
1291
|
+
reference, so the module carries no Node binding: a browser bundle sees
|
|
1292
|
+
`undefined` and draws an incarnation number from `Random` instead. The read
|
|
1293
|
+
happens at layer construction, so importing the module touches nothing. The
|
|
1294
|
+
nonce is a UUIDv4 from the injected `Crypto` service.
|
|
1295
|
+
|
|
1296
|
+
## WakeBus
|
|
1297
|
+
|
|
1298
|
+
[src/WakeBus.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/WakeBus.ts)
|
|
1299
|
+
|
|
1300
|
+
Edge-triggered in-process wakes, with durable polling as the miss-tolerant
|
|
1301
|
+
fallback.
|
|
1302
|
+
|
|
1303
|
+
| Export | Signature | Meaning |
|
|
1304
|
+
| ------------ | -------------------------------------------------- | ------------------------------------------- |
|
|
1305
|
+
| `WakeBus` | `Context.Service<Service>` | Service tag. |
|
|
1306
|
+
| `Service` | `{ wake, awaitWake, waiters }` | See below. |
|
|
1307
|
+
| `makeUnsafe` | `() => Service` | Constructs a bus. Nothing is durable. |
|
|
1308
|
+
| `make` | `Effect<Service>` | The same, as an effect. |
|
|
1309
|
+
| `layer` | `Layer<WakeBus>` | Provides a fresh bus. |
|
|
1310
|
+
| `makeNoop` | `(overrides?: Partial<Service>) => Service` | Wakes are dropped and waiters park forever. |
|
|
1311
|
+
| `layerNoop` | `(overrides?: Partial<Service>) => Layer<WakeBus>` | Provides it. |
|
|
1312
|
+
|
|
1313
|
+
`wake(executionId)` resumes every waiter parked on that execution; with no
|
|
1314
|
+
waiters the wake is dropped. `awaitWake(executionId)` parks until the next one,
|
|
1315
|
+
and registration is removed when the waiting fiber is interrupted, so an
|
|
1316
|
+
abandoned wait leaks nothing. The wait is keyed by execution id alone, because
|
|
1317
|
+
execution ids are unique across flows. `waiters(executionId)` is observability
|
|
1318
|
+
for tests and diagnostics, not a coordination primitive.
|
|
1319
|
+
|
|
1320
|
+
An engine composition resolves the bus optionally, so providing `layer` is how a
|
|
1321
|
+
host shares one bus between the engine and its own wake sources. Cross-process
|
|
1322
|
+
delivery stays store-driven.
|
|
1323
|
+
|
|
1324
|
+
## EngineStoreMetrics
|
|
1325
|
+
|
|
1326
|
+
[src/EngineStoreMetrics.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/EngineStoreMetrics.ts)
|
|
1327
|
+
|
|
1328
|
+
Metric handles and observation combinators for the engine hot paths. Exporters
|
|
1329
|
+
remain host-owned.
|
|
1330
|
+
|
|
1331
|
+
| Export | Kind |
|
|
1332
|
+
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
1333
|
+
| `ExitTag` | `"Success" \| "Failure" \| "Interrupt"` |
|
|
1334
|
+
| `exitTag` | `<A, E>(exit: Exit<A, E>) => ExitTag` |
|
|
1335
|
+
| `observe` | `(instruments: { timer, counter }) => <A, E, R>(effect: Effect<A, E, R>) => Effect<A, E, R>` |
|
|
1336
|
+
| `dispatches`, `dispatchDuration`, `dispatch` | counter, timer, and views keyed by `ExitTag` |
|
|
1337
|
+
| `schedulerAdmissions`, `schedulerDispatchDuration` | counter and timer |
|
|
1338
|
+
| `schedulerNodes`, `node` | counter and views keyed by `PlanScheduler.Outcome` |
|
|
1339
|
+
| `sandboxExecutions`, `sandboxExecutionDuration`, `sandboxExecution` | counter, timer, and views keyed by `ExitTag` |
|
|
1340
|
+
| `sandboxMaterializations`, `materializationDuration`, `materialization` | counter, timer, and views keyed by `ExitTag` |
|
|
1341
|
+
| `materializationConflicts` | counter |
|
|
1342
|
+
| `boundarySettlements`, `boundarySettlement` | counter and views keyed by `Clean`, `Deviation`, `Violation`, `Refused` |
|
|
1343
|
+
| `stepCacheDecisions`, `stepCacheDecision` | counter and views keyed by `VerifiedHit`, `Miss`, `UnverifiableEvidence`, `Unmeasurable`, `StaleReadSet`, `ReplayFailed` |
|
|
1344
|
+
| `claims`, `claim` | counter and views keyed by `Activated`, `Terminal`, `HeartbeatFresh`, `StealRefusedOwnerAlive`, `ClaimLost`, `ActivationLost` |
|
|
1345
|
+
|
|
1346
|
+
Outcome tags are rewritten as snake case attribute values, so `HeartbeatFresh`
|
|
1347
|
+
is recorded as `heartbeat_fresh`. `observe` composes `Effect.trackDuration` with
|
|
1348
|
+
an exit counter update and a span annotation, and propagates the instrumented
|
|
1349
|
+
effect's exit byte-identically.
|
|
1350
|
+
|
|
1351
|
+
Full guide: [Observe engine metrics](./guides/observe-engine-metrics.md).
|
|
1352
|
+
|
|
1353
|
+
## Migrations
|
|
1354
|
+
|
|
1355
|
+
[src/Migrations.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/Migrations.ts)
|
|
1356
|
+
|
|
1357
|
+
| Export | Signature | Meaning |
|
|
1358
|
+
| ------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
1359
|
+
| `set` | `MigrationSet` | This package's own set, namespace `engine-store`, migration id block `3000`. |
|
|
1360
|
+
| `sets` | `ReadonlyArray<MigrationSet>` | The complete durable engine schema in dependency order: journal, run store, step cache, this package, then the plan store. |
|
|
1361
|
+
| `run` | the migration effect | |
|
|
1362
|
+
| `layer` | `Layer` | Installs the complete schema before exposing the database to any durable service. |
|
|
1363
|
+
|
|
1364
|
+
This package's own set creates deferred/clock state, selection beliefs, and the
|
|
1365
|
+
`flows_plan_input_heads`, `flows_plan_input_generations`, and
|
|
1366
|
+
`flows_plan_input_legacy_runs` recovery tables. The plan set's block (`4000`)
|
|
1367
|
+
comes last. The database loader supports forward additions to an already
|
|
1368
|
+
installed lower block, so migrations `3003` and `3004` also run on existing installations
|
|
1369
|
+
whose global cursor reached `4003`; earlier holes remain refusals. See
|
|
1370
|
+
[`@smthrs/database`](/api/database) for how namespaced sets compose without
|
|
1371
|
+
colliding.
|
|
1372
|
+
|
|
1373
|
+
## RunState
|
|
1374
|
+
|
|
1375
|
+
[src/RunState.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/RunState.ts)
|
|
1376
|
+
|
|
1377
|
+
The versioned state envelope stored in each run row.
|
|
1378
|
+
|
|
1379
|
+
| Export | Shape |
|
|
1380
|
+
| -------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
1381
|
+
| `RunState` | `{ version: 1, flowName, payload, parentExecutionId?, onParentExit?, maxRounds?, result?, cancellation? }` |
|
|
1382
|
+
| `OnParentExit` | `"cancel"` or `"detach"` |
|
|
1383
|
+
|
|
1384
|
+
`ReleaseCause` is why a driver released a run it still owned, recorded as the
|
|
1385
|
+
optional `cause` of its `interrupt-released` run decision:
|
|
1386
|
+
`{ kind: "lease-lapsed", unconfirmedMs }` when the host went that long without
|
|
1387
|
+
confirming its run lease, or `{ kind: "interrupted" }` for any other
|
|
1388
|
+
non-cancel interruption, usually a host shutdown. `releaseCauseOf(decision)`
|
|
1389
|
+
reads it and answers `undefined` for decisions recorded before the field
|
|
1390
|
+
existed.
|
|
1391
|
+
|
|
1392
|
+
`parentExecutionId` is present only on a child run. `onParentExit` is recorded
|
|
1393
|
+
at first admission, including independent runs that a later caller may join as
|
|
1394
|
+
a child: the parent's terminal transition reads it to decide whether the child
|
|
1395
|
+
ends with it. The policy is recorded on the child rather than on the edge,
|
|
1396
|
+
because it is a property of how the child was started and because a child with
|
|
1397
|
+
two parents must answer the question once. Missing policies on legacy rows
|
|
1398
|
+
default to `"cancel"`. A later caller awaiting or ensuring the run does not
|
|
1399
|
+
rewrite its recorded policy.
|
|
1400
|
+
|
|
1401
|
+
## Errors
|
|
1402
|
+
|
|
1403
|
+
[src/Errors.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/Errors.ts)
|
|
1404
|
+
|
|
1405
|
+
The stable error contract. Every `code` literal here is public API: consumers
|
|
1406
|
+
may switch on `code` or `_tag`, and the strings will not change without a major
|
|
1407
|
+
version.
|
|
1408
|
+
|
|
1409
|
+
| Export | `code` | Fields |
|
|
1410
|
+
| ----------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
1411
|
+
| `AttemptSuspended` | `attempt_suspended` | `runId`, `keyDigest`, `attempt` |
|
|
1412
|
+
| `AttemptAdmissionRejected` | `attempt_admission_rejected` | `keyDigest`, `outcome` |
|
|
1413
|
+
| `AttemptEvidenceQuarantined` | `attempt_evidence_quarantined` | `keyDigest`, `attempt`, `path`, `recordedDigest`, `measuredDigest` |
|
|
1414
|
+
| `CacheConflictDetected` | `cache_conflict_detected` | `keyDigest`, `recordedRunId` |
|
|
1415
|
+
| `CacheCorruptionDetected` | `cache_corruption_detected` | `keyDigest`, `path`, `recordedDigest`, `measuredDigest` |
|
|
1416
|
+
| `RetentionError`, `RetentionErrorCode` | `scan_failed`, `delete_failed` | |
|
|
1417
|
+
| `RunCatalogError`, `RunCatalogErrorCode` | `invalid_options`, `invalid_cursor`, `source_changed`, `list_failed` | `cause` when a decode or store operation failed. |
|
|
1418
|
+
| `IrreversibleRetryRequiresIdempotencyKey` | re-exported from [`@smthrs/flow`](/api/flow)'s `Action` | |
|
|
1419
|
+
| `FlowCycleDetected` | re-exported from [`@smthrs/flow`](/api/flow)'s `FlowRuntime` | |
|
|
1420
|
+
|
|
1421
|
+
Each failure, with its cause and its fix, is in
|
|
1422
|
+
[Troubleshooting](./troubleshooting.md).
|
|
1423
|
+
|
|
1424
|
+
## test/TestStores
|
|
1425
|
+
|
|
1426
|
+
[src/test/TestStores.ts](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/engine-store/src/test/TestStores.ts)
|
|
1427
|
+
|
|
1428
|
+
Every durable engine service over one database, migrated.
|
|
1429
|
+
|
|
1430
|
+
| Export | Signature | Meaning |
|
|
1431
|
+
| ------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1432
|
+
| `layer` | `(options?: TestStoresOptions) => Layer<...>` | The private in-memory bundle: journal, run, attempt, cache, plan, and plan-input stores plus `OwnerIdentity.layer`, keeping `SqlClient` to itself. |
|
|
1433
|
+
| `layerAt` | `(filename: string, options?: TestStoresOptions) => Layer<...>` | The same over a named database, with the connection and `DurableEngineState` re-exported. |
|
|
1434
|
+
| `database` | `Layer` | The migrated in-memory database alone, as `SqlClient` and `DurableWriter`. |
|
|
1435
|
+
| `databaseAt` | `(filename: string) => Layer` | The same over a named file. |
|
|
1436
|
+
| `TestStoresOptions` | `{ capacity?, overflow?, batchSize? }` | Forwarded to the journal. |
|
|
1437
|
+
|
|
1438
|
+
Use `layerAt` with a real file to point two independently constructed bundles at
|
|
1439
|
+
one database: two connections, two engines, no shared object graph, which is
|
|
1440
|
+
what a second process actually has. `:memory:` gives each connection its own
|
|
1441
|
+
private database, so it cannot prove anything durable across compositions.
|
|
1442
|
+
|
|
1443
|
+
Full guide: [Test against a durable store](./guides/testing.md).
|
|
1444
|
+
|
|
1445
|
+
## Cache admission
|
|
1446
|
+
|
|
1447
|
+
The engine admits a cache record only when the action is sealed, the boundary is
|
|
1448
|
+
hard, no deviation occurred, and the evidence explicitly carries
|
|
1449
|
+
`wholeTreeWritesVerified: true`. Older evidence and boundaries that observe
|
|
1450
|
+
declared paths only are conservatively refused. Only a content-key record has an
|
|
1451
|
+
address another run can reproduce; an ordinal-key record remains run-local.
|
|
1452
|
+
|
|
1453
|
+
A cache hit is verified before it is served: the store calls `prepare` and
|
|
1454
|
+
compares the descriptor's declared `readSet` against the `readSnapshot` the host
|
|
1455
|
+
measured. Reuse happens only when every declared read still matches. Reads the
|
|
1456
|
+
host reports but the declaration never claimed are ignored, while a declared
|
|
1457
|
+
path that is missing or has a different digest refuses the hit, journals a
|
|
1458
|
+
`cache-provenance` record with `action: "stale_read_set"`, and falls through to a
|
|
1459
|
+
real execution. That is Skyframe's dirty-check invariant: the key alone detects a
|
|
1460
|
+
changed declaration, not a stale one.
|
|
1461
|
+
|
|
1462
|
+
A verified hit calls `replayOutputs` before returning the stored result. When
|
|
1463
|
+
that refuses with `MissingArtifact`, the dispatch hydrates from the shared tier
|
|
1464
|
+
and retries the replay exactly once before falling through to a real execution.
|
|
1465
|
+
A replay refused for confinement (a symlink on a replayed path, or a host that
|
|
1466
|
+
cannot be confined) journals `replay_failed` and executes the step for real;
|
|
1467
|
+
it never writes through the link.
|
|
1468
|
+
|
|
1469
|
+
Replaying a succeeded attempt row also converges the cache: if a crash landed
|
|
1470
|
+
between `attempts.finish` and `cache.put`, the restarted executor re-records the
|
|
1471
|
+
sealed completion with fresh cache provenance. A divergent first-recorded row
|
|
1472
|
+
surfaces through the [`Inconsistency`](#inconsistency) receiver, strict by
|
|
1473
|
+
default.
|
|
1474
|
+
|
|
1475
|
+
A persisted `failed` attempt row replays by rethrowing the persisted domain
|
|
1476
|
+
failure, never by readmission, so `AttemptAdmissionRejected` marks only
|
|
1477
|
+
genuinely mid-flight rows. The `Fail` errors were schema-encoded before
|
|
1478
|
+
persistence, so their `_tag` survives the JSON round trip and `RetryPolicy`
|
|
1479
|
+
non-retryable matching applies on replay. The composition also implements the
|
|
1480
|
+
engine's `actionLatestAttempt` and degrades `actionRetryOrigin` to the earliest
|
|
1481
|
+
surviving attempt row when a retention job pruned attempt 1.
|
|
1482
|
+
|
|
1483
|
+
Full model: [Cache admission](./concepts/cache-admission.md). See also
|
|
1484
|
+
[Run durably over SQLite](/docs/tutorials/first-flow/#6-run-durably-over-sqlite)
|
|
1485
|
+
and [Content addressing](/docs/concepts/content-addressing/).
|
|
1486
|
+
|
|
1487
|
+
## Durable failure warnings
|
|
1488
|
+
|
|
1489
|
+
If an unsuccessful irreversible action cannot append its `unknown` boundary
|
|
1490
|
+
record, the engine logs at Warning with the append cause, `runId`, and the
|
|
1491
|
+
action key as `step`. The action's original failure or interruption remains
|
|
1492
|
+
the primary outcome. Reading a completed trampoline lineage whose terminal
|
|
1493
|
+
flow is unregistered dies with `FlowEngine.FlowNotRegistered`, carrying the
|
|
1494
|
+
missing `flowName`, so callers can classify the registration defect.
|