@straiffi/archon 1.10.8 → 1.10.10

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 (169) hide show
  1. package/dist/client/assets/ActivityView-CRkM5TeN.js +1 -0
  2. package/dist/client/assets/GitHubActionsDialog-KFUiR00H.js +1 -0
  3. package/dist/client/assets/VerificationDialog-BIDttgiW.js +4 -0
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-CzUKSWJZ.js → architectureDiagram-3BPJPVTR-u0PSZlNP.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-DoNm6BnS.js → blockDiagram-GPEHLZMM-DFXLHRIk.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-C7ocs3wQ.js → c4Diagram-AAUBKEIU-Dvic8tCt.js} +1 -1
  7. package/dist/client/assets/channel-Dhv_uSKx.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-DUmaxCq8.js → chunk-2J33WTMH-t5CxhRXJ.js} +1 -1
  9. package/dist/client/assets/{chunk-3OPIFGDE-CX2MPsMQ.js → chunk-3OPIFGDE-YHT1dP8Q.js} +1 -1
  10. package/dist/client/assets/{chunk-5ZQYHXKU-B103kU_y.js → chunk-5ZQYHXKU-DcxuI7lb.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-BmLJ4h5X.js → chunk-727SXJPM-D1F6DmrT.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-Cnv1b4Vd.js → chunk-AQP2D5EJ-BIx6Ohbk.js} +1 -1
  13. package/dist/client/assets/{chunk-CSCIHK7Q-CkIUhpY9.js → chunk-CSCIHK7Q-B355h6HA.js} +1 -1
  14. package/dist/client/assets/{chunk-KSCS5N6A-wmkCuZ5U.js → chunk-KSCS5N6A-CLHGYOLQ.js} +1 -1
  15. package/dist/client/assets/{chunk-L5ZTLDWV-D9AdL5mv.js → chunk-L5ZTLDWV-DFLYcMx8.js} +1 -1
  16. package/dist/client/assets/{chunk-LZXEDZCA-rMyTvR3J.js → chunk-LZXEDZCA-DtljFujX.js} +2 -2
  17. package/dist/client/assets/{chunk-ND2GUHAM-19HTozu8.js → chunk-ND2GUHAM-CIuUQ86K.js} +1 -1
  18. package/dist/client/assets/{chunk-NZK2D7GU-DBHgwKcS.js → chunk-NZK2D7GU-CIwqlxek.js} +1 -1
  19. package/dist/client/assets/{chunk-O5CBEL6O-4WpSG06s.js → chunk-O5CBEL6O-W42BEPu4.js} +1 -1
  20. package/dist/client/assets/{chunk-WU5MYG2G-loqCl7ki.js → chunk-WU5MYG2G-C_zuaNxV.js} +1 -1
  21. package/dist/client/assets/classDiagram-4FO5ZUOK-9XKvSSLq.js +1 -0
  22. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Dbr5_49Y.js +1 -0
  23. package/dist/client/assets/{dagre-BM42HDAG-FVpIr0qZ.js → dagre-BM42HDAG-UKfIUJTP.js} +1 -1
  24. package/dist/client/assets/{diagram-2AECGRRQ-Dgj8FaDO.js → diagram-2AECGRRQ-Xn6yojqq.js} +1 -1
  25. package/dist/client/assets/{diagram-5GNKFQAL-DNpYKz_a.js → diagram-5GNKFQAL-BzpY1ZEl.js} +1 -1
  26. package/dist/client/assets/{diagram-KO2AKTUF-N7R7efvN.js → diagram-KO2AKTUF-Dyrvzvsg.js} +1 -1
  27. package/dist/client/assets/{diagram-LMA3HP47-hoOvd_tG.js → diagram-LMA3HP47-AqImKwAr.js} +1 -1
  28. package/dist/client/assets/{diagram-OG6HWLK6-ze25ZIy6.js → diagram-OG6HWLK6-CaQQ46mi.js} +1 -1
  29. package/dist/client/assets/{erDiagram-TEJ5UH35-780zvNNZ.js → erDiagram-TEJ5UH35-Dkw4Aa5u.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-I6XJVG4X-x4UB_M_4.js → flowDiagram-I6XJVG4X-CJTuGX9h.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-6RSMTGT7-B5Wl0y91.js → ganttDiagram-6RSMTGT7-CidgOdS-.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CZtVHpBH.js → gitGraphDiagram-PVQCEYII-Bj02mIQj.js} +1 -1
  33. package/dist/client/assets/index-ClRB9YWJ.css +2 -0
  34. package/dist/client/assets/index-DBMdsTvy.js +276 -0
  35. package/dist/client/assets/{infoDiagram-5YYISTIA-BOzLwsnA.js → infoDiagram-5YYISTIA-GwJWfo_r.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-ButBhNg6.js → ishikawaDiagram-YF4QCWOH-rbrlWbUn.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-JHISSGLW-CO0Db0Gn.js → journeyDiagram-JHISSGLW-BLi_uzQM.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-UN3LZRKU-B6KT8yln.js → kanban-definition-UN3LZRKU-ynlywm-_.js} +1 -1
  39. package/dist/client/assets/{line-Cv-WaOjt.js → line-BNyTZBB4.js} +1 -1
  40. package/dist/client/assets/{mermaid-parser.core-DgJy4Qlx.js → mermaid-parser.core-CzUOsZAD.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-YpvipwZo.js → mermaid.core-K0QR6SzN.js} +3 -3
  42. package/dist/client/assets/{mindmap-definition-RKZ34NQL-DGgtT98Y.js → mindmap-definition-RKZ34NQL-BKcgqXm0.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-4H26LBE5-CgqnLewO.js → pieDiagram-4H26LBE5-Bjx0lvDI.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Ci_0SzdR.js → quadrantDiagram-W4KKPZXB-CmUlgyUv.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-4Y6WPE33-wJl0DP74.js → requirementDiagram-4Y6WPE33-Dj2mzg1y.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CE7aDnzF.js → sankeyDiagram-5OEKKPKP-DC--H6qf.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-CW0bF1i6.js → sequenceDiagram-3UESZ5HK-BkNrt6Ra.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-AJRCARHV-Ccl0RXsJ.js → stateDiagram-AJRCARHV-BuxxRu1Y.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Cyo85GPN.js +1 -0
  50. package/dist/client/assets/{timeline-definition-PNZ67QCA-TuF6SUnb.js → timeline-definition-PNZ67QCA-Db3pCVSp.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-CIIHVFJN-D8OvSROI.js → vennDiagram-CIIHVFJN-BgBSGThQ.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DXW3R5NJ.js → wardleyDiagram-YWT4CUSO-BJhPdjYS.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-2RQKCTM6-qdiUQDW9.js → xychartDiagram-2RQKCTM6-BY4Ra0kX.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/server/db.js +341 -4
  56. package/dist/server/db.js.map +1 -1
  57. package/dist/server/index.js +197 -270
  58. package/dist/server/index.js.map +1 -1
  59. package/dist/server/lib/agentRuns.js +14 -1
  60. package/dist/server/lib/agentRuns.js.map +1 -1
  61. package/dist/server/lib/automationLoopProjection.js +55 -12
  62. package/dist/server/lib/automationLoopProjection.js.map +1 -1
  63. package/dist/server/lib/automationProgression.js +2 -5
  64. package/dist/server/lib/automationProgression.js.map +1 -1
  65. package/dist/server/lib/automationResumeDecision.js +19 -1
  66. package/dist/server/lib/automationResumeDecision.js.map +1 -1
  67. package/dist/server/lib/boundedProcess.js +129 -0
  68. package/dist/server/lib/boundedProcess.js.map +1 -0
  69. package/dist/server/lib/buildControlEligibility.js +4 -0
  70. package/dist/server/lib/buildControlEligibility.js.map +1 -0
  71. package/dist/server/lib/buildExecutionAdmission.js +76 -0
  72. package/dist/server/lib/buildExecutionAdmission.js.map +1 -0
  73. package/dist/server/lib/buildSequenceResume.js +79 -0
  74. package/dist/server/lib/buildSequenceResume.js.map +1 -0
  75. package/dist/server/lib/buildWorkload.js +11 -0
  76. package/dist/server/lib/buildWorkload.js.map +1 -0
  77. package/dist/server/lib/bundleReviewOperations.js +7 -0
  78. package/dist/server/lib/bundleReviewOperations.js.map +1 -1
  79. package/dist/server/lib/chats.js +12 -0
  80. package/dist/server/lib/chats.js.map +1 -1
  81. package/dist/server/lib/checkAutoFixDetails.js +36 -1
  82. package/dist/server/lib/checkAutoFixDetails.js.map +1 -1
  83. package/dist/server/lib/executionRetry.js +13 -1
  84. package/dist/server/lib/executionRetry.js.map +1 -1
  85. package/dist/server/lib/executionRunAuthority.js +2 -2
  86. package/dist/server/lib/executionRunAuthority.js.map +1 -1
  87. package/dist/server/lib/executionTargets.js +7 -4
  88. package/dist/server/lib/executionTargets.js.map +1 -1
  89. package/dist/server/lib/executionTimeline.js +8 -13
  90. package/dist/server/lib/executionTimeline.js.map +1 -1
  91. package/dist/server/lib/executionTimelineHierarchy.js +83 -0
  92. package/dist/server/lib/executionTimelineHierarchy.js.map +1 -0
  93. package/dist/server/lib/failedBuildSequenceRecovery.js +133 -0
  94. package/dist/server/lib/failedBuildSequenceRecovery.js.map +1 -0
  95. package/dist/server/lib/git.js +89 -48
  96. package/dist/server/lib/git.js.map +1 -1
  97. package/dist/server/lib/hostRunSession.js +62 -0
  98. package/dist/server/lib/hostRunSession.js.map +1 -0
  99. package/dist/server/lib/hostServiceRecovery.js +159 -0
  100. package/dist/server/lib/hostServiceRecovery.js.map +1 -0
  101. package/dist/server/lib/hostServiceRunProvider.js +32 -0
  102. package/dist/server/lib/hostServiceRunProvider.js.map +1 -0
  103. package/dist/server/lib/integrations/github.js +49 -105
  104. package/dist/server/lib/integrations/github.js.map +1 -1
  105. package/dist/server/lib/mobileAccess.js +6 -2
  106. package/dist/server/lib/mobileAccess.js.map +1 -1
  107. package/dist/server/lib/projectAutoConfig.js +5 -2
  108. package/dist/server/lib/projectAutoConfig.js.map +1 -1
  109. package/dist/server/lib/projectTargets.js +8 -2
  110. package/dist/server/lib/projectTargets.js.map +1 -1
  111. package/dist/server/lib/projects.js +25 -1
  112. package/dist/server/lib/projects.js.map +1 -1
  113. package/dist/server/lib/reviewFindingReconciliation.js +9 -2
  114. package/dist/server/lib/reviewFindingReconciliation.js.map +1 -1
  115. package/dist/server/lib/reviewFindings.js +113 -23
  116. package/dist/server/lib/reviewFindings.js.map +1 -1
  117. package/dist/server/lib/reviewRepairAssessmentRecords.js +41 -0
  118. package/dist/server/lib/reviewRepairAssessmentRecords.js.map +1 -0
  119. package/dist/server/lib/reviewRepairCompletion.js +78 -18
  120. package/dist/server/lib/reviewRepairCompletion.js.map +1 -1
  121. package/dist/server/lib/reviewRepairIterationRecords.js +4 -2
  122. package/dist/server/lib/reviewRepairIterationRecords.js.map +1 -1
  123. package/dist/server/lib/reviewRepairIterations.js +8 -2
  124. package/dist/server/lib/reviewRepairIterations.js.map +1 -1
  125. package/dist/server/lib/reviewRepairProgress.js +177 -0
  126. package/dist/server/lib/reviewRepairProgress.js.map +1 -0
  127. package/dist/server/lib/reviewRepairReconciliation.js +119 -140
  128. package/dist/server/lib/reviewRepairReconciliation.js.map +1 -1
  129. package/dist/server/lib/reviewRepairResumeOverride.js +55 -0
  130. package/dist/server/lib/reviewRepairResumeOverride.js.map +1 -0
  131. package/dist/server/lib/run.js +523 -504
  132. package/dist/server/lib/run.js.map +1 -1
  133. package/dist/server/lib/runProcessIdentity.js +71 -0
  134. package/dist/server/lib/runProcessIdentity.js.map +1 -0
  135. package/dist/server/lib/runProcesses.js +261 -0
  136. package/dist/server/lib/runProcesses.js.map +1 -0
  137. package/dist/server/lib/runServices.js +221 -0
  138. package/dist/server/lib/runServices.js.map +1 -0
  139. package/dist/server/lib/runSessionLifecycle.js +68 -0
  140. package/dist/server/lib/runSessionLifecycle.js.map +1 -0
  141. package/dist/server/lib/runStatus.js +7 -0
  142. package/dist/server/lib/runStatus.js.map +1 -0
  143. package/dist/server/lib/serviceRuns.js +273 -16
  144. package/dist/server/lib/serviceRuns.js.map +1 -1
  145. package/dist/server/lib/ticketAutomation.js +4 -27
  146. package/dist/server/lib/ticketAutomation.js.map +1 -1
  147. package/dist/server/lib/ticketRunOperations.js +103 -34
  148. package/dist/server/lib/ticketRunOperations.js.map +1 -1
  149. package/dist/server/lib/tickets.js +46 -21
  150. package/dist/server/lib/tickets.js.map +1 -1
  151. package/dist/server/lib/types.js.map +1 -1
  152. package/dist/server/workers/build.js +44 -69
  153. package/dist/server/workers/build.js.map +1 -1
  154. package/dist/server/workers/plan.js +0 -7
  155. package/dist/server/workers/plan.js.map +1 -1
  156. package/dist/server/workers/review.js +95 -146
  157. package/dist/server/workers/review.js.map +1 -1
  158. package/package.json +1 -1
  159. package/dist/client/assets/ActivityView-PGEex8sS.js +0 -1
  160. package/dist/client/assets/GitHubActionsDialog-CwsZRevc.js +0 -1
  161. package/dist/client/assets/VerificationDialog-BYE1M9T2.js +0 -4
  162. package/dist/client/assets/channel-b-KoJfeL.js +0 -1
  163. package/dist/client/assets/classDiagram-4FO5ZUOK-2c5Acffy.js +0 -1
  164. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-ClrW9LEB.js +0 -1
  165. package/dist/client/assets/index-BXsDja_y.css +0 -2
  166. package/dist/client/assets/index-Cy3ZoenG.js +0 -276
  167. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-k65NSsdn.js +0 -1
  168. package/dist/server/lib/automationDeliveryDecision.js +0 -34
  169. package/dist/server/lib/automationDeliveryDecision.js.map +0 -1
package/dist/server/db.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import Database from 'better-sqlite3';
2
- import { createHash } from 'crypto';
2
+ import { createHash, randomUUID } from 'crypto';
3
3
  import { parseCheckDefinitionsJsonStrict, parseLegacyTestCommandsJsonStrict, projectCheckDefinitionsToTestCommands, } from './lib/checkDefinitions.js';
4
4
  import { ensureParentDirectory, resolveArchonDbPath } from './lib/runtimePaths.js';
5
5
  import { TRANSCRIPT_RUN_TEXT_LIMIT_BYTES, truncateUtf8 } from './lib/transcriptRuns.js';
@@ -183,6 +183,46 @@ ensureColumn('projects', 'spec_aware_code_review_enabled', 'INTEGER NOT NULL DEF
183
183
  ensureColumn('projects', 'deletion_started_at', 'DATETIME');
184
184
  ensureColumn('projects', 'deletion_claim_id', 'TEXT');
185
185
  ensureColumn('projects', 'deletion_cleanup_started_at', 'DATETIME');
186
+ ensureColumn('projects', 'run_services', 'TEXT');
187
+ db.transaction(() => {
188
+ const projects = db.prepare('SELECT id, run_services FROM projects WHERE run_services IS NOT NULL').all();
189
+ const update = db.prepare('UPDATE projects SET run_services = ? WHERE id = ?');
190
+ for (const project of projects) {
191
+ let services;
192
+ try {
193
+ services = JSON.parse(project.run_services);
194
+ }
195
+ catch {
196
+ continue;
197
+ }
198
+ // Leave malformed legacy configuration intact rather than partially rewriting it.
199
+ if (!Array.isArray(services) || services.some(service => !service || typeof service !== 'object' || Array.isArray(service))) {
200
+ continue;
201
+ }
202
+ const entries = services;
203
+ const validId = (id) => typeof id === 'string' && id.trim().length > 0 && id === id.trim();
204
+ const reserved = new Set(entries.map(service => service.id).filter(validId));
205
+ const seen = new Set();
206
+ let changed = false;
207
+ for (const service of entries) {
208
+ if (validId(service.id) && !seen.has(service.id)) {
209
+ seen.add(service.id);
210
+ continue;
211
+ }
212
+ let id;
213
+ do {
214
+ id = randomUUID();
215
+ } while (reserved.has(id));
216
+ reserved.add(id);
217
+ seen.add(id);
218
+ service.id = id;
219
+ changed = true;
220
+ }
221
+ if (changed) {
222
+ update.run(JSON.stringify(entries), project.id);
223
+ }
224
+ }
225
+ }).immediate();
186
226
  const backfillAgentModelMaps = () => {
187
227
  db.transaction(() => {
188
228
  db.exec(`
@@ -2892,6 +2932,191 @@ ensureColumn('execution_environments', 'operation_claimed_at', 'DATETIME');
2892
2932
  ensureColumn('execution_environment_run_bindings', 'execution_profile_fingerprint', 'TEXT');
2893
2933
  ensureColumn('execution_environment_run_bindings', 'authority_json', 'TEXT');
2894
2934
  ensureColumn('execution_environment_run_bindings', 'placement_json', 'TEXT');
2935
+ db.exec(`
2936
+ CREATE VIEW IF NOT EXISTS active_project_service_authorities AS
2937
+ SELECT run.id AS execution_run_id, run.project_id
2938
+ FROM execution_runs run
2939
+ WHERE run.kind = 'service' AND (
2940
+ run.status NOT IN ('completed', 'failed', 'cancelled', 'interrupted')
2941
+ OR EXISTS (SELECT 1 FROM service_run_details details
2942
+ WHERE details.execution_run_id = run.id AND details.completed_at IS NULL)
2943
+ OR EXISTS (SELECT 1 FROM execution_environment_run_bindings binding
2944
+ WHERE binding.execution_run_id = run.id AND binding.relationship = 'execution'
2945
+ AND binding.released_at IS NULL)
2946
+ );
2947
+
2948
+ CREATE TRIGGER IF NOT EXISTS admit_project_service_run
2949
+ BEFORE INSERT ON execution_runs WHEN NEW.kind = 'service'
2950
+ BEGIN
2951
+ SELECT RAISE(ABORT, 'Project is being deleted') WHERE EXISTS (
2952
+ SELECT 1 FROM projects WHERE id = NEW.project_id AND deletion_started_at IS NOT NULL);
2953
+ SELECT RAISE(ABORT, 'Project already has active service authority') WHERE EXISTS (
2954
+ SELECT 1 FROM active_project_service_authorities WHERE project_id = NEW.project_id);
2955
+ END;
2956
+
2957
+ CREATE TRIGGER IF NOT EXISTS admit_project_service_details
2958
+ BEFORE INSERT ON service_run_details
2959
+ BEGIN
2960
+ SELECT RAISE(ABORT, 'New service authority must be unfinished') WHERE NEW.completed_at IS NOT NULL;
2961
+ SELECT RAISE(ABORT, 'Service profile fingerprint does not match') WHERE NOT EXISTS (
2962
+ SELECT 1 FROM execution_profile_revisions WHERE id = NEW.execution_profile_revision_id
2963
+ AND project_id = NEW.project_id AND fingerprint = NEW.execution_profile_fingerprint);
2964
+ SELECT RAISE(ABORT, 'Service details require an active service run') WHERE NOT EXISTS (
2965
+ SELECT 1 FROM execution_runs WHERE id = NEW.execution_run_id AND project_id = NEW.project_id
2966
+ AND kind = 'service' AND status NOT IN ('completed', 'failed', 'cancelled', 'interrupted'));
2967
+ SELECT RAISE(ABORT, 'Project is being deleted') WHERE EXISTS (
2968
+ SELECT 1 FROM projects WHERE id = NEW.project_id AND deletion_started_at IS NOT NULL);
2969
+ SELECT RAISE(ABORT, 'Project already has active service authority') WHERE EXISTS (
2970
+ SELECT 1 FROM active_project_service_authorities WHERE project_id = NEW.project_id
2971
+ AND execution_run_id <> NEW.execution_run_id);
2972
+ END;
2973
+
2974
+ CREATE TRIGGER IF NOT EXISTS preserve_service_detail_identity
2975
+ BEFORE UPDATE ON service_run_details
2976
+ WHEN NEW.execution_run_id IS NOT OLD.execution_run_id OR NEW.project_id IS NOT OLD.project_id
2977
+ OR NEW.context_key IS NOT OLD.context_key OR NEW.target_kind IS NOT OLD.target_kind
2978
+ OR NEW.target_key IS NOT OLD.target_key OR NEW.target_workspace_path IS NOT OLD.target_workspace_path
2979
+ OR NEW.execution_profile_revision_id IS NOT OLD.execution_profile_revision_id
2980
+ OR NEW.execution_profile_fingerprint IS NOT OLD.execution_profile_fingerprint
2981
+ OR NEW.service_snapshot_json IS NOT OLD.service_snapshot_json
2982
+ OR (OLD.completed_at IS NOT NULL AND NEW.completed_at IS NOT OLD.completed_at)
2983
+ BEGIN
2984
+ SELECT RAISE(ABORT, 'Service run authority is immutable');
2985
+ END;
2986
+
2987
+ CREATE TRIGGER IF NOT EXISTS protect_service_run_identity
2988
+ BEFORE UPDATE OF id, kind, project_id ON execution_runs
2989
+ WHEN (OLD.kind = 'service' OR NEW.kind = 'service') AND (
2990
+ NEW.id IS NOT OLD.id OR NEW.kind IS NOT OLD.kind OR NEW.project_id IS NOT OLD.project_id)
2991
+ BEGIN
2992
+ SELECT RAISE(ABORT, 'Service run authority is immutable');
2993
+ END;
2994
+
2995
+ CREATE TRIGGER IF NOT EXISTS protect_service_run_reactivation
2996
+ BEFORE UPDATE OF status ON execution_runs
2997
+ WHEN OLD.kind = 'service' AND OLD.status IN ('completed', 'failed', 'cancelled', 'interrupted')
2998
+ AND NEW.status NOT IN ('completed', 'failed', 'cancelled', 'interrupted')
2999
+ BEGIN
3000
+ SELECT RAISE(ABORT, 'Terminal service run cannot be reactivated');
3001
+ END;
3002
+
3003
+ CREATE TRIGGER IF NOT EXISTS admit_service_execution_binding
3004
+ BEFORE INSERT ON execution_environment_run_bindings
3005
+ WHEN NEW.relationship = 'execution' AND NEW.released_at IS NULL AND EXISTS (
3006
+ SELECT 1 FROM execution_runs WHERE id = NEW.execution_run_id AND kind = 'service')
3007
+ BEGIN
3008
+ SELECT RAISE(ABORT, 'Service binding requires unfinished authority') WHERE NOT EXISTS (
3009
+ SELECT 1 FROM service_run_details details JOIN execution_runs run ON run.id = details.execution_run_id
3010
+ JOIN projects project ON project.id = details.project_id
3011
+ WHERE details.execution_run_id = NEW.execution_run_id AND details.project_id = NEW.project_id
3012
+ AND details.completed_at IS NULL AND project.deletion_started_at IS NULL
3013
+ AND run.status NOT IN ('completed', 'failed', 'cancelled', 'interrupted'));
3014
+ SELECT RAISE(ABORT, 'Project already has active service authority') WHERE EXISTS (
3015
+ SELECT 1 FROM active_project_service_authorities WHERE project_id = NEW.project_id
3016
+ AND execution_run_id <> NEW.execution_run_id);
3017
+ END;
3018
+
3019
+ CREATE TRIGGER IF NOT EXISTS protect_service_binding_reactivation
3020
+ BEFORE UPDATE OF released_at ON execution_environment_run_bindings
3021
+ WHEN OLD.relationship = 'execution' AND OLD.released_at IS NOT NULL AND NEW.released_at IS NULL
3022
+ AND EXISTS (SELECT 1 FROM execution_runs WHERE id = OLD.execution_run_id AND kind = 'service')
3023
+ BEGIN
3024
+ SELECT RAISE(ABORT, 'Released service binding cannot be reactivated');
3025
+ END;
3026
+
3027
+ CREATE TRIGGER IF NOT EXISTS protect_service_binding_deletion
3028
+ BEFORE DELETE ON execution_environment_run_bindings
3029
+ WHEN OLD.relationship = 'execution' AND OLD.released_at IS NULL
3030
+ AND EXISTS (SELECT 1 FROM execution_runs WHERE id = OLD.execution_run_id AND kind = 'service')
3031
+ BEGIN
3032
+ SELECT RAISE(ABORT, 'Service execution binding is unreleased');
3033
+ END;
3034
+
3035
+ CREATE TRIGGER IF NOT EXISTS protect_service_completion
3036
+ BEFORE UPDATE OF completed_at ON service_run_details
3037
+ WHEN NEW.completed_at IS NOT NULL AND EXISTS (
3038
+ SELECT 1 FROM execution_environment_run_bindings WHERE execution_run_id = OLD.execution_run_id
3039
+ AND relationship = 'execution' AND released_at IS NULL)
3040
+ BEGIN
3041
+ SELECT RAISE(ABORT, 'Service execution binding is unreleased');
3042
+ END;
3043
+
3044
+ CREATE TRIGGER IF NOT EXISTS require_terminal_service_completion
3045
+ BEFORE UPDATE OF completed_at ON service_run_details
3046
+ WHEN NEW.completed_at IS NOT NULL AND EXISTS (
3047
+ SELECT 1 FROM execution_runs WHERE id = OLD.execution_run_id
3048
+ AND status NOT IN ('completed', 'failed', 'cancelled', 'interrupted'))
3049
+ BEGIN
3050
+ SELECT RAISE(ABORT, 'Service run must be terminal before completing details');
3051
+ END;
3052
+
3053
+ CREATE TRIGGER IF NOT EXISTS protect_service_detail_deletion
3054
+ BEFORE DELETE ON service_run_details
3055
+ WHEN EXISTS (SELECT 1 FROM active_project_service_authorities WHERE execution_run_id = OLD.execution_run_id)
3056
+ BEGIN
3057
+ SELECT RAISE(ABORT, 'Service run authority is active');
3058
+ END;
3059
+
3060
+ CREATE TRIGGER IF NOT EXISTS protect_service_run_deletion
3061
+ BEFORE DELETE ON execution_runs
3062
+ WHEN EXISTS (SELECT 1 FROM active_project_service_authorities WHERE execution_run_id = OLD.id)
3063
+ BEGIN
3064
+ SELECT RAISE(ABORT, 'Service run authority is active');
3065
+ END;
3066
+
3067
+ CREATE TRIGGER IF NOT EXISTS protect_service_ticket_deletion
3068
+ BEFORE DELETE ON tickets
3069
+ WHEN EXISTS (
3070
+ SELECT 1 FROM active_project_service_authorities active
3071
+ JOIN execution_runs run ON run.id = active.execution_run_id
3072
+ WHERE run.source_kind = 'ticket' AND run.source_id = OLD.id)
3073
+ BEGIN
3074
+ SELECT RAISE(ABORT, 'Ticket has active service authority');
3075
+ END;
3076
+
3077
+ CREATE TRIGGER IF NOT EXISTS protect_service_ticket_target
3078
+ BEFORE UPDATE OF project_id, worktree_bundle_id ON tickets
3079
+ WHEN (NEW.project_id IS NOT OLD.project_id OR NEW.worktree_bundle_id IS NOT OLD.worktree_bundle_id)
3080
+ AND EXISTS (
3081
+ SELECT 1 FROM active_project_service_authorities active
3082
+ JOIN execution_runs run ON run.id = active.execution_run_id
3083
+ WHERE run.source_kind = 'ticket' AND run.source_id = OLD.id)
3084
+ BEGIN
3085
+ SELECT RAISE(ABORT, 'Ticket has active service authority');
3086
+ END;
3087
+
3088
+ CREATE TRIGGER IF NOT EXISTS protect_service_project_deletion_claim
3089
+ BEFORE UPDATE OF deletion_started_at ON projects
3090
+ WHEN NEW.deletion_started_at IS NOT NULL AND EXISTS (
3091
+ SELECT 1 FROM active_project_service_authorities WHERE project_id = OLD.id)
3092
+ BEGIN
3093
+ SELECT RAISE(ABORT, 'Project has active service authority');
3094
+ END;
3095
+
3096
+ CREATE TRIGGER IF NOT EXISTS protect_service_project_deletion
3097
+ BEFORE DELETE ON projects
3098
+ WHEN EXISTS (SELECT 1 FROM active_project_service_authorities WHERE project_id = OLD.id)
3099
+ BEGIN
3100
+ SELECT RAISE(ABORT, 'Project has active service authority');
3101
+ END;
3102
+
3103
+ CREATE TRIGGER IF NOT EXISTS protect_service_project_target
3104
+ BEFORE UPDATE OF active_target_kind, active_target_bundle_id ON projects
3105
+ WHEN EXISTS (SELECT 1 FROM active_project_service_authorities WHERE project_id = OLD.id)
3106
+ OR OLD.deletion_started_at IS NOT NULL
3107
+ BEGIN
3108
+ SELECT RAISE(ABORT, 'Cannot change project target while service authority or deletion is active');
3109
+ END;
3110
+
3111
+ CREATE TRIGGER IF NOT EXISTS protect_service_project_repo_path
3112
+ BEFORE UPDATE OF repo_path ON projects
3113
+ WHEN NEW.repo_path IS NOT OLD.repo_path AND (
3114
+ EXISTS (SELECT 1 FROM active_project_service_authorities WHERE project_id = OLD.id)
3115
+ OR OLD.deletion_started_at IS NOT NULL)
3116
+ BEGIN
3117
+ SELECT RAISE(ABORT, 'Cannot change project repository while service authority or deletion is active');
3118
+ END;
3119
+ `);
2895
3120
  db.exec(`
2896
3121
  CREATE TABLE IF NOT EXISTS execution_run_authorities (
2897
3122
  execution_run_id TEXT NOT NULL,
@@ -5176,6 +5401,67 @@ db.exec(`
5176
5401
  FOREIGN KEY (review_run_id) REFERENCES review_runs(id) ON DELETE CASCADE
5177
5402
  )
5178
5403
  `);
5404
+ ensureColumn('review_runs', 'output_provenance', `TEXT CHECK (output_provenance IS NULL OR output_provenance IN ('structured_primary', 'structured_transcript', 'subagent_aggregation', 'markdown_fallback', 'parse_failed'))`);
5405
+ ensureColumn('review_runs', 'evaluator_fingerprint', `TEXT CHECK (evaluator_fingerprint IS NULL OR (length(evaluator_fingerprint) = 64 AND evaluator_fingerprint NOT GLOB '*[^0-9a-f]*'))`);
5406
+ // Historical identity survives deletion of the source ticket; writes are validated below.
5407
+ ensureColumn('review_findings', 'predecessor_finding_id', 'TEXT');
5408
+ ensureColumn('review_findings', 'evaluator_fingerprint', `TEXT CHECK (evaluator_fingerprint IS NULL OR (length(evaluator_fingerprint) = 64 AND evaluator_fingerprint NOT GLOB '*[^0-9a-f]*'))`);
5409
+ ensureColumn('review_findings', 'output_provenance', `TEXT CHECK (output_provenance IS NULL OR output_provenance IN ('structured_primary', 'structured_transcript', 'subagent_aggregation', 'markdown_fallback', 'parse_failed'))`);
5410
+ db.exec(`
5411
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_review_finding_predecessor
5412
+ ON review_findings(review_run_id, predecessor_finding_id) WHERE predecessor_finding_id IS NOT NULL;
5413
+ CREATE TRIGGER IF NOT EXISTS preserve_review_finding_predecessor
5414
+ BEFORE UPDATE OF predecessor_finding_id ON review_findings
5415
+ WHEN NEW.predecessor_finding_id IS NOT OLD.predecessor_finding_id
5416
+ BEGIN
5417
+ SELECT RAISE(ABORT, 'Review finding predecessor is immutable');
5418
+ END;
5419
+ CREATE TRIGGER IF NOT EXISTS preserve_review_finding_lineage_owner
5420
+ BEFORE UPDATE OF review_run_id ON review_findings
5421
+ WHEN NEW.review_run_id IS NOT OLD.review_run_id AND EXISTS (
5422
+ SELECT 1 FROM review_findings WHERE predecessor_finding_id = OLD.id
5423
+ )
5424
+ BEGIN
5425
+ SELECT RAISE(ABORT, 'Review finding lineage ownership is immutable');
5426
+ END;
5427
+ CREATE TRIGGER IF NOT EXISTS preserve_review_run_lineage_owner
5428
+ BEFORE UPDATE OF ticket_id, project_id, worktree_bundle_id, status ON review_runs
5429
+ WHEN (NEW.ticket_id IS NOT OLD.ticket_id OR NEW.project_id IS NOT OLD.project_id
5430
+ OR NEW.worktree_bundle_id IS NOT OLD.worktree_bundle_id OR NEW.status IS NOT OLD.status)
5431
+ AND EXISTS (
5432
+ SELECT 1 FROM review_findings predecessor
5433
+ JOIN review_findings successor ON successor.predecessor_finding_id = predecessor.id
5434
+ WHERE predecessor.review_run_id = OLD.id
5435
+ OR (successor.review_run_id = OLD.id AND (
5436
+ NEW.ticket_id IS NOT OLD.ticket_id OR NEW.project_id IS NOT OLD.project_id
5437
+ OR NEW.worktree_bundle_id IS NOT OLD.worktree_bundle_id
5438
+ ))
5439
+ )
5440
+ BEGIN
5441
+ SELECT RAISE(ABORT, 'Review run lineage ownership is immutable');
5442
+ END;
5443
+ `);
5444
+ for (const operation of ['INSERT', 'UPDATE']) {
5445
+ db.exec(`
5446
+ DROP TRIGGER IF EXISTS validate_review_finding_predecessor_${operation.toLowerCase()};
5447
+ CREATE TRIGGER validate_review_finding_predecessor_${operation.toLowerCase()}
5448
+ BEFORE ${operation} ON review_findings
5449
+ WHEN NEW.predecessor_finding_id IS NOT NULL
5450
+ BEGIN
5451
+ SELECT RAISE(ABORT, 'Invalid review finding predecessor') WHERE NOT EXISTS (
5452
+ SELECT 1 FROM review_findings predecessor
5453
+ JOIN review_runs prior ON prior.id = predecessor.review_run_id
5454
+ JOIN review_runs current ON current.id = NEW.review_run_id
5455
+ WHERE predecessor.id = NEW.predecessor_finding_id
5456
+ AND prior.id <> current.id
5457
+ AND prior.status = 'completed'
5458
+ AND prior.project_id IS current.project_id
5459
+ AND prior.worktree_bundle_id IS current.worktree_bundle_id
5460
+ AND (current.worktree_bundle_id IS NOT NULL OR prior.ticket_id = current.ticket_id)
5461
+ );
5462
+ END;
5463
+ `);
5464
+ }
5179
5465
  db.exec(`
5180
5466
  CREATE TABLE IF NOT EXISTS bundle_pull_requests (
5181
5467
  id TEXT PRIMARY KEY,
@@ -5215,7 +5501,6 @@ db.exec(`
5215
5501
  automation_paused_code TEXT,
5216
5502
  automation_paused_reason TEXT,
5217
5503
  automation_paused_at DATETIME,
5218
- automation_delivery_ready_stage_run_id TEXT,
5219
5504
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
5220
5505
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
5221
5506
  PRIMARY KEY (project_id, worktree_bundle_id),
@@ -5231,7 +5516,6 @@ ensureColumn('bundle_automation_state', 'automation_progression_run_id', 'TEXT')
5231
5516
  ensureColumn('bundle_automation_state', 'automation_paused_stage_run_id', 'TEXT');
5232
5517
  ensureColumn('bundle_automation_state', 'automation_paused_boundary_purpose', 'TEXT');
5233
5518
  ensureColumn('bundle_automation_state', 'automation_pause_reason_code', 'TEXT');
5234
- ensureColumn('bundle_automation_state', 'automation_delivery_ready_stage_run_id', 'TEXT');
5235
5519
  db.exec('DROP TRIGGER IF EXISTS validate_review_repair_result_update');
5236
5520
  db.exec('DROP TRIGGER IF EXISTS validate_review_repair_gate_update');
5237
5521
  db.exec('DROP TRIGGER IF EXISTS validate_review_repair_gate_evaluation_update');
@@ -5693,9 +5977,19 @@ db.exec(`
5693
5977
  SELECT RAISE(ABORT, 'Invalid review repair verification lineage');
5694
5978
  END;
5695
5979
 
5696
- CREATE TRIGGER IF NOT EXISTS validate_review_repair_result_update
5980
+ DROP TRIGGER IF EXISTS validate_review_repair_result_update;
5981
+ CREATE TRIGGER validate_review_repair_result_update
5697
5982
  BEFORE UPDATE OF resulting_review_run_id ON review_repair_iterations
5698
5983
  WHEN (OLD.resulting_review_run_id IS NOT NULL AND NEW.resulting_review_run_id IS NULL
5984
+ AND NOT (
5985
+ OLD.phase = 'paused' AND OLD.outcome IS 'paused' AND OLD.stop_reason IS 'stale_evidence'
5986
+ AND NEW.phase = 'gate_pending' AND NEW.outcome IS NULL AND NEW.stop_reason IS NULL
5987
+ AND NEW.resulting_review_execution_run_id IS NULL AND NEW.resulting_blocking_count IS NULL
5988
+ AND NEW.gate_batch_run_id IS NULL AND NEW.gate_source_batch_run_id IS NULL
5989
+ AND NEW.gate_evaluation_id IS NULL AND NEW.workspace_evidence_json IS NULL
5990
+ AND NEW.gate_active_at IS NULL AND NEW.verification_pending_at IS NULL
5991
+ AND NEW.verification_active_at IS NULL
5992
+ )
5699
5993
  AND NOT (OLD.resulting_review_execution_run_id <> NEW.resulting_review_execution_run_id
5700
5994
  AND EXISTS (
5701
5995
  SELECT 1 FROM review_runs prior_result
@@ -5941,6 +6235,49 @@ db.exec('CREATE INDEX IF NOT EXISTS idx_chat_sessions_project_updated_at ON chat
5941
6235
  db.exec('CREATE INDEX IF NOT EXISTS idx_chat_messages_session_created_at ON chat_messages(chat_session_id, created_at)');
5942
6236
  db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_ticket_stage_changes_ticket_stage ON ticket_stage_changes(ticket_id, stage)');
5943
6237
  db.exec('CREATE INDEX IF NOT EXISTS idx_ticket_stage_changes_bundle_stage_success ON ticket_stage_changes(project_id, worktree_bundle_id, stage, success_revision)');
6238
+ ensureColumn('review_repair_iterations', 'progress_assessment_json', `TEXT CHECK (progress_assessment_json IS NULL OR CASE WHEN json_valid(progress_assessment_json) THEN json_type(progress_assessment_json) = 'object' AND length(CAST(progress_assessment_json AS BLOB)) BETWEEN 2 AND 16384 ELSE 0 END)`);
6239
+ db.exec(`
6240
+ CREATE TRIGGER IF NOT EXISTS validate_review_repair_assessment_insert
6241
+ BEFORE INSERT ON review_repair_iterations
6242
+ WHEN NEW.progress_assessment_json IS NOT NULL
6243
+ BEGIN
6244
+ SELECT RAISE(ABORT, 'Review repair assessment requires existing verification evidence');
6245
+ END;
6246
+
6247
+ CREATE TRIGGER IF NOT EXISTS validate_review_repair_assessment_update
6248
+ BEFORE UPDATE ON review_repair_iterations
6249
+ WHEN (OLD.progress_assessment_json IS NULL AND NEW.progress_assessment_json IS NOT NULL AND (
6250
+ OLD.phase <> 'verification_active' OR NEW.phase <> 'verification_active'
6251
+ OR OLD.resulting_review_execution_run_id IS NULL OR OLD.resulting_review_run_id IS NULL
6252
+ OR NEW.resulting_review_execution_run_id IS NOT OLD.resulting_review_execution_run_id
6253
+ OR NEW.resulting_review_run_id IS NOT OLD.resulting_review_run_id
6254
+ )) OR (OLD.progress_assessment_json IS NOT NULL AND (
6255
+ NEW.progress_assessment_json IS NOT OLD.progress_assessment_json
6256
+ OR NEW.resulting_review_execution_run_id IS NOT OLD.resulting_review_execution_run_id
6257
+ OR NEW.resulting_review_run_id IS NOT OLD.resulting_review_run_id
6258
+ ) AND NOT (
6259
+ OLD.phase = 'paused' AND OLD.outcome IS 'paused' AND OLD.stop_reason IS 'stale_evidence'
6260
+ AND NEW.phase = 'gate_pending' AND NEW.outcome IS NULL AND NEW.stop_reason IS NULL
6261
+ AND NEW.progress_assessment_json IS NULL
6262
+ AND NEW.resulting_review_execution_run_id IS NULL AND NEW.resulting_review_run_id IS NULL
6263
+ AND NEW.resulting_blocking_count IS NULL AND NEW.gate_batch_run_id IS NULL
6264
+ AND NEW.gate_source_batch_run_id IS NULL AND NEW.gate_evaluation_id IS NULL
6265
+ AND NEW.workspace_evidence_json IS NULL AND NEW.gate_active_at IS NULL
6266
+ AND NEW.verification_pending_at IS NULL AND NEW.verification_active_at IS NULL
6267
+ ))
6268
+ BEGIN
6269
+ SELECT RAISE(ABORT, 'Invalid or immutable Review repair progress assessment');
6270
+ END;
6271
+
6272
+ CREATE TRIGGER IF NOT EXISTS prevent_review_repair_no_progress_resume
6273
+ BEFORE UPDATE ON review_repair_iterations
6274
+ WHEN OLD.phase = 'paused' AND OLD.stop_reason = 'no_progress'
6275
+ AND NEW.phase <> 'terminal'
6276
+ AND (NEW.phase <> 'paused' OR NEW.stop_reason IS NOT 'no_progress')
6277
+ BEGIN
6278
+ SELECT RAISE(ABORT, 'Review repair no_progress requires an explicit successor iteration');
6279
+ END;
6280
+ `);
5944
6281
  db.exec('CREATE INDEX IF NOT EXISTS idx_review_runs_ticket_created_at ON review_runs(ticket_id, created_at)');
5945
6282
  db.exec('CREATE INDEX IF NOT EXISTS idx_review_runs_bundle_created_at ON review_runs(project_id, worktree_bundle_id, created_at)');
5946
6283
  db.exec('CREATE INDEX IF NOT EXISTS idx_review_findings_review_run_ordinal ON review_findings(review_run_id, ordinal)');