@opengeni/db 4.3.0-canary.0 → 4.3.2-canary.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.
Files changed (36) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/{chunk-Y5XFVJJM.js → chunk-AI4NC6XW.js} +6 -5
  3. package/dist/chunk-AI4NC6XW.js.map +1 -0
  4. package/dist/{chunk-IHADTE7C.js → chunk-DVSLOUEI.js} +2 -2
  5. package/dist/{chunk-7JPLGBSA.js → chunk-F4BIHJ7J.js} +4 -4
  6. package/dist/{chunk-SGJDIF2D.js → chunk-I4ITNMX2.js} +2 -2
  7. package/dist/{chunk-NCSPNCQK.js → chunk-PFBUHOI5.js} +3 -3
  8. package/dist/{chunk-YGRT6VZO.js → chunk-UUPY6IGP.js} +2 -2
  9. package/dist/{chunk-YGBOVXC7.js → chunk-UVBPOGA7.js} +2 -2
  10. package/dist/{chunk-ILKJKDII.js → chunk-XBXANJQS.js} +2 -2
  11. package/dist/editable-artifact-durable-export.js +2 -2
  12. package/dist/editable-artifacts.js +3 -3
  13. package/dist/index.d.ts +9 -0
  14. package/dist/index.js +84 -26
  15. package/dist/index.js.map +1 -1
  16. package/dist/managed-auth-session-sets.js +3 -3
  17. package/dist/retained-provider-commands.js +2 -2
  18. package/dist/schema.js +1 -1
  19. package/dist/session-background-commands.js +4 -4
  20. package/dist/session-command-output.js +4 -4
  21. package/dist/session-event-slices.js +2 -2
  22. package/dist/session-tenancy.js +3 -3
  23. package/dist/video-generation.js +3 -3
  24. package/dist/workspace-tool-defaults.js +4 -4
  25. package/package.json +6 -6
  26. package/src/index.ts +83 -4
  27. package/src/schema.ts +5 -4
  28. package/src/workspace-artifacts.ts +10 -4
  29. package/dist/chunk-Y5XFVJJM.js.map +0 -1
  30. /package/dist/{chunk-IHADTE7C.js.map → chunk-DVSLOUEI.js.map} +0 -0
  31. /package/dist/{chunk-7JPLGBSA.js.map → chunk-F4BIHJ7J.js.map} +0 -0
  32. /package/dist/{chunk-SGJDIF2D.js.map → chunk-I4ITNMX2.js.map} +0 -0
  33. /package/dist/{chunk-NCSPNCQK.js.map → chunk-PFBUHOI5.js.map} +0 -0
  34. /package/dist/{chunk-YGRT6VZO.js.map → chunk-UUPY6IGP.js.map} +0 -0
  35. /package/dist/{chunk-YGBOVXC7.js.map → chunk-UVBPOGA7.js.map} +0 -0
  36. /package/dist/{chunk-ILKJKDII.js.map → chunk-XBXANJQS.js.map} +0 -0
@@ -20,9 +20,9 @@ import {
20
20
  reapManagedAuthIsolatedSessions,
21
21
  releaseManagedAuthActorMutationLease,
22
22
  validateManagedAuthActorMutationLease
23
- } from "./chunk-YGRT6VZO.js";
24
- import "./chunk-SGJDIF2D.js";
25
- import "./chunk-Y5XFVJJM.js";
23
+ } from "./chunk-UUPY6IGP.js";
24
+ import "./chunk-I4ITNMX2.js";
25
+ import "./chunk-AI4NC6XW.js";
26
26
  import "./chunk-PZ5AY32C.js";
27
27
  export {
28
28
  ManagedAuthActorMutationInFlightError,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  withRlsContext
3
- } from "./chunk-SGJDIF2D.js";
3
+ } from "./chunk-I4ITNMX2.js";
4
4
  import {
5
5
  sandboxRetainedProcesses
6
- } from "./chunk-Y5XFVJJM.js";
6
+ } from "./chunk-AI4NC6XW.js";
7
7
  import "./chunk-PZ5AY32C.js";
8
8
 
9
9
  // src/retained-provider-commands.ts
