@dsh-enhanced/plugin-control-plane 0.1.64 → 0.1.66

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 (121) hide show
  1. package/README.md +81 -5
  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 +9 -0
  10. package/lib/creation-capability-execution.d.ts.map +1 -0
  11. package/lib/creation-capability-execution.js +70 -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 +18 -0
  26. package/lib/creation-capability-runtime.d.ts.map +1 -1
  27. package/lib/creation-capability-runtime.js +76 -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 +12 -4
  48. package/lib/index.d.ts.map +1 -1
  49. package/lib/index.js +8 -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/scanner-owner-runtime.d.ts +34 -0
  70. package/lib/scanner-owner-runtime.d.ts.map +1 -0
  71. package/lib/scanner-owner-runtime.js +99 -0
  72. package/lib/scanner-owner-runtime.js.map +1 -0
  73. package/lib/service.d.ts +82 -5
  74. package/lib/service.d.ts.map +1 -1
  75. package/lib/service.js +450 -45
  76. package/lib/service.js.map +1 -1
  77. package/lib/source-approval-authority.d.ts.map +1 -1
  78. package/lib/source-approval-authority.js +25 -7
  79. package/lib/source-approval-authority.js.map +1 -1
  80. package/lib/source-build-diagnostics.d.ts +31 -0
  81. package/lib/source-build-diagnostics.d.ts.map +1 -0
  82. package/lib/source-build-diagnostics.js +110 -0
  83. package/lib/source-build-diagnostics.js.map +1 -0
  84. package/lib/source-build.d.ts +25 -2
  85. package/lib/source-build.d.ts.map +1 -1
  86. package/lib/source-build.js +145 -26
  87. package/lib/source-build.js.map +1 -1
  88. package/lib/source-creation.d.ts +6 -2
  89. package/lib/source-creation.d.ts.map +1 -1
  90. package/lib/source-creation.js +11 -2
  91. package/lib/source-creation.js.map +1 -1
  92. package/lib/source-job-types.d.ts +3 -1
  93. package/lib/source-job-types.d.ts.map +1 -1
  94. package/lib/source-jobs.d.ts +28 -1
  95. package/lib/source-jobs.d.ts.map +1 -1
  96. package/lib/source-jobs.js +117 -26
  97. package/lib/source-jobs.js.map +1 -1
  98. package/lib/sqlite.d.ts +1 -1
  99. package/lib/sqlite.d.ts.map +1 -1
  100. package/lib/sqlite.js +36 -1
  101. package/lib/sqlite.js.map +1 -1
  102. package/lib/store.d.ts +21 -1
  103. package/lib/store.d.ts.map +1 -1
  104. package/lib/store.js +470 -174
  105. package/lib/store.js.map +1 -1
  106. package/lib/systemd-host-authority.d.ts.map +1 -1
  107. package/lib/systemd-host-authority.js +4 -0
  108. package/lib/systemd-host-authority.js.map +1 -1
  109. package/lib/task-observation-runtime.d.ts +6 -2
  110. package/lib/task-observation-runtime.d.ts.map +1 -1
  111. package/lib/task-observation-runtime.js +69 -11
  112. package/lib/task-observation-runtime.js.map +1 -1
  113. package/lib/types.d.ts +47 -1
  114. package/lib/types.d.ts.map +1 -1
  115. package/lib/version.d.ts +1 -1
  116. package/lib/version.js +1 -1
  117. package/lib/work-item-creation-contract.d.ts +17 -0
  118. package/lib/work-item-creation-contract.d.ts.map +1 -0
  119. package/lib/work-item-creation-contract.js +61 -0
  120. package/lib/work-item-creation-contract.js.map +1 -0
  121. 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
  }
@@ -2411,9 +2663,9 @@ export class ControlPlaneStore {
2411
2663
  throw new ControlPlaneStoreError('invalid-input', 'modify source plan prepared digests are invalid');
2412
2664
  }
2413
2665
  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');
2666
+ if ((mode === 'modify' && preparedEvidence.schemaVersion === 2)
2667
+ || (preparedEvidence.schemaVersion !== 1 && input.sourceJob === undefined)) {
2668
+ throw new ControlPlaneStoreError('invalid-input', 'native source evidence requires a matching durable source job');
2417
2669
  }
2418
2670
  }
