@opengeni/db 3.5.2-canary.0 → 3.6.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/dist/canonical-human-identities.js +3 -3
- package/dist/{chunk-NRGUX3M5.js → chunk-2NT476HD.js} +2 -2
- package/dist/{chunk-PERPE2UY.js → chunk-744DFC65.js} +31 -1
- package/dist/chunk-744DFC65.js.map +1 -0
- package/dist/{chunk-FJHEWYV5.js → chunk-CDMMSUWJ.js} +3 -3
- package/dist/{chunk-TRCYF26N.js → chunk-G3732M3T.js} +2 -2
- package/dist/{chunk-ORKVXHL2.js → chunk-IW6O6G7R.js} +2 -2
- package/dist/{chunk-Y44FKTYU.js → chunk-Q5YUGQVK.js} +3 -3
- package/dist/{chunk-U7NKCX5E.js → chunk-UGDVSQ6U.js} +2 -2
- package/dist/{chunk-6NYKC5DD.js → chunk-UJ2EW3QI.js} +4729 -4583
- package/dist/chunk-UJ2EW3QI.js.map +1 -0
- package/dist/{chunk-X6T2P3L3.js → chunk-WW7AJ5IC.js} +2 -2
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +23 -3
- package/dist/index.js +2112 -1436
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +5 -4
- package/dist/schema.d.ts +7 -6
- package/dist/schema.js +7 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/work-claims-schema.d.ts +975 -0
- package/dist/work-claims.d.ts +83 -0
- package/drizzle/0365_permission_scoped_work_claims.sql +1306 -0
- package/drizzle/0366_session_discovery_title_search_index.sql +5 -0
- package/drizzle/0367_session_discovery_goal_search_index.sql +5 -0
- package/drizzle/0368_session_discovery_claim_search_index.sql +6 -0
- package/package.json +6 -6
- package/src/index.ts +1051 -330
- package/src/provision-roles.ts +18 -0
- package/src/runtime-posture.ts +12 -0
- package/src/schema.ts +1 -0
- package/src/work-claims-schema.ts +148 -0
- package/src/work-claims.ts +200 -0
- package/dist/chunk-6NYKC5DD.js.map +0 -1
- package/dist/chunk-PERPE2UY.js.map +0 -1
- /package/dist/{chunk-NRGUX3M5.js.map → chunk-2NT476HD.js.map} +0 -0
- /package/dist/{chunk-FJHEWYV5.js.map → chunk-CDMMSUWJ.js.map} +0 -0
- /package/dist/{chunk-TRCYF26N.js.map → chunk-G3732M3T.js.map} +0 -0
- /package/dist/{chunk-ORKVXHL2.js.map → chunk-IW6O6G7R.js.map} +0 -0
- /package/dist/{chunk-Y44FKTYU.js.map → chunk-Q5YUGQVK.js.map} +0 -0
- /package/dist/{chunk-U7NKCX5E.js.map → chunk-UGDVSQ6U.js.map} +0 -0
- /package/dist/{chunk-X6T2P3L3.js.map → chunk-WW7AJ5IC.js.map} +0 -0
|
@@ -0,0 +1,1306 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Durable, non-exclusive work claims for permission-scoped discovery. Claims
|
|
3
|
+
-- are evidence only: they grant no authority, acquire no lock/lease, and never
|
|
4
|
+
-- block another session from claiming the same typed subject.
|
|
5
|
+
|
|
6
|
+
SET LOCAL lock_timeout = '5s';
|
|
7
|
+
SET LOCAL statement_timeout = '10min';
|
|
8
|
+
|
|
9
|
+
-- Freeze the active data schema into every SECURITY DEFINER routine. This is
|
|
10
|
+
-- public for standalone installs and the host-selected schema when embedded.
|
|
11
|
+
SELECT pg_catalog.set_config(
|
|
12
|
+
'search_path',
|
|
13
|
+
pg_catalog.format('%I, pg_catalog, pg_temp', pg_catalog.current_schema()),
|
|
14
|
+
true
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE TABLE "session_work_claims" (
|
|
18
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
19
|
+
"account_id" uuid NOT NULL,
|
|
20
|
+
"workspace_id" uuid NOT NULL,
|
|
21
|
+
"session_id" uuid NOT NULL,
|
|
22
|
+
"root_session_id" uuid NOT NULL,
|
|
23
|
+
"subject_namespace" text NOT NULL,
|
|
24
|
+
"subject_type" text NOT NULL,
|
|
25
|
+
"canonical_key" text NOT NULL,
|
|
26
|
+
"subject_digest" text NOT NULL,
|
|
27
|
+
"display_label" text,
|
|
28
|
+
"role" text NOT NULL,
|
|
29
|
+
"state" text DEFAULT 'active' NOT NULL,
|
|
30
|
+
"revision" integer DEFAULT 1 NOT NULL,
|
|
31
|
+
"provenance" text NOT NULL,
|
|
32
|
+
"version_kind" text,
|
|
33
|
+
"version_value" text,
|
|
34
|
+
"observed_at" timestamptz NOT NULL,
|
|
35
|
+
"created_at" timestamptz DEFAULT now() NOT NULL,
|
|
36
|
+
"updated_at" timestamptz DEFAULT now() NOT NULL,
|
|
37
|
+
"settled_at" timestamptz,
|
|
38
|
+
CONSTRAINT "session_work_claims_workspace_account_fk"
|
|
39
|
+
FOREIGN KEY ("workspace_id", "account_id")
|
|
40
|
+
REFERENCES "workspaces" ("id", "account_id") ON DELETE CASCADE,
|
|
41
|
+
CONSTRAINT "session_work_claims_session_fk"
|
|
42
|
+
FOREIGN KEY ("workspace_id", "session_id")
|
|
43
|
+
REFERENCES "sessions" ("workspace_id", "id") ON DELETE CASCADE,
|
|
44
|
+
CONSTRAINT "session_work_claims_root_session_fk"
|
|
45
|
+
FOREIGN KEY ("workspace_id", "root_session_id")
|
|
46
|
+
REFERENCES "sessions" ("workspace_id", "id") ON DELETE CASCADE,
|
|
47
|
+
CONSTRAINT "session_work_claims_namespace_check" CHECK (
|
|
48
|
+
"subject_namespace" = lower(btrim("subject_namespace"))
|
|
49
|
+
AND octet_length("subject_namespace") BETWEEN 1 AND 64
|
|
50
|
+
AND "subject_namespace" ~ '^[a-z0-9]([a-z0-9._:-]{0,62}[a-z0-9])?$'
|
|
51
|
+
),
|
|
52
|
+
CONSTRAINT "session_work_claims_type_check" CHECK (
|
|
53
|
+
"subject_type" IN (
|
|
54
|
+
'repository','branch','pull_request','issue','artifact','release','ci_run','other'
|
|
55
|
+
)
|
|
56
|
+
),
|
|
57
|
+
CONSTRAINT "session_work_claims_key_check" CHECK (
|
|
58
|
+
"canonical_key" = btrim("canonical_key")
|
|
59
|
+
AND "canonical_key" = normalize("canonical_key", NFKC)
|
|
60
|
+
AND octet_length("canonical_key") BETWEEN 1 AND 512
|
|
61
|
+
AND "canonical_key" !~ '[[:cntrl:]]'
|
|
62
|
+
),
|
|
63
|
+
CONSTRAINT "session_work_claims_digest_check" CHECK (
|
|
64
|
+
"subject_digest" ~ '^[0-9a-f]{64}$'
|
|
65
|
+
),
|
|
66
|
+
CONSTRAINT "session_work_claims_label_check" CHECK (
|
|
67
|
+
"display_label" IS NULL OR (
|
|
68
|
+
"display_label" = btrim("display_label")
|
|
69
|
+
AND "display_label" = normalize("display_label", NFKC)
|
|
70
|
+
AND octet_length("display_label") BETWEEN 1 AND 256
|
|
71
|
+
AND "display_label" !~ '[[:cntrl:]]'
|
|
72
|
+
)
|
|
73
|
+
),
|
|
74
|
+
CONSTRAINT "session_work_claims_role_check" CHECK (
|
|
75
|
+
"role" IN ('working','reviewing','monitoring','delivering')
|
|
76
|
+
),
|
|
77
|
+
CONSTRAINT "session_work_claims_state_check" CHECK (
|
|
78
|
+
"state" IN ('active','released','superseded','stale')
|
|
79
|
+
),
|
|
80
|
+
CONSTRAINT "session_work_claims_revision_check" CHECK ("revision" > 0),
|
|
81
|
+
CONSTRAINT "session_work_claims_provenance_check" CHECK (
|
|
82
|
+
"provenance" IN (
|
|
83
|
+
'explicit_agent','user_api','trusted_integration','session_resource','system_lifecycle'
|
|
84
|
+
)
|
|
85
|
+
),
|
|
86
|
+
CONSTRAINT "session_work_claims_version_check" CHECK (
|
|
87
|
+
("version_kind" IS NULL AND "version_value" IS NULL)
|
|
88
|
+
OR (
|
|
89
|
+
"version_kind" IN (
|
|
90
|
+
'git_commit','branch_head','pull_request_head','artifact_version',
|
|
91
|
+
'release_version','ci_run','other'
|
|
92
|
+
)
|
|
93
|
+
AND "version_value" = btrim("version_value")
|
|
94
|
+
AND "version_value" = normalize("version_value", NFKC)
|
|
95
|
+
AND octet_length("version_value") BETWEEN 1 AND 256
|
|
96
|
+
AND "version_value" !~ '[[:cntrl:]]'
|
|
97
|
+
)
|
|
98
|
+
),
|
|
99
|
+
CONSTRAINT "session_work_claims_lifecycle_check" CHECK (
|
|
100
|
+
("state" = 'active' AND "settled_at" IS NULL)
|
|
101
|
+
OR ("state" <> 'active' AND "settled_at" IS NOT NULL)
|
|
102
|
+
),
|
|
103
|
+
CONSTRAINT "session_work_claims_timestamp_check" CHECK (
|
|
104
|
+
"observed_at" >= "created_at"
|
|
105
|
+
AND "updated_at" >= "observed_at"
|
|
106
|
+
AND ("settled_at" IS NULL OR "settled_at" >= "created_at")
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
CREATE UNIQUE INDEX "session_work_claims_workspace_id_uq"
|
|
111
|
+
ON "session_work_claims" ("workspace_id", "id");
|
|
112
|
+
CREATE UNIQUE INDEX "session_work_claims_active_identity_uq"
|
|
113
|
+
ON "session_work_claims" (
|
|
114
|
+
"workspace_id", "session_id", "subject_namespace", "subject_type", "subject_digest", "role"
|
|
115
|
+
) WHERE "state" = 'active';
|
|
116
|
+
CREATE INDEX "session_work_claims_session_state_idx"
|
|
117
|
+
ON "session_work_claims" (
|
|
118
|
+
"workspace_id", "session_id", "state", "updated_at" DESC, "id" DESC
|
|
119
|
+
);
|
|
120
|
+
CREATE INDEX "session_work_claims_subject_state_idx"
|
|
121
|
+
ON "session_work_claims" (
|
|
122
|
+
"workspace_id", "subject_namespace", "subject_type", "subject_digest", "state",
|
|
123
|
+
"updated_at" DESC, "id" DESC
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
CREATE TABLE "session_work_claim_revisions" (
|
|
127
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
128
|
+
"account_id" uuid NOT NULL,
|
|
129
|
+
"workspace_id" uuid NOT NULL,
|
|
130
|
+
"claim_id" uuid NOT NULL,
|
|
131
|
+
"session_id" uuid NOT NULL,
|
|
132
|
+
"root_session_id" uuid NOT NULL,
|
|
133
|
+
"operation_id" uuid NOT NULL,
|
|
134
|
+
"input_hash" text NOT NULL,
|
|
135
|
+
"mutation_kind" text NOT NULL,
|
|
136
|
+
"prior_revision" integer,
|
|
137
|
+
"resulting_revision" integer NOT NULL,
|
|
138
|
+
"subject_namespace" text NOT NULL,
|
|
139
|
+
"subject_type" text NOT NULL,
|
|
140
|
+
"canonical_key" text NOT NULL,
|
|
141
|
+
"subject_digest" text NOT NULL,
|
|
142
|
+
"display_label" text,
|
|
143
|
+
"role" text NOT NULL,
|
|
144
|
+
"state" text NOT NULL,
|
|
145
|
+
"provenance" text NOT NULL,
|
|
146
|
+
"version_kind" text,
|
|
147
|
+
"version_value" text,
|
|
148
|
+
"observed_at" timestamptz NOT NULL,
|
|
149
|
+
"claim_created_at" timestamptz NOT NULL,
|
|
150
|
+
"claim_updated_at" timestamptz NOT NULL,
|
|
151
|
+
"settled_at" timestamptz,
|
|
152
|
+
"actor_kind" text NOT NULL,
|
|
153
|
+
"actor_subject_id" text NOT NULL,
|
|
154
|
+
"actor_session_id" uuid,
|
|
155
|
+
"actor_turn_id" uuid,
|
|
156
|
+
"actor_attempt_id" uuid,
|
|
157
|
+
"actor_execution_generation" integer,
|
|
158
|
+
"reason" text,
|
|
159
|
+
"created_at" timestamptz DEFAULT now() NOT NULL,
|
|
160
|
+
CONSTRAINT "session_work_claim_revisions_workspace_account_fk"
|
|
161
|
+
FOREIGN KEY ("workspace_id", "account_id")
|
|
162
|
+
REFERENCES "workspaces" ("id", "account_id") ON DELETE CASCADE,
|
|
163
|
+
CONSTRAINT "session_work_claim_revisions_claim_fk"
|
|
164
|
+
FOREIGN KEY ("workspace_id", "claim_id")
|
|
165
|
+
REFERENCES "session_work_claims" ("workspace_id", "id") ON DELETE CASCADE,
|
|
166
|
+
CONSTRAINT "session_work_claim_revisions_session_fk"
|
|
167
|
+
FOREIGN KEY ("workspace_id", "session_id")
|
|
168
|
+
REFERENCES "sessions" ("workspace_id", "id") ON DELETE CASCADE,
|
|
169
|
+
CONSTRAINT "session_work_claim_revisions_root_session_fk"
|
|
170
|
+
FOREIGN KEY ("workspace_id", "root_session_id")
|
|
171
|
+
REFERENCES "sessions" ("workspace_id", "id") ON DELETE CASCADE,
|
|
172
|
+
CONSTRAINT "session_work_claim_revisions_actor_session_fk"
|
|
173
|
+
FOREIGN KEY ("workspace_id", "actor_session_id")
|
|
174
|
+
REFERENCES "sessions" ("workspace_id", "id") ON DELETE RESTRICT,
|
|
175
|
+
CONSTRAINT "session_work_claim_revisions_actor_turn_fk"
|
|
176
|
+
FOREIGN KEY ("workspace_id", "actor_turn_id")
|
|
177
|
+
REFERENCES "session_turns" ("workspace_id", "id") ON DELETE RESTRICT,
|
|
178
|
+
CONSTRAINT "session_work_claim_revisions_actor_attempt_fk"
|
|
179
|
+
FOREIGN KEY ("workspace_id", "actor_attempt_id")
|
|
180
|
+
REFERENCES "session_turn_attempts" ("workspace_id", "id") ON DELETE RESTRICT,
|
|
181
|
+
CONSTRAINT "session_work_claim_revisions_hash_check" CHECK (
|
|
182
|
+
"input_hash" ~ '^[0-9a-f]{64}$' AND "subject_digest" ~ '^[0-9a-f]{64}$'
|
|
183
|
+
),
|
|
184
|
+
CONSTRAINT "session_work_claim_revisions_mutation_check" CHECK (
|
|
185
|
+
"mutation_kind" IN ('created','updated','released','superseded','stale')
|
|
186
|
+
AND (("mutation_kind" = 'created' AND "prior_revision" IS NULL AND "resulting_revision" = 1)
|
|
187
|
+
OR ("mutation_kind" <> 'created' AND "prior_revision" > 0
|
|
188
|
+
AND "resulting_revision" = "prior_revision" + 1))
|
|
189
|
+
),
|
|
190
|
+
CONSTRAINT "session_work_claim_revisions_actor_check" CHECK (
|
|
191
|
+
"actor_kind" IN ('agent_attempt','human','integration','system')
|
|
192
|
+
AND octet_length("actor_subject_id") BETWEEN 1 AND 4096
|
|
193
|
+
AND (
|
|
194
|
+
("actor_kind" = 'agent_attempt'
|
|
195
|
+
AND "actor_session_id" IS NOT NULL
|
|
196
|
+
AND "actor_turn_id" IS NOT NULL
|
|
197
|
+
AND "actor_attempt_id" IS NOT NULL
|
|
198
|
+
AND "actor_execution_generation" > 0)
|
|
199
|
+
OR
|
|
200
|
+
("actor_kind" <> 'agent_attempt'
|
|
201
|
+
AND "actor_attempt_id" IS NULL
|
|
202
|
+
AND "actor_execution_generation" IS NULL)
|
|
203
|
+
)
|
|
204
|
+
),
|
|
205
|
+
CONSTRAINT "session_work_claim_revisions_reason_check" CHECK (
|
|
206
|
+
"reason" IS NULL OR "reason" IN (
|
|
207
|
+
'completed','cancelled','failed','superseded','no_longer_active',
|
|
208
|
+
'corrected','external_state_changed','other'
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
CREATE UNIQUE INDEX "session_work_claim_revisions_workspace_operation_uq"
|
|
214
|
+
ON "session_work_claim_revisions" ("workspace_id", "operation_id");
|
|
215
|
+
CREATE UNIQUE INDEX "session_work_claim_revisions_claim_revision_uq"
|
|
216
|
+
ON "session_work_claim_revisions" ("workspace_id", "claim_id", "resulting_revision");
|
|
217
|
+
CREATE INDEX "session_work_claim_revisions_session_timeline_idx"
|
|
218
|
+
ON "session_work_claim_revisions" (
|
|
219
|
+
"workspace_id", "session_id", "created_at" DESC, "id" DESC
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
CREATE TABLE "session_work_claim_write_capabilities" (
|
|
223
|
+
"backend_pid" integer NOT NULL,
|
|
224
|
+
"transaction_id" xid8 NOT NULL,
|
|
225
|
+
"capability_id" uuid NOT NULL,
|
|
226
|
+
PRIMARY KEY ("backend_pid", "transaction_id", "capability_id")
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
ALTER TABLE "session_work_claims" ENABLE ROW LEVEL SECURITY;
|
|
230
|
+
ALTER TABLE "session_work_claims" FORCE ROW LEVEL SECURITY;
|
|
231
|
+
ALTER TABLE "session_work_claim_revisions" ENABLE ROW LEVEL SECURITY;
|
|
232
|
+
ALTER TABLE "session_work_claim_revisions" FORCE ROW LEVEL SECURITY;
|
|
233
|
+
ALTER TABLE "session_work_claim_write_capabilities" ENABLE ROW LEVEL SECURITY;
|
|
234
|
+
ALTER TABLE "session_work_claim_write_capabilities" FORCE ROW LEVEL SECURITY;
|
|
235
|
+
|
|
236
|
+
CREATE OR REPLACE FUNCTION opengeni_private.session_work_claim_capability_active()
|
|
237
|
+
RETURNS boolean
|
|
238
|
+
LANGUAGE sql
|
|
239
|
+
STABLE
|
|
240
|
+
SECURITY DEFINER
|
|
241
|
+
SET search_path FROM CURRENT
|
|
242
|
+
AS $$
|
|
243
|
+
SELECT EXISTS (
|
|
244
|
+
SELECT 1 FROM session_work_claim_write_capabilities capability
|
|
245
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
246
|
+
AND capability.transaction_id = pg_current_xact_id_if_assigned()
|
|
247
|
+
AND capability.capability_id = nullif(
|
|
248
|
+
current_setting('opengeni.session_work_claim_write_capability', true), ''
|
|
249
|
+
)::uuid
|
|
250
|
+
)
|
|
251
|
+
$$;
|
|
252
|
+
REVOKE ALL ON FUNCTION opengeni_private.session_work_claim_capability_active() FROM PUBLIC;
|
|
253
|
+
|
|
254
|
+
DO $work_claim_policies$
|
|
255
|
+
DECLARE
|
|
256
|
+
data_schema text := current_schema();
|
|
257
|
+
migration_owner text := current_user;
|
|
258
|
+
BEGIN
|
|
259
|
+
EXECUTE format(
|
|
260
|
+
'CREATE POLICY session_work_claim_capability_owner ON %I.session_work_claim_write_capabilities '
|
|
261
|
+
|| 'FOR ALL USING (current_user = %L) WITH CHECK (current_user = %L)',
|
|
262
|
+
data_schema, migration_owner, migration_owner
|
|
263
|
+
);
|
|
264
|
+
END
|
|
265
|
+
$work_claim_policies$;
|
|
266
|
+
|
|
267
|
+
CREATE POLICY session_work_claims_tenant ON "session_work_claims"
|
|
268
|
+
USING (
|
|
269
|
+
"account_id" = opengeni_private.current_account_id()
|
|
270
|
+
AND "workspace_id" = opengeni_private.current_workspace_id()
|
|
271
|
+
)
|
|
272
|
+
WITH CHECK (
|
|
273
|
+
"account_id" = opengeni_private.current_account_id()
|
|
274
|
+
AND "workspace_id" = opengeni_private.current_workspace_id()
|
|
275
|
+
);
|
|
276
|
+
CREATE POLICY session_visibility_isolation ON "session_work_claims" AS RESTRICTIVE
|
|
277
|
+
FOR SELECT
|
|
278
|
+
USING (
|
|
279
|
+
session_reference_visible("account_id", "workspace_id", "session_id")
|
|
280
|
+
OR opengeni_private.session_work_claim_capability_active()
|
|
281
|
+
)
|
|
282
|
+
;
|
|
283
|
+
CREATE POLICY session_work_claims_lifecycle_insert ON "session_work_claims" AS RESTRICTIVE
|
|
284
|
+
FOR INSERT WITH CHECK (EXISTS (
|
|
285
|
+
SELECT 1 FROM "session_work_claim_write_capabilities" capability
|
|
286
|
+
WHERE capability."backend_pid" = pg_backend_pid()
|
|
287
|
+
AND capability."transaction_id" = pg_current_xact_id()
|
|
288
|
+
AND capability."capability_id" = nullif(
|
|
289
|
+
current_setting('opengeni.session_work_claim_write_capability', true), ''
|
|
290
|
+
)::uuid
|
|
291
|
+
));
|
|
292
|
+
CREATE POLICY session_work_claims_lifecycle_update ON "session_work_claims" AS RESTRICTIVE
|
|
293
|
+
FOR UPDATE USING (EXISTS (
|
|
294
|
+
SELECT 1 FROM "session_work_claim_write_capabilities" capability
|
|
295
|
+
WHERE capability."backend_pid" = pg_backend_pid()
|
|
296
|
+
AND capability."transaction_id" = pg_current_xact_id()
|
|
297
|
+
AND capability."capability_id" = nullif(
|
|
298
|
+
current_setting('opengeni.session_work_claim_write_capability', true), ''
|
|
299
|
+
)::uuid
|
|
300
|
+
)) WITH CHECK (EXISTS (
|
|
301
|
+
SELECT 1 FROM "session_work_claim_write_capabilities" capability
|
|
302
|
+
WHERE capability."backend_pid" = pg_backend_pid()
|
|
303
|
+
AND capability."transaction_id" = pg_current_xact_id()
|
|
304
|
+
AND capability."capability_id" = nullif(
|
|
305
|
+
current_setting('opengeni.session_work_claim_write_capability', true), ''
|
|
306
|
+
)::uuid
|
|
307
|
+
));
|
|
308
|
+
CREATE POLICY session_work_claims_lifecycle_delete ON "session_work_claims" AS RESTRICTIVE
|
|
309
|
+
FOR DELETE USING (EXISTS (
|
|
310
|
+
SELECT 1 FROM "session_work_claim_write_capabilities" capability
|
|
311
|
+
WHERE capability."backend_pid" = pg_backend_pid()
|
|
312
|
+
AND capability."transaction_id" = pg_current_xact_id()
|
|
313
|
+
AND capability."capability_id" = nullif(
|
|
314
|
+
current_setting('opengeni.session_work_claim_write_capability', true), ''
|
|
315
|
+
)::uuid
|
|
316
|
+
));
|
|
317
|
+
|
|
318
|
+
CREATE POLICY session_work_claim_revisions_tenant ON "session_work_claim_revisions"
|
|
319
|
+
USING (
|
|
320
|
+
"account_id" = opengeni_private.current_account_id()
|
|
321
|
+
AND "workspace_id" = opengeni_private.current_workspace_id()
|
|
322
|
+
)
|
|
323
|
+
WITH CHECK (
|
|
324
|
+
"account_id" = opengeni_private.current_account_id()
|
|
325
|
+
AND "workspace_id" = opengeni_private.current_workspace_id()
|
|
326
|
+
);
|
|
327
|
+
CREATE POLICY session_visibility_isolation ON "session_work_claim_revisions" AS RESTRICTIVE
|
|
328
|
+
FOR SELECT
|
|
329
|
+
USING (
|
|
330
|
+
session_reference_visible("account_id", "workspace_id", "session_id")
|
|
331
|
+
OR opengeni_private.session_work_claim_capability_active()
|
|
332
|
+
)
|
|
333
|
+
;
|
|
334
|
+
CREATE POLICY session_work_claim_revisions_lifecycle_insert
|
|
335
|
+
ON "session_work_claim_revisions" AS RESTRICTIVE
|
|
336
|
+
FOR INSERT WITH CHECK (EXISTS (
|
|
337
|
+
SELECT 1 FROM "session_work_claim_write_capabilities" capability
|
|
338
|
+
WHERE capability."backend_pid" = pg_backend_pid()
|
|
339
|
+
AND capability."transaction_id" = pg_current_xact_id()
|
|
340
|
+
AND capability."capability_id" = nullif(
|
|
341
|
+
current_setting('opengeni.session_work_claim_write_capability', true), ''
|
|
342
|
+
)::uuid
|
|
343
|
+
));
|
|
344
|
+
CREATE POLICY session_work_claim_revisions_immutable_update
|
|
345
|
+
ON "session_work_claim_revisions" AS RESTRICTIVE
|
|
346
|
+
FOR UPDATE USING (false) WITH CHECK (false);
|
|
347
|
+
CREATE POLICY session_work_claim_revisions_immutable_delete
|
|
348
|
+
ON "session_work_claim_revisions" AS RESTRICTIVE
|
|
349
|
+
FOR DELETE USING (false);
|
|
350
|
+
|
|
351
|
+
CREATE OR REPLACE FUNCTION session_work_claim_subject_digest(
|
|
352
|
+
p_namespace text, p_type text, p_canonical_key text
|
|
353
|
+
) RETURNS text
|
|
354
|
+
LANGUAGE sql
|
|
355
|
+
IMMUTABLE
|
|
356
|
+
STRICT
|
|
357
|
+
SET search_path FROM CURRENT
|
|
358
|
+
AS $$
|
|
359
|
+
SELECT encode(sha256(convert_to(
|
|
360
|
+
p_namespace || chr(31) || p_type || chr(31) || p_canonical_key,
|
|
361
|
+
'UTF8'
|
|
362
|
+
)), 'hex')
|
|
363
|
+
$$;
|
|
364
|
+
|
|
365
|
+
CREATE OR REPLACE FUNCTION guard_session_work_claim_mutation()
|
|
366
|
+
RETURNS trigger
|
|
367
|
+
LANGUAGE plpgsql
|
|
368
|
+
SECURITY DEFINER
|
|
369
|
+
SET search_path FROM CURRENT
|
|
370
|
+
AS $$
|
|
371
|
+
DECLARE
|
|
372
|
+
has_capability boolean;
|
|
373
|
+
BEGIN
|
|
374
|
+
IF TG_OP = 'DELETE'
|
|
375
|
+
AND pg_trigger_depth() > 1
|
|
376
|
+
AND (
|
|
377
|
+
NOT EXISTS (SELECT 1 FROM workspaces WHERE id = OLD.workspace_id)
|
|
378
|
+
OR NOT EXISTS (SELECT 1 FROM sessions WHERE id = OLD.session_id)
|
|
379
|
+
)
|
|
380
|
+
THEN
|
|
381
|
+
RETURN OLD;
|
|
382
|
+
END IF;
|
|
383
|
+
SELECT EXISTS (
|
|
384
|
+
SELECT 1 FROM session_work_claim_write_capabilities capability
|
|
385
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
386
|
+
AND capability.transaction_id = pg_current_xact_id()
|
|
387
|
+
AND capability.capability_id = nullif(
|
|
388
|
+
current_setting('opengeni.session_work_claim_write_capability', true), ''
|
|
389
|
+
)::uuid
|
|
390
|
+
) INTO has_capability;
|
|
391
|
+
IF NOT has_capability THEN
|
|
392
|
+
RAISE EXCEPTION 'work claim mutation requires lifecycle authority'
|
|
393
|
+
USING ERRCODE = '42501';
|
|
394
|
+
END IF;
|
|
395
|
+
IF TG_OP = 'DELETE' THEN
|
|
396
|
+
RAISE EXCEPTION 'work claims are settled, never deleted'
|
|
397
|
+
USING ERRCODE = '42501';
|
|
398
|
+
END IF;
|
|
399
|
+
IF NEW.subject_digest <> session_work_claim_subject_digest(
|
|
400
|
+
NEW.subject_namespace, NEW.subject_type, NEW.canonical_key
|
|
401
|
+
) THEN
|
|
402
|
+
RAISE EXCEPTION 'work claim subject digest is invalid' USING ERRCODE = '23514';
|
|
403
|
+
END IF;
|
|
404
|
+
IF TG_OP = 'INSERT' THEN
|
|
405
|
+
IF NEW.state <> 'active' OR NEW.revision <> 1 OR NEW.settled_at IS NOT NULL THEN
|
|
406
|
+
RAISE EXCEPTION 'work claim create projection is invalid' USING ERRCODE = '23514';
|
|
407
|
+
END IF;
|
|
408
|
+
RETURN NEW;
|
|
409
|
+
END IF;
|
|
410
|
+
IF NEW.account_id IS DISTINCT FROM OLD.account_id
|
|
411
|
+
OR NEW.workspace_id IS DISTINCT FROM OLD.workspace_id
|
|
412
|
+
OR NEW.session_id IS DISTINCT FROM OLD.session_id
|
|
413
|
+
OR NEW.root_session_id IS DISTINCT FROM OLD.root_session_id
|
|
414
|
+
OR NEW.subject_namespace IS DISTINCT FROM OLD.subject_namespace
|
|
415
|
+
OR NEW.subject_type IS DISTINCT FROM OLD.subject_type
|
|
416
|
+
OR NEW.canonical_key IS DISTINCT FROM OLD.canonical_key
|
|
417
|
+
OR NEW.subject_digest IS DISTINCT FROM OLD.subject_digest
|
|
418
|
+
OR NEW.role IS DISTINCT FROM OLD.role
|
|
419
|
+
OR NEW.provenance IS DISTINCT FROM OLD.provenance
|
|
420
|
+
OR NEW.created_at IS DISTINCT FROM OLD.created_at
|
|
421
|
+
OR OLD.state <> 'active'
|
|
422
|
+
OR NEW.revision <> OLD.revision + 1
|
|
423
|
+
OR NEW.updated_at <= OLD.updated_at
|
|
424
|
+
THEN
|
|
425
|
+
RAISE EXCEPTION 'work claim update violates immutable identity or revision fencing'
|
|
426
|
+
USING ERRCODE = '23514';
|
|
427
|
+
END IF;
|
|
428
|
+
IF NEW.state = 'active' THEN
|
|
429
|
+
IF NEW.settled_at IS NOT NULL OR NEW.observed_at < OLD.observed_at THEN
|
|
430
|
+
RAISE EXCEPTION 'active work claim refresh is invalid' USING ERRCODE = '23514';
|
|
431
|
+
END IF;
|
|
432
|
+
ELSIF NEW.state IN ('released','superseded','stale') THEN
|
|
433
|
+
IF NEW.settled_at IS NULL
|
|
434
|
+
OR NEW.display_label IS DISTINCT FROM OLD.display_label
|
|
435
|
+
OR NEW.version_kind IS DISTINCT FROM OLD.version_kind
|
|
436
|
+
OR NEW.version_value IS DISTINCT FROM OLD.version_value
|
|
437
|
+
OR NEW.observed_at IS DISTINCT FROM OLD.observed_at
|
|
438
|
+
THEN
|
|
439
|
+
RAISE EXCEPTION 'work claim settlement may only change lifecycle fields'
|
|
440
|
+
USING ERRCODE = '23514';
|
|
441
|
+
END IF;
|
|
442
|
+
ELSE
|
|
443
|
+
RAISE EXCEPTION 'work claim state transition is invalid' USING ERRCODE = '23514';
|
|
444
|
+
END IF;
|
|
445
|
+
RETURN NEW;
|
|
446
|
+
END;
|
|
447
|
+
$$;
|
|
448
|
+
|
|
449
|
+
CREATE TRIGGER session_work_claims_guard_mutation
|
|
450
|
+
BEFORE INSERT OR UPDATE OR DELETE ON "session_work_claims"
|
|
451
|
+
FOR EACH ROW EXECUTE FUNCTION guard_session_work_claim_mutation();
|
|
452
|
+
|
|
453
|
+
CREATE OR REPLACE FUNCTION guard_session_work_claim_revision_mutation()
|
|
454
|
+
RETURNS trigger
|
|
455
|
+
LANGUAGE plpgsql
|
|
456
|
+
SECURITY DEFINER
|
|
457
|
+
SET search_path FROM CURRENT
|
|
458
|
+
AS $$
|
|
459
|
+
DECLARE
|
|
460
|
+
claim_row session_work_claims%ROWTYPE;
|
|
461
|
+
BEGIN
|
|
462
|
+
IF TG_OP = 'DELETE'
|
|
463
|
+
AND pg_trigger_depth() > 1
|
|
464
|
+
AND (
|
|
465
|
+
NOT EXISTS (SELECT 1 FROM workspaces WHERE id = OLD.workspace_id)
|
|
466
|
+
OR NOT EXISTS (SELECT 1 FROM sessions WHERE id = OLD.session_id)
|
|
467
|
+
OR NOT EXISTS (
|
|
468
|
+
SELECT 1 FROM session_work_claims
|
|
469
|
+
WHERE workspace_id = OLD.workspace_id AND id = OLD.claim_id
|
|
470
|
+
)
|
|
471
|
+
)
|
|
472
|
+
THEN
|
|
473
|
+
RETURN OLD;
|
|
474
|
+
END IF;
|
|
475
|
+
IF TG_OP <> 'INSERT' THEN
|
|
476
|
+
RAISE EXCEPTION 'work claim revisions are immutable' USING ERRCODE = '42501';
|
|
477
|
+
END IF;
|
|
478
|
+
IF NOT EXISTS (
|
|
479
|
+
SELECT 1 FROM session_work_claim_write_capabilities capability
|
|
480
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
481
|
+
AND capability.transaction_id = pg_current_xact_id()
|
|
482
|
+
AND capability.capability_id = nullif(
|
|
483
|
+
current_setting('opengeni.session_work_claim_write_capability', true), ''
|
|
484
|
+
)::uuid
|
|
485
|
+
) THEN
|
|
486
|
+
RAISE EXCEPTION 'work claim revision requires lifecycle authority'
|
|
487
|
+
USING ERRCODE = '42501';
|
|
488
|
+
END IF;
|
|
489
|
+
SELECT * INTO STRICT claim_row FROM session_work_claims claim
|
|
490
|
+
WHERE claim.workspace_id = NEW.workspace_id AND claim.id = NEW.claim_id;
|
|
491
|
+
IF NEW.account_id IS DISTINCT FROM claim_row.account_id
|
|
492
|
+
OR NEW.session_id IS DISTINCT FROM claim_row.session_id
|
|
493
|
+
OR NEW.root_session_id IS DISTINCT FROM claim_row.root_session_id
|
|
494
|
+
OR NEW.resulting_revision IS DISTINCT FROM claim_row.revision
|
|
495
|
+
OR NEW.subject_namespace IS DISTINCT FROM claim_row.subject_namespace
|
|
496
|
+
OR NEW.subject_type IS DISTINCT FROM claim_row.subject_type
|
|
497
|
+
OR NEW.canonical_key IS DISTINCT FROM claim_row.canonical_key
|
|
498
|
+
OR NEW.subject_digest IS DISTINCT FROM claim_row.subject_digest
|
|
499
|
+
OR NEW.display_label IS DISTINCT FROM claim_row.display_label
|
|
500
|
+
OR NEW.role IS DISTINCT FROM claim_row.role
|
|
501
|
+
OR NEW.state IS DISTINCT FROM claim_row.state
|
|
502
|
+
OR NEW.provenance IS DISTINCT FROM claim_row.provenance
|
|
503
|
+
OR NEW.version_kind IS DISTINCT FROM claim_row.version_kind
|
|
504
|
+
OR NEW.version_value IS DISTINCT FROM claim_row.version_value
|
|
505
|
+
OR NEW.observed_at IS DISTINCT FROM claim_row.observed_at
|
|
506
|
+
OR NEW.claim_created_at IS DISTINCT FROM claim_row.created_at
|
|
507
|
+
OR NEW.claim_updated_at IS DISTINCT FROM claim_row.updated_at
|
|
508
|
+
OR NEW.settled_at IS DISTINCT FROM claim_row.settled_at
|
|
509
|
+
THEN
|
|
510
|
+
RAISE EXCEPTION 'work claim revision does not match its durable head receipt'
|
|
511
|
+
USING ERRCODE = '23514';
|
|
512
|
+
END IF;
|
|
513
|
+
RETURN NEW;
|
|
514
|
+
END;
|
|
515
|
+
$$;
|
|
516
|
+
|
|
517
|
+
CREATE TRIGGER session_work_claim_revisions_guard_mutation
|
|
518
|
+
BEFORE INSERT OR UPDATE OR DELETE ON "session_work_claim_revisions"
|
|
519
|
+
FOR EACH ROW EXECUTE FUNCTION guard_session_work_claim_revision_mutation();
|
|
520
|
+
|
|
521
|
+
CREATE OR REPLACE FUNCTION resolve_session_work_claim_attempt_authority(
|
|
522
|
+
p_account_id uuid,
|
|
523
|
+
p_workspace_id uuid,
|
|
524
|
+
p_session_id uuid,
|
|
525
|
+
p_turn_id uuid,
|
|
526
|
+
p_attempt_id uuid,
|
|
527
|
+
p_execution_generation integer
|
|
528
|
+
) RETURNS TABLE (
|
|
529
|
+
root_session_id uuid,
|
|
530
|
+
actor_subject_id text
|
|
531
|
+
)
|
|
532
|
+
LANGUAGE plpgsql
|
|
533
|
+
SECURITY DEFINER
|
|
534
|
+
SET search_path FROM CURRENT
|
|
535
|
+
AS $$
|
|
536
|
+
DECLARE
|
|
537
|
+
session_row sessions%ROWTYPE;
|
|
538
|
+
turn_row session_turns%ROWTYPE;
|
|
539
|
+
previous_subject_id text := current_setting('opengeni.subject_id', true);
|
|
540
|
+
previous_initiating_human_subject_id text := current_setting(
|
|
541
|
+
'opengeni.initiating_human_subject_id', true
|
|
542
|
+
);
|
|
543
|
+
visibility_context_set boolean := false;
|
|
544
|
+
BEGIN
|
|
545
|
+
IF p_account_id IS NULL OR p_workspace_id IS NULL OR p_session_id IS NULL
|
|
546
|
+
OR p_turn_id IS NULL OR p_attempt_id IS NULL
|
|
547
|
+
OR p_execution_generation IS NULL OR p_execution_generation < 1
|
|
548
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
549
|
+
OR p_workspace_id IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
550
|
+
THEN
|
|
551
|
+
RAISE EXCEPTION 'work claims require exact tenant and attempt authority'
|
|
552
|
+
USING ERRCODE = '42501';
|
|
553
|
+
END IF;
|
|
554
|
+
|
|
555
|
+
PERFORM 1 FROM workspaces workspace
|
|
556
|
+
WHERE workspace.account_id = p_account_id AND workspace.id = p_workspace_id
|
|
557
|
+
FOR KEY SHARE;
|
|
558
|
+
IF NOT FOUND THEN
|
|
559
|
+
RAISE EXCEPTION 'work claim workspace is unavailable' USING ERRCODE = '42501';
|
|
560
|
+
END IF;
|
|
561
|
+
|
|
562
|
+
SELECT * INTO session_row FROM sessions session
|
|
563
|
+
WHERE session.account_id = p_account_id
|
|
564
|
+
AND session.workspace_id = p_workspace_id
|
|
565
|
+
AND session.id = p_session_id
|
|
566
|
+
FOR NO KEY UPDATE;
|
|
567
|
+
IF NOT FOUND OR session_row.status IN ('failed','cancelled') THEN
|
|
568
|
+
RAISE EXCEPTION 'work claim session is unavailable' USING ERRCODE = '42501';
|
|
569
|
+
END IF;
|
|
570
|
+
IF EXISTS (
|
|
571
|
+
SELECT 1 FROM session_goals goal
|
|
572
|
+
WHERE goal.account_id = p_account_id
|
|
573
|
+
AND goal.workspace_id = p_workspace_id
|
|
574
|
+
AND goal.session_id = p_session_id
|
|
575
|
+
AND goal.status = 'completed'
|
|
576
|
+
) THEN
|
|
577
|
+
RAISE EXCEPTION 'completed session goals cannot create or mutate work claims'
|
|
578
|
+
USING ERRCODE = '42501';
|
|
579
|
+
END IF;
|
|
580
|
+
|
|
581
|
+
SELECT turn.* INTO turn_row FROM session_turns turn
|
|
582
|
+
WHERE turn.account_id = p_account_id
|
|
583
|
+
AND turn.workspace_id = p_workspace_id
|
|
584
|
+
AND turn.session_id = p_session_id
|
|
585
|
+
AND turn.id = p_turn_id
|
|
586
|
+
AND turn.active_attempt_id = p_attempt_id
|
|
587
|
+
AND session_row.active_turn_id = p_turn_id
|
|
588
|
+
AND turn.execution_generation = p_execution_generation
|
|
589
|
+
AND turn.status IN ('running','requires_action','recovering','waiting_capacity')
|
|
590
|
+
FOR UPDATE;
|
|
591
|
+
IF NOT FOUND THEN
|
|
592
|
+
RAISE EXCEPTION 'work claims require the exact current turn' USING ERRCODE = '42501';
|
|
593
|
+
END IF;
|
|
594
|
+
|
|
595
|
+
PERFORM 1 FROM session_turn_attempts attempt
|
|
596
|
+
WHERE attempt.account_id = p_account_id
|
|
597
|
+
AND attempt.workspace_id = p_workspace_id
|
|
598
|
+
AND attempt.session_id = p_session_id
|
|
599
|
+
AND attempt.turn_id = p_turn_id
|
|
600
|
+
AND attempt.id = p_attempt_id
|
|
601
|
+
AND attempt.execution_generation = p_execution_generation
|
|
602
|
+
AND attempt.state IN ('claimed','running')
|
|
603
|
+
AND NOT EXISTS (
|
|
604
|
+
SELECT 1 FROM session_attempt_interruptions interruption
|
|
605
|
+
WHERE interruption.workspace_id = attempt.workspace_id
|
|
606
|
+
AND interruption.attempt_id = attempt.id
|
|
607
|
+
AND interruption.state IN ('pending','delivered','acknowledged')
|
|
608
|
+
)
|
|
609
|
+
FOR UPDATE;
|
|
610
|
+
IF NOT FOUND THEN
|
|
611
|
+
RAISE EXCEPTION 'work claims require the exact current attempt' USING ERRCODE = '42501';
|
|
612
|
+
END IF;
|
|
613
|
+
|
|
614
|
+
PERFORM set_config(
|
|
615
|
+
'opengeni.subject_id',
|
|
616
|
+
CASE WHEN turn_row.initiator_kind = 'subject' THEN turn_row.initiator_subject_id ELSE '' END,
|
|
617
|
+
true
|
|
618
|
+
);
|
|
619
|
+
PERFORM set_config(
|
|
620
|
+
'opengeni.initiating_human_subject_id',
|
|
621
|
+
coalesce(turn_row.initiating_human_subject_id, ''),
|
|
622
|
+
true
|
|
623
|
+
);
|
|
624
|
+
visibility_context_set := true;
|
|
625
|
+
IF NOT (
|
|
626
|
+
session_row.visibility = 'workspace_shared'
|
|
627
|
+
OR session_private_actor_visible(
|
|
628
|
+
session_row.account_id,
|
|
629
|
+
session_row.workspace_id,
|
|
630
|
+
session_row.owner_organization_membership_id,
|
|
631
|
+
session_row.owner_subject_id
|
|
632
|
+
)
|
|
633
|
+
) THEN
|
|
634
|
+
RAISE EXCEPTION 'work claim session is not visible to this actor'
|
|
635
|
+
USING ERRCODE = '42501';
|
|
636
|
+
END IF;
|
|
637
|
+
PERFORM set_config('opengeni.subject_id', coalesce(previous_subject_id, ''), true);
|
|
638
|
+
PERFORM set_config(
|
|
639
|
+
'opengeni.initiating_human_subject_id',
|
|
640
|
+
coalesce(previous_initiating_human_subject_id, ''),
|
|
641
|
+
true
|
|
642
|
+
);
|
|
643
|
+
visibility_context_set := false;
|
|
644
|
+
|
|
645
|
+
root_session_id := session_row.root_session_id;
|
|
646
|
+
actor_subject_id := turn_row.initiator_subject_id;
|
|
647
|
+
RETURN NEXT;
|
|
648
|
+
EXCEPTION WHEN OTHERS THEN
|
|
649
|
+
IF visibility_context_set THEN
|
|
650
|
+
PERFORM set_config('opengeni.subject_id', coalesce(previous_subject_id, ''), true);
|
|
651
|
+
PERFORM set_config(
|
|
652
|
+
'opengeni.initiating_human_subject_id',
|
|
653
|
+
coalesce(previous_initiating_human_subject_id, ''),
|
|
654
|
+
true
|
|
655
|
+
);
|
|
656
|
+
END IF;
|
|
657
|
+
RAISE;
|
|
658
|
+
END;
|
|
659
|
+
$$;
|
|
660
|
+
|
|
661
|
+
CREATE OR REPLACE FUNCTION upsert_session_work_claim_for_attempt(
|
|
662
|
+
p_account_id uuid,
|
|
663
|
+
p_workspace_id uuid,
|
|
664
|
+
p_session_id uuid,
|
|
665
|
+
p_turn_id uuid,
|
|
666
|
+
p_attempt_id uuid,
|
|
667
|
+
p_execution_generation integer,
|
|
668
|
+
p_operation_id uuid,
|
|
669
|
+
p_expected_revision integer,
|
|
670
|
+
p_subject_namespace text,
|
|
671
|
+
p_subject_type text,
|
|
672
|
+
p_canonical_key text,
|
|
673
|
+
p_display_label text,
|
|
674
|
+
p_role text,
|
|
675
|
+
p_version_kind text,
|
|
676
|
+
p_version_value text
|
|
677
|
+
) RETURNS TABLE (
|
|
678
|
+
claim_id uuid, session_id uuid, root_session_id uuid,
|
|
679
|
+
subject_namespace text, subject_type text, canonical_key text,
|
|
680
|
+
display_label text, role text, state text, revision integer,
|
|
681
|
+
provenance text, version_kind text, version_value text,
|
|
682
|
+
observed_at timestamptz, created_at timestamptz, updated_at timestamptz,
|
|
683
|
+
settled_at timestamptz, mutation_kind text, replayed boolean
|
|
684
|
+
)
|
|
685
|
+
LANGUAGE plpgsql
|
|
686
|
+
SECURITY DEFINER
|
|
687
|
+
SET search_path FROM CURRENT
|
|
688
|
+
AS $$
|
|
689
|
+
DECLARE
|
|
690
|
+
authority record;
|
|
691
|
+
existing_revision session_work_claim_revisions%ROWTYPE;
|
|
692
|
+
claim_row session_work_claims%ROWTYPE;
|
|
693
|
+
calculated_digest text;
|
|
694
|
+
calculated_input_hash text;
|
|
695
|
+
mutation_value text;
|
|
696
|
+
now_value timestamptz := clock_timestamp();
|
|
697
|
+
write_capability_id uuid := gen_random_uuid();
|
|
698
|
+
previous_capability text := current_setting(
|
|
699
|
+
'opengeni.session_work_claim_write_capability', true
|
|
700
|
+
);
|
|
701
|
+
BEGIN
|
|
702
|
+
PERFORM acquire_session_tenancy_fence(p_workspace_id);
|
|
703
|
+
SELECT * INTO STRICT authority FROM resolve_session_work_claim_attempt_authority(
|
|
704
|
+
p_account_id, p_workspace_id, p_session_id, p_turn_id,
|
|
705
|
+
p_attempt_id, p_execution_generation
|
|
706
|
+
);
|
|
707
|
+
IF p_operation_id IS NULL OR p_expected_revision IS NULL OR p_expected_revision < 0
|
|
708
|
+
OR p_subject_namespace IS NULL
|
|
709
|
+
OR p_subject_namespace <> lower(btrim(p_subject_namespace))
|
|
710
|
+
OR octet_length(p_subject_namespace) NOT BETWEEN 1 AND 64
|
|
711
|
+
OR p_subject_namespace !~ '^[a-z0-9]([a-z0-9._:-]{0,62}[a-z0-9])?$'
|
|
712
|
+
OR p_subject_type NOT IN (
|
|
713
|
+
'repository','branch','pull_request','issue','artifact','release','ci_run','other'
|
|
714
|
+
)
|
|
715
|
+
OR p_canonical_key IS NULL OR p_canonical_key <> btrim(p_canonical_key)
|
|
716
|
+
OR p_canonical_key <> normalize(p_canonical_key, NFKC)
|
|
717
|
+
OR octet_length(p_canonical_key) NOT BETWEEN 1 AND 512
|
|
718
|
+
OR p_canonical_key ~ '[[:cntrl:]]'
|
|
719
|
+
OR (p_display_label IS NOT NULL AND (
|
|
720
|
+
p_display_label <> btrim(p_display_label)
|
|
721
|
+
OR p_display_label <> normalize(p_display_label, NFKC)
|
|
722
|
+
OR octet_length(p_display_label) NOT BETWEEN 1 AND 256
|
|
723
|
+
OR p_display_label ~ '[[:cntrl:]]'
|
|
724
|
+
))
|
|
725
|
+
OR p_role NOT IN ('working','reviewing','monitoring','delivering')
|
|
726
|
+
OR ((p_version_kind IS NULL) <> (p_version_value IS NULL))
|
|
727
|
+
OR (p_version_kind IS NOT NULL AND (
|
|
728
|
+
p_version_kind NOT IN (
|
|
729
|
+
'git_commit','branch_head','pull_request_head','artifact_version',
|
|
730
|
+
'release_version','ci_run','other'
|
|
731
|
+
)
|
|
732
|
+
OR p_version_value <> btrim(p_version_value)
|
|
733
|
+
OR p_version_value <> normalize(p_version_value, NFKC)
|
|
734
|
+
OR octet_length(p_version_value) NOT BETWEEN 1 AND 256
|
|
735
|
+
OR p_version_value ~ '[[:cntrl:]]'
|
|
736
|
+
))
|
|
737
|
+
THEN
|
|
738
|
+
RAISE EXCEPTION 'work claim upsert input is invalid' USING ERRCODE = '22023';
|
|
739
|
+
END IF;
|
|
740
|
+
|
|
741
|
+
calculated_digest := session_work_claim_subject_digest(
|
|
742
|
+
p_subject_namespace, p_subject_type, p_canonical_key
|
|
743
|
+
);
|
|
744
|
+
calculated_input_hash := encode(sha256(convert_to(jsonb_build_object(
|
|
745
|
+
'accountId', p_account_id,
|
|
746
|
+
'workspaceId', p_workspace_id,
|
|
747
|
+
'sessionId', p_session_id,
|
|
748
|
+
'turnId', p_turn_id,
|
|
749
|
+
'expectedRevision', p_expected_revision,
|
|
750
|
+
'subjectNamespace', p_subject_namespace,
|
|
751
|
+
'subjectType', p_subject_type,
|
|
752
|
+
'canonicalKey', p_canonical_key,
|
|
753
|
+
'displayLabel', p_display_label,
|
|
754
|
+
'role', p_role,
|
|
755
|
+
'versionKind', p_version_kind,
|
|
756
|
+
'versionValue', p_version_value
|
|
757
|
+
)::text, 'UTF8')), 'hex');
|
|
758
|
+
|
|
759
|
+
INSERT INTO session_work_claim_write_capabilities (
|
|
760
|
+
backend_pid, transaction_id, capability_id
|
|
761
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), write_capability_id);
|
|
762
|
+
PERFORM set_config(
|
|
763
|
+
'opengeni.session_work_claim_write_capability', write_capability_id::text, true
|
|
764
|
+
);
|
|
765
|
+
|
|
766
|
+
SELECT * INTO existing_revision FROM session_work_claim_revisions revision_row
|
|
767
|
+
WHERE revision_row.workspace_id = p_workspace_id
|
|
768
|
+
AND revision_row.operation_id = p_operation_id
|
|
769
|
+
FOR SHARE;
|
|
770
|
+
IF FOUND THEN
|
|
771
|
+
IF existing_revision.input_hash <> calculated_input_hash
|
|
772
|
+
OR existing_revision.session_id <> p_session_id
|
|
773
|
+
OR existing_revision.actor_turn_id <> p_turn_id
|
|
774
|
+
OR existing_revision.mutation_kind NOT IN ('created','updated')
|
|
775
|
+
THEN
|
|
776
|
+
RAISE EXCEPTION 'work claim operation conflicts with another input or logical turn'
|
|
777
|
+
USING ERRCODE = '23505';
|
|
778
|
+
END IF;
|
|
779
|
+
claim_row.id := existing_revision.claim_id;
|
|
780
|
+
claim_row.session_id := existing_revision.session_id;
|
|
781
|
+
claim_row.root_session_id := existing_revision.root_session_id;
|
|
782
|
+
claim_row.subject_namespace := existing_revision.subject_namespace;
|
|
783
|
+
claim_row.subject_type := existing_revision.subject_type;
|
|
784
|
+
claim_row.canonical_key := existing_revision.canonical_key;
|
|
785
|
+
claim_row.display_label := existing_revision.display_label;
|
|
786
|
+
claim_row.role := existing_revision.role;
|
|
787
|
+
claim_row.state := existing_revision.state;
|
|
788
|
+
claim_row.revision := existing_revision.resulting_revision;
|
|
789
|
+
claim_row.provenance := existing_revision.provenance;
|
|
790
|
+
claim_row.version_kind := existing_revision.version_kind;
|
|
791
|
+
claim_row.version_value := existing_revision.version_value;
|
|
792
|
+
claim_row.observed_at := existing_revision.observed_at;
|
|
793
|
+
claim_row.created_at := existing_revision.claim_created_at;
|
|
794
|
+
claim_row.updated_at := existing_revision.claim_updated_at;
|
|
795
|
+
claim_row.settled_at := existing_revision.settled_at;
|
|
796
|
+
mutation_value := existing_revision.mutation_kind;
|
|
797
|
+
replayed := true;
|
|
798
|
+
ELSE
|
|
799
|
+
SELECT * INTO claim_row FROM session_work_claims claim
|
|
800
|
+
WHERE claim.workspace_id = p_workspace_id
|
|
801
|
+
AND claim.session_id = p_session_id
|
|
802
|
+
AND claim.subject_namespace = p_subject_namespace
|
|
803
|
+
AND claim.subject_type = p_subject_type
|
|
804
|
+
AND claim.subject_digest = calculated_digest
|
|
805
|
+
AND claim.role = p_role
|
|
806
|
+
AND claim.state = 'active'
|
|
807
|
+
FOR UPDATE;
|
|
808
|
+
IF FOUND THEN
|
|
809
|
+
IF claim_row.revision <> p_expected_revision THEN
|
|
810
|
+
RAISE EXCEPTION 'work claim upsert revision conflict' USING ERRCODE = '40001';
|
|
811
|
+
END IF;
|
|
812
|
+
UPDATE session_work_claims claim SET
|
|
813
|
+
display_label = p_display_label,
|
|
814
|
+
version_kind = p_version_kind,
|
|
815
|
+
version_value = p_version_value,
|
|
816
|
+
observed_at = now_value,
|
|
817
|
+
revision = claim.revision + 1,
|
|
818
|
+
updated_at = now_value
|
|
819
|
+
WHERE claim.workspace_id = p_workspace_id
|
|
820
|
+
AND claim.id = claim_row.id
|
|
821
|
+
AND claim.state = 'active'
|
|
822
|
+
AND claim.revision = p_expected_revision
|
|
823
|
+
RETURNING * INTO STRICT claim_row;
|
|
824
|
+
mutation_value := 'updated';
|
|
825
|
+
ELSE
|
|
826
|
+
IF p_expected_revision <> 0 THEN
|
|
827
|
+
RAISE EXCEPTION 'work claim create revision conflict' USING ERRCODE = '40001';
|
|
828
|
+
END IF;
|
|
829
|
+
IF (SELECT count(*) FROM session_work_claims claim
|
|
830
|
+
WHERE claim.workspace_id = p_workspace_id
|
|
831
|
+
AND claim.session_id = p_session_id
|
|
832
|
+
AND claim.state = 'active') >= 64
|
|
833
|
+
THEN
|
|
834
|
+
RAISE EXCEPTION 'work claim active-record limit reached' USING ERRCODE = '54000';
|
|
835
|
+
END IF;
|
|
836
|
+
INSERT INTO session_work_claims (
|
|
837
|
+
account_id, workspace_id, session_id, root_session_id,
|
|
838
|
+
subject_namespace, subject_type, canonical_key, subject_digest,
|
|
839
|
+
display_label, role, provenance, version_kind, version_value,
|
|
840
|
+
observed_at, created_at, updated_at
|
|
841
|
+
) VALUES (
|
|
842
|
+
p_account_id, p_workspace_id, p_session_id, authority.root_session_id,
|
|
843
|
+
p_subject_namespace, p_subject_type, p_canonical_key, calculated_digest,
|
|
844
|
+
p_display_label, p_role, 'explicit_agent', p_version_kind, p_version_value,
|
|
845
|
+
now_value, now_value, now_value
|
|
846
|
+
) RETURNING * INTO claim_row;
|
|
847
|
+
mutation_value := 'created';
|
|
848
|
+
END IF;
|
|
849
|
+
|
|
850
|
+
INSERT INTO session_work_claim_revisions (
|
|
851
|
+
account_id, workspace_id, claim_id, session_id, root_session_id,
|
|
852
|
+
operation_id, input_hash, mutation_kind, prior_revision, resulting_revision,
|
|
853
|
+
subject_namespace, subject_type, canonical_key, subject_digest, display_label,
|
|
854
|
+
role, state, provenance, version_kind, version_value, observed_at,
|
|
855
|
+
claim_created_at, claim_updated_at, settled_at, actor_kind, actor_subject_id,
|
|
856
|
+
actor_session_id, actor_turn_id, actor_attempt_id, actor_execution_generation
|
|
857
|
+
) VALUES (
|
|
858
|
+
p_account_id, p_workspace_id, claim_row.id, p_session_id, claim_row.root_session_id,
|
|
859
|
+
p_operation_id, calculated_input_hash, mutation_value,
|
|
860
|
+
CASE WHEN mutation_value = 'created' THEN NULL ELSE p_expected_revision END,
|
|
861
|
+
claim_row.revision, claim_row.subject_namespace, claim_row.subject_type,
|
|
862
|
+
claim_row.canonical_key, claim_row.subject_digest, claim_row.display_label,
|
|
863
|
+
claim_row.role, claim_row.state, claim_row.provenance, claim_row.version_kind,
|
|
864
|
+
claim_row.version_value, claim_row.observed_at, claim_row.created_at,
|
|
865
|
+
claim_row.updated_at, claim_row.settled_at, 'agent_attempt', authority.actor_subject_id,
|
|
866
|
+
p_session_id, p_turn_id, p_attempt_id, p_execution_generation
|
|
867
|
+
);
|
|
868
|
+
UPDATE sessions session SET updated_at = now_value
|
|
869
|
+
WHERE session.workspace_id = p_workspace_id AND session.id = p_session_id;
|
|
870
|
+
replayed := false;
|
|
871
|
+
END IF;
|
|
872
|
+
|
|
873
|
+
claim_id := claim_row.id;
|
|
874
|
+
session_id := claim_row.session_id;
|
|
875
|
+
root_session_id := claim_row.root_session_id;
|
|
876
|
+
subject_namespace := claim_row.subject_namespace;
|
|
877
|
+
subject_type := claim_row.subject_type;
|
|
878
|
+
canonical_key := claim_row.canonical_key;
|
|
879
|
+
display_label := claim_row.display_label;
|
|
880
|
+
role := claim_row.role;
|
|
881
|
+
state := claim_row.state;
|
|
882
|
+
revision := claim_row.revision;
|
|
883
|
+
provenance := claim_row.provenance;
|
|
884
|
+
version_kind := claim_row.version_kind;
|
|
885
|
+
version_value := claim_row.version_value;
|
|
886
|
+
observed_at := claim_row.observed_at;
|
|
887
|
+
created_at := claim_row.created_at;
|
|
888
|
+
updated_at := claim_row.updated_at;
|
|
889
|
+
settled_at := claim_row.settled_at;
|
|
890
|
+
mutation_kind := mutation_value;
|
|
891
|
+
DELETE FROM session_work_claim_write_capabilities capability
|
|
892
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
893
|
+
AND capability.transaction_id = pg_current_xact_id()
|
|
894
|
+
AND capability.capability_id = write_capability_id;
|
|
895
|
+
PERFORM set_config(
|
|
896
|
+
'opengeni.session_work_claim_write_capability', coalesce(previous_capability, ''), true
|
|
897
|
+
);
|
|
898
|
+
RETURN NEXT;
|
|
899
|
+
EXCEPTION WHEN OTHERS THEN
|
|
900
|
+
PERFORM set_config(
|
|
901
|
+
'opengeni.session_work_claim_write_capability', coalesce(previous_capability, ''), true
|
|
902
|
+
);
|
|
903
|
+
RAISE;
|
|
904
|
+
END;
|
|
905
|
+
$$;
|
|
906
|
+
|
|
907
|
+
CREATE OR REPLACE FUNCTION release_session_work_claim_for_attempt(
|
|
908
|
+
p_account_id uuid,
|
|
909
|
+
p_workspace_id uuid,
|
|
910
|
+
p_session_id uuid,
|
|
911
|
+
p_turn_id uuid,
|
|
912
|
+
p_attempt_id uuid,
|
|
913
|
+
p_execution_generation integer,
|
|
914
|
+
p_operation_id uuid,
|
|
915
|
+
p_claim_id uuid,
|
|
916
|
+
p_expected_revision integer,
|
|
917
|
+
p_reason text
|
|
918
|
+
) RETURNS TABLE (
|
|
919
|
+
claim_id uuid, session_id uuid, root_session_id uuid,
|
|
920
|
+
subject_namespace text, subject_type text, canonical_key text,
|
|
921
|
+
display_label text, role text, state text, revision integer,
|
|
922
|
+
provenance text, version_kind text, version_value text,
|
|
923
|
+
observed_at timestamptz, created_at timestamptz, updated_at timestamptz,
|
|
924
|
+
settled_at timestamptz, mutation_kind text, replayed boolean
|
|
925
|
+
)
|
|
926
|
+
LANGUAGE plpgsql
|
|
927
|
+
SECURITY DEFINER
|
|
928
|
+
SET search_path FROM CURRENT
|
|
929
|
+
AS $$
|
|
930
|
+
DECLARE
|
|
931
|
+
authority record;
|
|
932
|
+
existing_revision session_work_claim_revisions%ROWTYPE;
|
|
933
|
+
claim_row session_work_claims%ROWTYPE;
|
|
934
|
+
calculated_input_hash text;
|
|
935
|
+
now_value timestamptz := clock_timestamp();
|
|
936
|
+
write_capability_id uuid := gen_random_uuid();
|
|
937
|
+
previous_capability text := current_setting(
|
|
938
|
+
'opengeni.session_work_claim_write_capability', true
|
|
939
|
+
);
|
|
940
|
+
BEGIN
|
|
941
|
+
PERFORM acquire_session_tenancy_fence(p_workspace_id);
|
|
942
|
+
SELECT * INTO STRICT authority FROM resolve_session_work_claim_attempt_authority(
|
|
943
|
+
p_account_id, p_workspace_id, p_session_id, p_turn_id,
|
|
944
|
+
p_attempt_id, p_execution_generation
|
|
945
|
+
);
|
|
946
|
+
IF p_operation_id IS NULL OR p_claim_id IS NULL
|
|
947
|
+
OR p_expected_revision IS NULL OR p_expected_revision < 1
|
|
948
|
+
OR p_reason NOT IN (
|
|
949
|
+
'completed','cancelled','failed','superseded','no_longer_active',
|
|
950
|
+
'corrected','external_state_changed','other'
|
|
951
|
+
)
|
|
952
|
+
THEN
|
|
953
|
+
RAISE EXCEPTION 'work claim release input is invalid' USING ERRCODE = '22023';
|
|
954
|
+
END IF;
|
|
955
|
+
calculated_input_hash := encode(sha256(convert_to(jsonb_build_object(
|
|
956
|
+
'accountId', p_account_id,
|
|
957
|
+
'workspaceId', p_workspace_id,
|
|
958
|
+
'sessionId', p_session_id,
|
|
959
|
+
'turnId', p_turn_id,
|
|
960
|
+
'claimId', p_claim_id,
|
|
961
|
+
'expectedRevision', p_expected_revision,
|
|
962
|
+
'reason', p_reason
|
|
963
|
+
)::text, 'UTF8')), 'hex');
|
|
964
|
+
|
|
965
|
+
INSERT INTO session_work_claim_write_capabilities (
|
|
966
|
+
backend_pid, transaction_id, capability_id
|
|
967
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), write_capability_id);
|
|
968
|
+
PERFORM set_config(
|
|
969
|
+
'opengeni.session_work_claim_write_capability', write_capability_id::text, true
|
|
970
|
+
);
|
|
971
|
+
|
|
972
|
+
SELECT * INTO existing_revision FROM session_work_claim_revisions revision_row
|
|
973
|
+
WHERE revision_row.workspace_id = p_workspace_id
|
|
974
|
+
AND revision_row.operation_id = p_operation_id
|
|
975
|
+
FOR SHARE;
|
|
976
|
+
IF FOUND THEN
|
|
977
|
+
IF existing_revision.input_hash <> calculated_input_hash
|
|
978
|
+
OR existing_revision.session_id <> p_session_id
|
|
979
|
+
OR existing_revision.actor_turn_id <> p_turn_id
|
|
980
|
+
OR existing_revision.mutation_kind <> 'released'
|
|
981
|
+
THEN
|
|
982
|
+
RAISE EXCEPTION 'work claim release conflicts with another input or logical turn'
|
|
983
|
+
USING ERRCODE = '23505';
|
|
984
|
+
END IF;
|
|
985
|
+
claim_row.id := existing_revision.claim_id;
|
|
986
|
+
claim_row.session_id := existing_revision.session_id;
|
|
987
|
+
claim_row.root_session_id := existing_revision.root_session_id;
|
|
988
|
+
claim_row.subject_namespace := existing_revision.subject_namespace;
|
|
989
|
+
claim_row.subject_type := existing_revision.subject_type;
|
|
990
|
+
claim_row.canonical_key := existing_revision.canonical_key;
|
|
991
|
+
claim_row.display_label := existing_revision.display_label;
|
|
992
|
+
claim_row.role := existing_revision.role;
|
|
993
|
+
claim_row.state := existing_revision.state;
|
|
994
|
+
claim_row.revision := existing_revision.resulting_revision;
|
|
995
|
+
claim_row.provenance := existing_revision.provenance;
|
|
996
|
+
claim_row.version_kind := existing_revision.version_kind;
|
|
997
|
+
claim_row.version_value := existing_revision.version_value;
|
|
998
|
+
claim_row.observed_at := existing_revision.observed_at;
|
|
999
|
+
claim_row.created_at := existing_revision.claim_created_at;
|
|
1000
|
+
claim_row.updated_at := existing_revision.claim_updated_at;
|
|
1001
|
+
claim_row.settled_at := existing_revision.settled_at;
|
|
1002
|
+
replayed := true;
|
|
1003
|
+
ELSE
|
|
1004
|
+
SELECT * INTO claim_row FROM session_work_claims claim
|
|
1005
|
+
WHERE claim.workspace_id = p_workspace_id
|
|
1006
|
+
AND claim.session_id = p_session_id
|
|
1007
|
+
AND claim.id = p_claim_id
|
|
1008
|
+
FOR UPDATE;
|
|
1009
|
+
IF NOT FOUND THEN
|
|
1010
|
+
RAISE EXCEPTION 'work claim is unavailable' USING ERRCODE = 'P0002';
|
|
1011
|
+
END IF;
|
|
1012
|
+
IF claim_row.state <> 'active' OR claim_row.revision <> p_expected_revision THEN
|
|
1013
|
+
RAISE EXCEPTION 'work claim release revision conflict' USING ERRCODE = '40001';
|
|
1014
|
+
END IF;
|
|
1015
|
+
UPDATE session_work_claims claim SET
|
|
1016
|
+
state = 'released',
|
|
1017
|
+
revision = claim.revision + 1,
|
|
1018
|
+
settled_at = now_value,
|
|
1019
|
+
updated_at = now_value
|
|
1020
|
+
WHERE claim.workspace_id = p_workspace_id
|
|
1021
|
+
AND claim.id = p_claim_id
|
|
1022
|
+
AND claim.state = 'active'
|
|
1023
|
+
AND claim.revision = p_expected_revision
|
|
1024
|
+
RETURNING * INTO STRICT claim_row;
|
|
1025
|
+
INSERT INTO session_work_claim_revisions (
|
|
1026
|
+
account_id, workspace_id, claim_id, session_id, root_session_id,
|
|
1027
|
+
operation_id, input_hash, mutation_kind, prior_revision, resulting_revision,
|
|
1028
|
+
subject_namespace, subject_type, canonical_key, subject_digest, display_label,
|
|
1029
|
+
role, state, provenance, version_kind, version_value, observed_at,
|
|
1030
|
+
claim_created_at, claim_updated_at, settled_at, actor_kind, actor_subject_id,
|
|
1031
|
+
actor_session_id, actor_turn_id, actor_attempt_id, actor_execution_generation, reason
|
|
1032
|
+
) VALUES (
|
|
1033
|
+
p_account_id, p_workspace_id, claim_row.id, p_session_id, claim_row.root_session_id,
|
|
1034
|
+
p_operation_id, calculated_input_hash, 'released', p_expected_revision,
|
|
1035
|
+
claim_row.revision, claim_row.subject_namespace, claim_row.subject_type,
|
|
1036
|
+
claim_row.canonical_key, claim_row.subject_digest, claim_row.display_label,
|
|
1037
|
+
claim_row.role, claim_row.state, claim_row.provenance, claim_row.version_kind,
|
|
1038
|
+
claim_row.version_value, claim_row.observed_at, claim_row.created_at,
|
|
1039
|
+
claim_row.updated_at, claim_row.settled_at, 'agent_attempt', authority.actor_subject_id,
|
|
1040
|
+
p_session_id, p_turn_id, p_attempt_id, p_execution_generation, p_reason
|
|
1041
|
+
);
|
|
1042
|
+
UPDATE sessions session SET updated_at = now_value
|
|
1043
|
+
WHERE session.workspace_id = p_workspace_id AND session.id = p_session_id;
|
|
1044
|
+
replayed := false;
|
|
1045
|
+
END IF;
|
|
1046
|
+
|
|
1047
|
+
claim_id := claim_row.id;
|
|
1048
|
+
session_id := claim_row.session_id;
|
|
1049
|
+
root_session_id := claim_row.root_session_id;
|
|
1050
|
+
subject_namespace := claim_row.subject_namespace;
|
|
1051
|
+
subject_type := claim_row.subject_type;
|
|
1052
|
+
canonical_key := claim_row.canonical_key;
|
|
1053
|
+
display_label := claim_row.display_label;
|
|
1054
|
+
role := claim_row.role;
|
|
1055
|
+
state := claim_row.state;
|
|
1056
|
+
revision := claim_row.revision;
|
|
1057
|
+
provenance := claim_row.provenance;
|
|
1058
|
+
version_kind := claim_row.version_kind;
|
|
1059
|
+
version_value := claim_row.version_value;
|
|
1060
|
+
observed_at := claim_row.observed_at;
|
|
1061
|
+
created_at := claim_row.created_at;
|
|
1062
|
+
updated_at := claim_row.updated_at;
|
|
1063
|
+
settled_at := claim_row.settled_at;
|
|
1064
|
+
mutation_kind := 'released';
|
|
1065
|
+
DELETE FROM session_work_claim_write_capabilities capability
|
|
1066
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
1067
|
+
AND capability.transaction_id = pg_current_xact_id()
|
|
1068
|
+
AND capability.capability_id = write_capability_id;
|
|
1069
|
+
PERFORM set_config(
|
|
1070
|
+
'opengeni.session_work_claim_write_capability', coalesce(previous_capability, ''), true
|
|
1071
|
+
);
|
|
1072
|
+
RETURN NEXT;
|
|
1073
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1074
|
+
PERFORM set_config(
|
|
1075
|
+
'opengeni.session_work_claim_write_capability', coalesce(previous_capability, ''), true
|
|
1076
|
+
);
|
|
1077
|
+
RAISE;
|
|
1078
|
+
END;
|
|
1079
|
+
$$;
|
|
1080
|
+
|
|
1081
|
+
CREATE OR REPLACE FUNCTION settle_active_session_work_claims(
|
|
1082
|
+
p_account_id uuid,
|
|
1083
|
+
p_workspace_id uuid,
|
|
1084
|
+
p_session_id uuid,
|
|
1085
|
+
p_state text,
|
|
1086
|
+
p_reason text
|
|
1087
|
+
) RETURNS integer
|
|
1088
|
+
LANGUAGE plpgsql
|
|
1089
|
+
SECURITY DEFINER
|
|
1090
|
+
SET search_path FROM CURRENT
|
|
1091
|
+
AS $$
|
|
1092
|
+
DECLARE
|
|
1093
|
+
claim_row session_work_claims%ROWTYPE;
|
|
1094
|
+
now_value timestamptz := clock_timestamp();
|
|
1095
|
+
settled_count integer := 0;
|
|
1096
|
+
write_capability_id uuid := gen_random_uuid();
|
|
1097
|
+
previous_account_id text := current_setting('opengeni.account_id', true);
|
|
1098
|
+
previous_workspace_id text := current_setting('opengeni.workspace_id', true);
|
|
1099
|
+
previous_capability text := current_setting(
|
|
1100
|
+
'opengeni.session_work_claim_write_capability', true
|
|
1101
|
+
);
|
|
1102
|
+
BEGIN
|
|
1103
|
+
IF p_state NOT IN ('released','superseded','stale')
|
|
1104
|
+
OR p_reason NOT IN ('completed','cancelled','failed','superseded')
|
|
1105
|
+
THEN
|
|
1106
|
+
RAISE EXCEPTION 'work claim lifecycle settlement is invalid' USING ERRCODE = '22023';
|
|
1107
|
+
END IF;
|
|
1108
|
+
PERFORM set_config('opengeni.account_id', p_account_id::text, true);
|
|
1109
|
+
PERFORM set_config('opengeni.workspace_id', p_workspace_id::text, true);
|
|
1110
|
+
INSERT INTO session_work_claim_write_capabilities (
|
|
1111
|
+
backend_pid, transaction_id, capability_id
|
|
1112
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), write_capability_id);
|
|
1113
|
+
PERFORM set_config(
|
|
1114
|
+
'opengeni.session_work_claim_write_capability', write_capability_id::text, true
|
|
1115
|
+
);
|
|
1116
|
+
FOR claim_row IN
|
|
1117
|
+
SELECT * FROM session_work_claims claim
|
|
1118
|
+
WHERE claim.account_id = p_account_id
|
|
1119
|
+
AND claim.workspace_id = p_workspace_id
|
|
1120
|
+
AND claim.session_id = p_session_id
|
|
1121
|
+
AND claim.state = 'active'
|
|
1122
|
+
ORDER BY claim.id
|
|
1123
|
+
FOR UPDATE
|
|
1124
|
+
LOOP
|
|
1125
|
+
UPDATE session_work_claims claim SET
|
|
1126
|
+
state = p_state,
|
|
1127
|
+
revision = claim.revision + 1,
|
|
1128
|
+
settled_at = now_value,
|
|
1129
|
+
updated_at = now_value
|
|
1130
|
+
WHERE claim.workspace_id = p_workspace_id
|
|
1131
|
+
AND claim.id = claim_row.id
|
|
1132
|
+
AND claim.state = 'active'
|
|
1133
|
+
AND claim.revision = claim_row.revision
|
|
1134
|
+
RETURNING * INTO STRICT claim_row;
|
|
1135
|
+
INSERT INTO session_work_claim_revisions (
|
|
1136
|
+
account_id, workspace_id, claim_id, session_id, root_session_id,
|
|
1137
|
+
operation_id, input_hash, mutation_kind, prior_revision, resulting_revision,
|
|
1138
|
+
subject_namespace, subject_type, canonical_key, subject_digest, display_label,
|
|
1139
|
+
role, state, provenance, version_kind, version_value, observed_at,
|
|
1140
|
+
claim_created_at, claim_updated_at, settled_at, actor_kind, actor_subject_id,
|
|
1141
|
+
reason
|
|
1142
|
+
) VALUES (
|
|
1143
|
+
claim_row.account_id, claim_row.workspace_id, claim_row.id,
|
|
1144
|
+
claim_row.session_id, claim_row.root_session_id, gen_random_uuid(),
|
|
1145
|
+
encode(sha256(convert_to(jsonb_build_object(
|
|
1146
|
+
'claimId', claim_row.id,
|
|
1147
|
+
'priorRevision', claim_row.revision - 1,
|
|
1148
|
+
'state', p_state,
|
|
1149
|
+
'reason', p_reason
|
|
1150
|
+
)::text, 'UTF8')), 'hex'),
|
|
1151
|
+
p_state, claim_row.revision - 1, claim_row.revision,
|
|
1152
|
+
claim_row.subject_namespace, claim_row.subject_type, claim_row.canonical_key,
|
|
1153
|
+
claim_row.subject_digest, claim_row.display_label, claim_row.role, claim_row.state,
|
|
1154
|
+
claim_row.provenance, claim_row.version_kind, claim_row.version_value,
|
|
1155
|
+
claim_row.observed_at, claim_row.created_at, claim_row.updated_at,
|
|
1156
|
+
claim_row.settled_at, 'system', 'system:lifecycle', p_reason
|
|
1157
|
+
);
|
|
1158
|
+
settled_count := settled_count + 1;
|
|
1159
|
+
END LOOP;
|
|
1160
|
+
DELETE FROM session_work_claim_write_capabilities capability
|
|
1161
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
1162
|
+
AND capability.transaction_id = pg_current_xact_id()
|
|
1163
|
+
AND capability.capability_id = write_capability_id;
|
|
1164
|
+
PERFORM set_config(
|
|
1165
|
+
'opengeni.session_work_claim_write_capability', coalesce(previous_capability, ''), true
|
|
1166
|
+
);
|
|
1167
|
+
PERFORM set_config('opengeni.account_id', coalesce(previous_account_id, ''), true);
|
|
1168
|
+
PERFORM set_config('opengeni.workspace_id', coalesce(previous_workspace_id, ''), true);
|
|
1169
|
+
RETURN settled_count;
|
|
1170
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1171
|
+
PERFORM set_config(
|
|
1172
|
+
'opengeni.session_work_claim_write_capability', coalesce(previous_capability, ''), true
|
|
1173
|
+
);
|
|
1174
|
+
PERFORM set_config('opengeni.account_id', coalesce(previous_account_id, ''), true);
|
|
1175
|
+
PERFORM set_config('opengeni.workspace_id', coalesce(previous_workspace_id, ''), true);
|
|
1176
|
+
RAISE;
|
|
1177
|
+
END;
|
|
1178
|
+
$$;
|
|
1179
|
+
|
|
1180
|
+
CREATE OR REPLACE FUNCTION settle_session_work_claims_on_session_terminal()
|
|
1181
|
+
RETURNS trigger
|
|
1182
|
+
LANGUAGE plpgsql
|
|
1183
|
+
SECURITY DEFINER
|
|
1184
|
+
SET search_path FROM CURRENT
|
|
1185
|
+
AS $$
|
|
1186
|
+
BEGIN
|
|
1187
|
+
IF NEW.status IS DISTINCT FROM OLD.status AND NEW.status = 'cancelled' THEN
|
|
1188
|
+
PERFORM settle_active_session_work_claims(
|
|
1189
|
+
NEW.account_id, NEW.workspace_id, NEW.id, 'released', 'cancelled'
|
|
1190
|
+
);
|
|
1191
|
+
ELSIF NEW.status IS DISTINCT FROM OLD.status AND NEW.status = 'failed' THEN
|
|
1192
|
+
PERFORM settle_active_session_work_claims(
|
|
1193
|
+
NEW.account_id, NEW.workspace_id, NEW.id, 'stale', 'failed'
|
|
1194
|
+
);
|
|
1195
|
+
END IF;
|
|
1196
|
+
RETURN NEW;
|
|
1197
|
+
END;
|
|
1198
|
+
$$;
|
|
1199
|
+
|
|
1200
|
+
CREATE TRIGGER sessions_settle_work_claims_terminal
|
|
1201
|
+
AFTER UPDATE OF "status" ON "sessions"
|
|
1202
|
+
FOR EACH ROW
|
|
1203
|
+
WHEN (NEW."status" IS DISTINCT FROM OLD."status")
|
|
1204
|
+
EXECUTE FUNCTION settle_session_work_claims_on_session_terminal();
|
|
1205
|
+
|
|
1206
|
+
CREATE OR REPLACE FUNCTION settle_session_work_claims_on_goal_completion()
|
|
1207
|
+
RETURNS trigger
|
|
1208
|
+
LANGUAGE plpgsql
|
|
1209
|
+
SECURITY DEFINER
|
|
1210
|
+
SET search_path FROM CURRENT
|
|
1211
|
+
AS $$
|
|
1212
|
+
BEGIN
|
|
1213
|
+
IF NEW.status IS DISTINCT FROM OLD.status AND NEW.status = 'completed' THEN
|
|
1214
|
+
PERFORM settle_active_session_work_claims(
|
|
1215
|
+
NEW.account_id, NEW.workspace_id, NEW.session_id, 'released', 'completed'
|
|
1216
|
+
);
|
|
1217
|
+
END IF;
|
|
1218
|
+
RETURN NEW;
|
|
1219
|
+
END;
|
|
1220
|
+
$$;
|
|
1221
|
+
|
|
1222
|
+
CREATE TRIGGER session_goals_settle_work_claims_completed
|
|
1223
|
+
AFTER UPDATE OF "status" ON "session_goals"
|
|
1224
|
+
FOR EACH ROW
|
|
1225
|
+
WHEN (NEW."status" IS DISTINCT FROM OLD."status" AND NEW."status" = 'completed')
|
|
1226
|
+
EXECUTE FUNCTION settle_session_work_claims_on_goal_completion();
|
|
1227
|
+
|
|
1228
|
+
DO $work_claim_search_paths$
|
|
1229
|
+
DECLARE
|
|
1230
|
+
data_schema text := current_schema();
|
|
1231
|
+
BEGIN
|
|
1232
|
+
EXECUTE format(
|
|
1233
|
+
'ALTER FUNCTION opengeni_private.session_work_claim_capability_active() '
|
|
1234
|
+
|| 'SET search_path = %I, pg_catalog', data_schema
|
|
1235
|
+
);
|
|
1236
|
+
EXECUTE format(
|
|
1237
|
+
'ALTER FUNCTION %1$I.session_work_claim_subject_digest(text,text,text) '
|
|
1238
|
+
|| 'SET search_path = %1$I, pg_catalog', data_schema
|
|
1239
|
+
);
|
|
1240
|
+
EXECUTE format(
|
|
1241
|
+
'ALTER FUNCTION %1$I.guard_session_work_claim_mutation() '
|
|
1242
|
+
|| 'SET search_path = %1$I, pg_catalog', data_schema
|
|
1243
|
+
);
|
|
1244
|
+
EXECUTE format(
|
|
1245
|
+
'ALTER FUNCTION %1$I.guard_session_work_claim_revision_mutation() '
|
|
1246
|
+
|| 'SET search_path = %1$I, pg_catalog', data_schema
|
|
1247
|
+
);
|
|
1248
|
+
EXECUTE format(
|
|
1249
|
+
'ALTER FUNCTION %1$I.resolve_session_work_claim_attempt_authority(uuid,uuid,uuid,uuid,uuid,integer) '
|
|
1250
|
+
|| 'SET search_path = %1$I, pg_catalog', data_schema
|
|
1251
|
+
);
|
|
1252
|
+
EXECUTE format(
|
|
1253
|
+
'ALTER FUNCTION %1$I.upsert_session_work_claim_for_attempt(uuid,uuid,uuid,uuid,uuid,integer,uuid,integer,text,text,text,text,text,text,text) '
|
|
1254
|
+
|| 'SET search_path = %1$I, pg_catalog', data_schema
|
|
1255
|
+
);
|
|
1256
|
+
EXECUTE format(
|
|
1257
|
+
'ALTER FUNCTION %1$I.release_session_work_claim_for_attempt(uuid,uuid,uuid,uuid,uuid,integer,uuid,uuid,integer,text) '
|
|
1258
|
+
|| 'SET search_path = %1$I, pg_catalog', data_schema
|
|
1259
|
+
);
|
|
1260
|
+
EXECUTE format(
|
|
1261
|
+
'ALTER FUNCTION %1$I.settle_active_session_work_claims(uuid,uuid,uuid,text,text) '
|
|
1262
|
+
|| 'SET search_path = %1$I, pg_catalog', data_schema
|
|
1263
|
+
);
|
|
1264
|
+
EXECUTE format(
|
|
1265
|
+
'ALTER FUNCTION %1$I.settle_session_work_claims_on_session_terminal() '
|
|
1266
|
+
|| 'SET search_path = %1$I, pg_catalog', data_schema
|
|
1267
|
+
);
|
|
1268
|
+
EXECUTE format(
|
|
1269
|
+
'ALTER FUNCTION %1$I.settle_session_work_claims_on_goal_completion() '
|
|
1270
|
+
|| 'SET search_path = %1$I, pg_catalog', data_schema
|
|
1271
|
+
);
|
|
1272
|
+
END
|
|
1273
|
+
$work_claim_search_paths$;
|
|
1274
|
+
|
|
1275
|
+
REVOKE ALL ON "session_work_claims" FROM PUBLIC;
|
|
1276
|
+
REVOKE ALL ON "session_work_claim_revisions" FROM PUBLIC;
|
|
1277
|
+
REVOKE ALL ON "session_work_claim_write_capabilities" FROM PUBLIC;
|
|
1278
|
+
REVOKE ALL ON FUNCTION opengeni_private.session_work_claim_capability_active() FROM PUBLIC;
|
|
1279
|
+
REVOKE ALL ON FUNCTION session_work_claim_subject_digest(text,text,text) FROM PUBLIC;
|
|
1280
|
+
REVOKE ALL ON FUNCTION guard_session_work_claim_mutation() FROM PUBLIC;
|
|
1281
|
+
REVOKE ALL ON FUNCTION guard_session_work_claim_revision_mutation() FROM PUBLIC;
|
|
1282
|
+
REVOKE ALL ON FUNCTION resolve_session_work_claim_attempt_authority(uuid,uuid,uuid,uuid,uuid,integer) FROM PUBLIC;
|
|
1283
|
+
REVOKE ALL ON FUNCTION upsert_session_work_claim_for_attempt(uuid,uuid,uuid,uuid,uuid,integer,uuid,integer,text,text,text,text,text,text,text) FROM PUBLIC;
|
|
1284
|
+
REVOKE ALL ON FUNCTION release_session_work_claim_for_attempt(uuid,uuid,uuid,uuid,uuid,integer,uuid,uuid,integer,text) FROM PUBLIC;
|
|
1285
|
+
REVOKE ALL ON FUNCTION settle_active_session_work_claims(uuid,uuid,uuid,text,text) FROM PUBLIC;
|
|
1286
|
+
REVOKE ALL ON FUNCTION settle_session_work_claims_on_session_terminal() FROM PUBLIC;
|
|
1287
|
+
REVOKE ALL ON FUNCTION settle_session_work_claims_on_goal_completion() FROM PUBLIC;
|
|
1288
|
+
|
|
1289
|
+
DO $work_claim_runtime_grants$
|
|
1290
|
+
BEGIN
|
|
1291
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
1292
|
+
REVOKE ALL ON "session_work_claims" FROM opengeni_app;
|
|
1293
|
+
REVOKE ALL ON "session_work_claim_revisions" FROM opengeni_app;
|
|
1294
|
+
REVOKE ALL ON "session_work_claim_write_capabilities" FROM opengeni_app;
|
|
1295
|
+
GRANT SELECT ON "session_work_claims" TO opengeni_app;
|
|
1296
|
+
GRANT EXECUTE ON FUNCTION opengeni_private.session_work_claim_capability_active()
|
|
1297
|
+
TO opengeni_app;
|
|
1298
|
+
GRANT EXECUTE ON FUNCTION upsert_session_work_claim_for_attempt(
|
|
1299
|
+
uuid,uuid,uuid,uuid,uuid,integer,uuid,integer,text,text,text,text,text,text,text
|
|
1300
|
+
) TO opengeni_app;
|
|
1301
|
+
GRANT EXECUTE ON FUNCTION release_session_work_claim_for_attempt(
|
|
1302
|
+
uuid,uuid,uuid,uuid,uuid,integer,uuid,uuid,integer,text
|
|
1303
|
+
) TO opengeni_app;
|
|
1304
|
+
END IF;
|
|
1305
|
+
END
|
|
1306
|
+
$work_claim_runtime_grants$;
|