package/dist/schema.js CHANGED
@@ -359,7 +359,7 @@ import {
359
359
  xaiRotationSettings,
360
360
  xaiSessionAccountPins,
361
361
  xaiSubscriptionCredentials
362
- } from "./chunk-Y5XFVJJM.js";
362
+ } from "./chunk-AI4NC6XW.js";
363
363
  import "./chunk-PZ5AY32C.js";
364
364
  export {
365
365
  agentRunStates,
@@ -22,10 +22,10 @@ import {
22
22
  settleClaimedConnectedMachineBackgroundCommandWithMutation,
23
23
  settleConnectedMachineSessionBackgroundCommandWithMutation,
24
24
  settleSessionBackgroundCommandForRetainedProcessInTransaction
25
- } from "./chunk-7JPLGBSA.js";
26
- import "./chunk-NCSPNCQK.js";
27
- import "./chunk-SGJDIF2D.js";
28
- import "./chunk-Y5XFVJJM.js";
25
+ } from "./chunk-F4BIHJ7J.js";
26
+ import "./chunk-PFBUHOI5.js";
27
+ import "./chunk-I4ITNMX2.js";
28
+ import "./chunk-AI4NC6XW.js";
29
29
  import "./chunk-PZ5AY32C.js";
30
30
  export {
31
31
  COMMAND_OUTPUT_DEFAULT_BYTES,
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  lockSessionEventWriteRows
3
- } from "./chunk-7JPLGBSA.js";
4
- import "./chunk-NCSPNCQK.js";
3
+ } from "./chunk-F4BIHJ7J.js";
4
+ import "./chunk-PFBUHOI5.js";
5
5
  import {
6
6
  withSessionActivityRlsContext
7
- } from "./chunk-SGJDIF2D.js";
7
+ } from "./chunk-I4ITNMX2.js";
8
8
  import {
9
9
  fromPostgresLosslessJson,
10
10
  sandboxRetainedProcesses,
@@ -12,7 +12,7 @@ import {
12
12
  sessionEvents,
13
13
  sessions,
14
14
  withLosslessContentWriteVersion
15
- } from "./chunk-Y5XFVJJM.js";
15
+ } from "./chunk-AI4NC6XW.js";
16
16
  import "./chunk-PZ5AY32C.js";
17
17
 
18
18
  // src/session-command-output.ts
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  rawRows,
3
3
  withWorkspaceRls
4
- } from "./chunk-SGJDIF2D.js";
4
+ } from "./chunk-I4ITNMX2.js";
5
5
  import {
6
6
  LOSSLESS_JSON_STRING_PREFIX,
7
7
  fromPostgresLosslessJson,
8
8
  sessionEvents,
9
9
  sessionTurns
10
- } from "./chunk-Y5XFVJJM.js";
10
+ } from "./chunk-AI4NC6XW.js";
11
11
  import "./chunk-PZ5AY32C.js";
12
12
 
13
13
  // src/session-event-slices.ts
@@ -14,9 +14,9 @@ import {
14
14
  replayAppliedSessionFork,
15
15
  sessionTenancyProductActivated,
16
16
  transitionSessionVisibility
17
- } from "./chunk-ILKJKDII.js";
18
- import "./chunk-SGJDIF2D.js";
19
- import "./chunk-Y5XFVJJM.js";
17
+ } from "./chunk-XBXANJQS.js";
18
+ import "./chunk-I4ITNMX2.js";
19
+ import "./chunk-AI4NC6XW.js";
20
20
  import "./chunk-PZ5AY32C.js";
21
21
  export {
22
22
  SessionTenancyAccessError,
@@ -29,9 +29,9 @@ import {
29
29
  settleVideoGenerationFailure,
30
30
  settleVideoGenerationReady,
31
31
  updateWorkspaceVideoGenerationPolicy
32
- } from "./chunk-NCSPNCQK.js";
33
- import "./chunk-SGJDIF2D.js";
34
- import "./chunk-Y5XFVJJM.js";
32
+ } from "./chunk-PFBUHOI5.js";
33
+ import "./chunk-I4ITNMX2.js";
34
+ import "./chunk-AI4NC6XW.js";
35
35
  import "./chunk-PZ5AY32C.js";