2419
2671
  else if (input.prepared !== undefined) {
@@ -2482,7 +2734,7 @@ export class ControlPlaneStore {
2482
2734
  ? { ...immutable, mode, ...(creation === undefined ? {} : { creation }), ...(revision === undefined ? {} : { sourceRevision: revision }), sourceCheck: { treeDigest: input.prepared.treeDigest, patchDigest: input.prepared.patchDigest,
2483
2735
  checkedAt: input.prepared.checkedAt }, preparedEvidence }
2484
2736
  : immutable);
2485
- this.#database.exec('BEGIN IMMEDIATE');
2737
+ this.#beginMutation();
2486
2738
  try {
2487
2739
  const currentGap = this.getGap(gap.id);
2488
2740
  if (currentGap.revision !== gap.revision || currentGap.inputDigest !== gap.inputDigest || currentGap.status !== 'open'
@@ -2497,7 +2749,7 @@ export class ControlPlaneStore {
2497
2749
  || sourceJob.intent.baseCommit !== input.baseCommit || sourceJob.intent.name !== name
2498
2750
  || sourceJob.intent.repository !== input.repository || sourceJob.intent.worktree !== input.worktree || sourceJob.intent.ttlMs !== input.ttlMs
2499
2751
  || (mode === 'prepared-create') !== (sourceJob.intent.mode === 'create')
2500
- || (preparedEvidence !== undefined && ('backend' in sourceJob.intent.build) !== (preparedEvidence.schemaVersion === 2))
2752
+ || (preparedEvidence !== undefined && preparedEvidence.schemaVersion !== sourceBuildEvidenceSchema(sourceJob.intent.build))
2501
2753
  || (mode === 'prepared-create' && controlPlaneDigest(sourceJob.intent.creation) !== controlPlaneDigest(creation))
2502
2754
  || (mode === 'prepared-revise') !== (sourceJob.intent.mode === 'revise-created')
2503
2755
  || (mode === 'prepared-revise' && controlPlaneDigest(sourceJob.intent.revision) !== controlPlaneDigest(revision))) {
@@ -2560,11 +2812,11 @@ export class ControlPlaneStore {
2560
2812
  const plan = this.getSourcePlan(id);
2561
2813
  const receipt = { idempotencyKey: key, operation: 'create-source-plan', inputDigest, result: plan, createdAt: now };
2562
2814
  this.#insertReceipt(receipt);
2563
- this.#database.exec('COMMIT');
2815
+ this.#commitMutation();
2564
2816
  return receipt;
2565
2817
  }
2566
2818
  catch (error) {
2567
- this.#database.exec('ROLLBACK');
2819
+ this.#rollbackMutation();
2568
2820
  throw error;
2569
2821
  }
2570
2822
  }
@@ -2614,6 +2866,7 @@ export class ControlPlaneStore {
2614
2866
  || job.intent.creation.generatorDigest !== plan.generatorDigest
2615
2867
  || job.intent.repository !== plan.repository || job.intent.worktree !== plan.worktree
2616
2868
  || job.intent.baseCommit !== plan.baseCommit || job.intent.name !== plan.name
2869
+ || plan.preparedEvidence.schemaVersion !== sourceBuildEvidenceSchema(job.intent.build)
2617
2870
  || plan.expiresAt !== plan.createdAt + job.intent.ttlMs) {
2618
2871
  throw new ControlPlaneStoreError('invalid-state', 'prepared creation source job binding is corrupt');
2619
2872
  }
@@ -2659,6 +2912,7 @@ export class ControlPlaneStore {
2659
2912
  || job.intent.revision.generatorDigest !== plan.generatorDigest
2660
2913
  || job.intent.repository !== plan.repository || job.intent.worktree !== plan.worktree
2661
2914
  || job.intent.baseCommit !== plan.baseCommit || job.intent.name !== plan.name
2915
+ || plan.preparedEvidence.schemaVersion !== sourceBuildEvidenceSchema(job.intent.build)
2662
2916
  || plan.expiresAt !== plan.createdAt + job.intent.ttlMs) {
2663
2917
  throw new ControlPlaneStoreError('invalid-state', 'prepared revision source job binding is corrupt');
2664
2918
  }
@@ -2693,6 +2947,7 @@ export class ControlPlaneStore {
2693
2947
  const job = this.getPreparedSourceJob(plan.id);
2694
2948
  const reference = this.getOwnerTaskFailureReference(plan.gapId);
2695
2949
  if (!reference || !plan.sourceCheck || !plan.preparedEvidence || !plan.creation?.growthRun
2950
+ || !creationVerificationMatchesPreparedEvidence(certificate, plan.preparedEvidence)
2696
2951
  || !job.intent.creation?.growthRun || plan.digest !== certificate.plan.digest || plan.name !== certificate.plan.name
2697
2952
  || plan.sourceCheck.treeDigest !== certificate.plan.sourceTreeDigest
2698
2953
  || plan.sourceCheck.patchDigest !== certificate.plan.sourcePatchDigest
@@ -2709,7 +2964,7 @@ export class ControlPlaneStore {
2709
2964
  }
2710
2965
  this.readPreparedSourceArtifact(plan.id);
2711
2966
  const serialized = JSON.stringify(certificate), certificateDigest = controlPlaneDigest(certificate);
2712
- this.#database.exec('BEGIN IMMEDIATE');
2967
+ this.#beginMutation();
2713
2968
  try {
2714
2969
  const previous = this.#database.prepare('SELECT status,certificate_digest FROM source_creation_verifications WHERE plan_id=?')
2715
2970
  .get(plan.id);
@@ -2719,10 +2974,10 @@ export class ControlPlaneStore {
2719
2974
  else
2720
2975
  this.#database.prepare(`UPDATE source_creation_verifications SET status='verified',certificate_json=?,certificate_digest=?,updated_at=?
2721
2976
  WHERE plan_id=? AND status='claimed'`).run(serialized, certificateDigest, this.#now(), plan.id);
2722
- this.#database.exec('COMMIT');
2977
+ this.#commitMutation();
2723
2978
  }
2724
2979
  catch (error) {
2725
- this.#database.exec('ROLLBACK');
2980
+ this.#rollbackMutation();
2726
2981
  throw error;
2727
2982
  }
2728
2983
  }
@@ -2743,6 +2998,11 @@ export class ControlPlaneStore {
2743
2998
  if (certificate.plan.id !== planId || controlPlaneDigest(certificate) !== row.certificate_digest) {
2744
2999
  throw new ControlPlaneStoreError('invalid-state', 'stored creation verification certificate digest changed');
2745
3000
  }
3001
+ const plan = this.getSourcePlan(planId);
3002
+ if (certificate.plan.digest !== plan.digest || !plan.preparedEvidence
3003
+ || !creationVerificationMatchesPreparedEvidence(certificate, plan.preparedEvidence)) {
3004
+ throw new ControlPlaneStoreError('invalid-state', 'stored creation verification certificate does not match exact prepared source');
3005
+ }
2746
3006
  return certificate;
2747
3007
  }
2748
3008
  getCreationVerificationStatus(planId) {
@@ -2769,7 +3029,7 @@ export class ControlPlaneStore {
2769
3029
  if (!pinned || this.#now() >= pinned.expiresAt) {
2770
3030
  throw new ControlPlaneStoreError('invalid-state', 'creation verification requires a current pre-author policy binding');
2771
3031
  }
2772
- this.#database.exec('BEGIN IMMEDIATE');
3032
+ this.#beginMutation();
2773
3033
  try {
2774
3034
  const count = this.#database.prepare('SELECT count(*) AS n FROM source_creation_verifications').get();
2775
3035
  if (count.n >= 256)
@@ -2778,10 +3038,10 @@ export class ControlPlaneStore {
2778
3038
  this.#database.prepare(`INSERT INTO source_creation_verifications
2779
3039
  (plan_id,status,certificate_json,certificate_digest,reason,created_at,updated_at) VALUES (?,'claimed',NULL,NULL,NULL,?,?)`)
2780
3040
  .run(planId, now, now);
2781
- this.#database.exec('COMMIT');
3041
+ this.#commitMutation();
2782
3042
  }
2783
3043
  catch (error) {
2784
- this.#database.exec('ROLLBACK');
3044
+ this.#rollbackMutation();
2785
3045
  throw error;
2786
3046
  }
2787
3047
  }
@@ -2802,6 +3062,7 @@ export class ControlPlaneStore {
2802
3062
  const reference = this.getOwnerTaskFailureReference(plan.gapId);
2803
3063
  validateCreationCapabilitySource(source, certificate);
2804
3064
  if (!reference || controlPlaneDigest(plan.sourceRevision?.parent) !== controlPlaneDigest(certificate.parent) || !plan.sourceCheck || !plan.preparedEvidence || !plan.sourceRevision?.growthRun
3065
+ || !revisionVerificationMatchesPreparedEvidence(certificate, plan.preparedEvidence)
2805
3066
  || !job.intent.revision?.growthRun || plan.digest !== certificate.plan.digest || plan.name !== certificate.plan.name
2806
3067
  || plan.sourceCheck.treeDigest !== certificate.plan.sourceTreeDigest
2807
3068
  || plan.sourceCheck.patchDigest !== certificate.plan.sourcePatchDigest
@@ -2823,7 +3084,7 @@ export class ControlPlaneStore {
2823
3084
  if (Buffer.byteLength(sourceJson) > 2_097_152)
2824
3085
  throw new ControlPlaneStoreError('invalid-input', 'revision source archive exceeds bound');
2825
3086
  const serialized = JSON.stringify(certificate), certificateDigest = controlPlaneDigest(certificate);
2826
- this.#database.exec('BEGIN IMMEDIATE');
3087
+ this.#beginMutation();
2827
3088
  try {
2828
3089
  const previous = this.#database.prepare('SELECT status,certificate_digest FROM source_revision_verifications WHERE plan_id=?')
2829
3090
  .get(plan.id);
@@ -2838,10 +3099,10 @@ export class ControlPlaneStore {
2838
3099
  throw new ControlPlaneStoreError('conflict', 'revision source archive changed');
2839
3100
  if (!oldSource)
2840
3101
  this.#database.prepare('INSERT INTO source_revision_sources VALUES(?,?)').run(plan.id, sourceJson);
2841
- this.#database.exec('COMMIT');
3102
+ this.#commitMutation();
2842
3103
  }
2843
3104
  catch (error) {
2844
- this.#database.exec('ROLLBACK');
3105
+ this.#rollbackMutation();
2845
3106
  throw error;
2846
3107
  }
2847
3108
  }
@@ -2862,6 +3123,19 @@ export class ControlPlaneStore {
2862
3123
  if (certificate.plan.id !== planId || controlPlaneDigest(certificate) !== row.certificate_digest) {
2863
3124
  throw new ControlPlaneStoreError('invalid-state', 'stored revision verification certificate digest changed');
2864
3125
  }
3126
+ // Check retained immutable metadata without reopening expired build/grant authority.
3127
+ const plan = this.getSourcePlan(planId);
3128
+ if (plan.mode !== 'prepared-revise' || !plan.sourceRevision || !plan.sourceCheck
3129
+ || certificate.plan.digest !== plan.digest || certificate.plan.name !== plan.name
3130
+ || certificate.plan.generatorDigest !== plan.generatorDigest
3131
+ || certificate.plan.sourceTreeDigest !== plan.sourceCheck.treeDigest
3132
+ || certificate.plan.sourcePatchDigest !== plan.sourceCheck.patchDigest
3133
+ || controlPlaneDigest(certificate.parent) !== controlPlaneDigest(plan.sourceRevision.parent)
3134
+ || !plan.preparedEvidence || certificate.plan.artifactSha256 !== plan.preparedEvidence.pack.sha256
3135
+ || certificate.plan.artifactBytes !== plan.preparedEvidence.pack.sizeBytes
3136
+ || !revisionVerificationMatchesPreparedEvidence(certificate, plan.preparedEvidence)) {
3137
+ throw new ControlPlaneStoreError('invalid-state', 'stored revision verification certificate does not match exact prepared source');
3138
+ }
2865
3139
  return certificate;
2866
3140
  }
2867
3141
  getRevisionVerificationStatus(planId) {
@@ -2888,7 +3162,7 @@ export class ControlPlaneStore {
2888
3162
  if (!pinned || this.#now() >= pinned.expiresAt) {
2889
3163
  throw new ControlPlaneStoreError('invalid-state', 'revision verification requires a current pre-author policy binding');
2890
3164
  }
2891
- this.#database.exec('BEGIN IMMEDIATE');
3165
+ this.#beginMutation();
2892
3166
  try {
2893
3167
  const count = this.#database.prepare('SELECT count(*) AS n FROM source_revision_verifications').get();
2894
3168
  if (count.n >= 256)
@@ -2897,10 +3171,10 @@ export class ControlPlaneStore {
2897
3171
  this.#database.prepare(`INSERT INTO source_revision_verifications
2898
3172
  (plan_id,status,certificate_json,certificate_digest,reason,created_at,updated_at) VALUES (?,'claimed',NULL,NULL,NULL,?,?)`)
2899
3173
  .run(planId, now, now);
2900
- this.#database.exec('COMMIT');
3174
+ this.#commitMutation();
2901
3175
  }
2902
3176
  catch (error) {
2903
- this.#database.exec('ROLLBACK');
3177
+ this.#rollbackMutation();
2904
3178
  throw error;
2905
3179
  }
2906
3180
  }
@@ -2926,6 +3200,7 @@ export class ControlPlaneStore {
2926
3200
  const previousJobs = new Set([...(parent?.observations ?? []), ...(candidate?.observations ?? [])].map(item => item.jobId));
2927
3201
  if (certificate.verifiedAt > this.#now() || certificate.expiresAt <= this.#now()
2928
3202
  || !candidate || !parent || !source || !reference || !run?.revisionRegressionAcceptance
3203
+ || !revisionRegressionMatchesCandidate(certificate, candidate, parent)
2929
3204
  || !job.intent.revision?.growthRun.revisionRegressionAcceptance
2930
3205
  || controlPlaneDigest(certificate.plan) !== controlPlaneDigest(candidate.plan)
2931
3206
  || controlPlaneDigest(certificate.source) !== controlPlaneDigest(candidate.source)
@@ -2951,7 +3226,7 @@ export class ControlPlaneStore {
2951
3226
  }
2952
3227
  this.readPreparedSourceArtifact(plan.id);
2953
3228
  const serialized = JSON.stringify(certificate), digest = controlPlaneDigest(certificate);
2954
- this.#database.exec('BEGIN IMMEDIATE');
3229
+ this.#beginMutation();
2955
3230
  try {
2956
3231
  const old = this.#database.prepare('SELECT status,certificate_digest FROM source_revision_regressions WHERE plan_id=?')
2957
3232
  .get(plan.id);
@@ -2961,10 +3236,10 @@ export class ControlPlaneStore {
2961
3236
  else
2962
3237
  this.#database.prepare(`UPDATE source_revision_regressions SET status='verified',certificate_json=?,certificate_digest=?,updated_at=?
2963
3238
  WHERE plan_id=? AND status='claimed'`).run(serialized, digest, this.#now(), plan.id);
2964
- this.#database.exec('COMMIT');
3239
+ this.#commitMutation();
2965
3240
  }
2966
3241
  catch (error) {
2967
- this.#database.exec('ROLLBACK');
3242
+ this.#rollbackMutation();
2968
3243
  throw error;
2969
3244
  }
2970
3245
  }
@@ -2985,6 +3260,10 @@ export class ControlPlaneStore {
2985
3260
  if (certificate.plan.id !== planId || controlPlaneDigest(certificate) !== row.certificate_digest) {
2986
3261
  throw new ControlPlaneStoreError('invalid-state', 'stored revision regression certificate digest changed');
2987
3262
  }
3263
+ const candidate = this.getRevisionVerification(planId), parent = this.getCreationVerification(certificate.parent.planId);
3264
+ if (!candidate || !parent || !revisionRegressionMatchesCandidate(certificate, candidate, parent)) {
3265
+ throw new ControlPlaneStoreError('invalid-state', 'stored revision regression certificate does not match exact candidate execution');
3266
+ }
2988
3267
  return certificate;
2989
3268
  }
2990
3269
  getRevisionRegressionStatus(planId) {
@@ -3011,7 +3290,7 @@ export class ControlPlaneStore {
3011
3290
  if (!pinned || this.#now() >= pinned.expiresAt) {
3012
3291
  throw new ControlPlaneStoreError('invalid-state', 'revision regression requires a current pre-author policy binding');
3013
3292
  }
3014
- this.#database.exec('BEGIN IMMEDIATE');
3293
+ this.#beginMutation();
3015
3294
  try {
3016
3295
  const count = this.#database.prepare('SELECT count(*) AS n FROM source_revision_regressions').get();
3017
3296
  if (count.n >= 256)
@@ -3020,10 +3299,10 @@ export class ControlPlaneStore {
3020
3299
  this.#database.prepare(`INSERT INTO source_revision_regressions
3021
3300
  (plan_id,status,certificate_json,certificate_digest,reason,created_at,updated_at) VALUES (?,'claimed',NULL,NULL,NULL,?,?)`)
3022
3301
  .run(planId, now, now);
3023
- this.#database.exec('COMMIT');
3302
+ this.#commitMutation();
3024
3303
  }
3025
3304
  catch (error) {
3026
- this.#database.exec('ROLLBACK');
3305
+ this.#rollbackMutation();
3027
3306
  throw error;
3028
3307
  }
3029
3308
  }
@@ -3054,7 +3333,7 @@ export class ControlPlaneStore {
3054
3333
  deleteExpiredPreparedSourceArtifacts(now) {
3055
3334
  if (!Number.isSafeInteger(now) || now < 0)
3056
3335
  throw new ControlPlaneStoreError('invalid-input', 'artifact expiry time is invalid');
3057
- this.#database.exec('BEGIN IMMEDIATE');
3336
+ this.#beginMutation();
3058
3337
  try {
3059
3338
  const references = this.#database.prepare('SELECT plan_id, pack_sha256 FROM source_prepared_artifact_refs')
3060
3339
  .all();
@@ -3076,11 +3355,11 @@ export class ControlPlaneStore {
3076
3355
  .run(artifact.pack_sha256);
3077
3356
  deleted += Number(result.changes);
3078
3357
  }
3079
- this.#database.exec('COMMIT');
3358
+ this.#commitMutation();
3080
3359
  return deleted;
3081
3360
  }
3082
3361
  catch (error) {
3083
- this.#database.exec('ROLLBACK');
3362
+ this.#rollbackMutation();
3084
3363
  throw error;
3085
3364
  }
3086
3365
  }
@@ -3341,7 +3620,7 @@ export class ControlPlaneStore {
3341
3620
  throw new ControlPlaneStoreError('invalid-state', 'expired source receipt is missing');
3342
3621
  return receipt.result;
3343
3622
  }
3344
- this.#database.exec('BEGIN IMMEDIATE');
3623
+ this.#beginMutation();
3345
3624
  try {
3346
3625
  const retired = this.#database.prepare(`UPDATE source_plans SET status = 'expired', revision = revision + 1, updated_at = ?
3347
3626
  WHERE id = ? AND revision = ? AND ((mode = 'modify' AND status IN ('pending-approval', 'approved'))
@@ -3358,11 +3637,11 @@ export class ControlPlaneStore {
3358
3637
  const result = this.getSourcePlan(plan.id);
3359
3638
  this.#insertReceipt({ idempotencyKey: `source-expired:${plan.id}`, operation: 'source-expired',
3360
3639
  inputDigest: expiryDigest, result, createdAt: input.now });
3361
- this.#database.exec('COMMIT');
3640
+ this.#commitMutation();
3362
3641
  return result;
3363
3642
  }
3364
3643
  catch (error) {
3365
- this.#database.exec('ROLLBACK');
3644
+ this.#rollbackMutation();
3366
3645
  throw error;
3367
3646
  }
3368
3647
  }
@@ -3420,7 +3699,7 @@ export class ControlPlaneStore {
3420
3699
  throw new ControlPlaneStoreError(verified.decision === 'approved' ? 'expired' : 'invalid-state', 'plan approval is not currently applicable');
3421
3700
  const table = kind === 'activation' ? 'activation_plans' : 'source_plans';
3422
3701
  const now = this.#now();
3423
- this.#database.exec('BEGIN IMMEDIATE');
3702
+ this.#beginMutation();
3424
3703
  try {
3425
3704
  const statement = this.#database.prepare(`UPDATE ${table} SET status = 'approved', revision = revision + 1, approval_json = ?,
3426
3705
  ${kind === 'activation' ? 'approval_receipt_json = ?,' : ''} updated_at = ?
@@ -3433,11 +3712,11 @@ export class ControlPlaneStore {
3433
3712
  const output = kind === 'activation' ? this.getPlan(plan.id) : this.getSourcePlan(plan.id);
3434
3713
  const receipt = { idempotencyKey: input.idempotencyKey, operation: 'approve-plan', inputDigest, result: output, createdAt: now };
3435
3714
  this.#insertReceipt(receipt);
3436
- this.#database.exec('COMMIT');
3715
+ this.#commitMutation();
3437
3716
  return receipt;
3438
3717
  }
3439
3718
  catch (error) {
3440
- this.#database.exec('ROLLBACK');
3719
+ this.#rollbackMutation();
3441
3720
  throw error;
3442
3721
  }
3443
3722
  });
@@ -3448,7 +3727,7 @@ export class ControlPlaneStore {
3448
3727
  if (input.leaseMs < 5_000 || input.leaseMs > 300_000)
3449
3728
  throw new ControlPlaneStoreError('invalid-input', 'leaseMs is invalid');
3450
3729
  this.assertOwnerActivationSource(input.planId);
3451
- this.#database.exec('BEGIN IMMEDIATE');
3730
+ this.#beginMutation();
3452
3731
  try {
3453
3732
  const row = this.#database.prepare('SELECT * FROM activation_plans WHERE id = ?').get(input.planId);
3454
3733
  if (row === undefined)
@@ -3493,12 +3772,12 @@ export class ControlPlaneStore {
3493
3772
  if (Number(result.changes) !== 1)
3494
3773
  throw new ControlPlaneStoreError('conflict', 'activation plan changed while being claimed');
3495
3774
  const claimed = this.getPlan(plan.id);
3496
- this.#database.exec('COMMIT');
3775
+ this.#commitMutation();
3497
3776
  return claimed;
3498
3777
  });
3499
3778
  }
3500
3779
  catch (error) {
3501
- this.#database.exec('ROLLBACK');
3780
+ this.#rollbackMutation();
3502
3781
  throw error;
3503
3782
  }
3504
3783
  }
@@ -3516,7 +3795,7 @@ export class ControlPlaneStore {
3516
3795
  recordActivationTargetBaseline(input) {
3517
3796
  return this.#withActivationSource(input.planId, () => {
3518
3797
  const now = this.#now();
3519
- this.#database.exec('BEGIN IMMEDIATE');
3798
+ this.#beginMutation();
3520
3799
  try {
3521
3800
  this.assertOwnerActivationSource(input.planId);
3522
3801
  const row = this.#database.prepare('SELECT * FROM activation_plans WHERE id = ?').get(input.planId);
@@ -3540,11 +3819,11 @@ export class ControlPlaneStore {
3540
3819
  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
3820
  }
3542
3821
  const result = this.getPlan(plan.id);
3543
- this.#database.exec('COMMIT');
3822
+ this.#commitMutation();
3544
3823
  return result;
3545
3824
  }
3546
3825
  catch (error) {
3547
- this.#database.exec('ROLLBACK');
3826
+ this.#rollbackMutation();
3548
3827
  throw error;
3549
3828
  }
3550
3829
  });
@@ -3558,7 +3837,7 @@ export class ControlPlaneStore {
3558
3837
  recordActivationInstalledBaseline(input) {
3559
3838
  return this.#withActivationSource(input.planId, () => {
3560
3839
  const now = this.#now();
3561
- this.#database.exec('BEGIN IMMEDIATE');
3840
+ this.#beginMutation();
3562
3841
  try {
3563
3842
  this.assertOwnerActivationSource(input.planId);
3564
3843
  const row = this.#database.prepare('SELECT * FROM activation_plans WHERE id = ?').get(input.planId);
@@ -3599,11 +3878,11 @@ export class ControlPlaneStore {
3599
3878
  successful_order, recorded_at, succeeded_at) VALUES (?, ?, ?, NULL, ?, NULL)`).run(plan.id, baseline, sequence.next_exposure_order, now);
3600
3879
  }
3601
3880
  const output = this.getPlan(plan.id);
3602
- this.#database.exec('COMMIT');
3881
+ this.#commitMutation();
3603
3882
  return output;
3604
3883
  }
3605
3884
  catch (error) {
3606
- this.#database.exec('ROLLBACK');
3885
+ this.#rollbackMutation();
3607
3886
  throw error;
3608
3887
  }
3609
3888
  });
@@ -3626,7 +3905,7 @@ export class ControlPlaneStore {
3626
3905
  /** Move a signed closed deployment watch into the existing physical rollback lifecycle. */
3627
3906
  beginPostActivationRollback(input) {
3628
3907
  const now = this.#now();
3629
- this.#database.exec('BEGIN IMMEDIATE');
3908
+ this.#beginMutation();
3630
3909
  try {
3631
3910
  const row = this.#database.prepare('SELECT * FROM activation_plans WHERE id = ?').get(input.planId);
3632
3911
  if (row === undefined)
@@ -3637,7 +3916,7 @@ export class ControlPlaneStore {
3637
3916
  // A restart or retry must retain the original rollback identity and never
3638
3917
  // manufacture another rollback from a now-closed watch.
3639
3918
  if ((plan.status === 'rollback-pending' || plan.status === 'rolled-back') && this.#hasPostActivationRollbackProvenance(plan)) {
3640
- this.#database.exec('COMMIT');
3919
+ this.#commitMutation();
3641
3920
  return plan;
3642
3921
  }
3643
3922
  if (plan.status !== 'activated' || plan.activation === undefined) {
@@ -3675,11 +3954,11 @@ export class ControlPlaneStore {
3675
3954
  if (Number(updated.changes) !== 1)
3676
3955
  throw new ControlPlaneStoreError('conflict', 'post-activation rollback lost its activation CAS');
3677
3956
  const output = this.getPlan(plan.id);
3678
- this.#database.exec('COMMIT');
3957
+ this.#commitMutation();
3679
3958
  return output;
3680
3959
  }
3681
3960
  catch (error) {
3682
- this.#database.exec('ROLLBACK');
3961
+ this.#rollbackMutation();
3683
3962
  throw error;
3684
3963
  }
3685
3964
  }
@@ -3800,7 +4079,7 @@ export class ControlPlaneStore {
3800
4079
  async withActivationFileSystemGuard(input, action) {
3801
4080
  positiveInteger(input.leaseMs, 'leaseMs');
3802
4081
  try {
3803
- this.#database.exec('BEGIN IMMEDIATE');
4082
+ this.#beginMutation();
3804
4083
  }
3805
4084
  catch {
3806
4085
  throw new ControlPlaneStoreError('conflict', 'activation filesystem mutex is held');
@@ -3825,30 +4104,30 @@ export class ControlPlaneStore {
3825
4104
  assertOwner(false);
3826
4105
  const finished = this.#now();
3827
4106
  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');
4107
+ this.#commitMutation();
3829
4108
  return result;
3830
4109
  });
3831
4110
  }
3832
4111
  catch (error) {
3833
- this.#database.exec('ROLLBACK');
4112
+ this.#rollbackMutation();
3834
4113
  throw error;
3835
4114
  }
3836
4115
  }
3837
4116
  /** Serialize lock-file lifecycle operations even after a plan became terminal. */
3838
4117
  async withExclusiveWrite(action) {
3839
4118
  try {
3840
- this.#database.exec('BEGIN IMMEDIATE');
4119
+ this.#beginMutation();
3841
4120
  }
3842
4121
  catch {
3843
4122
  throw new ControlPlaneStoreError('conflict', 'control-plane mutex is held');
3844
4123
  }
3845
4124
  try {
3846
4125
  const result = await action();
3847
- this.#database.exec('COMMIT');
4126
+ this.#commitMutation();
3848
4127
  return result;
3849
4128
  }
3850
4129
  catch (error) {
3851
- this.#database.exec('ROLLBACK');
4130
+ this.#rollbackMutation();
3852
4131
  throw error;
3853
4132
  }
3854
4133
  }
@@ -3987,7 +4266,7 @@ export class ControlPlaneStore {
3987
4266
  ORDER BY epoch.created_at,epoch.operation_id LIMIT 100`).all(coordinatorId, this.#now()).map(runtimeEpochFromRow);
3988
4267
  }
3989
4268
  claimRuntimeEpoch(operationId, coordinatorId) {
3990
- this.#database.exec('BEGIN IMMEDIATE');
4269
+ this.#beginMutation();
3991
4270
  try {
3992
4271
  const record = this.getRuntimeEpoch(operationId);
3993
4272
  if (!['pending', 'claimed'].includes(record.status) || this.#now() >= record.request.requestedAt + record.request.receiptTtlMs) {
@@ -3998,17 +4277,17 @@ export class ControlPlaneStore {
3998
4277
  if (context.handoff.coordinatorId !== coordinatorId || (this.#adoptionCoordinatorId !== undefined && this.#adoptionCoordinatorId !== coordinatorId)) {
3999
4278
  throw new ControlPlaneStoreError('conflict', 'runtime epoch coordinator changed');
4000
4279
  }
4001
- this.#database.exec('COMMIT');
4280
+ this.#commitMutation();
4002
4281
  return { ...record, status: 'claimed' };
4003
4282
  }
4004
4283
  catch (error) {
4005
- this.#database.exec('ROLLBACK');
4284
+ this.#rollbackMutation();
4006
4285
  throw error;
4007
4286
  }
4008
4287
  }
4009
4288
  applyRuntimeEpoch(receiptInput, trust) {
4010
4289
  const receipt = parseRuntimeEpochReceipt(receiptInput);
4011
- this.#database.exec('BEGIN IMMEDIATE');
4290
+ this.#beginMutation();
4012
4291
  try {
4013
4292
  const context = readOwnerRuntimeEpochContext(this.#database, receipt.operationId);
4014
4293
  if (this.#now() >= context.request.requestedAt + context.request.receiptTtlMs)
@@ -4022,18 +4301,18 @@ export class ControlPlaneStore {
4022
4301
  this.#database.prepare("UPDATE deployment_runtime_epochs SET status='applied',receipt_json=? WHERE operation_id=? AND status='claimed'")
4023
4302
  .run(JSON.stringify(receipt), receipt.operationId);
4024
4303
  const result = this.getRuntimeEpoch(receipt.operationId);
4025
- this.#database.exec('COMMIT');
4304
+ this.#commitMutation();
4026
4305
  return result;
4027
4306
  }
4028
4307
  catch (error) {
4029
- this.#database.exec('ROLLBACK');
4308
+ this.#rollbackMutation();
4030
4309
  throw error;
4031
4310
  }
4032
4311
  }
4033
4312
  prepareHostAttestationOperation(input) {
4034
4313
  return this.#withActivationSource(input.planId, () => {
4035
4314
  const now = this.#now();
4036
- this.#database.exec('BEGIN IMMEDIATE');
4315
+ this.#beginMutation();
4037
4316
  try {
4038
4317
  this.assertOwnerActivationSource(input.planId);
4039
4318
  const plan = this.getPlan(input.planId);
@@ -4085,18 +4364,18 @@ export class ControlPlaneStore {
4085
4364
  }
4086
4365
  if (prior.bindingDigest !== bindingDigest)
4087
4366
  throw new ControlPlaneStoreError('conflict', 'durable Host operation payload changed for the same phase');
4088
- this.#database.exec('COMMIT');
4367
+ this.#commitMutation();
4089
4368
  return prior;
4090
4369
  }
4091
4370
  this.#database.prepare(`INSERT INTO host_attestation_operations (plan_id, phase, operation_id, binding_digest,
4092
4371
  request_digest, request_json, status, receipt_digest, receipt_json, created_at, completed_at, applied_at)
4093
4372
  VALUES (?, ?, ?, ?, ?, ?, 'pending', NULL, NULL, ?, NULL, NULL)`).run(plan.id, expected.phase, operationId, bindingDigest, controlPlaneDigest(request), JSON.stringify(request), now);
4094
4373
  const operation = this.getHostAttestationOperation(operationId);
4095
- this.#database.exec('COMMIT');
4374
+ this.#commitMutation();
4096
4375
  return operation;
4097
4376
  }
4098
4377
  catch (error) {
4099
- this.#database.exec('ROLLBACK');
4378
+ this.#rollbackMutation();
4100
4379
  throw error;
4101
4380
  }
4102
4381
  });
@@ -4143,7 +4422,7 @@ export class ControlPlaneStore {
4143
4422
  if (operation.phase === 'rollback' && input.receipt.outcome !== 'passed')
4144
4423
  throw new ControlPlaneStoreError('conflict', 'failed physical rollback receipt cannot settle recovery');
4145
4424
  const commit = () => {
4146
- this.#database.exec('BEGIN IMMEDIATE');
4425
+ this.#beginMutation();
4147
4426
  try {
4148
4427
  if (input.requireCurrentSource)
4149
4428
  this.assertOwnerActivationSource(plan.id);
@@ -4157,7 +4436,7 @@ export class ControlPlaneStore {
4157
4436
  if (controlPlaneDigest(currentOperation.receipt) !== controlPlaneDigest(input.receipt)) {
4158
4437
  throw new ControlPlaneStoreError('conflict', 'Host attestation receipt changed after completion');
4159
4438
  }
4160
- this.#database.exec('COMMIT');
4439
+ this.#commitMutation();
4161
4440
  return currentOperation.receipt;
4162
4441
  }
4163
4442
  const dispatch = this.getHostAttestationDispatchStatus(operation.operationId);
@@ -4172,11 +4451,11 @@ export class ControlPlaneStore {
4172
4451
  if (Number(completed.changes) !== 1)
4173
4452
  throw new ControlPlaneStoreError('conflict', 'Host attestation dispatch completion lost its claim');
4174
4453
  }
4175
- this.#database.exec('COMMIT');
4454
+ this.#commitMutation();
4176
4455
  return input.receipt;
4177
4456
  }
4178
4457
  catch (error) {
4179
- this.#database.exec('ROLLBACK');
4458
+ this.#rollbackMutation();
4180
4459
  throw error;
4181
4460
  }
4182
4461
  };
@@ -4228,7 +4507,7 @@ export class ControlPlaneStore {
4228
4507
  }
4229
4508
  putLiveQualification(batch) {
4230
4509
  assertLiveQualificationBatch(batch);
4231
- this.#database.exec('BEGIN IMMEDIATE');
4510
+ this.#beginMutation();
4232
4511
  try {
4233
4512
  const plan = this.getPlan(batch.planId);
4234
4513
  if (plan.status !== 'awaiting-live-tasks' || this.#now() >= batch.deadlineAt || this.#now() >= batch.expiresAt)
@@ -4242,10 +4521,10 @@ export class ControlPlaneStore {
4242
4521
  this.#database.prepare(`INSERT INTO live_qualification_batches
4243
4522
  (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
4523
  readLiveQualificationContext(this.#database, batch.id);
4245
- this.#database.exec('COMMIT');
4524
+ this.#commitMutation();
4246
4525
  }
4247
4526
  catch (error) {
4248
- this.#database.exec('ROLLBACK');
4527
+ this.#rollbackMutation();
4249
4528
  throw error;
4250
4529
  }
4251
4530
  }
@@ -4258,13 +4537,13 @@ export class ControlPlaneStore {
4258
4537
  throw new ControlPlaneStoreError('not-found', 'live qualification batch absent');
4259
4538
  const receipt = verifyLiveQualificationReceipt(input.receipt, record.batch, input.publicKeyPem, this.#now());
4260
4539
  return input.withSourceFence(() => {
4261
- this.#database.exec('BEGIN IMMEDIATE');
4540
+ this.#beginMutation();
4262
4541
  try {
4263
4542
  const current = this.getLiveQualification(record.batch.id);
4264
4543
  if (current.state === 'applied') {
4265
4544
  if (controlPlaneDigest(current.receipt) !== controlPlaneDigest(receipt))
4266
4545
  throw new ControlPlaneStoreError('conflict', 'qualification receipt changed');
4267
- this.#database.exec('COMMIT');
4546
+ this.#commitMutation();
4268
4547
  return this.getPlan(record.batch.planId);
4269
4548
  }
4270
4549
  this.assertCurrentLiveQualification(record.batch.id);
@@ -4277,11 +4556,11 @@ export class ControlPlaneStore {
4277
4556
  throw new ControlPlaneStoreError('conflict', 'qualification lost plan CAS');
4278
4557
  this.#database.prepare("UPDATE live_qualification_batches SET state='applied',receipt_json=?,receipt_digest=? WHERE id=? AND state IN ('pending','signed')")
4279
4558
  .run(JSON.stringify(receipt), controlPlaneDigest(receipt), record.batch.id);
4280
- this.#database.exec('COMMIT');
4559
+ this.#commitMutation();
4281
4560
  return this.getPlan(plan.id);
4282
4561
  }
4283
4562
  catch (error) {
4284
- this.#database.exec('ROLLBACK');
4563
+ this.#rollbackMutation();
4285
4564
  throw error;
4286
4565
  }
4287
4566
  });
@@ -4289,7 +4568,7 @@ export class ControlPlaneStore {
4289
4568
  /** Negative-only Host seam. The signed terms permit conservative removal of unconfirmable qualification. */
4290
4569
  invalidateLiveQualification(input) {
4291
4570
  const reason = bounded(input.reason, 'qualification invalidation reason', 160);
4292
- this.#database.exec('BEGIN IMMEDIATE');
4571
+ this.#beginMutation();
4293
4572
  try {
4294
4573
  const plan = this.getPlan(input.planId), record = this.getLiveQualification(input.batchId);
4295
4574
  if (!record || record.batch.planId !== plan.id || record.state !== 'applied' || record.receipt?.disposition !== 'qualified'
@@ -4297,7 +4576,7 @@ export class ControlPlaneStore {
4297
4576
  throw new ControlPlaneStoreError('conflict', 'qualification invalidation lacks exact accepted evidence');
4298
4577
  }
4299
4578
  if (plan.status === 'rollback-pending' || plan.status === 'rolled-back') {
4300
- this.#database.exec('COMMIT');
4579
+ this.#commitMutation();
4301
4580
  return plan;
4302
4581
  }
4303
4582
  if (!['commit-pending', 'activated'].includes(plan.status) || record.batch.fence !== plan.activation.fence)
@@ -4321,11 +4600,11 @@ export class ControlPlaneStore {
4321
4600
  this.#database.prepare(`UPDATE activation_plans SET status='rollback-pending',revision=revision+1,
4322
4601
  activation_lease_until=NULL,rollback_profile_restored=0,failure_code='live-qualification-invalidated',updated_at=?
4323
4602
  WHERE id=? AND revision=? AND activation_fence=?`).run(now, plan.id, plan.revision, plan.activation.fence);
4324
- this.#database.exec('COMMIT');
4603
+ this.#commitMutation();
4325
4604
  return this.getPlan(plan.id);
4326
4605
  }
4327
4606
  catch (error) {
4328
- this.#database.exec('ROLLBACK');
4607
+ this.#rollbackMutation();
4329
4608
  throw error;
4330
4609
  }
4331
4610
  }
@@ -4343,7 +4622,7 @@ export class ControlPlaneStore {
4343
4622
  /** Same synchronous transaction owns current-profile selection and capture. */
4344
4623
  withForegroundDeployment(task, profilePath, callback) {
4345
4624
  assertForegroundTask(task);
4346
- this.#database.exec('BEGIN IMMEDIATE');
4625
+ this.#beginMutation();
4347
4626
  try {
4348
4627
  const active = this.#database.prepare(`SELECT id FROM activation_plans WHERE target_path = ? AND status IN (
4349
4628
  'staging', 'awaiting-reload', 'awaiting-readiness', 'awaiting-live-tasks', 'awaiting-effect-blocked-replay', 'awaiting-shadow',
@@ -4387,11 +4666,11 @@ export class ControlPlaneStore {
4387
4666
  const result = callback(plan, hostOperationFromRow(row));
4388
4667
  if (result && (typeof result === 'object' || typeof result === 'function') && 'then' in result)
4389
4668
  throw new ControlPlaneStoreError('invalid-input', 'foreground capture must be synchronous');
4390
- this.#database.exec('COMMIT');
4669
+ this.#commitMutation();
4391
4670
  return result;
4392
4671
  }
4393
4672
  catch (error) {
4394
- this.#database.exec('ROLLBACK');
4673
+ this.#rollbackMutation();
4395
4674
  throw error;
4396
4675
  }
4397
4676
  finally {
@@ -4458,7 +4737,7 @@ export class ControlPlaneStore {
4458
4737
  }
4459
4738
  putTaskObservation(batch) {
4460
4739
  assertTaskObservationBatch(batch);
4461
- this.#database.exec('BEGIN IMMEDIATE');
4740
+ this.#beginMutation();
4462
4741
  try {
4463
4742
  if (batch.createdAt > this.#now() || batch.expiresAt <= this.#now())
4464
4743
  throw new ControlPlaneStoreError('expired', 'task observation is outside its validity interval');
@@ -4466,22 +4745,39 @@ export class ControlPlaneStore {
4466
4745
  if (prior) {
4467
4746
  if (prior.batch.digest !== batch.digest)
4468
4747
  throw new ControlPlaneStoreError('conflict', 'task observation identity reused');
4469
- this.#database.exec('COMMIT');
4748
+ this.#commitMutation();
4470
4749
  return prior;
4471
4750
  }
4472
4751
  const encoded = JSON.stringify(batch);
4473
4752
  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
4753
  readTaskObservationContext(this.#database, batch.id);
4475
- this.#database.exec('COMMIT');
4754
+ this.#commitMutation();
4476
4755
  return Object.freeze({ batch, state: 'pending' });
4477
4756
  }
4478
4757
  catch (error) {
4479
- this.#database.exec('ROLLBACK');
4758
+ this.#rollbackMutation();
4480
4759
  throw error;
4481
4760
  }
4482
4761
  }
4483
4762
  getTaskObservation(id) { return getTaskObservationRecord(this.#database, id); }
4484
4763
  assertCurrentTaskObservation(id) { readTaskObservationContext(this.#database, id); }
4764
+ /** Seal one observational trend result into the append-only snapshot ledger; same trend, same row. */
4765
+ sealCreatedCapabilityTrendSnapshot(trend) {
4766
+ const snapshot = createCreationCapabilityTrendSnapshot(trend);
4767
+ this.#beginMutation();
4768
+ try {
4769
+ const result = sealCreationCapabilityTrendSnapshot(this.#database, snapshot, this.#now());
4770
+ this.#commitMutation();
4771
+ return result;
4772
+ }
4773
+ catch (error) {
4774
+ this.#rollbackMutation();
4775
+ throw error;
4776
+ }
4777
+ }
4778
+ listCreatedCapabilityTrendSnapshots(query = {}) {
4779
+ return listCreationCapabilityTrendSnapshots(this.#database, query);
4780
+ }
4485
4781
  listTaskObservations(lane, limit = 100) {
4486
4782
  if (!KEY.test(lane) || !Number.isSafeInteger(limit) || limit < 1 || limit > 1000)
4487
4783
  throw new ControlPlaneStoreError('invalid-input', 'task observation list is invalid');
@@ -4640,7 +4936,7 @@ export class ControlPlaneStore {
4640
4936
  const claim = () => {
4641
4937
  const candidate = this.getHostAttestationOperation(input.operationId);
4642
4938
  return this.#withActivationSource(candidate.planId, () => {
4643
- this.#database.exec('BEGIN IMMEDIATE');
4939
+ this.#beginMutation();
4644
4940
  try {
4645
4941
  this.assertOwnerActivationSource(candidate.planId);
4646
4942
  const operation = this.getHostAttestationOperation(input.operationId), plan = this.getPlan(operation.planId), expected = expectedAttestation[plan.status];
@@ -4648,17 +4944,17 @@ export class ControlPlaneStore {
4648
4944
  throw new ControlPlaneStoreError('conflict', 'Host attestation operation lost its plan revision/fence/phase');
4649
4945
  const request = this.#assertHostAttestationChain(operation, plan);
4650
4946
  if (operation.receipt) {
4651
- this.#database.exec('COMMIT');
4947
+ this.#commitMutation();
4652
4948
  return { operation, plan, request };
4653
4949
  }
4654
4950
  if (this.getHostAttestationDispatchStatus(operation.operationId) !== undefined)
4655
4951
  throw new ControlPlaneStoreError('conflict', 'Host attestation outcome is unknown; receipt reconciliation is required');
4656
4952
  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');
4953
+ this.#commitMutation();
4658
4954
  return { operation, plan, request };
4659
4955
  }
4660
4956
  catch (error) {
4661
- this.#database.exec('ROLLBACK');
4957
+ this.#rollbackMutation();
4662
4958
  throw error;
4663
4959
  }
4664
4960
  });
@@ -4725,7 +5021,7 @@ export class ControlPlaneStore {
4725
5021
  this.#assertAdoptionHandoff(input.planId);
4726
5022
  }
4727
5023
  const now = this.#now();
4728
- this.#database.exec('BEGIN IMMEDIATE');
5024
+ this.#beginMutation();
4729
5025
  try {
4730
5026
  this.#withActivationSource(input.planId, () => { }, input.to === 'rollback-pending' || input.to === 'rolled-back');
4731
5027
  const result = this.#database.prepare(`UPDATE activation_plans SET status = ?, revision = revision + 1,
@@ -4744,11 +5040,11 @@ export class ControlPlaneStore {
4744
5040
  this.#database.prepare(`UPDATE capability_gaps SET status = 'closed', revision = revision + 1, updated_at = ? WHERE id = ?`).run(now, plan.gapId);
4745
5041
  this.#finishActivation(plan, input.fence, now);
4746
5042
  }
4747
- this.#database.exec('COMMIT');
5043
+ this.#commitMutation();
4748
5044
  return plan;
4749
5045
  }
4750
5046
  catch (error) {
4751
- this.#database.exec('ROLLBACK');
5047
+ this.#rollbackMutation();
4752
5048
  throw error;
4753
5049
  }
4754
5050
  }, input.to === 'rollback-pending' || input.to === 'rolled-back');
@@ -4791,7 +5087,7 @@ export class ControlPlaneStore {
4791
5087
  const now = this.#now();
4792
5088
  const nextStatus = verified.outcome === 'passed' ? (verified.phase === 'readiness' && plan.dossier.liveQualification ? 'awaiting-live-tasks' : expected.next) : 'rollback-pending';
4793
5089
  return this.#withActivationSource(input.planId, () => {
4794
- this.#database.exec('BEGIN IMMEDIATE');
5090
+ this.#beginMutation();
4795
5091
  try {
4796
5092
  this.#withActivationSource(input.planId, () => { }, recovery);
4797
5093
  const lockedOperation = this.getHostAttestationOperation(input.receipt.operationId);
@@ -4822,11 +5118,11 @@ export class ControlPlaneStore {
4822
5118
  this.#finishActivation(output, input.expectedFence, now);
4823
5119
  const operationReceipt = { idempotencyKey: input.idempotencyKey, operation: 'host-attestation', inputDigest, result: output, createdAt: now };
4824
5120
  this.#insertReceipt(operationReceipt);
4825
- this.#database.exec('COMMIT');
5121
+ this.#commitMutation();
4826
5122
  return operationReceipt;
4827
5123
  }
4828
5124
  catch (error) {
4829
- this.#database.exec('ROLLBACK');
5125
+ this.#rollbackMutation();
4830
5126
  throw error;
4831
5127
  }
4832
5128
  }, recovery);
@@ -4884,7 +5180,7 @@ export class ControlPlaneStore {
4884
5180
  }
4885
5181
  }
4886
5182
  const commit = () => {
4887
- this.#database.exec('BEGIN IMMEDIATE');
5183
+ this.#beginMutation();
4888
5184
  try {
4889
5185
  if (input.taskObservationId !== undefined) {
4890
5186
  const context = readTaskObservationContext(this.#database, input.taskObservationId);
@@ -4922,11 +5218,11 @@ export class ControlPlaneStore {
4922
5218
  }
4923
5219
  const operationReceipt = { idempotencyKey: key, operation: 'post-activation-observation', inputDigest, result: output, createdAt: now };
4924
5220
  this.#insertReceipt(operationReceipt);
4925
- this.#database.exec('COMMIT');
5221
+ this.#commitMutation();
4926
5222
  return operationReceipt;
4927
5223
  }
4928
5224
  catch (error) {
4929
- this.#database.exec('ROLLBACK');
5225
+ this.#rollbackMutation();
4930
5226
  throw error;
4931
5227
  }
4932
5228
  };
@@ -4953,7 +5249,7 @@ export class ControlPlaneStore {
4953
5249
  }
4954
5250
  const verified = await input.resolveAuthority(input.receipt).verify(input.receipt, plan, watch.exact);
4955
5251
  const now = this.#now();
4956
- this.#database.exec('BEGIN IMMEDIATE');
5252
+ this.#beginMutation();
4957
5253
  try {
4958
5254
  const current = this.#database.prepare('SELECT * FROM activation_watch WHERE plan_id = ?').get(plan.id);
4959
5255
  if (current.state === 'closed-retracted')
@@ -4970,11 +5266,11 @@ export class ControlPlaneStore {
4970
5266
  const output = this.getActivationWatch(plan.id);
4971
5267
  const operationReceipt = { idempotencyKey: key, operation: 'activation-retraction', inputDigest, result: output, createdAt: now };
4972
5268
  this.#insertReceipt(operationReceipt);
4973
- this.#database.exec('COMMIT');
5269
+ this.#commitMutation();
4974
5270
  return operationReceipt;
4975
5271
  }
4976
5272
  catch (error) {
4977
- this.#database.exec('ROLLBACK');
5273
+ this.#rollbackMutation();
4978
5274
  throw error;
4979
5275
  }
4980
5276
  }
@@ -5060,7 +5356,7 @@ export class ControlPlaneStore {
5060
5356
  if (!input.succeeded && (input.checkedTreeDigest !== undefined || input.checkedPatchDigest !== undefined)) {
5061
5357
  throw new ControlPlaneStoreError('invalid-input', 'failed source checks cannot carry successful check evidence');
5062
5358
  }
5063
- this.#database.exec('BEGIN IMMEDIATE');
5359
+ this.#beginMutation();
5064
5360
  try {
5065
5361
  const result = this.#database.prepare(`UPDATE source_plans SET status = ?, revision = revision + 1,
5066
5362
  checked_tree_digest = ?, checked_patch_digest = ?, checked_at = ?, updated_at = ?
@@ -5074,11 +5370,11 @@ export class ControlPlaneStore {
5074
5370
  }
5075
5371
  const digest = controlPlaneDigest({ planId: output.id, planDigest: output.digest, status, sourceCheck: output.sourceCheck });
5076
5372
  this.#insertReceipt({ idempotencyKey: `source-checks:${output.id}`, operation: 'source-local-checks', inputDigest: digest, result: output, createdAt: now });
5077
- this.#database.exec('COMMIT');
5373
+ this.#commitMutation();
5078
5374
  return output;
5079
5375
  }
5080
5376
  catch (error) {
5081
- this.#database.exec('ROLLBACK');
5377
+ this.#rollbackMutation();
5082
5378
  throw error;
5083
5379
  }
5084
5380
  }
@@ -5111,7 +5407,7 @@ export class ControlPlaneStore {
5111
5407
  }
5112
5408
  const commit = () => {
5113
5409
  this.#assertOwnerTaskFailureGapAdmission(plan.gapId);
5114
- this.#database.exec('BEGIN IMMEDIATE');
5410
+ this.#beginMutation();
5115
5411
  try {
5116
5412
  const result = this.#database.prepare(`UPDATE source_plans SET status = 'ready-for-human-review', revision = revision + 1, updated_at = ?
5117
5413
  WHERE id = ? AND revision = ? AND status = 'approved' AND mode = 'modify'
@@ -5124,11 +5420,11 @@ export class ControlPlaneStore {
5124
5420
  const receipt = { idempotencyKey: `source-verify-prepared:${output.id}:${output.revision}`,
5125
5421
  operation: 'source-verify-prepared', inputDigest, result: output, createdAt: now };
5126
5422
  this.#insertReceipt(receipt);
5127
- this.#database.exec('COMMIT');
5423
+ this.#commitMutation();
5128
5424
  return receipt;
5129
5425
  }
5130
5426
  catch (error) {
5131
- this.#database.exec('ROLLBACK');
5427
+ this.#rollbackMutation();
5132
5428
  throw error;
5133
5429
  }
5134
5430
  };
@@ -5188,7 +5484,7 @@ export class ControlPlaneStore {
5188
5484
  lineageDigest = controlPlaneDigest({ history: prehistory, maintenance: premaintenance, trust: input.trust, baseline: prebaseline });
5189
5485
  }
5190
5486
  return withCurrentSource(plan.gapId, () => {
5191
- this.#database.exec('BEGIN IMMEDIATE');
5487
+ this.#beginMutation();
5192
5488
  try {
5193
5489
  const linkedRows = this.#database.prepare('SELECT * FROM source_jobs WHERE plan_id = ? LIMIT 2')
5194
5490
  .all(plan.id);
@@ -5243,11 +5539,11 @@ export class ControlPlaneStore {
5243
5539
  const output = this.getSourcePlan(plan.id);
5244
5540
  const operationReceipt = { idempotencyKey: key, operation: 'start-source-release', inputDigest, result: output, createdAt: now };
5245
5541
  this.#insertReceipt(operationReceipt);
5246
- this.#database.exec('COMMIT');
5542
+ this.#commitMutation();
5247
5543
  return operationReceipt;
5248
5544
  }
5249
5545
  catch (error) {
5250
- this.#database.exec('ROLLBACK');
5546
+ this.#rollbackMutation();
5251
5547
  throw error;
5252
5548
  }
5253
5549
  });
@@ -5271,7 +5567,7 @@ export class ControlPlaneStore {
5271
5567
  await reverifySourceReleaseAuthorization(plan.releaseAuthorization, plan, input.resolveAuthorizationAuthority);
5272
5568
  return withCurrentSource(plan.gapId, () => {
5273
5569
  const now = this.#now();
5274
- this.#database.exec('BEGIN IMMEDIATE');
5570
+ this.#beginMutation();
5275
5571
  try {
5276
5572
  const current = this.getSourcePlan(plan.id);
5277
5573
  const currentExpected = expectedSourceRelease(current.status);
@@ -5291,7 +5587,7 @@ export class ControlPlaneStore {
5291
5587
  if (prior.bindingDigest !== controlPlaneDigest(releaseRequestBinding(expectedRequest))) {
5292
5588
  throw new ControlPlaneStoreError('conflict', 'durable release operation payload changed for the same phase/fence');
5293
5589
  }
5294
- this.#database.exec('COMMIT');
5590
+ this.#commitMutation();
5295
5591
  return prior;
5296
5592
  }
5297
5593
  const operationId = `release-operation-${randomUUID()}`;
@@ -5301,11 +5597,11 @@ export class ControlPlaneStore {
5301
5597
  operation_id, binding_digest, request_digest, request_json, status, receipt_digest, receipt_json, created_at, completed_at, applied_at)
5302
5598
  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
5599
  const operation = this.getSourceReleaseOperation(operationId);
5304
- this.#database.exec('COMMIT');
5600
+ this.#commitMutation();
5305
5601
  return operation;
5306
5602
  }
5307
5603
  catch (error) {
5308
- this.#database.exec('ROLLBACK');
5604
+ this.#rollbackMutation();
5309
5605
  throw error;
5310
5606
  }
5311
5607
  });
@@ -5370,13 +5666,13 @@ export class ControlPlaneStore {
5370
5666
  withCurrentSource(plan.gapId, () => { });
5371
5667
  await reverifySourceReleaseAuthorization(operation.request.authorization, plan, input.resolveAuthorizationAuthority);
5372
5668
  const claimed = withCurrentSource(plan.gapId, () => {
5373
- this.#database.exec('BEGIN IMMEDIATE');
5669
+ this.#beginMutation();
5374
5670
  try {
5375
5671
  const current = this.getSourcePlan(plan.id);
5376
5672
  const currentOperation = this.getSourceReleaseOperation(operation.operationId);
5377
5673
  assertCurrent(current, currentOperation);
5378
5674
  if (currentOperation.receipt !== undefined) {
5379
- this.#database.exec('COMMIT');
5675
+ this.#commitMutation();
5380
5676
  return false;
5381
5677
  }
5382
5678
  const dispatch = this.#database.prepare('SELECT * FROM source_release_dispatches WHERE operation_id = ?').get(operation.operationId);
@@ -5386,11 +5682,11 @@ export class ControlPlaneStore {
5386
5682
  }
5387
5683
  this.#database.prepare(`INSERT INTO source_release_dispatches (operation_id, status, claimed_at, completed_at)
5388
5684
  VALUES (?, 'claimed', ?, NULL)`).run(operation.operationId, this.#now());
5389
- this.#database.exec('COMMIT');
5685
+ this.#commitMutation();
5390
5686
  return true;
5391
5687
  }
5392
5688
  catch (error) {
5393
- this.#database.exec('ROLLBACK');
5689
+ this.#rollbackMutation();
5394
5690
  throw error;
5395
5691
  }
5396
5692
  });
@@ -5399,7 +5695,7 @@ export class ControlPlaneStore {
5399
5695
  const receipt = await input.execute(operation.request);
5400
5696
  await input.resolveAuthority(receipt).verify(receipt, plan, operation.request);
5401
5697
  return withCurrentSource(plan.gapId, () => {
5402
- this.#database.exec('BEGIN IMMEDIATE');
5698
+ this.#beginMutation();
5403
5699
  try {
5404
5700
  const current = this.getSourcePlan(plan.id);
5405
5701
  const currentOperation = this.getSourceReleaseOperation(operation.operationId);
@@ -5417,11 +5713,11 @@ export class ControlPlaneStore {
5417
5713
  WHERE operation_id = ? AND status = 'claimed'`).run(now, operation.operationId);
5418
5714
  if (Number(completed.changes) !== 1)
5419
5715
  throw new ControlPlaneStoreError('conflict', 'source release completion lost its dispatch claim');
5420
- this.#database.exec('COMMIT');
5716
+ this.#commitMutation();
5421
5717
  return receipt;
5422
5718
  }
5423
5719
  catch (error) {
5424
- this.#database.exec('ROLLBACK');
5720
+ this.#rollbackMutation();
5425
5721
  throw error;
5426
5722
  }
5427
5723
  });
@@ -5455,14 +5751,14 @@ export class ControlPlaneStore {
5455
5751
  await reverifySourceReleaseAuthorization(operation.request.authorization, plan, input.resolveAuthorizationAuthority);
5456
5752
  await input.resolveAuthority(input.receipt).verify(input.receipt, plan, operation.request);
5457
5753
  return withCurrentSource(plan.gapId, () => {
5458
- this.#database.exec('BEGIN IMMEDIATE');
5754
+ this.#beginMutation();
5459
5755
  try {
5460
5756
  const current = this.getSourcePlan(plan.id), currentOperation = this.getSourceReleaseOperation(operation.operationId);
5461
5757
  assertCurrent(current, currentOperation);
5462
5758
  if (currentOperation.receipt !== undefined) {
5463
5759
  if (controlPlaneDigest(currentOperation.receipt) !== controlPlaneDigest(input.receipt))
5464
5760
  throw new ControlPlaneStoreError('conflict', 'source release receipt differs from completed operation');
5465
- this.#database.exec('COMMIT');
5761
+ this.#commitMutation();
5466
5762
  return currentOperation.receipt;
5467
5763
  }
5468
5764
  const dispatch = this.#database.prepare('SELECT * FROM source_release_dispatches WHERE operation_id = ?').get(operation.operationId);
@@ -5479,11 +5775,11 @@ export class ControlPlaneStore {
5479
5775
  if (Number(completed.changes) !== 1)
5480
5776
  throw new ControlPlaneStoreError('conflict', 'source release receipt lost its dispatch claim');
5481
5777
  }
5482
- this.#database.exec('COMMIT');
5778
+ this.#commitMutation();
5483
5779
  return input.receipt;
5484
5780
  }
5485
5781
  catch (error) {
5486
- this.#database.exec('ROLLBACK');
5782
+ this.#rollbackMutation();
5487
5783
  throw error;
5488
5784
  }
5489
5785
  });
@@ -5549,7 +5845,7 @@ export class ControlPlaneStore {
5549
5845
  }
5550
5846
  const now = this.#now();
5551
5847
  return withCurrentSource(plan.gapId, () => {
5552
- this.#database.exec('BEGIN IMMEDIATE');
5848
+ this.#beginMutation();
5553
5849
  try {
5554
5850
  const current = this.getSourcePlan(plan.id);
5555
5851
  const currentExpected = expectedSourceRelease(current.status);
@@ -5589,18 +5885,18 @@ export class ControlPlaneStore {
5589
5885
  }
5590
5886
  const operationReceipt = { idempotencyKey: key, operation: 'source-release', inputDigest, result: output, createdAt: now };
5591
5887
  this.#insertReceipt(operationReceipt);
5592
- this.#database.exec('COMMIT');
5888
+ this.#commitMutation();
5593
5889
  return operationReceipt;
5594
5890
  }
5595
5891
  catch (error) {
5596
- this.#database.exec('ROLLBACK');
5892
+ this.#rollbackMutation();
5597
5893
  throw error;
5598
5894
  }
5599
5895
  });
5600
5896
  }
5601
5897
  async prepareSourcePublishReconciliation(input) {
5602
5898
  const now = this.#now();
5603
- this.#database.exec('BEGIN IMMEDIATE');
5899
+ this.#beginMutation();
5604
5900
  try {
5605
5901
  const plan = this.getSourcePlan(input.planId);
5606
5902
  if (plan.status !== 'publish-ambiguous' || plan.revision !== input.expectedRevision || plan.release?.fence !== input.expectedFence) {
@@ -5643,18 +5939,18 @@ export class ControlPlaneStore {
5643
5939
  const prior = sourcePublishReconciliationFromRow(priorRow);
5644
5940
  if (prior.bindingDigest !== bindingDigest)
5645
5941
  throw new ControlPlaneStoreError('conflict', 'durable publish reconciliation payload changed');
5646
- this.#database.exec('COMMIT');
5942
+ this.#commitMutation();
5647
5943
  return prior;
5648
5944
  }
5649
5945
  this.#database.prepare(`INSERT INTO source_publish_reconciliations (plan_id, release_id, release_fence, attempt, operation_id,
5650
5946
  binding_digest, request_digest, request_json, status, receipt_digest, receipt_json, created_at, completed_at, applied_at)
5651
5947
  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
5948
  const operation = this.getSourcePublishReconciliationOperation(operationId);
5653
- this.#database.exec('COMMIT');
5949
+ this.#commitMutation();
5654
5950
  return operation;
5655
5951
  }
5656
5952
  catch (error) {
5657
- this.#database.exec('ROLLBACK');
5953
+ this.#rollbackMutation();
5658
5954
  throw error;
5659
5955
  }
5660
5956
  }
@@ -5666,7 +5962,7 @@ export class ControlPlaneStore {
5666
5962
  }
5667
5963
  async runSourcePublishReconciliation(input) {
5668
5964
  try {
5669
- this.#database.exec('BEGIN IMMEDIATE');
5965
+ this.#beginMutation();
5670
5966
  }
5671
5967
  catch {
5672
5968
  throw new ControlPlaneStoreError('conflict', 'publish reconciliation single-flight is held');
@@ -5678,7 +5974,7 @@ export class ControlPlaneStore {
5678
5974
  || operation.fence !== input.expectedFence)
5679
5975
  throw new ControlPlaneStoreError('conflict', 'publish reconciliation lost its revision/fence');
5680
5976
  if (operation.receipt !== undefined) {
5681
- this.#database.exec('COMMIT');
5977
+ this.#commitMutation();
5682
5978
  return operation.receipt;
5683
5979
  }
5684
5980
  await reverifySourceReleaseAuthorization(operation.request.authorization, plan, input.resolveAuthorizationAuthority);
@@ -5689,11 +5985,11 @@ export class ControlPlaneStore {
5689
5985
  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
5986
  if (Number(result.changes) !== 1)
5691
5987
  throw new ControlPlaneStoreError('conflict', 'publish reconciliation completion lost single-flight');
5692
- this.#database.exec('COMMIT');
5988
+ this.#commitMutation();
5693
5989
  return receipt;
5694
5990
  }
5695
5991
  catch (error) {
5696
- this.#database.exec('ROLLBACK');
5992
+ this.#rollbackMutation();
5697
5993
  throw error;
5698
5994
  }
5699
5995
  }
@@ -5726,7 +6022,7 @@ export class ControlPlaneStore {
5726
6022
  const verified = await input.resolveAuthority(input.receipt).verify(input.receipt, plan, operation.request);
5727
6023
  const outcome = verified.evidence.outcome;
5728
6024
  const now = this.#now();
5729
- this.#database.exec('BEGIN IMMEDIATE');
6025
+ this.#beginMutation();
5730
6026
  try {
5731
6027
  let nextStatus = 'publish-ambiguous';
5732
6028
  let nextFence = input.expectedFence;
@@ -5759,11 +6055,11 @@ export class ControlPlaneStore {
5759
6055
  const output = this.getSourcePlan(plan.id);
5760
6056
  const operationReceipt = { idempotencyKey: input.idempotencyKey, operation: 'reconcile-source-publish', inputDigest, result: output, createdAt: now };
5761
6057
  this.#insertReceipt(operationReceipt);
5762
- this.#database.exec('COMMIT');
6058
+ this.#commitMutation();
5763
6059
  return operationReceipt;
5764
6060
  }
5765
6061
  catch (error) {
5766
- this.#database.exec('ROLLBACK');
6062
+ this.#rollbackMutation();
5767
6063
  throw error;
5768
6064
  }
5769
6065
  }