@straiffi/archon 1.7.10 → 1.8.1

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 (162) hide show
  1. package/dist/client/assets/{ActivityView-IUWr92uj.js → ActivityView-CbdDNsdO.js} +1 -1
  2. package/dist/client/assets/{GitHubActionsDialog-0YtWvbFk.js → GitHubActionsDialog-Cmb9Yebr.js} +1 -1
  3. package/dist/client/assets/VerificationDialog-YUi8g2ou.js +4 -0
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-LLMyOR-6.js → architectureDiagram-3BPJPVTR-BgoQ_BIc.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-aa1HitBJ.js → blockDiagram-GPEHLZMM-Xdp2x8BW.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-BpSt5sdj.js → c4Diagram-AAUBKEIU-B220zS5a.js} +1 -1
  7. package/dist/client/assets/channel-CZ1HsfsD.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-D8pf3RNr.js → chunk-2J33WTMH-tYM2mjFx.js} +1 -1
  9. package/dist/client/assets/{chunk-3OPIFGDE-_yx1N2iT.js → chunk-3OPIFGDE-CTITOUvV.js} +1 -1
  10. package/dist/client/assets/{chunk-5ZQYHXKU-CYURGSbL.js → chunk-5ZQYHXKU-D45jJDNN.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-Ci2Mu1zz.js → chunk-727SXJPM-X90EdJX4.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-CqVgrKze.js → chunk-AQP2D5EJ-PEoVUnQP.js} +1 -1
  13. package/dist/client/assets/{chunk-CSCIHK7Q-CT8aPk-o.js → chunk-CSCIHK7Q-CLWG2R5F.js} +1 -1
  14. package/dist/client/assets/{chunk-KSCS5N6A-yM1i0J-p.js → chunk-KSCS5N6A-BamDWXh_.js} +1 -1
  15. package/dist/client/assets/{chunk-L5ZTLDWV-BKMGr31Z.js → chunk-L5ZTLDWV-05TaQDcG.js} +1 -1
  16. package/dist/client/assets/{chunk-LZXEDZCA-A7oUDETP.js → chunk-LZXEDZCA-CjsbWN3c.js} +2 -2
  17. package/dist/client/assets/{chunk-ND2GUHAM-Cagc-XAG.js → chunk-ND2GUHAM-BVmPBEeD.js} +1 -1
  18. package/dist/client/assets/{chunk-NZK2D7GU-pe8tKLWI.js → chunk-NZK2D7GU-DNZ6WouC.js} +1 -1
  19. package/dist/client/assets/{chunk-O5CBEL6O-BahwghWW.js → chunk-O5CBEL6O-Dfonikgz.js} +1 -1
  20. package/dist/client/assets/{chunk-WU5MYG2G-BfSAFufr.js → chunk-WU5MYG2G-CyiTxcGh.js} +1 -1
  21. package/dist/client/assets/classDiagram-4FO5ZUOK-D4DcLL7k.js +1 -0
  22. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Benwuous.js +1 -0
  23. package/dist/client/assets/{dagre-BM42HDAG-DCma6-hS.js → dagre-BM42HDAG-9jv2zUKk.js} +1 -1
  24. package/dist/client/assets/{diagram-2AECGRRQ-DUeggZrO.js → diagram-2AECGRRQ-DmF0Td45.js} +1 -1
  25. package/dist/client/assets/{diagram-5GNKFQAL-CdjOKGtl.js → diagram-5GNKFQAL-DJNMQ7tI.js} +1 -1
  26. package/dist/client/assets/{diagram-KO2AKTUF-CYjXhgh_.js → diagram-KO2AKTUF-EfcoL9E4.js} +1 -1
  27. package/dist/client/assets/{diagram-LMA3HP47-DYBCh4cm.js → diagram-LMA3HP47-C77Lzbhk.js} +1 -1
  28. package/dist/client/assets/{diagram-OG6HWLK6-D3HPQt3z.js → diagram-OG6HWLK6-7GPyDhpX.js} +1 -1
  29. package/dist/client/assets/{erDiagram-TEJ5UH35-mG8Yztg-.js → erDiagram-TEJ5UH35-B-mOpWb2.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-I6XJVG4X-4FvjuoSZ.js → flowDiagram-I6XJVG4X-Bbte_qNo.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-6RSMTGT7-ByCQI8ft.js → ganttDiagram-6RSMTGT7-DC4ltCss.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CzEUA7nB.js → gitGraphDiagram-PVQCEYII-4F87Sxzr.js} +1 -1
  33. package/dist/client/assets/index-D4qr0sQx.css +2 -0
  34. package/dist/client/assets/index-DCCSprul.js +274 -0
  35. package/dist/client/assets/{infoDiagram-5YYISTIA-Q9CqnXO1.js → infoDiagram-5YYISTIA-CHQaSUgi.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CO8BToGf.js → ishikawaDiagram-YF4QCWOH-CuXLE_L1.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-JHISSGLW-DtOyWvDJ.js → journeyDiagram-JHISSGLW-Dkj3B7DF.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-UN3LZRKU-DIn9bYpk.js → kanban-definition-UN3LZRKU-DAH_WodG.js} +1 -1
  39. package/dist/client/assets/{line-H-k0M9_s.js → line-CxewTz3a.js} +1 -1
  40. package/dist/client/assets/{mermaid-parser.core-CZOUS5zK.js → mermaid-parser.core-W88fOlNl.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-Ck71Qr7h.js → mermaid.core-CjJzzlTK.js} +3 -3
  42. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BRFEcJw_.js → mindmap-definition-RKZ34NQL-BdCMvjnB.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-4H26LBE5-CwihXXcJ.js → pieDiagram-4H26LBE5-HeTRmhyt.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-moiRw4Cm.js → quadrantDiagram-W4KKPZXB-CLXv5YTL.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-4Y6WPE33-tshvM_vV.js → requirementDiagram-4Y6WPE33-DB4DS6CS.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-rtKIuBEp.js → sankeyDiagram-5OEKKPKP-BjUVvwEU.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-lkTCo4wE.js → sequenceDiagram-3UESZ5HK-FH39XKuz.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-AJRCARHV-CFQhVUSd.js → stateDiagram-AJRCARHV-U8DIBjJj.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Cw8SkdDx.js +1 -0
  50. package/dist/client/assets/{timeline-definition-PNZ67QCA-BmlQ7Wiy.js → timeline-definition-PNZ67QCA-cuO_PRfp.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-CIIHVFJN-CZyZepcT.js → vennDiagram-CIIHVFJN-BR7Vk6hi.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-C2b52gni.js → wardleyDiagram-YWT4CUSO-C443pGVk.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-2RQKCTM6-FqnROUHq.js → xychartDiagram-2RQKCTM6-BtjwBwmu.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/server/db.js +503 -9
  56. package/dist/server/db.js.map +1 -1
  57. package/dist/server/index.js +374 -201
  58. package/dist/server/index.js.map +1 -1
  59. package/dist/server/lib/activeAutomationChild.js +6 -1
  60. package/dist/server/lib/activeAutomationChild.js.map +1 -1
  61. package/dist/server/lib/automationCheckGate.js +183 -50
  62. package/dist/server/lib/automationCheckGate.js.map +1 -1
  63. package/dist/server/lib/automationProgression.js +76 -9
  64. package/dist/server/lib/automationProgression.js.map +1 -1
  65. package/dist/server/lib/automationReconciliation.js +65 -0
  66. package/dist/server/lib/automationReconciliation.js.map +1 -1
  67. package/dist/server/lib/automationResumeDecision.js +69 -32
  68. package/dist/server/lib/automationResumeDecision.js.map +1 -1
  69. package/dist/server/lib/automationReviewGate.js +6 -1
  70. package/dist/server/lib/automationReviewGate.js.map +1 -1
  71. package/dist/server/lib/bundleActivity.js +20 -0
  72. package/dist/server/lib/bundleActivity.js.map +1 -1
  73. package/dist/server/lib/bundleReviewOperations.js +36 -5
  74. package/dist/server/lib/bundleReviewOperations.js.map +1 -1
  75. package/dist/server/lib/chatOperations.js +9 -0
  76. package/dist/server/lib/chatOperations.js.map +1 -1
  77. package/dist/server/lib/chatTicketProposals.js +2 -1
  78. package/dist/server/lib/chatTicketProposals.js.map +1 -1
  79. package/dist/server/lib/chats.js +2 -0
  80. package/dist/server/lib/chats.js.map +1 -1
  81. package/dist/server/lib/checkDiagnosticParsers.js +301 -0
  82. package/dist/server/lib/checkDiagnosticParsers.js.map +1 -0
  83. package/dist/server/lib/checkDiagnostics.js +142 -0
  84. package/dist/server/lib/checkDiagnostics.js.map +1 -0
  85. package/dist/server/lib/checkGateEvaluations.js +89 -1
  86. package/dist/server/lib/checkGateEvaluations.js.map +1 -1
  87. package/dist/server/lib/checkRepair.js +1031 -0
  88. package/dist/server/lib/checkRepair.js.map +1 -0
  89. package/dist/server/lib/checkRepairLifecycle.js +56 -0
  90. package/dist/server/lib/checkRepairLifecycle.js.map +1 -0
  91. package/dist/server/lib/checkRepairReconciliation.js +145 -0
  92. package/dist/server/lib/checkRepairReconciliation.js.map +1 -0
  93. package/dist/server/lib/checkRunner.js +122 -23
  94. package/dist/server/lib/checkRunner.js.map +1 -1
  95. package/dist/server/lib/checkRuns.js +60 -6
  96. package/dist/server/lib/checkRuns.js.map +1 -1
  97. package/dist/server/lib/executionReconciliation.js +21 -11
  98. package/dist/server/lib/executionReconciliation.js.map +1 -1
  99. package/dist/server/lib/executionRetryPredicates.js +3 -1
  100. package/dist/server/lib/executionRetryPredicates.js.map +1 -1
  101. package/dist/server/lib/executionRunRetention.js +24 -3
  102. package/dist/server/lib/executionRunRetention.js.map +1 -1
  103. package/dist/server/lib/executionRuns.js +15 -3
  104. package/dist/server/lib/executionRuns.js.map +1 -1
  105. package/dist/server/lib/git.js +57 -7
  106. package/dist/server/lib/git.js.map +1 -1
  107. package/dist/server/lib/planModeQuestions.js +2 -0
  108. package/dist/server/lib/planModeQuestions.js.map +1 -1
  109. package/dist/server/lib/planQuestions.js +39 -7
  110. package/dist/server/lib/planQuestions.js.map +1 -1
  111. package/dist/server/lib/postBuildCheckRuns.js +43 -0
  112. package/dist/server/lib/postBuildCheckRuns.js.map +1 -0
  113. package/dist/server/lib/postBuildChecks.js +122 -0
  114. package/dist/server/lib/postBuildChecks.js.map +1 -0
  115. package/dist/server/lib/projectMemorySuggestions.js +2 -1
  116. package/dist/server/lib/projectMemorySuggestions.js.map +1 -1
  117. package/dist/server/lib/projects.js +24 -25
  118. package/dist/server/lib/projects.js.map +1 -1
  119. package/dist/server/lib/pullRequestContent.js +14 -0
  120. package/dist/server/lib/pullRequestContent.js.map +1 -1
  121. package/dist/server/lib/requiredCheckPolicy.js +13 -49
  122. package/dist/server/lib/requiredCheckPolicy.js.map +1 -1
  123. package/dist/server/lib/requiredCheckPolicyPrompt.js +46 -0
  124. package/dist/server/lib/requiredCheckPolicyPrompt.js.map +1 -0
  125. package/dist/server/lib/specDraft.js +6 -0
  126. package/dist/server/lib/specDraft.js.map +1 -1
  127. package/dist/server/lib/specResources.js +42 -0
  128. package/dist/server/lib/specResources.js.map +1 -0
  129. package/dist/server/lib/specs.js +9 -5
  130. package/dist/server/lib/specs.js.map +1 -1
  131. package/dist/server/lib/ticketAutomation.js +10 -3
  132. package/dist/server/lib/ticketAutomation.js.map +1 -1
  133. package/dist/server/lib/ticketAutomationCommit.js +36 -11
  134. package/dist/server/lib/ticketAutomationCommit.js.map +1 -1
  135. package/dist/server/lib/ticketFollowUpOperations.js +33 -9
  136. package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
  137. package/dist/server/lib/tickets.js +11 -32
  138. package/dist/server/lib/tickets.js.map +1 -1
  139. package/dist/server/lib/types.js +1 -0
  140. package/dist/server/lib/types.js.map +1 -1
  141. package/dist/server/workers/build.js +42 -12
  142. package/dist/server/workers/build.js.map +1 -1
  143. package/dist/server/workers/chat.js +18 -3
  144. package/dist/server/workers/chat.js.map +1 -1
  145. package/dist/server/workers/followUp.js +70 -17
  146. package/dist/server/workers/followUp.js.map +1 -1
  147. package/dist/server/workers/plan.js +5 -3
  148. package/dist/server/workers/plan.js.map +1 -1
  149. package/dist/server/workers/planFollowUp.js +5 -6
  150. package/dist/server/workers/planFollowUp.js.map +1 -1
  151. package/dist/server/workers/review.js +38 -6
  152. package/dist/server/workers/review.js.map +1 -1
  153. package/dist/server/workers/specSession.js +10 -4
  154. package/dist/server/workers/specSession.js.map +1 -1
  155. package/package.json +1 -1
  156. package/dist/client/assets/VerificationDialog-B4Pyz1LV.js +0 -4
  157. package/dist/client/assets/channel-DWPRC-jg.js +0 -1
  158. package/dist/client/assets/classDiagram-4FO5ZUOK-DBZaZLLc.js +0 -1
  159. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B7HsVFoJ.js +0 -1
  160. package/dist/client/assets/index-D-miHTTb.js +0 -273
  161. package/dist/client/assets/index-TAQIlNdJ.css +0 -2
  162. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-rrN4DgWp.js +0 -1
package/dist/server/db.js CHANGED
@@ -59,6 +59,7 @@ db.exec(`
59
59
  check_definitions TEXT,
60
60
  required_check_definition_ids TEXT NOT NULL DEFAULT '[]',
61
61
  pre_review_check_definition_ids TEXT NOT NULL DEFAULT '[]',
62
+ post_build_check_definition_ids TEXT NOT NULL DEFAULT '[]',
62
63
  test_commands TEXT,
63
64
  run_ide TEXT,
64
65
  preview_service_name TEXT,
@@ -132,12 +133,14 @@ ensureColumn('worktree_bundles', 'automation_depth', 'TEXT');
132
133
  ensureColumn('worktree_bundles', 'automation_review_fix_enabled', 'INTEGER');
133
134
  ensureColumn('worktree_bundles', 'automation_pause_on_open_questions', 'INTEGER');
134
135
  ensureColumn('worktree_bundles', 'required_check_policy_override', 'TEXT');
136
+ db.exec('UPDATE worktree_bundles SET required_check_policy_override = NULL WHERE required_check_policy_override IS NOT NULL');
135
137
  ensureColumn('projects', 'worktree_sync', 'TEXT');
136
138
  ensureColumn('projects', 'active_target_kind', "TEXT NOT NULL DEFAULT 'repo_root'");
137
139
  ensureColumn('projects', 'active_target_bundle_id', 'TEXT');
138
140
  ensureColumn('projects', 'check_definitions', 'TEXT');
139
141
  ensureColumn('projects', 'required_check_definition_ids', "TEXT NOT NULL DEFAULT '[]'");
140
142
  ensureColumn('projects', 'pre_review_check_definition_ids', "TEXT NOT NULL DEFAULT '[]'");
143
+ ensureColumn('projects', 'post_build_check_definition_ids', "TEXT NOT NULL DEFAULT '[]'");
141
144
  ensureColumn('projects', 'test_commands', 'TEXT');
142
145
  ensureColumn('projects', 'helper_model', 'TEXT');
143
146
  ensureColumn('projects', 'helper_variant', 'TEXT');
@@ -824,6 +827,7 @@ db.exec(`
824
827
  likely_files_json TEXT,
825
828
  tickets_json TEXT,
826
829
  dependencies_json TEXT,
830
+ resource_urls_json TEXT,
827
831
  mermaid TEXT,
828
832
  mockup TEXT,
829
833
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
@@ -833,6 +837,7 @@ db.exec(`
833
837
  ensureColumn('spec_snapshots', 'plan_text', 'TEXT');
834
838
  ensureColumn('spec_snapshots', 'mockup', 'TEXT');
835
839
  ensureColumn('spec_snapshots', 'file_relation_graph', 'TEXT');
840
+ ensureColumn('spec_snapshots', 'resource_urls_json', 'TEXT');
836
841
  db.exec(`
837
842
  CREATE TABLE IF NOT EXISTS spec_message_attachments (
838
843
  attachment_id TEXT PRIMARY KEY,
@@ -1812,6 +1817,7 @@ db.exec(`
1812
1817
  target_label TEXT,
1813
1818
  selected_definition_snapshots_json TEXT NOT NULL,
1814
1819
  initial_workspace_identity_json TEXT NOT NULL,
1820
+ final_workspace_identity_json TEXT,
1815
1821
  configuration_fingerprint TEXT NOT NULL,
1816
1822
  stop_requested INTEGER NOT NULL DEFAULT 0,
1817
1823
  queued_count INTEGER NOT NULL DEFAULT 0,
@@ -1822,8 +1828,11 @@ db.exec(`
1822
1828
  interrupted_count INTEGER NOT NULL DEFAULT 0,
1823
1829
  rerun_of_batch_run_id TEXT,
1824
1830
  automation_boundary_purpose TEXT,
1831
+ purpose TEXT NOT NULL DEFAULT 'manual',
1832
+ causal_source_run_id TEXT,
1825
1833
  FOREIGN KEY (execution_run_id) REFERENCES execution_runs(id) ON DELETE CASCADE,
1826
1834
  FOREIGN KEY (rerun_of_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
1835
+ FOREIGN KEY (causal_source_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
1827
1836
  CHECK (target_kind IN ('repo_root', 'bundle')),
1828
1837
  CHECK ((target_kind = 'repo_root' AND target_bundle_id IS NULL)
1829
1838
  OR (target_kind = 'bundle' AND target_bundle_id IS NOT NULL AND length(trim(target_bundle_id)) > 0)),
@@ -1837,6 +1846,19 @@ db.exec(`
1837
1846
  )
1838
1847
  `);
1839
1848
  ensureColumn('check_batch_details', 'automation_boundary_purpose', 'TEXT');
1849
+ ensureColumn('check_batch_details', 'final_workspace_identity_json', 'TEXT');
1850
+ ensureColumn('check_batch_details', 'purpose', "TEXT NOT NULL DEFAULT 'manual'");
1851
+ ensureColumn('check_batch_details', 'causal_source_run_id', 'TEXT');
1852
+ db.exec(`
1853
+ UPDATE check_batch_details
1854
+ SET purpose = 'automation_gate'
1855
+ WHERE automation_boundary_purpose IS NOT NULL AND purpose = 'manual'
1856
+ `);
1857
+ db.exec(`
1858
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_check_batch_post_build_source
1859
+ ON check_batch_details(causal_source_run_id)
1860
+ WHERE purpose = 'post_build'
1861
+ `);
1840
1862
  db.exec(`
1841
1863
  CREATE TABLE IF NOT EXISTS check_attempt_details (
1842
1864
  execution_run_id TEXT PRIMARY KEY,
@@ -1862,13 +1884,420 @@ db.exec(`
1862
1884
  FOREIGN KEY (rerun_of_attempt_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
1863
1885
  CHECK (length(definition_id) BETWEEN 1 AND 500),
1864
1886
  CHECK (length(definition_fingerprint) = 64 AND definition_fingerprint NOT GLOB '*[^0-9a-f]*'),
1865
- CHECK (outcome IS NULL OR outcome IN ('passed', 'command_failure', 'spawn_failure', 'timeout',
1887
+ CHECK (outcome IS NULL OR outcome IN ('passed', 'command_failure', 'spawn_failure',
1888
+ 'infrastructure_failure', 'evidence_persistence_failure', 'timeout',
1866
1889
  'workspace_changed_before_start', 'user_stop', 'restart_loss', 'shutdown_loss')),
1867
1890
  CHECK (output_revision >= 0 AND retained_start_offset >= 0 AND retained_end_offset >= retained_start_offset
1868
1891
  AND total_persisted_bytes >= retained_end_offset),
1869
1892
  CHECK (output_retention_state IN ('retained', 'expired'))
1870
1893
  )
1871
1894
  `);
1895
+ ensureColumn('check_attempt_details', 'retained_end_offset', 'INTEGER NOT NULL DEFAULT 0');
1896
+ ensureColumn('check_attempt_details', 'retained_output', "TEXT NOT NULL DEFAULT ''");
1897
+ const checkAttemptSchema = db.prepare(`
1898
+ SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'check_attempt_details'
1899
+ `).get();
1900
+ if (checkAttemptSchema && !checkAttemptSchema.sql.includes('evidence_persistence_failure')) {
1901
+ db.transaction(() => {
1902
+ db.exec(`
1903
+ DROP TRIGGER IF EXISTS preserve_check_attempt_snapshots;
1904
+ DROP TRIGGER IF EXISTS preserve_completed_check_attempt_details;
1905
+ ALTER TABLE check_attempt_details RENAME TO check_attempt_details_legacy;
1906
+
1907
+ CREATE TABLE check_attempt_details (
1908
+ execution_run_id TEXT PRIMARY KEY,
1909
+ batch_run_id TEXT NOT NULL,
1910
+ definition_id TEXT NOT NULL,
1911
+ command_snapshot_json TEXT NOT NULL,
1912
+ definition_fingerprint TEXT NOT NULL,
1913
+ initial_workspace_identity_json TEXT NOT NULL,
1914
+ final_workspace_identity_json TEXT,
1915
+ outcome TEXT,
1916
+ rerun_of_attempt_run_id TEXT,
1917
+ exit_code INTEGER,
1918
+ exit_signal TEXT,
1919
+ exit_error TEXT,
1920
+ output_revision INTEGER NOT NULL DEFAULT 0,
1921
+ retained_start_offset INTEGER NOT NULL DEFAULT 0,
1922
+ retained_end_offset INTEGER NOT NULL DEFAULT 0,
1923
+ total_persisted_bytes INTEGER NOT NULL DEFAULT 0,
1924
+ retained_output TEXT NOT NULL DEFAULT '',
1925
+ output_retention_state TEXT NOT NULL DEFAULT 'retained',
1926
+ FOREIGN KEY (execution_run_id) REFERENCES execution_runs(id) ON DELETE CASCADE,
1927
+ FOREIGN KEY (batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
1928
+ FOREIGN KEY (rerun_of_attempt_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
1929
+ CHECK (length(definition_id) BETWEEN 1 AND 500),
1930
+ CHECK (length(definition_fingerprint) = 64 AND definition_fingerprint NOT GLOB '*[^0-9a-f]*'),
1931
+ CHECK (outcome IS NULL OR outcome IN ('passed', 'command_failure', 'spawn_failure',
1932
+ 'infrastructure_failure', 'evidence_persistence_failure', 'timeout',
1933
+ 'workspace_changed_before_start', 'user_stop', 'restart_loss', 'shutdown_loss')),
1934
+ CHECK (output_revision >= 0 AND retained_start_offset >= 0 AND retained_end_offset >= retained_start_offset
1935
+ AND total_persisted_bytes >= retained_end_offset),
1936
+ CHECK (output_retention_state IN ('retained', 'expired'))
1937
+ );
1938
+
1939
+ INSERT INTO check_attempt_details (
1940
+ execution_run_id, batch_run_id, definition_id, command_snapshot_json, definition_fingerprint,
1941
+ initial_workspace_identity_json, final_workspace_identity_json, outcome, rerun_of_attempt_run_id,
1942
+ exit_code, exit_signal, exit_error, output_revision, retained_start_offset, retained_end_offset,
1943
+ total_persisted_bytes, retained_output, output_retention_state
1944
+ )
1945
+ SELECT
1946
+ execution_run_id, batch_run_id, definition_id, command_snapshot_json, definition_fingerprint,
1947
+ initial_workspace_identity_json, final_workspace_identity_json, outcome, rerun_of_attempt_run_id,
1948
+ exit_code, exit_signal, exit_error, output_revision, retained_start_offset, retained_end_offset,
1949
+ total_persisted_bytes, retained_output, output_retention_state
1950
+ FROM check_attempt_details_legacy;
1951
+
1952
+ DROP TABLE check_attempt_details_legacy;
1953
+ `);
1954
+ })();
1955
+ }
1956
+ db.exec(`
1957
+ CREATE TABLE IF NOT EXISTS check_attempt_diagnostic_parsers (
1958
+ attempt_run_id TEXT PRIMARY KEY,
1959
+ definition_id TEXT NOT NULL,
1960
+ parser_kind TEXT NOT NULL DEFAULT 'none',
1961
+ parser_version TEXT,
1962
+ status TEXT NOT NULL DEFAULT 'pending',
1963
+ diagnostic_count INTEGER NOT NULL DEFAULT 0,
1964
+ truncated INTEGER NOT NULL DEFAULT 0,
1965
+ retention_state TEXT NOT NULL DEFAULT 'retained',
1966
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1967
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1968
+ FOREIGN KEY (attempt_run_id) REFERENCES check_attempt_details(execution_run_id) ON DELETE CASCADE,
1969
+ CHECK (length(definition_id) BETWEEN 1 AND 500),
1970
+ CHECK (parser_kind IN ('typescript', 'eslint', 'none')),
1971
+ CHECK ((parser_kind = 'none' AND parser_version IS NULL) OR (parser_kind <> 'none' AND length(parser_version) BETWEEN 1 AND 32)),
1972
+ CHECK (status IN ('pending', 'complete', 'unsupported', 'malformed', 'interrupted')),
1973
+ CHECK (diagnostic_count BETWEEN 0 AND 500),
1974
+ CHECK (truncated IN (0, 1)),
1975
+ CHECK (retention_state IN ('retained', 'expired'))
1976
+ );
1977
+
1978
+ CREATE TABLE IF NOT EXISTS check_attempt_diagnostics (
1979
+ id TEXT PRIMARY KEY,
1980
+ attempt_run_id TEXT NOT NULL,
1981
+ definition_id TEXT NOT NULL,
1982
+ ordinal INTEGER NOT NULL,
1983
+ severity TEXT NOT NULL,
1984
+ message TEXT NOT NULL,
1985
+ code TEXT,
1986
+ file_path TEXT,
1987
+ start_line INTEGER,
1988
+ start_column INTEGER,
1989
+ end_line INTEGER,
1990
+ end_column INTEGER,
1991
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1992
+ UNIQUE (attempt_run_id, ordinal),
1993
+ FOREIGN KEY (attempt_run_id) REFERENCES check_attempt_details(execution_run_id) ON DELETE CASCADE,
1994
+ CHECK (length(id) BETWEEN 1 AND 100),
1995
+ CHECK (length(definition_id) BETWEEN 1 AND 500),
1996
+ CHECK (severity IN ('error', 'warning', 'info', 'unknown')),
1997
+ CHECK (length(CAST(message AS BLOB)) BETWEEN 1 AND 16384),
1998
+ CHECK (code IS NULL OR length(CAST(code AS BLOB)) BETWEEN 1 AND 256),
1999
+ CHECK (file_path IS NULL OR length(CAST(file_path AS BLOB)) BETWEEN 1 AND 4096),
2000
+ CHECK ((file_path IS NULL AND start_line IS NULL AND start_column IS NULL AND end_line IS NULL AND end_column IS NULL)
2001
+ OR (file_path IS NOT NULL AND start_line >= 1 AND start_column >= 1
2002
+ AND ((end_line IS NULL AND end_column IS NULL)
2003
+ OR (end_line >= start_line AND end_column >= 1 AND (end_line > start_line OR end_column >= start_column))))),
2004
+ CHECK (ordinal BETWEEN 0 AND 499)
2005
+ );
2006
+
2007
+ CREATE TRIGGER IF NOT EXISTS validate_check_diagnostic_parser_insert
2008
+ BEFORE INSERT ON check_attempt_diagnostic_parsers
2009
+ WHEN NOT EXISTS (
2010
+ SELECT 1 FROM check_attempt_details attempt
2011
+ WHERE attempt.execution_run_id = NEW.attempt_run_id AND attempt.definition_id = NEW.definition_id
2012
+ )
2013
+ BEGIN
2014
+ SELECT RAISE(ABORT, 'Invalid check diagnostic parser ownership');
2015
+ END;
2016
+
2017
+ CREATE TRIGGER IF NOT EXISTS validate_check_diagnostic_insert
2018
+ BEFORE INSERT ON check_attempt_diagnostics
2019
+ WHEN NOT EXISTS (
2020
+ SELECT 1
2021
+ FROM check_attempt_diagnostic_parsers parser
2022
+ WHERE parser.attempt_run_id = NEW.attempt_run_id
2023
+ AND parser.definition_id = NEW.definition_id
2024
+ AND parser.status = 'pending'
2025
+ AND parser.retention_state = 'retained'
2026
+ )
2027
+ BEGIN
2028
+ SELECT RAISE(ABORT, 'Invalid check diagnostic ownership');
2029
+ END;
2030
+
2031
+ CREATE TRIGGER IF NOT EXISTS preserve_check_diagnostics
2032
+ BEFORE UPDATE ON check_attempt_diagnostics
2033
+ BEGIN
2034
+ SELECT RAISE(ABORT, 'Check diagnostics are immutable');
2035
+ END;
2036
+
2037
+ CREATE TRIGGER IF NOT EXISTS preserve_check_diagnostic_parser_ownership
2038
+ BEFORE UPDATE OF attempt_run_id, definition_id, created_at ON check_attempt_diagnostic_parsers
2039
+ BEGIN
2040
+ SELECT RAISE(ABORT, 'Check diagnostic parser ownership is immutable');
2041
+ END;
2042
+
2043
+ CREATE TRIGGER IF NOT EXISTS interrupt_incomplete_check_diagnostic_parser
2044
+ AFTER UPDATE OF outcome ON check_attempt_details
2045
+ WHEN NEW.outcome IS NOT NULL
2046
+ BEGIN
2047
+ UPDATE check_attempt_diagnostic_parsers
2048
+ SET status = 'interrupted', diagnostic_count = (
2049
+ SELECT COUNT(*) FROM check_attempt_diagnostics diagnostic
2050
+ WHERE diagnostic.attempt_run_id = NEW.execution_run_id
2051
+ ), updated_at = CURRENT_TIMESTAMP
2052
+ WHERE attempt_run_id = NEW.execution_run_id AND status = 'pending';
2053
+ END;
2054
+ `);
2055
+ db.exec(`
2056
+ CREATE TABLE IF NOT EXISTS check_repair_details (
2057
+ execution_run_id TEXT PRIMARY KEY,
2058
+ source_batch_run_id TEXT NOT NULL,
2059
+ target_kind TEXT NOT NULL,
2060
+ target_bundle_id TEXT,
2061
+ ticket_id TEXT NOT NULL,
2062
+ automation_progression_run_id TEXT,
2063
+ automation_boundary_purpose TEXT,
2064
+ initial_workspace_identity_json TEXT NOT NULL,
2065
+ configuration_fingerprint TEXT NOT NULL,
2066
+ effective_policy_fingerprint TEXT,
2067
+ definition_ids_json TEXT NOT NULL,
2068
+ follow_up_run_id TEXT,
2069
+ rerun_batch_run_id TEXT,
2070
+ terminal_result TEXT,
2071
+ terminal_message TEXT,
2072
+ retry_of_check_repair_run_id TEXT,
2073
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
2074
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
2075
+ FOREIGN KEY (execution_run_id) REFERENCES execution_runs(id) ON DELETE CASCADE,
2076
+ FOREIGN KEY (source_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2077
+ FOREIGN KEY (automation_progression_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2078
+ FOREIGN KEY (follow_up_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2079
+ FOREIGN KEY (rerun_batch_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2080
+ FOREIGN KEY (retry_of_check_repair_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2081
+ CHECK (target_kind IN ('repo_root', 'bundle')),
2082
+ CHECK ((target_kind = 'repo_root' AND target_bundle_id IS NULL)
2083
+ OR (target_kind = 'bundle' AND target_bundle_id IS NOT NULL AND length(trim(target_bundle_id)) > 0)),
2084
+ CHECK (length(configuration_fingerprint) = 64 AND configuration_fingerprint NOT GLOB '*[^0-9a-f]*'),
2085
+ CHECK (effective_policy_fingerprint IS NULL OR (
2086
+ length(effective_policy_fingerprint) = 64 AND effective_policy_fingerprint NOT GLOB '*[^0-9a-f]*'
2087
+ )),
2088
+ CHECK (automation_boundary_purpose IS NULL OR automation_boundary_purpose IN (
2089
+ 'automation_review', 'automation_commit', 'automation_push', 'automation_pull_request'
2090
+ )),
2091
+ CHECK ((automation_progression_run_id IS NULL AND automation_boundary_purpose IS NULL)
2092
+ OR (automation_progression_run_id IS NOT NULL AND automation_boundary_purpose IS NOT NULL)),
2093
+ CHECK ((automation_progression_run_id IS NULL AND effective_policy_fingerprint IS NULL)
2094
+ OR (automation_progression_run_id IS NOT NULL AND effective_policy_fingerprint IS NOT NULL)),
2095
+ CHECK (terminal_result IS NULL OR terminal_result IN ('passed', 'failed', 'cancelled', 'interrupted')),
2096
+ CHECK (terminal_message IS NULL OR length(CAST(terminal_message AS BLOB)) <= 16384)
2097
+ );
2098
+
2099
+ CREATE TABLE IF NOT EXISTS check_repair_diagnostic_provenance (
2100
+ check_repair_run_id TEXT NOT NULL,
2101
+ ordinal INTEGER NOT NULL,
2102
+ diagnostic_id TEXT NOT NULL,
2103
+ attempt_run_id TEXT NOT NULL,
2104
+ definition_id TEXT NOT NULL,
2105
+ PRIMARY KEY (check_repair_run_id, ordinal),
2106
+ UNIQUE (check_repair_run_id, diagnostic_id),
2107
+ FOREIGN KEY (check_repair_run_id) REFERENCES check_repair_details(execution_run_id) ON DELETE CASCADE,
2108
+ FOREIGN KEY (attempt_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2109
+ CHECK (ordinal >= 0)
2110
+ );
2111
+
2112
+ CREATE TABLE IF NOT EXISTS check_repair_raw_output_provenance (
2113
+ check_repair_run_id TEXT NOT NULL,
2114
+ ordinal INTEGER NOT NULL,
2115
+ attempt_run_id TEXT NOT NULL,
2116
+ definition_id TEXT NOT NULL,
2117
+ PRIMARY KEY (check_repair_run_id, ordinal),
2118
+ UNIQUE (check_repair_run_id, attempt_run_id),
2119
+ FOREIGN KEY (check_repair_run_id) REFERENCES check_repair_details(execution_run_id) ON DELETE CASCADE,
2120
+ FOREIGN KEY (attempt_run_id) REFERENCES execution_runs(id) ON DELETE RESTRICT,
2121
+ CHECK (ordinal >= 0)
2122
+ );
2123
+
2124
+ CREATE TRIGGER IF NOT EXISTS preserve_check_repair_provenance
2125
+ BEFORE UPDATE OF execution_run_id, source_batch_run_id, target_kind, target_bundle_id, ticket_id,
2126
+ automation_progression_run_id, automation_boundary_purpose, initial_workspace_identity_json,
2127
+ configuration_fingerprint, effective_policy_fingerprint, definition_ids_json,
2128
+ retry_of_check_repair_run_id, created_at
2129
+ ON check_repair_details
2130
+ BEGIN
2131
+ SELECT RAISE(ABORT, 'Check repair provenance is immutable');
2132
+ END;
2133
+
2134
+ CREATE TRIGGER IF NOT EXISTS preserve_check_repair_diagnostic_provenance
2135
+ BEFORE UPDATE ON check_repair_diagnostic_provenance
2136
+ BEGIN
2137
+ SELECT RAISE(ABORT, 'Check repair diagnostic provenance is immutable');
2138
+ END;
2139
+
2140
+ CREATE TRIGGER IF NOT EXISTS preserve_check_repair_raw_output_provenance
2141
+ BEFORE UPDATE ON check_repair_raw_output_provenance
2142
+ BEGIN
2143
+ SELECT RAISE(ABORT, 'Check repair raw output provenance is immutable');
2144
+ END;
2145
+
2146
+ CREATE TRIGGER IF NOT EXISTS prevent_check_repair_raw_output_provenance_delete
2147
+ BEFORE DELETE ON check_repair_raw_output_provenance
2148
+ WHEN EXISTS (
2149
+ SELECT 1
2150
+ FROM check_repair_details repair
2151
+ JOIN execution_runs repair_run ON repair_run.id = repair.execution_run_id
2152
+ JOIN projects project ON project.id = repair_run.project_id
2153
+ WHERE repair.execution_run_id = OLD.check_repair_run_id
2154
+ AND project.deletion_started_at IS NULL
2155
+ )
2156
+ BEGIN
2157
+ SELECT RAISE(ABORT, 'Check repair raw output provenance is immutable');
2158
+ END;
2159
+
2160
+ CREATE TRIGGER IF NOT EXISTS validate_check_repair_details_insert
2161
+ BEFORE INSERT ON check_repair_details
2162
+ WHEN NOT EXISTS (
2163
+ SELECT 1
2164
+ FROM execution_runs repair
2165
+ JOIN execution_runs source ON source.id = NEW.source_batch_run_id AND source.project_id = repair.project_id
2166
+ JOIN check_batch_details source_details ON source_details.execution_run_id = source.id
2167
+ JOIN tickets ticket ON ticket.id = NEW.ticket_id AND ticket.project_id = repair.project_id
2168
+ WHERE repair.id = NEW.execution_run_id AND repair.kind = 'check_repair'
2169
+ AND NEW.target_kind = 'bundle' AND NEW.target_bundle_id IS NOT NULL
2170
+ AND source.kind = 'check_batch' AND source.status = 'failed'
2171
+ AND source_details.target_kind = NEW.target_kind
2172
+ AND source_details.target_bundle_id IS NEW.target_bundle_id
2173
+ AND source_details.automation_boundary_purpose IS NEW.automation_boundary_purpose
2174
+ AND (NEW.follow_up_run_id IS NULL OR EXISTS (
2175
+ SELECT 1 FROM execution_runs follow_up
2176
+ WHERE follow_up.id = NEW.follow_up_run_id AND follow_up.project_id = repair.project_id
2177
+ AND follow_up.kind = 'build_follow_up' AND follow_up.parent_run_id = repair.id
2178
+ ))
2179
+ AND ticket.worktree_bundle_id = NEW.target_bundle_id
2180
+ AND (NEW.retry_of_check_repair_run_id IS NULL OR EXISTS (
2181
+ SELECT 1 FROM execution_runs retry_source
2182
+ WHERE retry_source.id = NEW.retry_of_check_repair_run_id
2183
+ AND retry_source.project_id = repair.project_id
2184
+ AND retry_source.kind = 'check_repair'
2185
+ AND retry_source.status IN ('completed', 'failed', 'cancelled', 'interrupted')
2186
+ ))
2187
+ AND ((NEW.automation_progression_run_id IS NULL
2188
+ AND repair.parent_run_id IS NULL AND repair.initiated_by = 'user'
2189
+ AND source.parent_run_id IS NULL AND source.initiated_by = 'user')
2190
+ OR (NEW.automation_progression_run_id = repair.parent_run_id AND repair.initiated_by = 'automation'
2191
+ AND source.parent_run_id = NEW.automation_progression_run_id AND source.initiated_by = 'automation'
2192
+ AND EXISTS (
2193
+ SELECT 1 FROM execution_runs progression
2194
+ WHERE progression.id = NEW.automation_progression_run_id
2195
+ AND progression.project_id = repair.project_id
2196
+ AND progression.kind = 'automation_progression'
2197
+ AND progression.status IN ('queued', 'running', 'waiting')
2198
+ )))
2199
+ )
2200
+ BEGIN
2201
+ SELECT RAISE(ABORT, 'Invalid check repair details');
2202
+ END;
2203
+
2204
+ CREATE TRIGGER IF NOT EXISTS validate_check_repair_diagnostic_provenance_insert
2205
+ BEFORE INSERT ON check_repair_diagnostic_provenance
2206
+ WHEN NOT EXISTS (
2207
+ SELECT 1
2208
+ FROM check_repair_details repair
2209
+ JOIN check_attempt_diagnostics diagnostic ON diagnostic.id = NEW.diagnostic_id
2210
+ JOIN check_attempt_details attempt ON attempt.execution_run_id = NEW.attempt_run_id
2211
+ JOIN execution_runs attempt_run ON attempt_run.id = attempt.execution_run_id
2212
+ JOIN check_attempt_diagnostic_parsers parser ON parser.attempt_run_id = attempt.execution_run_id
2213
+ WHERE repair.execution_run_id = NEW.check_repair_run_id
2214
+ AND diagnostic.attempt_run_id = attempt.execution_run_id
2215
+ AND diagnostic.definition_id = NEW.definition_id
2216
+ AND attempt.definition_id = NEW.definition_id
2217
+ AND attempt.batch_run_id = repair.source_batch_run_id
2218
+ AND attempt.outcome = 'command_failure' AND attempt_run.status = 'failed'
2219
+ AND attempt.output_retention_state = 'retained' AND length(attempt.retained_output) > 0
2220
+ AND parser.status = 'complete' AND parser.retention_state = 'retained'
2221
+ )
2222
+ BEGIN
2223
+ SELECT RAISE(ABORT, 'Invalid check repair diagnostic provenance');
2224
+ END;
2225
+
2226
+ CREATE TRIGGER IF NOT EXISTS validate_check_repair_raw_output_provenance_insert
2227
+ BEFORE INSERT ON check_repair_raw_output_provenance
2228
+ WHEN NOT EXISTS (
2229
+ SELECT 1
2230
+ FROM check_repair_details repair
2231
+ JOIN execution_runs repair_run ON repair_run.id = repair.execution_run_id
2232
+ JOIN check_attempt_details attempt ON attempt.execution_run_id = NEW.attempt_run_id
2233
+ JOIN execution_runs attempt_run ON attempt_run.id = attempt.execution_run_id
2234
+ AND attempt_run.project_id = repair_run.project_id
2235
+ JOIN execution_runs batch_run ON batch_run.id = attempt.batch_run_id
2236
+ AND batch_run.project_id = repair_run.project_id
2237
+ WHERE repair.execution_run_id = NEW.check_repair_run_id
2238
+ AND repair_run.kind = 'check_repair'
2239
+ AND attempt.definition_id = NEW.definition_id
2240
+ AND attempt.batch_run_id = repair.source_batch_run_id
2241
+ AND attempt.outcome = 'command_failure'
2242
+ AND attempt_run.kind = 'check_attempt' AND attempt_run.status = 'failed'
2243
+ AND batch_run.kind = 'check_batch' AND batch_run.status = 'failed'
2244
+ AND attempt.output_retention_state = 'retained'
2245
+ AND length(attempt.retained_output) > 0
2246
+ AND attempt.final_workspace_identity_json IS NOT NULL
2247
+ )
2248
+ BEGIN
2249
+ SELECT RAISE(ABORT, 'Invalid check repair raw output provenance');
2250
+ END;
2251
+
2252
+ CREATE TRIGGER IF NOT EXISTS validate_check_repair_follow_up_update
2253
+ BEFORE UPDATE OF follow_up_run_id ON check_repair_details
2254
+ WHEN NEW.follow_up_run_id IS NOT OLD.follow_up_run_id AND (
2255
+ OLD.follow_up_run_id IS NOT NULL OR NEW.follow_up_run_id IS NULL OR NOT EXISTS (
2256
+ SELECT 1
2257
+ FROM execution_runs repair
2258
+ JOIN execution_runs follow_up ON follow_up.id = NEW.follow_up_run_id
2259
+ AND follow_up.project_id = repair.project_id
2260
+ WHERE repair.id = OLD.execution_run_id AND repair.kind = 'check_repair'
2261
+ AND follow_up.kind = 'build_follow_up' AND follow_up.parent_run_id = repair.id
2262
+ )
2263
+ )
2264
+ BEGIN
2265
+ SELECT RAISE(ABORT, 'Invalid check repair follow-up ownership');
2266
+ END;
2267
+
2268
+ CREATE TRIGGER IF NOT EXISTS validate_check_repair_rerun_update
2269
+ BEFORE UPDATE OF rerun_batch_run_id ON check_repair_details
2270
+ WHEN NEW.rerun_batch_run_id IS NOT OLD.rerun_batch_run_id AND (
2271
+ OLD.rerun_batch_run_id IS NOT NULL OR NEW.rerun_batch_run_id IS NULL OR NOT EXISTS (
2272
+ SELECT 1
2273
+ FROM execution_runs repair
2274
+ JOIN execution_runs rerun ON rerun.id = NEW.rerun_batch_run_id
2275
+ AND rerun.project_id = repair.project_id
2276
+ JOIN check_batch_details rerun_details ON rerun_details.execution_run_id = rerun.id
2277
+ WHERE repair.id = OLD.execution_run_id AND repair.kind = 'check_repair'
2278
+ AND rerun.kind = 'check_batch'
2279
+ AND rerun_details.rerun_of_batch_run_id = OLD.source_batch_run_id
2280
+ AND rerun_details.target_kind = OLD.target_kind
2281
+ AND rerun_details.target_bundle_id IS OLD.target_bundle_id
2282
+ AND rerun_details.automation_boundary_purpose IS OLD.automation_boundary_purpose
2283
+ AND ((OLD.automation_progression_run_id IS NULL
2284
+ AND rerun.initiated_by = 'user' AND rerun.parent_run_id IS NULL)
2285
+ OR (OLD.automation_progression_run_id IS NOT NULL
2286
+ AND rerun.initiated_by = 'automation'
2287
+ AND rerun.parent_run_id = OLD.automation_progression_run_id))
2288
+ )
2289
+ )
2290
+ BEGIN
2291
+ SELECT RAISE(ABORT, 'Invalid check repair rerun ownership');
2292
+ END;
2293
+
2294
+ CREATE TRIGGER IF NOT EXISTS preserve_check_repair_terminal_result
2295
+ BEFORE UPDATE OF terminal_result ON check_repair_details
2296
+ WHEN OLD.terminal_result IS NOT NULL AND NEW.terminal_result IS NOT OLD.terminal_result
2297
+ BEGIN
2298
+ SELECT RAISE(ABORT, 'Check repair terminal result is immutable');
2299
+ END;
2300
+ `);
1872
2301
  db.exec(`
1873
2302
  CREATE TABLE IF NOT EXISTS check_gate_evaluations (
1874
2303
  id TEXT PRIMARY KEY,
@@ -2075,15 +2504,23 @@ db.prepare(`
2075
2504
  `).run();
2076
2505
  db.exec('DROP TRIGGER IF EXISTS validate_check_batch_details_insert');
2077
2506
  db.exec('DROP TRIGGER IF EXISTS preserve_check_batch_snapshots');
2507
+ db.exec('DROP TRIGGER IF EXISTS preserve_completed_check_batch_details');
2078
2508
  db.exec(`
2079
2509
  CREATE TRIGGER IF NOT EXISTS preserve_check_batch_snapshots
2080
2510
  BEFORE UPDATE OF execution_run_id, target_kind, target_bundle_id, target_label,
2081
2511
  selected_definition_snapshots_json, initial_workspace_identity_json, configuration_fingerprint,
2082
- rerun_of_batch_run_id, automation_boundary_purpose ON check_batch_details
2512
+ rerun_of_batch_run_id, automation_boundary_purpose, purpose, causal_source_run_id ON check_batch_details
2083
2513
  BEGIN
2084
2514
  SELECT RAISE(ABORT, 'Check batch snapshots are immutable');
2085
2515
  END;
2086
2516
 
2517
+ CREATE TRIGGER IF NOT EXISTS preserve_completed_check_batch_details
2518
+ BEFORE UPDATE OF final_workspace_identity_json ON check_batch_details
2519
+ WHEN OLD.final_workspace_identity_json IS NOT NULL
2520
+ BEGIN
2521
+ SELECT RAISE(ABORT, 'Completed check batch details are immutable');
2522
+ END;
2523
+
2087
2524
  CREATE TRIGGER IF NOT EXISTS preserve_check_attempt_snapshots
2088
2525
  BEFORE UPDATE OF execution_run_id, batch_run_id, definition_id, command_snapshot_json,
2089
2526
  definition_fingerprint, initial_workspace_identity_json, rerun_of_attempt_run_id
@@ -2108,9 +2545,41 @@ db.exec(`
2108
2545
  WHERE run.id = NEW.execution_run_id
2109
2546
  AND run.kind = 'check_batch'
2110
2547
  AND (
2111
- (run.parent_run_id IS NULL AND run.initiated_by <> 'automation')
2548
+ (run.parent_run_id IS NULL AND run.initiated_by <> 'automation'
2549
+ AND NEW.purpose = 'manual' AND NEW.causal_source_run_id IS NULL)
2550
+ OR (
2551
+ run.initiated_by = 'system'
2552
+ AND NEW.purpose = 'post_build'
2553
+ AND NEW.automation_boundary_purpose IS NULL
2554
+ AND NEW.rerun_of_batch_run_id IS NULL
2555
+ AND NEW.causal_source_run_id = run.parent_run_id
2556
+ AND EXISTS (
2557
+ SELECT 1
2558
+ FROM execution_runs source
2559
+ WHERE source.id = run.parent_run_id
2560
+ AND source.project_id = run.project_id
2561
+ AND source.kind IN ('build', 'build_follow_up', 'chat_turn')
2562
+ AND source.status = 'completed'
2563
+ AND EXISTS (
2564
+ SELECT 1
2565
+ FROM execution_run_links source_owner
2566
+ JOIN execution_run_links batch_owner
2567
+ ON batch_owner.execution_run_id = run.id
2568
+ AND batch_owner.project_id = run.project_id
2569
+ AND batch_owner.relationship = 'primary'
2570
+ AND batch_owner.entity_type = source_owner.entity_type
2571
+ AND batch_owner.entity_id = source_owner.entity_id
2572
+ WHERE source_owner.execution_run_id = source.id
2573
+ AND source_owner.project_id = source.project_id
2574
+ AND source_owner.relationship = 'primary'
2575
+ AND source_owner.entity_type IN ('ticket', 'chat_session')
2576
+ )
2577
+ )
2578
+ )
2112
2579
  OR (
2113
2580
  run.initiated_by = 'automation'
2581
+ AND NEW.purpose = 'automation_gate'
2582
+ AND NEW.causal_source_run_id IS NULL
2114
2583
  AND NEW.automation_boundary_purpose IN ('automation_review', 'automation_commit', 'automation_push', 'automation_pull_request')
2115
2584
  AND EXISTS (
2116
2585
  SELECT 1
@@ -2137,14 +2606,28 @@ db.exec(`
2137
2606
  OR (NEW.target_kind = 'repo_root' AND NEW.target_bundle_id IS NULL
2138
2607
  AND bundle.kind = 'project_root')
2139
2608
  )
2140
- AND NOT EXISTS (
2141
- SELECT 1 FROM execution_runs sibling
2609
+ AND NOT EXISTS (
2610
+ SELECT 1 FROM execution_runs sibling
2142
2611
  WHERE sibling.project_id = run.project_id
2143
2612
  AND sibling.parent_run_id = progression.id
2144
2613
  AND sibling.id <> run.id
2145
- AND sibling.kind IN ('check_batch', 'review', 'automation_review_fix', 'automation_commit', 'automation_push', 'automation_pull_request')
2146
- AND sibling.status IN ('queued', 'running', 'waiting')
2147
- )
2614
+ AND sibling.kind IN ('check_batch', 'check_repair', 'review', 'automation_review_fix', 'automation_commit', 'automation_push', 'automation_pull_request')
2615
+ AND sibling.status IN ('queued', 'running', 'waiting')
2616
+ AND NOT (
2617
+ sibling.kind = 'check_repair'
2618
+ AND NEW.rerun_of_batch_run_id IS NOT NULL
2619
+ AND EXISTS (
2620
+ SELECT 1 FROM check_repair_details repair_details
2621
+ WHERE repair_details.execution_run_id = sibling.id
2622
+ AND repair_details.source_batch_run_id = NEW.rerun_of_batch_run_id
2623
+ AND repair_details.terminal_result IS NULL
2624
+ AND repair_details.automation_progression_run_id = progression.id
2625
+ AND repair_details.automation_boundary_purpose = NEW.automation_boundary_purpose
2626
+ AND repair_details.target_kind = NEW.target_kind
2627
+ AND repair_details.target_bundle_id IS NEW.target_bundle_id
2628
+ )
2629
+ )
2630
+ )
2148
2631
  )
2149
2632
  )
2150
2633
  AND (run.initiated_by = 'automation' OR NEW.automation_boundary_purpose IS NULL)
@@ -2414,6 +2897,12 @@ db.exec('CREATE INDEX IF NOT EXISTS idx_github_actions_details_dispatch ON githu
2414
2897
  db.exec('CREATE INDEX IF NOT EXISTS idx_check_batches_rerun ON check_batch_details(rerun_of_batch_run_id)');
2415
2898
  db.exec('CREATE INDEX IF NOT EXISTS idx_check_attempts_batch ON check_attempt_details(batch_run_id, definition_id)');
2416
2899
  db.exec('CREATE INDEX IF NOT EXISTS idx_check_attempts_rerun ON check_attempt_details(rerun_of_attempt_run_id)');
2900
+ db.exec('CREATE INDEX IF NOT EXISTS idx_check_diagnostics_attempt ON check_attempt_diagnostics(attempt_run_id, ordinal)');
2901
+ db.exec('CREATE INDEX IF NOT EXISTS idx_check_diagnostic_parsers_retention ON check_attempt_diagnostic_parsers(retention_state, attempt_run_id)');
2902
+ db.exec('CREATE INDEX IF NOT EXISTS idx_check_repairs_source ON check_repair_details(source_batch_run_id, created_at DESC)');
2903
+ db.exec('CREATE INDEX IF NOT EXISTS idx_check_repairs_progression ON check_repair_details(automation_progression_run_id, terminal_result)');
2904
+ db.exec('CREATE INDEX IF NOT EXISTS idx_check_repair_diagnostics_attempt ON check_repair_diagnostic_provenance(attempt_run_id)');
2905
+ db.exec('CREATE INDEX IF NOT EXISTS idx_check_repair_raw_output_attempt ON check_repair_raw_output_provenance(attempt_run_id)');
2417
2906
  db.exec('CREATE INDEX IF NOT EXISTS idx_check_gate_evaluations_target ON check_gate_evaluations(project_id, target_kind, target_bundle_id, created_at DESC, id DESC)');
2418
2907
  db.exec('CREATE INDEX IF NOT EXISTS idx_check_gate_evaluations_batch ON check_gate_evaluations(source_batch_run_id)');
2419
2908
  db.exec('CREATE INDEX IF NOT EXISTS idx_check_gate_evaluation_attempts_run ON check_gate_evaluation_attempts(attempt_run_id)');
@@ -2537,6 +3026,7 @@ db.exec(`
2537
3026
  fix_loop_active INTEGER NOT NULL DEFAULT 0,
2538
3027
  automation_progression_run_id TEXT,
2539
3028
  automation_paused_stage_run_id TEXT,
3029
+ automation_paused_boundary_purpose TEXT,
2540
3030
  automation_pause_reason_code TEXT,
2541
3031
  automation_paused_code TEXT,
2542
3032
  automation_paused_reason TEXT,
@@ -2545,12 +3035,16 @@ db.exec(`
2545
3035
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
2546
3036
  PRIMARY KEY (project_id, worktree_bundle_id),
2547
3037
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
2548
- FOREIGN KEY (worktree_bundle_id) REFERENCES worktree_bundles(id) ON DELETE CASCADE
3038
+ FOREIGN KEY (worktree_bundle_id) REFERENCES worktree_bundles(id) ON DELETE CASCADE,
3039
+ CHECK (automation_paused_boundary_purpose IS NULL OR automation_paused_boundary_purpose IN (
3040
+ 'automation_review', 'automation_commit', 'automation_push', 'automation_pull_request'
3041
+ ))
2549
3042
  )
2550
3043
  `);
2551
3044
  ensureColumn('bundle_automation_state', 'automation_paused_code', 'TEXT');
2552
3045
  ensureColumn('bundle_automation_state', 'automation_progression_run_id', 'TEXT');
2553
3046
  ensureColumn('bundle_automation_state', 'automation_paused_stage_run_id', 'TEXT');
3047
+ ensureColumn('bundle_automation_state', 'automation_paused_boundary_purpose', 'TEXT');
2554
3048
  ensureColumn('bundle_automation_state', 'automation_pause_reason_code', 'TEXT');
2555
3049
  db.exec(`
2556
3050
  CREATE TABLE IF NOT EXISTS automation_stage_details (