36
36
  export {
37
37
  ACTIVE_VIDEO_GENERATION_STATUSES,
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  lockWorkspaceInferenceControl
3
- } from "./chunk-7JPLGBSA.js";
4
- import "./chunk-NCSPNCQK.js";
3
+ } from "./chunk-F4BIHJ7J.js";
4
+ import "./chunk-PFBUHOI5.js";
5
5
  import {
6
6
  withWorkspaceRls
7
- } from "./chunk-SGJDIF2D.js";
8
- import "./chunk-Y5XFVJJM.js";
7
+ } from "./chunk-I4ITNMX2.js";
8
+ import "./chunk-AI4NC6XW.js";
9
9
  import "./chunk-PZ5AY32C.js";
10
10
 
11
11
  // src/workspace-tool-defaults.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "4.3.0-canary.0",
3
+ "version": "4.3.2-canary.0",
4
4
  "description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -94,11 +94,11 @@
94
94
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
95
95
  },
96
96
  "dependencies": {
97
- "@opengeni/codemode": "^0.5.4-canary.0",
98
- "@opengeni/codex": "^0.2.22-canary.6",
99
- "@opengeni/config": "^1.1.0-canary.0",
100
- "@opengeni/contracts": "^3.0.0-canary.0",
101
- "@opengeni/network": "^0.3.1-canary.6",
97
+ "@opengeni/codemode": "^0.5.5-canary.0",
98
+ "@opengeni/codex": "^0.2.22-canary.7",
99
+ "@opengeni/config": "^1.1.1-canary.0",
100
+ "@opengeni/contracts": "^3.0.1-canary.0",
101
+ "@opengeni/network": "^0.3.1-canary.7",
102
102
  "drizzle-orm": "^0.45.2",
103
103
  "postgres": "^3.4.7"
104
104
  },
