@smthrs/control 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 +194 -0
- package/LICENSE +21 -0
- package/README.md +168 -2
- package/dist/cjs/ApprovalAuthority.d.ts +73 -0
- package/dist/cjs/ApprovalAuthority.d.ts.map +1 -0
- package/dist/cjs/ApprovalAuthority.js +62 -0
- package/dist/cjs/ApprovalAuthority.js.map +7 -0
- package/dist/cjs/Cancellation.d.ts +107 -0
- package/dist/cjs/Cancellation.d.ts.map +1 -0
- package/dist/cjs/Cancellation.js +72 -0
- package/dist/cjs/Cancellation.js.map +7 -0
- package/dist/cjs/Channels.d.ts +170 -0
- package/dist/cjs/Channels.d.ts.map +1 -0
- package/dist/cjs/Channels.js +278 -0
- package/dist/cjs/Channels.js.map +7 -0
- package/dist/cjs/Control.d.ts +202 -0
- package/dist/cjs/Control.d.ts.map +1 -0
- package/dist/cjs/Control.js +47 -0
- package/dist/cjs/Control.js.map +7 -0
- package/dist/cjs/ControlClient.d.ts +52 -0
- package/dist/cjs/ControlClient.d.ts.map +1 -0
- package/dist/cjs/ControlClient.js +191 -0
- package/dist/cjs/ControlClient.js.map +7 -0
- package/dist/cjs/ControlError.d.ts +318 -0
- package/dist/cjs/ControlError.d.ts.map +1 -0
- package/dist/cjs/ControlError.js +249 -0
- package/dist/cjs/ControlError.js.map +7 -0
- package/dist/cjs/ControlExecutor.d.ts +372 -0
- package/dist/cjs/ControlExecutor.d.ts.map +1 -0
- package/dist/cjs/ControlExecutor.js +123 -0
- package/dist/cjs/ControlExecutor.js.map +7 -0
- package/dist/cjs/ControlFacts.d.ts +454 -0
- package/dist/cjs/ControlFacts.d.ts.map +1 -0
- package/dist/cjs/ControlFacts.js +261 -0
- package/dist/cjs/ControlFacts.js.map +7 -0
- package/dist/cjs/ControlLive.d.ts +23 -0
- package/dist/cjs/ControlLive.d.ts.map +1 -0
- package/dist/cjs/ControlLive.js +1280 -0
- package/dist/cjs/ControlLive.js.map +7 -0
- package/dist/cjs/ControlRpcs.d.ts +1204 -0
- package/dist/cjs/ControlRpcs.d.ts.map +1 -0
- package/dist/cjs/ControlRpcs.js +247 -0
- package/dist/cjs/ControlRpcs.js.map +7 -0
- package/dist/cjs/ControlRuntime.d.ts +635 -0
- package/dist/cjs/ControlRuntime.d.ts.map +1 -0
- package/dist/cjs/ControlRuntime.js +740 -0
- package/dist/cjs/ControlRuntime.js.map +7 -0
- package/dist/cjs/ControlSchema.d.ts +2642 -0
- package/dist/cjs/ControlSchema.d.ts.map +1 -0
- package/dist/cjs/ControlSchema.js +634 -0
- package/dist/cjs/ControlSchema.js.map +7 -0
- package/dist/cjs/ControlServer.d.ts +51 -0
- package/dist/cjs/ControlServer.d.ts.map +1 -0
- package/dist/cjs/ControlServer.js +121 -0
- package/dist/cjs/ControlServer.js.map +7 -0
- package/dist/cjs/Credential.d.ts +136 -0
- package/dist/cjs/Credential.d.ts.map +1 -0
- package/dist/cjs/Credential.js +168 -0
- package/dist/cjs/Credential.js.map +7 -0
- package/dist/cjs/CredentialCipher.d.ts +90 -0
- package/dist/cjs/CredentialCipher.d.ts.map +1 -0
- package/dist/cjs/CredentialCipher.js +45 -0
- package/dist/cjs/CredentialCipher.js.map +7 -0
- package/dist/cjs/CredentialStore.d.ts +97 -0
- package/dist/cjs/CredentialStore.d.ts.map +1 -0
- package/dist/cjs/CredentialStore.js +81 -0
- package/dist/cjs/CredentialStore.js.map +7 -0
- package/dist/cjs/DispatchReader.d.ts +112 -0
- package/dist/cjs/DispatchReader.d.ts.map +1 -0
- package/dist/cjs/DispatchReader.js +45 -0
- package/dist/cjs/DispatchReader.js.map +7 -0
- package/dist/cjs/Health.d.ts +333 -0
- package/dist/cjs/Health.d.ts.map +1 -0
- package/dist/cjs/Health.js +311 -0
- package/dist/cjs/Health.js.map +7 -0
- package/dist/cjs/JevSessionChecker.d.ts +57 -0
- package/dist/cjs/JevSessionChecker.d.ts.map +1 -0
- package/dist/cjs/JevSessionChecker.js +113 -0
- package/dist/cjs/JevSessionChecker.js.map +7 -0
- package/dist/cjs/Lineage.d.ts +131 -0
- package/dist/cjs/Lineage.d.ts.map +1 -0
- package/dist/cjs/Lineage.js +81 -0
- package/dist/cjs/Lineage.js.map +7 -0
- package/dist/cjs/Migrations.d.ts +34 -0
- package/dist/cjs/Migrations.d.ts.map +1 -0
- package/dist/cjs/Migrations.js +60 -0
- package/dist/cjs/Migrations.js.map +7 -0
- package/dist/cjs/Monitor.d.ts +282 -0
- package/dist/cjs/Monitor.d.ts.map +1 -0
- package/dist/cjs/Monitor.js +283 -0
- package/dist/cjs/Monitor.js.map +7 -0
- package/dist/cjs/ScopedToken.d.ts +193 -0
- package/dist/cjs/ScopedToken.d.ts.map +1 -0
- package/dist/cjs/ScopedToken.js +135 -0
- package/dist/cjs/ScopedToken.js.map +7 -0
- package/dist/cjs/SqlControlRuntime.d.ts +161 -0
- package/dist/cjs/SqlControlRuntime.d.ts.map +1 -0
- package/dist/cjs/SqlControlRuntime.js +1521 -0
- package/dist/cjs/SqlControlRuntime.js.map +7 -0
- package/dist/cjs/SqlCredentialStore.d.ts +43 -0
- package/dist/cjs/SqlCredentialStore.d.ts.map +1 -0
- package/dist/cjs/SqlCredentialStore.js +113 -0
- package/dist/cjs/SqlCredentialStore.js.map +7 -0
- package/dist/cjs/Steering.d.ts +69 -0
- package/dist/cjs/Steering.d.ts.map +1 -0
- package/dist/cjs/Steering.js +49 -0
- package/dist/cjs/Steering.js.map +7 -0
- package/dist/cjs/SystemFlows.d.ts +223 -0
- package/dist/cjs/SystemFlows.d.ts.map +1 -0
- package/dist/cjs/SystemFlows.js +195 -0
- package/dist/cjs/SystemFlows.js.map +7 -0
- package/dist/cjs/WebCryptoCipher.d.ts +49 -0
- package/dist/cjs/WebCryptoCipher.d.ts.map +1 -0
- package/dist/cjs/WebCryptoCipher.js +129 -0
- package/dist/cjs/WebCryptoCipher.js.map +7 -0
- package/dist/cjs/WebhookChannel.d.ts +113 -0
- package/dist/cjs/WebhookChannel.d.ts.map +1 -0
- package/dist/cjs/WebhookChannel.js +98 -0
- package/dist/cjs/WebhookChannel.js.map +7 -0
- package/dist/cjs/index.d.ts +160 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +91 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/MutationBoundary.d.ts +27 -0
- package/dist/cjs/internal/MutationBoundary.d.ts.map +1 -0
- package/dist/cjs/internal/MutationBoundary.js +50 -0
- package/dist/cjs/internal/MutationBoundary.js.map +7 -0
- package/dist/cjs/internal/activeFibers.d.ts +12 -0
- package/dist/cjs/internal/activeFibers.d.ts.map +1 -0
- package/dist/cjs/internal/activeFibers.js +30 -0
- package/dist/cjs/internal/activeFibers.js.map +7 -0
- package/dist/cjs/internal/issues.d.ts +28 -0
- package/dist/cjs/internal/issues.d.ts.map +1 -0
- package/dist/cjs/internal/issues.js +34 -0
- package/dist/cjs/internal/issues.js.map +7 -0
- package/dist/cjs/internal/planning.d.ts +347 -0
- package/dist/cjs/internal/planning.d.ts.map +1 -0
- package/dist/cjs/internal/planning.js +137 -0
- package/dist/cjs/internal/planning.js.map +7 -0
- package/dist/cjs/internal/sqlSchemaErrors.d.ts +18 -0
- package/dist/cjs/internal/sqlSchemaErrors.d.ts.map +1 -0
- package/dist/cjs/internal/sqlSchemaErrors.js +40 -0
- package/dist/cjs/internal/sqlSchemaErrors.js.map +7 -0
- package/dist/cjs/migrations/0001_control_tables.d.ts +18 -0
- package/dist/cjs/migrations/0001_control_tables.d.ts.map +1 -0
- package/dist/cjs/migrations/0001_control_tables.js +115 -0
- package/dist/cjs/migrations/0001_control_tables.js.map +7 -0
- package/dist/cjs/migrations/0002_run_keys.d.ts +15 -0
- package/dist/cjs/migrations/0002_run_keys.d.ts.map +1 -0
- package/dist/cjs/migrations/0002_run_keys.js +44 -0
- package/dist/cjs/migrations/0002_run_keys.js.map +7 -0
- package/dist/cjs/migrations/0003_signal_commands.d.ts +13 -0
- package/dist/cjs/migrations/0003_signal_commands.d.ts.map +1 -0
- package/dist/cjs/migrations/0003_signal_commands.js +50 -0
- package/dist/cjs/migrations/0003_signal_commands.js.map +7 -0
- package/dist/cjs/migrations/0004_approval_decisions.d.ts +13 -0
- package/dist/cjs/migrations/0004_approval_decisions.d.ts.map +1 -0
- package/dist/cjs/migrations/0004_approval_decisions.js +45 -0
- package/dist/cjs/migrations/0004_approval_decisions.js.map +7 -0
- package/dist/cjs/migrations/0005_signal_principals.d.ts +18 -0
- package/dist/cjs/migrations/0005_signal_principals.d.ts.map +1 -0
- package/dist/cjs/migrations/0005_signal_principals.js +45 -0
- package/dist/cjs/migrations/0005_signal_principals.js.map +7 -0
- package/dist/cjs/migrations/0006_run_principals.d.ts +18 -0
- package/dist/cjs/migrations/0006_run_principals.d.ts.map +1 -0
- package/dist/cjs/migrations/0006_run_principals.js +49 -0
- package/dist/cjs/migrations/0006_run_principals.js.map +7 -0
- package/dist/cjs/migrations/0007_resume_consent.d.ts +18 -0
- package/dist/cjs/migrations/0007_resume_consent.d.ts.map +1 -0
- package/dist/cjs/migrations/0007_resume_consent.js +46 -0
- package/dist/cjs/migrations/0007_resume_consent.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/TestControl.d.ts +19 -0
- package/dist/cjs/test/TestControl.d.ts.map +1 -0
- package/dist/cjs/test/TestControl.js +62 -0
- package/dist/cjs/test/TestControl.js.map +7 -0
- package/dist/esm/ApprovalAuthority.d.ts +73 -0
- package/dist/esm/ApprovalAuthority.d.ts.map +1 -0
- package/dist/esm/ApprovalAuthority.js +72 -0
- package/dist/esm/ApprovalAuthority.js.map +1 -0
- package/dist/esm/Cancellation.d.ts +107 -0
- package/dist/esm/Cancellation.d.ts.map +1 -0
- package/dist/esm/Cancellation.js +116 -0
- package/dist/esm/Cancellation.js.map +1 -0
- package/dist/esm/Channels.d.ts +170 -0
- package/dist/esm/Channels.d.ts.map +1 -0
- package/dist/esm/Channels.js +312 -0
- package/dist/esm/Channels.js.map +1 -0
- package/dist/esm/Control.d.ts +202 -0
- package/dist/esm/Control.d.ts.map +1 -0
- package/dist/esm/Control.js +42 -0
- package/dist/esm/Control.js.map +1 -0
- package/dist/esm/ControlClient.d.ts +52 -0
- package/dist/esm/ControlClient.d.ts.map +1 -0
- package/dist/esm/ControlClient.js +217 -0
- package/dist/esm/ControlClient.js.map +1 -0
- package/dist/esm/ControlError.d.ts +318 -0
- package/dist/esm/ControlError.d.ts.map +1 -0
- package/dist/esm/ControlError.js +359 -0
- package/dist/esm/ControlError.js.map +1 -0
- package/dist/esm/ControlExecutor.d.ts +372 -0
- package/dist/esm/ControlExecutor.d.ts.map +1 -0
- package/dist/esm/ControlExecutor.js +212 -0
- package/dist/esm/ControlExecutor.js.map +1 -0
- package/dist/esm/ControlFacts.d.ts +454 -0
- package/dist/esm/ControlFacts.d.ts.map +1 -0
- package/dist/esm/ControlFacts.js +324 -0
- package/dist/esm/ControlFacts.js.map +1 -0
- package/dist/esm/ControlLive.d.ts +23 -0
- package/dist/esm/ControlLive.d.ts.map +1 -0
- package/dist/esm/ControlLive.js +1585 -0
- package/dist/esm/ControlLive.js.map +1 -0
- package/dist/esm/ControlRpcs.d.ts +1204 -0
- package/dist/esm/ControlRpcs.d.ts.map +1 -0
- package/dist/esm/ControlRpcs.js +299 -0
- package/dist/esm/ControlRpcs.js.map +1 -0
- package/dist/esm/ControlRuntime.d.ts +635 -0
- package/dist/esm/ControlRuntime.d.ts.map +1 -0
- package/dist/esm/ControlRuntime.js +807 -0
- package/dist/esm/ControlRuntime.js.map +1 -0
- package/dist/esm/ControlSchema.d.ts +2642 -0
- package/dist/esm/ControlSchema.d.ts.map +1 -0
- package/dist/esm/ControlSchema.js +1030 -0
- package/dist/esm/ControlSchema.js.map +1 -0
- package/dist/esm/ControlServer.d.ts +51 -0
- package/dist/esm/ControlServer.d.ts.map +1 -0
- package/dist/esm/ControlServer.js +145 -0
- package/dist/esm/ControlServer.js.map +1 -0
- package/dist/esm/Credential.d.ts +136 -0
- package/dist/esm/Credential.d.ts.map +1 -0
- package/dist/esm/Credential.js +190 -0
- package/dist/esm/Credential.js.map +1 -0
- package/dist/esm/CredentialCipher.d.ts +90 -0
- package/dist/esm/CredentialCipher.d.ts.map +1 -0
- package/dist/esm/CredentialCipher.js +56 -0
- package/dist/esm/CredentialCipher.js.map +1 -0
- package/dist/esm/CredentialStore.d.ts +97 -0
- package/dist/esm/CredentialStore.d.ts.map +1 -0
- package/dist/esm/CredentialStore.js +101 -0
- package/dist/esm/CredentialStore.js.map +1 -0
- package/dist/esm/DispatchReader.d.ts +112 -0
- package/dist/esm/DispatchReader.d.ts.map +1 -0
- package/dist/esm/DispatchReader.js +76 -0
- package/dist/esm/DispatchReader.js.map +1 -0
- package/dist/esm/Health.d.ts +333 -0
- package/dist/esm/Health.d.ts.map +1 -0
- package/dist/esm/Health.js +400 -0
- package/dist/esm/Health.js.map +1 -0
- package/dist/esm/JevSessionChecker.d.ts +57 -0
- package/dist/esm/JevSessionChecker.d.ts.map +1 -0
- package/dist/esm/JevSessionChecker.js +108 -0
- package/dist/esm/JevSessionChecker.js.map +1 -0
- package/dist/esm/Lineage.d.ts +131 -0
- package/dist/esm/Lineage.d.ts.map +1 -0
- package/dist/esm/Lineage.js +174 -0
- package/dist/esm/Lineage.js.map +1 -0
- package/dist/esm/Migrations.d.ts +34 -0
- package/dist/esm/Migrations.d.ts.map +1 -0
- package/dist/esm/Migrations.js +53 -0
- package/dist/esm/Migrations.js.map +1 -0
- package/dist/esm/Monitor.d.ts +282 -0
- package/dist/esm/Monitor.d.ts.map +1 -0
- package/dist/esm/Monitor.js +415 -0
- package/dist/esm/Monitor.js.map +1 -0
- package/dist/esm/ScopedToken.d.ts +193 -0
- package/dist/esm/ScopedToken.d.ts.map +1 -0
- package/dist/esm/ScopedToken.js +224 -0
- package/dist/esm/ScopedToken.js.map +1 -0
- package/dist/esm/SqlControlRuntime.d.ts +161 -0
- package/dist/esm/SqlControlRuntime.d.ts.map +1 -0
- package/dist/esm/SqlControlRuntime.js +1756 -0
- package/dist/esm/SqlControlRuntime.js.map +1 -0
- package/dist/esm/SqlCredentialStore.d.ts +43 -0
- package/dist/esm/SqlCredentialStore.d.ts.map +1 -0
- package/dist/esm/SqlCredentialStore.js +97 -0
- package/dist/esm/SqlCredentialStore.js.map +1 -0
- package/dist/esm/Steering.d.ts +69 -0
- package/dist/esm/Steering.d.ts.map +1 -0
- package/dist/esm/Steering.js +89 -0
- package/dist/esm/Steering.js.map +1 -0
- package/dist/esm/SystemFlows.d.ts +223 -0
- package/dist/esm/SystemFlows.d.ts.map +1 -0
- package/dist/esm/SystemFlows.js +198 -0
- package/dist/esm/SystemFlows.js.map +1 -0
- package/dist/esm/WebCryptoCipher.d.ts +49 -0
- package/dist/esm/WebCryptoCipher.d.ts.map +1 -0
- package/dist/esm/WebCryptoCipher.js +123 -0
- package/dist/esm/WebCryptoCipher.js.map +1 -0
- package/dist/esm/WebhookChannel.d.ts +113 -0
- package/dist/esm/WebhookChannel.d.ts.map +1 -0
- package/dist/esm/WebhookChannel.js +109 -0
- package/dist/esm/WebhookChannel.js.map +1 -0
- package/dist/esm/index.d.ts +160 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +160 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/MutationBoundary.d.ts +27 -0
- package/dist/esm/internal/MutationBoundary.d.ts.map +1 -0
- package/dist/esm/internal/MutationBoundary.js +40 -0
- package/dist/esm/internal/MutationBoundary.js.map +1 -0
- package/dist/esm/internal/activeFibers.d.ts +12 -0
- package/dist/esm/internal/activeFibers.d.ts.map +1 -0
- package/dist/esm/internal/activeFibers.js +17 -0
- package/dist/esm/internal/activeFibers.js.map +1 -0
- package/dist/esm/internal/issues.d.ts +28 -0
- package/dist/esm/internal/issues.d.ts.map +1 -0
- package/dist/esm/internal/issues.js +35 -0
- package/dist/esm/internal/issues.js.map +1 -0
- package/dist/esm/internal/planning.d.ts +347 -0
- package/dist/esm/internal/planning.d.ts.map +1 -0
- package/dist/esm/internal/planning.js +199 -0
- package/dist/esm/internal/planning.js.map +1 -0
- package/dist/esm/internal/sqlSchemaErrors.d.ts +18 -0
- package/dist/esm/internal/sqlSchemaErrors.d.ts.map +1 -0
- package/dist/esm/internal/sqlSchemaErrors.js +38 -0
- package/dist/esm/internal/sqlSchemaErrors.js.map +1 -0
- package/dist/esm/migrations/0001_control_tables.d.ts +18 -0
- package/dist/esm/migrations/0001_control_tables.d.ts.map +1 -0
- package/dist/esm/migrations/0001_control_tables.js +96 -0
- package/dist/esm/migrations/0001_control_tables.js.map +1 -0
- package/dist/esm/migrations/0002_run_keys.d.ts +15 -0
- package/dist/esm/migrations/0002_run_keys.d.ts.map +1 -0
- package/dist/esm/migrations/0002_run_keys.js +22 -0
- package/dist/esm/migrations/0002_run_keys.js.map +1 -0
- package/dist/esm/migrations/0003_signal_commands.d.ts +13 -0
- package/dist/esm/migrations/0003_signal_commands.d.ts.map +1 -0
- package/dist/esm/migrations/0003_signal_commands.js +26 -0
- package/dist/esm/migrations/0003_signal_commands.js.map +1 -0
- package/dist/esm/migrations/0004_approval_decisions.d.ts +13 -0
- package/dist/esm/migrations/0004_approval_decisions.d.ts.map +1 -0
- package/dist/esm/migrations/0004_approval_decisions.js +25 -0
- package/dist/esm/migrations/0004_approval_decisions.js.map +1 -0
- package/dist/esm/migrations/0005_signal_principals.d.ts +18 -0
- package/dist/esm/migrations/0005_signal_principals.d.ts.map +1 -0
- package/dist/esm/migrations/0005_signal_principals.js +27 -0
- package/dist/esm/migrations/0005_signal_principals.js.map +1 -0
- package/dist/esm/migrations/0006_run_principals.d.ts +18 -0
- package/dist/esm/migrations/0006_run_principals.d.ts.map +1 -0
- package/dist/esm/migrations/0006_run_principals.js +30 -0
- package/dist/esm/migrations/0006_run_principals.js.map +1 -0
- package/dist/esm/migrations/0007_resume_consent.d.ts +18 -0
- package/dist/esm/migrations/0007_resume_consent.d.ts.map +1 -0
- package/dist/esm/migrations/0007_resume_consent.js +27 -0
- package/dist/esm/migrations/0007_resume_consent.js.map +1 -0
- package/dist/esm/test/TestControl.d.ts +19 -0
- package/dist/esm/test/TestControl.d.ts.map +1 -0
- package/dist/esm/test/TestControl.js +30 -0
- package/dist/esm/test/TestControl.js.map +1 -0
- package/docs/README.md +189 -0
- package/docs/api.md +982 -0
- package/docs/concepts/authority.md +109 -0
- package/docs/concepts/cancellation.md +129 -0
- package/docs/concepts/lineage.md +132 -0
- package/docs/concepts/ownership.md +139 -0
- package/docs/concepts/projections.md +203 -0
- package/docs/concepts/receipts.md +128 -0
- package/docs/guides/approvals.md +284 -0
- package/docs/guides/cancel-and-resume.md +162 -0
- package/docs/guides/durable-storage.md +147 -0
- package/docs/guides/implement-an-executor.md +173 -0
- package/docs/guides/ingest-a-webhook.md +177 -0
- package/docs/guides/list-runs.md +160 -0
- package/docs/guides/monitor-runs.md +176 -0
- package/docs/guides/observe-health.md +147 -0
- package/docs/guides/postgres-tests.md +5 -0
- package/docs/guides/serve-over-rpc.md +220 -0
- package/docs/guides/signal-a-run.md +53 -0
- package/docs/guides/steer-a-run.md +138 -0
- package/docs/guides/store-credentials.md +164 -0
- package/docs/guides/testing.md +139 -0
- package/docs/guides/watch-a-run.md +154 -0
- package/docs/installation.md +106 -0
- package/docs/quickstart.md +163 -0
- package/docs/troubleshooting.md +208 -0
- package/package.json +405 -3
- package/src/ApprovalAuthority.ts +114 -0
- package/src/Cancellation.ts +172 -0
- package/src/Channels.ts +493 -0
- package/src/Control.ts +337 -0
- package/src/ControlClient.ts +319 -0
- package/src/ControlError.ts +378 -0
- package/src/ControlExecutor.ts +490 -0
- package/src/ControlFacts.ts +383 -0
- package/src/ControlLive.ts +2113 -0
- package/src/ControlRpcs.ts +443 -0
- package/src/ControlRuntime.ts +1597 -0
- package/src/ControlSchema.ts +1380 -0
- package/src/ControlServer.ts +182 -0
- package/src/Credential.ts +310 -0
- package/src/CredentialCipher.ts +110 -0
- package/src/CredentialStore.ts +152 -0
- package/src/DispatchReader.ts +122 -0
- package/src/Health.ts +591 -0
- package/src/JevSessionChecker.ts +127 -0
- package/src/Lineage.ts +203 -0
- package/src/Migrations.ts +56 -0
- package/src/Monitor.ts +600 -0
- package/src/ScopedToken.ts +306 -0
- package/src/SqlControlRuntime.ts +2476 -0
- package/src/SqlCredentialStore.ts +148 -0
- package/src/Steering.ts +96 -0
- package/src/SystemFlows.ts +225 -0
- package/src/WebCryptoCipher.ts +169 -0
- package/src/WebhookChannel.ts +166 -0
- package/src/index.ts +188 -0
- package/src/internal/MutationBoundary.ts +46 -0
- package/src/internal/activeFibers.ts +22 -0
- package/src/internal/issues.ts +40 -0
- package/src/internal/planning.ts +262 -0
- package/src/internal/sqlSchemaErrors.ts +37 -0
- package/src/migrations/0001_control_tables.ts +99 -0
- package/src/migrations/0002_run_keys.ts +23 -0
- package/src/migrations/0003_signal_commands.ts +27 -0
- package/src/migrations/0004_approval_decisions.ts +25 -0
- package/src/migrations/0005_signal_principals.ts +27 -0
- package/src/migrations/0006_run_principals.ts +31 -0
- package/src/migrations/0007_resume_consent.ts +28 -0
- package/src/test/TestControl.ts +47 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Authority, not execution"
|
|
3
|
+
description: "Why the control plane records decisions instead of running work, the three ports that keep that split honest, and what a composition looks like with each of them present or absent."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 1
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
`Control` decides. It does not run anything.
|
|
9
|
+
|
|
10
|
+
Every operation the service exposes either records an intent or reads back
|
|
11
|
+
evidence. `plan` writes a reviewable card. `approve` resolves a durable token
|
|
12
|
+
and installs a grant. `cancel` writes a request and an attribution. `watch`
|
|
13
|
+
replays a journal it did not write. Nothing in this package executes a flow,
|
|
14
|
+
opens a step, or interprets a graph.
|
|
15
|
+
|
|
16
|
+
That is not a limitation to work around. It is what lets one control plane
|
|
17
|
+
answer for runs that several processes own, on machines it cannot reach, in a
|
|
18
|
+
database it shares with an engine it never imports.
|
|
19
|
+
|
|
20
|
+
## The three ports
|
|
21
|
+
|
|
22
|
+
A host chooses an implementation of each seam, and the seams are what make the
|
|
23
|
+
plane portable.
|
|
24
|
+
|
|
25
|
+
| Port | Question it answers | Implementations here |
|
|
26
|
+
| ----------------- | ----------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
27
|
+
| `ControlRuntime` | Where do plans, tokens, grants, idempotency records, and run rows live? | `ControlRuntime.layerMemory`, `SqlControlRuntime.layer` |
|
|
28
|
+
| `ControlExecutor` | Who actually runs the work, and what did they do with my request? | `ControlExecutor.makeNoop`, your own |
|
|
29
|
+
| `Journal` | Where is the evidence of what was decided? | [`@smthrs/journal`](/api/journal) |
|
|
30
|
+
|
|
31
|
+
`ControlLive.layer` is the implementation over those three plus the
|
|
32
|
+
[notification queue](/api/notifications) a steer travels through and the
|
|
33
|
+
[registry](/api/registry) a flow listing reads.
|
|
34
|
+
|
|
35
|
+
Both runtimes are held to one
|
|
36
|
+
[shared contract suite](https://github.com/smithersai/smithers/blob/main/packages/smithers/control/test/ControlContract.ts),
|
|
37
|
+
so a behavior you observe against the memory runtime is a behavior the durable
|
|
38
|
+
one owes you.
|
|
39
|
+
|
|
40
|
+
## The executor is optional, and the absence is a real composition
|
|
41
|
+
|
|
42
|
+
`ControlLive` reads `ControlExecutor` through `Effect.serviceOption`. A
|
|
43
|
+
composition with no executor is not broken; it is a plane that starts nothing:
|
|
44
|
+
|
|
45
|
+
- `run` on an approved plan still mints the run row, still journals
|
|
46
|
+
`control.run.accepted`, and then releases the row as `control.run.pending`,
|
|
47
|
+
because nothing here took the launch.
|
|
48
|
+
- `cancel` still writes its attribution and still interrupts a fiber this
|
|
49
|
+
process is driving, but nothing reaches an engine row in another database.
|
|
50
|
+
- `signal` still records the fact, and no wait point is completed by this call.
|
|
51
|
+
- `resume` and `run` with a Resume input join or claim control-launched runs
|
|
52
|
+
and journal `control.run.resume`. A caller or journal subscriber must drive
|
|
53
|
+
the execution. Explicit resume does not offer work through
|
|
54
|
+
`ControlExecutor.resumeRun`. A run a live host parked is handed to that
|
|
55
|
+
host as a `requestResume` delegation that carries the operator's consent.
|
|
56
|
+
- A node-approval decision records a durable `requestResume` delegation.
|
|
57
|
+
Without an executor, it remains available for the owning host's next poll.
|
|
58
|
+
|
|
59
|
+
That is the shape a monitor, a dashboard, or a read-only operator tool has, and
|
|
60
|
+
it is what [`examples/src/38-monitor-and-alert.ts`](https://github.com/smithersai/smithers/blob/main/examples/src/38-monitor-and-alert.ts)
|
|
61
|
+
builds on purpose.
|
|
62
|
+
|
|
63
|
+
## Two run tables, one journal
|
|
64
|
+
|
|
65
|
+
A control run row and an engine run row are different documents about the same
|
|
66
|
+
work, and they do not collide by accident: the plane keeps its own
|
|
67
|
+
`flows_runs`, and the engine keeps its own. The [`smthrs` CLI](/api/cli) runs
|
|
68
|
+
them as two files, `.flows/control.db` and `.flows/engine.db`.
|
|
69
|
+
|
|
70
|
+
They share the journal, and that is what makes `watch` worth having: one stream
|
|
71
|
+
carries `control.run.accepted` and `flows.engine.attempt-started` in the order
|
|
72
|
+
they happened.
|
|
73
|
+
|
|
74
|
+
Sharing one database instead is a deployment choice with consequences, because
|
|
75
|
+
`SqlControlRuntime` reads the engine's own columns for several projections:
|
|
76
|
+
|
|
77
|
+
| Projection | Column or entry it reads |
|
|
78
|
+
| ------------------------------------------------- | ----------------------------------------------------- |
|
|
79
|
+
| `RunSummary.waitingReason` | `flows_runs.waiting_reason` |
|
|
80
|
+
| Engine-created children and forks in `list` | `flows_run_parents`, `flows.time-travel.fork-created` |
|
|
81
|
+
| `RunSummary.cancellation` with `source: "engine"` | `cancel_requested_at_ms`, `flows.engine.interrupted` |
|
|
82
|
+
|
|
83
|
+
Give the control runtime and the engine one `SqlClient` and those projections
|
|
84
|
+
fill in. Keep them apart and the projections are empty, while cancellation
|
|
85
|
+
still converges, because the request travels through the `ControlExecutor` port
|
|
86
|
+
and the owning driver settles from it.
|
|
87
|
+
|
|
88
|
+
## What the plane owes a caller
|
|
89
|
+
|
|
90
|
+
Three properties hold across every implementation of every port, and the rest
|
|
91
|
+
of this package exists to keep them:
|
|
92
|
+
|
|
93
|
+
1. **Every mutation is idempotent under its key.** A retry answers the first
|
|
94
|
+
call's receipt rather than doing the work twice. See
|
|
95
|
+
[Receipts and idempotency](./receipts.md).
|
|
96
|
+
2. **Every mutation is attributed.** The runtime stamps a principal, and a
|
|
97
|
+
server stamps the one it authenticated rather than the one a client claimed.
|
|
98
|
+
3. **Every mutation leaves evidence beside the state it changed.** The journal
|
|
99
|
+
entry and the state write commit together, so a reader cannot see one
|
|
100
|
+
without the other.
|
|
101
|
+
|
|
102
|
+
## Where to go next
|
|
103
|
+
|
|
104
|
+
- [Receipts and idempotency](./receipts.md): what a receipt means, and what a
|
|
105
|
+
second ask is worth.
|
|
106
|
+
- [Ownership, fences, and claims](./ownership.md): why a mutation can answer
|
|
107
|
+
`ClaimLost`, and what a park releases.
|
|
108
|
+
- [Journal projections](./projections.md): how `watch` turns entries into
|
|
109
|
+
`ControlEvent` values.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Cancellation attribution"
|
|
3
|
+
description: "A durable cancellation is anonymous on its own. How the journal adds back who asked and why, the three sources in the order they rank, and why a cascade inherits its ancestor's principal."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 6
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A durable cancellation records that somebody asked and when, and nothing else.
|
|
9
|
+
`flows_runs.cancel_requested_at_ms` is one number. It cannot say who, why, or
|
|
10
|
+
whether this run was asked for by name rather than swept up in an ancestor's
|
|
11
|
+
cascade.
|
|
12
|
+
|
|
13
|
+
`RunSummary.cancellation` is the attribution the journal adds back:
|
|
14
|
+
|
|
15
|
+
| Field | Meaning |
|
|
16
|
+
| -------------- | ------------------------------------------------------------------------- |
|
|
17
|
+
| `requestedAt` | When the cancellation was asked for. |
|
|
18
|
+
| `source` | `control`, `cascade`, or `engine`. |
|
|
19
|
+
| `principal` | Who asked. Present on a `control` source and on the `cascade` it started. |
|
|
20
|
+
| `reason` | Why, as the operator stated it. |
|
|
21
|
+
| `cascadedFrom` | The cancelled ancestor this run was swept up with. |
|
|
22
|
+
|
|
23
|
+
`control` is an operator asking through this plane, and it is the only source
|
|
24
|
+
that can name a principal. `cascade` is a run swept up in an ancestor's
|
|
25
|
+
cancellation. `engine` is everything the runtime decided on its own account: a
|
|
26
|
+
lease expiry, a budget, a supervisor.
|
|
27
|
+
|
|
28
|
+
## The three sources, in order
|
|
29
|
+
|
|
30
|
+
`Cancellation.attribute` is the fold, and a run's own evidence outranks its
|
|
31
|
+
ancestors':
|
|
32
|
+
|
|
33
|
+
1. **A `control.run.cancel-requested` entry names this run.** Somebody asked
|
|
34
|
+
for it by name, and the entry says who and why.
|
|
35
|
+
2. **A cancelled ancestor exists.** The run reports `cascade`, names the
|
|
36
|
+
nearest cancelled ancestor, and inherits that ancestor's principal and
|
|
37
|
+
reason. The honest answer to "who cancelled this child" is the operator who
|
|
38
|
+
cancelled its parent.
|
|
39
|
+
3. **Neither.** The engine cancelled the run on its own account. There is no
|
|
40
|
+
principal to report, and inventing one would be worse than saying nothing.
|
|
41
|
+
|
|
42
|
+
A run counts as cancelled when any of three things is true: the run store's
|
|
43
|
+
`cancel_requested_at_ms` is set, the engine journaled
|
|
44
|
+
`flows.engine.interrupted` with outcome `cancelled`, or an attributed request
|
|
45
|
+
names the run. The third matters because a control plane cancelling a run it
|
|
46
|
+
owns interrupts the fiber rather than writing the request column, and its
|
|
47
|
+
journal entry is the whole record.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import * as Cancellation from "@smthrs/control/Cancellation"
|
|
51
|
+
|
|
52
|
+
const attributed = Cancellation.attribute({
|
|
53
|
+
runs: [
|
|
54
|
+
{ runId: "run-1", cancelRequestedAt: 10 },
|
|
55
|
+
{ runId: "run-2", parentRunId: "run-1", cancelledAt: 12 }
|
|
56
|
+
],
|
|
57
|
+
requests: new Map([[
|
|
58
|
+
"run-1",
|
|
59
|
+
{ requestedAt: 10, principal: { id: "ada", kind: "user", stampedAt: 10 }, reason: "budget" }
|
|
60
|
+
]])
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
attributed.get("run-1")
|
|
64
|
+
// { requestedAt: 10, source: "control", principal: { id: "ada", ... }, reason: "budget" }
|
|
65
|
+
attributed.get("run-2")
|
|
66
|
+
// { requestedAt: 12, source: "cascade", principal: { id: "ada", ... }, reason: "budget", cascadedFrom: "run-1" }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Why the fold is pure and scope-independent
|
|
70
|
+
|
|
71
|
+
`attribute` reads whatever evidence it is handed and never issues a query, so
|
|
72
|
+
the caller chooses how much to read. `SqlControlRuntime` uses two scopes:
|
|
73
|
+
|
|
74
|
+
- A **listing** folds the whole database, because every row is going to be
|
|
75
|
+
answered for anyway.
|
|
76
|
+
- **Reading one run** folds that run and its ancestor chain, which is the
|
|
77
|
+
smallest scope that can still answer the question.
|
|
78
|
+
|
|
79
|
+
Cascade is a fact about a run's ancestors, so it cannot be decided one row at a
|
|
80
|
+
time: the request that cancelled a child may be several rounds up the chain.
|
|
81
|
+
Reading one run therefore costs one recursive walk over `parent_run_id` plus
|
|
82
|
+
one spawn-edge read per nesting level, and never grows with the size of the
|
|
83
|
+
database. That is what keeps the cost of steering or cancelling a run
|
|
84
|
+
independent of how many runs exist.
|
|
85
|
+
|
|
86
|
+
The ancestor walk carries a visited set. A cyclic parent chain is not reachable
|
|
87
|
+
through the engine's own cycle detection, but a projection that hung on corrupt
|
|
88
|
+
ancestry would take the control plane down with it.
|
|
89
|
+
|
|
90
|
+
## Where the attribution is written
|
|
91
|
+
|
|
92
|
+
`cancel` writes the principal and the reason onto its
|
|
93
|
+
`control.run.cancel-requested` entry, inside the mutation's own transaction, so
|
|
94
|
+
a cancellation cannot commit anonymously. `resume` records the same pair on its
|
|
95
|
+
`control.run.resume` entry.
|
|
96
|
+
|
|
97
|
+
The request, attribution, and acceptance receipt commit before the local fiber
|
|
98
|
+
is interrupted. Cancellation then awaits its finalizers without holding the
|
|
99
|
+
mutation semaphore or journal transaction, so cleanup can signal another run
|
|
100
|
+
or use the same durable writer. A second transaction rechecks the original
|
|
101
|
+
ownership fence and commits the terminal status and event together. A terminal
|
|
102
|
+
outcome reached during cleanup is preserved. If cleanup loses ownership, the
|
|
103
|
+
durable request remains for the owner or a later cancel attempt.
|
|
104
|
+
|
|
105
|
+
Attribution is keyed on the request being newly recorded. `cancel` re-executes
|
|
106
|
+
on every ask, so attributing every ask would journal one
|
|
107
|
+
`control.run.cancel-requested` per ask for a single cancellation. The executor
|
|
108
|
+
answers `already-requested` when the engine column was set before this call
|
|
109
|
+
arrived, and that answer suppresses the second record.
|
|
110
|
+
|
|
111
|
+
A cancel whose executor reports that the engine row has already settled writes
|
|
112
|
+
no attribution, because nobody cancelled anything, and reconciles the control
|
|
113
|
+
row onto the engine's own status instead. Nothing else converges the two rows,
|
|
114
|
+
so a control row left disagreeing with a settled engine row would list the run
|
|
115
|
+
as live forever.
|
|
116
|
+
|
|
117
|
+
Over RPC the `Cancel` procedure carries the reason and refuses a caller-named
|
|
118
|
+
principal. The server stamps the identity it authenticated, so a remote
|
|
119
|
+
operator states why and never states who. The principal's `stampedAt` records
|
|
120
|
+
when that authentication happened; it is evidence about an external event,
|
|
121
|
+
never a value any decision is replayed from.
|
|
122
|
+
|
|
123
|
+
## Where to go next
|
|
124
|
+
|
|
125
|
+
- [Cancel a run, and restart one](../guides/cancel-and-resume.md): the verb,
|
|
126
|
+
and what each receipt means.
|
|
127
|
+
- [Run lineage](./lineage.md): the ancestor chain a cascade walks.
|
|
128
|
+
- [Store control state in a database](../guides/durable-storage.md): the only
|
|
129
|
+
runtime that fills `RunSummary.cancellation` in.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Run lineage"
|
|
3
|
+
description: "The one vocabulary four ancestry records project onto: child, fork, and continuation, where each is written, which record wins, and how watch derives exactly one lineage delta per edge."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 5
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A run's ancestry is recorded by whoever created it, in four different places:
|
|
9
|
+
the run row's `parent_run_id`, `lineage_id`, and `round_ordinal` columns; the
|
|
10
|
+
`flows_run_parents` edge a spawn writes; the `created` and `handed-off` run
|
|
11
|
+
decisions the engine journals; and the `fork-created` marker time travel writes
|
|
12
|
+
on a forked child.
|
|
13
|
+
|
|
14
|
+
`Lineage` owns the one vocabulary all four project onto, and the pure functions
|
|
15
|
+
that do the projecting, so the durable runtime and the watch stream cannot
|
|
16
|
+
disagree about what a run's ancestry means.
|
|
17
|
+
|
|
18
|
+
## The vocabulary
|
|
19
|
+
|
|
20
|
+
`Origin` has three values, and a run with no ancestor has no origin at all:
|
|
21
|
+
|
|
22
|
+
| Origin | What it means |
|
|
23
|
+
| -------------- | ---------------------------------------------- |
|
|
24
|
+
| `child` | Another run spawned it. |
|
|
25
|
+
| `fork` | It was branched off a parent frame. |
|
|
26
|
+
| `continuation` | It is a later round of one trampoline lineage. |
|
|
27
|
+
|
|
28
|
+
A rewind is deliberately absent. It truncates a run in place and creates none,
|
|
29
|
+
so it is a thing that happened to a run rather than a reason a run exists.
|
|
30
|
+
|
|
31
|
+
`Lineage.originOf` is the derivation, and it is pure:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import * as Lineage from "@smthrs/control/Lineage"
|
|
35
|
+
|
|
36
|
+
Lineage.originOf({ parentRunId: "run-1" }) // "child"
|
|
37
|
+
Lineage.originOf({ parentRunId: "run-1", forked: true }) // "fork"
|
|
38
|
+
Lineage.originOf({ parentRunId: "run-1", roundOrdinal: 2 }) // "continuation"
|
|
39
|
+
Lineage.originOf({}) // undefined
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
A fork wins over a plain child because a fork records `parent_run_id` too.
|
|
43
|
+
Without the marker, every fork would be reported as an ordinary child.
|
|
44
|
+
|
|
45
|
+
## What a run summary reports
|
|
46
|
+
|
|
47
|
+
`RunSummary` carries all of it under one vocabulary:
|
|
48
|
+
|
|
49
|
+
| Field | Source | Meaning |
|
|
50
|
+
| -------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| `parentRunId` | `flows_runs.parent_run_id`, else the `flows_run_parents` spawn edge | The run this one branched from: its spawner, the run it was forked off, or the previous trampoline round. |
|
|
52
|
+
| `lineageId` | `flows_runs.lineage_id` | The trampoline lineage this run is a round of. |
|
|
53
|
+
| `roundOrdinal` | `flows_runs.round_ordinal` | Which round. Absent means a lineage of one, read as round 0 of itself. |
|
|
54
|
+
| `origin` | derived | `child`, `fork`, or `continuation`. |
|
|
55
|
+
|
|
56
|
+
The projection reads both recording places because the engine uses both.
|
|
57
|
+
`parent_run_id` is the trampoline chain: the round before this one. A run that
|
|
58
|
+
another run _spawned_ writes nothing in its own row, because the edge lives in
|
|
59
|
+
the `flows_run_parents` graph that cycle detection walks. A projection that
|
|
60
|
+
read the column alone would report every child of every run as an orphan.
|
|
61
|
+
|
|
62
|
+
The column wins when a row has both. That is round 1 of a run that was itself
|
|
63
|
+
spawned: its nearest ancestor is the round before it.
|
|
64
|
+
|
|
65
|
+
## The delta `watch` derives
|
|
66
|
+
|
|
67
|
+
Three journal entries disclose an edge, and each names a different pair:
|
|
68
|
+
|
|
69
|
+
| Entry | Producer | Delta |
|
|
70
|
+
| ---------------------------------------------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------- |
|
|
71
|
+
| `flows.engine.run-decision` with `decision: "created"` at round 0 or with no round | [`@smthrs/engine-store`](/api/engine-store) | `{ runId, parentRunId, origin: "child" }` |
|
|
72
|
+
| `flows.engine.run-decision` with `decision: "handed-off"` | [`@smthrs/engine-store`](/api/engine-store) | `{ runId, parentRunId, lineageId, roundOrdinal, origin: "continuation" }` |
|
|
73
|
+
| `flows.time-travel.fork-created` | [`@smthrs/time-travel`](/api/time-travel) | `{ runId, parentRunId, origin: "fork" }` |
|
|
74
|
+
|
|
75
|
+
The handoff is what carries a trampoline, and a continuation round's own
|
|
76
|
+
`created` decision is deliberately skipped. The engine journals both in one
|
|
77
|
+
transaction: it creates the next round with
|
|
78
|
+
`{decision: "created", lineageId, roundOrdinal, parentExecutionId}` and records
|
|
79
|
+
`{decision: "handed-off", nextExecutionId}` on the round that finished. Both
|
|
80
|
+
name the same pair, so deriving from both would report one run as a `child` of
|
|
81
|
+
its predecessor on one entry and a `continuation` of it on the other.
|
|
82
|
+
|
|
83
|
+
The handoff is the one kept, because it reaches a consumer watching the run
|
|
84
|
+
that hands off, which is the run an operator is already following when a
|
|
85
|
+
trampoline advances. Exactly one delta therefore names each continuation round,
|
|
86
|
+
whichever round of the lineage the consumer is watching.
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
Lineage.derive({
|
|
90
|
+
sequence: 12,
|
|
91
|
+
kind: Lineage.runDecisionEventType,
|
|
92
|
+
runId: "run-1",
|
|
93
|
+
occurredAt: 1_700_000_000_000,
|
|
94
|
+
payload: { decision: "handed-off", nextExecutionId: "run-2", lineageId: "run-1", roundOrdinal: 1 }
|
|
95
|
+
})
|
|
96
|
+
// {
|
|
97
|
+
// sequence: 12,
|
|
98
|
+
// kind: "control.run.lineage",
|
|
99
|
+
// runId: "run-1",
|
|
100
|
+
// occurredAt: 1700000000000,
|
|
101
|
+
// payload: { runId: "run-2", parentRunId: "run-1", lineageId: "run-1", roundOrdinal: 1, origin: "continuation" }
|
|
102
|
+
// }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Everything else derives nothing. This is a projection over entries the control
|
|
106
|
+
plane did not write, so an entry it does not recognize is not an error, and a
|
|
107
|
+
`created` decision that names no parent discloses no ancestry.
|
|
108
|
+
|
|
109
|
+
## Selecting on lineage
|
|
110
|
+
|
|
111
|
+
`list` filters on the same fields, so an operator can ask both ancestry
|
|
112
|
+
questions:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
const children = yield * control.list({ _tag: "runs", filters: { parentRunId: "run-17" } })
|
|
116
|
+
const rounds = yield * control.list({ _tag: "runs", filters: { lineageId: "run-17" } })
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The durable listing covers every row in `flows_runs`, not only the runs the
|
|
120
|
+
control plane launched itself. A child, a fork, and a later trampoline round
|
|
121
|
+
are all created by the engine straight into the run store, and a plane that
|
|
122
|
+
listed only its own launches could not answer what a run spawned. Runs the
|
|
123
|
+
plane launched keep launch order; the rest follow in creation order. A run
|
|
124
|
+
whose `state_json` is not a control summary is projected from the run row's own
|
|
125
|
+
columns instead, with the engine's `flowName` as its `flowId`.
|
|
126
|
+
|
|
127
|
+
## Where to go next
|
|
128
|
+
|
|
129
|
+
- [Find runs and page through them](../guides/list-runs.md): the filters as a
|
|
130
|
+
task.
|
|
131
|
+
- [Watch a run's events](../guides/watch-a-run.md): where the delta arrives.
|
|
132
|
+
- [Time travel on smithers.sh](/docs/concepts/time-travel/): what makes a fork.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Ownership, fences, and claims"
|
|
3
|
+
description: "How a fence makes every owner-sensitive write a compare-and-swap, what a park releases, why a resume can be scoped to runs this plane launched, and how a resume delegation reaches the process that can act on it."
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 3
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Several processes share one control database, and only one of them may be
|
|
9
|
+
driving a given run. Ownership is how the plane decides which, and a **fence**
|
|
10
|
+
is the token that decides it.
|
|
11
|
+
|
|
12
|
+
A fence is a serialized owner identity: a host id, a process id, and a nonce
|
|
13
|
+
that is regenerated on every claim. Every owner-sensitive write presents its
|
|
14
|
+
fence, and the runtime turns that into a single SQL compare-and-swap, so a
|
|
15
|
+
stale writer loses the `UPDATE` rather than racing a read-then-write. A fence
|
|
16
|
+
taken before a park is not the fence held after the resume that follows it, and
|
|
17
|
+
the stale one is refused.
|
|
18
|
+
|
|
19
|
+
When a write presents a fence the row has moved past, the plane answers
|
|
20
|
+
[`ClaimLost`](../troubleshooting.md).
|
|
21
|
+
|
|
22
|
+
## Status is ownership, spelled for an operator
|
|
23
|
+
|
|
24
|
+
`SqlControlRuntime` maps the control plane's vocabulary onto the run store's:
|
|
25
|
+
|
|
26
|
+
| Control status | Run store status | Ownership |
|
|
27
|
+
| ------------------------------------- | ---------------- | -------------------- |
|
|
28
|
+
| `accepted`, `running` | `running` | Held by this process |
|
|
29
|
+
| `accepted` after an executor declines | `suspended` | Released |
|
|
30
|
+
| `parked`, `waiting-approval` | `suspended` | Released |
|
|
31
|
+
| `cancelled`, `completed`, `failed` | same | Released, terminal |
|
|
32
|
+
|
|
33
|
+
A claim writes `accepted`. `Control.run` promotes it to `running` when its
|
|
34
|
+
executor takes the launch; an explicit resume leaves it `accepted` until the
|
|
35
|
+
driver writes another status. An `ownerId` distinguishes an owned `accepted`
|
|
36
|
+
run from a released pending launch. Losing a claim to an owned `accepted` or
|
|
37
|
+
`running` row means a live peer holds it.
|
|
38
|
+
|
|
39
|
+
The authoritative `RunSummary` is written into the row's `state_json` by the
|
|
40
|
+
same fenced `UPDATE` that moves the status, so a projection can never be read
|
|
41
|
+
out of step with the lifecycle.
|
|
42
|
+
|
|
43
|
+
## A park releases the row, and records who parked it
|
|
44
|
+
|
|
45
|
+
A parked execution releases its owner columns. That is what makes it resumable
|
|
46
|
+
at all, and it is also why every process sharing the database can see the park.
|
|
47
|
+
|
|
48
|
+
The fence the park was written under is kept in `RunSummary.parkedBy`, and only
|
|
49
|
+
on a park. It is the one thing left on the row that says which host parked the
|
|
50
|
+
run, so that host recognizes its own park and a short-lived process that would
|
|
51
|
+
drive the run and then exit can tell the execution is not its to take up.
|
|
52
|
+
|
|
53
|
+
`RunSummary.waitingReason` is the other half of the picture, and the control
|
|
54
|
+
plane only ever reads it. The engine writes it when it parks a run and clears
|
|
55
|
+
it on the wake, so an operator park written through
|
|
56
|
+
`ControlRuntime.writeStatus(runId, fence, "parked")` leaves the column empty.
|
|
57
|
+
An empty column is exactly how an operator park is told apart from an engine
|
|
58
|
+
park, and several behaviors turn on that:
|
|
59
|
+
|
|
60
|
+
| `waitingReason` | A steer arriving | A monitor's reading |
|
|
61
|
+
| ---------------- | ---------------- | ------------------- |
|
|
62
|
+
| `event` | Resumes the run | Ordinary park |
|
|
63
|
+
| `released` | Resumes the run | Ordinary park |
|
|
64
|
+
| `approval` | Leaves it parked | `awaiting-human` |
|
|
65
|
+
| `timer`, `quota` | Leaves it parked | Ordinary park |
|
|
66
|
+
| absent | Leaves it parked | `awaiting-human` |
|
|
67
|
+
|
|
68
|
+
## Claim scope: launched, or any
|
|
69
|
+
|
|
70
|
+
`ControlRuntime.resume` joins a non-terminal run whose fence this process
|
|
71
|
+
still holds, including an `accepted` run. A join preserves the original fence.
|
|
72
|
+
An `accepted` run released by `releasePending` is claimable under a new fence.
|
|
73
|
+
|
|
74
|
+
`scope: "launched"` restricts claims to runs recorded in `control_runs`, the
|
|
75
|
+
shared index of control-launched runs. Both `Control.resume` and `Control.run`
|
|
76
|
+
with a Resume input, plus every steer wake, pass this scope. An engine-created
|
|
77
|
+
child, fork, or later trampoline round keeps its own continuation and driver.
|
|
78
|
+
|
|
79
|
+
`scope: "any"`, also the runtime default, is a trusted low-level runtime
|
|
80
|
+
capability for hosts that can drive the claimed execution. It is not the
|
|
81
|
+
public Control resume contract.
|
|
82
|
+
|
|
83
|
+
## Explicit resume records a journal intent
|
|
84
|
+
|
|
85
|
+
Both public resume spellings journal `control.run.resume`. A suspended run
|
|
86
|
+
outside the launch index remains unclaimed; the receipt is `Accepted` after
|
|
87
|
+
the journal intent is recorded. A live peer's owned run fails with `ClaimLost`.
|
|
88
|
+
A caller or journal subscriber must drive the execution, including after a
|
|
89
|
+
successful claim. An `Accepted` receipt does not establish that work started.
|
|
90
|
+
|
|
91
|
+
Explicit resume never calls `ControlExecutor.resumeRun`. A run the caller can
|
|
92
|
+
claim creates no `pendingResumes` entry.
|
|
93
|
+
|
|
94
|
+
A run a live host parked is the exception. `resume` may take a park only after
|
|
95
|
+
a same-host probe proves the parking process dead. Otherwise the runtime
|
|
96
|
+
refuses with `ClaimLost` naming the host in `parkedBy`, and `resume` hands the
|
|
97
|
+
restart to that host. It journals `control.run.resume` with `handedTo` and
|
|
98
|
+
records a `requestResume` delegation whose `consent` is that entry's journal
|
|
99
|
+
sequence. The receipt is `Accepted` with `handedTo`. The parking host takes the
|
|
100
|
+
delegation up on its next poll, records the per-release retry permission under
|
|
101
|
+
that sequence, and re-drives the run. See
|
|
102
|
+
[Cancel a run, and restart one](../guides/cancel-and-resume.md#a-run-a-live-host-parked).
|
|
103
|
+
|
|
104
|
+
## Node approval records a durable resume delegation
|
|
105
|
+
|
|
106
|
+
A decision on an in-run approval restarts the run server-side, and the process
|
|
107
|
+
that decides is usually not the process hosting the execution: an operator's
|
|
108
|
+
`smthrs approvals approve`, a gateway, a second CLI.
|
|
109
|
+
|
|
110
|
+
So the intent is recorded durably rather than published in process:
|
|
111
|
+
|
|
112
|
+
1. `requestResume(runId)` writes the delegation and returns its sequence.
|
|
113
|
+
`RunSummary.pendingResume` reports that sequence while it is outstanding.
|
|
114
|
+
An approval delegation carries no `consent`: it is background intent, and
|
|
115
|
+
it keeps an operator's consent that no host has taken up yet.
|
|
116
|
+
2. The plane offers it to its own executor through `ControlExecutor.resumeRun`.
|
|
117
|
+
An executor that answers `resuming` has claimed the row and is driving, so
|
|
118
|
+
the delegation is cleared with `clearResume(runId, sequence)`.
|
|
119
|
+
3. An executor that answers `unknown` leaves the delegation standing.
|
|
120
|
+
`pendingResumes` is what every host polls, and a run parked by a process
|
|
121
|
+
that has since exited is taken up by whichever host can drive it once the
|
|
122
|
+
delegation has gone unanswered for the run store's heartbeat staleness
|
|
123
|
+
window.
|
|
124
|
+
|
|
125
|
+
The sequence check is what makes the clear safe. A resume requested between the
|
|
126
|
+
read and the clear has a higher sequence and survives, so the host takes it up
|
|
127
|
+
on its next tick instead of losing it.
|
|
128
|
+
|
|
129
|
+
A settled run's delegation is never reported: no host will ever take it up, and
|
|
130
|
+
reporting it forever would turn a finished run into an unbounded backlog.
|
|
131
|
+
|
|
132
|
+
## Where to go next
|
|
133
|
+
|
|
134
|
+
- [Cancel a run, and restart one](../guides/cancel-and-resume.md): the verbs
|
|
135
|
+
that meet these rules head on.
|
|
136
|
+
- [Connect an execution engine](../guides/implement-an-executor.md): the port
|
|
137
|
+
that turns a delegation into a running fiber.
|
|
138
|
+
- [Ownership on smithers.sh](/docs/concepts/ownership/): the same fence, from
|
|
139
|
+
the engine's side.
|