@plurnk/plurnk-service 1.8.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 +20 -0
- package/INSTALL.md +14 -16
- package/README.md +1 -1
- package/SPEC.md +635 -296
- package/digest-sql/channel-dispositions/dispositions.sql +2 -2
- package/digest-sql/entry-dispositions/dispositions.sql +2 -2
- package/dist/Paths.d.ts +1 -2
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +1 -9
- package/dist/Paths.js.map +1 -1
- package/dist/build-info.json +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 +17 -4
- 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 +9 -1
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +321 -221
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Engine.d.ts +13 -4
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +44 -22
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +101 -81
- 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 +4 -4
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +28 -28
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/LogBody.d.ts.map +1 -1
- package/dist/core/LogBody.js +19 -1
- package/dist/core/LogBody.js.map +1 -1
- package/dist/core/OperatorConfig.d.ts.map +1 -1
- package/dist/core/OperatorConfig.js +5 -2
- package/dist/core/OperatorConfig.js.map +1 -1
- package/dist/core/OverflowTurn.d.ts.map +1 -1
- package/dist/core/OverflowTurn.js +5 -1
- package/dist/core/OverflowTurn.js.map +1 -1
- package/dist/core/Owner.js +1 -1
- package/dist/core/Owner.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +5 -1
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +21 -11
- package/dist/core/PacketBuilder.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 +85 -26
- package/dist/core/ProposalLifecycle.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 +113 -38
- package/dist/core/ResourceMutations.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +12 -11
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +52 -47
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/ToolResources.d.ts.map +1 -1
- package/dist/core/ToolResources.js +20 -19
- package/dist/core/ToolResources.js.map +1 -1
- package/dist/core/Turn.d.ts +1 -1
- package/dist/core/Turn.d.ts.map +1 -1
- package/dist/core/TurnOps.d.ts +2 -2
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +14 -4
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +46 -3
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +165 -78
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/WorkerName.d.ts +1 -0
- 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/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 +27 -15
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +35 -22
- 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 +225 -50
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +28 -19
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +13 -2
- package/dist/core/plurnk-uri.d.ts.map +1 -1
- package/dist/core/plurnk-uri.js +59 -21
- 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/worker-ops.sql +10 -6
- package/dist/core/worker-settings.d.ts +1 -0
- package/dist/core/worker-settings.d.ts.map +1 -1
- package/dist/core/worker-settings.js +7 -0
- package/dist/core/worker-settings.js.map +1 -1
- package/dist/core/workspace-settings.d.ts +2 -0
- package/dist/core/workspace-settings.d.ts.map +1 -1
- package/dist/core/workspace-settings.js +5 -1
- package/dist/core/workspace-settings.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +1 -1
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +1 -1
- package/dist/schemes/Exec.d.ts +3 -2
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +55 -22
- 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/Prompt.d.ts +2 -3
- package/dist/schemes/Prompt.d.ts.map +1 -1
- package/dist/schemes/Prompt.js +4 -7
- package/dist/schemes/Prompt.js.map +1 -1
- package/dist/schemes/Skill.d.ts.map +1 -1
- package/dist/schemes/Skill.js +3 -1
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +95 -49
- 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 +58 -23
- package/dist/schemes/_entry-find.d.ts +3 -2
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +26 -25
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-find.sql +10 -7
- 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 +6 -5
- 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 +2 -0
- package/dist/schemes/_search-index.js.map +1 -1
- package/dist/server/Daemon.d.ts +42 -10
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +326 -248
- 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 +3 -1
- package/dist/server/DrainSupervisor.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.js +11 -9
- 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 +1 -0
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +22 -1
- 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 +89 -25
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/drain.sql +11 -6
- package/dist/server/envelope.d.ts +6 -1
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +8 -2
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +11 -3
- package/dist/server/lifecycle-recovery.sql +13 -16
- package/dist/server/loopDocs.d.ts +2 -1
- package/dist/server/loopDocs.d.ts.map +1 -1
- package/dist/server/loopDocs.js +67 -51
- package/dist/server/loopDocs.js.map +1 -1
- package/dist/server/loopDocs.sql +14 -10
- package/dist/server/module-discovery.d.ts +2 -2
- package/dist/server/module-discovery.d.ts.map +1 -1
- package/dist/server/module-discovery.js +27 -2
- package/dist/server/module-discovery.js.map +1 -1
- 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 +11 -1
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +486 -136
- package/package.json +34 -32
- package/dist/server/skillDocs.d.ts +0 -9
- package/dist/server/skillDocs.d.ts.map +0 -1
- package/dist/server/skillDocs.js +0 -212
- package/dist/server/skillDocs.js.map +0 -1
- package/dist/server/skillDocs.sql +0 -14
- package/dist/server/workspace-capabilities.sql +0 -19
|
@@ -19,17 +19,6 @@ 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
|
-
-- {§module-workspace-state}: one opaque, provider-validated JSON snapshot per
|
|
23
|
-
-- module owner and workspace. Core owns isolation and lifecycle only.
|
|
24
|
-
CREATE TABLE IF NOT EXISTS workspace_module_state (
|
|
25
|
-
workspace_id INTEGER NOT NULL,
|
|
26
|
-
namespace_owner TEXT NOT NULL CHECK (length(namespace_owner) > 0),
|
|
27
|
-
state TEXT NOT NULL CHECK (json_valid(state)),
|
|
28
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
29
|
-
PRIMARY KEY (workspace_id, namespace_owner),
|
|
30
|
-
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
|
|
31
|
-
) STRICT;
|
|
32
|
-
|
|
33
22
|
-- model_routes — the immutable resolved model route ({§worker-model-selection}). One row per
|
|
34
23
|
-- complete resolved tuple; append-only. Alias is provenance plus a tuning scope,
|
|
35
24
|
-- not route identity, and is absent on a direct provider/model selection.
|
|
@@ -77,8 +66,11 @@ CREATE TABLE IF NOT EXISTS workers (
|
|
|
77
66
|
-- never persist).
|
|
78
67
|
settings TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(settings)),
|
|
79
68
|
-- {§env-delta-log-pull}: monotonic observation progress, not a private world snapshot.
|
|
80
|
-
--
|
|
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.
|
|
81
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),
|
|
82
74
|
CHECK (default_conversation = 0 OR (origin = 'model' AND parent_worker_id IS NULL)),
|
|
83
75
|
CHECK ((model_route_id IS NULL) = (reasoning_policy IS NULL)),
|
|
84
76
|
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
@@ -103,48 +95,133 @@ BEGIN
|
|
|
103
95
|
SELECT RAISE(ABORT, 'workers.provider_identity is immutable');
|
|
104
96
|
END;
|
|
105
97
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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.
|
|
109
133
|
-- source_record_id is forensic identity for the originating log/loop row, not a
|
|
110
134
|
-- foreign key: model log curation must not erase an already-recorded occurrence.
|
|
111
135
|
CREATE TABLE IF NOT EXISTS ambient_events (
|
|
112
|
-
id
|
|
113
|
-
workspace_id
|
|
114
|
-
producer_worker_id
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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),
|
|
128
169
|
CHECK (
|
|
129
|
-
(kind = '
|
|
170
|
+
(kind = 'activity' AND terminated_by IS NULL)
|
|
130
171
|
OR
|
|
131
172
|
(kind = 'loop_termination'
|
|
132
173
|
AND op = 'SEND'
|
|
133
174
|
AND scheme = 'worker'
|
|
134
|
-
AND rx IS NOT NULL
|
|
135
175
|
AND json_valid(rx)
|
|
136
176
|
AND json_type(rx) = 'object'
|
|
137
177
|
AND json_type(rx, '$.status') = 'integer'
|
|
138
178
|
AND json_extract(rx, '$.status') = status_rx)
|
|
139
179
|
),
|
|
140
|
-
FOREIGN KEY (workspace_id)
|
|
141
|
-
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
|
|
142
183
|
) STRICT;
|
|
143
184
|
|
|
144
185
|
CREATE INDEX IF NOT EXISTS ambient_events_workspace_id_id
|
|
145
186
|
ON ambient_events (workspace_id, id);
|
|
146
187
|
CREATE INDEX IF NOT EXISTS ambient_events_producer_kind_id
|
|
147
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;
|
|
148
225
|
|
|
149
226
|
-- loops
|
|
150
227
|
-- flags: per-loop runtime flags (auto, noProposals, noWeb, noInteraction,
|
|
@@ -158,6 +235,9 @@ CREATE TABLE IF NOT EXISTS loops (
|
|
|
158
235
|
sequence INTEGER NOT NULL CHECK (sequence >= 1),
|
|
159
236
|
status INTEGER NOT NULL DEFAULT 102 CHECK (status IN (100, 102, 200, 202, 413, 429, 499, 500, 504, 508)),
|
|
160
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),
|
|
161
241
|
flags TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(flags)),
|
|
162
242
|
-- {§worker-model-selection}: immutable loop snapshots of the resolved model route and the
|
|
163
243
|
-- effective spawn route (was provider_spec/child_provider_spec JSON).
|
|
@@ -237,22 +317,33 @@ BEGIN
|
|
|
237
317
|
UPDATE loops SET terminated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = NEW.id;
|
|
238
318
|
END;
|
|
239
319
|
|
|
240
|
-
-- A terminal transition is an
|
|
241
|
-
--
|
|
320
|
+
-- A child terminal transition is an occurrence addressed only to its direct
|
|
321
|
+
-- parent. Directly inserted fork history never crosses this transition and
|
|
242
322
|
-- therefore cannot fabricate a new conclusion event.
|
|
243
323
|
CREATE TRIGGER IF NOT EXISTS loops_append_ambient_event
|
|
244
324
|
AFTER UPDATE OF status ON loops
|
|
245
325
|
WHEN NEW.status IN (200, 413, 429, 499, 500, 504, 508) AND OLD.status NOT IN (200, 413, 429, 499, 500, 504, 508)
|
|
246
326
|
BEGIN
|
|
247
327
|
INSERT INTO ambient_events (
|
|
248
|
-
workspace_id, producer_worker_id,
|
|
249
|
-
|
|
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
|
|
250
332
|
)
|
|
251
|
-
SELECT w.workspace_id, NEW.worker_id,
|
|
252
|
-
|
|
253
|
-
|
|
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
|
|
254
338
|
FROM workers w
|
|
255
|
-
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
|
+
);
|
|
256
347
|
END;
|
|
257
348
|
|
|
258
349
|
-- turns
|
|
@@ -268,7 +359,7 @@ CREATE TABLE IF NOT EXISTS turns (
|
|
|
268
359
|
-- {§turn-record}: producer and purpose define the turn. Packet/provider
|
|
269
360
|
-- evidence below is an optional inference specialization.
|
|
270
361
|
producer TEXT NOT NULL CHECK (producer IN ('model', 'client', '_plurnk', 'plugin')),
|
|
271
|
-
kind TEXT NOT NULL CHECK (kind IN ('inference', 'initialization', 'overflow', 'operation')),
|
|
362
|
+
kind TEXT NOT NULL CHECK (kind IN ('inference', 'initialization', 'overflow', 'operation', 'maintenance')),
|
|
272
363
|
status INTEGER NOT NULL CHECK (status BETWEEN 100 AND 599),
|
|
273
364
|
-- NULL while the producer still owns this turn. Status 102 is both the
|
|
274
365
|
-- provisional running value and the exact completed continue disposition;
|
|
@@ -653,15 +744,18 @@ CREATE TABLE IF NOT EXISTS derivations (
|
|
|
653
744
|
) STRICT;
|
|
654
745
|
|
|
655
746
|
-- entries
|
|
656
|
-
-- The canonical addressable store. (
|
|
747
|
+
-- The canonical addressable store. (owner, scheme, authority, pathname) is the identity
|
|
657
748
|
-- tuple, and NO component may be NULL: NULLs are distinct under SQL UNIQUE, so a nullable
|
|
658
749
|
-- component voids the identity index. Bare/file paths persist under the reserved `file`
|
|
659
750
|
-- scheme; they still render as bare paths. {§entry-identity-no-null}
|
|
660
751
|
CREATE TABLE IF NOT EXISTS entries (
|
|
661
752
|
id INTEGER NOT NULL PRIMARY KEY,
|
|
662
753
|
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
|
|
663
|
-
workspace_id INTEGER,
|
|
664
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 '',
|
|
665
759
|
pathname TEXT NOT NULL,
|
|
666
760
|
-- {§entry-owner} — every entry is owned by a worker: the spawning worker for capability
|
|
667
761
|
-- streams, the workspace's reserved 'commons' worker for shared content. A real row, never
|
|
@@ -675,9 +769,9 @@ CREATE TABLE IF NOT EXISTS entries (
|
|
|
675
769
|
-- SPEC {§membership} — how a file member entered the curated surface. 'git' rows are
|
|
676
770
|
-- reconciled against the repo's members each turn — tracked ls-files PLUS untracked-
|
|
677
771
|
-- but-not-ignored files ({§membership-auto-add}) — registered + un-registered so entries
|
|
678
|
-
-- == members; '
|
|
772
|
+
-- == members; 'constraint' rows are reconciled from ordinary pick policy.
|
|
679
773
|
-- NULL = not a file member (other schemes don't carry origin).
|
|
680
|
-
membership_origin TEXT CHECK (membership_origin IS NULL OR membership_origin IN ('git', '
|
|
774
|
+
membership_origin TEXT CHECK (membership_origin IS NULL OR membership_origin IN ('git', 'constraint')),
|
|
681
775
|
-- SPEC {§membership-change-gated-sync} — the per-member sync stat-detect:
|
|
682
776
|
-- "<mtimeMs>:<size>" of the disk file at its last materialization, or `absent`
|
|
683
777
|
-- after an observed deletion. The pre-turn
|
|
@@ -689,8 +783,6 @@ CREATE TABLE IF NOT EXISTS entries (
|
|
|
689
783
|
-- by it ASC so dormant entries hold the stable prompt-cache prefix. Private derivation
|
|
690
784
|
-- attachment does not make an entry recently touched.
|
|
691
785
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
692
|
-
CHECK (workspace_id IS NOT NULL),
|
|
693
|
-
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
694
786
|
FOREIGN KEY (owner_id) REFERENCES workers(id) ON DELETE CASCADE
|
|
695
787
|
) STRICT;
|
|
696
788
|
|
|
@@ -698,7 +790,8 @@ CREATE TABLE IF NOT EXISTS entries (
|
|
|
698
790
|
-- Concurrent workers' capability streams share the loop-relative coordinate (every worker's first
|
|
699
791
|
-- loop is seq 1), so identity keys on the owner and identical coordinates are distinct rows
|
|
700
792
|
-- ({§stream-owner-scoped}).
|
|
701
|
-
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);
|
|
702
795
|
|
|
703
796
|
-- The ONE engine-imposed constraint (SPEC {§stream-constraints}, {§stream-constraints-engine-one-cap}): 100 MiB char-length cap
|
|
704
797
|
-- per channel content body. All other limits are extrinsic.
|
|
@@ -867,12 +960,19 @@ CREATE TABLE IF NOT EXISTS log_entries (
|
|
|
867
960
|
-- Engine-owned occurrence identity. Source rows are stamped NULL→id by the
|
|
868
961
|
-- journal trigger; observer and fork copies carry it at insertion.
|
|
869
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)),
|
|
870
966
|
-- Search derivation attached to this durable log result, when available.
|
|
871
967
|
deep_hash TEXT REFERENCES derivations(deep_hash),
|
|
872
968
|
-- Exact logical provider call represented by a BARE result, admitted
|
|
873
969
|
-- model turnOps, or rejected emissionAttempt. Other operation and ambient
|
|
874
970
|
-- rows carry no model-call identity.
|
|
875
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,
|
|
876
976
|
|
|
877
977
|
-- 'error' is an ACTIONLESS row ({§operation-results} — errors are log items): a parse failure that
|
|
878
978
|
-- produced no op still records a log entry (op='error', status_rx≥400, no target) so the model
|
|
@@ -938,6 +1038,9 @@ CREATE INDEX IF NOT EXISTS log_entries_at ON log_entries (a
|
|
|
938
1038
|
CREATE UNIQUE INDEX IF NOT EXISTS log_entries_model_call_id
|
|
939
1039
|
ON log_entries (model_call_id)
|
|
940
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;
|
|
941
1044
|
CREATE UNIQUE INDEX IF NOT EXISTS log_entries_worker_ambient_event
|
|
942
1045
|
ON log_entries (worker_id, ambient_event_id)
|
|
943
1046
|
WHERE ambient_event_id IS NOT NULL;
|
|
@@ -1157,7 +1260,7 @@ BEGIN
|
|
|
1157
1260
|
SELECT json_group_array(tag)
|
|
1158
1261
|
FROM (SELECT tag FROM log_tags WHERE log_entry_id = NEW.id ORDER BY tag)
|
|
1159
1262
|
), '[]')
|
|
1160
|
-
WHERE kind = '
|
|
1263
|
+
WHERE kind = 'activity'
|
|
1161
1264
|
AND producer_worker_id = NEW.worker_id
|
|
1162
1265
|
AND source_record_id = NEW.id;
|
|
1163
1266
|
END;
|
|
@@ -1185,6 +1288,15 @@ BEGIN
|
|
|
1185
1288
|
END
|
|
1186
1289
|
FROM turns
|
|
1187
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'
|
|
1298
|
+
AND producer_worker_id = NEW.worker_id
|
|
1299
|
+
AND source_record_id = NEW.id;
|
|
1188
1300
|
END;
|
|
1189
1301
|
|
|
1190
1302
|
-- Overflow admission reclassifies a would-be inference turn only after its
|
|
@@ -1204,6 +1316,24 @@ BEGIN
|
|
|
1204
1316
|
END
|
|
1205
1317
|
FROM log_entries
|
|
1206
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
|
+
);
|
|
1207
1337
|
END;
|
|
1208
1338
|
|
|
1209
1339
|
-- Successful OPEN/FOLD rows are durable curation events even though their
|
|
@@ -1531,7 +1661,7 @@ END;
|
|
|
1531
1661
|
-- private-payload removal cannot exempt changes to any other core column.
|
|
1532
1662
|
CREATE TRIGGER IF NOT EXISTS log_entries_immutable_core
|
|
1533
1663
|
BEFORE UPDATE OF
|
|
1534
|
-
worker_id, loop_id, turn_id, sequence, at, origin, source, model_call_id,
|
|
1664
|
+
worker_id, loop_id, turn_id, sequence, at, origin, source, inherited_history, model_call_id,
|
|
1535
1665
|
op, delimiter, signal,
|
|
1536
1666
|
scheme, username, password, hostname,
|
|
1537
1667
|
port, pathname, query, fragment,
|
|
@@ -1560,72 +1690,168 @@ BEGIN
|
|
|
1560
1690
|
SELECT RAISE(ABORT, 'log_entries ambient event identity may only be assigned once');
|
|
1561
1691
|
END;
|
|
1562
1692
|
|
|
1563
|
-
--
|
|
1564
|
-
--
|
|
1565
|
-
|
|
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.
|
|
1566
1782
|
CREATE TRIGGER IF NOT EXISTS log_entries_append_ambient_event_insert
|
|
1567
1783
|
AFTER INSERT ON log_entries
|
|
1568
|
-
WHEN NEW.
|
|
1569
|
-
AND NEW.op = 'EDIT'
|
|
1570
|
-
AND NEW.state = 'resolved'
|
|
1571
|
-
AND NEW.status_rx IN (200, 201)
|
|
1572
|
-
AND (NEW.scheme IS NULL OR NEW.scheme != 'plurnk')
|
|
1573
|
-
AND (NEW.scheme IS NULL OR NEW.scheme != 'worker' OR NEW.hostname IS NULL OR NEW.hostname = 'plurnk')
|
|
1574
|
-
AND (
|
|
1575
|
-
NEW.origin != '_plurnk'
|
|
1576
|
-
OR EXISTS (SELECT 1 FROM workers w WHERE w.id = NEW.worker_id AND w.name = 'plurnk')
|
|
1577
|
-
)
|
|
1784
|
+
WHEN NEW.state != 'proposed'
|
|
1578
1785
|
BEGIN
|
|
1579
1786
|
INSERT INTO ambient_events (
|
|
1580
|
-
workspace_id, producer_worker_id,
|
|
1581
|
-
|
|
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
|
|
1582
1793
|
)
|
|
1583
|
-
SELECT
|
|
1584
|
-
'
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
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
|
+
);
|
|
1595
1817
|
END;
|
|
1596
1818
|
|
|
1597
|
-
-- A proposed
|
|
1598
|
-
-- resolves successfully. Rejection, cancellation, and failed application publish nothing.
|
|
1819
|
+
-- A proposed operation becomes activity only when its lifecycle settles.
|
|
1599
1820
|
CREATE TRIGGER IF NOT EXISTS log_entries_append_ambient_event_resolve
|
|
1600
|
-
AFTER UPDATE OF state, status_rx ON log_entries
|
|
1601
|
-
WHEN OLD.state = 'proposed'
|
|
1602
|
-
AND NEW.state = 'resolved'
|
|
1603
|
-
AND NEW.ambient_event_id IS NULL
|
|
1604
|
-
AND NEW.op = 'EDIT'
|
|
1605
|
-
AND NEW.status_rx IN (200, 201)
|
|
1606
|
-
AND (NEW.scheme IS NULL OR NEW.scheme != 'plurnk')
|
|
1607
|
-
AND (NEW.scheme IS NULL OR NEW.scheme != 'worker' OR NEW.hostname IS NULL OR NEW.hostname = 'plurnk')
|
|
1608
|
-
AND (
|
|
1609
|
-
NEW.origin != '_plurnk'
|
|
1610
|
-
OR EXISTS (SELECT 1 FROM workers w WHERE w.id = NEW.worker_id AND w.name = 'plurnk')
|
|
1611
|
-
)
|
|
1821
|
+
AFTER UPDATE OF state, status_rx, rx, outcome ON log_entries
|
|
1822
|
+
WHEN OLD.state = 'proposed' AND NEW.state != 'proposed'
|
|
1612
1823
|
BEGIN
|
|
1613
1824
|
INSERT INTO ambient_events (
|
|
1614
|
-
workspace_id, producer_worker_id,
|
|
1615
|
-
|
|
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
|
|
1616
1831
|
)
|
|
1617
|
-
SELECT
|
|
1618
|
-
'
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
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
|
+
);
|
|
1629
1855
|
END;
|
|
1630
1856
|
|
|
1631
1857
|
-- {§client-interactions}: the durable discoverable half of a client-owned
|
|
@@ -1652,7 +1878,8 @@ CREATE INDEX IF NOT EXISTS client_interactions_worker_id_id
|
|
|
1652
1878
|
-- Durable subscription lifecycle per SPEC {§subscriptions}. The row records what
|
|
1653
1879
|
-- the worker holds and routes cancellation to a separate process-local callable;
|
|
1654
1880
|
-- it never serializes that callable. Closed rows persist for forensics; partial
|
|
1655
|
-
-- 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.
|
|
1656
1883
|
CREATE TABLE IF NOT EXISTS subscriptions (
|
|
1657
1884
|
id INTEGER NOT NULL PRIMARY KEY,
|
|
1658
1885
|
version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
|
|
@@ -1671,14 +1898,15 @@ CREATE TABLE IF NOT EXISTS subscriptions (
|
|
|
1671
1898
|
closed_at TEXT,
|
|
1672
1899
|
close_status INTEGER CHECK (close_status IS NULL OR (close_status BETWEEN 100 AND 599)),
|
|
1673
1900
|
close_result TEXT CHECK (close_result IS NULL OR json_valid(close_result)),
|
|
1674
|
-
CHECK (
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
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
|
|
1678
1906
|
) STRICT;
|
|
1679
1907
|
|
|
1680
1908
|
CREATE UNIQUE INDEX IF NOT EXISTS subscriptions_active_one_per_entry
|
|
1681
|
-
ON subscriptions (
|
|
1909
|
+
ON subscriptions (entry_id)
|
|
1682
1910
|
WHERE closed_at IS NULL;
|
|
1683
1911
|
|
|
1684
1912
|
CREATE INDEX IF NOT EXISTS subscriptions_scheme_active
|
|
@@ -1689,13 +1917,26 @@ CREATE INDEX IF NOT EXISTS subscriptions_opened_at ON subscriptions (opened_at);
|
|
|
1689
1917
|
|
|
1690
1918
|
CREATE TRIGGER IF NOT EXISTS subscriptions_result_contract_insert
|
|
1691
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
|
|
1692
1930
|
WHEN NOT (
|
|
1693
|
-
(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)
|
|
1694
1932
|
OR (
|
|
1695
1933
|
NEW.closed_at IS NOT NULL
|
|
1696
1934
|
AND NEW.close_status IS NOT NULL
|
|
1697
1935
|
AND NEW.close_result IS NOT NULL
|
|
1936
|
+
AND NEW.channel_results IS NOT NULL
|
|
1698
1937
|
AND json_valid(NEW.close_result)
|
|
1938
|
+
AND json_valid(NEW.channel_results)
|
|
1939
|
+
AND json_type(NEW.channel_results) = 'object'
|
|
1699
1940
|
AND json_type(NEW.close_result, '$.status') = 'integer'
|
|
1700
1941
|
AND json_extract(NEW.close_result, '$.status') = NEW.close_status
|
|
1701
1942
|
AND (
|
|
@@ -1712,35 +1953,142 @@ BEGIN
|
|
|
1712
1953
|
SELECT RAISE(ABORT, 'subscription terminal result violates the operation-result contract');
|
|
1713
1954
|
END;
|
|
1714
1955
|
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
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
|
+
)
|
|
1734
1985
|
)
|
|
1735
1986
|
)
|
|
1736
1987
|
BEGIN
|
|
1737
|
-
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;
|
|
1738
2085
|
END;
|
|
1739
2086
|
|
|
1740
2087
|
-- (worker_watermarks removed — {§env-delta} is now pull-from-log, no per-worker snapshot.)
|
|
1741
2088
|
|
|
1742
2089
|
-- workspace_constraints
|
|
1743
|
-
-- 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.
|
|
1744
2092
|
-- Per (workspace, effect, glob/target): `pick` (members git misses, resolved by a
|
|
1745
2093
|
-- targeted client-dictated scan), `hide` (drop git-tracked matches), `view` (member
|
|
1746
2094
|
-- for read; File.edit rejects the write). git-absent, `pick` rows are the sole substrate
|
|
@@ -1749,6 +2097,8 @@ CREATE TABLE IF NOT EXISTS workspace_constraints (
|
|
|
1749
2097
|
workspace_id INTEGER NOT NULL,
|
|
1750
2098
|
effect TEXT NOT NULL CHECK (effect IN ('pick', 'hide', 'view')),
|
|
1751
2099
|
glob TEXT NOT NULL,
|
|
2100
|
+
source TEXT NOT NULL CHECK (source IN ('explicit', 'create')),
|
|
2101
|
+
CHECK (source = 'explicit' OR effect = 'pick'),
|
|
1752
2102
|
PRIMARY KEY (workspace_id, effect, glob),
|
|
1753
2103
|
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
|
|
1754
2104
|
) STRICT, WITHOUT ROWID;
|