package/src/index.ts CHANGED
@@ -272,6 +272,7 @@ import {
272
272
  SANDBOX_PROVIDER_INSTANCE_ID_FIELDS_BY_BACKEND,
273
273
  parseWorkspaceArchiveDescriptor,
274
274
  parseWorkspaceArchiveObjectRef,
275
+ validateWorkspaceArchiveObjectRef,
275
276
  workspaceArchivePayloadPresent,
276
277
  omitInlineWorkspaceArchiveWhenObjectRefPresent,
277
278
  type WorkspaceArchiveObjectRef,
@@ -53448,13 +53449,30 @@ export async function adoptLegacyModalCheckpointArtifact(
53448
53449
  }
53449
53450
 
53450
53451
  function publishedWorkspaceArchiveFields(input: {
53452
+ accountId: string;
53453
+ workspaceId: string;
53454
+ sandboxGroupId: string;
53455
+ workspaceArchiveMeta: SandboxArchiveRevision;
53451
53456
  workspaceArchive?: string | null;
53452
53457
  workspaceArchiveRef?: WorkspaceArchiveObjectRef | null;
53453
53458
  }): {
53454
53459
  workspaceArchive?: string;
53455
53460
  workspaceArchiveRef?: WorkspaceArchiveObjectRef;
53456
53461
  } {
53457
- const ref = parseWorkspaceArchiveObjectRef(input.workspaceArchiveRef) ?? undefined;
53462
+ const ref =
53463
+ input.workspaceArchiveRef == null
53464
+ ? undefined
53465
+ : (validateWorkspaceArchiveObjectRef(input.workspaceArchiveRef, {
53466
+ accountId: input.accountId,
53467
+ workspaceId: input.workspaceId,
53468
+ sandboxGroupId: input.sandboxGroupId,
53469
+ descriptor: input.workspaceArchiveMeta,
53470
+ }) ?? undefined);
53471
+ if (input.workspaceArchiveRef != null && !ref) {
53472
+ throw new Error(
53473
+ "Invalid workspace archive object ref: publication scope or descriptor mismatch",
53474
+ );
53475
+ }
53458
53476
  const inline =
53459
53477
  typeof input.workspaceArchive === "string" && input.workspaceArchive.length > 0
53460
53478
  ? input.workspaceArchive
@@ -53475,6 +53493,30 @@ function publishedWorkspaceArchiveFields(input: {
53475
53493
  return { workspaceArchive: inline };
53476
53494
  }
53477
53495
 
53496
+ /** Object-candidate outcome from the publication transaction, independent of
53497
+ * lifecycle `wrote`. Absent for inline/provider receipts and pure CAS checks.
53498
+ * `unused` is NOT a retirement receipt: only the owner of a fresh candidate with
53499
+ * no other publication in flight may use it for immediate cleanup. Exceptions
53500
+ * give no disposition (commit may have happened). Legacy/shared keys and evicted
53501
+ * refs need durable no-reattachment evidence before deletion. */
53502
+ export type WorkspaceArchiveCandidateDisposition = "adopted" | "already_referenced" | "unused";
53503
+
53504
+ function workspaceArchiveCandidateFields(
53505
+ candidate: WorkspaceArchiveObjectRef | undefined,
53506
+ lockedResumeState: Record<string, unknown> | null | undefined,
53507
+ ): { candidateDisposition?: WorkspaceArchiveCandidateDisposition } {
53508
+ if (!candidate) return {};
53509
+ const sessionState = lockedResumeState?.sessionState as Record<string, unknown> | undefined;
53510
+ const referenced = [
53511
+ sessionState?.workspaceArchiveRef,
53512
+ sessionState?.workspaceArchivePrevRef,
53513
+ ].some((value) => {
53514
+ const ref = parseWorkspaceArchiveObjectRef(value);
53515
+ return ref?.key === candidate.key && ref.backend === candidate.backend;
53516
+ });
53517
+ return { candidateDisposition: referenced ? "already_referenced" : "unused" };
53518
+ }
53519
+
53478
53520
  export async function persistDrainSnapshot(
53479
53521
  db: Database,
53480
53522
  input: {
@@ -53511,12 +53553,14 @@ export async function persistDrainSnapshot(
53511
53553
  ): Promise<{
53512
53554
  wrote: boolean;
53513
53555
  archiveRevision: string | null;
53556
+ candidateDisposition?: WorkspaceArchiveCandidateDisposition;
53514
53557
  }> {
53515
53558
  const workspaceArchiveMeta =
53516
53559
  input.workspaceArchive === null ? null : parseArchiveRevision(input.workspaceArchiveMeta);
53517
53560
  if (input.workspaceArchive !== null && !workspaceArchiveMeta) {
53518
53561
  throw new Error("Invalid verified workspace archive descriptor");
53519
53562
  }
53563
+ const published = input.workspaceArchive === null ? null : publishedWorkspaceArchiveFields(input);
53520
53564
  if (
53521
53565
  workspaceArchiveMeta?.version === 2 &&
53522
53566
  (workspaceArchiveMeta.provider === "modal_snapshot_filesystem" ||
@@ -53575,10 +53619,15 @@ export async function persistDrainSnapshot(
53575
53619
  for update
53576
53620
  `);
53577
53621
  const row = guard[0];
53622
+ const candidateFields = workspaceArchiveCandidateFields(
53623
+ published?.workspaceArchiveRef,
53624
+ row?.resume_state,
53625
+ );
53578
53626
  if (!row) {
53579
53627
  return {
53580
53628
  wrote: false,
53581
53629
  archiveRevision: null,
53630
+ ...candidateFields,
53582
53631
  };
53583
53632
  }
53584
53633
  const sourceLeaseMatches =
@@ -53624,7 +53673,7 @@ export async function persistDrainSnapshot(
53624
53673
  lateReceipt.providerRequestId === input.providerRequestId &&
53625
53674
  !row.unsettled_mutation;
53626
53675
  if (!activePublication && !coldLatePublication) {
53627
- return { wrote: false, archiveRevision: null };
53676
+ return { wrote: false, archiveRevision: null, ...candidateFields };
53628
53677
  }
53629
53678
  const priorArchive = row.prior_archive ?? null;
53630
53679
  const priorArchivePrev = row.prior_archive_prev ?? null;
@@ -53642,7 +53691,7 @@ export async function persistDrainSnapshot(
53642
53691
  archiveRevision: null,
53643
53692
  };
53644
53693
  }
53645
- const published = publishedWorkspaceArchiveFields(input);
53694
+ if (!published) throw new Error("Missing workspace archive publication fields");
53646
53695
  const priorMeta = parseArchiveRevision(priorSessionState?.workspaceArchiveMeta);
53647
53696
  if (activePublication && row.archive_capture_published_at !== null) {
53648
53697
  // A predecessor and its successor may receive the same provider result.
@@ -53668,7 +53717,7 @@ export async function persistDrainSnapshot(
53668
53717
  )
53669
53718
  `);
53670
53719
  }
53671
- return { wrote: true, archiveRevision: priorMeta.revision };
53720
+ return { wrote: true, archiveRevision: priorMeta.revision, ...candidateFields };
53672
53721
  }
53673
53722
  const rotation = rotateWorkspaceArchives({
53674
53723
  resumeState: row.resume_state,
@@ -53729,11 +53778,13 @@ export async function persistDrainSnapshot(
53729
53778
  return {
53730
53779
  wrote: false,
53731
53780
  archiveRevision: null,
53781
+ ...candidateFields,
53732
53782
  };
53733
53783
  }
53734
53784
  return {
53735
53785
  wrote: true,
53736
53786
  archiveRevision: workspaceArchiveMeta?.revision ?? null,
53787
+ ...(published.workspaceArchiveRef ? { candidateDisposition: "adopted" as const } : {}),
53737
53788
  };
53738
53789
  },
53739
53790
  );
@@ -54132,6 +54183,7 @@ export async function persistWarmSnapshot(
54132
54183
  throttled: boolean;
54133
54184
  superseded: boolean;
54134
54185
  archiveRevision: string | null;
54186
+ candidateDisposition?: WorkspaceArchiveCandidateDisposition;
54135
54187
  }> {
54136
54188
  const capturedAtMs = input.capturedAtMs ?? Date.now();
54137
54189
  const workspaceArchiveMeta = parseArchiveRevision(input.workspaceArchiveMeta);
@@ -54201,6 +54253,26 @@ export async function persistWarmSnapshot(
54201
54253
  (attempt.outcome === "completed" ||
54202
54254
  attempt.outcome === "failed" ||
54203
54255
  attempt.outcome === "requires_action"));
54256
+ // Even a replay rejected by the attempt/epoch/capture guards may name
54257
+ // a committed current or previous object. Inspect those slots under the
54258
+ // same lease lock as publication, after the canonical workspace lock.
54259
+ // This conveys no retirement or no-future-reattachment authority.
54260
+ const candidateLease = published.workspaceArchiveRef
54261
+ ? await scopedDb.execute<{ resume_state: Record<string, unknown> | null }>(sql`
54262
+ select jsonb_build_object('sessionState', jsonb_build_object(
54263
+ 'workspaceArchiveRef', resume_state #> '{sessionState,workspaceArchiveRef}',
54264
+ 'workspaceArchivePrevRef', resume_state #> '{sessionState,workspaceArchivePrevRef}'
54265
+ )) as resume_state
54266
+ from sandbox_leases
54267
+ where workspace_id = ${input.workspaceId}
54268
+ and sandbox_group_id = ${input.sandboxGroupId}
54269
+ for update
54270
+ `)
54271
+ : [];
54272
+ const candidateFields = workspaceArchiveCandidateFields(
54273
+ published.workspaceArchiveRef,
54274
+ candidateLease[0]?.resume_state,
54275
+ );
54204
54276
  if (
54205
54277
  !attempt ||
54206
54278
  attempt.accountId !== input.accountId ||
@@ -54213,6 +54285,7 @@ export async function persistWarmSnapshot(
54213
54285
  throttled: false,
54214
54286
  superseded: true,
54215
54287
  archiveRevision: null,
54288
+ ...candidateFields,
54216
54289
  };
54217
54290
  }
54218
54291
  const guard = await scopedDb.execute<{
@@ -54270,6 +54343,7 @@ export async function persistWarmSnapshot(
54270
54343
  throttled: false,
54271
54344
  superseded: false,
54272
54345
  archiveRevision: null,
54346
+ ...candidateFields,
54273
54347
  };
54274
54348
  }
54275
54349
  const priorArchive = guard[0]!.prior_archive ?? null;
@@ -54290,6 +54364,7 @@ export async function persistWarmSnapshot(
54290
54364
  throttled: false,
54291
54365
  superseded: true,
54292
54366
  archiveRevision: null,
54367
+ ...candidateFields,
54293
54368
  };
54294
54369
  }
54295
54370
  if (
@@ -54302,6 +54377,7 @@ export async function persistWarmSnapshot(
54302
54377
  throttled: true,
54303
54378
  superseded: false,
54304
54379
  archiveRevision: null,
54380
+ ...candidateFields,
54305
54381
  };
54306
54382
  }
54307
54383
  const rotation = rotateWorkspaceArchives({
@@ -54323,6 +54399,7 @@ export async function persistWarmSnapshot(
54323
54399
  throttled: false,
54324
54400
  superseded: false,
54325
54401
  archiveRevision: null,
54402
+ ...candidateFields,
54326
54403
  };
54327
54404
  }
54328
54405
  const livenessGuard: "warm" | "draining" = rowLiveness;
@@ -54355,6 +54432,7 @@ export async function persistWarmSnapshot(
54355
54432
  throttled: false,
54356
54433
  superseded: false,
54357
54434
  archiveRevision: null,
54435
+ ...candidateFields,
54358
54436
  };
54359
54437
  }
54360
54438
  return {
@@ -54362,6 +54440,7 @@ export async function persistWarmSnapshot(
54362
54440
  throttled: false,
54363
54441
  superseded: false,
54364
54442
  archiveRevision: workspaceArchiveMeta.revision,
54443
+ ...(published.workspaceArchiveRef ? { candidateDisposition: "adopted" as const } : {}),
54365
54444
  };
54366
54445
  },
54367
54446
  );
package/src/schema.ts CHANGED
@@ -6646,10 +6646,11 @@ export const sessionTurns = pgTable(
6646
6646
  .$type<Record<string, unknown>>()
6647
6647
  .notNull()
6648
6648
  .default({ backfill: true }),
6649
- // Immutable human authority for exact-attempt governance. Human turns bind
6650
- // their own subject; trusted continuations/compactions may inherit the
6651
- // causal turn's value while retaining a service initiator. Null means the
6652
- // turn has no human preference authority.
6649
+ // Bounded, immutable causal-human selector for named attempt-bound
6650
+ // capabilities. Human turns bind their own subject; trusted continuations
6651
+ // and compactions may inherit the causal turn's value while retaining a
6652
+ // service initiator. It never authorizes by itself. Null means pure service
6653
+ // work has no human-bound authority.
6653
6654
  initiatingHumanSubjectId: text("initiating_human_subject_id"),
6654
6655
  // Exact goal authority frozen when the logical turn is accepted. The
6655
6656
  // migration trigger fills this for old and rolling writers; claim only
@@ -538,7 +538,11 @@ async function assertAttemptAuthority(
538
538
  FOR NO KEY UPDATE OF session
539
539
  ), locked_turn AS MATERIALIZED (
540
540
  SELECT turn.id, turn.account_id, turn.workspace_id, turn.session_id,
541
- turn.active_attempt_id, turn.execution_generation, turn.initiator_subject_id
541
+ turn.active_attempt_id, turn.execution_generation,
542
+ coalesce(
543
+ turn.initiating_human_subject_id,
544
+ case when turn.initiator_kind = 'subject' then turn.initiator_subject_id end
545
+ ) as initiating_human_subject_id
542
546
  FROM session_turns turn
543
547
  JOIN locked_session session
544
548
  ON session.id = turn.session_id
@@ -548,8 +552,10 @@ async function assertAttemptAuthority(
548
552
  AND turn.active_attempt_id = ${input.sourceAttemptId}::uuid
549
553
  AND turn.execution_generation = ${input.sourceExecutionGeneration}
550
554
  AND turn.status IN ('running', 'requires_action', 'recovering', 'waiting_capacity')
551
- AND turn.initiator_kind = 'subject'
552
- AND length(btrim(turn.initiator_subject_id)) BETWEEN 1 AND 1024
555
+ AND length(btrim(coalesce(
556
+ turn.initiating_human_subject_id,
557
+ case when turn.initiator_kind = 'subject' then turn.initiator_subject_id end
558
+ ))) BETWEEN 1 AND 1024
553
559
  FOR UPDATE OF turn
554
560
  ), locked_attempt AS MATERIALIZED (
555
561
  SELECT attempt.id, attempt.account_id, attempt.workspace_id,
@@ -578,7 +584,7 @@ async function assertAttemptAuthority(
578
584
  `)) as unknown as Array<{ id: string }>;
579
585
  if (!rows[0]) {
580
586
  throw new WorkspaceArtifactOperationError(
581
- "Artifact mutation requires the exact active attempt and execution generation",
587
+ "Artifact mutation requires the exact active attempt, execution generation, and immutable human authority",
582
588
  );
583
589
  }
584
590
  }