@plurnk/plurnk-service 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.defaults +49 -42
- package/INSTALL.md +48 -11
- package/README.md +12 -2
- package/SPEC.md +1166 -475
- package/digest-sql/channel-dispositions/dispositions.sql +15 -0
- package/digest-sql/channel-state/state.sql +6 -0
- package/digest-sql/curation/curation.sql +5 -0
- package/digest-sql/derivations/derivations.sql +4 -0
- package/digest-sql/embeddings/embeddings.sql +4 -0
- package/digest-sql/entry-dispositions/dispositions.sql +15 -0
- package/digest-sql/entry-state/state.sql +6 -0
- package/digest-sql/token-counts/token-counts.sql +2 -0
- package/dist/Paths.d.ts +0 -4
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +1 -21
- package/dist/Paths.js.map +1 -1
- package/dist/build-info.json +1 -1
- package/dist/content/read-projector.d.ts.map +1 -1
- package/dist/content/read-projector.js +4 -3
- package/dist/content/read-projector.js.map +1 -1
- package/dist/core/BudgetReadout.d.ts.map +1 -1
- package/dist/core/BudgetReadout.js +12 -22
- package/dist/core/BudgetReadout.js.map +1 -1
- package/dist/core/ChannelWrite.d.ts +8 -3
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +40 -8
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +19 -5
- package/dist/core/CoreSchemeServices.d.ts +7 -3
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +8 -1
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +15 -6
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +352 -306
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Engine.d.ts +22 -10
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +86 -46
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +202 -177
- package/dist/core/EntryAddressBinding.d.ts +32 -0
- package/dist/core/EntryAddressBinding.d.ts.map +1 -0
- package/dist/core/EntryAddressBinding.js +93 -0
- package/dist/core/EntryAddressBinding.js.map +1 -0
- package/dist/core/ExecutorRegistry.d.ts +12 -6
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +34 -31
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/HostPaths.d.ts +29 -0
- package/dist/core/HostPaths.d.ts.map +1 -0
- package/dist/core/HostPaths.js +74 -0
- package/dist/core/HostPaths.js.map +1 -0
- package/dist/core/LegacyHome.d.ts +10 -0
- package/dist/core/LegacyHome.d.ts.map +1 -0
- package/dist/core/LegacyHome.js +170 -0
- package/dist/core/LegacyHome.js.map +1 -0
- package/dist/core/LogBody.d.ts +1 -1
- package/dist/core/LogBody.d.ts.map +1 -1
- package/dist/core/LogBody.js +21 -3
- package/dist/core/LogBody.js.map +1 -1
- package/dist/core/LogEntryProjection.js +1 -1
- package/dist/core/LogEntryProjection.js.map +1 -1
- package/dist/core/LogVisibility.d.ts +28 -0
- package/dist/core/LogVisibility.d.ts.map +1 -0
- package/dist/core/LogVisibility.js +215 -0
- package/dist/core/LogVisibility.js.map +1 -0
- package/dist/core/LoopLifecycle.d.ts +1 -0
- package/dist/core/LoopLifecycle.d.ts.map +1 -1
- package/dist/core/LoopLifecycle.js +8 -0
- package/dist/core/LoopLifecycle.js.map +1 -1
- package/dist/core/LoopLifecycle.sql +10 -0
- package/dist/core/OperatorConfig.d.ts +6 -0
- package/dist/core/OperatorConfig.d.ts.map +1 -0
- package/dist/core/OperatorConfig.js +95 -0
- package/dist/core/OperatorConfig.js.map +1 -0
- package/dist/core/OverflowTurn.d.ts +11 -0
- package/dist/core/OverflowTurn.d.ts.map +1 -0
- package/dist/core/OverflowTurn.js +81 -0
- package/dist/core/OverflowTurn.js.map +1 -0
- package/dist/core/Owner.js +3 -3
- package/dist/core/Owner.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +7 -15
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +64 -127
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProblemLog.js +1 -1
- package/dist/core/ProblemLog.js.map +1 -1
- package/dist/core/ProposalLifecycle.d.ts +4 -1
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +86 -47
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ProviderInstantiate.d.ts +9 -5
- package/dist/core/ProviderInstantiate.d.ts.map +1 -1
- package/dist/core/ProviderInstantiate.js +99 -104
- package/dist/core/ProviderInstantiate.js.map +1 -1
- package/dist/core/ReasoningEvent.d.ts +13 -0
- package/dist/core/ReasoningEvent.d.ts.map +1 -0
- package/dist/core/ReasoningEvent.js +6 -0
- package/dist/core/ReasoningEvent.js.map +1 -0
- package/dist/core/ResourceMutations.d.ts +15 -19
- package/dist/core/ResourceMutations.d.ts.map +1 -1
- package/dist/core/ResourceMutations.js +118 -41
- package/dist/core/ResourceMutations.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +12 -12
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +58 -57
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/ToolResources.d.ts +19 -0
- package/dist/core/ToolResources.d.ts.map +1 -0
- package/dist/core/ToolResources.js +123 -0
- package/dist/core/ToolResources.js.map +1 -0
- package/dist/core/Turn.d.ts +27 -0
- package/dist/core/Turn.d.ts.map +1 -0
- package/dist/core/Turn.js +43 -0
- package/dist/core/Turn.js.map +1 -0
- package/dist/core/Turn.sql +72 -0
- package/dist/core/TurnOps.d.ts +7 -0
- package/dist/core/TurnOps.d.ts.map +1 -0
- package/dist/core/TurnOps.js +73 -0
- package/dist/core/TurnOps.js.map +1 -0
- package/dist/core/TurnRunner.d.ts +56 -5
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +1163 -929
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/WorkerName.d.ts +2 -1
- package/dist/core/WorkerName.d.ts.map +1 -1
- package/dist/core/WorkerName.js +2 -1
- package/dist/core/WorkerName.js.map +1 -1
- package/dist/core/WorkerName.sql +17 -2
- package/dist/core/WorkspaceGate.js +1 -1
- package/dist/core/WorkspaceGate.js.map +1 -1
- package/dist/core/caps/CapsResolve.d.ts +2 -2
- package/dist/core/caps/CapsResolve.d.ts.map +1 -1
- package/dist/core/caps/CapsResolve.js +5 -7
- package/dist/core/caps/CapsResolve.js.map +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
- package/dist/core/caps/DbChannelCaps.js +13 -11
- package/dist/core/caps/DbChannelCaps.js.map +1 -1
- package/dist/core/caps/DbEntryCaps.d.ts +5 -5
- package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
- package/dist/core/caps/DbEntryCaps.js +27 -30
- package/dist/core/caps/DbEntryCaps.js.map +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
- package/dist/core/caps/DbNotifyCaps.js +5 -3
- package/dist/core/caps/DbNotifyCaps.js.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.d.ts +3 -3
- package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.js +16 -26
- package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts +6 -4
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +37 -5
- package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
- package/dist/core/env-defaults.d.ts.map +1 -1
- package/dist/core/env-defaults.js +7 -5
- package/dist/core/env-defaults.js.map +1 -1
- package/dist/core/file-creation-policy.d.ts +9 -0
- package/dist/core/file-creation-policy.d.ts.map +1 -0
- package/dist/core/file-creation-policy.js +25 -0
- package/dist/core/file-creation-policy.js.map +1 -0
- package/dist/core/fork.d.ts +4 -1
- package/dist/core/fork.d.ts.map +1 -1
- package/dist/core/fork.js +39 -16
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +83 -40
- package/dist/core/git-env.d.ts +0 -1
- package/dist/core/git-env.d.ts.map +1 -1
- package/dist/core/git-env.js +0 -3
- package/dist/core/git-env.js.map +1 -1
- package/dist/core/git-membership.d.ts +19 -1
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +233 -74
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.d.ts.map +1 -1
- package/dist/core/git-state.js +13 -14
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/operation-target-groups.d.ts +3 -0
- package/dist/core/operation-target-groups.d.ts.map +1 -0
- package/dist/core/operation-target-groups.js +53 -0
- package/dist/core/operation-target-groups.js.map +1 -0
- package/dist/core/packet-inject.d.ts +0 -1
- package/dist/core/packet-inject.d.ts.map +1 -1
- package/dist/core/packet-inject.js +6 -13
- package/dist/core/packet-inject.js.map +1 -1
- package/dist/core/packet-wire.d.ts +4 -0
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +226 -53
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +14 -3
- package/dist/core/plurnk-uri.d.ts.map +1 -1
- package/dist/core/plurnk-uri.js +62 -24
- package/dist/core/plurnk-uri.js.map +1 -1
- package/dist/core/scheme-types.d.ts +1 -0
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/teaching.d.ts.map +1 -1
- package/dist/core/teaching.js +4 -5
- package/dist/core/teaching.js.map +1 -1
- package/dist/core/worker-ops.sql +10 -6
- package/dist/core/worker-settings.d.ts +10 -0
- package/dist/core/worker-settings.d.ts.map +1 -0
- package/dist/core/worker-settings.js +32 -0
- package/dist/core/worker-settings.js.map +1 -0
- package/dist/core/workspace-settings.d.ts +2 -11
- package/dist/core/workspace-settings.d.ts.map +1 -1
- package/dist/core/workspace-settings.js +7 -47
- package/dist/core/workspace-settings.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +164 -92
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +9 -3
- package/dist/observe/genai.d.ts +9 -0
- package/dist/observe/genai.d.ts.map +1 -0
- package/dist/observe/genai.js +31 -0
- package/dist/observe/genai.js.map +1 -0
- package/dist/observe/spans.d.ts +3 -3
- package/dist/observe/spans.d.ts.map +1 -1
- package/dist/observe/spans.js +20 -5
- package/dist/observe/spans.js.map +1 -1
- package/dist/schemes/EffectPolicy.d.ts +3 -0
- package/dist/schemes/EffectPolicy.d.ts.map +1 -1
- package/dist/schemes/EffectPolicy.js +38 -6
- package/dist/schemes/EffectPolicy.js.map +1 -1
- package/dist/schemes/Exec.d.ts +3 -2
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +109 -34
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +5 -4
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +170 -80
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts +6 -2
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +73 -22
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +25 -7
- package/dist/schemes/Prompt.d.ts +2 -3
- package/dist/schemes/Prompt.d.ts.map +1 -1
- package/dist/schemes/Prompt.js +5 -9
- package/dist/schemes/Prompt.js.map +1 -1
- package/dist/schemes/QuestionTool.d.ts +10 -0
- package/dist/schemes/QuestionTool.d.ts.map +1 -0
- package/dist/schemes/QuestionTool.js +81 -0
- package/dist/schemes/QuestionTool.js.map +1 -0
- package/dist/schemes/Skill.d.ts.map +1 -1
- package/dist/schemes/Skill.js +6 -1
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +97 -34
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts +5 -5
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +22 -17
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +77 -24
- package/dist/schemes/_entry-find.d.ts +8 -5
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +121 -40
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-find.sql +16 -11
- package/dist/schemes/_entry-graph.sql +2 -6
- package/dist/schemes/_entry-manifest.d.ts +2 -2
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +18 -6
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts +6 -5
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +39 -46
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +9 -6
- package/dist/schemes/_entry-send.d.ts +2 -2
- package/dist/schemes/_entry-send.d.ts.map +1 -1
- package/dist/schemes/_entry-send.js +16 -14
- package/dist/schemes/_entry-send.js.map +1 -1
- package/dist/schemes/_search-index.d.ts.map +1 -1
- package/dist/schemes/_search-index.js +39 -25
- package/dist/schemes/_search-index.js.map +1 -1
- package/dist/server/Daemon.d.ts +98 -15
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +864 -348
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonModule.d.ts +84 -19
- package/dist/server/DaemonModule.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.d.ts +15 -10
- package/dist/server/DrainSupervisor.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.js +70 -19
- package/dist/server/DrainSupervisor.js.map +1 -1
- package/dist/server/Functionality.d.ts +44 -0
- package/dist/server/Functionality.d.ts.map +1 -0
- package/dist/server/Functionality.js +422 -0
- package/dist/server/Functionality.js.map +1 -0
- package/dist/server/FunctionalityManager.d.ts +22 -0
- package/dist/server/FunctionalityManager.d.ts.map +1 -0
- package/dist/server/FunctionalityManager.js +121 -0
- package/dist/server/FunctionalityManager.js.map +1 -0
- package/dist/server/SkillsFunctionality.d.ts +61 -0
- package/dist/server/SkillsFunctionality.d.ts.map +1 -0
- package/dist/server/SkillsFunctionality.js +449 -0
- package/dist/server/SkillsFunctionality.js.map +1 -0
- package/dist/server/WorkerCapabilities.d.ts +21 -0
- package/dist/server/WorkerCapabilities.d.ts.map +1 -0
- package/dist/server/WorkerCapabilities.js +214 -0
- package/dist/server/WorkerCapabilities.js.map +1 -0
- package/dist/server/WorkerResidency.d.ts +40 -0
- package/dist/server/WorkerResidency.d.ts.map +1 -0
- package/dist/server/WorkerResidency.js +283 -0
- package/dist/server/WorkerResidency.js.map +1 -0
- package/dist/server/client-input.d.ts +6 -2
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +72 -32
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/dispatch-as-plurnk.d.ts +2 -2
- package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
- package/dist/server/dispatch-as-plurnk.js +109 -23
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/drain.sql +33 -13
- package/dist/server/envelope.d.ts +7 -2
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +12 -6
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +36 -4
- package/dist/server/lifecycle-recovery.sql +24 -17
- package/dist/server/logEntry.d.ts +2 -1
- package/dist/server/logEntry.d.ts.map +1 -1
- package/dist/server/logEntry.js +3 -2
- package/dist/server/logEntry.js.map +1 -1
- package/dist/server/logEntry.sql +7 -0
- package/dist/server/loop-model.d.ts +2 -2
- package/dist/server/loop-model.d.ts.map +1 -1
- package/dist/server/loop-model.js +23 -33
- package/dist/server/loop-model.js.map +1 -1
- package/dist/server/loopDocs.d.ts +2 -1
- package/dist/server/loopDocs.d.ts.map +1 -1
- package/dist/server/loopDocs.js +88 -21
- package/dist/server/loopDocs.js.map +1 -1
- package/dist/server/loopDocs.sql +14 -8
- package/dist/server/model-catalog.d.ts +3 -0
- package/dist/server/model-catalog.d.ts.map +1 -0
- package/dist/server/model-catalog.js +83 -0
- package/dist/server/model-catalog.js.map +1 -0
- package/dist/server/model-route.d.ts +7 -0
- package/dist/server/model-route.d.ts.map +1 -0
- package/dist/server/model-route.js +47 -0
- package/dist/server/model-route.js.map +1 -0
- package/dist/server/module-discovery.d.ts +13 -0
- package/dist/server/module-discovery.d.ts.map +1 -0
- package/dist/server/module-discovery.js +103 -0
- package/dist/server/module-discovery.js.map +1 -0
- package/dist/server/seam-entry-read.sql +9 -7
- package/dist/server/seam-log-read.sql +1 -1
- package/dist/server/seam-loop.sql +10 -1
- package/dist/server/seam-proposal-list.sql +4 -2
- package/dist/server/worker-capabilities.sql +19 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +193 -124
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +905 -196
- package/package.json +40 -38
- package/dist/core/ExecutableTools.d.ts +0 -11
- package/dist/core/ExecutableTools.d.ts.map +0 -1
- package/dist/core/ExecutableTools.js +0 -49
- package/dist/core/ExecutableTools.js.map +0 -1
- package/dist/core/JournalTurn.d.ts +0 -9
- package/dist/core/JournalTurn.d.ts.map +0 -1
- package/dist/core/JournalTurn.js +0 -14
- package/dist/core/JournalTurn.js.map +0 -1
- package/dist/core/JournalTurn.sql +0 -10
- package/dist/core/git-iso.d.ts +0 -13
- package/dist/core/git-iso.d.ts.map +0 -1
- package/dist/core/git-iso.js +0 -267
- package/dist/core/git-iso.js.map +0 -1
- package/dist/core/search-gate.d.ts +0 -17
- package/dist/core/search-gate.d.ts.map +0 -1
- package/dist/core/search-gate.js +0 -77
- package/dist/core/search-gate.js.map +0 -1
- package/dist/digest/Digest.sql +0 -1
- package/dist/server/workspace-capabilities.sql +0 -19
|
@@ -19,17 +19,21 @@ CREATE TABLE IF NOT EXISTS workspaces (
|
|
|
19
19
|
|
|
20
20
|
CREATE INDEX IF NOT EXISTS workspaces_created_at ON workspaces (created_at);
|
|
21
21
|
|
|
22
|
-
-- {§
|
|
23
|
-
--
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
-- model_routes — the immutable resolved model route ({§worker-model-selection}). One row per
|
|
23
|
+
-- complete resolved tuple; append-only. Alias is provenance plus a tuning scope,
|
|
24
|
+
-- not route identity, and is absent on a direct provider/model selection.
|
|
25
|
+
CREATE TABLE IF NOT EXISTS model_routes (
|
|
26
|
+
id INTEGER NOT NULL PRIMARY KEY,
|
|
27
|
+
alias TEXT CHECK (alias IS NULL OR length(alias) > 0),
|
|
28
|
+
provider TEXT NOT NULL CHECK (length(provider) > 0),
|
|
29
|
+
model TEXT NOT NULL CHECK (length(model) > 0),
|
|
30
|
+
base_url TEXT CHECK (base_url IS NULL OR length(base_url) > 0),
|
|
31
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
31
32
|
) STRICT;
|
|
32
33
|
|
|
34
|
+
CREATE UNIQUE INDEX IF NOT EXISTS model_routes_identity
|
|
35
|
+
ON model_routes (coalesce(alias, ''), provider, model, coalesce(base_url, ''));
|
|
36
|
+
|
|
33
37
|
-- workers
|
|
34
38
|
CREATE TABLE IF NOT EXISTS workers (
|
|
35
39
|
id INTEGER NOT NULL PRIMARY KEY,
|
|
@@ -41,16 +45,34 @@ CREATE TABLE IF NOT EXISTS workers (
|
|
|
41
45
|
-- independent databases reuse local integer ids.
|
|
42
46
|
provider_identity TEXT NOT NULL DEFAULT (lower(hex(randomblob(16))))
|
|
43
47
|
CHECK (length(provider_identity) = 32 AND provider_identity NOT GLOB '*[^0-9a-f]*'),
|
|
48
|
+
-- {§worker-model-selection}: the worker's durable resolved model, NULL for non-model
|
|
49
|
+
-- workers or a deliberately modelless unresolved worker. spawn_model_route_id is the
|
|
50
|
+
-- persistent spawn override; NULL means "use my model."
|
|
51
|
+
model_route_id INTEGER REFERENCES model_routes(id),
|
|
52
|
+
spawn_model_route_id INTEGER REFERENCES model_routes(id),
|
|
53
|
+
-- {§worker-reasoning-policy}: nullable only while the worker has no model;
|
|
54
|
+
-- once selected, model and reasoning policy form one durable generation policy.
|
|
55
|
+
reasoning_policy TEXT CHECK (reasoning_policy IS NULL OR reasoning_policy IN ('off', 'adaptive', 'low', 'medium', 'high')),
|
|
44
56
|
-- workers fork via parent_worker_id; workspaces carry no parent — {§machine-processes-no-fork-workspace}
|
|
45
57
|
parent_worker_id INTEGER CHECK (parent_worker_id IS NULL OR parent_worker_id != id),
|
|
46
|
-
origin TEXT NOT NULL DEFAULT 'client' CHECK (origin IN ('model', 'client', '
|
|
58
|
+
origin TEXT NOT NULL DEFAULT 'client' CHECK (origin IN ('model', 'client', '_plurnk')),
|
|
47
59
|
-- {§methods-model-worker}: durable identity for the workspace's stable
|
|
48
60
|
-- default conversation; unrelated to its human-facing, reclaimable name.
|
|
49
61
|
default_conversation INTEGER NOT NULL DEFAULT 0 CHECK (default_conversation IN (0, 1)),
|
|
62
|
+
-- {§worker-settings}: the worker's own behavioral rules inside the workspace's
|
|
63
|
+
-- world — the workspace is how things are; each worker carries the rules its
|
|
64
|
+
-- loops obey. Client-declared at worker creation, mutable between loops,
|
|
65
|
+
-- validated at the client-input boundary (closed known-key set; unknown keys
|
|
66
|
+
-- never persist).
|
|
67
|
+
settings TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(settings)),
|
|
50
68
|
-- {§env-delta-log-pull}: monotonic observation progress, not a private world snapshot.
|
|
51
|
-
--
|
|
69
|
+
-- Creation captures the workspace high-water; a fork instead copies its
|
|
70
|
+
-- parent's cursor and records the closed event boundary of its snapshot.
|
|
52
71
|
ambient_event_cursor INTEGER CHECK (ambient_event_cursor IS NULL OR ambient_event_cursor >= 0),
|
|
72
|
+
fork_event_boundary INTEGER CHECK (fork_event_boundary IS NULL OR fork_event_boundary >= 0),
|
|
73
|
+
CHECK (fork_event_boundary IS NULL OR parent_worker_id IS NOT NULL),
|
|
53
74
|
CHECK (default_conversation = 0 OR (origin = 'model' AND parent_worker_id IS NULL)),
|
|
75
|
+
CHECK ((model_route_id IS NULL) = (reasoning_policy IS NULL)),
|
|
54
76
|
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
55
77
|
FOREIGN KEY (parent_worker_id) REFERENCES workers(id) ON DELETE CASCADE
|
|
56
78
|
) STRICT;
|
|
@@ -73,48 +95,133 @@ BEGIN
|
|
|
73
95
|
SELECT RAISE(ABORT, 'workers.provider_identity is immutable');
|
|
74
96
|
END;
|
|
75
97
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
98
|
+
CREATE TRIGGER IF NOT EXISTS workers_fork_event_boundary_immutable
|
|
99
|
+
BEFORE UPDATE OF fork_event_boundary ON workers
|
|
100
|
+
WHEN NEW.fork_event_boundary IS NOT OLD.fork_event_boundary
|
|
101
|
+
BEGIN
|
|
102
|
+
SELECT RAISE(ABORT, 'workers.fork_event_boundary is immutable');
|
|
103
|
+
END;
|
|
104
|
+
|
|
105
|
+
-- {§module-worker-state}: one opaque, provider-validated JSON snapshot per
|
|
106
|
+
-- module owner and Worker. Core owns isolation, inheritance, and lifecycle;
|
|
107
|
+
-- the module owns its state schema and semantics.
|
|
108
|
+
CREATE TABLE IF NOT EXISTS worker_module_state (
|
|
109
|
+
worker_id INTEGER NOT NULL,
|
|
110
|
+
namespace_owner TEXT NOT NULL CHECK (length(namespace_owner) > 0),
|
|
111
|
+
state TEXT NOT NULL CHECK (json_valid(state)),
|
|
112
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
113
|
+
PRIMARY KEY (worker_id, namespace_owner),
|
|
114
|
+
FOREIGN KEY (worker_id) REFERENCES workers(id) ON DELETE CASCADE
|
|
115
|
+
) STRICT;
|
|
116
|
+
|
|
117
|
+
-- A child snapshots every initialized module state at birth. Later parent
|
|
118
|
+
-- mutations do not propagate; the child and parent are independent authorities.
|
|
119
|
+
CREATE TRIGGER IF NOT EXISTS worker_module_state_inherit
|
|
120
|
+
AFTER INSERT ON workers
|
|
121
|
+
WHEN NEW.parent_worker_id IS NOT NULL
|
|
122
|
+
BEGIN
|
|
123
|
+
INSERT INTO worker_module_state (worker_id, namespace_owner, state, updated_at)
|
|
124
|
+
SELECT NEW.id, namespace_owner, state, updated_at
|
|
125
|
+
FROM worker_module_state
|
|
126
|
+
WHERE worker_id = NEW.parent_worker_id;
|
|
127
|
+
END;
|
|
128
|
+
|
|
129
|
+
-- {§env-delta-log-pull}: one append-only occurrence journal gives every
|
|
130
|
+
-- producer a shared monotonic order. Audience is structural: direct parent,
|
|
131
|
+
-- explicit workspace broadcast, or their union. The event snapshots exactly
|
|
132
|
+
-- what an observer row needs because source-log curation cannot erase history.
|
|
79
133
|
-- source_record_id is forensic identity for the originating log/loop row, not a
|
|
80
134
|
-- foreign key: model log curation must not erase an already-recorded occurrence.
|
|
81
135
|
CREATE TABLE IF NOT EXISTS ambient_events (
|
|
82
|
-
id
|
|
83
|
-
workspace_id
|
|
84
|
-
producer_worker_id
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
136
|
+
id INTEGER NOT NULL PRIMARY KEY,
|
|
137
|
+
workspace_id INTEGER NOT NULL,
|
|
138
|
+
producer_worker_id INTEGER NOT NULL,
|
|
139
|
+
target_parent_worker_id INTEGER,
|
|
140
|
+
workspace_broadcast INTEGER NOT NULL DEFAULT 0 CHECK (workspace_broadcast IN (0, 1)),
|
|
141
|
+
kind TEXT NOT NULL CHECK (kind IN ('activity', 'loop_termination')),
|
|
142
|
+
source_record_id INTEGER NOT NULL CHECK (source_record_id >= 1),
|
|
143
|
+
at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
144
|
+
source TEXT,
|
|
145
|
+
op TEXT NOT NULL,
|
|
146
|
+
delimiter TEXT NOT NULL DEFAULT '',
|
|
147
|
+
signal TEXT CHECK (signal IS NULL OR json_valid(signal)),
|
|
148
|
+
scheme TEXT,
|
|
149
|
+
username TEXT,
|
|
150
|
+
password TEXT,
|
|
151
|
+
hostname TEXT,
|
|
152
|
+
port INTEGER CHECK (port IS NULL OR (port BETWEEN 0 AND 65535)),
|
|
153
|
+
pathname TEXT,
|
|
154
|
+
query TEXT,
|
|
155
|
+
fragment TEXT,
|
|
156
|
+
line_marker TEXT CHECK (line_marker IS NULL OR json_valid(line_marker)),
|
|
157
|
+
tx TEXT NOT NULL,
|
|
158
|
+
mimetype_tx TEXT NOT NULL CHECK (length(mimetype_tx) > 0),
|
|
159
|
+
rx TEXT NOT NULL,
|
|
160
|
+
mimetype_rx TEXT NOT NULL CHECK (length(mimetype_rx) > 0),
|
|
161
|
+
status_rx INTEGER NOT NULL CHECK (status_rx BETWEEN 100 AND 599),
|
|
162
|
+
state TEXT NOT NULL CHECK (state IN ('resolved', 'failed', 'cancelled')),
|
|
163
|
+
outcome TEXT,
|
|
164
|
+
attrs TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(attrs)),
|
|
165
|
+
tags TEXT NOT NULL DEFAULT '[]' CHECK (json_valid(tags) AND json_type(tags) = 'array'),
|
|
166
|
+
terminated_by TEXT CHECK (terminated_by IS NULL OR terminated_by = 'cancel'),
|
|
167
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
168
|
+
CHECK (target_parent_worker_id IS NOT NULL OR workspace_broadcast = 1),
|
|
98
169
|
CHECK (
|
|
99
|
-
(kind = '
|
|
170
|
+
(kind = 'activity' AND terminated_by IS NULL)
|
|
100
171
|
OR
|
|
101
172
|
(kind = 'loop_termination'
|
|
102
173
|
AND op = 'SEND'
|
|
103
174
|
AND scheme = 'worker'
|
|
104
|
-
AND rx IS NOT NULL
|
|
105
175
|
AND json_valid(rx)
|
|
106
176
|
AND json_type(rx) = 'object'
|
|
107
177
|
AND json_type(rx, '$.status') = 'integer'
|
|
108
178
|
AND json_extract(rx, '$.status') = status_rx)
|
|
109
179
|
),
|
|
110
|
-
FOREIGN KEY (workspace_id)
|
|
111
|
-
FOREIGN KEY (producer_worker_id)
|
|
180
|
+
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
181
|
+
FOREIGN KEY (producer_worker_id) REFERENCES workers(id) ON DELETE CASCADE,
|
|
182
|
+
FOREIGN KEY (target_parent_worker_id) REFERENCES workers(id) ON DELETE CASCADE
|
|
112
183
|
) STRICT;
|
|
113
184
|
|
|
114
185
|
CREATE INDEX IF NOT EXISTS ambient_events_workspace_id_id
|
|
115
186
|
ON ambient_events (workspace_id, id);
|
|
116
187
|
CREATE INDEX IF NOT EXISTS ambient_events_producer_kind_id
|
|
117
188
|
ON ambient_events (producer_worker_id, kind, id);
|
|
189
|
+
CREATE INDEX IF NOT EXISTS ambient_events_parent_id
|
|
190
|
+
ON ambient_events (target_parent_worker_id, id)
|
|
191
|
+
WHERE target_parent_worker_id IS NOT NULL;
|
|
192
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ambient_events_source_identity
|
|
193
|
+
ON ambient_events (producer_worker_id, kind, source_record_id);
|
|
194
|
+
|
|
195
|
+
CREATE TRIGGER IF NOT EXISTS ambient_events_structural_audience
|
|
196
|
+
BEFORE INSERT ON ambient_events
|
|
197
|
+
WHEN NOT EXISTS (
|
|
198
|
+
SELECT 1
|
|
199
|
+
FROM workers producer
|
|
200
|
+
WHERE producer.id = NEW.producer_worker_id
|
|
201
|
+
AND producer.workspace_id = NEW.workspace_id
|
|
202
|
+
AND (
|
|
203
|
+
NEW.target_parent_worker_id IS NULL
|
|
204
|
+
OR NEW.target_parent_worker_id = producer.parent_worker_id
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
BEGIN
|
|
208
|
+
SELECT RAISE(ABORT, 'ambient event audience must be the producer direct parent in the same workspace');
|
|
209
|
+
END;
|
|
210
|
+
|
|
211
|
+
-- A worker begins after the history that predates its existence. This trigger
|
|
212
|
+
-- runs in the worker INSERT statement, so an occurrence is either in the
|
|
213
|
+
-- baseline or after it. Fork INSERTs supply their own cursor/boundary and skip
|
|
214
|
+
-- this ordinary-worker baseline. {§env-delta-log-pull}
|
|
215
|
+
CREATE TRIGGER IF NOT EXISTS workers_capture_ambient_baseline
|
|
216
|
+
AFTER INSERT ON workers
|
|
217
|
+
WHEN NEW.ambient_event_cursor IS NULL
|
|
218
|
+
BEGIN
|
|
219
|
+
UPDATE workers
|
|
220
|
+
SET ambient_event_cursor = COALESCE((
|
|
221
|
+
SELECT MAX(ae.id) FROM ambient_events ae WHERE ae.workspace_id = NEW.workspace_id
|
|
222
|
+
), 0)
|
|
223
|
+
WHERE id = NEW.id;
|
|
224
|
+
END;
|
|
118
225
|
|
|
119
226
|
-- loops
|
|
120
227
|
-- flags: per-loop runtime flags (auto, noProposals, noWeb, noInteraction,
|
|
@@ -128,9 +235,15 @@ CREATE TABLE IF NOT EXISTS loops (
|
|
|
128
235
|
sequence INTEGER NOT NULL CHECK (sequence >= 1),
|
|
129
236
|
status INTEGER NOT NULL DEFAULT 102 CHECK (status IN (100, 102, 200, 202, 413, 429, 499, 500, 504, 508)),
|
|
130
237
|
prompt TEXT NOT NULL,
|
|
238
|
+
-- {§prompt-causal-source}: canonical actor address for the initial prompt;
|
|
239
|
+
-- NULL means the owning worker itself.
|
|
240
|
+
prompt_source TEXT CHECK (prompt_source IS NULL OR length(prompt_source) > 0),
|
|
131
241
|
flags TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(flags)),
|
|
132
|
-
|
|
133
|
-
|
|
242
|
+
-- {§worker-model-selection}: immutable loop snapshots of the resolved model route and the
|
|
243
|
+
-- effective spawn route (was provider_spec/child_provider_spec JSON).
|
|
244
|
+
model_route_id INTEGER REFERENCES model_routes(id),
|
|
245
|
+
spawn_model_route_id INTEGER REFERENCES model_routes(id),
|
|
246
|
+
reasoning_policy TEXT CHECK (reasoning_policy IS NULL OR reasoning_policy IN ('off', 'adaptive', 'low', 'medium', 'high')),
|
|
134
247
|
max_turns INTEGER NOT NULL DEFAULT 50 CHECK (max_turns >= -1),
|
|
135
248
|
-- {§methods-loop-run-open-paths}: the initial prompt frame's selected paths,
|
|
136
249
|
-- held here until turn 1 materializes that frame (string[] JSON).
|
|
@@ -145,6 +258,9 @@ CREATE TABLE IF NOT EXISTS loops (
|
|
|
145
258
|
-- {§loop-terminal-authorship}: 'cancel' names an external loop.cancel;
|
|
146
259
|
-- NULL covers model terminals and engine verdicts whose result carries the story.
|
|
147
260
|
terminated_by TEXT CHECK (terminated_by IS NULL OR terminated_by = 'cancel'),
|
|
261
|
+
CONSTRAINT loops_generation_policy_contract CHECK (
|
|
262
|
+
(model_route_id IS NULL) = (reasoning_policy IS NULL)
|
|
263
|
+
),
|
|
148
264
|
CONSTRAINT loops_terminal_result_contract CHECK (
|
|
149
265
|
CASE
|
|
150
266
|
WHEN status IN (100, 102, 202) THEN terminal_result IS NULL
|
|
@@ -191,7 +307,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS loops_worker_id_sequence ON loops (worker_id,
|
|
|
191
307
|
CREATE UNIQUE INDEX IF NOT EXISTS loops_orphan_source_loop_id ON loops (orphan_source_loop_id);
|
|
192
308
|
-- {§worker-scheme}: a loop crossing into a terminal status stamps terminated_at, so sibling
|
|
193
309
|
-- workers pull the termination as a folded ambient delta — caught uniformly across every
|
|
194
|
-
-- death-path (SEND,
|
|
310
|
+
-- death-path (SEND, overflow recovery, max-turns, strike, KILL). The stamp updates terminated_at,
|
|
195
311
|
-- never status, so it cannot re-fire this trigger. Terminals: 200 done · 413 budget ·
|
|
196
312
|
-- 429 turn-ceiling · 499 cancel · 500 fail · 504 wall-clock timeout · 508 runaway. (202 = parked/sleeping, NOT terminal.)
|
|
197
313
|
CREATE TRIGGER IF NOT EXISTS loops_stamp_terminated_at
|
|
@@ -201,22 +317,33 @@ BEGIN
|
|
|
201
317
|
UPDATE loops SET terminated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = NEW.id;
|
|
202
318
|
END;
|
|
203
319
|
|
|
204
|
-
-- A terminal transition is an
|
|
205
|
-
--
|
|
320
|
+
-- A child terminal transition is an occurrence addressed only to its direct
|
|
321
|
+
-- parent. Directly inserted fork history never crosses this transition and
|
|
206
322
|
-- therefore cannot fabricate a new conclusion event.
|
|
207
323
|
CREATE TRIGGER IF NOT EXISTS loops_append_ambient_event
|
|
208
324
|
AFTER UPDATE OF status ON loops
|
|
209
325
|
WHEN NEW.status IN (200, 413, 429, 499, 500, 504, 508) AND OLD.status NOT IN (200, 413, 429, 499, 500, 504, 508)
|
|
210
326
|
BEGIN
|
|
211
327
|
INSERT INTO ambient_events (
|
|
212
|
-
workspace_id, producer_worker_id,
|
|
213
|
-
|
|
328
|
+
workspace_id, producer_worker_id, target_parent_worker_id,
|
|
329
|
+
workspace_broadcast, kind, source_record_id, source,
|
|
330
|
+
op, delimiter, scheme, pathname,
|
|
331
|
+
tx, mimetype_tx, rx, mimetype_rx, status_rx, state, terminated_by
|
|
214
332
|
)
|
|
215
|
-
SELECT w.workspace_id, NEW.worker_id,
|
|
216
|
-
|
|
217
|
-
|
|
333
|
+
SELECT w.workspace_id, NEW.worker_id, w.parent_worker_id,
|
|
334
|
+
0, 'loop_termination', NEW.id, NULL,
|
|
335
|
+
'SEND', '', 'worker', '/' || w.name,
|
|
336
|
+
'', 'text/plain', NEW.terminal_result, 'application/json',
|
|
337
|
+
json_extract(NEW.terminal_result, '$.status'), 'resolved', NEW.terminated_by
|
|
218
338
|
FROM workers w
|
|
219
|
-
WHERE w.id = NEW.worker_id
|
|
339
|
+
WHERE w.id = NEW.worker_id
|
|
340
|
+
AND w.parent_worker_id IS NOT NULL
|
|
341
|
+
AND EXISTS (
|
|
342
|
+
SELECT 1
|
|
343
|
+
FROM turns t
|
|
344
|
+
WHERE t.loop_id = NEW.id
|
|
345
|
+
AND NOT (t.producer = '_plurnk' AND t.kind = 'operation')
|
|
346
|
+
);
|
|
220
347
|
END;
|
|
221
348
|
|
|
222
349
|
-- turns
|
|
@@ -229,7 +356,15 @@ CREATE TABLE IF NOT EXISTS turns (
|
|
|
229
356
|
loop_id INTEGER NOT NULL,
|
|
230
357
|
sequence INTEGER NOT NULL CHECK (sequence >= 1),
|
|
231
358
|
timestamp TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
359
|
+
-- {§turn-record}: producer and purpose define the turn. Packet/provider
|
|
360
|
+
-- evidence below is an optional inference specialization.
|
|
361
|
+
producer TEXT NOT NULL CHECK (producer IN ('model', 'client', '_plurnk', 'plugin')),
|
|
362
|
+
kind TEXT NOT NULL CHECK (kind IN ('inference', 'initialization', 'overflow', 'operation', 'maintenance')),
|
|
232
363
|
status INTEGER NOT NULL CHECK (status BETWEEN 100 AND 599),
|
|
364
|
+
-- NULL while the producer still owns this turn. Status 102 is both the
|
|
365
|
+
-- provisional running value and the exact completed continue disposition;
|
|
366
|
+
-- completed_at distinguishes those states without inventing another status.
|
|
367
|
+
completed_at TEXT DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
233
368
|
-- Provider-derived curation calibration for this turn; NULL when input
|
|
234
369
|
-- capacity is unknown. {§tokenomics-client-gauge}
|
|
235
370
|
usage_curation_budget INTEGER CHECK (usage_curation_budget IS NULL OR usage_curation_budget >= 1),
|
|
@@ -264,16 +399,59 @@ CREATE TABLE IF NOT EXISTS turns (
|
|
|
264
399
|
END
|
|
265
400
|
),
|
|
266
401
|
finish_reason TEXT,
|
|
267
|
-
model TEXT
|
|
268
|
-
-- Opaque provider→client metadata plus engine rail keys;
|
|
269
|
-
--
|
|
270
|
-
meta TEXT
|
|
402
|
+
model TEXT CHECK (model IS NULL OR length(model) >= 1),
|
|
403
|
+
-- Opaque provider→client metadata plus engine rail keys; absent outside
|
|
404
|
+
-- recorded inference evidence. {§meta-passthrough}, {§rail-truth-engine-verdict}
|
|
405
|
+
meta TEXT CHECK (meta IS NULL OR json_valid(meta)),
|
|
406
|
+
CHECK (completed_at IS NOT NULL OR status = 102),
|
|
407
|
+
CHECK ((producer = 'model') = (kind = 'inference')),
|
|
408
|
+
CHECK (kind NOT IN ('initialization', 'overflow') OR producer = '_plurnk'),
|
|
409
|
+
CHECK (
|
|
410
|
+
kind = 'inference'
|
|
411
|
+
OR (
|
|
412
|
+
usage_curation_budget IS NULL
|
|
413
|
+
AND packet IS NULL
|
|
414
|
+
AND finish_reason IS NULL
|
|
415
|
+
AND model IS NULL
|
|
416
|
+
AND meta IS NULL
|
|
417
|
+
)
|
|
418
|
+
),
|
|
271
419
|
FOREIGN KEY (loop_id) REFERENCES loops(id) ON DELETE CASCADE
|
|
272
420
|
) STRICT;
|
|
273
421
|
|
|
274
422
|
CREATE UNIQUE INDEX IF NOT EXISTS turns_loop_id_sequence ON turns (loop_id, sequence);
|
|
275
423
|
CREATE INDEX IF NOT EXISTS turns_timestamp ON turns (timestamp);
|
|
276
424
|
|
|
425
|
+
-- Producer and purpose are immutable except for the single pre-inference
|
|
426
|
+
-- diversion into overflow recovery. The transition is allowed only before any
|
|
427
|
+
-- model or non-kernel evidence exists.
|
|
428
|
+
CREATE TRIGGER IF NOT EXISTS turns_identity_forward_only
|
|
429
|
+
BEFORE UPDATE OF producer, kind ON turns
|
|
430
|
+
WHEN NOT (
|
|
431
|
+
OLD.producer = NEW.producer
|
|
432
|
+
AND OLD.kind = NEW.kind
|
|
433
|
+
)
|
|
434
|
+
AND NOT (
|
|
435
|
+
OLD.producer = 'model'
|
|
436
|
+
AND OLD.kind = 'inference'
|
|
437
|
+
AND NEW.producer = '_plurnk'
|
|
438
|
+
AND NEW.kind = 'overflow'
|
|
439
|
+
AND OLD.completed_at IS NULL
|
|
440
|
+
AND OLD.packet IS NULL
|
|
441
|
+
AND OLD.usage_curation_budget IS NULL
|
|
442
|
+
AND OLD.finish_reason IS NULL
|
|
443
|
+
AND OLD.model IS NULL
|
|
444
|
+
AND OLD.meta IS NULL
|
|
445
|
+
AND NOT EXISTS (SELECT 1 FROM model_calls WHERE turn_id = OLD.id)
|
|
446
|
+
AND NOT EXISTS (
|
|
447
|
+
SELECT 1 FROM log_entries
|
|
448
|
+
WHERE turn_id = OLD.id AND origin != '_plurnk'
|
|
449
|
+
)
|
|
450
|
+
)
|
|
451
|
+
BEGIN
|
|
452
|
+
SELECT RAISE(ABORT, 'turn producer and kind are immutable outside pre-inference overflow diversion');
|
|
453
|
+
END;
|
|
454
|
+
|
|
277
455
|
-- One logical provider.generate call. Emission attempts and BARE inferences
|
|
278
456
|
-- share response/failure evidence and cardinal physical request accounting;
|
|
279
457
|
-- operation-specific semantics live in their specializing relations.
|
|
@@ -321,6 +499,17 @@ CREATE TABLE IF NOT EXISTS model_calls (
|
|
|
321
499
|
|
|
322
500
|
CREATE INDEX IF NOT EXISTS model_calls_turn_id ON model_calls (turn_id, sequence);
|
|
323
501
|
|
|
502
|
+
CREATE TRIGGER IF NOT EXISTS model_calls_inference_turn_only
|
|
503
|
+
BEFORE INSERT ON model_calls
|
|
504
|
+
WHEN COALESCE((
|
|
505
|
+
SELECT producer = 'model' AND kind = 'inference'
|
|
506
|
+
FROM turns
|
|
507
|
+
WHERE id = NEW.turn_id
|
|
508
|
+
), 0) != 1
|
|
509
|
+
BEGIN
|
|
510
|
+
SELECT RAISE(ABORT, 'model call requires a model inference turn');
|
|
511
|
+
END;
|
|
512
|
+
|
|
324
513
|
CREATE TRIGGER IF NOT EXISTS model_calls_request_identity_immutable
|
|
325
514
|
BEFORE UPDATE OF turn_id, sequence, kind, attributions, timestamp ON model_calls
|
|
326
515
|
BEGIN
|
|
@@ -532,9 +721,9 @@ BEGIN
|
|
|
532
721
|
END;
|
|
533
722
|
|
|
534
723
|
-- derivations
|
|
535
|
-
-- Content-addressed deep projections.
|
|
536
|
-
-- deep_hash; graph, FTS, and vectors are stored once
|
|
537
|
-
--
|
|
724
|
+
-- Content-addressed deep projections. Entry channels and log projections point
|
|
725
|
+
-- at a COMPLETE artifact by deep_hash; graph, FTS, and vectors are stored once
|
|
726
|
+
-- regardless of how many addresses carry identical content under the same reader/config.
|
|
538
727
|
-- A building row is unattached and safely replaceable after interruption.
|
|
539
728
|
CREATE TABLE IF NOT EXISTS derivations (
|
|
540
729
|
id INTEGER NOT NULL PRIMARY KEY,
|
|
@@ -543,19 +732,30 @@ CREATE TABLE IF NOT EXISTS derivations (
|
|
|
543
732
|
disposition TEXT CHECK (disposition IN ('vector', 'lexical', 'excluded', 'nonsemantic', 'failed')),
|
|
544
733
|
reason TEXT,
|
|
545
734
|
parse_issues INTEGER CHECK (parse_issues IS NULL OR parse_issues > 0),
|
|
735
|
+
summary TEXT CHECK (
|
|
736
|
+
summary IS NULL OR (
|
|
737
|
+
length(summary) > 0
|
|
738
|
+
AND summary = trim(summary)
|
|
739
|
+
AND instr(summary, char(10)) = 0
|
|
740
|
+
AND instr(summary, char(13)) = 0
|
|
741
|
+
)
|
|
742
|
+
),
|
|
546
743
|
CHECK ((state = 'building' AND disposition IS NULL) OR (state = 'complete' AND disposition IS NOT NULL))
|
|
547
744
|
) STRICT;
|
|
548
745
|
|
|
549
746
|
-- entries
|
|
550
|
-
-- The canonical addressable store. (
|
|
747
|
+
-- The canonical addressable store. (owner, scheme, authority, pathname) is the identity
|
|
551
748
|
-- tuple, and NO component may be NULL: NULLs are distinct under SQL UNIQUE, so a nullable
|
|
552
749
|
-- component voids the identity index. Bare/file paths persist under the reserved `file`
|
|
553
750
|
-- scheme; they still render as bare paths. {§entry-identity-no-null}
|
|
554
751
|
CREATE TABLE IF NOT EXISTS entries (
|
|
555
752
|
id INTEGER NOT NULL PRIMARY KEY,
|
|
556
753
|
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
|
|
557
|
-
workspace_id INTEGER,
|
|
558
754
|
scheme TEXT NOT NULL CHECK (length(scheme) > 0),
|
|
755
|
+
-- Canonical RFC authority for resource-addressed schemes. Namespace schemes
|
|
756
|
+
-- fold authored authority into pathname; owner-addressed schemes consume it
|
|
757
|
+
-- into owner_id. Empty is therefore the canonical no-resource-authority value.
|
|
758
|
+
authority TEXT NOT NULL DEFAULT '',
|
|
559
759
|
pathname TEXT NOT NULL,
|
|
560
760
|
-- {§entry-owner} — every entry is owned by a worker: the spawning worker for capability
|
|
561
761
|
-- streams, the workspace's reserved 'commons' worker for shared content. A real row, never
|
|
@@ -569,14 +769,9 @@ CREATE TABLE IF NOT EXISTS entries (
|
|
|
569
769
|
-- SPEC {§membership} — how a file member entered the curated surface. 'git' rows are
|
|
570
770
|
-- reconciled against the repo's members each turn — tracked ls-files PLUS untracked-
|
|
571
771
|
-- but-not-ignored files ({§membership-auto-add}) — registered + un-registered so entries
|
|
572
|
-
-- == members; '
|
|
772
|
+
-- == members; 'constraint' rows are reconciled from ordinary pick policy.
|
|
573
773
|
-- NULL = not a file member (other schemes don't carry origin).
|
|
574
|
-
membership_origin TEXT CHECK (membership_origin IS NULL OR membership_origin IN ('git', '
|
|
575
|
-
-- {§membership-change-gated-sync}: hash of the body content at the last
|
|
576
|
-
-- deep-channel derivation. The manifest-add pass re-derives symbols/refs (and
|
|
577
|
-
-- embeddings, later) ONLY when this differs from the current body hash — an
|
|
578
|
-
-- unchanged entry is skipped, never re-metadatafied every turn.
|
|
579
|
-
deep_hash TEXT,
|
|
774
|
+
membership_origin TEXT CHECK (membership_origin IS NULL OR membership_origin IN ('git', 'constraint')),
|
|
580
775
|
-- SPEC {§membership-change-gated-sync} — the per-member sync stat-detect:
|
|
581
776
|
-- "<mtimeMs>:<size>" of the disk file at its last materialization, or `absent`
|
|
582
777
|
-- after an observed deletion. The pre-turn
|
|
@@ -584,20 +779,19 @@ CREATE TABLE IF NOT EXISTS entries (
|
|
|
584
779
|
-- signature changed; an unchanged member is a no-op. NULL = never synced.
|
|
585
780
|
synced_sig TEXT,
|
|
586
781
|
-- User Note 5 — manifest cache-friendliness. Last-modified stamp, bumped on every
|
|
587
|
-
--
|
|
588
|
-
--
|
|
782
|
+
-- addressable representation change; engine_list_workspace_entries orders the catalog
|
|
783
|
+
-- by it ASC so dormant entries hold the stable prompt-cache prefix. Private derivation
|
|
784
|
+
-- attachment does not make an entry recently touched.
|
|
589
785
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
590
|
-
|
|
591
|
-
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
592
|
-
FOREIGN KEY (owner_id) REFERENCES workers(id) ON DELETE CASCADE,
|
|
593
|
-
FOREIGN KEY (deep_hash) REFERENCES derivations(deep_hash)
|
|
786
|
+
FOREIGN KEY (owner_id) REFERENCES workers(id) ON DELETE CASCADE
|
|
594
787
|
) STRICT;
|
|
595
788
|
|
|
596
789
|
-- {§entry-owner} / {§stream-owner-scoped} — owner is the sole visibility and identity axis.
|
|
597
790
|
-- Concurrent workers' capability streams share the loop-relative coordinate (every worker's first
|
|
598
791
|
-- loop is seq 1), so identity keys on the owner and identical coordinates are distinct rows
|
|
599
792
|
-- ({§stream-owner-scoped}).
|
|
600
|
-
CREATE UNIQUE INDEX IF NOT EXISTS entries_identity ON entries (
|
|
793
|
+
CREATE UNIQUE INDEX IF NOT EXISTS entries_identity ON entries (owner_id, scheme, authority, pathname);
|
|
794
|
+
CREATE UNIQUE INDEX IF NOT EXISTS entries_id_owner ON entries (id, owner_id);
|
|
601
795
|
|
|
602
796
|
-- The ONE engine-imposed constraint (SPEC {§stream-constraints}, {§stream-constraints-engine-one-cap}): 100 MiB char-length cap
|
|
603
797
|
-- per channel content body. All other limits are extrinsic.
|
|
@@ -610,6 +804,9 @@ CREATE TABLE IF NOT EXISTS entry_channels (
|
|
|
610
804
|
-- Content identity: sha256 of content, stamped at static writes; streamed
|
|
611
805
|
-- appends leave it NULL. Curation weight remains model-independent.
|
|
612
806
|
content_hash TEXT,
|
|
807
|
+
-- Search derivation for this exact addressable channel representation.
|
|
808
|
+
-- Content or mimetype changes invalidate it at the owning channel row.
|
|
809
|
+
deep_hash TEXT,
|
|
613
810
|
state TEXT NOT NULL DEFAULT 'static' CHECK (state IN ('static', 'active', 'closed', 'errored')),
|
|
614
811
|
-- Exact terminal producer evidence for this representation channel. NULL is
|
|
615
812
|
-- the ordinary implicit {status:200}; selection/projection metadata belongs
|
|
@@ -642,12 +839,26 @@ CREATE TABLE IF NOT EXISTS entry_channels (
|
|
|
642
839
|
END
|
|
643
840
|
),
|
|
644
841
|
PRIMARY KEY (entry_id, name),
|
|
645
|
-
FOREIGN KEY (entry_id) REFERENCES entries(id) ON DELETE CASCADE
|
|
842
|
+
FOREIGN KEY (entry_id) REFERENCES entries(id) ON DELETE CASCADE,
|
|
843
|
+
FOREIGN KEY (deep_hash) REFERENCES derivations(deep_hash)
|
|
646
844
|
) STRICT, WITHOUT ROWID;
|
|
647
845
|
|
|
648
|
-
--
|
|
649
|
-
--
|
|
650
|
-
--
|
|
846
|
+
-- A changed channel representation cannot retain search evidence derived from
|
|
847
|
+
-- its predecessor. This trigger is the one invalidation owner for every write
|
|
848
|
+
-- path, including model EDIT, plugin channel capabilities, and streams.
|
|
849
|
+
CREATE TRIGGER IF NOT EXISTS entry_channels_invalidate_derivation
|
|
850
|
+
AFTER UPDATE OF content, mimetype ON entry_channels
|
|
851
|
+
WHEN OLD.content IS NOT NEW.content OR OLD.mimetype IS NOT NEW.mimetype
|
|
852
|
+
BEGIN
|
|
853
|
+
UPDATE entry_channels
|
|
854
|
+
SET deep_hash = NULL
|
|
855
|
+
WHERE entry_id = NEW.entry_id AND name = NEW.name AND deep_hash IS NOT NULL;
|
|
856
|
+
END;
|
|
857
|
+
|
|
858
|
+
-- User Note 5 — bump the entry's updated_at on addressable representation or
|
|
859
|
+
-- lifecycle writes so the catalog (ordered by updated_at ASC) keeps recently-
|
|
860
|
+
-- touched entries at the tail and holds the prompt-cache prefix stable across
|
|
861
|
+
-- turns. Content hashes and search attachments are private metadata, not touches.
|
|
651
862
|
CREATE TRIGGER IF NOT EXISTS entries_touch_on_channel_write
|
|
652
863
|
AFTER INSERT ON entry_channels
|
|
653
864
|
BEGIN
|
|
@@ -655,7 +866,7 @@ BEGIN
|
|
|
655
866
|
END;
|
|
656
867
|
|
|
657
868
|
CREATE TRIGGER IF NOT EXISTS entries_touch_on_channel_update
|
|
658
|
-
AFTER UPDATE ON entry_channels
|
|
869
|
+
AFTER UPDATE OF content, mimetype, weight, state, producer_result ON entry_channels
|
|
659
870
|
BEGIN
|
|
660
871
|
UPDATE entries SET updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = NEW.entry_id;
|
|
661
872
|
END;
|
|
@@ -731,7 +942,8 @@ CREATE TABLE IF NOT EXISTS derivation_embeddings (
|
|
|
731
942
|
-- status⊥state: status is the HTTP outcome, state is where in the
|
|
732
943
|
-- lifecycle the entry sits. Most rows write 'resolved' directly;
|
|
733
944
|
-- proposing schemes transition 'proposed' → resolved/failed/cancelled.
|
|
734
|
-
--
|
|
945
|
+
-- folded: canonical hidden log-body line intervals for OPEN/FOLD. [] is
|
|
946
|
+
-- wholly open and [[1,-1]] is wholly folded.
|
|
735
947
|
CREATE TABLE IF NOT EXISTS log_entries (
|
|
736
948
|
id INTEGER NOT NULL PRIMARY KEY,
|
|
737
949
|
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
|
|
@@ -741,30 +953,38 @@ CREATE TABLE IF NOT EXISTS log_entries (
|
|
|
741
953
|
turn_id INTEGER NOT NULL,
|
|
742
954
|
sequence INTEGER NOT NULL CHECK (sequence >= 1),
|
|
743
955
|
at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
744
|
-
origin TEXT NOT NULL CHECK (origin IN ('model', 'client', '
|
|
956
|
+
origin TEXT NOT NULL CHECK (origin IN ('model', 'client', '_plurnk', 'plugin')),
|
|
745
957
|
-- {§env-delta-attribution}: a worker:// name or stable subsystem token ('file');
|
|
746
958
|
-- NULL = the owning worker itself, rendered without causal attribution.
|
|
747
959
|
source TEXT,
|
|
748
960
|
-- Engine-owned occurrence identity. Source rows are stamped NULL→id by the
|
|
749
961
|
-- journal trigger; observer and fork copies carry it at insertion.
|
|
750
962
|
ambient_event_id INTEGER REFERENCES ambient_events(id),
|
|
963
|
+
-- Fork history is copied evidence, never new activity. Rows with no source
|
|
964
|
+
-- occurrence still need this structural guard against republication.
|
|
965
|
+
inherited_history INTEGER NOT NULL DEFAULT 0 CHECK (inherited_history IN (0, 1)),
|
|
751
966
|
-- Search derivation attached to this durable log result, when available.
|
|
752
967
|
deep_hash TEXT REFERENCES derivations(deep_hash),
|
|
753
|
-
-- Exact logical provider call represented by a BARE result
|
|
754
|
-
--
|
|
968
|
+
-- Exact logical provider call represented by a BARE result, admitted
|
|
969
|
+
-- model turnOps, or rejected emissionAttempt. Other operation and ambient
|
|
970
|
+
-- rows carry no model-call identity.
|
|
755
971
|
model_call_id INTEGER REFERENCES model_calls(id),
|
|
972
|
+
-- Engine-owned stream publication that produced this observation. The
|
|
973
|
+
-- publication cursor survives log curation; this relation is evidence,
|
|
974
|
+
-- never the acknowledgement itself. {§exec-stream}
|
|
975
|
+
subscription_publication_id INTEGER REFERENCES subscription_publications(id) ON DELETE SET NULL,
|
|
756
976
|
|
|
757
977
|
-- 'error' is an ACTIONLESS row ({§operation-results} — errors are log items): a parse failure that
|
|
758
978
|
-- produced no op still records a log entry (op='error', status_rx≥400, no target) so the model
|
|
759
979
|
-- can fold/kill/recall its own mistakes like any other log row — one budget surface, the log.
|
|
760
|
-
--
|
|
761
|
-
-- ({§
|
|
980
|
+
-- Exact producer source and rejected-attempt artifacts carry NULL here
|
|
981
|
+
-- ({§turn-ops-entry}, {§rejected-emission-entry}).
|
|
762
982
|
-- No op enum here: the grammar op set is grammar's contract (PlurnkOp), and this column is written
|
|
763
983
|
-- only by the PlurnkOp-typed engine (grammar ops), service row selectors, or NULL for no op.
|
|
764
984
|
-- A SQL enum would be a hand-copy of grammar's op list that silently goes stale on every new verb
|
|
765
985
|
-- (it did — FORK/WORK). Validity lives at the parse + type layer, not duplicated in DDL.
|
|
766
986
|
op TEXT,
|
|
767
|
-
|
|
987
|
+
delimiter TEXT NOT NULL DEFAULT '',
|
|
768
988
|
signal TEXT CHECK (signal IS NULL OR json_valid(signal)),
|
|
769
989
|
|
|
770
990
|
scheme TEXT CHECK (scheme IS NULL OR length(scheme) > 0),
|
|
@@ -795,11 +1015,16 @@ CREATE TABLE IF NOT EXISTS log_entries (
|
|
|
795
1015
|
outcome TEXT,
|
|
796
1016
|
attrs TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(attrs)),
|
|
797
1017
|
|
|
798
|
-
|
|
1018
|
+
folded TEXT NOT NULL DEFAULT '[]'
|
|
1019
|
+
CHECK (json_valid(folded) AND json_type(folded) = 'array'),
|
|
799
1020
|
|
|
800
|
-
CHECK (
|
|
801
|
-
|
|
802
|
-
|
|
1021
|
+
CHECK (
|
|
1022
|
+
(op IS NULL) = COALESCE(
|
|
1023
|
+
json_extract(attrs, '$.kind') IN ('turnOps', 'emissionAttempt'),
|
|
1024
|
+
0
|
|
1025
|
+
)
|
|
1026
|
+
),
|
|
1027
|
+
CHECK (json_extract(attrs, '$.kind') != 'emissionAttempt' OR origin = 'model'),
|
|
803
1028
|
|
|
804
1029
|
FOREIGN KEY (worker_id) REFERENCES workers(id) ON DELETE CASCADE,
|
|
805
1030
|
FOREIGN KEY (loop_id) REFERENCES loops(id) ON DELETE CASCADE,
|
|
@@ -813,10 +1038,115 @@ CREATE INDEX IF NOT EXISTS log_entries_at ON log_entries (a
|
|
|
813
1038
|
CREATE UNIQUE INDEX IF NOT EXISTS log_entries_model_call_id
|
|
814
1039
|
ON log_entries (model_call_id)
|
|
815
1040
|
WHERE model_call_id IS NOT NULL;
|
|
1041
|
+
CREATE INDEX IF NOT EXISTS log_entries_subscription_publication_id
|
|
1042
|
+
ON log_entries (subscription_publication_id)
|
|
1043
|
+
WHERE subscription_publication_id IS NOT NULL;
|
|
816
1044
|
CREATE UNIQUE INDEX IF NOT EXISTS log_entries_worker_ambient_event
|
|
817
1045
|
ON log_entries (worker_id, ambient_event_id)
|
|
818
1046
|
WHERE ambient_event_id IS NOT NULL;
|
|
819
1047
|
|
|
1048
|
+
-- A log row belongs to one exact worker/loop/turn chain. Its writer is either
|
|
1049
|
+
-- that turn's producer or `_plurnk` observing the turn. Primitive absence,
|
|
1050
|
+
-- enum, and foreign-key failures remain owned by their column constraints.
|
|
1051
|
+
CREATE TRIGGER IF NOT EXISTS log_entries_turn_ownership
|
|
1052
|
+
BEFORE INSERT ON log_entries
|
|
1053
|
+
WHEN NEW.worker_id IS NOT NULL
|
|
1054
|
+
AND NEW.loop_id IS NOT NULL
|
|
1055
|
+
AND NEW.turn_id IS NOT NULL
|
|
1056
|
+
AND NEW.origin IN ('model', 'client', '_plurnk', 'plugin')
|
|
1057
|
+
AND EXISTS (SELECT 1 FROM workers WHERE id = NEW.worker_id)
|
|
1058
|
+
AND EXISTS (SELECT 1 FROM loops WHERE id = NEW.loop_id)
|
|
1059
|
+
AND EXISTS (SELECT 1 FROM turns WHERE id = NEW.turn_id)
|
|
1060
|
+
AND NOT EXISTS (
|
|
1061
|
+
SELECT 1
|
|
1062
|
+
FROM turns
|
|
1063
|
+
JOIN loops ON loops.id = turns.loop_id
|
|
1064
|
+
WHERE turns.id = NEW.turn_id
|
|
1065
|
+
AND loops.id = NEW.loop_id
|
|
1066
|
+
AND loops.worker_id = NEW.worker_id
|
|
1067
|
+
AND (NEW.origin = turns.producer OR NEW.origin = '_plurnk')
|
|
1068
|
+
)
|
|
1069
|
+
BEGIN
|
|
1070
|
+
SELECT RAISE(ABORT, 'log entry must match its turn ownership and producer');
|
|
1071
|
+
END;
|
|
1072
|
+
|
|
1073
|
+
-- {§turn-ops-entry} — exact admitted source is authored by the turn producer.
|
|
1074
|
+
-- `_plurnk` may observe another producer's turn through ordinary operation
|
|
1075
|
+
-- rows, but it cannot impersonate that producer's source program.
|
|
1076
|
+
CREATE TRIGGER IF NOT EXISTS log_entries_turn_ops_producer
|
|
1077
|
+
BEFORE INSERT ON log_entries
|
|
1078
|
+
WHEN NEW.op IS NULL
|
|
1079
|
+
AND json_extract(NEW.attrs, '$.kind') = 'turnOps'
|
|
1080
|
+
AND EXISTS (SELECT 1 FROM turns WHERE id = NEW.turn_id)
|
|
1081
|
+
AND NOT EXISTS (
|
|
1082
|
+
SELECT 1
|
|
1083
|
+
FROM turns
|
|
1084
|
+
WHERE turns.id = NEW.turn_id
|
|
1085
|
+
AND turns.producer = NEW.origin
|
|
1086
|
+
)
|
|
1087
|
+
BEGIN
|
|
1088
|
+
SELECT RAISE(ABORT, 'actionless log entry does not match its turn producer');
|
|
1089
|
+
END;
|
|
1090
|
+
|
|
1091
|
+
-- A folded interval is an inclusive [start,end] pair. Ranges are positive,
|
|
1092
|
+
-- sorted, disjoint, and non-adjacent; -1 is the final open-ended endpoint.
|
|
1093
|
+
-- Canonical intervals make visibility equality and curation effects exact.
|
|
1094
|
+
CREATE TRIGGER IF NOT EXISTS log_entries_folded_valid_insert
|
|
1095
|
+
BEFORE INSERT ON log_entries
|
|
1096
|
+
WHEN EXISTS (
|
|
1097
|
+
SELECT 1
|
|
1098
|
+
FROM json_each(NEW.folded) range
|
|
1099
|
+
WHERE range.type != 'array'
|
|
1100
|
+
OR json_array_length(range.value) != 2
|
|
1101
|
+
OR COALESCE(json_type(range.value, '$[0]'), '') != 'integer'
|
|
1102
|
+
OR COALESCE(json_type(range.value, '$[1]'), '') != 'integer'
|
|
1103
|
+
OR json_extract(range.value, '$[0]') < 1
|
|
1104
|
+
OR (
|
|
1105
|
+
json_extract(range.value, '$[1]') != -1
|
|
1106
|
+
AND json_extract(range.value, '$[1]') < json_extract(range.value, '$[0]')
|
|
1107
|
+
)
|
|
1108
|
+
OR EXISTS (
|
|
1109
|
+
SELECT 1
|
|
1110
|
+
FROM json_each(NEW.folded) previous
|
|
1111
|
+
WHERE previous.key = range.key - 1
|
|
1112
|
+
AND (
|
|
1113
|
+
json_extract(previous.value, '$[1]') = -1
|
|
1114
|
+
OR json_extract(range.value, '$[0]') <= json_extract(previous.value, '$[1]') + 1
|
|
1115
|
+
)
|
|
1116
|
+
)
|
|
1117
|
+
)
|
|
1118
|
+
BEGIN
|
|
1119
|
+
SELECT RAISE(ABORT, 'log entry folded ranges are invalid');
|
|
1120
|
+
END;
|
|
1121
|
+
|
|
1122
|
+
CREATE TRIGGER IF NOT EXISTS log_entries_folded_valid_update
|
|
1123
|
+
BEFORE UPDATE OF folded ON log_entries
|
|
1124
|
+
WHEN EXISTS (
|
|
1125
|
+
SELECT 1
|
|
1126
|
+
FROM json_each(NEW.folded) range
|
|
1127
|
+
WHERE range.type != 'array'
|
|
1128
|
+
OR json_array_length(range.value) != 2
|
|
1129
|
+
OR COALESCE(json_type(range.value, '$[0]'), '') != 'integer'
|
|
1130
|
+
OR COALESCE(json_type(range.value, '$[1]'), '') != 'integer'
|
|
1131
|
+
OR json_extract(range.value, '$[0]') < 1
|
|
1132
|
+
OR (
|
|
1133
|
+
json_extract(range.value, '$[1]') != -1
|
|
1134
|
+
AND json_extract(range.value, '$[1]') < json_extract(range.value, '$[0]')
|
|
1135
|
+
)
|
|
1136
|
+
OR EXISTS (
|
|
1137
|
+
SELECT 1
|
|
1138
|
+
FROM json_each(NEW.folded) previous
|
|
1139
|
+
WHERE previous.key = range.key - 1
|
|
1140
|
+
AND (
|
|
1141
|
+
json_extract(previous.value, '$[1]') = -1
|
|
1142
|
+
OR json_extract(range.value, '$[0]') <= json_extract(previous.value, '$[1]') + 1
|
|
1143
|
+
)
|
|
1144
|
+
)
|
|
1145
|
+
)
|
|
1146
|
+
BEGIN
|
|
1147
|
+
SELECT RAISE(ABORT, 'log entry folded ranges are invalid');
|
|
1148
|
+
END;
|
|
1149
|
+
|
|
820
1150
|
CREATE TRIGGER IF NOT EXISTS log_entries_model_call_valid
|
|
821
1151
|
BEFORE INSERT ON log_entries
|
|
822
1152
|
WHEN NEW.model_call_id IS NOT NULL
|
|
@@ -831,7 +1161,7 @@ WHEN NEW.model_call_id IS NOT NULL
|
|
|
831
1161
|
OR (
|
|
832
1162
|
call.kind = 'emission'
|
|
833
1163
|
AND NEW.op IS NULL
|
|
834
|
-
AND json_extract(NEW.attrs, '$.kind')
|
|
1164
|
+
AND json_extract(NEW.attrs, '$.kind') IN ('turnOps', 'emissionAttempt')
|
|
835
1165
|
)
|
|
836
1166
|
)
|
|
837
1167
|
)
|
|
@@ -930,19 +1260,93 @@ BEGIN
|
|
|
930
1260
|
SELECT json_group_array(tag)
|
|
931
1261
|
FROM (SELECT tag FROM log_tags WHERE log_entry_id = NEW.id ORDER BY tag)
|
|
932
1262
|
), '[]')
|
|
933
|
-
WHERE kind = '
|
|
1263
|
+
WHERE kind = 'activity'
|
|
1264
|
+
AND producer_worker_id = NEW.worker_id
|
|
1265
|
+
AND source_record_id = NEW.id;
|
|
1266
|
+
END;
|
|
1267
|
+
|
|
1268
|
+
-- {§worker-initialization-entry} {§overflow-turn-script} — the turn identity
|
|
1269
|
+
-- classifies every row in a kernel-authored recovery/orientation turn. No
|
|
1270
|
+
-- caller can omit provenance from either its exact turnOps or its operation
|
|
1271
|
+
-- outcomes.
|
|
1272
|
+
CREATE TRIGGER IF NOT EXISTS log_entries_classify_plurnk_turn
|
|
1273
|
+
AFTER INSERT ON log_entries
|
|
1274
|
+
WHEN EXISTS (
|
|
1275
|
+
SELECT 1
|
|
1276
|
+
FROM turns
|
|
1277
|
+
WHERE turns.id = NEW.turn_id
|
|
1278
|
+
AND turns.producer = '_plurnk'
|
|
1279
|
+
AND turns.kind IN ('initialization', 'overflow')
|
|
1280
|
+
)
|
|
1281
|
+
BEGIN
|
|
1282
|
+
INSERT OR IGNORE INTO log_tags (log_entry_id, tag)
|
|
1283
|
+
VALUES (NEW.id, '_plurnk');
|
|
1284
|
+
INSERT OR IGNORE INTO log_tags (log_entry_id, tag)
|
|
1285
|
+
SELECT NEW.id, CASE turns.kind
|
|
1286
|
+
WHEN 'initialization' THEN 'init'
|
|
1287
|
+
ELSE 'overflow'
|
|
1288
|
+
END
|
|
1289
|
+
FROM turns
|
|
1290
|
+
WHERE turns.id = NEW.turn_id;
|
|
1291
|
+
|
|
1292
|
+
UPDATE ambient_events
|
|
1293
|
+
SET tags = COALESCE((
|
|
1294
|
+
SELECT json_group_array(tag)
|
|
1295
|
+
FROM (SELECT tag FROM log_tags WHERE log_entry_id = NEW.id ORDER BY tag)
|
|
1296
|
+
), '[]')
|
|
1297
|
+
WHERE kind = 'activity'
|
|
934
1298
|
AND producer_worker_id = NEW.worker_id
|
|
935
1299
|
AND source_record_id = NEW.id;
|
|
936
1300
|
END;
|
|
937
1301
|
|
|
1302
|
+
-- Overflow admission reclassifies a would-be inference turn only after its
|
|
1303
|
+
-- pre-model rows exist. Apply the same turn-owned classification to those
|
|
1304
|
+
-- rows; later rows are covered by log_entries_classify_plurnk_turn above.
|
|
1305
|
+
CREATE TRIGGER IF NOT EXISTS turns_classify_existing_plurnk_rows
|
|
1306
|
+
AFTER UPDATE OF producer, kind ON turns
|
|
1307
|
+
WHEN NEW.producer = '_plurnk'
|
|
1308
|
+
AND NEW.kind IN ('initialization', 'overflow')
|
|
1309
|
+
BEGIN
|
|
1310
|
+
INSERT OR IGNORE INTO log_tags (log_entry_id, tag)
|
|
1311
|
+
SELECT id, '_plurnk' FROM log_entries WHERE turn_id = NEW.id;
|
|
1312
|
+
INSERT OR IGNORE INTO log_tags (log_entry_id, tag)
|
|
1313
|
+
SELECT id, CASE NEW.kind
|
|
1314
|
+
WHEN 'initialization' THEN 'init'
|
|
1315
|
+
ELSE 'overflow'
|
|
1316
|
+
END
|
|
1317
|
+
FROM log_entries
|
|
1318
|
+
WHERE turn_id = NEW.id;
|
|
1319
|
+
|
|
1320
|
+
UPDATE ambient_events
|
|
1321
|
+
SET tags = COALESCE((
|
|
1322
|
+
SELECT json_group_array(tag)
|
|
1323
|
+
FROM (
|
|
1324
|
+
SELECT lt.tag
|
|
1325
|
+
FROM log_tags lt
|
|
1326
|
+
WHERE lt.log_entry_id = ambient_events.source_record_id
|
|
1327
|
+
ORDER BY lt.tag
|
|
1328
|
+
)
|
|
1329
|
+
), '[]')
|
|
1330
|
+
WHERE kind = 'activity'
|
|
1331
|
+
AND producer_worker_id = (
|
|
1332
|
+
SELECT l.worker_id FROM loops l WHERE l.id = NEW.loop_id
|
|
1333
|
+
)
|
|
1334
|
+
AND source_record_id IN (
|
|
1335
|
+
SELECT id FROM log_entries WHERE turn_id = NEW.id
|
|
1336
|
+
);
|
|
1337
|
+
END;
|
|
1338
|
+
|
|
938
1339
|
-- Successful OPEN/FOLD rows are durable curation events even though their
|
|
939
1340
|
-- ordinary packet projection is suppressed ({§fold-open-meta-operations}).
|
|
940
|
-
-- Preserve the exact selected set and each target's
|
|
1341
|
+
-- Preserve the exact selected set and each target's before/after visibility so a
|
|
941
1342
|
-- broad selector never collapses into the lossy fact `matched: N`.
|
|
942
1343
|
CREATE TABLE IF NOT EXISTS log_curation_effects (
|
|
943
1344
|
operation_log_entry_id INTEGER NOT NULL,
|
|
944
1345
|
target_log_entry_id INTEGER NOT NULL,
|
|
945
|
-
|
|
1346
|
+
folded_before TEXT NOT NULL
|
|
1347
|
+
CHECK (json_valid(folded_before) AND json_type(folded_before) = 'array'),
|
|
1348
|
+
folded_after TEXT NOT NULL
|
|
1349
|
+
CHECK (json_valid(folded_after) AND json_type(folded_after) = 'array'),
|
|
946
1350
|
tags_added TEXT NOT NULL DEFAULT '[]'
|
|
947
1351
|
CHECK (json_valid(tags_added) AND json_type(tags_added) = 'array'),
|
|
948
1352
|
tags_removed TEXT NOT NULL DEFAULT '[]'
|
|
@@ -972,6 +1376,39 @@ BEGIN
|
|
|
972
1376
|
AND operation.status_rx < 400
|
|
973
1377
|
AND operation.worker_id = target.worker_id
|
|
974
1378
|
)
|
|
1379
|
+
OR EXISTS (
|
|
1380
|
+
SELECT 1
|
|
1381
|
+
FROM (
|
|
1382
|
+
SELECT 'before' AS side, key, value, type FROM json_each(NEW.folded_before)
|
|
1383
|
+
UNION ALL
|
|
1384
|
+
SELECT 'after' AS side, key, value, type FROM json_each(NEW.folded_after)
|
|
1385
|
+
) range
|
|
1386
|
+
WHERE range.type != 'array'
|
|
1387
|
+
OR json_array_length(range.value) != 2
|
|
1388
|
+
OR COALESCE(json_type(range.value, '$[0]'), '') != 'integer'
|
|
1389
|
+
OR COALESCE(json_type(range.value, '$[1]'), '') != 'integer'
|
|
1390
|
+
OR json_extract(range.value, '$[0]') < 1
|
|
1391
|
+
OR (
|
|
1392
|
+
json_extract(range.value, '$[1]') != -1
|
|
1393
|
+
AND json_extract(range.value, '$[1]') < json_extract(range.value, '$[0]')
|
|
1394
|
+
)
|
|
1395
|
+
OR EXISTS (
|
|
1396
|
+
SELECT 1
|
|
1397
|
+
FROM (
|
|
1398
|
+
SELECT key, value FROM json_each(
|
|
1399
|
+
CASE range.side
|
|
1400
|
+
WHEN 'before' THEN NEW.folded_before
|
|
1401
|
+
ELSE NEW.folded_after
|
|
1402
|
+
END
|
|
1403
|
+
)
|
|
1404
|
+
) previous
|
|
1405
|
+
WHERE previous.key = range.key - 1
|
|
1406
|
+
AND (
|
|
1407
|
+
json_extract(previous.value, '$[1]') = -1
|
|
1408
|
+
OR json_extract(range.value, '$[0]') <= json_extract(previous.value, '$[1]') + 1
|
|
1409
|
+
)
|
|
1410
|
+
)
|
|
1411
|
+
)
|
|
975
1412
|
OR EXISTS (
|
|
976
1413
|
SELECT 1
|
|
977
1414
|
FROM (
|
|
@@ -1027,7 +1464,7 @@ BEGIN
|
|
|
1027
1464
|
END;
|
|
1028
1465
|
|
|
1029
1466
|
-- One outer INSERT owns the whole landed curation event: exact selected rows,
|
|
1030
|
-
-- their
|
|
1467
|
+
-- their before/after visibility and tag deltas, and the
|
|
1031
1468
|
-- resulting classifications. Trigger failure rolls the operation row and all
|
|
1032
1469
|
-- effects back together. The private plan is erased before INSERT returns.
|
|
1033
1470
|
CREATE TRIGGER IF NOT EXISTS log_entries_apply_curation
|
|
@@ -1037,26 +1474,70 @@ WHEN NEW.op IN ('OPEN', 'FOLD')
|
|
|
1037
1474
|
AND json_type(NEW.attrs, '$.__plurnk_curation') = 'object'
|
|
1038
1475
|
BEGIN
|
|
1039
1476
|
SELECT CASE WHEN
|
|
1040
|
-
COALESCE(json_type(NEW.attrs, '$.__plurnk_curation.
|
|
1477
|
+
COALESCE(json_type(NEW.attrs, '$.__plurnk_curation.targets'), '') != 'array'
|
|
1041
1478
|
OR COALESCE(json_type(NEW.attrs, '$.__plurnk_curation.add'), '') != 'array'
|
|
1042
1479
|
OR COALESCE(json_type(NEW.attrs, '$.__plurnk_curation.remove'), '') != 'array'
|
|
1043
|
-
OR
|
|
1044
|
-
OR json_extract(NEW.attrs, '$.__plurnk_curation.expanded') NOT IN (0, 1)
|
|
1045
|
-
OR json_array_length(NEW.attrs, '$.__plurnk_curation.ids') = 0
|
|
1480
|
+
OR json_array_length(NEW.attrs, '$.__plurnk_curation.targets') = 0
|
|
1046
1481
|
OR EXISTS (
|
|
1047
|
-
SELECT 1
|
|
1048
|
-
|
|
1482
|
+
SELECT 1
|
|
1483
|
+
FROM json_each(NEW.attrs, '$.__plurnk_curation.targets') selected
|
|
1484
|
+
WHERE selected.type != 'object'
|
|
1485
|
+
OR COALESCE(json_type(selected.value, '$.id'), '') != 'integer'
|
|
1486
|
+
OR json_extract(selected.value, '$.id') <= 0
|
|
1487
|
+
OR COALESCE(json_type(selected.value, '$.before'), '') != 'array'
|
|
1488
|
+
OR COALESCE(json_type(selected.value, '$.after'), '') != 'array'
|
|
1489
|
+
OR EXISTS (
|
|
1490
|
+
SELECT 1 FROM json_each(selected.value) field
|
|
1491
|
+
WHERE field.key NOT IN ('id', 'before', 'after')
|
|
1492
|
+
)
|
|
1493
|
+
OR EXISTS (
|
|
1494
|
+
SELECT 1
|
|
1495
|
+
FROM (
|
|
1496
|
+
SELECT 'before' AS side, range.key, range.value, range.type
|
|
1497
|
+
FROM json_each(json_extract(selected.value, '$.before')) range
|
|
1498
|
+
UNION ALL
|
|
1499
|
+
SELECT 'after' AS side, range.key, range.value, range.type
|
|
1500
|
+
FROM json_each(json_extract(selected.value, '$.after')) range
|
|
1501
|
+
) range
|
|
1502
|
+
WHERE range.type != 'array'
|
|
1503
|
+
OR json_array_length(range.value) != 2
|
|
1504
|
+
OR COALESCE(json_type(range.value, '$[0]'), '') != 'integer'
|
|
1505
|
+
OR COALESCE(json_type(range.value, '$[1]'), '') != 'integer'
|
|
1506
|
+
OR json_extract(range.value, '$[0]') < 1
|
|
1507
|
+
OR (
|
|
1508
|
+
json_extract(range.value, '$[1]') != -1
|
|
1509
|
+
AND json_extract(range.value, '$[1]') < json_extract(range.value, '$[0]')
|
|
1510
|
+
)
|
|
1511
|
+
OR EXISTS (
|
|
1512
|
+
SELECT 1
|
|
1513
|
+
FROM json_each(
|
|
1514
|
+
CASE range.side
|
|
1515
|
+
WHEN 'before' THEN json_extract(selected.value, '$.before')
|
|
1516
|
+
ELSE json_extract(selected.value, '$.after')
|
|
1517
|
+
END
|
|
1518
|
+
) previous
|
|
1519
|
+
WHERE previous.key = range.key - 1
|
|
1520
|
+
AND (
|
|
1521
|
+
json_extract(previous.value, '$[1]') = -1
|
|
1522
|
+
OR json_extract(range.value, '$[0]') <= json_extract(previous.value, '$[1]') + 1
|
|
1523
|
+
)
|
|
1524
|
+
)
|
|
1525
|
+
)
|
|
1049
1526
|
)
|
|
1050
1527
|
OR (
|
|
1051
|
-
SELECT COUNT(*) FROM json_each(NEW.attrs, '$.__plurnk_curation.
|
|
1528
|
+
SELECT COUNT(*) FROM json_each(NEW.attrs, '$.__plurnk_curation.targets')
|
|
1052
1529
|
) != (
|
|
1053
|
-
SELECT COUNT(DISTINCT value
|
|
1530
|
+
SELECT COUNT(DISTINCT json_extract(value, '$.id'))
|
|
1531
|
+
FROM json_each(NEW.attrs, '$.__plurnk_curation.targets')
|
|
1054
1532
|
)
|
|
1055
1533
|
OR EXISTS (
|
|
1056
1534
|
SELECT 1
|
|
1057
|
-
FROM json_each(NEW.attrs, '$.__plurnk_curation.
|
|
1058
|
-
LEFT JOIN log_entries target ON target.id = selected.value
|
|
1059
|
-
WHERE target.id IS NULL
|
|
1535
|
+
FROM json_each(NEW.attrs, '$.__plurnk_curation.targets') selected
|
|
1536
|
+
LEFT JOIN log_entries target ON target.id = json_extract(selected.value, '$.id')
|
|
1537
|
+
WHERE target.id IS NULL
|
|
1538
|
+
OR target.worker_id != NEW.worker_id
|
|
1539
|
+
OR target.id = NEW.id
|
|
1540
|
+
OR json(target.folded) != json(json_extract(selected.value, '$.before'))
|
|
1060
1541
|
)
|
|
1061
1542
|
OR EXISTS (
|
|
1062
1543
|
SELECT 1
|
|
@@ -1107,14 +1588,16 @@ BEGIN
|
|
|
1107
1588
|
INSERT INTO log_curation_effects (
|
|
1108
1589
|
operation_log_entry_id,
|
|
1109
1590
|
target_log_entry_id,
|
|
1110
|
-
|
|
1591
|
+
folded_before,
|
|
1592
|
+
folded_after,
|
|
1111
1593
|
tags_added,
|
|
1112
1594
|
tags_removed
|
|
1113
1595
|
)
|
|
1114
1596
|
SELECT
|
|
1115
1597
|
NEW.id,
|
|
1116
1598
|
target.id,
|
|
1117
|
-
|
|
1599
|
+
json_extract(selected.value, '$.before'),
|
|
1600
|
+
json_extract(selected.value, '$.after'),
|
|
1118
1601
|
COALESCE((
|
|
1119
1602
|
SELECT json_group_array(ordered.tag)
|
|
1120
1603
|
FROM (
|
|
@@ -1139,26 +1622,32 @@ BEGIN
|
|
|
1139
1622
|
ORDER BY removal.value
|
|
1140
1623
|
) ordered
|
|
1141
1624
|
), '[]')
|
|
1142
|
-
FROM json_each(NEW.attrs, '$.__plurnk_curation.
|
|
1143
|
-
JOIN log_entries target ON target.id = selected.value;
|
|
1625
|
+
FROM json_each(NEW.attrs, '$.__plurnk_curation.targets') selected
|
|
1626
|
+
JOIN log_entries target ON target.id = json_extract(selected.value, '$.id');
|
|
1144
1627
|
|
|
1145
1628
|
UPDATE log_entries
|
|
1146
|
-
SET
|
|
1629
|
+
SET folded = (
|
|
1630
|
+
SELECT json_extract(selected.value, '$.after')
|
|
1631
|
+
FROM json_each(NEW.attrs, '$.__plurnk_curation.targets') selected
|
|
1632
|
+
WHERE json_extract(selected.value, '$.id') = log_entries.id
|
|
1633
|
+
)
|
|
1147
1634
|
WHERE id IN (
|
|
1148
|
-
SELECT value
|
|
1635
|
+
SELECT json_extract(value, '$.id')
|
|
1636
|
+
FROM json_each(NEW.attrs, '$.__plurnk_curation.targets')
|
|
1149
1637
|
);
|
|
1150
1638
|
|
|
1151
1639
|
DELETE FROM log_tags
|
|
1152
1640
|
WHERE log_entry_id IN (
|
|
1153
|
-
SELECT value
|
|
1641
|
+
SELECT json_extract(value, '$.id')
|
|
1642
|
+
FROM json_each(NEW.attrs, '$.__plurnk_curation.targets')
|
|
1154
1643
|
)
|
|
1155
1644
|
AND tag IN (
|
|
1156
1645
|
SELECT value FROM json_each(NEW.attrs, '$.__plurnk_curation.remove')
|
|
1157
1646
|
);
|
|
1158
1647
|
|
|
1159
1648
|
INSERT OR IGNORE INTO log_tags (log_entry_id, tag)
|
|
1160
|
-
SELECT selected.value, addition.value
|
|
1161
|
-
FROM json_each(NEW.attrs, '$.__plurnk_curation.
|
|
1649
|
+
SELECT json_extract(selected.value, '$.id'), addition.value
|
|
1650
|
+
FROM json_each(NEW.attrs, '$.__plurnk_curation.targets') selected
|
|
1162
1651
|
CROSS JOIN json_each(NEW.attrs, '$.__plurnk_curation.add') addition;
|
|
1163
1652
|
|
|
1164
1653
|
UPDATE log_entries
|
|
@@ -1168,18 +1657,18 @@ END;
|
|
|
1168
1657
|
|
|
1169
1658
|
-- Column-scoped immutability: the original action's identity and target never
|
|
1170
1659
|
-- change; the proposal lifecycle is allowed to mutate state, outcome,
|
|
1171
|
-
-- status_rx, rx,
|
|
1660
|
+
-- status_rx, rx, folded. Keep attrs separate so the curation trigger's one
|
|
1172
1661
|
-- private-payload removal cannot exempt changes to any other core column.
|
|
1173
1662
|
CREATE TRIGGER IF NOT EXISTS log_entries_immutable_core
|
|
1174
1663
|
BEFORE UPDATE OF
|
|
1175
|
-
worker_id, loop_id, turn_id, sequence, at, origin, source, model_call_id,
|
|
1176
|
-
op,
|
|
1664
|
+
worker_id, loop_id, turn_id, sequence, at, origin, source, inherited_history, model_call_id,
|
|
1665
|
+
op, delimiter, signal,
|
|
1177
1666
|
scheme, username, password, hostname,
|
|
1178
1667
|
port, pathname, query, fragment,
|
|
1179
1668
|
lineMarker, tx, mimetype_tx, mimetype_rx
|
|
1180
1669
|
ON log_entries
|
|
1181
1670
|
BEGIN
|
|
1182
|
-
SELECT RAISE(ABORT, 'log_entries core fields are immutable; only state/outcome/status_rx/rx/
|
|
1671
|
+
SELECT RAISE(ABORT, 'log_entries core fields are immutable; only state/outcome/status_rx/rx/folded may change');
|
|
1183
1672
|
END;
|
|
1184
1673
|
|
|
1185
1674
|
CREATE TRIGGER IF NOT EXISTS log_entries_immutable_attrs
|
|
@@ -1201,72 +1690,168 @@ BEGIN
|
|
|
1201
1690
|
SELECT RAISE(ABORT, 'log_entries ambient event identity may only be assigned once');
|
|
1202
1691
|
END;
|
|
1203
1692
|
|
|
1204
|
-
--
|
|
1205
|
-
--
|
|
1206
|
-
|
|
1693
|
+
-- One relational projection owns activity eligibility, audience, and snapshot
|
|
1694
|
+
-- shape for both immediately-final and proposal-settlement paths.
|
|
1695
|
+
CREATE VIEW IF NOT EXISTS ambient_activity_candidates AS
|
|
1696
|
+
SELECT *
|
|
1697
|
+
FROM (
|
|
1698
|
+
SELECT w.workspace_id,
|
|
1699
|
+
le.worker_id AS producer_worker_id,
|
|
1700
|
+
CASE
|
|
1701
|
+
WHEN t.producer = '_plurnk' AND t.kind = 'operation' THEN NULL
|
|
1702
|
+
ELSE w.parent_worker_id
|
|
1703
|
+
END AS target_parent_worker_id,
|
|
1704
|
+
CASE
|
|
1705
|
+
WHEN le.state = 'resolved'
|
|
1706
|
+
AND le.status_rx BETWEEN 200 AND 399
|
|
1707
|
+
AND le.status_rx != 304
|
|
1708
|
+
AND (
|
|
1709
|
+
(
|
|
1710
|
+
(
|
|
1711
|
+
le.op IN ('EDIT', 'KILL')
|
|
1712
|
+
OR (
|
|
1713
|
+
le.op = 'SEND'
|
|
1714
|
+
AND json_valid(le.signal)
|
|
1715
|
+
AND json_extract(le.signal, '$') = 410
|
|
1716
|
+
)
|
|
1717
|
+
)
|
|
1718
|
+
AND le.scheme = 'worker'
|
|
1719
|
+
AND le.hostname IS NULL
|
|
1720
|
+
AND le.pathname IS NOT NULL
|
|
1721
|
+
)
|
|
1722
|
+
OR (
|
|
1723
|
+
le.op IN ('COPY', 'MOVE')
|
|
1724
|
+
AND json_valid(le.rx)
|
|
1725
|
+
AND EXISTS (
|
|
1726
|
+
SELECT 1 FROM json_each(le.rx, '$.effects') effect
|
|
1727
|
+
WHERE json_type(effect.value) = 'object'
|
|
1728
|
+
AND substr(CAST(json_extract(effect.value, '$.target') AS TEXT), 1, 10) = 'worker:///'
|
|
1729
|
+
)
|
|
1730
|
+
)
|
|
1731
|
+
)
|
|
1732
|
+
THEN 1 ELSE 0
|
|
1733
|
+
END AS workspace_broadcast,
|
|
1734
|
+
le.id AS source_record_id,
|
|
1735
|
+
le.at,
|
|
1736
|
+
le.source,
|
|
1737
|
+
le.op,
|
|
1738
|
+
le.delimiter,
|
|
1739
|
+
le.signal,
|
|
1740
|
+
le.scheme,
|
|
1741
|
+
le.username,
|
|
1742
|
+
le.password,
|
|
1743
|
+
le.hostname,
|
|
1744
|
+
le.port,
|
|
1745
|
+
le.pathname,
|
|
1746
|
+
le.query,
|
|
1747
|
+
le.fragment,
|
|
1748
|
+
le.lineMarker AS line_marker,
|
|
1749
|
+
le.tx,
|
|
1750
|
+
le.mimetype_tx,
|
|
1751
|
+
le.rx,
|
|
1752
|
+
le.mimetype_rx,
|
|
1753
|
+
le.status_rx,
|
|
1754
|
+
le.state,
|
|
1755
|
+
le.outcome,
|
|
1756
|
+
json_remove(le.attrs, '$.__plurnk_curation') AS attrs,
|
|
1757
|
+
COALESCE((
|
|
1758
|
+
SELECT json_group_array(ordered.tag)
|
|
1759
|
+
FROM (
|
|
1760
|
+
SELECT tag FROM log_tags WHERE log_entry_id = le.id ORDER BY tag
|
|
1761
|
+
) ordered
|
|
1762
|
+
), '[]') AS tags
|
|
1763
|
+
FROM log_entries le
|
|
1764
|
+
JOIN workers w ON w.id = le.worker_id
|
|
1765
|
+
JOIN loops l ON l.id = le.loop_id
|
|
1766
|
+
JOIN turns t ON t.id = le.turn_id
|
|
1767
|
+
WHERE le.ambient_event_id IS NULL
|
|
1768
|
+
AND le.inherited_history = 0
|
|
1769
|
+
AND le.op IS NOT NULL
|
|
1770
|
+
AND le.state != 'proposed'
|
|
1771
|
+
AND NOT (
|
|
1772
|
+
le.op = 'SEND'
|
|
1773
|
+
AND le.scheme IS NULL
|
|
1774
|
+
AND l.status IN (200, 413, 429, 499, 500, 504, 508)
|
|
1775
|
+
)
|
|
1776
|
+
) candidate
|
|
1777
|
+
WHERE target_parent_worker_id IS NOT NULL OR workspace_broadcast = 1;
|
|
1778
|
+
|
|
1779
|
+
-- Every final op-bearing child row is parent activity. A successful operation
|
|
1780
|
+
-- whose landed effects touch worker:/// additionally acquires the workspace
|
|
1781
|
+
-- audience. Observer rows and copied fork history cannot republish themselves.
|
|
1207
1782
|
CREATE TRIGGER IF NOT EXISTS log_entries_append_ambient_event_insert
|
|
1208
1783
|
AFTER INSERT ON log_entries
|
|
1209
|
-
WHEN NEW.
|
|
1210
|
-
AND NEW.op = 'EDIT'
|
|
1211
|
-
AND NEW.state = 'resolved'
|
|
1212
|
-
AND NEW.status_rx IN (200, 201)
|
|
1213
|
-
AND (NEW.scheme IS NULL OR NEW.scheme != 'plurnk')
|
|
1214
|
-
AND (NEW.scheme IS NULL OR NEW.scheme != 'worker' OR NEW.hostname IS NULL OR NEW.hostname = 'plurnk')
|
|
1215
|
-
AND (
|
|
1216
|
-
NEW.origin != 'plurnk'
|
|
1217
|
-
OR EXISTS (SELECT 1 FROM workers w WHERE w.id = NEW.worker_id AND w.name = 'plurnk')
|
|
1218
|
-
)
|
|
1784
|
+
WHEN NEW.state != 'proposed'
|
|
1219
1785
|
BEGIN
|
|
1220
1786
|
INSERT INTO ambient_events (
|
|
1221
|
-
workspace_id, producer_worker_id,
|
|
1222
|
-
|
|
1787
|
+
workspace_id, producer_worker_id, target_parent_worker_id,
|
|
1788
|
+
workspace_broadcast, kind, source_record_id, at, source,
|
|
1789
|
+
op, delimiter, signal,
|
|
1790
|
+
scheme, username, password, hostname, port, pathname, query, fragment,
|
|
1791
|
+
line_marker, tx, mimetype_tx, rx, mimetype_rx, status_rx,
|
|
1792
|
+
state, outcome, attrs, tags
|
|
1223
1793
|
)
|
|
1224
|
-
SELECT
|
|
1225
|
-
'
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1794
|
+
SELECT workspace_id, producer_worker_id, target_parent_worker_id,
|
|
1795
|
+
workspace_broadcast, 'activity', source_record_id, at, source,
|
|
1796
|
+
op, delimiter, signal,
|
|
1797
|
+
scheme, username, password, hostname, port, pathname, query, fragment,
|
|
1798
|
+
line_marker, tx, mimetype_tx, rx, mimetype_rx, status_rx,
|
|
1799
|
+
state, outcome, attrs, tags
|
|
1800
|
+
FROM ambient_activity_candidates
|
|
1801
|
+
WHERE source_record_id = NEW.id;
|
|
1802
|
+
|
|
1803
|
+
UPDATE log_entries
|
|
1804
|
+
SET ambient_event_id = (
|
|
1805
|
+
SELECT id FROM ambient_events
|
|
1806
|
+
WHERE producer_worker_id = NEW.worker_id
|
|
1807
|
+
AND kind = 'activity'
|
|
1808
|
+
AND source_record_id = NEW.id
|
|
1809
|
+
)
|
|
1810
|
+
WHERE id = NEW.id
|
|
1811
|
+
AND EXISTS (
|
|
1812
|
+
SELECT 1 FROM ambient_events
|
|
1813
|
+
WHERE producer_worker_id = NEW.worker_id
|
|
1814
|
+
AND kind = 'activity'
|
|
1815
|
+
AND source_record_id = NEW.id
|
|
1816
|
+
);
|
|
1236
1817
|
END;
|
|
1237
1818
|
|
|
1238
|
-
-- A proposed
|
|
1239
|
-
-- resolves successfully. Rejection, cancellation, and failed application publish nothing.
|
|
1819
|
+
-- A proposed operation becomes activity only when its lifecycle settles.
|
|
1240
1820
|
CREATE TRIGGER IF NOT EXISTS log_entries_append_ambient_event_resolve
|
|
1241
|
-
AFTER UPDATE OF state, status_rx ON log_entries
|
|
1242
|
-
WHEN OLD.state = 'proposed'
|
|
1243
|
-
AND NEW.state = 'resolved'
|
|
1244
|
-
AND NEW.ambient_event_id IS NULL
|
|
1245
|
-
AND NEW.op = 'EDIT'
|
|
1246
|
-
AND NEW.status_rx IN (200, 201)
|
|
1247
|
-
AND (NEW.scheme IS NULL OR NEW.scheme != 'plurnk')
|
|
1248
|
-
AND (NEW.scheme IS NULL OR NEW.scheme != 'worker' OR NEW.hostname IS NULL OR NEW.hostname = 'plurnk')
|
|
1249
|
-
AND (
|
|
1250
|
-
NEW.origin != 'plurnk'
|
|
1251
|
-
OR EXISTS (SELECT 1 FROM workers w WHERE w.id = NEW.worker_id AND w.name = 'plurnk')
|
|
1252
|
-
)
|
|
1821
|
+
AFTER UPDATE OF state, status_rx, rx, outcome ON log_entries
|
|
1822
|
+
WHEN OLD.state = 'proposed' AND NEW.state != 'proposed'
|
|
1253
1823
|
BEGIN
|
|
1254
1824
|
INSERT INTO ambient_events (
|
|
1255
|
-
workspace_id, producer_worker_id,
|
|
1256
|
-
|
|
1825
|
+
workspace_id, producer_worker_id, target_parent_worker_id,
|
|
1826
|
+
workspace_broadcast, kind, source_record_id, at, source,
|
|
1827
|
+
op, delimiter, signal,
|
|
1828
|
+
scheme, username, password, hostname, port, pathname, query, fragment,
|
|
1829
|
+
line_marker, tx, mimetype_tx, rx, mimetype_rx, status_rx,
|
|
1830
|
+
state, outcome, attrs, tags
|
|
1257
1831
|
)
|
|
1258
|
-
SELECT
|
|
1259
|
-
'
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1832
|
+
SELECT workspace_id, producer_worker_id, target_parent_worker_id,
|
|
1833
|
+
workspace_broadcast, 'activity', source_record_id, at, source,
|
|
1834
|
+
op, delimiter, signal,
|
|
1835
|
+
scheme, username, password, hostname, port, pathname, query, fragment,
|
|
1836
|
+
line_marker, tx, mimetype_tx, rx, mimetype_rx, status_rx,
|
|
1837
|
+
state, outcome, attrs, tags
|
|
1838
|
+
FROM ambient_activity_candidates
|
|
1839
|
+
WHERE source_record_id = NEW.id;
|
|
1840
|
+
|
|
1841
|
+
UPDATE log_entries
|
|
1842
|
+
SET ambient_event_id = (
|
|
1843
|
+
SELECT id FROM ambient_events
|
|
1844
|
+
WHERE producer_worker_id = NEW.worker_id
|
|
1845
|
+
AND kind = 'activity'
|
|
1846
|
+
AND source_record_id = NEW.id
|
|
1847
|
+
)
|
|
1848
|
+
WHERE id = NEW.id
|
|
1849
|
+
AND EXISTS (
|
|
1850
|
+
SELECT 1 FROM ambient_events
|
|
1851
|
+
WHERE producer_worker_id = NEW.worker_id
|
|
1852
|
+
AND kind = 'activity'
|
|
1853
|
+
AND source_record_id = NEW.id
|
|
1854
|
+
);
|
|
1270
1855
|
END;
|
|
1271
1856
|
|
|
1272
1857
|
-- {§client-interactions}: the durable discoverable half of a client-owned
|
|
@@ -1293,7 +1878,8 @@ CREATE INDEX IF NOT EXISTS client_interactions_worker_id_id
|
|
|
1293
1878
|
-- Durable subscription lifecycle per SPEC {§subscriptions}. The row records what
|
|
1294
1879
|
-- the worker holds and routes cancellation to a separate process-local callable;
|
|
1295
1880
|
-- it never serializes that callable. Closed rows persist for forensics; partial
|
|
1296
|
-
-- unique index enforces one active subscription per
|
|
1881
|
+
-- unique index enforces one active subscription per entry. The composite
|
|
1882
|
+
-- foreign key makes that entry part of the subscribing Worker's own space.
|
|
1297
1883
|
CREATE TABLE IF NOT EXISTS subscriptions (
|
|
1298
1884
|
id INTEGER NOT NULL PRIMARY KEY,
|
|
1299
1885
|
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
|
|
@@ -1312,14 +1898,15 @@ CREATE TABLE IF NOT EXISTS subscriptions (
|
|
|
1312
1898
|
closed_at TEXT,
|
|
1313
1899
|
close_status INTEGER CHECK (close_status IS NULL OR (close_status BETWEEN 100 AND 599)),
|
|
1314
1900
|
close_result TEXT CHECK (close_result IS NULL OR json_valid(close_result)),
|
|
1315
|
-
CHECK (
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
FOREIGN KEY (
|
|
1901
|
+
channel_results TEXT CHECK (channel_results IS NULL OR json_valid(channel_results)),
|
|
1902
|
+
CHECK ((closed_at IS NULL AND close_status IS NULL AND close_result IS NULL AND channel_results IS NULL)
|
|
1903
|
+
OR (closed_at IS NOT NULL AND close_status IS NOT NULL AND close_result IS NOT NULL AND channel_results IS NOT NULL)),
|
|
1904
|
+
FOREIGN KEY (worker_id) REFERENCES workers(id) ON DELETE CASCADE,
|
|
1905
|
+
FOREIGN KEY (entry_id, worker_id) REFERENCES entries(id, owner_id) ON DELETE CASCADE
|
|
1319
1906
|
) STRICT;
|
|
1320
1907
|
|
|
1321
1908
|
CREATE UNIQUE INDEX IF NOT EXISTS subscriptions_active_one_per_entry
|
|
1322
|
-
ON subscriptions (
|
|
1909
|
+
ON subscriptions (entry_id)
|
|
1323
1910
|
WHERE closed_at IS NULL;
|
|
1324
1911
|
|
|
1325
1912
|
CREATE INDEX IF NOT EXISTS subscriptions_scheme_active
|
|
@@ -1330,13 +1917,26 @@ CREATE INDEX IF NOT EXISTS subscriptions_opened_at ON subscriptions (opened_at);
|
|
|
1330
1917
|
|
|
1331
1918
|
CREATE TRIGGER IF NOT EXISTS subscriptions_result_contract_insert
|
|
1332
1919
|
BEFORE INSERT ON subscriptions
|
|
1920
|
+
WHEN NEW.closed_at IS NOT NULL
|
|
1921
|
+
OR NEW.close_status IS NOT NULL
|
|
1922
|
+
OR NEW.close_result IS NOT NULL
|
|
1923
|
+
OR NEW.channel_results IS NOT NULL
|
|
1924
|
+
BEGIN
|
|
1925
|
+
SELECT RAISE(ABORT, 'subscription must open before it can settle');
|
|
1926
|
+
END;
|
|
1927
|
+
|
|
1928
|
+
CREATE TRIGGER IF NOT EXISTS subscriptions_result_contract_update
|
|
1929
|
+
BEFORE UPDATE OF closed_at, close_status, close_result, channel_results ON subscriptions
|
|
1333
1930
|
WHEN NOT (
|
|
1334
|
-
(NEW.closed_at IS NULL AND NEW.close_status IS NULL AND NEW.close_result IS NULL)
|
|
1931
|
+
(NEW.closed_at IS NULL AND NEW.close_status IS NULL AND NEW.close_result IS NULL AND NEW.channel_results IS NULL)
|
|
1335
1932
|
OR (
|
|
1336
1933
|
NEW.closed_at IS NOT NULL
|
|
1337
1934
|
AND NEW.close_status IS NOT NULL
|
|
1338
1935
|
AND NEW.close_result IS NOT NULL
|
|
1936
|
+
AND NEW.channel_results IS NOT NULL
|
|
1339
1937
|
AND json_valid(NEW.close_result)
|
|
1938
|
+
AND json_valid(NEW.channel_results)
|
|
1939
|
+
AND json_type(NEW.channel_results) = 'object'
|
|
1340
1940
|
AND json_type(NEW.close_result, '$.status') = 'integer'
|
|
1341
1941
|
AND json_extract(NEW.close_result, '$.status') = NEW.close_status
|
|
1342
1942
|
AND (
|
|
@@ -1353,35 +1953,142 @@ BEGIN
|
|
|
1353
1953
|
SELECT RAISE(ABORT, 'subscription terminal result violates the operation-result contract');
|
|
1354
1954
|
END;
|
|
1355
1955
|
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1956
|
+
-- A settlement may override the universal result for exact named channels.
|
|
1957
|
+
-- Unknown channels and malformed results fail before the subscription or any
|
|
1958
|
+
-- representation changes. The JS boundary additionally rejects projection
|
|
1959
|
+
-- fields that are structurally unavailable in ChannelProducerResult.
|
|
1960
|
+
CREATE TRIGGER IF NOT EXISTS subscriptions_channel_results_contract
|
|
1961
|
+
BEFORE UPDATE OF closed_at, close_status, close_result, channel_results ON subscriptions
|
|
1962
|
+
WHEN NEW.closed_at IS NOT NULL AND (
|
|
1963
|
+
EXISTS (
|
|
1964
|
+
SELECT 1
|
|
1965
|
+
FROM json_each(NEW.channel_results) AS channel_result
|
|
1966
|
+
LEFT JOIN entry_channels ec
|
|
1967
|
+
ON ec.entry_id = NEW.entry_id AND ec.name = channel_result.key
|
|
1968
|
+
WHERE ec.name IS NULL
|
|
1969
|
+
OR json_type(channel_result.value) != 'object'
|
|
1970
|
+
OR json_type(channel_result.value, '$.status') != 'integer'
|
|
1971
|
+
OR json_extract(channel_result.value, '$.status') NOT BETWEEN 200 AND 599
|
|
1972
|
+
OR json_extract(channel_result.value, '$.status') = 202
|
|
1973
|
+
OR (
|
|
1974
|
+
json_extract(channel_result.value, '$.status') < 400
|
|
1975
|
+
AND json_type(channel_result.value, '$.problem') IS NOT NULL
|
|
1976
|
+
)
|
|
1977
|
+
OR (
|
|
1978
|
+
json_extract(channel_result.value, '$.status') >= 400
|
|
1979
|
+
AND (
|
|
1980
|
+
json_type(channel_result.value, '$.problem') != 'object'
|
|
1981
|
+
OR json_extract(channel_result.value, '$.problem.status')
|
|
1982
|
+
!= json_extract(channel_result.value, '$.status')
|
|
1983
|
+
)
|
|
1984
|
+
)
|
|
1375
1985
|
)
|
|
1376
1986
|
)
|
|
1377
1987
|
BEGIN
|
|
1378
|
-
SELECT RAISE(ABORT, 'subscription
|
|
1988
|
+
SELECT RAISE(ABORT, 'subscription channel result violates the channel producer contract');
|
|
1989
|
+
END;
|
|
1990
|
+
|
|
1991
|
+
-- Subscription settlement is the single atomic transition that closes the
|
|
1992
|
+
-- lifecycle and installs current terminal producer evidence on every channel.
|
|
1993
|
+
-- Overrides are exact; all other channels inherit the universal result.
|
|
1994
|
+
CREATE TRIGGER IF NOT EXISTS subscriptions_settle_channels
|
|
1995
|
+
AFTER UPDATE OF closed_at, close_status, close_result, channel_results ON subscriptions
|
|
1996
|
+
WHEN OLD.closed_at IS NULL AND NEW.closed_at IS NOT NULL
|
|
1997
|
+
BEGIN
|
|
1998
|
+
UPDATE entry_channels
|
|
1999
|
+
SET producer_result = COALESCE(
|
|
2000
|
+
(
|
|
2001
|
+
SELECT json(channel_result.value)
|
|
2002
|
+
FROM json_each(NEW.channel_results) AS channel_result
|
|
2003
|
+
WHERE channel_result.key = entry_channels.name
|
|
2004
|
+
),
|
|
2005
|
+
NEW.close_result
|
|
2006
|
+
),
|
|
2007
|
+
state = CASE WHEN json_extract(
|
|
2008
|
+
COALESCE(
|
|
2009
|
+
(
|
|
2010
|
+
SELECT json(channel_result.value)
|
|
2011
|
+
FROM json_each(NEW.channel_results) AS channel_result
|
|
2012
|
+
WHERE channel_result.key = entry_channels.name
|
|
2013
|
+
),
|
|
2014
|
+
NEW.close_result
|
|
2015
|
+
),
|
|
2016
|
+
'$.status'
|
|
2017
|
+
) >= 400 THEN 'errored' ELSE 'closed' END
|
|
2018
|
+
WHERE entry_id = NEW.entry_id;
|
|
2019
|
+
END;
|
|
2020
|
+
|
|
2021
|
+
-- One durable publication cursor per selected subscription channel. Log rows
|
|
2022
|
+
-- are curated model context and therefore cannot own this lifecycle fact.
|
|
2023
|
+
-- The row survives KILLing any generated observation and disappears only with
|
|
2024
|
+
-- its subscription. {§exec-stream}
|
|
2025
|
+
CREATE TABLE IF NOT EXISTS subscription_publications (
|
|
2026
|
+
id INTEGER NOT NULL PRIMARY KEY,
|
|
2027
|
+
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
|
|
2028
|
+
subscription_id INTEGER NOT NULL,
|
|
2029
|
+
channel TEXT NOT NULL CHECK (length(channel) > 0),
|
|
2030
|
+
published_end INTEGER NOT NULL DEFAULT 0 CHECK (published_end >= 0),
|
|
2031
|
+
terminal_published INTEGER NOT NULL DEFAULT 0 CHECK (terminal_published IN (0, 1)),
|
|
2032
|
+
UNIQUE (subscription_id, channel),
|
|
2033
|
+
FOREIGN KEY (subscription_id) REFERENCES subscriptions(id) ON DELETE CASCADE
|
|
2034
|
+
) STRICT;
|
|
2035
|
+
|
|
2036
|
+
CREATE INDEX IF NOT EXISTS subscription_publications_pending
|
|
2037
|
+
ON subscription_publications (subscription_id, terminal_published);
|
|
2038
|
+
|
|
2039
|
+
CREATE TRIGGER IF NOT EXISTS subscriptions_seed_publications
|
|
2040
|
+
AFTER INSERT ON subscriptions
|
|
2041
|
+
BEGIN
|
|
2042
|
+
INSERT INTO subscription_publications (subscription_id, channel)
|
|
2043
|
+
SELECT NEW.id, ec.name
|
|
2044
|
+
FROM entry_channels ec
|
|
2045
|
+
WHERE ec.entry_id = NEW.entry_id
|
|
2046
|
+
AND (NEW.published_channel IS NULL OR ec.name = NEW.published_channel);
|
|
2047
|
+
|
|
2048
|
+
SELECT CASE WHEN NOT EXISTS (
|
|
2049
|
+
SELECT 1 FROM subscription_publications WHERE subscription_id = NEW.id
|
|
2050
|
+
) THEN RAISE(ABORT, 'subscription has no publishable channel') END;
|
|
2051
|
+
END;
|
|
2052
|
+
|
|
2053
|
+
-- The generated READ and its cursor transition are one SQLite statement. A
|
|
2054
|
+
-- later log KILL removes evidence from active context without rewinding the
|
|
2055
|
+
-- subscription/channel publication state. {§exec-stream}
|
|
2056
|
+
CREATE TRIGGER IF NOT EXISTS log_entries_advance_subscription_publication
|
|
2057
|
+
AFTER INSERT ON log_entries
|
|
2058
|
+
WHEN NEW.subscription_publication_id IS NOT NULL
|
|
2059
|
+
BEGIN
|
|
2060
|
+
SELECT CASE WHEN
|
|
2061
|
+
NEW.origin != '_plurnk'
|
|
2062
|
+
OR NEW.op != 'READ'
|
|
2063
|
+
OR json_type(NEW.attrs, '$.streamEnd') != 'integer'
|
|
2064
|
+
OR json_extract(NEW.attrs, '$.streamEnd') < 0
|
|
2065
|
+
OR json_type(NEW.attrs, '$.terminal') NOT IN ('true', 'false')
|
|
2066
|
+
THEN RAISE(ABORT, 'subscription publication requires one canonical stream observation') END;
|
|
2067
|
+
|
|
2068
|
+
UPDATE subscription_publications
|
|
2069
|
+
SET published_end = json_extract(NEW.attrs, '$.streamEnd'),
|
|
2070
|
+
terminal_published = json_extract(NEW.attrs, '$.terminal'),
|
|
2071
|
+
version = version + 1
|
|
2072
|
+
WHERE id = NEW.subscription_publication_id
|
|
2073
|
+
AND terminal_published = 0
|
|
2074
|
+
AND (
|
|
2075
|
+
(json_extract(NEW.attrs, '$.terminal') = 0
|
|
2076
|
+
AND json_extract(NEW.attrs, '$.streamEnd') > published_end)
|
|
2077
|
+
OR
|
|
2078
|
+
(json_extract(NEW.attrs, '$.terminal') = 1
|
|
2079
|
+
AND json_extract(NEW.attrs, '$.streamEnd') >= published_end)
|
|
2080
|
+
);
|
|
2081
|
+
|
|
2082
|
+
SELECT CASE WHEN changes() != 1
|
|
2083
|
+
THEN RAISE(ABORT, 'subscription publication transition is stale or already terminal')
|
|
2084
|
+
END;
|
|
1379
2085
|
END;
|
|
1380
2086
|
|
|
1381
2087
|
-- (worker_watermarks removed — {§env-delta} is now pull-from-log, no per-worker snapshot.)
|
|
1382
2088
|
|
|
1383
2089
|
-- workspace_constraints
|
|
1384
|
-
-- SPEC {§membership} constraint overlay —
|
|
2090
|
+
-- SPEC {§membership} constraint overlay — explicit policy and the inspectable
|
|
2091
|
+
-- exact pick produced by accepted file creation share one representation.
|
|
1385
2092
|
-- Per (workspace, effect, glob/target): `pick` (members git misses, resolved by a
|
|
1386
2093
|
-- targeted client-dictated scan), `hide` (drop git-tracked matches), `view` (member
|
|
1387
2094
|
-- for read; File.edit rejects the write). git-absent, `pick` rows are the sole substrate
|
|
@@ -1390,6 +2097,8 @@ CREATE TABLE IF NOT EXISTS workspace_constraints (
|
|
|
1390
2097
|
workspace_id INTEGER NOT NULL,
|
|
1391
2098
|
effect TEXT NOT NULL CHECK (effect IN ('pick', 'hide', 'view')),
|
|
1392
2099
|
glob TEXT NOT NULL,
|
|
2100
|
+
source TEXT NOT NULL CHECK (source IN ('explicit', 'create')),
|
|
2101
|
+
CHECK (source = 'explicit' OR effect = 'pick'),
|
|
1393
2102
|
PRIMARY KEY (workspace_id, effect, glob),
|
|
1394
2103
|
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
|
|
1395
2104
|
) STRICT, WITHOUT ROWID;
|