@dsh-enhanced/plugin-control-plane 0.1.65 → 0.1.67

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 (136) hide show
  1. package/README.md +123 -7
  2. package/lib/attestation.d.ts.map +1 -1
  3. package/lib/attestation.js +33 -1
  4. package/lib/attestation.js.map +1 -1
  5. package/lib/creation-capability-empty-handoff.d.ts +57 -0
  6. package/lib/creation-capability-empty-handoff.d.ts.map +1 -0
  7. package/lib/creation-capability-empty-handoff.js +147 -0
  8. package/lib/creation-capability-empty-handoff.js.map +1 -0
  9. package/lib/creation-capability-execution.d.ts +13 -0
  10. package/lib/creation-capability-execution.d.ts.map +1 -0
  11. package/lib/creation-capability-execution.js +112 -0
  12. package/lib/creation-capability-execution.js.map +1 -0
  13. package/lib/creation-capability-feedback.d.ts +58 -1
  14. package/lib/creation-capability-feedback.d.ts.map +1 -1
  15. package/lib/creation-capability-feedback.js +532 -50
  16. package/lib/creation-capability-feedback.js.map +1 -1
  17. package/lib/creation-capability-handoff-common.d.ts +60 -0
  18. package/lib/creation-capability-handoff-common.d.ts.map +1 -0
  19. package/lib/creation-capability-handoff-common.js +195 -0
  20. package/lib/creation-capability-handoff-common.js.map +1 -0
  21. package/lib/creation-capability-journal.d.ts +19 -0
  22. package/lib/creation-capability-journal.d.ts.map +1 -1
  23. package/lib/creation-capability-journal.js +62 -21
  24. package/lib/creation-capability-journal.js.map +1 -1
  25. package/lib/creation-capability-runtime.d.ts +65 -1
  26. package/lib/creation-capability-runtime.d.ts.map +1 -1
  27. package/lib/creation-capability-runtime.js +178 -18
  28. package/lib/creation-capability-runtime.js.map +1 -1
  29. package/lib/creation-capability-sealed-audit.d.ts +19 -0
  30. package/lib/creation-capability-sealed-audit.d.ts.map +1 -0
  31. package/lib/creation-capability-sealed-audit.js +114 -0
  32. package/lib/creation-capability-sealed-audit.js.map +1 -0
  33. package/lib/creation-capability-sealed-handoff.d.ts +160 -0
  34. package/lib/creation-capability-sealed-handoff.d.ts.map +1 -0
  35. package/lib/creation-capability-sealed-handoff.js +695 -0
  36. package/lib/creation-capability-sealed-handoff.js.map +1 -0
  37. package/lib/creation-capability-trend-snapshot.d.ts +70 -0
  38. package/lib/creation-capability-trend-snapshot.d.ts.map +1 -0
  39. package/lib/creation-capability-trend-snapshot.js +117 -0
  40. package/lib/creation-capability-trend-snapshot.js.map +1 -0
  41. package/lib/creation-capability-types.d.ts +254 -4
  42. package/lib/creation-capability-types.d.ts.map +1 -1
  43. package/lib/in-process-reload.d.ts +191 -0
  44. package/lib/in-process-reload.d.ts.map +1 -0
  45. package/lib/in-process-reload.js +284 -0
  46. package/lib/in-process-reload.js.map +1 -0
  47. package/lib/index.d.ts +15 -4
  48. package/lib/index.d.ts.map +1 -1
  49. package/lib/index.js +10 -2
  50. package/lib/index.js.map +1 -1
  51. package/lib/linux-source-build.d.ts +16 -0
  52. package/lib/linux-source-build.d.ts.map +1 -0
  53. package/lib/linux-source-build.js +268 -0
  54. package/lib/linux-source-build.js.map +1 -0
  55. package/lib/linux-source-preflight.d.ts +31 -0
  56. package/lib/linux-source-preflight.d.ts.map +1 -0
  57. package/lib/linux-source-preflight.js +101 -0
  58. package/lib/linux-source-preflight.js.map +1 -0
  59. package/lib/live-qualification-runtime.d.ts +6 -2
  60. package/lib/live-qualification-runtime.d.ts.map +1 -1
  61. package/lib/live-qualification-runtime.js +65 -11
  62. package/lib/live-qualification-runtime.js.map +1 -1
  63. package/lib/owner-task-gap-types.d.ts +23 -0
  64. package/lib/owner-task-gap-types.d.ts.map +1 -1
  65. package/lib/owner-task-gaps.d.ts +4 -0
  66. package/lib/owner-task-gaps.d.ts.map +1 -1
  67. package/lib/owner-task-gaps.js +35 -1
  68. package/lib/owner-task-gaps.js.map +1 -1
  69. package/lib/revision-capability-journal.d.ts +103 -0
  70. package/lib/revision-capability-journal.d.ts.map +1 -0
  71. package/lib/revision-capability-journal.js +872 -0
  72. package/lib/revision-capability-journal.js.map +1 -0
  73. package/lib/revision-capability-runtime.d.ts +74 -0
  74. package/lib/revision-capability-runtime.d.ts.map +1 -0
  75. package/lib/revision-capability-runtime.js +562 -0
  76. package/lib/revision-capability-runtime.js.map +1 -0
  77. package/lib/revision-capability-types.d.ts +289 -0
  78. package/lib/revision-capability-types.d.ts.map +1 -0
  79. package/lib/revision-capability-types.js +2 -0
  80. package/lib/revision-capability-types.js.map +1 -0
  81. package/lib/scanner-owner-runtime.d.ts +34 -0
  82. package/lib/scanner-owner-runtime.d.ts.map +1 -0
  83. package/lib/scanner-owner-runtime.js +99 -0
  84. package/lib/scanner-owner-runtime.js.map +1 -0
  85. package/lib/service.d.ts +107 -6
  86. package/lib/service.d.ts.map +1 -1
  87. package/lib/service.js +643 -47
  88. package/lib/service.js.map +1 -1
  89. package/lib/source-approval-authority.d.ts.map +1 -1
  90. package/lib/source-approval-authority.js +25 -7
  91. package/lib/source-approval-authority.js.map +1 -1
  92. package/lib/source-build-diagnostics.d.ts +31 -0
  93. package/lib/source-build-diagnostics.d.ts.map +1 -0
  94. package/lib/source-build-diagnostics.js +110 -0
  95. package/lib/source-build-diagnostics.js.map +1 -0
  96. package/lib/source-build.d.ts +25 -2
  97. package/lib/source-build.d.ts.map +1 -1
  98. package/lib/source-build.js +145 -26
  99. package/lib/source-build.js.map +1 -1
  100. package/lib/source-creation.d.ts +6 -2
  101. package/lib/source-creation.d.ts.map +1 -1
  102. package/lib/source-creation.js +11 -2
  103. package/lib/source-creation.js.map +1 -1
  104. package/lib/source-job-types.d.ts +3 -1
  105. package/lib/source-job-types.d.ts.map +1 -1
  106. package/lib/source-jobs.d.ts +33 -1
  107. package/lib/source-jobs.d.ts.map +1 -1
  108. package/lib/source-jobs.js +141 -29
  109. package/lib/source-jobs.js.map +1 -1
  110. package/lib/source-workspace.d.ts.map +1 -1
  111. package/lib/source-workspace.js +0 -1
  112. package/lib/source-workspace.js.map +1 -1
  113. package/lib/sqlite.d.ts +1 -1
  114. package/lib/sqlite.d.ts.map +1 -1
  115. package/lib/sqlite.js +36 -1
  116. package/lib/sqlite.js.map +1 -1
  117. package/lib/store.d.ts +22 -2
  118. package/lib/store.d.ts.map +1 -1
  119. package/lib/store.js +477 -177
  120. package/lib/store.js.map +1 -1
  121. package/lib/systemd-host-authority.d.ts.map +1 -1
  122. package/lib/systemd-host-authority.js +4 -0
  123. package/lib/systemd-host-authority.js.map +1 -1
  124. package/lib/task-observation-runtime.d.ts +6 -2
  125. package/lib/task-observation-runtime.d.ts.map +1 -1
  126. package/lib/task-observation-runtime.js +69 -11
  127. package/lib/task-observation-runtime.js.map +1 -1
  128. package/lib/types.d.ts +47 -1
  129. package/lib/types.d.ts.map +1 -1
  130. package/lib/version.d.ts +1 -1
  131. package/lib/version.js +1 -1
  132. package/lib/work-item-creation-contract.d.ts +17 -0
  133. package/lib/work-item-creation-contract.d.ts.map +1 -0
  134. package/lib/work-item-creation-contract.js +61 -0
  135. package/lib/work-item-creation-contract.js.map +1 -0
  136. package/package.json +10 -10
