@straiffi/archon 1.8.8 → 1.8.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 (182) hide show
  1. package/dist/client/assets/{ActivityView--4X_m6mR.js → ActivityView-ew2HI3iv.js} +1 -1
  2. package/dist/client/assets/{GitHubActionsDialog-DZ1lHM01.js → GitHubActionsDialog-C9lg87qK.js} +1 -1
  3. package/dist/client/assets/RepositoryGraphDialog-CnED9oZO.js +1 -0
  4. package/dist/client/assets/VerificationDialog-BFEA4LcO.js +4 -0
  5. package/dist/client/assets/{architectureDiagram-3BPJPVTR-CY5xTOZJ.js → architectureDiagram-3BPJPVTR-B468SqWl.js} +1 -1
  6. package/dist/client/assets/{blockDiagram-GPEHLZMM-DkMi7gMh.js → blockDiagram-GPEHLZMM-CcuVr5zL.js} +1 -1
  7. package/dist/client/assets/{c4Diagram-AAUBKEIU-ZZxXBBU4.js → c4Diagram-AAUBKEIU-CIVHHAmy.js} +1 -1
  8. package/dist/client/assets/channel-i8pCfCxf.js +1 -0
  9. package/dist/client/assets/{chunk-2J33WTMH-C9PiKMs-.js → chunk-2J33WTMH-BdX4DCtG.js} +1 -1
  10. package/dist/client/assets/{chunk-3OPIFGDE-B81aUmvi.js → chunk-3OPIFGDE-skObTH8f.js} +1 -1
  11. package/dist/client/assets/{chunk-5ZQYHXKU-Bk2Owm90.js → chunk-5ZQYHXKU-DgfXNdzK.js} +1 -1
  12. package/dist/client/assets/{chunk-727SXJPM-Bx-79aJk.js → chunk-727SXJPM-mnXVo2HU.js} +1 -1
  13. package/dist/client/assets/{chunk-AQP2D5EJ-GQNXYOf0.js → chunk-AQP2D5EJ-C3mXtCLv.js} +1 -1
  14. package/dist/client/assets/{chunk-CSCIHK7Q-BV22j6vn.js → chunk-CSCIHK7Q-BrUDaK4w.js} +1 -1
  15. package/dist/client/assets/{chunk-KSCS5N6A-C9LlcbWv.js → chunk-KSCS5N6A-B7ZXhQZZ.js} +1 -1
  16. package/dist/client/assets/{chunk-L5ZTLDWV-D8PJqJnh.js → chunk-L5ZTLDWV-CfWv4E3P.js} +1 -1
  17. package/dist/client/assets/{chunk-LZXEDZCA-DcbPPozR.js → chunk-LZXEDZCA-ByKWnYgl.js} +2 -2
  18. package/dist/client/assets/{chunk-ND2GUHAM-NV-_2KqZ.js → chunk-ND2GUHAM-C3xamRop.js} +1 -1
  19. package/dist/client/assets/{chunk-NZK2D7GU-CbqA26gb.js → chunk-NZK2D7GU-CMbTC0VA.js} +1 -1
  20. package/dist/client/assets/{chunk-O5CBEL6O-B0DMN44B.js → chunk-O5CBEL6O-IrjlcgnD.js} +1 -1
  21. package/dist/client/assets/{chunk-WU5MYG2G-CYpcnwf9.js → chunk-WU5MYG2G-WPjA4G6U.js} +1 -1
  22. package/dist/client/assets/classDiagram-4FO5ZUOK-CZ3Y7ahg.js +1 -0
  23. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-eRiSIrc8.js +1 -0
  24. package/dist/client/assets/{dagre-BM42HDAG-B5HD9fyK.js → dagre-BM42HDAG-B8MWc9Vt.js} +1 -1
  25. package/dist/client/assets/{diagram-2AECGRRQ-BCXvWFpM.js → diagram-2AECGRRQ-CUG9b88X.js} +1 -1
  26. package/dist/client/assets/{diagram-5GNKFQAL-CSpWGfAO.js → diagram-5GNKFQAL-C0sLSoID.js} +1 -1
  27. package/dist/client/assets/{diagram-KO2AKTUF-CFECGppT.js → diagram-KO2AKTUF-D5tls_Ye.js} +1 -1
  28. package/dist/client/assets/{diagram-LMA3HP47-Dpb99QbZ.js → diagram-LMA3HP47-B4X4DO9t.js} +1 -1
  29. package/dist/client/assets/{diagram-OG6HWLK6-DJcTGnx0.js → diagram-OG6HWLK6--Fkpu1xR.js} +1 -1
  30. package/dist/client/assets/{erDiagram-TEJ5UH35-jWArJxNU.js → erDiagram-TEJ5UH35-BcbdxB1u.js} +1 -1
  31. package/dist/client/assets/{flowDiagram-I6XJVG4X-DDF3P1hY.js → flowDiagram-I6XJVG4X-DwNYkjtu.js} +1 -1
  32. package/dist/client/assets/{ganttDiagram-6RSMTGT7-Corwr_18.js → ganttDiagram-6RSMTGT7-Sh_eU-Z9.js} +1 -1
  33. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-WzlpszWn.js → gitGraphDiagram-PVQCEYII-C5FDjOX5.js} +1 -1
  34. package/dist/client/assets/index-BqrNPQ_0.js +274 -0
  35. package/dist/client/assets/index-BsjoiBxI.css +2 -0
  36. package/dist/client/assets/{infoDiagram-5YYISTIA-C0VwTwNC.js → infoDiagram-5YYISTIA-Ck9OJOWG.js} +1 -1
  37. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-m9l3k2qX.js → ishikawaDiagram-YF4QCWOH-DYXgrkHN.js} +1 -1
  38. package/dist/client/assets/{journeyDiagram-JHISSGLW-DMhT8HTW.js → journeyDiagram-JHISSGLW-BwNJvlUR.js} +1 -1
  39. package/dist/client/assets/{kanban-definition-UN3LZRKU-C2ZkcOi0.js → kanban-definition-UN3LZRKU-CQPcarO3.js} +1 -1
  40. package/dist/client/assets/{line-gKkUdUWR.js → line-C0_WiC76.js} +1 -1
  41. package/dist/client/assets/{mermaid-parser.core-DdBfJWbP.js → mermaid-parser.core-DZFY-NQY.js} +1 -1
  42. package/dist/client/assets/{mermaid.core-DXnhq-eu.js → mermaid.core-CQd5d2Vh.js} +3 -3
  43. package/dist/client/assets/{mindmap-definition-RKZ34NQL-B0gX4aJy.js → mindmap-definition-RKZ34NQL-DM1_aBMT.js} +1 -1
  44. package/dist/client/assets/{pieDiagram-4H26LBE5-BHYdLmpx.js → pieDiagram-4H26LBE5-CsErXHmB.js} +1 -1
  45. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-C-H-yQwU.js → quadrantDiagram-W4KKPZXB-Dbvx_hzM.js} +1 -1
  46. package/dist/client/assets/{requirementDiagram-4Y6WPE33-DY_pSCFy.js → requirementDiagram-4Y6WPE33-DKjfBx4s.js} +1 -1
  47. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CC61HRGf.js → sankeyDiagram-5OEKKPKP-BoZ5QkYh.js} +1 -1
  48. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-B8fXWNxw.js → sequenceDiagram-3UESZ5HK-zx3eTqJo.js} +1 -1
  49. package/dist/client/assets/{stateDiagram-AJRCARHV-C_VYKsXW.js → stateDiagram-AJRCARHV-CwjCCvKl.js} +1 -1
  50. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-B0RJBNmi.js +1 -0
  51. package/dist/client/assets/{timeline-definition-PNZ67QCA-0fB4PIIX.js → timeline-definition-PNZ67QCA-_vAeRe5B.js} +1 -1
  52. package/dist/client/assets/{vennDiagram-CIIHVFJN-CfpVF7Aa.js → vennDiagram-CIIHVFJN-BrY9ZFi8.js} +1 -1
  53. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-CgdK4FcC.js → wardleyDiagram-YWT4CUSO-ffsNUFbG.js} +1 -1
  54. package/dist/client/assets/{xychartDiagram-2RQKCTM6-BEfatQYR.js → xychartDiagram-2RQKCTM6-Cwg8OKEm.js} +1 -1
  55. package/dist/client/index.html +2 -2
  56. package/dist/server/db.js +481 -37
  57. package/dist/server/db.js.map +1 -1
  58. package/dist/server/index.js +72 -18
  59. package/dist/server/index.js.map +1 -1
  60. package/dist/server/lib/activeAutomationChild.js +1 -0
  61. package/dist/server/lib/activeAutomationChild.js.map +1 -1
  62. package/dist/server/lib/agent.js +127 -15
  63. package/dist/server/lib/agent.js.map +1 -1
  64. package/dist/server/lib/agentRuns.js +12 -0
  65. package/dist/server/lib/agentRuns.js.map +1 -1
  66. package/dist/server/lib/agentSpawnPermits.js +37 -0
  67. package/dist/server/lib/agentSpawnPermits.js.map +1 -0
  68. package/dist/server/lib/automationCheckGate.js +94 -32
  69. package/dist/server/lib/automationCheckGate.js.map +1 -1
  70. package/dist/server/lib/automationProgression.js +19 -5
  71. package/dist/server/lib/automationProgression.js.map +1 -1
  72. package/dist/server/lib/automationReviewGate.js +1 -0
  73. package/dist/server/lib/automationReviewGate.js.map +1 -1
  74. package/dist/server/lib/buildCompletionContinuation.js +144 -4
  75. package/dist/server/lib/buildCompletionContinuation.js.map +1 -1
  76. package/dist/server/lib/buildSequences.js +13 -1
  77. package/dist/server/lib/buildSequences.js.map +1 -1
  78. package/dist/server/lib/bundleActivity.js +19 -0
  79. package/dist/server/lib/bundleActivity.js.map +1 -1
  80. package/dist/server/lib/chatOperations.js +86 -4
  81. package/dist/server/lib/chatOperations.js.map +1 -1
  82. package/dist/server/lib/chatPostBuildRuns.js +98 -0
  83. package/dist/server/lib/chatPostBuildRuns.js.map +1 -0
  84. package/dist/server/lib/chats.js +13 -0
  85. package/dist/server/lib/chats.js.map +1 -1
  86. package/dist/server/lib/checkAutoFix.js +604 -0
  87. package/dist/server/lib/checkAutoFix.js.map +1 -0
  88. package/dist/server/lib/checkAutoFixDetails.js +83 -0
  89. package/dist/server/lib/checkAutoFixDetails.js.map +1 -0
  90. package/dist/server/lib/checkAutoFixPolicy.js +59 -0
  91. package/dist/server/lib/checkAutoFixPolicy.js.map +1 -0
  92. package/dist/server/lib/checkGateEvaluations.js +41 -16
  93. package/dist/server/lib/checkGateEvaluations.js.map +1 -1
  94. package/dist/server/lib/checkRepair.js +158 -63
  95. package/dist/server/lib/checkRepair.js.map +1 -1
  96. package/dist/server/lib/checkRepairDispatch.js +91 -0
  97. package/dist/server/lib/checkRepairDispatch.js.map +1 -0
  98. package/dist/server/lib/checkRepairEvidence.js +84 -0
  99. package/dist/server/lib/checkRepairEvidence.js.map +1 -0
  100. package/dist/server/lib/checkRepairParent.js +21 -0
  101. package/dist/server/lib/checkRepairParent.js.map +1 -0
  102. package/dist/server/lib/checkRepairReconciliation.js +14 -5
  103. package/dist/server/lib/checkRepairReconciliation.js.map +1 -1
  104. package/dist/server/lib/checkRunner.js +27 -8
  105. package/dist/server/lib/checkRunner.js.map +1 -1
  106. package/dist/server/lib/checkRuns.js +63 -4
  107. package/dist/server/lib/checkRuns.js.map +1 -1
  108. package/dist/server/lib/executionReconciliation.js +417 -8
  109. package/dist/server/lib/executionReconciliation.js.map +1 -1
  110. package/dist/server/lib/executionRuns.js +25 -2
  111. package/dist/server/lib/executionRuns.js.map +1 -1
  112. package/dist/server/lib/integrations/github.js +10 -7
  113. package/dist/server/lib/integrations/github.js.map +1 -1
  114. package/dist/server/lib/postBuildCheckRuns.js +200 -5
  115. package/dist/server/lib/postBuildCheckRuns.js.map +1 -1
  116. package/dist/server/lib/postBuildChecks.js +107 -21
  117. package/dist/server/lib/postBuildChecks.js.map +1 -1
  118. package/dist/server/lib/projects.js +25 -1
  119. package/dist/server/lib/projects.js.map +1 -1
  120. package/dist/server/lib/queue.js +9 -3
  121. package/dist/server/lib/queue.js.map +1 -1
  122. package/dist/server/lib/repositoryGraphQueries.js +5 -3
  123. package/dist/server/lib/repositoryGraphQueries.js.map +1 -1
  124. package/dist/server/lib/repositoryGraphScan.js +13 -2
  125. package/dist/server/lib/repositoryGraphScan.js.map +1 -1
  126. package/dist/server/lib/repositoryGraphStore.js +61 -1
  127. package/dist/server/lib/repositoryGraphStore.js.map +1 -1
  128. package/dist/server/lib/specArtifact.js +10 -1
  129. package/dist/server/lib/specArtifact.js.map +1 -1
  130. package/dist/server/lib/specDraft.js +10 -2
  131. package/dist/server/lib/specDraft.js.map +1 -1
  132. package/dist/server/lib/specExport.js +70 -6
  133. package/dist/server/lib/specExport.js.map +1 -1
  134. package/dist/server/lib/specFormatting.js +3 -2
  135. package/dist/server/lib/specFormatting.js.map +1 -1
  136. package/dist/server/lib/specRepositoryEvidenceFormatting.js +75 -0
  137. package/dist/server/lib/specRepositoryEvidenceFormatting.js.map +1 -0
  138. package/dist/server/lib/specRepositoryImpact.js +447 -0
  139. package/dist/server/lib/specRepositoryImpact.js.map +1 -0
  140. package/dist/server/lib/specRepositoryImpactLimits.js +6 -0
  141. package/dist/server/lib/specRepositoryImpactLimits.js.map +1 -0
  142. package/dist/server/lib/specReviewContext.js +15 -5
  143. package/dist/server/lib/specReviewContext.js.map +1 -1
  144. package/dist/server/lib/specReviewValidation.js +1 -1
  145. package/dist/server/lib/specReviewValidation.js.map +1 -1
  146. package/dist/server/lib/specSections.js +28 -3
  147. package/dist/server/lib/specSections.js.map +1 -1
  148. package/dist/server/lib/specSnapshotEvidence.js +162 -0
  149. package/dist/server/lib/specSnapshotEvidence.js.map +1 -0
  150. package/dist/server/lib/specSnapshotEvidenceSerialization.js +202 -0
  151. package/dist/server/lib/specSnapshotEvidenceSerialization.js.map +1 -0
  152. package/dist/server/lib/specs.js +31 -5
  153. package/dist/server/lib/specs.js.map +1 -1
  154. package/dist/server/lib/ticketAutomationCommit.js +3 -1
  155. package/dist/server/lib/ticketAutomationCommit.js.map +1 -1
  156. package/dist/server/lib/ticketFollowUpOperations.js +16 -12
  157. package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
  158. package/dist/server/lib/ticketRunOperations.js +13 -1
  159. package/dist/server/lib/ticketRunOperations.js.map +1 -1
  160. package/dist/server/lib/ticketWorkflowOperations.js +5 -2
  161. package/dist/server/lib/ticketWorkflowOperations.js.map +1 -1
  162. package/dist/server/lib/types.js +1 -0
  163. package/dist/server/lib/types.js.map +1 -1
  164. package/dist/server/workers/build.js +46 -148
  165. package/dist/server/workers/build.js.map +1 -1
  166. package/dist/server/workers/chat.js +185 -43
  167. package/dist/server/workers/chat.js.map +1 -1
  168. package/dist/server/workers/followUp.js +210 -48
  169. package/dist/server/workers/followUp.js.map +1 -1
  170. package/dist/server/workers/specReview.js +17 -4
  171. package/dist/server/workers/specReview.js.map +1 -1
  172. package/dist/server/workers/specSession.js +127 -22
  173. package/dist/server/workers/specSession.js.map +1 -1
  174. package/package.json +1 -1
  175. package/dist/client/assets/RepositoryGraphDialog-B--YlQcV.js +0 -1
  176. package/dist/client/assets/VerificationDialog-BIWPtBg3.js +0 -4
  177. package/dist/client/assets/channel-b2AN-U3x.js +0 -1
  178. package/dist/client/assets/classDiagram-4FO5ZUOK-C0exS5oM.js +0 -1
  179. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DkB9dQZ1.js +0 -1
  180. package/dist/client/assets/index-Bs5SQZhz.css +0 -2
  181. package/dist/client/assets/index-Dy-7h3lN.js +0 -274
  182. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ctk1r914.js +0 -1