package/lib/store.js CHANGED
@@ -2,6 +2,7 @@ import { validateLiveQualificationTerms, assertLiveQualificationBatch, parseLive
2
2
  import { createHash, randomUUID } from 'node:crypto';
3
3
  import { createPublicKey, verify } from 'node:crypto';
4
4
  import { spawnSync } from 'node:child_process';
5
+ import { types as utilTypes } from 'node:util';
5
6
  import { realpathSync } from 'node:fs';
6
7
  import { basename, isAbsolute, posix, resolve, win32 } from 'node:path';
7
8
  import { discover, parseCatalog } from './catalog.js';
@@ -13,11 +14,12 @@ import { validateSourceBaselineConfig, verifySourceBaselineHistory } from './sou
13
14
  import { validateScopedPluginFiles } from './source-workspace.js';
14
15
  import { validateCreationCapabilitySource } from './creation-capability-source.js';
15
16
  import { validateSourceRevisionBinding } from './source-revision.js';
16
- import { validateSourceCreationFiles, validateSourceCreationGrant } from './source-creation.js';
17
- import { sourceGrowthRunDigest, validatePluginRevisionRegressionCertificate, validatePluginRevisionVerificationCertificate, validatePluginCreationVerificationCertificate, validateSourceGrowthRunBinding } from '@dsh-enhanced/assistant-growth-contract';
17
+ import { validateSourceCreationFiles, validateSourceCreationGrant, normalizeSourceCreationGrant } from './source-creation.js';
18
+ import { anySourceGrowthRunDigest as sourceGrowthRunDigest, isSourceOwnerContinuation, validateAnySourceGrowthRunBinding, validatePluginRevisionRegressionCertificate, validatePluginRevisionVerificationCertificate, validatePluginCreationVerificationCertificate } from '@dsh-enhanced/assistant-growth-contract';
18
19
  import { validateAdoptionHandoffTerms } from './adoption-handoff.js';
19
20
  import { assertForegroundDeployment, assertForegroundTask } from './foreground-deployment.js';
20
21
  import { assertTaskObservationBatch, getTaskObservationRecord, readTaskObservationContext } from './task-observation-store.js';
22
+ import { createCreationCapabilityTrendSnapshot, listCreationCapabilityTrendSnapshots, sealCreationCapabilityTrendSnapshot } from './creation-capability-trend-snapshot.js';
21
23
  import { parseRuntimeEpochRequest, parseRuntimeEpochReceipt, verifyRuntimeEpochReceipt } from './runtime-epoch.js';
22
24
  import { runtimeIdentityDigest } from './foreground-deployment.js';
23
25
  import { readHostMaintenanceRecords, verifyHostMaintenanceChain, hostMaintenanceCanonical, hostMaintenanceDigest } from './host-maintenance.js';
@@ -123,6 +125,61 @@ function canonical(value) {
123
125
  export function controlPlaneDigest(value) {
124
126
  return createHash('sha256').update(canonical(value)).digest('hex');
125
127
  }
128
+ /** Structural validation precedes this exact prepared-build binding check. */
129
+ function creationVerificationMatchesPreparedEvidence(certificate, evidence) {
130
+ if (certificate.protocol === 'assistant-growth/creation-verification/v1') {
131
+ return evidence.schemaVersion === 1 && !Object.hasOwn(certificate, 'execution');
132
+ }
133
+ const execution = certificate.execution;
134
+ if (certificate.protocol === 'assistant-growth/creation-verification/v2') {
135
+ return evidence.schemaVersion === 2 && evidence.boundary === 'macos-seatbelt-single-process-v1'
136
+ && execution?.boundary === 'macos-seatbelt-single-process-v1'
137
+ && execution.runtimeDigest === evidence.runtimeDigest
138
+ && execution.buildEvidenceDigest === controlPlaneDigest(evidence);
139
+ }
140
+ return certificate.protocol === 'assistant-growth/creation-verification/v3'
141
+ && evidence.schemaVersion === 3 && evidence.boundary === 'linux-native-repository-v1'
142
+ && execution?.boundary === 'linux-native-process-seccomp-v1'
143
+ && execution.buildRuntimeDigest === evidence.runtimeDigest
144
+ && execution.buildEvidenceDigest === controlPlaneDigest(evidence);
145
+ }
146
+ /** Legacy revision v1 has no execution claim; Linux requires its own signed build provenance. */
147
+ function revisionVerificationMatchesPreparedEvidence(certificate, evidence) {
148
+ if (certificate.protocol === 'assistant-growth/revision-verification/v1') {
149
+ return evidence.schemaVersion !== 3 && !Object.hasOwn(certificate, 'execution');
150
+ }
151
+ const execution = certificate.execution;
152
+ return certificate.protocol === 'assistant-growth/revision-verification/v2'
153
+ && evidence.schemaVersion === 3 && evidence.boundary === 'linux-native-repository-v1'
154
+ && execution?.boundary === 'linux-native-process-seccomp-v1'
155
+ && execution.buildRuntimeDigest === evidence.runtimeDigest
156
+ && execution.buildEvidenceDigest === controlPlaneDigest(evidence);
157
+ }
158
+ /** Retained certificates must refer to the exact candidate and one parent/candidate observer family. */
159
+ function revisionRegressionMatchesCandidate(certificate, candidate, parent) {
160
+ if (certificate.candidateVerificationDigest !== controlPlaneDigest(candidate)
161
+ || controlPlaneDigest(certificate.plan) !== controlPlaneDigest(candidate.plan)
162
+ || controlPlaneDigest(certificate.source) !== controlPlaneDigest(candidate.source)
163
+ || controlPlaneDigest(certificate.parent) !== controlPlaneDigest(candidate.parent)
164
+ || certificate.contractDigest !== parent.contractDigest
165
+ || candidate.parent.planId !== parent.plan.id
166
+ || candidate.parent.certificateDigest !== controlPlaneDigest(parent)
167
+ || candidate.parent.artifactSha256 !== parent.plan.artifactSha256)
168
+ return false;
169
+ if (candidate.protocol === 'assistant-growth/revision-verification/v1') {
170
+ return certificate.protocol === 'assistant-growth/revision-regression/v1'
171
+ && !Object.hasOwn(certificate, 'execution') && parent.protocol !== 'assistant-growth/creation-verification/v3';
172
+ }
173
+ const execution = certificate.execution, candidateExecution = candidate.execution, parentExecution = parent.execution;
174
+ return certificate.protocol === 'assistant-growth/revision-regression/v2'
175
+ && parent.protocol === 'assistant-growth/creation-verification/v3'
176
+ && execution?.boundary === 'linux-native-process-seccomp-v1'
177
+ && candidateExecution?.boundary === execution.boundary && parentExecution?.boundary === execution.boundary
178
+ && execution.runtimeDigest === candidateExecution.runtimeDigest && execution.runtimeDigest === parentExecution.runtimeDigest
179
+ && execution.observerPreparationDigest === candidateExecution.observerPreparationDigest
180
+ && execution.observerPreparationDigest === parentExecution.observerPreparationDigest
181
+ && candidateExecution.buildRuntimeDigest === parentExecution.buildRuntimeDigest;
182
+ }
126
183
  function bounded(value, field, maximum = 1_000) {
127
184
  if (typeof value !== 'string')
128
185
  throw new ControlPlaneStoreError('invalid-input', `${field} must be text`);
@@ -172,14 +229,16 @@ function creationBindingFromStored(value) {
172
229
  let growthRun;
173
230
  if (Object.hasOwn(binding, 'growthRun')) {
174
231
  try {
175
- validateSourceGrowthRunBinding(binding['growthRun']);
232
+ validateAnySourceGrowthRunBinding(binding['growthRun']);
176
233
  }
177
234
  catch {
178
235
  throw new ControlPlaneStoreError('invalid-input', 'source creation growth run is invalid');
179
236
  }
180
237
  growthRun = structuredClone(binding['growthRun']);
181
238
  }
182
- return Object.freeze({ grant: Object.freeze(structuredClone(binding['grant'])), generatorDigest: binding['generatorDigest'],
239
+ if (growthRun?.protocol === 'assistant-growth/source-run/v2' && binding['grant'].workItemFailures !== true)
240
+ throw new ControlPlaneStoreError('invalid-input', 'work item creation grant must explicitly permit work item failures');
241
+ return Object.freeze({ grant: Object.freeze(normalizeSourceCreationGrant(binding['grant'])), generatorDigest: binding['generatorDigest'],
183
242
  ...(growthRun === undefined ? {} : { growthRun: Object.freeze(growthRun) }) });
184
243
  }
185
244
  function revisionBindingFromStored(value) {
@@ -643,6 +702,51 @@ function activationFromRow(row) {
643
702
  }
644
703
  function preparedEvidenceFromStored(value) {
645
704
  const item = objectRecord(value, 'stored source prepared evidence');
705
+ if (item['schemaVersion'] === 3) {
706
+ exactKeys(item, ['schemaVersion', 'kind', 'boundary', 'runtimeDigest', 'executionContractDigest', 'buildScriptSha256',
707
+ 'archive', 'receipt', 'environment', 'checks', 'pack', 'logDigest', 'durationMs', 'preparedAt'], 'stored Linux source prepared evidence');
708
+ const archive = objectRecord(item['archive'], 'stored Linux source archive');
709
+ exactKeys(archive, ['sizeBytes', 'sha256'], 'stored Linux source archive');
710
+ const receipt = objectRecord(item['receipt'], 'stored Linux source receipt binding');
711
+ exactKeys(receipt, ['requestDigest', 'nativeNonce', 'nativeResourceId', 'intentDigest', 'resourcesDigest', 'receiptDigest'], 'stored Linux source receipt binding');
712
+ const environment = objectRecord(item['environment'], 'stored Linux source environment');
713
+ exactKeys(environment, ['npmConfigIgnoreScripts', 'frozenLockfile', 'offline', 'nodeVersion', 'pnpmVersion'], 'stored Linux source environment');
714
+ const pack = objectRecord(item['pack'], 'stored Linux source pack');
715
+ exactKeys(pack, ['name', 'version', 'sizeBytes', 'sha256'], 'stored Linux source pack');
716
+ const digest = (value) => typeof value === 'string' && DIGEST.test(value);
717
+ const size = (value, maximum) => Number.isSafeInteger(value) && Number(value) >= 1 && Number(value) <= maximum;
718
+ const storedChecks = item['checks'];
719
+ const checks = ['source-archive-integrity', 'frozen-offline-install', 'repository-pnpm-check', 'source-input-recheck', 'package-integrity'];
720
+ if (item['kind'] !== 'dsh-source-prepared-evidence' || item['boundary'] !== 'linux-native-repository-v1'
721
+ || !digest(item['runtimeDigest']) || !digest(item['executionContractDigest']) || !digest(item['buildScriptSha256'])
722
+ || !size(archive['sizeBytes'], 64 * 1024 * 1024) || !digest(archive['sha256'])
723
+ || !['requestDigest', 'intentDigest', 'resourcesDigest', 'receiptDigest'].every(key => digest(receipt[key]))
724
+ || typeof receipt['nativeNonce'] !== 'string' || !/^[a-f0-9]{32}$/u.test(receipt['nativeNonce'])
725
+ || typeof receipt['nativeResourceId'] !== 'string' || !/^linux-[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/u.test(receipt['nativeResourceId'])
726
+ || environment['npmConfigIgnoreScripts'] !== true || environment['frozenLockfile'] !== true || environment['offline'] !== true
727
+ || !['nodeVersion', 'pnpmVersion'].every(key => typeof environment[key] === 'string' && environment[key] !== '' && environment[key].length <= 64)
728
+ || !Array.isArray(storedChecks) || storedChecks.length !== checks.length || checks.some((check, index) => storedChecks[index] !== check)
729
+ || typeof pack['name'] !== 'string' || pack['name'] === '' || pack['name'].length > 214
730
+ || typeof pack['version'] !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._+=-]{0,63}$/u.test(pack['version'])
731
+ || !size(pack['sizeBytes'], MAX_PREPARED_ARTIFACT_BYTES) || !digest(pack['sha256']) || !digest(item['logDigest'])
732
+ || !Number.isSafeInteger(item['durationMs']) || Number(item['durationMs']) < 0
733
+ || !Number.isSafeInteger(item['preparedAt']) || Number(item['preparedAt']) < 0) {
734
+ throw new ControlPlaneStoreError('invalid-state', 'stored Linux source prepared evidence is corrupt');
735
+ }
736
+ // Pure structural validation keeps the independent authority runtime free of
737
+ // an isolation executor import. The runner verifies the private receipt.
738
+ return Object.freeze({ schemaVersion: 3, kind: 'dsh-source-prepared-evidence',
739
+ boundary: 'linux-native-repository-v1', runtimeDigest: item['runtimeDigest'],
740
+ executionContractDigest: item['executionContractDigest'], buildScriptSha256: item['buildScriptSha256'],
741
+ archive: Object.freeze({ sizeBytes: Number(archive['sizeBytes']), sha256: String(archive['sha256']) }),
742
+ receipt: Object.freeze({ requestDigest: String(receipt['requestDigest']), nativeNonce: String(receipt['nativeNonce']),
743
+ nativeResourceId: String(receipt['nativeResourceId']), intentDigest: String(receipt['intentDigest']),
744
+ resourcesDigest: String(receipt['resourcesDigest']), receiptDigest: String(receipt['receiptDigest']) }),
745
+ environment: Object.freeze({ npmConfigIgnoreScripts: true, frozenLockfile: true, offline: true,
746
+ nodeVersion: String(environment['nodeVersion']), pnpmVersion: String(environment['pnpmVersion']) }), checks: Object.freeze(checks),
747
+ pack: Object.freeze({ name: pack['name'], version: pack['version'], sizeBytes: Number(pack['sizeBytes']), sha256: pack['sha256'] }),
748
+ logDigest: item['logDigest'], durationMs: Number(item['durationMs']), preparedAt: Number(item['preparedAt']) });
749
+ }
646
750
  if (item['schemaVersion'] === 2) {
647
751
  exactKeys(item, ['schemaVersion', 'kind', 'boundary', 'runtimeDigest', 'fixedCompilerSha256', 'checks', 'pack', 'preparedAt'], 'stored native source prepared evidence');
648
752
  const pack = objectRecord(item['pack'], 'stored native source pack');
@@ -759,7 +863,7 @@ function sourceFromRow(row) {
759
863
  }
760
864
  const revision = row.revision_json === null ? undefined : revisionBindingFromStored(JSON.parse(row.revision_json));
761
865
  const prepared = row.mode === 'modify' || row.mode === 'prepared-create' || row.mode === 'prepared-revise';
762
- if (prepared !== (preparedEvidence !== undefined) || (row.mode === 'modify' && preparedEvidence?.schemaVersion !== 1)
866
+ if (prepared !== (preparedEvidence !== undefined) || (row.mode === 'modify' && preparedEvidence?.schemaVersion === 2)
763
867
  || (row.mode === 'prepared-create') !== (creation !== undefined) || (row.mode === 'prepared-revise') !== (revision !== undefined)
764
868
  || ((row.mode === 'prepared-create' || row.mode === 'prepared-revise') && (!['pending-approval', 'expired'].includes(row.status) || (creation ?? revision)?.generatorDigest !== row.generator_digest
765
869
  || row.approval_json !== null || row.release_authorization_json !== null || row.release_id !== null))
@@ -1110,10 +1214,23 @@ function releaseArtifact(evidence) {
1110
1214
  provenanceSha256: evidence.provenanceSha256, mergedCommit: evidence.mergedCommit, dshBaseline: evidence.dshBaseline,
1111
1215
  capabilities: evidence.capabilities, authorities: evidence.authorities, requires: evidence.requires };
1112
1216
  }
1217
+ function sourceBuildEvidenceSchema(build) {
1218
+ if (!('backend' in build))
1219
+ return 1;
1220
+ if (build.backend === 'macos-seatbelt')
1221
+ return 2;
1222
+ if (build.backend === 'linux-native')
1223
+ return 3;
1224
+ throw new ControlPlaneStoreError('invalid-state', 'source job build backend is unknown');
1225
+ }
1113
1226
  function sourceJobIntentFromStored(value) {
1114
1227
  const intent = objectRecord(value, 'source job intent');
1115
- const native = typeof intent['build'] === 'object' && intent['build'] !== null
1116
- && intent['build']['backend'] === 'macos-seatbelt';
1228
+ const build = objectRecord(intent['build'], 'source job build');
1229
+ const backend = build['backend'];
1230
+ if (Object.hasOwn(build, 'backend') && backend !== 'macos-seatbelt' && backend !== 'linux-native') {
1231
+ throw new ControlPlaneStoreError('invalid-input', 'source job build backend is unknown');
1232
+ }
1233
+ const native = backend === 'macos-seatbelt' || backend === 'linux-native';
1117
1234
  exactKeys(intent, ['authority', 'owner', 'ownerDigest', 'trustDigest', 'repository', 'name', 'gapId', 'gapRevision', 'gapDigest',
1118
1235
  'baseCommit', 'files', 'ttlMs', 'build', 'worktree', native ? 'nativeResource' : 'containerName', ...(Object.hasOwn(intent, 'baseline') ? ['baseline'] : []),
1119
1236
  ...(Object.hasOwn(intent, 'mode') ? ['mode'] : []), ...(Object.hasOwn(intent, 'creation') ? ['creation'] : []), ...(Object.hasOwn(intent, 'revision') ? ['revision'] : [])], 'source job intent');
@@ -1139,7 +1256,7 @@ function sourceJobIntentFromStored(value) {
1139
1256
  || !Array.isArray(intent['files']) || intent['files'].length === 0 || intent['files'].length > 64
1140
1257
  || !Number.isSafeInteger(intent['ttlMs']) || Number(intent['ttlMs']) < 60_000 || Number(intent['ttlMs']) > 86_400_000
1141
1258
  || (!native && (typeof intent['containerName'] !== 'string' || !/^dsh-source-job-[a-f0-9]{64}$/u.test(intent['containerName'])))
1142
- || (native && (intent['mode'] !== 'create' && intent['mode'] !== 'revise-created'))
1259
+ || (backend === 'macos-seatbelt' && (intent['mode'] !== 'create' && intent['mode'] !== 'revise-created'))
1143
1260
  || (intent['mode'] !== undefined && intent['mode'] !== 'create' && intent['mode'] !== 'revise-created')
1144
1261
  || (intent['mode'] === 'create') !== Object.hasOwn(intent, 'creation')
1145
1262
  || (intent['mode'] === 'revise-created') !== Object.hasOwn(intent, 'revision')) {
@@ -1148,7 +1265,7 @@ function sourceJobIntentFromStored(value) {
1148
1265
  if (native) {
1149
1266
  const resource = objectRecord(intent['nativeResource'], 'native source job resource');
1150
1267
  exactKeys(resource, ['backend', 'key'], 'native source job resource');
1151
- if (resource['backend'] !== 'macos-seatbelt' || typeof resource['key'] !== 'string'
1268
+ if (resource['backend'] !== backend || typeof resource['key'] !== 'string'
1152
1269
  || !/^source-job-[a-f0-9]{64}$/u.test(resource['key'])) {
1153
1270
  throw new ControlPlaneStoreError('invalid-input', 'native source job resource is invalid');
1154
1271
  }
@@ -1208,7 +1325,7 @@ function sourceJobIntentFromStored(value) {
1208
1325
  name: intent['name'], gapId: intent['gapId'], gapRevision: Number(intent['gapRevision']), gapDigest: intent['gapDigest'],
1209
1326
  baseCommit: intent['baseCommit'], files: Object.freeze(files), ttlMs: Number(intent['ttlMs']), build: intent['build'],
1210
1327
  worktree: intent['worktree'],
1211
- ...(native ? { nativeResource: Object.freeze({ backend: 'macos-seatbelt',
1328
+ ...(native ? { nativeResource: Object.freeze({ backend,
1212
1329
  key: intent['nativeResource'].key }) }
1213
1330
  : { containerName: intent['containerName'] }),
1214
1331
  ...(creation === undefined ? {} : { mode: 'create', creation }),
@@ -1236,6 +1353,21 @@ function ownerTaskFailureReferenceFromStored(value, errorCode = 'invalid-input')
1236
1353
  try {
1237
1354
  const reference = errorCode === 'invalid-input' ? exactInputRecord(value, 'owner task failure reference') : objectRecord(value, 'owner task failure reference');
1238
1355
  const keys = errorCode === 'invalid-input' ? exactInputKeys : exactKeys;
1356
+ if (reference['schemaVersion'] === 2) {
1357
+ keys(reference, ['schemaVersion', 'sourceProtocol', 'owner', 'outcomeId', 'projection', 'sourceDigest', 'presentationDigest', 'context'], 'work item failure reference');
1358
+ const projection = errorCode === 'invalid-input' ? exactInputRecord(reference['projection'], 'work item projection') : objectRecord(reference['projection'], 'work item projection');
1359
+ const context = errorCode === 'invalid-input' ? exactInputRecord(reference['context'], 'work item context') : objectRecord(reference['context'], 'work item context');
1360
+ keys(context, ['manifestDigest', 'objectiveDigest', 'objectiveBytes'], 'work item context');
1361
+ if (reference['sourceProtocol'] !== 'assistant-delivery/work-item-engineering-context/v1' || projection['subjectKind'] !== 'work-item'
1362
+ || typeof reference['presentationDigest'] !== 'string' || !DIGEST.test(reference['presentationDigest'])
1363
+ || typeof context['manifestDigest'] !== 'string' || !DIGEST.test(context['manifestDigest']) || typeof context['objectiveDigest'] !== 'string' || !DIGEST.test(context['objectiveDigest'])
1364
+ || !Number.isSafeInteger(context['objectiveBytes']) || Number(context['objectiveBytes']) < 1 || Number(context['objectiveBytes']) > 16384)
1365
+ throw new ControlPlaneStoreError(errorCode, 'work item failure reference is invalid');
1366
+ const legacy = ownerTaskFailureReferenceFromStored({ schemaVersion: 1, owner: reference['owner'], outcomeId: reference['outcomeId'], projection: { ...projection, subjectKind: 'foreground-turn' }, sourceDigest: reference['sourceDigest'] }, errorCode);
1367
+ return Object.freeze({ schemaVersion: 2, sourceProtocol: 'assistant-delivery/work-item-engineering-context/v1', owner: legacy.owner, outcomeId: legacy.outcomeId,
1368
+ projection: Object.freeze({ ...legacy.projection, subjectKind: 'work-item' }), sourceDigest: legacy.sourceDigest, presentationDigest: reference['presentationDigest'],
1369
+ context: Object.freeze({ manifestDigest: context['manifestDigest'], objectiveDigest: context['objectiveDigest'], objectiveBytes: Number(context['objectiveBytes']) }) });
1370
+ }
1239
1371
  keys(reference, ['schemaVersion', 'owner', 'outcomeId', 'projection', 'sourceDigest'], 'owner task failure reference');
1240
1372
  const owner = errorCode === 'invalid-input' ? exactInputRecord(reference['owner'], 'owner task failure owner') : objectRecord(reference['owner'], 'owner task failure owner');
1241
1373
  keys(owner, ['receiptVersion', 'authorityId', 'authorityHash', 'principalId', 'principalRecordId', 'principalVersion',
@@ -1733,6 +1865,9 @@ export class ControlPlaneStore {
1733
1865
  #database;
1734
1866
  #now;
1735
1867
  #ownerTaskFailureGapAdmission;
1868
+ #workItemWriter = false;
1869
+ #workItemSavepoints = [];
1870
+ #workItemSavepointSequence = 0;
1736
1871
  #foregroundDeploymentAdmission;
1737
1872
  #liveQualificationAdmission;
1738
1873
  #withLiveQualificationFence;
@@ -1804,14 +1939,41 @@ export class ControlPlaneStore {
1804
1939
  const reference = ownerTaskFailureReferenceFromStored(referenceInput);
1805
1940
  const referenceDigest = controlPlaneDigest(reference);
1806
1941
  const idempotencyKey = `owner-task-failure:${referenceDigest}`;
1807
- const normalized = { idempotencyKey, capability: 'foreground-task-repair',
1808
- context: 'Owner-verified foreground task failure. Private source reference retained.',
1942
+ const normalized = { idempotencyKey, capability: reference.schemaVersion === 2 ? 'work-item-task-repair' : 'foreground-task-repair',
1943
+ context: reference.schemaVersion === 2 ? 'Owner-verified background work-item failure. Private engineering reference retained.' : 'Owner-verified foreground task failure. Private source reference retained.',
1809
1944
  expectedValue: 0, frequency: 1, estimatedCost: 1, risk: 1 };
1810
1945
  const inputDigest = controlPlaneDigest(normalized);
1811
1946
  const now = this.#now();
1812
1947
  const id = `gap-${randomUUID()}`;
1813
- this.#database.exec('BEGIN IMMEDIATE');
1948
+ this.#beginMutation();
1814
1949
  try {
1950
+ if (reference.schemaVersion === 2) {
1951
+ // /new changes only a route generation. Preserve the first frozen owner
1952
+ // bytes for the same authenticated source rather than minting another gap.
1953
+ const stable = (value) => {
1954
+ const { bindingVersion: _bindingVersion, generation: _generation, ...owner } = value.owner;
1955
+ return controlPlaneDigest({ ...value, owner });
1956
+ };
1957
+ const candidates = this.#database.prepare(`SELECT gap_id FROM owner_task_failure_gaps
1958
+ WHERE json_extract(reference_json, '$.schemaVersion') = 2
1959
+ AND json_extract(reference_json, '$.sourceDigest') = ?
1960
+ AND json_extract(reference_json, '$.outcomeId') = ?
1961
+ AND json_extract(reference_json, '$.projection.subjectRef') = ? LIMIT 1001`)
1962
+ .all(reference.sourceDigest, reference.outcomeId, reference.projection.subjectRef);
1963
+ if (candidates.length > 1000)
1964
+ throw new ControlPlaneStoreError('conflict', 'work item failure historical lookup exceeds bound');
1965
+ const matches = candidates.filter(row => { const prior = this.getOwnerTaskFailureReference(row.gap_id); return prior?.schemaVersion === 2 && stable(prior) === stable(reference); });
1966
+ if (matches.length > 1)
1967
+ throw new ControlPlaneStoreError('conflict', 'work item failure source has ambiguous historical gaps');
1968
+ if (matches[0]) {
1969
+ const original = this.getOwnerTaskFailureReference(matches[0].gap_id);
1970
+ if (!isSourceOwnerContinuation(reference.owner, original.owner))
1971
+ throw new ControlPlaneStoreError('conflict', 'work item failure owner generation regressed');
1972
+ const gap = this.getGap(matches[0].gap_id);
1973
+ this.#commitMutation();
1974
+ return gap;
1975
+ }
1976
+ }
1815
1977
  const prior = this.#database.prepare('SELECT * FROM capability_gaps WHERE idempotency_key = ?').get(idempotencyKey);
1816
1978
  if (prior !== undefined) {
1817
1979
  if (prior.input_digest !== inputDigest)
@@ -1820,7 +1982,7 @@ export class ControlPlaneStore {
1820
1982
  if (stored === undefined || controlPlaneDigest(stored) !== referenceDigest) {
1821
1983
  throw new ControlPlaneStoreError('invalid-state', 'owner task failure gap sidecar is corrupt');
1822
1984
  }
1823
- this.#database.exec('COMMIT');
1985
+ this.#commitMutation();
1824
1986
  return gapFromRow(prior);
1825
1987
  }
1826
1988
  this.#database.prepare(`INSERT INTO capability_gaps (id, idempotency_key, input_digest, capability, context,
@@ -1829,14 +1991,70 @@ export class ControlPlaneStore {
1829
1991
  this.#database.prepare(`INSERT INTO owner_task_failure_gaps (gap_id, reference_json, reference_digest)
1830
1992
  VALUES (?, ?, ?)`).run(id, JSON.stringify(reference), referenceDigest);
1831
1993
  const gap = this.getGap(id);
1832
- this.#database.exec('COMMIT');
1994
+ this.#commitMutation();
1833
1995
  return gap;
1834
1996
  }
1835
1997
  catch (error) {
1836
- this.#database.exec('ROLLBACK');
1998
+ this.#rollbackMutation();
1837
1999
  throw error;
1838
2000
  }
1839
2001
  }
2002
+ #assertWorkItemSynchronous(result) {
2003
+ const refuse = () => { throw new ControlPlaneStoreError('invalid-input', 'owner task failure gap admission must be synchronous'); };
2004
+ if (result === null || (typeof result !== 'object' && typeof result !== 'function'))
2005
+ return;
2006
+ if (utilTypes.isProxy(result))
2007
+ refuse();
2008
+ if (utilTypes.isPromise(result)) {
2009
+ // Best effort only for ordinary native Promises. Do not execute an own
2010
+ // constructor or subclass species while refusing an asynchronous callback.
2011
+ if (Object.getPrototypeOf(result) === Promise.prototype && !Object.hasOwn(result, 'constructor')) {
2012
+ try {
2013
+ void Promise.prototype.then.call(result, undefined, () => undefined);
2014
+ }
2015
+ catch { /* synchronous refusal remains authoritative */ }
2016
+ }
2017
+ refuse();
2018
+ }
2019
+ let prototype = result;
2020
+ for (let depth = 0; prototype !== null && depth < 64; depth++) {
2021
+ if (utilTypes.isProxy(prototype) || Object.getOwnPropertyDescriptor(prototype, 'then'))
2022
+ refuse();
2023
+ prototype = Object.getPrototypeOf(prototype);
2024
+ }
2025
+ if (prototype !== null)
2026
+ refuse();
2027
+ }
2028
+ #beginMutation() {
2029
+ if (!this.#workItemWriter) {
2030
+ this.#database.exec('BEGIN IMMEDIATE');
2031
+ return;
2032
+ }
2033
+ const name = `cp_work_item_${++this.#workItemSavepointSequence}`;
2034
+ this.#database.exec(`SAVEPOINT ${name}`);
2035
+ this.#workItemSavepoints.push(name);
2036
+ }
2037
+ #commitMutation() {
2038
+ if (!this.#workItemWriter) {
2039
+ this.#database.exec('COMMIT');
2040
+ return;
2041
+ }
2042
+ const name = this.#workItemSavepoints.pop();
2043
+ if (!name)
2044
+ throw new ControlPlaneStoreError('invalid-state', 'work item nested writer is unbalanced');
2045
+ this.#database.exec(`RELEASE SAVEPOINT ${name}`);
2046
+ }
2047
+ #rollbackMutation() {
2048
+ if (!this.#workItemWriter) {
2049
+ this.#database.exec('ROLLBACK');
2050
+ return;
2051
+ }
2052
+ const name = this.#workItemSavepoints.pop();
2053
+ if (name) {
2054
+ this.#database.exec(`ROLLBACK TO SAVEPOINT ${name}`);
2055
+ this.#database.exec(`RELEASE SAVEPOINT ${name}`);
2056
+ }
2057
+ }
1840
2058
  getOwnerTaskFailureReference(gapId) {
1841
2059
  return readOwnerTaskFailureReference(this.#database, gapId);
1842
2060
  }
@@ -1846,22 +2064,48 @@ export class ControlPlaneStore {
1846
2064
  if (typeof gapId !== 'string' || !KEY.test(gapId) || typeof callback !== 'function') {
1847
2065
  throw new ControlPlaneStoreError('invalid-input', 'owner task failure gap admission is invalid');
1848
2066
  }
1849
- if (callback.constructor.name === 'AsyncFunction')
1850
- throw new ControlPlaneStoreError('invalid-input', 'owner task failure gap admission must be synchronous');
1851
2067
  if (this.#ownerTaskFailureGapAdmission !== undefined)
1852
2068
  throw new ControlPlaneStoreError('conflict', 'owner task failure gap admission is already active');
1853
- if (this.getOwnerTaskFailureReference(gapId) === undefined)
2069
+ const reference = this.getOwnerTaskFailureReference(gapId);
2070
+ if (reference === undefined)
1854
2071
  throw new ControlPlaneStoreError('not-found', 'owner task failure gap is absent');
2072
+ const workItem = reference.schemaVersion === 2;
2073
+ if (workItem ? utilTypes.isProxy(callback) || utilTypes.isAsyncFunction(callback) : callback.constructor.name === 'AsyncFunction')
2074
+ throw new ControlPlaneStoreError('invalid-input', 'owner task failure gap admission must be synchronous');
2075
+ if (workItem) {
2076
+ if (this.#database.isTransaction)
2077
+ throw new ControlPlaneStoreError('conflict', 'work item writer must be entered before CP mutation');
2078
+ this.#database.exec('BEGIN IMMEDIATE');
2079
+ this.#workItemWriter = true;
2080
+ }
1855
2081
  this.#ownerTaskFailureGapAdmission = gapId;
1856
2082
  try {
2083
+ if (workItem && controlPlaneDigest(this.getOwnerTaskFailureReference(gapId)) !== controlPlaneDigest(reference))
2084
+ throw new ControlPlaneStoreError('conflict', 'work item reference changed at writer boundary');
1857
2085
  const result = callback();
1858
- if (typeof result?.then === 'function') {
2086
+ if (workItem)
2087
+ this.#assertWorkItemSynchronous(result);
2088
+ else if (typeof result?.then === 'function') {
1859
2089
  throw new ControlPlaneStoreError('invalid-input', 'owner task failure gap admission must be synchronous');
1860
2090
  }
2091
+ if (workItem) {
2092
+ if (this.#workItemSavepoints.length)
2093
+ throw new ControlPlaneStoreError('invalid-state', 'work item nested writer was not retired');
2094
+ this.#database.exec('COMMIT');
2095
+ }
1861
2096
  return result;
1862
2097
  }
2098
+ catch (error) {
2099
+ if (workItem && this.#database.isTransaction)
2100
+ this.#database.exec('ROLLBACK');
2101
+ throw error;
2102
+ }
1863
2103
  finally {
1864
2104
  this.#ownerTaskFailureGapAdmission = undefined;
2105
+ if (workItem) {
2106
+ this.#workItemWriter = false;
2107
+ this.#workItemSavepoints = [];
2108
+ }
1865
2109
  }
1866
2110
  }
1867
2111
  #assertOwnerTaskFailureGapAdmission(gapId) {
@@ -1916,16 +2160,16 @@ export class ControlPlaneStore {
1916
2160
  return this.#withActivationSource(planId, () => {
1917
2161
  const own = !this.#database.isTransaction;
1918
2162
  if (own)
1919
- this.#database.exec('BEGIN IMMEDIATE');
2163
+ this.#beginMutation();
1920
2164
  try {
1921
2165
  const result = this.#withActivationSource(planId, callback, recovery);
1922
2166
  if (own)
1923
- this.#database.exec('COMMIT');
2167
+ this.#commitMutation();
1924
2168
  return result;
1925
2169
  }
1926
2170
  catch (error) {
1927
2171
  if (own)
1928
- this.#database.exec('ROLLBACK');
2172
+ this.#rollbackMutation();
1929
2173
  throw error;
1930
2174
  }
1931
2175
  }, recovery);
@@ -2004,11 +2248,11 @@ export class ControlPlaneStore {
2004
2248
  target: input.target, executor: input.executor,
2005
2249
  createdAt: now, expiresAt };
2006
2250
  const planDigest = controlPlaneDigest(immutable);
2007
- this.#database.exec('BEGIN IMMEDIATE');
2251
+ this.#beginMutation();
2008
2252
  try {
2009
2253
  const raced = this.#activationPlanReceiptByKey(idempotencyKey, 'create-activation-plan', inputDigest);
2010
2254
  if (raced !== undefined) {
2011
- this.#database.exec('COMMIT');
2255
+ this.#commitMutation();
2012
2256
  return raced;
2013
2257
  }
2014
2258
  const currentGap = this.getGap(gap.id);
@@ -2031,11 +2275,11 @@ export class ControlPlaneStore {
2031
2275
  }
2032
2276
  const receipt = { idempotencyKey, operation: 'create-activation-plan', inputDigest, result: plan, createdAt: now };
2033
2277
  this.#insertReceipt(receipt);
2034
- this.#database.exec('COMMIT');
2278
+ this.#commitMutation();
2035
2279
  return receipt;
2036
2280
  }
2037
2281
  catch (error) {
2038
- this.#database.exec('ROLLBACK');
2282
+ this.#rollbackMutation();
2039
2283
  throw error;
2040
2284
  }
2041
2285
  }
@@ -2071,7 +2315,7 @@ export class ControlPlaneStore {
2071
2315
  if (this.#adoptionCoordinatorId !== undefined)
2072
2316
  throw new ControlPlaneStoreError('invalid-state', 'adoption coordinator cannot prepare a target handoff');
2073
2317
  return this.#withActivationSource(input.planId, () => {
2074
- this.#database.exec('BEGIN IMMEDIATE');
2318
+ this.#beginMutation();
2075
2319
  try {
2076
2320
  this.assertOwnerActivationSource(input.planId);
2077
2321
  const plan = this.getPlan(input.planId), terms = plan.dossier.handoff;
@@ -2092,7 +2336,7 @@ export class ControlPlaneStore {
2092
2336
  if (prior.planDigest !== plan.digest || prior.coordinatorId !== terms.coordinatorId) {
2093
2337
  throw new ControlPlaneStoreError('invalid-state', 'stored adoption handoff is corrupt');
2094
2338
  }
2095
- this.#database.exec('COMMIT');
2339
+ this.#commitMutation();
2096
2340
  return prior;
2097
2341
  }
2098
2342
  const now = this.#now(), expiresAt = Math.min(now + terms.maximumWindowMs, plan.expiresAt, plan.approval.expiresAt);
@@ -2101,11 +2345,11 @@ export class ControlPlaneStore {
2101
2345
  this.#database.prepare('INSERT INTO adoption_handoffs (plan_id,plan_digest,coordinator_id,created_at,expires_at,revoked_at) VALUES (?,?,?,?,?,NULL)')
2102
2346
  .run(plan.id, plan.digest, terms.coordinatorId, now, expiresAt);
2103
2347
  const handoff = this.getAdoptionHandoff(plan.id);
2104
- this.#database.exec('COMMIT');
2348
+ this.#commitMutation();
2105
2349
  return handoff;
2106
2350
  }
2107
2351
  catch (error) {
2108
- this.#database.exec('ROLLBACK');
2352
+ this.#rollbackMutation();
2109
2353
  throw error;
2110
2354
  }
2111
2355
  });
@@ -2147,6 +2391,14 @@ export class ControlPlaneStore {
2147
2391
  throw new ControlPlaneStoreError('conflict', 'adoption handoff is inactive');
2148
2392
  }
2149
2393
  assertAdoptionHandoff(planId) { this.#assertAdoptionHandoff(planId, this.#adoptionCoordinatorId); }
2394
+ inspectSourceJobQuota(input) {
2395
+ const a = input.authority, c = input.creation;
2396
+ const row = this.#database.prepare('SELECT * FROM source_job_authorities WHERE authority_id=?').get(a.id);
2397
+ const grant = this.#database.prepare('SELECT * FROM source_creation_grants WHERE grant_id=?').get(c.id);
2398
+ if (row && (row.authority_digest !== a.digest || row.expires_at !== a.expiresAt || row.max_submissions !== a.maxSubmissions) || grant && (grant.grant_digest !== controlPlaneDigest(c) || grant.expires_at !== c.expiresAt || grant.max_creates !== c.maxCreates))
2399
+ throw new ControlPlaneStoreError('conflict', 'source job quota authority changed');
2400
+ return { sourceSubmissions: Math.max(0, a.maxSubmissions - (row?.submissions ?? 0)), creates: Math.max(0, c.maxCreates - (grant?.creates ?? 0)) };
2401
+ }
2150
2402
  enqueueSourceJob(input) {
2151
2403
  if (!/^source-job-[a-f0-9]{64}$/u.test(input.id) || input.automationId !== input.id) {
2152
2404
  throw new ControlPlaneStoreError('invalid-input', 'source job identity is invalid');
@@ -2175,7 +2427,7 @@ export class ControlPlaneStore {
2175
2427
  const growthRun = intent.creation?.growthRun ?? intent.revision?.growthRun;
2176
2428
  if (growthRun !== undefined && (now >= growthRun.generationDeadlineAt || now >= growthRun.expiresAt))
2177
2429
  throw new ControlPlaneStoreError('expired', 'source growth generation window is expired');
2178
- this.#database.exec('BEGIN IMMEDIATE');
2430
+ this.#beginMutation();
2179
2431
  try {
2180
2432
  const prior = this.#database.prepare('SELECT * FROM source_jobs WHERE authority_id = ? AND idempotency_key = ?')
2181
2433
  .get(intent.authority.id, idempotencyKey);
@@ -2184,7 +2436,7 @@ export class ControlPlaneStore {
2184
2436
  if (record.intentDigest !== intentDigest || record.id !== input.id || record.automationId !== input.automationId) {
2185
2437
  throw new ControlPlaneStoreError('conflict', 'source job idempotency key was reused with different input');
2186
2438
  }
2187
- this.#database.exec('COMMIT');
2439
+ this.#commitMutation();
2188
2440
  return record;
2189
2441
  }
2190
2442
  if (intent.baseline !== undefined) {
@@ -2260,11 +2512,11 @@ export class ControlPlaneStore {
2260
2512
  const record = this.getSourceJob(input.id);
2261
2513
  if (record === undefined)
2262
2514
  throw new ControlPlaneStoreError('invalid-state', 'inserted source job could not be read');
2263
- this.#database.exec('COMMIT');
2515
+ this.#commitMutation();
2264
2516
  return record;
2265
2517
  }
2266
2518
  catch (error) {
2267
- this.#database.exec('ROLLBACK');
2519
+ this.#rollbackMutation();
2268
2520
  throw error;
2269
2521
  }
2270
2522
  }
@@ -2284,7 +2536,7 @@ export class ControlPlaneStore {
2284
2536
  created_at DESC, id DESC LIMIT ?`).all(limit).map(sourceJobFromRow);
2285
2537
  }
2286
2538
  /** Prepared owner continuations have a bounded recovery query, independent of job history. */
2287
- listPreparedSourceApprovalJobs(includeRelease = false, includeExecution = false, includeAdoption = false, includeCreation = false, includeCreationAdoption = false, includeRevision = false, includeRegression = false) {
2539
+ listPreparedSourceApprovalJobs(includeRelease = false, includeExecution = false, includeAdoption = false, includeCreation = false, includeCreationAdoption = false, includeRevision = false, includeRegression = false, includeRevisionAdoption = false) {
2288
2540
  return this.#database.prepare(`SELECT j.* FROM source_jobs j JOIN source_plans p ON p.id = j.plan_id
2289
2541
  JOIN owner_task_failure_gaps g ON g.gap_id = p.gap_id
2290
2542
  LEFT JOIN source_adoptions a ON a.source_plan_id = p.id
@@ -2304,9 +2556,13 @@ export class ControlPlaneStore {
2304
2556
  OR (? = 1 AND p.mode = 'prepared-revise' AND p.status = 'pending-approval'
2305
2557
  AND json_type(j.intent_json, '$.revision.growthRun.revisionRegressionAcceptance') IS NOT NULL
2306
2558
  AND EXISTS (SELECT 1 FROM source_revision_verifications v WHERE v.plan_id=p.id AND v.status='verified')
2307
- AND NOT EXISTS (SELECT 1 FROM source_revision_regressions v WHERE v.plan_id=p.id)))
2559
+ AND NOT EXISTS (SELECT 1 FROM source_revision_regressions v WHERE v.plan_id=p.id))
2560
+ OR (? = 1 AND p.mode = 'prepared-revise' AND p.status = 'pending-approval'
2561
+ AND EXISTS (SELECT 1 FROM source_revision_verifications v WHERE v.plan_id=p.id AND v.status='verified')
2562
+ AND (json_type(j.intent_json, '$.revision.growthRun.revisionRegressionAcceptance') IS NULL
2563
+ OR EXISTS (SELECT 1 FROM source_revision_regressions r WHERE r.plan_id=p.id AND r.status='verified'))))
2308
2564
  AND (p.expires_at > ? OR ap.status IN ('staging', 'awaiting-reload', 'awaiting-readiness', 'awaiting-live-tasks', 'awaiting-effect-blocked-replay', 'awaiting-shadow', 'awaiting-canary', 'awaiting-soak', 'awaiting-health', 'commit-pending', 'rollback-pending'))
2309
- ORDER BY j.created_at, j.id LIMIT 1000`).all(includeRelease ? 1 : 0, includeExecution ? 1 : 0, includeAdoption ? 1 : 0, includeCreation ? 1 : 0, includeCreationAdoption ? 1 : 0, includeRevision ? 1 : 0, includeRegression ? 1 : 0, this.#now()).map(sourceJobFromRow);
2565
+ ORDER BY j.created_at, j.id LIMIT 1000`).all(includeRelease ? 1 : 0, includeExecution ? 1 : 0, includeAdoption ? 1 : 0, includeCreation ? 1 : 0, includeCreationAdoption ? 1 : 0, includeRevision ? 1 : 0, includeRegression ? 1 : 0, includeRevisionAdoption ? 1 : 0, this.#now()).map(sourceJobFromRow);
2310
2566
  }
2311
2567
  bindSourceJobDefinition(input) {
2312
2568
  if (!Number.isSafeInteger(input.revision) || input.revision < 1 || !DIGEST.test(input.definitionHash))
@@ -2411,9 +2667,9 @@ export class ControlPlaneStore {
2411
2667
  throw new ControlPlaneStoreError('invalid-input', 'modify source plan prepared digests are invalid');
2412
2668
  }
2413
2669
  preparedEvidence = preparedEvidenceFromStored(input.prepared.evidence);
2414
- if ((mode === 'modify' && preparedEvidence.schemaVersion !== 1)
2415
- || (preparedEvidence.schemaVersion === 2 && input.sourceJob === undefined)) {
2416
- throw new ControlPlaneStoreError('invalid-input', 'native source evidence requires a durable created plugin job');
2670
+ if ((mode === 'modify' && preparedEvidence.schemaVersion === 2)
2671
+ || (preparedEvidence.schemaVersion !== 1 && input.sourceJob === undefined)) {
2672
+ throw new ControlPlaneStoreError('invalid-input', 'native source evidence requires a matching durable source job');
2417
2673
  }
2418
2674
  }
2419
2675
  else if (input.prepared !== undefined) {
@@ -2482,7 +2738,7 @@ export class ControlPlaneStore {
2482
2738
  ? { ...immutable, mode, ...(creation === undefined ? {} : { creation }), ...(revision === undefined ? {} : { sourceRevision: revision }), sourceCheck: { treeDigest: input.prepared.treeDigest, patchDigest: input.prepared.patchDigest,
2483
2739
  checkedAt: input.prepared.checkedAt }, preparedEvidence }
2484
2740
  : immutable);
2485
- this.#database.exec('BEGIN IMMEDIATE');
2741
+ this.#beginMutation();
2486
2742
  try {
2487
2743
  const currentGap = this.getGap(gap.id);
2488
2744
  if (currentGap.revision !== gap.revision || currentGap.inputDigest !== gap.inputDigest || currentGap.status !== 'open'
@@ -2497,7 +2753,7 @@ export class ControlPlaneStore {
2497
2753
  || sourceJob.intent.baseCommit !== input.baseCommit || sourceJob.intent.name !== name
2498
2754
  || sourceJob.intent.repository !== input.repository || sourceJob.intent.worktree !== input.worktree || sourceJob.intent.ttlMs !== input.ttlMs
2499
2755
  || (mode === 'prepared-create') !== (sourceJob.intent.mode === 'create')
2500
- || (preparedEvidence !== undefined && ('backend' in sourceJob.intent.build) !== (preparedEvidence.schemaVersion === 2))
2756
+ || (preparedEvidence !== undefined && preparedEvidence.schemaVersion !== sourceBuildEvidenceSchema(sourceJob.intent.build))
2501
2757
  || (mode === 'prepared-create' && controlPlaneDigest(sourceJob.intent.creation) !== controlPlaneDigest(creation))
2502
2758
  || (mode === 'prepared-revise') !== (sourceJob.intent.mode === 'revise-created')
2503
2759
  || (mode === 'prepared-revise' && controlPlaneDigest(sourceJob.intent.revision) !== controlPlaneDigest(revision))) {
@@ -2560,11 +2816,11 @@ export class ControlPlaneStore {
2560
2816
  const plan = this.getSourcePlan(id);
2561
2817
  const receipt = { idempotencyKey: key, operation: 'create-source-plan', inputDigest, result: plan, createdAt: now };
2562
2818
  this.#insertReceipt(receipt);
2563
- this.#database.exec('COMMIT');
2819
+ this.#commitMutation();
2564
2820
  return receipt;
2565
2821
  }
2566
2822
  catch (error) {
2567
- this.#database.exec('ROLLBACK');
2823
+ this.#rollbackMutation();
2568
2824
  throw error;
2569
2825
  }
2570
2826
  }
@@ -2614,6 +2870,7 @@ export class ControlPlaneStore {
2614
2870
  || job.intent.creation.generatorDigest !== plan.generatorDigest
2615
2871
  || job.intent.repository !== plan.repository || job.intent.worktree !== plan.worktree
2616
2872
  || job.intent.baseCommit !== plan.baseCommit || job.intent.name !== plan.name
2873
+ || plan.preparedEvidence.schemaVersion !== sourceBuildEvidenceSchema(job.intent.build)
2617
2874
  || plan.expiresAt !== plan.createdAt + job.intent.ttlMs) {
2618
2875
  throw new ControlPlaneStoreError('invalid-state', 'prepared creation source job binding is corrupt');
2619
2876
  }
@@ -2659,6 +2916,7 @@ export class ControlPlaneStore {
2659
2916
  || job.intent.revision.generatorDigest !== plan.generatorDigest
2660
2917
  || job.intent.repository !== plan.repository || job.intent.worktree !== plan.worktree
2661
2918
  || job.intent.baseCommit !== plan.baseCommit || job.intent.name !== plan.name
2919
+ || plan.preparedEvidence.schemaVersion !== sourceBuildEvidenceSchema(job.intent.build)
2662
2920
  || plan.expiresAt !== plan.createdAt + job.intent.ttlMs) {
2663
2921
  throw new ControlPlaneStoreError('invalid-state', 'prepared revision source job binding is corrupt');
2664
2922
  }
@@ -2693,6 +2951,7 @@ export class ControlPlaneStore {
2693
2951
  const job = this.getPreparedSourceJob(plan.id);
2694
2952
  const reference = this.getOwnerTaskFailureReference(plan.gapId);
2695
2953
  if (!reference || !plan.sourceCheck || !plan.preparedEvidence || !plan.creation?.growthRun
2954
+ || !creationVerificationMatchesPreparedEvidence(certificate, plan.preparedEvidence)
2696
2955
  || !job.intent.creation?.growthRun || plan.digest !== certificate.plan.digest || plan.name !== certificate.plan.name
2697
2956
  || plan.sourceCheck.treeDigest !== certificate.plan.sourceTreeDigest
2698
2957
  || plan.sourceCheck.patchDigest !== certificate.plan.sourcePatchDigest
@@ -2709,7 +2968,7 @@ export class ControlPlaneStore {
2709
2968
  }
2710
2969
  this.readPreparedSourceArtifact(plan.id);
2711
2970
  const serialized = JSON.stringify(certificate), certificateDigest = controlPlaneDigest(certificate);
2712
- this.#database.exec('BEGIN IMMEDIATE');
2971
+ this.#beginMutation();
2713
2972
  try {
2714
2973
  const previous = this.#database.prepare('SELECT status,certificate_digest FROM source_creation_verifications WHERE plan_id=?')
2715
2974
  .get(plan.id);
@@ -2719,10 +2978,10 @@ export class ControlPlaneStore {
2719
2978
  else
2720
2979
  this.#database.prepare(`UPDATE source_creation_verifications SET status='verified',certificate_json=?,certificate_digest=?,updated_at=?
2721
2980
  WHERE plan_id=? AND status='claimed'`).run(serialized, certificateDigest, this.#now(), plan.id);
2722
- this.#database.exec('COMMIT');
2981
+ this.#commitMutation();
2723
2982
  }
2724
2983
  catch (error) {
2725
- this.#database.exec('ROLLBACK');
2984
+ this.#rollbackMutation();
2726
2985
  throw error;
2727
2986
  }
2728
2987
  }
@@ -2743,6 +3002,11 @@ export class ControlPlaneStore {
2743
3002
  if (certificate.plan.id !== planId || controlPlaneDigest(certificate) !== row.certificate_digest) {
2744
3003
  throw new ControlPlaneStoreError('invalid-state', 'stored creation verification certificate digest changed');
2745
3004
  }
3005
+ const plan = this.getSourcePlan(planId);
3006
+ if (certificate.plan.digest !== plan.digest || !plan.preparedEvidence
3007
+ || !creationVerificationMatchesPreparedEvidence(certificate, plan.preparedEvidence)) {
3008
+ throw new ControlPlaneStoreError('invalid-state', 'stored creation verification certificate does not match exact prepared source');
3009
+ }
2746
3010
  return certificate;
2747
3011
  }
2748
3012
  getCreationVerificationStatus(planId) {
@@ -2769,7 +3033,7 @@ export class ControlPlaneStore {
2769
3033
  if (!pinned || this.#now() >= pinned.expiresAt) {
2770
3034
  throw new ControlPlaneStoreError('invalid-state', 'creation verification requires a current pre-author policy binding');
2771
3035
  }
2772
- this.#database.exec('BEGIN IMMEDIATE');
3036
+ this.#beginMutation();
2773
3037
  try {
2774
3038
  const count = this.#database.prepare('SELECT count(*) AS n FROM source_creation_verifications').get();
2775
3039
  if (count.n >= 256)
@@ -2778,10 +3042,10 @@ export class ControlPlaneStore {
2778
3042
  this.#database.prepare(`INSERT INTO source_creation_verifications
2779
3043
  (plan_id,status,certificate_json,certificate_digest,reason,created_at,updated_at) VALUES (?,'claimed',NULL,NULL,NULL,?,?)`)
2780
3044
  .run(planId, now, now);
2781
- this.#database.exec('COMMIT');
3045
+ this.#commitMutation();
2782
3046
  }
2783
3047
  catch (error) {
2784
- this.#database.exec('ROLLBACK');
3048
+ this.#rollbackMutation();
2785
3049
  throw error;
2786
3050
  }
2787
3051
  }
@@ -2802,6 +3066,7 @@ export class ControlPlaneStore {
2802
3066
  const reference = this.getOwnerTaskFailureReference(plan.gapId);
2803
3067
  validateCreationCapabilitySource(source, certificate);
2804
3068
  if (!reference || controlPlaneDigest(plan.sourceRevision?.parent) !== controlPlaneDigest(certificate.parent) || !plan.sourceCheck || !plan.preparedEvidence || !plan.sourceRevision?.growthRun
3069
+ || !revisionVerificationMatchesPreparedEvidence(certificate, plan.preparedEvidence)
2805
3070
  || !job.intent.revision?.growthRun || plan.digest !== certificate.plan.digest || plan.name !== certificate.plan.name
2806
3071
  || plan.sourceCheck.treeDigest !== certificate.plan.sourceTreeDigest
2807
3072
  || plan.sourceCheck.patchDigest !== certificate.plan.sourcePatchDigest
@@ -2823,7 +3088,7 @@ export class ControlPlaneStore {
2823
3088
  if (Buffer.byteLength(sourceJson) > 2_097_152)
2824
3089
  throw new ControlPlaneStoreError('invalid-input', 'revision source archive exceeds bound');
2825
3090
  const serialized = JSON.stringify(certificate), certificateDigest = controlPlaneDigest(certificate);
2826
- this.#database.exec('BEGIN IMMEDIATE');
3091
+ this.#beginMutation();
2827
3092
  try {
2828
3093
  const previous = this.#database.prepare('SELECT status,certificate_digest FROM source_revision_verifications WHERE plan_id=?')
2829
3094
  .get(plan.id);
@@ -2838,10 +3103,10 @@ export class ControlPlaneStore {
2838
3103
  throw new ControlPlaneStoreError('conflict', 'revision source archive changed');
2839
3104
  if (!oldSource)
2840
3105
  this.#database.prepare('INSERT INTO source_revision_sources VALUES(?,?)').run(plan.id, sourceJson);
2841
- this.#database.exec('COMMIT');
3106
+ this.#commitMutation();
2842
3107
  }
2843
3108
  catch (error) {
2844
- this.#database.exec('ROLLBACK');
3109
+ this.#rollbackMutation();
2845
3110
  throw error;
2846
3111
  }
2847
3112
  }
@@ -2862,6 +3127,19 @@ export class ControlPlaneStore {
2862
3127
  if (certificate.plan.id !== planId || controlPlaneDigest(certificate) !== row.certificate_digest) {
2863
3128
  throw new ControlPlaneStoreError('invalid-state', 'stored revision verification certificate digest changed');
2864
3129
  }
3130
+ // Check retained immutable metadata without reopening expired build/grant authority.
3131
+ const plan = this.getSourcePlan(planId);
3132
+ if (plan.mode !== 'prepared-revise' || !plan.sourceRevision || !plan.sourceCheck
3133
+ || certificate.plan.digest !== plan.digest || certificate.plan.name !== plan.name
3134
+ || certificate.plan.generatorDigest !== plan.generatorDigest
3135
+ || certificate.plan.sourceTreeDigest !== plan.sourceCheck.treeDigest
3136
+ || certificate.plan.sourcePatchDigest !== plan.sourceCheck.patchDigest
3137
+ || controlPlaneDigest(certificate.parent) !== controlPlaneDigest(plan.sourceRevision.parent)
3138
+ || !plan.preparedEvidence || certificate.plan.artifactSha256 !== plan.preparedEvidence.pack.sha256
3139
+ || certificate.plan.artifactBytes !== plan.preparedEvidence.pack.sizeBytes
3140
+ || !revisionVerificationMatchesPreparedEvidence(certificate, plan.preparedEvidence)) {
3141
+ throw new ControlPlaneStoreError('invalid-state', 'stored revision verification certificate does not match exact prepared source');
3142
+ }
2865
3143
  return certificate;
2866
3144
  }
2867
3145
  getRevisionVerificationStatus(planId) {
@@ -2888,7 +3166,7 @@ export class ControlPlaneStore {
2888
3166
  if (!pinned || this.#now() >= pinned.expiresAt) {
2889
3167
  throw new ControlPlaneStoreError('invalid-state', 'revision verification requires a current pre-author policy binding');
2890
3168
  }
2891
- this.#database.exec('BEGIN IMMEDIATE');
3169
+ this.#beginMutation();
2892
3170
  try {
2893
3171
  const count = this.#database.prepare('SELECT count(*) AS n FROM source_revision_verifications').get();
2894
3172
  if (count.n >= 256)
@@ -2897,10 +3175,10 @@ export class ControlPlaneStore {
2897
3175
  this.#database.prepare(`INSERT INTO source_revision_verifications
2898
3176
  (plan_id,status,certificate_json,certificate_digest,reason,created_at,updated_at) VALUES (?,'claimed',NULL,NULL,NULL,?,?)`)
2899
3177
  .run(planId, now, now);
2900
- this.#database.exec('COMMIT');
3178
+ this.#commitMutation();
2901
3179
  }
2902
3180
  catch (error) {
2903
- this.#database.exec('ROLLBACK');
3181
+ this.#rollbackMutation();
2904
3182
  throw error;
2905
3183
  }
2906
3184
  }
@@ -2926,6 +3204,7 @@ export class ControlPlaneStore {
2926
3204
  const previousJobs = new Set([...(parent?.observations ?? []), ...(candidate?.observations ?? [])].map(item => item.jobId));
2927
3205
  if (certificate.verifiedAt > this.#now() || certificate.expiresAt <= this.#now()
2928
3206
  || !candidate || !parent || !source || !reference || !run?.revisionRegressionAcceptance
3207
+ || !revisionRegressionMatchesCandidate(certificate, candidate, parent)
2929
3208
  || !job.intent.revision?.growthRun.revisionRegressionAcceptance
2930
3209
  || controlPlaneDigest(certificate.plan) !== controlPlaneDigest(candidate.plan)
2931
3210
  || controlPlaneDigest(certificate.source) !== controlPlaneDigest(candidate.source)
@@ -2951,7 +3230,7 @@ export class ControlPlaneStore {
2951
3230
  }
2952
3231
  this.readPreparedSourceArtifact(plan.id);
2953
3232
  const serialized = JSON.stringify(certificate), digest = controlPlaneDigest(certificate);
2954
- this.#database.exec('BEGIN IMMEDIATE');
3233
+ this.#beginMutation();
2955
3234
  try {
2956
3235
  const old = this.#database.prepare('SELECT status,certificate_digest FROM source_revision_regressions WHERE plan_id=?')
2957
3236
  .get(plan.id);
@@ -2961,10 +3240,10 @@ export class ControlPlaneStore {
2961
3240
  else
2962
3241
  this.#database.prepare(`UPDATE source_revision_regressions SET status='verified',certificate_json=?,certificate_digest=?,updated_at=?
2963
3242
  WHERE plan_id=? AND status='claimed'`).run(serialized, digest, this.#now(), plan.id);
2964
- this.#database.exec('COMMIT');
3243
+ this.#commitMutation();
2965
3244
  }
2966
3245
  catch (error) {
2967
- this.#database.exec('ROLLBACK');
3246
+ this.#rollbackMutation();
2968
3247
  throw error;
2969
3248
  }
2970
3249
  }
@@ -2985,6 +3264,10 @@ export class ControlPlaneStore {
2985
3264
  if (certificate.plan.id !== planId || controlPlaneDigest(certificate) !== row.certificate_digest) {
2986
3265
  throw new ControlPlaneStoreError('invalid-state', 'stored revision regression certificate digest changed');
2987
3266
  }
3267
+ const candidate = this.getRevisionVerification(planId), parent = this.getCreationVerification(certificate.parent.planId);
3268
+ if (!candidate || !parent || !revisionRegressionMatchesCandidate(certificate, candidate, parent)) {
3269
+ throw new ControlPlaneStoreError('invalid-state', 'stored revision regression certificate does not match exact candidate execution');
3270
+ }
2988
3271
  return certificate;
2989
3272
  }
2990
3273
  getRevisionRegressionStatus(planId) {
@@ -3011,7 +3294,7 @@ export class ControlPlaneStore {
3011
3294
  if (!pinned || this.#now() >= pinned.expiresAt) {
3012
3295
  throw new ControlPlaneStoreError('invalid-state', 'revision regression requires a current pre-author policy binding');
3013
3296
  }
3014
- this.#database.exec('BEGIN IMMEDIATE');
3297
+ this.#beginMutation();
3015
3298
  try {
3016
3299
  const count = this.#database.prepare('SELECT count(*) AS n FROM source_revision_regressions').get();
3017
3300
  if (count.n >= 256)
@@ -3020,10 +3303,10 @@ export class ControlPlaneStore {
3020
3303
  this.#database.prepare(`INSERT INTO source_revision_regressions
3021
3304
  (plan_id,status,certificate_json,certificate_digest,reason,created_at,updated_at) VALUES (?,'claimed',NULL,NULL,NULL,?,?)`)
3022
3305
  .run(planId, now, now);
3023
- this.#database.exec('COMMIT');
3306
+ this.#commitMutation();
3024
3307
  }
3025
3308
  catch (error) {
3026
- this.#database.exec('ROLLBACK');
3309
+ this.#rollbackMutation();
3027
3310
  throw error;
3028
3311
  }
3029
3312
  }
@@ -3054,7 +3337,7 @@ export class ControlPlaneStore {
3054
3337
  deleteExpiredPreparedSourceArtifacts(now) {
3055
3338
  if (!Number.isSafeInteger(now) || now < 0)
3056
3339
  throw new ControlPlaneStoreError('invalid-input', 'artifact expiry time is invalid');
3057
- this.#database.exec('BEGIN IMMEDIATE');
3340
+ this.#beginMutation();
3058
3341
  try {
3059
3342
  const references = this.#database.prepare('SELECT plan_id, pack_sha256 FROM source_prepared_artifact_refs')
3060
3343
  .all();
@@ -3076,11 +3359,11 @@ export class ControlPlaneStore {
3076
3359
  .run(artifact.pack_sha256);
3077
3360
  deleted += Number(result.changes);
3078
3361
  }
3079
- this.#database.exec('COMMIT');
3362
+ this.#commitMutation();
3080
3363
  return deleted;
3081
3364
  }
3082
3365
  catch (error) {
3083
- this.#database.exec('ROLLBACK');
3366
+ this.#rollbackMutation();
3084
3367
  throw error;
3085
3368
  }
3086
3369
  }
@@ -3341,7 +3624,7 @@ export class ControlPlaneStore {
3341
3624
  throw new ControlPlaneStoreError('invalid-state', 'expired source receipt is missing');
3342
3625
  return receipt.result;
3343
3626
  }
3344
- this.#database.exec('BEGIN IMMEDIATE');
3627
+ this.#beginMutation();
3345
3628
  try {
3346
3629
  const retired = this.#database.prepare(`UPDATE source_plans SET status = 'expired', revision = revision + 1, updated_at = ?
3347
3630
  WHERE id = ? AND revision = ? AND ((mode = 'modify' AND status IN ('pending-approval', 'approved'))
@@ -3358,11 +3641,11 @@ export class ControlPlaneStore {
3358
3641
  const result = this.getSourcePlan(plan.id);
3359
3642
  this.#insertReceipt({ idempotencyKey: `source-expired:${plan.id}`, operation: 'source-expired',
3360
3643
  inputDigest: expiryDigest, result, createdAt: input.now });
3361
- this.#database.exec('COMMIT');
3644
+ this.#commitMutation();
3362
3645
  return result;
3363
3646
  }
3364
3647
  catch (error) {
3365
- this.#database.exec('ROLLBACK');
3648
+ this.#rollbackMutation();
3366
3649
  throw error;
3367
3650
  }
3368
3651
  }
@@ -3420,7 +3703,7 @@ export class ControlPlaneStore {
3420
3703
  throw new ControlPlaneStoreError(verified.decision === 'approved' ? 'expired' : 'invalid-state', 'plan approval is not currently applicable');
3421
3704
  const table = kind === 'activation' ? 'activation_plans' : 'source_plans';
3422
3705
  const now = this.#now();
3423
- this.#database.exec('BEGIN IMMEDIATE');
3706
+ this.#beginMutation();
3424
3707
  try {
3425
3708
  const statement = this.#database.prepare(`UPDATE ${table} SET status = 'approved', revision = revision + 1, approval_json = ?,
3426
3709
  ${kind === 'activation' ? 'approval_receipt_json = ?,' : ''} updated_at = ?
@@ -3433,11 +3716,11 @@ export class ControlPlaneStore {
3433
3716
  const output = kind === 'activation' ? this.getPlan(plan.id) : this.getSourcePlan(plan.id);
3434
3717
  const receipt = { idempotencyKey: input.idempotencyKey, operation: 'approve-plan', inputDigest, result: output, createdAt: now };
3435
3718
  this.#insertReceipt(receipt);
3436
- this.#database.exec('COMMIT');
3719
+ this.#commitMutation();
3437
3720
  return receipt;
3438
3721
  }
3439
3722
  catch (error) {
3440
- this.#database.exec('ROLLBACK');
3723
+ this.#rollbackMutation();
3441
3724
  throw error;
3442
3725
  }
3443
3726
  });
@@ -3448,7 +3731,7 @@ export class ControlPlaneStore {
3448
3731
  if (input.leaseMs < 5_000 || input.leaseMs > 300_000)
3449
3732
  throw new ControlPlaneStoreError('invalid-input', 'leaseMs is invalid');
3450
3733
  this.assertOwnerActivationSource(input.planId);
3451
- this.#database.exec('BEGIN IMMEDIATE');
3734
+ this.#beginMutation();
3452
3735
  try {
3453
3736
  const row = this.#database.prepare('SELECT * FROM activation_plans WHERE id = ?').get(input.planId);
3454
3737
  if (row === undefined)
@@ -3493,12 +3776,12 @@ export class ControlPlaneStore {
3493
3776
  if (Number(result.changes) !== 1)
3494
3777
  throw new ControlPlaneStoreError('conflict', 'activation plan changed while being claimed');
3495
3778
  const claimed = this.getPlan(plan.id);
3496
- this.#database.exec('COMMIT');
3779
+ this.#commitMutation();
3497
3780
  return claimed;
3498
3781
  });
3499
3782
  }
3500
3783
  catch (error) {
3501
- this.#database.exec('ROLLBACK');
3784
+ this.#rollbackMutation();
3502
3785
  throw error;
3503
3786
  }
3504
3787
  }
@@ -3516,7 +3799,7 @@ export class ControlPlaneStore {
3516
3799
  recordActivationTargetBaseline(input) {
3517
3800
  return this.#withActivationSource(input.planId, () => {
3518
3801
  const now = this.#now();
3519
- this.#database.exec('BEGIN IMMEDIATE');
3802
+ this.#beginMutation();
3520
3803
  try {
3521
3804
  this.assertOwnerActivationSource(input.planId);
3522
3805
  const row = this.#database.prepare('SELECT * FROM activation_plans WHERE id = ?').get(input.planId);
@@ -3540,11 +3823,11 @@ export class ControlPlaneStore {
3540
3823
  WHERE id = ? AND revision = ? AND activation_fence = ? AND activation_target_existed IS NULL AND activation_target_baseline_json IS NULL`).run(input.existed ? 1 : 0, baseline, now, plan.id, plan.revision, input.fence);
3541
3824
  }
3542
3825
  const result = this.getPlan(plan.id);
3543
- this.#database.exec('COMMIT');
3826
+ this.#commitMutation();
3544
3827
  return result;
3545
3828
  }
3546
3829
  catch (error) {
3547
- this.#database.exec('ROLLBACK');
3830
+ this.#rollbackMutation();
3548
3831
  throw error;
3549
3832
  }
3550
3833
  });
@@ -3558,7 +3841,7 @@ export class ControlPlaneStore {
3558
3841
  recordActivationInstalledBaseline(input) {
3559
3842
  return this.#withActivationSource(input.planId, () => {
3560
3843
  const now = this.#now();
3561
- this.#database.exec('BEGIN IMMEDIATE');
3844
+ this.#beginMutation();
3562
3845
  try {
3563
3846
  this.assertOwnerActivationSource(input.planId);
3564
3847
  const row = this.#database.prepare('SELECT * FROM activation_plans WHERE id = ?').get(input.planId);
@@ -3599,11 +3882,11 @@ export class ControlPlaneStore {
3599
3882
  successful_order, recorded_at, succeeded_at) VALUES (?, ?, ?, NULL, ?, NULL)`).run(plan.id, baseline, sequence.next_exposure_order, now);
3600
3883
  }
3601
3884
  const output = this.getPlan(plan.id);
3602
- this.#database.exec('COMMIT');
3885
+ this.#commitMutation();
3603
3886
  return output;
3604
3887
  }
3605
3888
  catch (error) {
3606
- this.#database.exec('ROLLBACK');
3889
+ this.#rollbackMutation();
3607
3890
  throw error;
3608
3891
  }
3609
3892
  });
@@ -3626,7 +3909,7 @@ export class ControlPlaneStore {
3626
3909
  /** Move a signed closed deployment watch into the existing physical rollback lifecycle. */
3627
3910
  beginPostActivationRollback(input) {
3628
3911
  const now = this.#now();
3629
- this.#database.exec('BEGIN IMMEDIATE');
3912
+ this.#beginMutation();
3630
3913
  try {
3631
3914
  const row = this.#database.prepare('SELECT * FROM activation_plans WHERE id = ?').get(input.planId);
3632
3915
  if (row === undefined)
@@ -3637,7 +3920,7 @@ export class ControlPlaneStore {
3637
3920
  // A restart or retry must retain the original rollback identity and never
3638
3921
  // manufacture another rollback from a now-closed watch.
3639
3922
  if ((plan.status === 'rollback-pending' || plan.status === 'rolled-back') && this.#hasPostActivationRollbackProvenance(plan)) {
3640
- this.#database.exec('COMMIT');
3923
+ this.#commitMutation();
3641
3924
  return plan;
3642
3925
  }
3643
3926
  if (plan.status !== 'activated' || plan.activation === undefined) {
@@ -3675,11 +3958,11 @@ export class ControlPlaneStore {
3675
3958
  if (Number(updated.changes) !== 1)
3676
3959
  throw new ControlPlaneStoreError('conflict', 'post-activation rollback lost its activation CAS');
3677
3960
  const output = this.getPlan(plan.id);
3678
- this.#database.exec('COMMIT');
3961
+ this.#commitMutation();
3679
3962
  return output;
3680
3963
  }
3681
3964
  catch (error) {
3682
- this.#database.exec('ROLLBACK');
3965
+ this.#rollbackMutation();
3683
3966
  throw error;
3684
3967
  }
3685
3968
  }
@@ -3800,7 +4083,7 @@ export class ControlPlaneStore {
3800
4083
  async withActivationFileSystemGuard(input, action) {
3801
4084
  positiveInteger(input.leaseMs, 'leaseMs');
3802
4085
  try {
3803
- this.#database.exec('BEGIN IMMEDIATE');
4086
+ this.#beginMutation();
3804
4087
  }
3805
4088
  catch {
3806
4089
  throw new ControlPlaneStoreError('conflict', 'activation filesystem mutex is held');
@@ -3825,30 +4108,30 @@ export class ControlPlaneStore {
3825
4108
  assertOwner(false);
3826
4109
  const finished = this.#now();
3827
4110
  this.#database.prepare('UPDATE activation_plans SET activation_lease_until = ?, updated_at = ? WHERE id = ?').run(finished + input.leaseMs, finished, input.planId);
3828
- this.#database.exec('COMMIT');
4111
+ this.#commitMutation();
3829
4112
  return result;
3830
4113
  });
3831
4114
  }
3832
4115
  catch (error) {
3833
- this.#database.exec('ROLLBACK');
4116
+ this.#rollbackMutation();
3834
4117
  throw error;
3835
4118
  }
3836
4119
  }
3837
4120
  /** Serialize lock-file lifecycle operations even after a plan became terminal. */
3838
4121
  async withExclusiveWrite(action) {
3839
4122
  try {
3840
- this.#database.exec('BEGIN IMMEDIATE');
4123
+ this.#beginMutation();
3841
4124
  }
3842
4125
  catch {
3843
4126
  throw new ControlPlaneStoreError('conflict', 'control-plane mutex is held');
3844
4127
  }
3845
4128
  try {
3846
4129
  const result = await action();
3847
- this.#database.exec('COMMIT');
4130
+ this.#commitMutation();
3848
4131
  return result;
3849
4132
  }
3850
4133
  catch (error) {
3851
- this.#database.exec('ROLLBACK');
4134
+ this.#rollbackMutation();
3852
4135
  throw error;
3853
4136
  }
3854
4137
  }
@@ -3987,7 +4270,7 @@ export class ControlPlaneStore {
3987
4270
  ORDER BY epoch.created_at,epoch.operation_id LIMIT 100`).all(coordinatorId, this.#now()).map(runtimeEpochFromRow);
3988
4271
  }
3989
4272
  claimRuntimeEpoch(operationId, coordinatorId) {
3990
- this.#database.exec('BEGIN IMMEDIATE');
4273
+ this.#beginMutation();
3991
4274
  try {
3992
4275
  const record = this.getRuntimeEpoch(operationId);
3993
4276
  if (!['pending', 'claimed'].includes(record.status) || this.#now() >= record.request.requestedAt + record.request.receiptTtlMs) {
@@ -3998,17 +4281,17 @@ export class ControlPlaneStore {
3998
4281
  if (context.handoff.coordinatorId !== coordinatorId || (this.#adoptionCoordinatorId !== undefined && this.#adoptionCoordinatorId !== coordinatorId)) {
3999
4282
  throw new ControlPlaneStoreError('conflict', 'runtime epoch coordinator changed');
4000
4283
  }
4001
- this.#database.exec('COMMIT');
4284
+ this.#commitMutation();
4002
4285
  return { ...record, status: 'claimed' };
4003
4286
  }
4004
4287
  catch (error) {
4005
- this.#database.exec('ROLLBACK');
4288
+ this.#rollbackMutation();
4006
4289
  throw error;
4007
4290
  }
4008
4291
  }
4009
4292
  applyRuntimeEpoch(receiptInput, trust) {
4010
4293
  const receipt = parseRuntimeEpochReceipt(receiptInput);
4011
- this.#database.exec('BEGIN IMMEDIATE');
4294
+ this.#beginMutation();
4012
4295
  try {
4013
4296
  const context = readOwnerRuntimeEpochContext(this.#database, receipt.operationId);
4014
4297
  if (this.#now() >= context.request.requestedAt + context.request.receiptTtlMs)
@@ -4022,18 +4305,18 @@ export class ControlPlaneStore {
4022
4305
  this.#database.prepare("UPDATE deployment_runtime_epochs SET status='applied',receipt_json=? WHERE operation_id=? AND status='claimed'")
4023
4306
  .run(JSON.stringify(receipt), receipt.operationId);
4024
4307
  const result = this.getRuntimeEpoch(receipt.operationId);
4025
- this.#database.exec('COMMIT');
4308
+ this.#commitMutation();
4026
4309
  return result;
4027
4310
  }
4028
4311
  catch (error) {
4029
- this.#database.exec('ROLLBACK');
4312
+ this.#rollbackMutation();
4030
4313
  throw error;
4031
4314
  }
4032
4315
  }
4033
4316
  prepareHostAttestationOperation(input) {
4034
4317
  return this.#withActivationSource(input.planId, () => {
4035
4318
  const now = this.#now();
4036
- this.#database.exec('BEGIN IMMEDIATE');
4319
+ this.#beginMutation();
4037
4320
  try {
4038
4321
  this.assertOwnerActivationSource(input.planId);
4039
4322
  const plan = this.getPlan(input.planId);
@@ -4085,18 +4368,18 @@ export class ControlPlaneStore {
4085
4368
  }
4086
4369
  if (prior.bindingDigest !== bindingDigest)
4087
4370
  throw new ControlPlaneStoreError('conflict', 'durable Host operation payload changed for the same phase');
4088
- this.#database.exec('COMMIT');
4371
+ this.#commitMutation();
4089
4372
  return prior;
4090
4373
  }
4091
4374
  this.#database.prepare(`INSERT INTO host_attestation_operations (plan_id, phase, operation_id, binding_digest,
4092
4375
  request_digest, request_json, status, receipt_digest, receipt_json, created_at, completed_at, applied_at)
4093
4376
  VALUES (?, ?, ?, ?, ?, ?, 'pending', NULL, NULL, ?, NULL, NULL)`).run(plan.id, expected.phase, operationId, bindingDigest, controlPlaneDigest(request), JSON.stringify(request), now);
4094
4377
  const operation = this.getHostAttestationOperation(operationId);
4095
- this.#database.exec('COMMIT');
4378
+ this.#commitMutation();
4096
4379
  return operation;
4097
4380
  }
4098
4381
  catch (error) {
4099
- this.#database.exec('ROLLBACK');
4382
+ this.#rollbackMutation();
4100
4383
  throw error;
4101
4384
  }
4102
4385
  });
@@ -4143,7 +4426,7 @@ export class ControlPlaneStore {
4143
4426
  if (operation.phase === 'rollback' && input.receipt.outcome !== 'passed')
4144
4427
  throw new ControlPlaneStoreError('conflict', 'failed physical rollback receipt cannot settle recovery');
4145
4428
  const commit = () => {
4146
- this.#database.exec('BEGIN IMMEDIATE');
4429
+ this.#beginMutation();
4147
4430
  try {
4148
4431
  if (input.requireCurrentSource)
4149
4432
  this.assertOwnerActivationSource(plan.id);
@@ -4157,7 +4440,7 @@ export class ControlPlaneStore {
4157
4440
  if (controlPlaneDigest(currentOperation.receipt) !== controlPlaneDigest(input.receipt)) {
4158
4441
  throw new ControlPlaneStoreError('conflict', 'Host attestation receipt changed after completion');
4159
4442
  }
4160
- this.#database.exec('COMMIT');
4443
+ this.#commitMutation();
4161
4444
  return currentOperation.receipt;
4162
4445
  }
4163
4446
  const dispatch = this.getHostAttestationDispatchStatus(operation.operationId);
@@ -4172,11 +4455,11 @@ export class ControlPlaneStore {
4172
4455
  if (Number(completed.changes) !== 1)
4173
4456
  throw new ControlPlaneStoreError('conflict', 'Host attestation dispatch completion lost its claim');
4174
4457
  }
4175
- this.#database.exec('COMMIT');
4458
+ this.#commitMutation();
4176
4459
  return input.receipt;
4177
4460
  }
4178
4461
  catch (error) {
4179
- this.#database.exec('ROLLBACK');
4462
+ this.#rollbackMutation();
4180
4463
  throw error;
4181
4464
  }
4182
4465
  };
@@ -4228,7 +4511,7 @@ export class ControlPlaneStore {
4228
4511
  }
4229
4512
  putLiveQualification(batch) {
4230
4513
  assertLiveQualificationBatch(batch);
4231
- this.#database.exec('BEGIN IMMEDIATE');
4514
+ this.#beginMutation();
4232
4515
  try {
4233
4516
  const plan = this.getPlan(batch.planId);
4234
4517
  if (plan.status !== 'awaiting-live-tasks' || this.#now() >= batch.deadlineAt || this.#now() >= batch.expiresAt)
@@ -4242,10 +4525,10 @@ export class ControlPlaneStore {
4242
4525
  this.#database.prepare(`INSERT INTO live_qualification_batches
4243
4526
  (id,lane,plan_id,batch_json,batch_digest,state,created_at) VALUES (?,?,?,?,?,'pending',?)`).run(batch.id, batch.lane, batch.planId, JSON.stringify(batch), controlPlaneDigest(batch), this.#now());
4244
4527
  readLiveQualificationContext(this.#database, batch.id);
4245
- this.#database.exec('COMMIT');
4528
+ this.#commitMutation();
4246
4529
  }
4247
4530
  catch (error) {
4248
- this.#database.exec('ROLLBACK');
4531
+ this.#rollbackMutation();
4249
4532
  throw error;
4250
4533
  }
4251
4534
  }
@@ -4258,13 +4541,13 @@ export class ControlPlaneStore {
4258
4541
  throw new ControlPlaneStoreError('not-found', 'live qualification batch absent');
4259
4542
  const receipt = verifyLiveQualificationReceipt(input.receipt, record.batch, input.publicKeyPem, this.#now());
4260
4543
  return input.withSourceFence(() => {
4261
- this.#database.exec('BEGIN IMMEDIATE');
4544
+ this.#beginMutation();
4262
4545
  try {
4263
4546
  const current = this.getLiveQualification(record.batch.id);
4264
4547
  if (current.state === 'applied') {
4265
4548
  if (controlPlaneDigest(current.receipt) !== controlPlaneDigest(receipt))
4266
4549
  throw new ControlPlaneStoreError('conflict', 'qualification receipt changed');
4267
- this.#database.exec('COMMIT');
4550
+ this.#commitMutation();
4268
4551
  return this.getPlan(record.batch.planId);
4269
4552
  }
4270
4553
  this.assertCurrentLiveQualification(record.batch.id);
@@ -4277,11 +4560,11 @@ export class ControlPlaneStore {
4277
4560
  throw new ControlPlaneStoreError('conflict', 'qualification lost plan CAS');
4278
4561
  this.#database.prepare("UPDATE live_qualification_batches SET state='applied',receipt_json=?,receipt_digest=? WHERE id=? AND state IN ('pending','signed')")
4279
4562
  .run(JSON.stringify(receipt), controlPlaneDigest(receipt), record.batch.id);
4280
- this.#database.exec('COMMIT');
4563
+ this.#commitMutation();
4281
4564
  return this.getPlan(plan.id);
4282
4565
  }
4283
4566
  catch (error) {
4284
- this.#database.exec('ROLLBACK');
4567
+ this.#rollbackMutation();
4285
4568
  throw error;
4286
4569
  }
4287
4570
  });
@@ -4289,7 +4572,7 @@ export class ControlPlaneStore {
4289
4572
  /** Negative-only Host seam. The signed terms permit conservative removal of unconfirmable qualification. */
4290
4573
  invalidateLiveQualification(input) {
4291
4574
  const reason = bounded(input.reason, 'qualification invalidation reason', 160);
4292
- this.#database.exec('BEGIN IMMEDIATE');
4575
+ this.#beginMutation();
4293
4576
  try {
4294
4577
  const plan = this.getPlan(input.planId), record = this.getLiveQualification(input.batchId);
4295
4578
  if (!record || record.batch.planId !== plan.id || record.state !== 'applied' || record.receipt?.disposition !== 'qualified'
@@ -4297,7 +4580,7 @@ export class ControlPlaneStore {
4297
4580
  throw new ControlPlaneStoreError('conflict', 'qualification invalidation lacks exact accepted evidence');
4298
4581
  }
4299
4582
  if (plan.status === 'rollback-pending' || plan.status === 'rolled-back') {
4300
- this.#database.exec('COMMIT');
4583
+ this.#commitMutation();
4301
4584
  return plan;
4302
4585
  }
4303
4586
  if (!['commit-pending', 'activated'].includes(plan.status) || record.batch.fence !== plan.activation.fence)
@@ -4321,11 +4604,11 @@ export class ControlPlaneStore {
4321
4604
  this.#database.prepare(`UPDATE activation_plans SET status='rollback-pending',revision=revision+1,
4322
4605
  activation_lease_until=NULL,rollback_profile_restored=0,failure_code='live-qualification-invalidated',updated_at=?
4323
4606
  WHERE id=? AND revision=? AND activation_fence=?`).run(now, plan.id, plan.revision, plan.activation.fence);
4324
- this.#database.exec('COMMIT');
4607
+ this.#commitMutation();
4325
4608
  return this.getPlan(plan.id);
4326
4609
  }
4327
4610
  catch (error) {
4328
- this.#database.exec('ROLLBACK');
4611
+ this.#rollbackMutation();
4329
4612
  throw error;
4330
4613
  }
4331
4614
  }
@@ -4343,7 +4626,7 @@ export class ControlPlaneStore {
4343
4626
  /** Same synchronous transaction owns current-profile selection and capture. */
4344
4627
  withForegroundDeployment(task, profilePath, callback) {
4345
4628
  assertForegroundTask(task);
4346
- this.#database.exec('BEGIN IMMEDIATE');
4629
+ this.#beginMutation();
4347
4630
  try {
4348
4631
  const active = this.#database.prepare(`SELECT id FROM activation_plans WHERE target_path = ? AND status IN (
4349
4632
  'staging', 'awaiting-reload', 'awaiting-readiness', 'awaiting-live-tasks', 'awaiting-effect-blocked-replay', 'awaiting-shadow',
@@ -4387,11 +4670,11 @@ export class ControlPlaneStore {
4387
4670
  const result = callback(plan, hostOperationFromRow(row));
4388
4671
  if (result && (typeof result === 'object' || typeof result === 'function') && 'then' in result)
4389
4672
  throw new ControlPlaneStoreError('invalid-input', 'foreground capture must be synchronous');
4390
- this.#database.exec('COMMIT');
4673
+ this.#commitMutation();
4391
4674
  return result;
4392
4675
  }
4393
4676
  catch (error) {
4394
- this.#database.exec('ROLLBACK');
4677
+ this.#rollbackMutation();
4395
4678
  throw error;
4396
4679
  }
4397
4680
  finally {
@@ -4458,7 +4741,7 @@ export class ControlPlaneStore {
4458
4741
  }
4459
4742
  putTaskObservation(batch) {
4460
4743
  assertTaskObservationBatch(batch);
4461
- this.#database.exec('BEGIN IMMEDIATE');
4744
+ this.#beginMutation();
4462
4745
  try {
4463
4746
  if (batch.createdAt > this.#now() || batch.expiresAt <= this.#now())
4464
4747
  throw new ControlPlaneStoreError('expired', 'task observation is outside its validity interval');
@@ -4466,22 +4749,39 @@ export class ControlPlaneStore {
4466
4749
  if (prior) {
4467
4750
  if (prior.batch.digest !== batch.digest)
4468
4751
  throw new ControlPlaneStoreError('conflict', 'task observation identity reused');
4469
- this.#database.exec('COMMIT');
4752
+ this.#commitMutation();
4470
4753
  return prior;
4471
4754
  }
4472
4755
  const encoded = JSON.stringify(batch);
4473
4756
  this.#database.prepare('INSERT INTO task_observation_batches (id,lane,plan_id,batch_json,batch_digest,state,created_at) VALUES (?,?,?,?,?,?,?)').run(batch.id, batch.lane, batch.planId, encoded, controlPlaneDigest(batch), 'pending', batch.createdAt);
4474
4757
  readTaskObservationContext(this.#database, batch.id);
4475
- this.#database.exec('COMMIT');
4758
+ this.#commitMutation();
4476
4759
  return Object.freeze({ batch, state: 'pending' });
4477
4760
  }
4478
4761
  catch (error) {
4479
- this.#database.exec('ROLLBACK');
4762
+ this.#rollbackMutation();
4480
4763
  throw error;
4481
4764
  }
4482
4765
  }
4483
4766
  getTaskObservation(id) { return getTaskObservationRecord(this.#database, id); }
4484
4767
  assertCurrentTaskObservation(id) { readTaskObservationContext(this.#database, id); }
4768
+ /** Seal one observational trend result into the append-only snapshot ledger; same trend, same row. */
4769
+ sealCreatedCapabilityTrendSnapshot(trend) {
4770
+ const snapshot = createCreationCapabilityTrendSnapshot(trend);
4771
+ this.#beginMutation();
4772
+ try {
4773
+ const result = sealCreationCapabilityTrendSnapshot(this.#database, snapshot, this.#now());
4774
+ this.#commitMutation();
4775
+ return result;
4776
+ }
4777
+ catch (error) {
4778
+ this.#rollbackMutation();
4779
+ throw error;
4780
+ }
4781
+ }
4782
+ listCreatedCapabilityTrendSnapshots(query = {}) {
4783
+ return listCreationCapabilityTrendSnapshots(this.#database, query);
4784
+ }
4485
4785
  listTaskObservations(lane, limit = 100) {
4486
4786
  if (!KEY.test(lane) || !Number.isSafeInteger(limit) || limit < 1 || limit > 1000)
4487
4787
  throw new ControlPlaneStoreError('invalid-input', 'task observation list is invalid');
@@ -4640,7 +4940,7 @@ export class ControlPlaneStore {
4640
4940
  const claim = () => {
4641
4941
  const candidate = this.getHostAttestationOperation(input.operationId);
4642
4942
  return this.#withActivationSource(candidate.planId, () => {
4643
- this.#database.exec('BEGIN IMMEDIATE');
4943
+ this.#beginMutation();
4644
4944
  try {
4645
4945
  this.assertOwnerActivationSource(candidate.planId);
4646
4946
  const operation = this.getHostAttestationOperation(input.operationId), plan = this.getPlan(operation.planId), expected = expectedAttestation[plan.status];
@@ -4648,17 +4948,17 @@ export class ControlPlaneStore {
4648
4948
  throw new ControlPlaneStoreError('conflict', 'Host attestation operation lost its plan revision/fence/phase');
4649
4949
  const request = this.#assertHostAttestationChain(operation, plan);
4650
4950
  if (operation.receipt) {
4651
- this.#database.exec('COMMIT');
4951
+ this.#commitMutation();
4652
4952
  return { operation, plan, request };
4653
4953
  }
4654
4954
  if (this.getHostAttestationDispatchStatus(operation.operationId) !== undefined)
4655
4955
  throw new ControlPlaneStoreError('conflict', 'Host attestation outcome is unknown; receipt reconciliation is required');
4656
4956
  this.#database.prepare("INSERT INTO host_attestation_dispatches (operation_id,status,claimed_at,completed_at) VALUES (?,'claimed',?,NULL)").run(operation.operationId, this.#now());
4657
- this.#database.exec('COMMIT');
4957
+ this.#commitMutation();
4658
4958
  return { operation, plan, request };
4659
4959
  }
4660
4960
  catch (error) {
4661
- this.#database.exec('ROLLBACK');
4961
+ this.#rollbackMutation();
4662
4962
  throw error;
4663
4963
  }
4664
4964
  });
@@ -4725,7 +5025,7 @@ export class ControlPlaneStore {
4725
5025
  this.#assertAdoptionHandoff(input.planId);
4726
5026
  }
4727
5027
  const now = this.#now();
4728
- this.#database.exec('BEGIN IMMEDIATE');
5028
+ this.#beginMutation();
4729
5029
  try {
4730
5030
  this.#withActivationSource(input.planId, () => { }, input.to === 'rollback-pending' || input.to === 'rolled-back');
4731
5031
  const result = this.#database.prepare(`UPDATE activation_plans SET status = ?, revision = revision + 1,
@@ -4744,11 +5044,11 @@ export class ControlPlaneStore {
4744
5044
  this.#database.prepare(`UPDATE capability_gaps SET status = 'closed', revision = revision + 1, updated_at = ? WHERE id = ?`).run(now, plan.gapId);
4745
5045
  this.#finishActivation(plan, input.fence, now);
4746
5046
  }
4747
- this.#database.exec('COMMIT');
5047
+ this.#commitMutation();
4748
5048
  return plan;
4749
5049
  }
4750
5050
  catch (error) {
4751
- this.#database.exec('ROLLBACK');
5051
+ this.#rollbackMutation();
4752
5052
  throw error;
4753
5053
  }
4754
5054
  }, input.to === 'rollback-pending' || input.to === 'rolled-back');
@@ -4791,7 +5091,7 @@ export class ControlPlaneStore {
4791
5091
  const now = this.#now();
4792
5092
  const nextStatus = verified.outcome === 'passed' ? (verified.phase === 'readiness' && plan.dossier.liveQualification ? 'awaiting-live-tasks' : expected.next) : 'rollback-pending';
4793
5093
  return this.#withActivationSource(input.planId, () => {
4794
- this.#database.exec('BEGIN IMMEDIATE');
5094
+ this.#beginMutation();
4795
5095
  try {
4796
5096
  this.#withActivationSource(input.planId, () => { }, recovery);
4797
5097
  const lockedOperation = this.getHostAttestationOperation(input.receipt.operationId);
@@ -4822,11 +5122,11 @@ export class ControlPlaneStore {
4822
5122
  this.#finishActivation(output, input.expectedFence, now);
4823
5123
  const operationReceipt = { idempotencyKey: input.idempotencyKey, operation: 'host-attestation', inputDigest, result: output, createdAt: now };
4824
5124
  this.#insertReceipt(operationReceipt);
4825
- this.#database.exec('COMMIT');
5125
+ this.#commitMutation();
4826
5126
  return operationReceipt;
4827
5127
  }
4828
5128
  catch (error) {
4829
- this.#database.exec('ROLLBACK');
5129
+ this.#rollbackMutation();
4830
5130
  throw error;
4831
5131
  }
4832
5132
  }, recovery);
@@ -4884,7 +5184,7 @@ export class ControlPlaneStore {
4884
5184
  }
4885
5185
  }
4886
5186
  const commit = () => {
4887
- this.#database.exec('BEGIN IMMEDIATE');
5187
+ this.#beginMutation();
4888
5188
  try {
4889
5189
  if (input.taskObservationId !== undefined) {
4890
5190
  const context = readTaskObservationContext(this.#database, input.taskObservationId);
@@ -4922,11 +5222,11 @@ export class ControlPlaneStore {
4922
5222
  }
4923
5223
  const operationReceipt = { idempotencyKey: key, operation: 'post-activation-observation', inputDigest, result: output, createdAt: now };
4924
5224
  this.#insertReceipt(operationReceipt);
4925
- this.#database.exec('COMMIT');
5225
+ this.#commitMutation();
4926
5226
  return operationReceipt;
4927
5227
  }
4928
5228
  catch (error) {
4929
- this.#database.exec('ROLLBACK');
5229
+ this.#rollbackMutation();
4930
5230
  throw error;
4931
5231
  }
4932
5232
  };
@@ -4953,7 +5253,7 @@ export class ControlPlaneStore {
4953
5253
  }
4954
5254
  const verified = await input.resolveAuthority(input.receipt).verify(input.receipt, plan, watch.exact);
4955
5255
  const now = this.#now();
4956
- this.#database.exec('BEGIN IMMEDIATE');
5256
+ this.#beginMutation();
4957
5257
  try {
4958
5258
  const current = this.#database.prepare('SELECT * FROM activation_watch WHERE plan_id = ?').get(plan.id);
4959
5259
  if (current.state === 'closed-retracted')
@@ -4970,11 +5270,11 @@ export class ControlPlaneStore {
4970
5270
  const output = this.getActivationWatch(plan.id);
4971
5271
  const operationReceipt = { idempotencyKey: key, operation: 'activation-retraction', inputDigest, result: output, createdAt: now };
4972
5272
  this.#insertReceipt(operationReceipt);
4973
- this.#database.exec('COMMIT');
5273
+ this.#commitMutation();
4974
5274
  return operationReceipt;
4975
5275
  }
4976
5276
  catch (error) {
4977
- this.#database.exec('ROLLBACK');
5277
+ this.#rollbackMutation();
4978
5278
  throw error;
4979
5279
  }
4980
5280
  }
@@ -5060,7 +5360,7 @@ export class ControlPlaneStore {
5060
5360
  if (!input.succeeded && (input.checkedTreeDigest !== undefined || input.checkedPatchDigest !== undefined)) {
5061
5361
  throw new ControlPlaneStoreError('invalid-input', 'failed source checks cannot carry successful check evidence');
5062
5362
  }
5063
- this.#database.exec('BEGIN IMMEDIATE');
5363
+ this.#beginMutation();
5064
5364
  try {
5065
5365
  const result = this.#database.prepare(`UPDATE source_plans SET status = ?, revision = revision + 1,
5066
5366
  checked_tree_digest = ?, checked_patch_digest = ?, checked_at = ?, updated_at = ?
@@ -5074,11 +5374,11 @@ export class ControlPlaneStore {
5074
5374
  }
5075
5375
  const digest = controlPlaneDigest({ planId: output.id, planDigest: output.digest, status, sourceCheck: output.sourceCheck });
5076
5376
  this.#insertReceipt({ idempotencyKey: `source-checks:${output.id}`, operation: 'source-local-checks', inputDigest: digest, result: output, createdAt: now });
5077
- this.#database.exec('COMMIT');
5377
+ this.#commitMutation();
5078
5378
  return output;
5079
5379
  }
5080
5380
  catch (error) {
5081
- this.#database.exec('ROLLBACK');
5381
+ this.#rollbackMutation();
5082
5382
  throw error;
5083
5383
  }
5084
5384
  }
@@ -5111,7 +5411,7 @@ export class ControlPlaneStore {
5111
5411
  }
5112
5412
  const commit = () => {
5113
5413
  this.#assertOwnerTaskFailureGapAdmission(plan.gapId);
5114
- this.#database.exec('BEGIN IMMEDIATE');
5414
+ this.#beginMutation();
5115
5415
  try {
5116
5416
  const result = this.#database.prepare(`UPDATE source_plans SET status = 'ready-for-human-review', revision = revision + 1, updated_at = ?
5117
5417
  WHERE id = ? AND revision = ? AND status = 'approved' AND mode = 'modify'
@@ -5124,11 +5424,11 @@ export class ControlPlaneStore {
5124
5424
  const receipt = { idempotencyKey: `source-verify-prepared:${output.id}:${output.revision}`,
5125
5425
  operation: 'source-verify-prepared', inputDigest, result: output, createdAt: now };
5126
5426
  this.#insertReceipt(receipt);
5127
- this.#database.exec('COMMIT');
5427
+ this.#commitMutation();
5128
5428
  return receipt;
5129
5429
  }
5130
5430
  catch (error) {
5131
- this.#database.exec('ROLLBACK');
5431
+ this.#rollbackMutation();
5132
5432
  throw error;
5133
5433
  }
5134
5434
  };
@@ -5188,7 +5488,7 @@ export class ControlPlaneStore {
5188
5488
  lineageDigest = controlPlaneDigest({ history: prehistory, maintenance: premaintenance, trust: input.trust, baseline: prebaseline });
5189
5489
  }
5190
5490
  return withCurrentSource(plan.gapId, () => {
5191
- this.#database.exec('BEGIN IMMEDIATE');
5491
+ this.#beginMutation();
5192
5492
  try {
5193
5493
  const linkedRows = this.#database.prepare('SELECT * FROM source_jobs WHERE plan_id = ? LIMIT 2')
5194
5494
  .all(plan.id);
@@ -5243,11 +5543,11 @@ export class ControlPlaneStore {
5243
5543
  const output = this.getSourcePlan(plan.id);
5244
5544
  const operationReceipt = { idempotencyKey: key, operation: 'start-source-release', inputDigest, result: output, createdAt: now };
5245
5545
  this.#insertReceipt(operationReceipt);
5246
- this.#database.exec('COMMIT');
5546
+ this.#commitMutation();
5247
5547
  return operationReceipt;
5248
5548
  }
5249
5549
  catch (error) {
5250
- this.#database.exec('ROLLBACK');
5550
+ this.#rollbackMutation();
5251
5551
  throw error;
5252
5552
  }
5253
5553
  });
@@ -5271,7 +5571,7 @@ export class ControlPlaneStore {
5271
5571
  await reverifySourceReleaseAuthorization(plan.releaseAuthorization, plan, input.resolveAuthorizationAuthority);
5272
5572
  return withCurrentSource(plan.gapId, () => {
5273
5573
  const now = this.#now();
5274
- this.#database.exec('BEGIN IMMEDIATE');
5574
+ this.#beginMutation();
5275
5575
  try {
5276
5576
  const current = this.getSourcePlan(plan.id);
5277
5577
  const currentExpected = expectedSourceRelease(current.status);
@@ -5291,7 +5591,7 @@ export class ControlPlaneStore {
5291
5591
  if (prior.bindingDigest !== controlPlaneDigest(releaseRequestBinding(expectedRequest))) {
5292
5592
  throw new ControlPlaneStoreError('conflict', 'durable release operation payload changed for the same phase/fence');
5293
5593
  }
5294
- this.#database.exec('COMMIT');
5594
+ this.#commitMutation();
5295
5595
  return prior;
5296
5596
  }
5297
5597
  const operationId = `release-operation-${randomUUID()}`;
@@ -5301,11 +5601,11 @@ export class ControlPlaneStore {
5301
5601
  operation_id, binding_digest, request_digest, request_json, status, receipt_digest, receipt_json, created_at, completed_at, applied_at)
5302
5602
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', NULL, NULL, ?, NULL, NULL)`).run(current.id, expected.phase, current.release.id, current.release.fence, attempt, operationId, bindingDigest, controlPlaneDigest(request), JSON.stringify(request), now);
5303
5603
  const operation = this.getSourceReleaseOperation(operationId);
5304
- this.#database.exec('COMMIT');
5604
+ this.#commitMutation();
5305
5605
  return operation;
5306
5606
  }
5307
5607
  catch (error) {
5308
- this.#database.exec('ROLLBACK');
5608
+ this.#rollbackMutation();
5309
5609
  throw error;
5310
5610
  }
5311
5611
  });
@@ -5370,13 +5670,13 @@ export class ControlPlaneStore {
5370
5670
  withCurrentSource(plan.gapId, () => { });
5371
5671
  await reverifySourceReleaseAuthorization(operation.request.authorization, plan, input.resolveAuthorizationAuthority);
5372
5672
  const claimed = withCurrentSource(plan.gapId, () => {
5373
- this.#database.exec('BEGIN IMMEDIATE');
5673
+ this.#beginMutation();
5374
5674
  try {
5375
5675
  const current = this.getSourcePlan(plan.id);
5376
5676
  const currentOperation = this.getSourceReleaseOperation(operation.operationId);
5377
5677
  assertCurrent(current, currentOperation);
5378
5678
  if (currentOperation.receipt !== undefined) {
5379
- this.#database.exec('COMMIT');
5679
+ this.#commitMutation();
5380
5680
  return false;
5381
5681
  }
5382
5682
  const dispatch = this.#database.prepare('SELECT * FROM source_release_dispatches WHERE operation_id = ?').get(operation.operationId);
@@ -5386,11 +5686,11 @@ export class ControlPlaneStore {
5386
5686
  }
5387
5687
  this.#database.prepare(`INSERT INTO source_release_dispatches (operation_id, status, claimed_at, completed_at)
5388
5688
  VALUES (?, 'claimed', ?, NULL)`).run(operation.operationId, this.#now());
5389
- this.#database.exec('COMMIT');
5689
+ this.#commitMutation();
5390
5690
  return true;
5391
5691
  }
5392
5692
  catch (error) {
5393
- this.#database.exec('ROLLBACK');
5693
+ this.#rollbackMutation();
5394
5694
  throw error;
5395
5695
  }
5396
5696
  });
@@ -5399,7 +5699,7 @@ export class ControlPlaneStore {
5399
5699
  const receipt = await input.execute(operation.request);
5400
5700
  await input.resolveAuthority(receipt).verify(receipt, plan, operation.request);
5401
5701
  return withCurrentSource(plan.gapId, () => {
5402
- this.#database.exec('BEGIN IMMEDIATE');
5702
+ this.#beginMutation();
5403
5703
  try {
5404
5704
  const current = this.getSourcePlan(plan.id);
5405
5705
  const currentOperation = this.getSourceReleaseOperation(operation.operationId);
@@ -5417,11 +5717,11 @@ export class ControlPlaneStore {
5417
5717
  WHERE operation_id = ? AND status = 'claimed'`).run(now, operation.operationId);
5418
5718
  if (Number(completed.changes) !== 1)
5419
5719
  throw new ControlPlaneStoreError('conflict', 'source release completion lost its dispatch claim');
5420
- this.#database.exec('COMMIT');
5720
+ this.#commitMutation();
5421
5721
  return receipt;
5422
5722
  }
5423
5723
  catch (error) {
5424
- this.#database.exec('ROLLBACK');
5724
+ this.#rollbackMutation();
5425
5725
  throw error;
5426
5726
  }
5427
5727
  });
@@ -5455,14 +5755,14 @@ export class ControlPlaneStore {
5455
5755
  await reverifySourceReleaseAuthorization(operation.request.authorization, plan, input.resolveAuthorizationAuthority);
5456
5756
  await input.resolveAuthority(input.receipt).verify(input.receipt, plan, operation.request);
5457
5757
  return withCurrentSource(plan.gapId, () => {
5458
- this.#database.exec('BEGIN IMMEDIATE');
5758
+ this.#beginMutation();
5459
5759
  try {
5460
5760
  const current = this.getSourcePlan(plan.id), currentOperation = this.getSourceReleaseOperation(operation.operationId);
5461
5761
  assertCurrent(current, currentOperation);
5462
5762
  if (currentOperation.receipt !== undefined) {
5463
5763
  if (controlPlaneDigest(currentOperation.receipt) !== controlPlaneDigest(input.receipt))
5464
5764
  throw new ControlPlaneStoreError('conflict', 'source release receipt differs from completed operation');
5465
- this.#database.exec('COMMIT');
5765
+ this.#commitMutation();
5466
5766
  return currentOperation.receipt;
5467
5767
  }
5468
5768
  const dispatch = this.#database.prepare('SELECT * FROM source_release_dispatches WHERE operation_id = ?').get(operation.operationId);
@@ -5479,11 +5779,11 @@ export class ControlPlaneStore {
5479
5779
  if (Number(completed.changes) !== 1)
5480
5780
  throw new ControlPlaneStoreError('conflict', 'source release receipt lost its dispatch claim');
5481
5781
  }
5482
- this.#database.exec('COMMIT');
5782
+ this.#commitMutation();
5483
5783
  return input.receipt;
5484
5784
  }
5485
5785
  catch (error) {
5486
- this.#database.exec('ROLLBACK');
5786
+ this.#rollbackMutation();
5487
5787
  throw error;
5488
5788
  }
5489
5789
  });
@@ -5549,7 +5849,7 @@ export class ControlPlaneStore {
5549
5849
  }
5550
5850
  const now = this.#now();
5551
5851
  return withCurrentSource(plan.gapId, () => {
5552
- this.#database.exec('BEGIN IMMEDIATE');
5852
+ this.#beginMutation();
5553
5853
  try {
5554
5854
  const current = this.getSourcePlan(plan.id);
5555
5855
  const currentExpected = expectedSourceRelease(current.status);
@@ -5589,18 +5889,18 @@ export class ControlPlaneStore {
5589
5889
  }
5590
5890
  const operationReceipt = { idempotencyKey: key, operation: 'source-release', inputDigest, result: output, createdAt: now };
5591
5891
  this.#insertReceipt(operationReceipt);
5592
- this.#database.exec('COMMIT');
5892
+ this.#commitMutation();
5593
5893
  return operationReceipt;
5594
5894
  }
5595
5895
  catch (error) {
5596
- this.#database.exec('ROLLBACK');
5896
+ this.#rollbackMutation();
5597
5897
  throw error;
5598
5898
  }
5599
5899
  });
5600
5900
  }
5601
5901
  async prepareSourcePublishReconciliation(input) {
5602
5902
  const now = this.#now();
5603
- this.#database.exec('BEGIN IMMEDIATE');
5903
+ this.#beginMutation();
5604
5904
  try {
5605
5905
  const plan = this.getSourcePlan(input.planId);
5606
5906
  if (plan.status !== 'publish-ambiguous' || plan.revision !== input.expectedRevision || plan.release?.fence !== input.expectedFence) {
@@ -5643,18 +5943,18 @@ export class ControlPlaneStore {
5643
5943
  const prior = sourcePublishReconciliationFromRow(priorRow);
5644
5944
  if (prior.bindingDigest !== bindingDigest)
5645
5945
  throw new ControlPlaneStoreError('conflict', 'durable publish reconciliation payload changed');
5646
- this.#database.exec('COMMIT');
5946
+ this.#commitMutation();
5647
5947
  return prior;
5648
5948
  }
5649
5949
  this.#database.prepare(`INSERT INTO source_publish_reconciliations (plan_id, release_id, release_fence, attempt, operation_id,
5650
5950
  binding_digest, request_digest, request_json, status, receipt_digest, receipt_json, created_at, completed_at, applied_at)
5651
5951
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', NULL, NULL, ?, NULL, NULL)`).run(plan.id, plan.release.id, plan.release.fence, attempt, operationId, bindingDigest, controlPlaneDigest(parsed), JSON.stringify(parsed), now);
5652
5952
  const operation = this.getSourcePublishReconciliationOperation(operationId);
5653
- this.#database.exec('COMMIT');
5953
+ this.#commitMutation();
5654
5954
  return operation;
5655
5955
  }
5656
5956
  catch (error) {
5657
- this.#database.exec('ROLLBACK');
5957
+ this.#rollbackMutation();
5658
5958
  throw error;
5659
5959
  }
5660
5960
  }
@@ -5666,7 +5966,7 @@ export class ControlPlaneStore {
5666
5966
  }
5667
5967
  async runSourcePublishReconciliation(input) {
5668
5968
  try {
5669
- this.#database.exec('BEGIN IMMEDIATE');
5969
+ this.#beginMutation();
5670
5970
  }
5671
5971
  catch {
5672
5972
  throw new ControlPlaneStoreError('conflict', 'publish reconciliation single-flight is held');
@@ -5678,7 +5978,7 @@ export class ControlPlaneStore {
5678
5978
  || operation.fence !== input.expectedFence)
5679
5979
  throw new ControlPlaneStoreError('conflict', 'publish reconciliation lost its revision/fence');
5680
5980
  if (operation.receipt !== undefined) {
5681
- this.#database.exec('COMMIT');
5981
+ this.#commitMutation();
5682
5982
  return operation.receipt;
5683
5983
  }
5684
5984
  await reverifySourceReleaseAuthorization(operation.request.authorization, plan, input.resolveAuthorizationAuthority);
@@ -5689,11 +5989,11 @@ export class ControlPlaneStore {
5689
5989
  receipt_digest = ?, receipt_json = ?, completed_at = ? WHERE operation_id = ? AND status = 'pending' AND request_digest = ?`).run(controlPlaneDigest(receipt), JSON.stringify(receipt), now, operation.operationId, operation.requestDigest);
5690
5990
  if (Number(result.changes) !== 1)
5691
5991
  throw new ControlPlaneStoreError('conflict', 'publish reconciliation completion lost single-flight');
5692
- this.#database.exec('COMMIT');
5992
+ this.#commitMutation();
5693
5993
  return receipt;
5694
5994
  }
5695
5995
  catch (error) {
5696
- this.#database.exec('ROLLBACK');
5996
+ this.#rollbackMutation();
5697
5997
  throw error;
5698
5998
  }
5699
5999
  }
@@ -5726,7 +6026,7 @@ export class ControlPlaneStore {
5726
6026
  const verified = await input.resolveAuthority(input.receipt).verify(input.receipt, plan, operation.request);
5727
6027
  const outcome = verified.evidence.outcome;
5728
6028
  const now = this.#now();
5729
- this.#database.exec('BEGIN IMMEDIATE');
6029
+ this.#beginMutation();
5730
6030
  try {
5731
6031
  let nextStatus = 'publish-ambiguous';
5732
6032
  let nextFence = input.expectedFence;
@@ -5759,11 +6059,11 @@ export class ControlPlaneStore {
5759
6059
  const output = this.getSourcePlan(plan.id);
5760
6060
  const operationReceipt = { idempotencyKey: input.idempotencyKey, operation: 'reconcile-source-publish', inputDigest, result: output, createdAt: now };
5761
6061
  this.#insertReceipt(operationReceipt);
5762
- this.#database.exec('COMMIT');
6062
+ this.#commitMutation();
5763
6063
  return operationReceipt;
5764
6064
  }
5765
6065
  catch (error) {
5766
- this.#database.exec('ROLLBACK');
6066
+ this.#rollbackMutation();
5767
6067
  throw error;
5768
6068
  }
5769
6069
  }