package/dist/server/db.js CHANGED
@@ -61,6 +61,7 @@ db.exec(`
61
61
  required_check_definition_ids TEXT NOT NULL DEFAULT '[]',
62
62
  pre_review_check_definition_ids TEXT NOT NULL DEFAULT '[]',
63
63
  post_build_check_definition_ids TEXT NOT NULL DEFAULT '[]',
64
+ auto_fix_check_definition_ids TEXT NOT NULL DEFAULT '[]',
64
65
  test_commands TEXT,
65
66
  run_ide TEXT,
66
67
  preview_service_name TEXT,
@@ -144,6 +145,7 @@ ensureColumn('projects', 'check_definitions', 'TEXT');
144
145
  ensureColumn('projects', 'required_check_definition_ids', "TEXT NOT NULL DEFAULT '[]'");
145
146
  ensureColumn('projects', 'pre_review_check_definition_ids', "TEXT NOT NULL DEFAULT '[]'");
146
147
  ensureColumn('projects', 'post_build_check_definition_ids', "TEXT NOT NULL DEFAULT '[]'");
148
+ ensureColumn('projects', 'auto_fix_check_definition_ids', "TEXT NOT NULL DEFAULT '[]'");
147
149
  ensureColumn('projects', 'test_commands', 'TEXT');
148
150
  ensureColumn('projects', 'helper_model', 'TEXT');
149
151
  ensureColumn('projects', 'helper_variant', 'TEXT');
@@ -1016,6 +1018,7 @@ db.exec(`
1016
1018
  previous_agent_log TEXT,
1017
1019
  previous_agent_message_id TEXT,
1018
1020
  streaming_response TEXT,
1021
+ post_build_source_run_id TEXT,
1019
1022
  target_kind TEXT NOT NULL DEFAULT 'repo_root',
1020
1023
  target_bundle_id TEXT,
1021
1024
  target_branch_snapshot TEXT,
@@ -1038,6 +1041,12 @@ db.exec(`
1038
1041
  ensureColumn('chat_sessions', 'agent_message_id', 'TEXT');
1039
1042
  ensureColumn('chat_sessions', 'previous_agent_log', 'TEXT');
1040
1043
  ensureColumn('chat_sessions', 'previous_agent_message_id', 'TEXT');
1044
+ ensureColumn('chat_sessions', 'post_build_source_run_id', 'TEXT');
1045
+ db.exec(`
1046
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_chat_sessions_post_build_source
1047
+ ON chat_sessions(post_build_source_run_id)
1048
+ WHERE post_build_source_run_id IS NOT NULL
1049
+ `);
1041
1050
  db.exec(`
1042
1051
  CREATE TABLE IF NOT EXISTS chat_messages (
1043
1052
  id TEXT PRIMARY KEY,
@@ -1213,6 +1222,7 @@ db.exec(`
1213
1222
  tickets_json TEXT,
1214
1223
  dependencies_json TEXT,
1215
1224
  resource_urls_json TEXT,
1225
+ repository_evidence_json TEXT,
1216
1226
  mermaid TEXT,
1217
1227
  mockup TEXT,
1218
1228
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
@@ -1223,6 +1233,65 @@ ensureColumn('spec_snapshots', 'plan_text', 'TEXT');
1223
1233
  ensureColumn('spec_snapshots', 'mockup', 'TEXT');
1224
1234
  ensureColumn('spec_snapshots', 'file_relation_graph', 'TEXT');
1225
1235
  ensureColumn('spec_snapshots', 'resource_urls_json', 'TEXT');
1236
+ ensureColumn('spec_snapshots', 'repository_evidence_json', 'TEXT');
1237
+ db.exec(`
1238
+ DELETE FROM repository_graph_scan_references
1239
+ WHERE reference_type = 'spec_snapshot'
1240
+ AND NOT EXISTS (
1241
+ SELECT 1
1242
+ FROM spec_snapshots
1243
+ INNER JOIN spec_sessions ON spec_sessions.id = spec_snapshots.spec_session_id
1244
+ INNER JOIN repository_graph_scans
1245
+ ON repository_graph_scans.id = repository_graph_scan_references.scan_id
1246
+ AND repository_graph_scans.project_id = repository_graph_scan_references.project_id
1247
+ WHERE spec_snapshots.id = repository_graph_scan_references.reference_id
1248
+ AND spec_sessions.project_id = repository_graph_scan_references.project_id
1249
+ AND repository_graph_scans.status = 'completed'
1250
+ );
1251
+
1252
+ DROP TRIGGER IF EXISTS validate_spec_snapshot_graph_reference_insert;
1253
+ DROP TRIGGER IF EXISTS reject_spec_snapshot_graph_reference_update;
1254
+ DROP TRIGGER IF EXISTS delete_spec_snapshot_graph_reference;
1255
+ DROP TRIGGER IF EXISTS reject_spec_snapshot_repository_evidence_update;
1256
+
1257
+ CREATE TRIGGER validate_spec_snapshot_graph_reference_insert
1258
+ BEFORE INSERT ON repository_graph_scan_references
1259
+ WHEN NEW.reference_type = 'spec_snapshot' AND NOT EXISTS (
1260
+ SELECT 1
1261
+ FROM spec_snapshots
1262
+ INNER JOIN spec_sessions ON spec_sessions.id = spec_snapshots.spec_session_id
1263
+ INNER JOIN repository_graph_scans
1264
+ ON repository_graph_scans.id = NEW.scan_id
1265
+ AND repository_graph_scans.project_id = NEW.project_id
1266
+ WHERE spec_snapshots.id = NEW.reference_id
1267
+ AND spec_sessions.project_id = NEW.project_id
1268
+ AND repository_graph_scans.status = 'completed'
1269
+ )
1270
+ BEGIN
1271
+ SELECT RAISE(ABORT, 'Spec snapshot graph reference requires an owned snapshot and completed scan');
1272
+ END;
1273
+
1274
+ CREATE TRIGGER reject_spec_snapshot_graph_reference_update
1275
+ BEFORE UPDATE ON repository_graph_scan_references
1276
+ WHEN OLD.reference_type = 'spec_snapshot' OR NEW.reference_type = 'spec_snapshot'
1277
+ BEGIN
1278
+ SELECT RAISE(ABORT, 'Spec snapshot graph references are immutable');
1279
+ END;
1280
+
1281
+ CREATE TRIGGER delete_spec_snapshot_graph_reference
1282
+ AFTER DELETE ON spec_snapshots
1283
+ BEGIN
1284
+ DELETE FROM repository_graph_scan_references
1285
+ WHERE reference_type = 'spec_snapshot' AND reference_id = OLD.id;
1286
+ END;
1287
+
1288
+ CREATE TRIGGER reject_spec_snapshot_repository_evidence_update
1289
+ BEFORE UPDATE OF repository_evidence_json ON spec_snapshots
1290
+ WHEN OLD.repository_evidence_json IS NOT NEW.repository_evidence_json
1291
+ BEGIN
1292
+ SELECT RAISE(ABORT, 'Spec snapshot repository evidence is immutable');
1293
+ END;
1294
+ `);
1226
1295
  db.exec(`
1227
1296
  CREATE TABLE IF NOT EXISTS spec_message_attachments (
1228
1297
  attachment_id TEXT PRIMARY KEY,
@@ -2463,12 +2532,27 @@ db.exec(`
2463
2532
  END;
2464
2533
  `);
2465
2534
  db.exec(`
2535
+ CREATE TABLE IF NOT EXISTS agent_spawn_permits (
2536
+ id TEXT PRIMARY KEY,
2537
+ execution_run_id TEXT NOT NULL UNIQUE,
2538
+ status TEXT NOT NULL DEFAULT 'active',
2539
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
2540
+ revoked_at DATETIME,
2541
+ FOREIGN KEY (execution_run_id) REFERENCES execution_runs(id) ON DELETE CASCADE,
2542
+ CHECK (status IN ('active', 'revoked')),
2543
+ CHECK ((status = 'active' AND revoked_at IS NULL) OR (status = 'revoked' AND revoked_at IS NOT NULL))
2544
+ );
2545
+
2466
2546
  CREATE TABLE IF NOT EXISTS check_repair_details (
2467
2547
  execution_run_id TEXT PRIMARY KEY,
2468
2548
  source_batch_run_id TEXT NOT NULL,
2469
2549
  target_kind TEXT NOT NULL,
2470
2550
  target_bundle_id TEXT,
2471
- ticket_id TEXT NOT NULL,
2551
+ owner_type TEXT NOT NULL,
2552
+ owner_id TEXT NOT NULL,
2553
+ ticket_id TEXT,
2554
+ causal_execution_run_id TEXT NOT NULL,
2555
+ causal_agent_run_id TEXT NOT NULL,
2472
2556
  automation_progression_run_id TEXT,
2473
2557
  automation_boundary_purpose TEXT,
2474
2558
  initial_workspace_identity_json TEXT NOT NULL,
@@ -2484,11 +2568,16 @@ db.exec(`
2484
2568
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
2485
2569
  FOREIGN KEY (execution_run_id) REFERENCES execution_runs(id) ON DELETE CASCADE,
2486
2570
  FOREIGN KEY (source_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2571
+ FOREIGN KEY (causal_execution_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2572
+ FOREIGN KEY (causal_agent_run_id) REFERENCES agent_runs(id) ON DELETE RESTRICT,
2487
2573
  FOREIGN KEY (automation_progression_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2488
2574
  FOREIGN KEY (follow_up_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2489
2575
  FOREIGN KEY (rerun_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2490
2576
  FOREIGN KEY (retry_of_check_repair_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2491
2577
  CHECK (target_kind IN ('repo_root', 'bundle')),
2578
+ CHECK (owner_type IN ('ticket', 'chat_session')),
2579
+ CHECK ((owner_type = 'ticket' AND ticket_id = owner_id)
2580
+ OR (owner_type = 'chat_session' AND ticket_id IS NULL)),
2492
2581
  CHECK ((target_kind = 'repo_root' AND target_bundle_id IS NULL)
2493
2582
  OR (target_kind = 'bundle' AND target_bundle_id IS NOT NULL AND length(trim(target_bundle_id)) > 0)),
2494
2583
  CHECK (length(configuration_fingerprint) = 64 AND configuration_fingerprint NOT GLOB '*[^0-9a-f]*'),
@@ -2505,6 +2594,236 @@ db.exec(`
2505
2594
  CHECK (terminal_result IS NULL OR terminal_result IN ('passed', 'failed', 'cancelled', 'interrupted')),
2506
2595
  CHECK (terminal_message IS NULL OR length(CAST(terminal_message AS BLOB)) <= 16384)
2507
2596
  );
2597
+ `);
2598
+ const checkRepairSchema = db.prepare(`
2599
+ SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'check_repair_details'
2600
+ `).get();
2601
+ if (checkRepairSchema && !checkRepairSchema.sql.includes('owner_type')) {
2602
+ db.transaction(() => {
2603
+ db.exec(`
2604
+ DROP TRIGGER IF EXISTS preserve_check_repair_provenance;
2605
+ DROP TRIGGER IF EXISTS validate_check_repair_details_insert;
2606
+ DROP TRIGGER IF EXISTS validate_check_repair_follow_up_update;
2607
+ DROP TRIGGER IF EXISTS validate_check_repair_rerun_update;
2608
+ DROP TRIGGER IF EXISTS preserve_check_repair_terminal_result;
2609
+ DROP TRIGGER IF EXISTS preserve_check_repair_diagnostic_provenance;
2610
+ DROP TRIGGER IF EXISTS preserve_check_repair_raw_output_provenance;
2611
+ DROP TRIGGER IF EXISTS prevent_check_repair_raw_output_provenance_delete;
2612
+ DROP TRIGGER IF EXISTS validate_check_repair_diagnostic_provenance_insert;
2613
+ DROP TRIGGER IF EXISTS validate_check_repair_raw_output_provenance_insert;
2614
+ CREATE TEMP TABLE check_repair_diagnostic_provenance_migration AS
2615
+ SELECT * FROM check_repair_diagnostic_provenance;
2616
+ CREATE TEMP TABLE check_repair_raw_output_provenance_migration AS
2617
+ SELECT * FROM check_repair_raw_output_provenance;
2618
+ DROP TABLE check_repair_diagnostic_provenance;
2619
+ DROP TABLE check_repair_raw_output_provenance;
2620
+ ALTER TABLE check_repair_details RENAME TO check_repair_details_legacy;
2621
+
2622
+ CREATE TABLE check_repair_details (
2623
+ execution_run_id TEXT PRIMARY KEY,
2624
+ source_batch_run_id TEXT NOT NULL,
2625
+ target_kind TEXT NOT NULL,
2626
+ target_bundle_id TEXT,
2627
+ owner_type TEXT NOT NULL,
2628
+ owner_id TEXT NOT NULL,
2629
+ ticket_id TEXT,
2630
+ causal_execution_run_id TEXT NOT NULL,
2631
+ causal_agent_run_id TEXT NOT NULL,
2632
+ automation_progression_run_id TEXT,
2633
+ automation_boundary_purpose TEXT,
2634
+ initial_workspace_identity_json TEXT NOT NULL,
2635
+ configuration_fingerprint TEXT NOT NULL,
2636
+ effective_policy_fingerprint TEXT,
2637
+ definition_ids_json TEXT NOT NULL,
2638
+ follow_up_run_id TEXT,
2639
+ rerun_batch_run_id TEXT,
2640
+ terminal_result TEXT,
2641
+ terminal_message TEXT,
2642
+ retry_of_check_repair_run_id TEXT,
2643
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
2644
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
2645
+ FOREIGN KEY (execution_run_id) REFERENCES execution_runs(id) ON DELETE CASCADE,
2646
+ FOREIGN KEY (source_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2647
+ FOREIGN KEY (causal_execution_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2648
+ FOREIGN KEY (causal_agent_run_id) REFERENCES agent_runs(id) ON DELETE RESTRICT,
2649
+ FOREIGN KEY (automation_progression_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2650
+ FOREIGN KEY (follow_up_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2651
+ FOREIGN KEY (rerun_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2652
+ FOREIGN KEY (retry_of_check_repair_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2653
+ CHECK (target_kind IN ('repo_root', 'bundle')),
2654
+ CHECK (owner_type IN ('ticket', 'chat_session')),
2655
+ CHECK ((owner_type = 'ticket' AND ticket_id = owner_id)
2656
+ OR (owner_type = 'chat_session' AND ticket_id IS NULL)),
2657
+ CHECK ((target_kind = 'repo_root' AND target_bundle_id IS NULL)
2658
+ OR (target_kind = 'bundle' AND target_bundle_id IS NOT NULL AND length(trim(target_bundle_id)) > 0)),
2659
+ CHECK (length(configuration_fingerprint) = 64 AND configuration_fingerprint NOT GLOB '*[^0-9a-f]*'),
2660
+ CHECK (effective_policy_fingerprint IS NULL OR (
2661
+ length(effective_policy_fingerprint) = 64 AND effective_policy_fingerprint NOT GLOB '*[^0-9a-f]*'
2662
+ )),
2663
+ CHECK (automation_boundary_purpose IS NULL OR automation_boundary_purpose IN (
2664
+ 'automation_review', 'automation_commit', 'automation_push', 'automation_pull_request'
2665
+ )),
2666
+ CHECK ((automation_progression_run_id IS NULL AND automation_boundary_purpose IS NULL)
2667
+ OR (automation_progression_run_id IS NOT NULL AND automation_boundary_purpose IS NOT NULL)),
2668
+ CHECK ((automation_progression_run_id IS NULL AND effective_policy_fingerprint IS NULL)
2669
+ OR (automation_progression_run_id IS NOT NULL AND effective_policy_fingerprint IS NOT NULL)),
2670
+ CHECK (terminal_result IS NULL OR terminal_result IN ('passed', 'failed', 'cancelled', 'interrupted')),
2671
+ CHECK (terminal_message IS NULL OR length(CAST(terminal_message AS BLOB)) <= 16384)
2672
+ );
2673
+
2674
+ CREATE TEMP TABLE check_repair_causal_migration AS
2675
+ SELECT legacy.execution_run_id AS repair_run_id,
2676
+ (
2677
+ SELECT candidate.id
2678
+ FROM execution_runs candidate
2679
+ JOIN execution_run_links owner_link ON owner_link.execution_run_id = candidate.id
2680
+ AND owner_link.project_id = candidate.project_id
2681
+ AND owner_link.relationship = 'primary'
2682
+ AND owner_link.entity_type = 'ticket'
2683
+ AND owner_link.entity_id = legacy.ticket_id
2684
+ WHERE candidate.project_id = source.project_id
2685
+ AND candidate.kind IN ('build', 'build_follow_up')
2686
+ AND candidate.id IS NOT legacy.follow_up_run_id
2687
+ AND candidate.parent_run_id IS NOT legacy.execution_run_id
2688
+ AND candidate.created_at <= source.created_at
2689
+ AND EXISTS (SELECT 1 FROM agent_runs agent WHERE agent.execution_run_id = candidate.id)
2690
+ ORDER BY candidate.created_at DESC, candidate.id DESC
2691
+ LIMIT 1
2692
+ ) AS causal_execution_run_id
2693
+ FROM check_repair_details_legacy legacy
2694
+ JOIN execution_runs source ON source.id = legacy.source_batch_run_id;
2695
+
2696
+ INSERT INTO check_repair_details (
2697
+ execution_run_id, source_batch_run_id, target_kind, target_bundle_id,
2698
+ owner_type, owner_id, ticket_id, causal_execution_run_id, causal_agent_run_id,
2699
+ automation_progression_run_id, automation_boundary_purpose, initial_workspace_identity_json,
2700
+ configuration_fingerprint, effective_policy_fingerprint, definition_ids_json,
2701
+ follow_up_run_id, rerun_batch_run_id, terminal_result, terminal_message,
2702
+ retry_of_check_repair_run_id, created_at, updated_at
2703
+ )
2704
+ SELECT legacy.execution_run_id, legacy.source_batch_run_id, legacy.target_kind, legacy.target_bundle_id,
2705
+ 'ticket', legacy.ticket_id, legacy.ticket_id,
2706
+ causal_run.id, causal_agent.id,
2707
+ legacy.automation_progression_run_id, legacy.automation_boundary_purpose,
2708
+ legacy.initial_workspace_identity_json, legacy.configuration_fingerprint,
2709
+ legacy.effective_policy_fingerprint, legacy.definition_ids_json, legacy.follow_up_run_id,
2710
+ legacy.rerun_batch_run_id, legacy.terminal_result, legacy.terminal_message,
2711
+ legacy.retry_of_check_repair_run_id, legacy.created_at, legacy.updated_at
2712
+ FROM check_repair_details_legacy legacy
2713
+ JOIN execution_runs source ON source.id = legacy.source_batch_run_id
2714
+ JOIN check_repair_causal_migration causal ON causal.repair_run_id = legacy.execution_run_id
2715
+ JOIN execution_runs causal_run ON causal_run.id = causal.causal_execution_run_id
2716
+ AND causal_run.project_id = source.project_id
2717
+ AND causal_run.kind IN ('build', 'build_follow_up')
2718
+ JOIN agent_runs causal_agent ON causal_agent.id = (
2719
+ SELECT candidate_agent.id FROM agent_runs candidate_agent
2720
+ WHERE candidate_agent.execution_run_id = causal_run.id
2721
+ ORDER BY CASE WHEN candidate_agent.role = 'main' THEN 0 ELSE 1 END,
2722
+ candidate_agent.run_index ASC, candidate_agent.created_at ASC, candidate_agent.id ASC
2723
+ LIMIT 1
2724
+ )
2725
+ JOIN tickets owner ON owner.id = legacy.ticket_id AND owner.project_id = source.project_id
2726
+ ;
2727
+
2728
+ CREATE TEMP TABLE check_repair_unrecoverable_migration AS
2729
+ SELECT legacy.execution_run_id AS repair_run_id, repair.project_id, legacy.ticket_id,
2730
+ legacy.follow_up_run_id
2731
+ FROM check_repair_details_legacy legacy
2732
+ JOIN execution_runs repair ON repair.id = legacy.execution_run_id AND repair.kind = 'check_repair'
2733
+ LEFT JOIN check_repair_details migrated ON migrated.execution_run_id = legacy.execution_run_id
2734
+ WHERE migrated.execution_run_id IS NULL;
2735
+
2736
+ CREATE TEMP TABLE check_repair_unrecoverable_children_migration AS
2737
+ SELECT child.id AS child_run_id, unrecoverable.project_id, unrecoverable.ticket_id
2738
+ FROM check_repair_unrecoverable_migration unrecoverable
2739
+ JOIN execution_runs child ON child.id = unrecoverable.follow_up_run_id
2740
+ AND child.project_id = unrecoverable.project_id
2741
+ AND child.parent_run_id = unrecoverable.repair_run_id
2742
+ AND child.kind = 'build_follow_up'
2743
+ AND child.status IN ('queued', 'running', 'waiting')
2744
+ JOIN execution_run_links owner_link ON owner_link.execution_run_id = child.id
2745
+ AND owner_link.project_id = child.project_id
2746
+ AND owner_link.entity_type = 'ticket'
2747
+ AND owner_link.entity_id = unrecoverable.ticket_id
2748
+ AND owner_link.relationship = 'primary';
2749
+
2750
+ UPDATE agent_runs
2751
+ SET agent_status = 'error', response_text = COALESCE(response_text, streaming_response),
2752
+ streaming_response = NULL,
2753
+ failure_message = 'Legacy check repair causal provenance could not be recovered',
2754
+ completed_at = COALESCE(completed_at, CURRENT_TIMESTAMP), updated_at = CURRENT_TIMESTAMP
2755
+ WHERE execution_run_id IN (
2756
+ SELECT child_run_id FROM check_repair_unrecoverable_children_migration
2757
+ ) AND agent_status = 'running' AND completed_at IS NULL;
2758
+
2759
+ UPDATE execution_runs
2760
+ SET status = 'interrupted', domain_status = 'interrupted',
2761
+ failure_code = 'legacy_repair_provenance_unavailable',
2762
+ failure_message = 'Legacy check repair causal provenance could not be recovered',
2763
+ completed_at = COALESCE(completed_at, CURRENT_TIMESTAMP), updated_at = CURRENT_TIMESTAMP
2764
+ WHERE id IN (
2765
+ SELECT child_run_id FROM check_repair_unrecoverable_children_migration
2766
+ ) AND status IN ('queued', 'running', 'waiting');
2767
+
2768
+ UPDATE tickets
2769
+ SET agent_status = 'error',
2770
+ agent_log = 'Legacy check repair causal provenance could not be recovered',
2771
+ streaming_response = NULL, updated_at = CURRENT_TIMESTAMP
2772
+ WHERE agent_status = 'running' AND EXISTS (
2773
+ SELECT 1 FROM check_repair_unrecoverable_children_migration child
2774
+ WHERE child.project_id = tickets.project_id AND child.ticket_id = tickets.id
2775
+ );
2776
+
2777
+ UPDATE execution_runs
2778
+ SET status = 'interrupted', domain_status = 'interrupted',
2779
+ failure_code = 'legacy_repair_provenance_unavailable',
2780
+ failure_message = 'Legacy check repair causal provenance could not be recovered',
2781
+ completed_at = COALESCE(completed_at, CURRENT_TIMESTAMP), updated_at = CURRENT_TIMESTAMP
2782
+ WHERE kind = 'check_repair' AND status IN ('queued', 'running', 'waiting')
2783
+ AND id IN (
2784
+ SELECT repair_run_id FROM check_repair_unrecoverable_migration
2785
+ );
2786
+
2787
+ DROP TABLE check_repair_details_legacy;
2788
+
2789
+ CREATE TABLE check_repair_diagnostic_provenance (
2790
+ check_repair_run_id TEXT NOT NULL,
2791
+ ordinal INTEGER NOT NULL,
2792
+ diagnostic_id TEXT NOT NULL,
2793
+ attempt_run_id TEXT NOT NULL,
2794
+ definition_id TEXT NOT NULL,
2795
+ PRIMARY KEY (check_repair_run_id, ordinal),
2796
+ UNIQUE (check_repair_run_id, diagnostic_id),
2797
+ FOREIGN KEY (check_repair_run_id) REFERENCES check_repair_details(execution_run_id) ON DELETE CASCADE,
2798
+ FOREIGN KEY (attempt_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2799
+ CHECK (ordinal >= 0)
2800
+ );
2801
+ CREATE TABLE check_repair_raw_output_provenance (
2802
+ check_repair_run_id TEXT NOT NULL,
2803
+ ordinal INTEGER NOT NULL,
2804
+ attempt_run_id TEXT NOT NULL,
2805
+ definition_id TEXT NOT NULL,
2806
+ PRIMARY KEY (check_repair_run_id, ordinal),
2807
+ UNIQUE (check_repair_run_id, attempt_run_id),
2808
+ FOREIGN KEY (check_repair_run_id) REFERENCES check_repair_details(execution_run_id) ON DELETE CASCADE,
2809
+ FOREIGN KEY (attempt_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2810
+ CHECK (ordinal >= 0)
2811
+ );
2812
+ INSERT INTO check_repair_diagnostic_provenance
2813
+ SELECT migration.* FROM check_repair_diagnostic_provenance_migration migration
2814
+ JOIN check_repair_details repair ON repair.execution_run_id = migration.check_repair_run_id;
2815
+ INSERT INTO check_repair_raw_output_provenance
2816
+ SELECT migration.* FROM check_repair_raw_output_provenance_migration migration
2817
+ JOIN check_repair_details repair ON repair.execution_run_id = migration.check_repair_run_id;
2818
+ DROP TABLE check_repair_diagnostic_provenance_migration;
2819
+ DROP TABLE check_repair_raw_output_provenance_migration;
2820
+ DROP TABLE check_repair_causal_migration;
2821
+ DROP TABLE check_repair_unrecoverable_children_migration;
2822
+ DROP TABLE check_repair_unrecoverable_migration;
2823
+ `);
2824
+ })();
2825
+ }
2826
+ db.exec(`
2508
2827
 
2509
2828
  CREATE TABLE IF NOT EXISTS check_repair_diagnostic_provenance (
2510
2829
  check_repair_run_id TEXT NOT NULL,
@@ -2532,7 +2851,8 @@ db.exec(`
2532
2851
  );
2533
2852
 
2534
2853
  CREATE TRIGGER IF NOT EXISTS preserve_check_repair_provenance
2535
- BEFORE UPDATE OF execution_run_id, source_batch_run_id, target_kind, target_bundle_id, ticket_id,
2854
+ BEFORE UPDATE OF execution_run_id, source_batch_run_id, target_kind, target_bundle_id,
2855
+ owner_type, owner_id, ticket_id, causal_execution_run_id, causal_agent_run_id,
2536
2856
  automation_progression_run_id, automation_boundary_purpose, initial_workspace_identity_json,
2537
2857
  configuration_fingerprint, effective_policy_fingerprint, definition_ids_json,
2538
2858
  retry_of_check_repair_run_id, created_at
@@ -2574,19 +2894,30 @@ db.exec(`
2574
2894
  FROM execution_runs repair
2575
2895
  JOIN execution_runs source ON source.id = NEW.source_batch_run_id AND source.project_id = repair.project_id
2576
2896
  JOIN check_batch_details source_details ON source_details.execution_run_id = source.id
2577
- JOIN tickets ticket ON ticket.id = NEW.ticket_id AND ticket.project_id = repair.project_id
2897
+ JOIN execution_runs causal ON causal.id = NEW.causal_execution_run_id AND causal.project_id = repair.project_id
2898
+ JOIN agent_runs causal_agent ON causal_agent.id = NEW.causal_agent_run_id
2899
+ AND causal_agent.execution_run_id = causal.id
2578
2900
  WHERE repair.id = NEW.execution_run_id AND repair.kind = 'check_repair'
2579
- AND NEW.target_kind = 'bundle' AND NEW.target_bundle_id IS NOT NULL
2901
+ AND ((NEW.target_kind = 'bundle' AND NEW.target_bundle_id IS NOT NULL)
2902
+ OR (NEW.target_kind = 'repo_root' AND NEW.target_bundle_id IS NULL))
2580
2903
  AND source.kind = 'check_batch' AND source.status = 'failed'
2581
2904
  AND source_details.target_kind = NEW.target_kind
2582
2905
  AND source_details.target_bundle_id IS NEW.target_bundle_id
2583
2906
  AND source_details.automation_boundary_purpose IS NEW.automation_boundary_purpose
2907
+ AND causal.kind IN ('build', 'build_follow_up', 'chat_turn')
2908
+ AND ((NEW.owner_type = 'ticket' AND NEW.ticket_id = NEW.owner_id AND causal.kind IN ('build', 'build_follow_up')
2909
+ AND EXISTS (SELECT 1 FROM tickets owner WHERE owner.id = NEW.owner_id AND owner.project_id = repair.project_id
2910
+ AND owner.worktree_bundle_id = NEW.target_bundle_id))
2911
+ OR (NEW.owner_type = 'chat_session' AND NEW.ticket_id IS NULL AND causal.kind = 'chat_turn'
2912
+ AND EXISTS (SELECT 1 FROM chat_sessions owner WHERE owner.id = NEW.owner_id AND owner.project_id = repair.project_id
2913
+ AND owner.mode = 'build' AND owner.target_kind = NEW.target_kind
2914
+ AND owner.target_bundle_id IS NEW.target_bundle_id)))
2584
2915
  AND (NEW.follow_up_run_id IS NULL OR EXISTS (
2585
2916
  SELECT 1 FROM execution_runs follow_up
2586
2917
  WHERE follow_up.id = NEW.follow_up_run_id AND follow_up.project_id = repair.project_id
2587
- AND follow_up.kind = 'build_follow_up' AND follow_up.parent_run_id = repair.id
2918
+ AND follow_up.kind = CASE NEW.owner_type WHEN 'ticket' THEN 'build_follow_up' ELSE 'chat_turn' END
2919
+ AND follow_up.parent_run_id = repair.id
2588
2920
  ))
2589
- AND ticket.worktree_bundle_id = NEW.target_bundle_id
2590
2921
  AND (NEW.retry_of_check_repair_run_id IS NULL OR EXISTS (
2591
2922
  SELECT 1 FROM execution_runs retry_source
2592
2923
  WHERE retry_source.id = NEW.retry_of_check_repair_run_id
@@ -2668,7 +2999,8 @@ db.exec(`
2668
2999
  JOIN execution_runs follow_up ON follow_up.id = NEW.follow_up_run_id
2669
3000
  AND follow_up.project_id = repair.project_id
2670
3001
  WHERE repair.id = OLD.execution_run_id AND repair.kind = 'check_repair'
2671
- AND follow_up.kind = 'build_follow_up' AND follow_up.parent_run_id = repair.id
3002
+ AND follow_up.kind = CASE OLD.owner_type WHEN 'ticket' THEN 'build_follow_up' ELSE 'chat_turn' END
3003
+ AND follow_up.parent_run_id = repair.id
2672
3004
  )
2673
3005
  )
2674
3006
  BEGIN
@@ -2709,6 +3041,97 @@ db.exec(`
2709
3041
  END;
2710
3042
  `);
2711
3043
  db.exec(`
3044
+
3045
+ CREATE TABLE IF NOT EXISTS check_auto_fix_loops (
3046
+ execution_run_id TEXT PRIMARY KEY,
3047
+ project_id TEXT NOT NULL,
3048
+ originating_batch_run_id TEXT NOT NULL UNIQUE,
3049
+ target_kind TEXT NOT NULL,
3050
+ target_bundle_id TEXT,
3051
+ owner_type TEXT NOT NULL,
3052
+ owner_id TEXT NOT NULL,
3053
+ causal_execution_run_id TEXT NOT NULL,
3054
+ causal_agent_run_id TEXT NOT NULL,
3055
+ automation_progression_run_id TEXT,
3056
+ automation_boundary_purpose TEXT,
3057
+ selected_definitions_json TEXT NOT NULL,
3058
+ auto_fix_definition_ids_json TEXT NOT NULL,
3059
+ definition_fingerprint TEXT NOT NULL,
3060
+ owner_config_json TEXT NOT NULL,
3061
+ initial_workspace_identity_json TEXT NOT NULL,
3062
+ cancellation_requested INTEGER NOT NULL DEFAULT 0,
3063
+ attempt_count INTEGER NOT NULL DEFAULT 0,
3064
+ final_batch_run_id TEXT,
3065
+ terminal_reason TEXT,
3066
+ terminal_message TEXT,
3067
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
3068
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
3069
+ FOREIGN KEY (execution_run_id) REFERENCES execution_runs(id) ON DELETE CASCADE,
3070
+ FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
3071
+ FOREIGN KEY (originating_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
3072
+ FOREIGN KEY (causal_execution_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
3073
+ FOREIGN KEY (causal_agent_run_id) REFERENCES agent_runs(id) ON DELETE RESTRICT,
3074
+ FOREIGN KEY (automation_progression_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
3075
+ FOREIGN KEY (final_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
3076
+ CHECK (target_kind IN ('repo_root', 'bundle')),
3077
+ CHECK ((target_kind = 'repo_root' AND target_bundle_id IS NULL)
3078
+ OR (target_kind = 'bundle' AND target_bundle_id IS NOT NULL)),
3079
+ CHECK (owner_type IN ('ticket', 'chat_session')),
3080
+ CHECK (length(definition_fingerprint) = 64),
3081
+ CHECK (cancellation_requested IN (0, 1)),
3082
+ CHECK (attempt_count BETWEEN 0 AND 3),
3083
+ CHECK (terminal_reason IS NULL OR terminal_reason IN (
3084
+ 'passed', 'ineligible', 'non_auto_fixable_only', 'agent_failure',
3085
+ 'no_workspace_changes', 'unchanged_failures', 'user_cancelled',
3086
+ 'stale', 'exhausted', 'interrupted'
3087
+ ))
3088
+ );
3089
+
3090
+ CREATE TABLE IF NOT EXISTS check_auto_fix_attempts (
3091
+ loop_run_id TEXT NOT NULL,
3092
+ ordinal INTEGER NOT NULL,
3093
+ source_batch_run_id TEXT NOT NULL,
3094
+ enabled_failure_signature TEXT NOT NULL,
3095
+ workspace_before_json TEXT NOT NULL,
3096
+ child_run_id TEXT,
3097
+ launch_outcome TEXT,
3098
+ rerun_batch_run_id TEXT,
3099
+ workspace_after_json TEXT,
3100
+ terminal_reason TEXT,
3101
+ terminal_message TEXT,
3102
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
3103
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
3104
+ PRIMARY KEY (loop_run_id, ordinal),
3105
+ FOREIGN KEY (loop_run_id) REFERENCES check_auto_fix_loops(execution_run_id) ON DELETE CASCADE,
3106
+ FOREIGN KEY (source_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
3107
+ FOREIGN KEY (child_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
3108
+ FOREIGN KEY (rerun_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
3109
+ CHECK (ordinal BETWEEN 1 AND 3),
3110
+ CHECK (length(enabled_failure_signature) = 64),
3111
+ CHECK (launch_outcome IS NULL OR launch_outcome IN ('continued', 'fresh', 'rejected', 'completed', 'failed', 'cancelled'))
3112
+ );
3113
+
3114
+ CREATE TRIGGER IF NOT EXISTS preserve_check_auto_fix_loop_snapshot
3115
+ BEFORE UPDATE OF execution_run_id, originating_batch_run_id, target_kind, target_bundle_id,
3116
+ owner_type, owner_id, causal_execution_run_id, causal_agent_run_id,
3117
+ automation_progression_run_id, automation_boundary_purpose, selected_definitions_json,
3118
+ auto_fix_definition_ids_json, definition_fingerprint, owner_config_json,
3119
+ initial_workspace_identity_json, created_at ON check_auto_fix_loops
3120
+ BEGIN
3121
+ SELECT RAISE(ABORT, 'Check auto-fix loop snapshot is immutable');
3122
+ END;
3123
+
3124
+ CREATE TRIGGER IF NOT EXISTS preserve_check_auto_fix_attempt_snapshot
3125
+ BEFORE UPDATE OF loop_run_id, ordinal, source_batch_run_id, enabled_failure_signature,
3126
+ workspace_before_json, created_at ON check_auto_fix_attempts
3127
+ BEGIN
3128
+ SELECT RAISE(ABORT, 'Check auto-fix attempt snapshot is immutable');
3129
+ END;
3130
+
3131
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_check_auto_fix_active_target
3132
+ ON check_auto_fix_loops(target_kind, COALESCE(target_bundle_id, ''))
3133
+ WHERE terminal_reason IS NULL;
3134
+
2712
3135
  CREATE TABLE IF NOT EXISTS check_gate_evaluations (
2713
3136
  id TEXT PRIMARY KEY,
2714
3137
  project_id TEXT NOT NULL,
@@ -2831,6 +3254,23 @@ db.exec(`
2831
3254
  SELECT RAISE(ABORT, 'Invalid check gate evaluation attempt lineage');
2832
3255
  END;
2833
3256
  `);
3257
+ ensureColumn('check_auto_fix_loops', 'project_id', 'TEXT');
3258
+ db.exec(`
3259
+ UPDATE check_auto_fix_loops
3260
+ SET project_id = (
3261
+ SELECT project_id FROM execution_runs WHERE execution_runs.id = check_auto_fix_loops.execution_run_id
3262
+ )
3263
+ WHERE project_id IS NULL;
3264
+ DROP INDEX IF EXISTS idx_check_auto_fix_active_target;
3265
+ CREATE UNIQUE INDEX idx_check_auto_fix_active_target
3266
+ ON check_auto_fix_loops(project_id, target_kind, COALESCE(target_bundle_id, ''))
3267
+ WHERE terminal_reason IS NULL;
3268
+ CREATE TRIGGER IF NOT EXISTS preserve_check_auto_fix_loop_project
3269
+ BEFORE UPDATE OF project_id ON check_auto_fix_loops
3270
+ BEGIN
3271
+ SELECT RAISE(ABORT, 'Check auto-fix loop project is immutable');
3272
+ END;
3273
+ `);
2834
3274
  ensureColumn('check_gate_evaluations', 'expected_attempt_count', 'INTEGER NOT NULL DEFAULT 0');
2835
3275
  ensureColumn('check_gate_evaluations', 'attempts_sealed', 'INTEGER NOT NULL DEFAULT 1');
2836
3276
  db.exec(`
@@ -2871,16 +3311,6 @@ db.exec(`
2871
3311
  WHERE id = NEW.automation_progression_run_id
2872
3312
  AND project_id = NEW.project_id AND kind = 'automation_progression'
2873
3313
  )
2874
- ) OR (
2875
- NEW.source_batch_run_id IS NOT NULL
2876
- AND NOT EXISTS (
2877
- SELECT 1 FROM execution_runs batch
2878
- WHERE batch.id = NEW.source_batch_run_id
2879
- AND (
2880
- (batch.initiated_by <> 'automation' AND batch.parent_run_id IS NULL)
2881
- OR (batch.initiated_by = 'automation' AND batch.parent_run_id = NEW.automation_progression_run_id)
2882
- )
2883
- )
2884
3314
  )
2885
3315
  BEGIN
2886
3316
  SELECT RAISE(ABORT, 'Invalid check gate evaluation lineage');
@@ -3016,32 +3446,46 @@ db.exec(`
3016
3446
  OR (NEW.target_kind = 'repo_root' AND NEW.target_bundle_id IS NULL
3017
3447
  AND bundle.kind = 'project_root')
3018
3448
  )
3019
- AND NOT EXISTS (
3020
- SELECT 1 FROM execution_runs sibling
3449
+ AND NOT EXISTS (
3450
+ SELECT 1 FROM execution_runs sibling
3021
3451
  WHERE sibling.project_id = run.project_id
3022
3452
  AND sibling.parent_run_id = progression.id
3023
3453
  AND sibling.id <> run.id
3024
- AND sibling.kind IN ('check_batch', 'check_repair', 'review', 'automation_review_fix', 'automation_commit', 'automation_push', 'automation_pull_request')
3025
- AND sibling.status IN ('queued', 'running', 'waiting')
3026
- AND NOT (
3027
- sibling.kind = 'check_repair'
3028
- AND NEW.rerun_of_batch_run_id IS NOT NULL
3029
- AND EXISTS (
3030
- SELECT 1 FROM check_repair_details repair_details
3031
- WHERE repair_details.execution_run_id = sibling.id
3032
- AND repair_details.source_batch_run_id = NEW.rerun_of_batch_run_id
3033
- AND repair_details.terminal_result IS NULL
3034
- AND repair_details.automation_progression_run_id = progression.id
3035
- AND repair_details.automation_boundary_purpose = NEW.automation_boundary_purpose
3036
- AND repair_details.target_kind = NEW.target_kind
3037
- AND repair_details.target_bundle_id IS NEW.target_bundle_id
3038
- )
3039
- )
3040
- )
3454
+ AND sibling.kind IN ('check_batch', 'check_repair', 'review', 'automation_review_fix', 'automation_commit', 'automation_push', 'automation_pull_request')
3455
+ AND sibling.status IN ('queued', 'running', 'waiting')
3456
+ AND NOT (
3457
+ sibling.kind = 'check_repair'
3458
+ AND NEW.rerun_of_batch_run_id IS NOT NULL
3459
+ AND EXISTS (
3460
+ SELECT 1 FROM check_repair_details repair_details
3461
+ WHERE repair_details.execution_run_id = sibling.id
3462
+ AND repair_details.source_batch_run_id = NEW.rerun_of_batch_run_id
3463
+ AND repair_details.terminal_result IS NULL
3464
+ AND repair_details.automation_progression_run_id = progression.id
3465
+ AND repair_details.automation_boundary_purpose = NEW.automation_boundary_purpose
3466
+ AND repair_details.target_kind = NEW.target_kind
3467
+ AND repair_details.target_bundle_id IS NEW.target_bundle_id
3468
+ )
3469
+ )
3470
+ )
3041
3471
  )
3472
+ )
3473
+ OR (
3474
+ run.initiated_by = 'automation'
3475
+ AND NEW.purpose = 'auto_fix'
3476
+ AND NEW.rerun_of_batch_run_id IS NOT NULL
3477
+ AND EXISTS (
3478
+ SELECT 1 FROM check_auto_fix_loops loop
3479
+ WHERE loop.execution_run_id = run.parent_run_id
3480
+ AND loop.originating_batch_run_id = NEW.causal_source_run_id
3481
+ AND loop.final_batch_run_id = NEW.rerun_of_batch_run_id
3482
+ AND loop.target_kind = NEW.target_kind
3483
+ AND loop.target_bundle_id IS NEW.target_bundle_id
3484
+ AND loop.terminal_reason IS NULL
3485
+ )
3486
+ )
3042
3487
  )
3043
3488
  AND (run.initiated_by = 'automation' OR NEW.automation_boundary_purpose IS NULL)
3044
- )
3045
3489
  ) OR (
3046
3490
  NEW.rerun_of_batch_run_id IS NOT NULL AND NOT EXISTS (
3047
3491
  SELECT 1 FROM execution_runs source, execution_runs current