@straiffi/archon 1.8.20 → 1.8.22

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 (170) hide show
  1. package/dist/client/assets/{ActivityView-DDE3U1zB.js → ActivityView-CdEPjmqD.js} +1 -1
  2. package/dist/client/assets/GitHubActionsDialog-CA189pr2.js +1 -0
  3. package/dist/client/assets/VerificationDialog-BPVkhKRj.js +4 -0
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-oPUmRpZr.js → architectureDiagram-3BPJPVTR-BrZ08mqq.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-0sq1jkkp.js → blockDiagram-GPEHLZMM-b93XwJ6Z.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-BeDxMy7J.js → c4Diagram-AAUBKEIU-BKOdbZ_v.js} +1 -1
  7. package/dist/client/assets/channel-D0zqzBT_.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-D8VGsje2.js → chunk-2J33WTMH-D7Vxn_1m.js} +1 -1
  9. package/dist/client/assets/{chunk-3OPIFGDE-DuTUTeBe.js → chunk-3OPIFGDE-B1zo2UZq.js} +1 -1
  10. package/dist/client/assets/{chunk-5ZQYHXKU-BzX5dPNW.js → chunk-5ZQYHXKU-BajvkDdQ.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-DS9putxN.js → chunk-727SXJPM-CSH7cVpV.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-CWzFZRBv.js → chunk-AQP2D5EJ-DE-wgUNP.js} +1 -1
  13. package/dist/client/assets/{chunk-CSCIHK7Q-DXZ-ADYW.js → chunk-CSCIHK7Q-Ba7JgCEf.js} +1 -1
  14. package/dist/client/assets/{chunk-KSCS5N6A-BD7mL3KK.js → chunk-KSCS5N6A-0o3YWkHM.js} +1 -1
  15. package/dist/client/assets/{chunk-L5ZTLDWV-DmYQi9AP.js → chunk-L5ZTLDWV-DihWczP8.js} +1 -1
  16. package/dist/client/assets/{chunk-LZXEDZCA-CZyFYHvS.js → chunk-LZXEDZCA-Bxnu-hme.js} +2 -2
  17. package/dist/client/assets/{chunk-ND2GUHAM-BxdjvT9I.js → chunk-ND2GUHAM-CoaR5kmP.js} +1 -1
  18. package/dist/client/assets/{chunk-NZK2D7GU-BwuZcZ_9.js → chunk-NZK2D7GU-By92Rd3d.js} +1 -1
  19. package/dist/client/assets/{chunk-O5CBEL6O-CC-TZXr3.js → chunk-O5CBEL6O-D83Pkgfu.js} +1 -1
  20. package/dist/client/assets/{chunk-WU5MYG2G-CJZJkca2.js → chunk-WU5MYG2G-v8OVf5Lz.js} +1 -1
  21. package/dist/client/assets/classDiagram-4FO5ZUOK-ChhyvHH2.js +1 -0
  22. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Cl02kurK.js +1 -0
  23. package/dist/client/assets/{dagre-BM42HDAG-D2L5RvPF.js → dagre-BM42HDAG-Dq1ALF7Z.js} +1 -1
  24. package/dist/client/assets/{diagram-2AECGRRQ-DBJzxSqZ.js → diagram-2AECGRRQ-1t1_TKtu.js} +1 -1
  25. package/dist/client/assets/{diagram-5GNKFQAL-J_1M-Wou.js → diagram-5GNKFQAL-DtvYRL6f.js} +1 -1
  26. package/dist/client/assets/{diagram-KO2AKTUF-CmT1Vbmc.js → diagram-KO2AKTUF-PRNrSX5F.js} +1 -1
  27. package/dist/client/assets/{diagram-LMA3HP47-DfPoJyQI.js → diagram-LMA3HP47-DaTX0DhT.js} +1 -1
  28. package/dist/client/assets/{diagram-OG6HWLK6-D4uEb6vo.js → diagram-OG6HWLK6-C2p9p-92.js} +1 -1
  29. package/dist/client/assets/{erDiagram-TEJ5UH35-l7yIFj0e.js → erDiagram-TEJ5UH35-DKzLtnp-.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-I6XJVG4X-B3caTMes.js → flowDiagram-I6XJVG4X-GdL8lm6e.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-6RSMTGT7-9Am36v9m.js → ganttDiagram-6RSMTGT7-Ok-4XYwT.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-B7OjYsv_.js → gitGraphDiagram-PVQCEYII-DSMrOCK7.js} +1 -1
  33. package/dist/client/assets/index-Bs8KsdV9.js +276 -0
  34. package/dist/client/assets/index-DdwM8Wgt.css +2 -0
  35. package/dist/client/assets/{infoDiagram-5YYISTIA-ZAcNejkW.js → infoDiagram-5YYISTIA-Dl4hRXis.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-BaaJwuN0.js → ishikawaDiagram-YF4QCWOH-C1Dc1fTT.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-JHISSGLW-DjzTU3kM.js → journeyDiagram-JHISSGLW-CUscmJze.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-UN3LZRKU-DYRchzub.js → kanban-definition-UN3LZRKU-C0nwYjQV.js} +1 -1
  39. package/dist/client/assets/{line-X0j9gj61.js → line-ByiuEIg_.js} +1 -1
  40. package/dist/client/assets/{mermaid-parser.core-DCKG87Vl.js → mermaid-parser.core-B-XdAyDR.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-BxI5T2gs.js → mermaid.core-D_bIfuZD.js} +3 -3
  42. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BE7MZzQO.js → mindmap-definition-RKZ34NQL-BGNiPJDK.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-4H26LBE5-D3fTYovg.js → pieDiagram-4H26LBE5-CP6FpAoO.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-B35nXRdQ.js → quadrantDiagram-W4KKPZXB-dsAWBAQn.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-4Y6WPE33-CLBcHvP0.js → requirementDiagram-4Y6WPE33-DJH-zHqF.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DdBCxGHF.js → sankeyDiagram-5OEKKPKP-BJMnZrqH.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-KcLt0X1K.js → sequenceDiagram-3UESZ5HK-CzNjUSlM.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-AJRCARHV-Bq23ObLK.js → stateDiagram-AJRCARHV-jppMhOwh.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CLR1WgdV.js +1 -0
  50. package/dist/client/assets/{timeline-definition-PNZ67QCA-SK3oVUD9.js → timeline-definition-PNZ67QCA-BX2CI5H_.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-CIIHVFJN-COVtAlEC.js → vennDiagram-CIIHVFJN-Bd1KZaXh.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-BvfOOwga.js → wardleyDiagram-YWT4CUSO-D-0BNz49.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DFnAD9k4.js → xychartDiagram-2RQKCTM6-DbYa0ZWK.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/server/db.js +780 -16
  56. package/dist/server/db.js.map +1 -1
  57. package/dist/server/index.js +198 -20
  58. package/dist/server/index.js.map +1 -1
  59. package/dist/server/lib/agent.js +8 -4
  60. package/dist/server/lib/agent.js.map +1 -1
  61. package/dist/server/lib/agentProvider.js +8 -1
  62. package/dist/server/lib/agentProvider.js.map +1 -1
  63. package/dist/server/lib/approvalReconciliation.js +101 -0
  64. package/dist/server/lib/approvalReconciliation.js.map +1 -0
  65. package/dist/server/lib/approvalRequests.js +587 -0
  66. package/dist/server/lib/approvalRequests.js.map +1 -0
  67. package/dist/server/lib/approvalRoutes.js +143 -0
  68. package/dist/server/lib/approvalRoutes.js.map +1 -0
  69. package/dist/server/lib/automationProgression.js +44 -14
  70. package/dist/server/lib/automationProgression.js.map +1 -1
  71. package/dist/server/lib/buildCompletionContinuation.js +14 -3
  72. package/dist/server/lib/buildCompletionContinuation.js.map +1 -1
  73. package/dist/server/lib/buildExecutionEnvironment.js +350 -66
  74. package/dist/server/lib/buildExecutionEnvironment.js.map +1 -1
  75. package/dist/server/lib/buildOpenQuestionOperations.js +47 -3
  76. package/dist/server/lib/buildOpenQuestionOperations.js.map +1 -1
  77. package/dist/server/lib/buildSequences.js +267 -18
  78. package/dist/server/lib/buildSequences.js.map +1 -1
  79. package/dist/server/lib/buildSetupAuthority.js +138 -0
  80. package/dist/server/lib/buildSetupAuthority.js.map +1 -0
  81. package/dist/server/lib/bundleReviewOperations.js +15 -4
  82. package/dist/server/lib/bundleReviewOperations.js.map +1 -1
  83. package/dist/server/lib/checkDefinitions.js +29 -1
  84. package/dist/server/lib/checkDefinitions.js.map +1 -1
  85. package/dist/server/lib/checkRunner.js +306 -27
  86. package/dist/server/lib/checkRunner.js.map +1 -1
  87. package/dist/server/lib/checkRuns.js +4 -2
  88. package/dist/server/lib/checkRuns.js.map +1 -1
  89. package/dist/server/lib/consequentialActionAuthority.js +142 -0
  90. package/dist/server/lib/consequentialActionAuthority.js.map +1 -0
  91. package/dist/server/lib/consequentialActions.js +231 -0
  92. package/dist/server/lib/consequentialActions.js.map +1 -0
  93. package/dist/server/lib/dockerSandboxCredentials.js +64 -10
  94. package/dist/server/lib/dockerSandboxCredentials.js.map +1 -1
  95. package/dist/server/lib/dockerSandboxEnvironmentOperations.js +119 -0
  96. package/dist/server/lib/dockerSandboxEnvironmentOperations.js.map +1 -1
  97. package/dist/server/lib/dockerSandboxExecutionEnvironment.js +174 -11
  98. package/dist/server/lib/dockerSandboxExecutionEnvironment.js.map +1 -1
  99. package/dist/server/lib/dockerSandboxExecutionProvider.js +12 -3
  100. package/dist/server/lib/dockerSandboxExecutionProvider.js.map +1 -1
  101. package/dist/server/lib/dockerSandboxNetworkPolicy.js +222 -0
  102. package/dist/server/lib/dockerSandboxNetworkPolicy.js.map +1 -0
  103. package/dist/server/lib/executionCredentialCapabilities.js +106 -0
  104. package/dist/server/lib/executionCredentialCapabilities.js.map +1 -0
  105. package/dist/server/lib/executionEnvironment.js.map +1 -1
  106. package/dist/server/lib/executionEnvironmentContexts.js +59 -3
  107. package/dist/server/lib/executionEnvironmentContexts.js.map +1 -1
  108. package/dist/server/lib/executionEnvironmentLifecycle.js +34 -0
  109. package/dist/server/lib/executionEnvironmentLifecycle.js.map +1 -1
  110. package/dist/server/lib/executionEnvironmentRoutes.js +127 -9
  111. package/dist/server/lib/executionEnvironmentRoutes.js.map +1 -1
  112. package/dist/server/lib/executionEnvironmentValidation.js +169 -45
  113. package/dist/server/lib/executionEnvironmentValidation.js.map +1 -1
  114. package/dist/server/lib/executionEnvironmentValidationOperations.js +203 -19
  115. package/dist/server/lib/executionEnvironmentValidationOperations.js.map +1 -1
  116. package/dist/server/lib/executionEnvironments.js +103 -14
  117. package/dist/server/lib/executionEnvironments.js.map +1 -1
  118. package/dist/server/lib/executionProfileRoutes.js +67 -0
  119. package/dist/server/lib/executionProfileRoutes.js.map +1 -0
  120. package/dist/server/lib/executionProfileStore.js +117 -0
  121. package/dist/server/lib/executionProfileStore.js.map +1 -0
  122. package/dist/server/lib/executionProfiles.js +324 -0
  123. package/dist/server/lib/executionProfiles.js.map +1 -0
  124. package/dist/server/lib/executionReconciliation.js +345 -197
  125. package/dist/server/lib/executionReconciliation.js.map +1 -1
  126. package/dist/server/lib/executionRetry.js +24 -5
  127. package/dist/server/lib/executionRetry.js.map +1 -1
  128. package/dist/server/lib/executionRunAuthority.js +467 -0
  129. package/dist/server/lib/executionRunAuthority.js.map +1 -0
  130. package/dist/server/lib/executionRuns.js +30 -4
  131. package/dist/server/lib/executionRuns.js.map +1 -1
  132. package/dist/server/lib/executionTimeline.js +2 -1
  133. package/dist/server/lib/executionTimeline.js.map +1 -1
  134. package/dist/server/lib/git.js +77 -10
  135. package/dist/server/lib/git.js.map +1 -1
  136. package/dist/server/lib/gitPushApprovals.js +367 -0
  137. package/dist/server/lib/gitPushApprovals.js.map +1 -0
  138. package/dist/server/lib/hostExecutionEnvironment.js +7 -1
  139. package/dist/server/lib/hostExecutionEnvironment.js.map +1 -1
  140. package/dist/server/lib/mobileAccess.js +115 -1
  141. package/dist/server/lib/mobileAccess.js.map +1 -1
  142. package/dist/server/lib/productionCheckApprovals.js +106 -0
  143. package/dist/server/lib/productionCheckApprovals.js.map +1 -0
  144. package/dist/server/lib/projects.js +1 -0
  145. package/dist/server/lib/projects.js.map +1 -1
  146. package/dist/server/lib/run.js +58 -2
  147. package/dist/server/lib/run.js.map +1 -1
  148. package/dist/server/lib/ticketFollowUpOperations.js +32 -6
  149. package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
  150. package/dist/server/lib/transcriptRuns.js +3 -0
  151. package/dist/server/lib/transcriptRuns.js.map +1 -1
  152. package/dist/server/lib/types.js +40 -0
  153. package/dist/server/lib/types.js.map +1 -1
  154. package/dist/server/lib/workspaceProcessEnvironment.js +72 -1
  155. package/dist/server/lib/workspaceProcessEnvironment.js.map +1 -1
  156. package/dist/server/workers/build.js +214 -46
  157. package/dist/server/workers/build.js.map +1 -1
  158. package/dist/server/workers/followUp.js +74 -1
  159. package/dist/server/workers/followUp.js.map +1 -1
  160. package/dist/server/workers/review.js +30 -13
  161. package/dist/server/workers/review.js.map +1 -1
  162. package/package.json +1 -1
  163. package/dist/client/assets/GitHubActionsDialog-CM1z-amZ.js +0 -1
  164. package/dist/client/assets/VerificationDialog-oHjNllh6.js +0 -4
  165. package/dist/client/assets/channel-CF8hkOdu.js +0 -1
  166. package/dist/client/assets/classDiagram-4FO5ZUOK-D6FiG9_B.js +0 -1
  167. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Bim05ijr.js +0 -1
  168. package/dist/client/assets/index-CsZfTdMO.css +0 -2
  169. package/dist/client/assets/index-DDefs3iM.js +0 -275
  170. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-BLzZdVo0.js +0 -1
@@ -1,7 +1,8 @@
1
1
  import db from '../db.js';
2
2
  import { terminalizeAgentRunsForExecutionRun, terminalizeLegacyAgentRuns } from './agentRuns.js';
3
3
  import { recoverInterruptedBuildOpenQuestionBatch } from './buildOpenQuestions.js';
4
- import { activateQueuedBuildSequenceTicket, getBuildSequenceIdForTicket, reconcileActiveBuildSequences, stopBuildSequence } from './buildSequences.js';
4
+ import { activateQueuedBuildSequenceTicket, getBuildSequenceIdForTicket, getBuildSequenceTicketExecutionProfileSelection, reconcileActiveBuildSequences, stopBuildSequence, } from './buildSequences.js';
5
+ import { parseExecutionProfileSelectionMetadata } from './executionRunAuthority.js';
5
6
  import { aggregateCheckBatch, completeCheckAttempt, getExecutionRun, reconcileInterruptedTestRuns, transitionExecutionRun, withExecutionRunTransaction, } from './executionRuns.js';
6
7
  import { getCheckBatchDetails } from './checkRuns.js';
7
8
  import { reconcileGitHubActionsRuns } from './integrations/githubActionsSessions.js';
@@ -18,6 +19,72 @@ const TERMINAL_EXECUTION_STATUSES = "('completed', 'failed', 'cancelled', 'inter
18
19
  const ACTIVE_EXECUTION_STATUS_SET = new Set(['queued', 'running', 'waiting']);
19
20
  const TERMINAL_EXECUTION_STATUS_SET = new Set(['completed', 'failed', 'cancelled', 'interrupted']);
20
21
  const RESTART_FAILURE_MESSAGE = 'Interrupted by server restart';
22
+ const parseRunSelection = (metadataJson) => {
23
+ try {
24
+ return parseExecutionProfileSelectionMetadata(metadataJson ? JSON.parse(metadataJson) : null);
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ };
30
+ const isQualifiedQueuedBuildHandoff = (projectId, sequenceId, ticketId, runId) => {
31
+ let itemSelection;
32
+ try {
33
+ itemSelection = getBuildSequenceTicketExecutionProfileSelection(sequenceId, ticketId);
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ const source = db.prepare(`
39
+ SELECT source.id, previous_item.ticket_id
40
+ FROM build_sequences sequence
41
+ JOIN build_sequence_tickets current_item ON current_item.build_sequence_id = sequence.id
42
+ AND current_item.ticket_id = ? AND current_item.status IN ('active', 'queued')
43
+ JOIN build_sequence_tickets previous_item ON previous_item.build_sequence_id = sequence.id
44
+ AND previous_item.position = current_item.position - 1 AND previous_item.status = 'done'
45
+ JOIN execution_runs source ON source.project_id = sequence.project_id
46
+ AND source.kind IN ('build', 'build_follow_up') AND source.status = 'completed'
47
+ AND json_extract(source.metadata_json, '$.post_build_finalization.status') IN ('pending', 'finalized', 'completed')
48
+ JOIN execution_run_links source_ticket ON source_ticket.execution_run_id = source.id
49
+ AND source_ticket.project_id = source.project_id AND source_ticket.entity_type = 'ticket'
50
+ AND source_ticket.entity_id = previous_item.ticket_id AND source_ticket.relationship = 'primary'
51
+ JOIN execution_run_links source_sequence ON source_sequence.execution_run_id = source.id
52
+ AND source_sequence.project_id = source.project_id AND source_sequence.entity_type = 'build_sequence'
53
+ AND source_sequence.entity_id = sequence.id AND source_sequence.relationship = 'context'
54
+ WHERE sequence.id = ? AND sequence.project_id = ? AND sequence.status = 'active'
55
+ ORDER BY source.created_at DESC, source.id DESC
56
+ LIMIT 1
57
+ `).get(ticketId, sequenceId, projectId);
58
+ if (!source || !isLatestTicketBuildSource(projectId, source.id, source.ticket_id)) {
59
+ return false;
60
+ }
61
+ const activeRuns = db.prepare(`
62
+ SELECT run.id, run.status, run.parent_run_id, run.metadata_json, item.activation_execution_run_id
63
+ FROM execution_runs run
64
+ JOIN execution_run_links ticket_link ON ticket_link.execution_run_id = run.id
65
+ AND ticket_link.project_id = run.project_id AND ticket_link.entity_type = 'ticket'
66
+ AND ticket_link.entity_id = ? AND ticket_link.relationship = 'primary'
67
+ LEFT JOIN build_sequence_tickets item ON item.build_sequence_id = ? AND item.ticket_id = ?
68
+ WHERE run.project_id = ? AND run.kind = 'build' AND run.status IN ${ACTIVE_EXECUTION_STATUSES}
69
+ ORDER BY run.created_at ASC, run.id ASC
70
+ `).all(ticketId, sequenceId, ticketId, projectId);
71
+ if (!runId) {
72
+ return activeRuns.length === 0;
73
+ }
74
+ if (activeRuns.length !== 1) {
75
+ return false;
76
+ }
77
+ const [run] = activeRuns;
78
+ const runSelection = parseRunSelection(run.metadata_json);
79
+ return Boolean(run.id === runId
80
+ && run.status === 'queued'
81
+ && run.parent_run_id === source.id
82
+ && run.activation_execution_run_id === run.id
83
+ && runSelection
84
+ && runSelection.revisionId === itemSelection.revisionId
85
+ && runSelection.fingerprint === itemSelection.fingerprint
86
+ && runSelection.reason === itemSelection.reason);
87
+ };
21
88
  const terminalizeInterruptedReviewRuns = (executionRunId, ticketId) => {
22
89
  const linked = db.prepare(`
23
90
  UPDATE review_runs
@@ -390,6 +457,8 @@ const reconcileBuildOpenQuestionBatches = () => {
390
457
  let questionsReopened = 0;
391
458
  let automationPaused = 0;
392
459
  let inconsistentBatchesRecovered = 0;
460
+ let orphanedProgressionChildrenInterrupted = 0;
461
+ const interruptedRunIds = new Set();
393
462
  const createLegacyRecoveryProgression = (batch) => {
394
463
  const state = getBundleAutomationState(batch.project_id, batch.worktree_bundle_id);
395
464
  if (!state?.automation_progression_run_id
@@ -426,6 +495,7 @@ const reconcileBuildOpenQuestionBatches = () => {
426
495
  let terminalized = 0;
427
496
  let errored = 0;
428
497
  for (const referencedRun of referencedRuns) {
498
+ interruptedRunIds.add(referencedRun.id);
429
499
  const interrupted = interruptBuildOpenQuestionRun(transaction, referencedRun);
430
500
  terminalized += interrupted.terminalized;
431
501
  errored += interrupted.errored;
@@ -451,6 +521,7 @@ const reconcileBuildOpenQuestionBatches = () => {
451
521
  childRunId: ownedRun.id,
452
522
  });
453
523
  const recovered = withExecutionRunTransaction(transaction => {
524
+ interruptedRunIds.add(ownedRun.id);
454
525
  const interrupted = interruptBuildOpenQuestionRun(transaction, ownedRun);
455
526
  const batchRecovery = recoverInterruptedBuildOpenQuestionBatch(batch.id);
456
527
  waitAutomationProgression({
@@ -477,6 +548,34 @@ const reconcileBuildOpenQuestionBatches = () => {
477
548
  questionsReopened += recovered.questions_reopened;
478
549
  automationPaused += recovered.automation_paused;
479
550
  }
551
+ // Follow-up runs owned by an automation progression but referencing no pending or
552
+ // running batch survive restarts as active zombies. The direct-workflow sweep skips
553
+ // them (progression parents are excluded), so interrupt them here to keep waiting
554
+ // open-question progressions adoptable.
555
+ const orphanedProgressionChildren = db.prepare(`
556
+ SELECT id, project_id, source_kind, source_id, metadata_json, initiated_by
557
+ FROM execution_runs orphan_run
558
+ WHERE orphan_run.kind = 'build_follow_up'
559
+ AND orphan_run.status IN ${ACTIVE_EXECUTION_STATUSES}
560
+ AND EXISTS (
561
+ SELECT 1 FROM execution_runs parent
562
+ WHERE parent.id = orphan_run.parent_run_id
563
+ AND parent.project_id = orphan_run.project_id
564
+ AND parent.kind = 'automation_progression'
565
+ )
566
+ ORDER BY orphan_run.created_at ASC, orphan_run.id ASC
567
+ `).all();
568
+ for (const run of orphanedProgressionChildren) {
569
+ if (interruptedRunIds.has(run.id)) {
570
+ continue;
571
+ }
572
+ withExecutionRunTransaction(transaction => {
573
+ const interrupted = interruptBuildOpenQuestionRun(transaction, run);
574
+ childRowsTerminalized += interrupted.terminalized;
575
+ ticketsErrored += interrupted.errored;
576
+ });
577
+ orphanedProgressionChildrenInterrupted += 1;
578
+ }
480
579
  return {
481
580
  adopted_interrupted: adoptedInterrupted,
482
581
  legacy_recovered: legacyRecovered,
@@ -485,18 +584,13 @@ const reconcileBuildOpenQuestionBatches = () => {
485
584
  questions_reopened: questionsReopened,
486
585
  automation_paused: automationPaused,
487
586
  inconsistent_batches_recovered: inconsistentBatchesRecovered,
587
+ orphaned_progression_children_interrupted: orphanedProgressionChildrenInterrupted,
488
588
  };
489
589
  };
490
590
  const interruptSequenceBuild = (input) => {
491
591
  const rows = db.prepare(`
492
592
  SELECT execution_runs.id, execution_runs.project_id, execution_runs.status
493
593
  FROM execution_runs
494
- INNER JOIN execution_run_links sequence_link
495
- ON sequence_link.execution_run_id = execution_runs.id
496
- AND sequence_link.project_id = execution_runs.project_id
497
- AND sequence_link.entity_type = 'build_sequence'
498
- AND sequence_link.entity_id = ?
499
- AND sequence_link.relationship = 'context'
500
594
  INNER JOIN execution_run_links ticket_link
501
595
  ON ticket_link.execution_run_id = execution_runs.id
502
596
  AND ticket_link.project_id = execution_runs.project_id
@@ -506,31 +600,11 @@ const interruptSequenceBuild = (input) => {
506
600
  WHERE execution_runs.project_id = ?
507
601
  AND execution_runs.kind = 'build'
508
602
  AND execution_runs.status IN ${ACTIVE_EXECUTION_STATUSES}
509
- `).all(input.sequenceId, input.ticketId, input.projectId);
510
- const queuedHandoff = input.itemStatus !== null && Boolean(db.prepare(`
511
- SELECT 1
512
- FROM build_sequence_tickets current_item
513
- JOIN build_sequence_tickets previous_item ON previous_item.build_sequence_id = current_item.build_sequence_id
514
- AND previous_item.position = current_item.position - 1
515
- AND previous_item.status = 'done'
516
- JOIN execution_runs source ON source.project_id = ?
517
- JOIN execution_run_links source_ticket ON source_ticket.execution_run_id = source.id
518
- AND source_ticket.project_id = source.project_id
519
- AND source_ticket.entity_type = 'ticket'
520
- AND source_ticket.entity_id = previous_item.ticket_id
521
- AND source_ticket.relationship = 'primary'
522
- WHERE current_item.build_sequence_id = ?
523
- AND current_item.ticket_id = ?
524
- AND current_item.status IN ('active', 'queued')
525
- AND source.kind IN ('build', 'build_follow_up')
526
- AND source.status = 'completed'
527
- AND json_extract(source.metadata_json, '$.post_build_finalization.status') IN ('pending', 'blocked', 'finalized', 'completed')
528
- LIMIT 1
529
- `).get(input.projectId, input.sequenceId, input.ticketId));
530
- if (queuedHandoff && rows.every(row => row.status === 'queued')) {
531
- return false;
532
- }
533
- if (rows.length > 1) {
603
+ `).all(input.ticketId, input.projectId);
604
+ const preservesHandoff = input.itemStatus !== null && (rows.length === 0
605
+ ? isQualifiedQueuedBuildHandoff(input.projectId, input.sequenceId, input.ticketId)
606
+ : rows.length === 1 && isQualifiedQueuedBuildHandoff(input.projectId, input.sequenceId, input.ticketId, rows[0].id));
607
+ if (preservesHandoff) {
534
608
  return false;
535
609
  }
536
610
  if (rows.length === 0) {
@@ -540,23 +614,24 @@ const interruptSequenceBuild = (input) => {
540
614
  `).run(input.ticketId, input.projectId);
541
615
  return true;
542
616
  }
543
- const [run] = rows;
544
- withExecutionRunTransaction(transaction => {
545
- transaction.transitionExecutionRun({
546
- projectId: run.project_id,
547
- runId: run.id,
548
- status: 'interrupted',
549
- domainStatus: 'interrupted',
550
- failure: { code: 'server_restarted', message: 'The server restarted while this sequenced Build run was active.' },
551
- eventType: 'build_interrupted',
552
- eventMessage: 'The server restarted while this sequenced Build run was active.',
617
+ for (const run of rows) {
618
+ withExecutionRunTransaction(transaction => {
619
+ transaction.transitionExecutionRun({
620
+ projectId: run.project_id,
621
+ runId: run.id,
622
+ status: 'interrupted',
623
+ domainStatus: 'interrupted',
624
+ failure: { code: 'server_restarted', message: 'The server restarted while this sequenced Build run was active.' },
625
+ eventType: 'build_interrupted',
626
+ eventMessage: 'The server restarted while this sequenced Build run was active.',
627
+ });
628
+ terminalizeAgentRunsForExecutionRun(run.id, 'error', RESTART_FAILURE_MESSAGE);
629
+ db.prepare(`
630
+ UPDATE tickets SET agent_status = 'error'
631
+ WHERE id = ? AND project_id = ? AND (agent_status = 'running' OR agent_status IS NULL)
632
+ `).run(input.ticketId, input.projectId);
553
633
  });
554
- terminalizeAgentRunsForExecutionRun(run.id, 'error', RESTART_FAILURE_MESSAGE);
555
- db.prepare(`
556
- UPDATE tickets SET agent_status = 'error'
557
- WHERE id = ? AND project_id = ? AND (agent_status = 'running' OR agent_status IS NULL)
558
- `).run(input.ticketId, input.projectId);
559
- });
634
+ }
560
635
  return true;
561
636
  };
562
637
  const reconcileSpecReviews = () => {
@@ -606,27 +681,10 @@ const reconcileSpecReviews = () => {
606
681
  };
607
682
  };
608
683
  const reconcileDirectWorkflow = (policy) => {
609
- const queuedBuildHandoffCondition = policy.kind === 'build'
610
- ? `
611
- AND NOT (
612
- execution_runs.status = 'queued'
613
- AND EXISTS (
614
- SELECT 1
615
- FROM execution_runs parent
616
- WHERE parent.id = execution_runs.parent_run_id
617
- AND parent.project_id = execution_runs.project_id
618
- AND parent.kind IN ('build', 'build_follow_up')
619
- AND parent.status = 'completed'
620
- AND json_extract(parent.metadata_json, '$.post_build_finalization.status') IN ('pending', 'blocked', 'finalized', 'completed')
621
- )
622
- )
623
- `
624
- : '';
625
684
  const rows = db.prepare(`
626
685
  SELECT id, project_id, source_kind, source_id
627
686
  FROM execution_runs
628
687
  WHERE kind = ? AND status IN ${ACTIVE_EXECUTION_STATUSES}
629
- ${queuedBuildHandoffCondition}
630
688
  AND NOT EXISTS (
631
689
  SELECT 1 FROM execution_runs parent
632
690
  WHERE parent.id = execution_runs.parent_run_id
@@ -637,7 +695,20 @@ const reconcileDirectWorkflow = (policy) => {
637
695
  `).all(policy.kind);
638
696
  let childRowsTerminalized = 0;
639
697
  let domainParentsErrored = 0;
698
+ let interrupted = 0;
640
699
  const interruptRun = (row) => {
700
+ if (policy.kind === 'build' && row.source_id) {
701
+ const item = db.prepare(`
702
+ SELECT build_sequence_id
703
+ FROM build_sequence_tickets
704
+ WHERE ticket_id = ? AND activation_execution_run_id = ?
705
+ `).get(row.source_id, row.id);
706
+ // A durable post-Build handoff is the sole Build restart-preserved scheduling class.
707
+ if (item && isQualifiedQueuedBuildHandoff(row.project_id, item.build_sequence_id, row.source_id, row.id)) {
708
+ return;
709
+ }
710
+ }
711
+ interrupted += 1;
641
712
  withExecutionRunTransaction(transaction => {
642
713
  transaction.transitionExecutionRun({
643
714
  projectId: row.project_id,
@@ -713,16 +784,22 @@ const reconcileDirectWorkflow = (policy) => {
713
784
  `).run(policy.domainParent.sourceKind).changes;
714
785
  }
715
786
  return {
716
- interrupted: rows.length,
787
+ interrupted,
717
788
  child_rows_terminalized: childRowsTerminalized,
718
789
  domain_parents_errored: domainParentsErrored,
719
790
  };
720
791
  };
721
792
  const reconcileCheckBatches = async () => {
793
+ const hasApprovalRequests = Boolean(db.prepare(`
794
+ SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'approval_requests'
795
+ `).get());
722
796
  const attempts = db.prepare(`
723
- SELECT run.id, run.project_id, run.parent_run_id, details.execution_run_id AS details_run_id
797
+ SELECT run.id, run.project_id, run.parent_run_id, details.execution_run_id AS details_run_id,
798
+ details.command_snapshot_json, ${hasApprovalRequests ? 'approval.status' : 'NULL'} AS approval_status
724
799
  FROM execution_runs run
725
800
  LEFT JOIN check_attempt_details details ON details.execution_run_id = run.id
801
+ ${hasApprovalRequests ? `LEFT JOIN approval_requests approval
802
+ ON approval.execution_run_id = run.id AND approval.project_id = run.project_id` : ''}
726
803
  WHERE run.kind = 'check_attempt' AND run.status IN ${ACTIVE_EXECUTION_STATUSES}
727
804
  ORDER BY run.created_at ASC, run.id ASC
728
805
  `).all();
@@ -733,20 +810,30 @@ const reconcileCheckBatches = async () => {
733
810
  let postBuildChatsFinalized = 0;
734
811
  let postBuildContinuationsResumed = 0;
735
812
  for (const attempt of attempts) {
813
+ if (attempt.approval_status === 'pending' || attempt.approval_status === 'approved') {
814
+ continue;
815
+ }
736
816
  if (attempt.parent_run_id) {
737
817
  affectedBatchIds.add(attempt.parent_run_id);
738
818
  }
739
819
  if (attempt.details_run_id) {
820
+ const productionConsumed = attempt.approval_status === 'consumed'
821
+ && attempt.command_snapshot_json !== null
822
+ && JSON.parse(attempt.command_snapshot_json).consequence === 'production';
740
823
  completeCheckAttempt({
741
824
  projectId: attempt.project_id,
742
825
  runId: attempt.id,
743
- outcome: 'restart_loss',
826
+ outcome: productionConsumed ? 'ambiguous_process_loss' : 'restart_loss',
744
827
  finalWorkspaceIdentity: {
745
828
  state: 'unavailable',
746
829
  reason: 'worktree_unavailable',
747
- message: 'The server restarted before this check attempt completed',
830
+ message: productionConsumed
831
+ ? 'The production command process identity was lost; its external outcome is ambiguous'
832
+ : 'The server restarted before this check attempt completed',
748
833
  },
749
- exitError: 'The server restarted before this check attempt completed',
834
+ exitError: productionConsumed
835
+ ? 'The production command process identity was lost; automatic replay is prohibited'
836
+ : 'The server restarted before this check attempt completed',
750
837
  });
751
838
  interruptedAttempts += 1;
752
839
  continue;
@@ -1039,135 +1126,196 @@ export const reconcileEnvironmentValidationRuns = async (adapter) => {
1039
1126
  }
1040
1127
  return { interrupted };
1041
1128
  };
1042
- export const createExecutionReconciliationPolicies = (input) => [
1043
- {
1044
- key: 'environment_validation',
1045
- workLocality: 'local_process',
1046
- startupStrategy: 'interrupt',
1047
- terminalStatus: 'interrupted',
1048
- failureCode: 'server_restarted',
1049
- updatesDomainRecords: false,
1050
- pausesAutomation: false,
1051
- reconcile: reconcileEnvironmentValidationRuns,
1052
- },
1053
- {
1054
- key: 'test',
1055
- workLocality: 'local_process',
1056
- startupStrategy: 'interrupt',
1057
- terminalStatus: 'interrupted',
1058
- failureCode: 'server_restarted',
1059
- updatesDomainRecords: false,
1060
- pausesAutomation: true,
1061
- reconcile: () => ({ interrupted: reconcileInterruptedTestRuns() }),
1062
- },
1063
- {
1064
- key: 'check_batch',
1065
- workLocality: 'local_process',
1066
- startupStrategy: 'interrupt',
1067
- terminalStatus: 'interrupted',
1068
- failureCode: 'restart_loss',
1069
- updatesDomainRecords: true,
1070
- pausesAutomation: false,
1071
- reconcile: reconcileCheckBatches,
1072
- },
1073
- {
1074
- key: 'check_auto_fix',
1075
- workLocality: 'durable_orchestration',
1076
- startupStrategy: 'reconcile',
1077
- terminalStatus: null,
1078
- failureCode: null,
1079
- updatesDomainRecords: true,
1080
- pausesAutomation: true,
1081
- reconcile: async () => db.prepare(`
1129
+ export const createExecutionReconciliationPolicies = (input) => {
1130
+ const approvedRequests = [];
1131
+ return [
1132
+ {
1133
+ key: 'approval',
1134
+ workLocality: 'durable_orchestration',
1135
+ startupStrategy: 'reconcile',
1136
+ terminalStatus: null,
1137
+ failureCode: null,
1138
+ updatesDomainRecords: true,
1139
+ pausesAutomation: true,
1140
+ reconcile: async () => db.prepare(`
1141
+ SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'approval_requests'
1142
+ `).get()
1143
+ ? (await import('./approvalReconciliation.js')).reconcileApprovalRequests({
1144
+ resumeApproved: request => {
1145
+ approvedRequests.push(request);
1146
+ },
1147
+ reconcileConsumed: request => {
1148
+ if (request.action_kind === 'git_push') {
1149
+ return import('./gitPushApprovals.js').then(async (pushes) => {
1150
+ await pushes.reconcileConsumedGitPush(request);
1151
+ });
1152
+ }
1153
+ },
1154
+ resolveTerminal: async (request) => {
1155
+ if (request.action_kind === 'git_push') {
1156
+ await (await import('./gitPushApprovals.js')).finalizeGitPushApproval(request.project_id, request.id);
1157
+ }
1158
+ else {
1159
+ await (await import('./checkRunner.js')).finalizeProductionCheckApproval(request.project_id, request.id);
1160
+ }
1161
+ },
1162
+ })
1163
+ : { expired: 0, invalidated: 0, pending: 0, approved_unclaimed: 0, consumed_delegated: 0 },
1164
+ },
1165
+ {
1166
+ key: 'environment_validation',
1167
+ workLocality: 'local_process',
1168
+ startupStrategy: 'interrupt',
1169
+ terminalStatus: 'interrupted',
1170
+ failureCode: 'server_restarted',
1171
+ updatesDomainRecords: false,
1172
+ pausesAutomation: false,
1173
+ reconcile: reconcileEnvironmentValidationRuns,
1174
+ },
1175
+ {
1176
+ key: 'test',
1177
+ workLocality: 'local_process',
1178
+ startupStrategy: 'interrupt',
1179
+ terminalStatus: 'interrupted',
1180
+ failureCode: 'server_restarted',
1181
+ updatesDomainRecords: false,
1182
+ pausesAutomation: true,
1183
+ reconcile: () => ({ interrupted: reconcileInterruptedTestRuns() }),
1184
+ },
1185
+ {
1186
+ key: 'check_batch',
1187
+ workLocality: 'local_process',
1188
+ startupStrategy: 'interrupt',
1189
+ terminalStatus: 'interrupted',
1190
+ failureCode: 'restart_loss',
1191
+ updatesDomainRecords: true,
1192
+ pausesAutomation: false,
1193
+ reconcile: async () => {
1194
+ const result = await reconcileCheckBatches();
1195
+ for (const request of approvedRequests.splice(0)) {
1196
+ if (request.action_kind === 'git_push') {
1197
+ await (await import('./gitPushApprovals.js')).resumeGitPushApproval(request.project_id, request.id);
1198
+ }
1199
+ else {
1200
+ await (await import('./checkRunner.js')).resumeProductionCheckApproval(request.project_id, request.id);
1201
+ }
1202
+ }
1203
+ return result;
1204
+ },
1205
+ },
1206
+ {
1207
+ key: 'check_auto_fix',
1208
+ workLocality: 'durable_orchestration',
1209
+ startupStrategy: 'reconcile',
1210
+ terminalStatus: null,
1211
+ failureCode: null,
1212
+ updatesDomainRecords: true,
1213
+ pausesAutomation: true,
1214
+ reconcile: async () => db.prepare(`
1082
1215
  SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'check_auto_fix_loops'
1083
1216
  `).get()
1084
- ? (await import('./checkAutoFix.js')).reconcileCheckAutoFixLoops()
1085
- : { interrupted: 0 },
1086
- },
1087
- {
1088
- key: 'check_repair',
1089
- workLocality: 'durable_orchestration',
1090
- startupStrategy: 'reconcile',
1091
- terminalStatus: null,
1092
- failureCode: null,
1093
- updatesDomainRecords: true,
1094
- pausesAutomation: true,
1095
- reconcile: async () => (await import('./checkRepairReconciliation.js')).reconcileCheckRepairs(),
1096
- },
1097
- {
1098
- key: 'repository_graph_scan',
1099
- workLocality: 'local_process',
1100
- startupStrategy: 'interrupt',
1101
- terminalStatus: 'interrupted',
1102
- failureCode: 'server_restarted',
1103
- updatesDomainRecords: true,
1104
- pausesAutomation: false,
1105
- reconcile: reconcileRepositoryGraphScans,
1106
- },
1107
- {
1108
- key: 'automation',
1109
- workLocality: 'durable_orchestration',
1110
- startupStrategy: 'reconcile',
1111
- terminalStatus: null,
1112
- failureCode: null,
1113
- updatesDomainRecords: true,
1114
- pausesAutomation: true,
1115
- reconcile: reconcileAutomationExecutions,
1116
- },
1117
- {
1118
- key: 'build_sequence',
1119
- workLocality: 'durable_orchestration',
1120
- startupStrategy: 'reconcile',
1121
- terminalStatus: 'interrupted',
1122
- failureCode: 'server_restarted',
1123
- updatesDomainRecords: true,
1124
- pausesAutomation: false,
1125
- reconcile: () => reconcileActiveBuildSequences({ interruptActiveChild: interruptSequenceBuild }),
1126
- },
1127
- {
1128
- key: 'build_open_question_batch',
1129
- workLocality: 'durable_orchestration',
1130
- startupStrategy: 'reconcile',
1131
- terminalStatus: 'interrupted',
1132
- failureCode: 'server_restarted',
1133
- updatesDomainRecords: true,
1134
- pausesAutomation: true,
1135
- reconcile: reconcileBuildOpenQuestionBatches,
1136
- },
1137
- ...directWorkflowPolicies.map(policy => ({
1138
- key: policy.kind,
1139
- workLocality: 'local_process',
1140
- startupStrategy: 'interrupt',
1141
- terminalStatus: 'interrupted',
1142
- failureCode: 'server_restarted',
1143
- updatesDomainRecords: Boolean(policy.domainParent),
1144
- pausesAutomation: false,
1145
- reconcile: () => reconcileDirectWorkflow(policy),
1146
- })),
1147
- {
1148
- key: 'spec_review',
1149
- workLocality: 'local_process',
1150
- startupStrategy: 'interrupt',
1151
- terminalStatus: 'interrupted',
1152
- failureCode: 'server_restarted',
1153
- updatesDomainRecords: true,
1154
- pausesAutomation: false,
1155
- reconcile: reconcileSpecReviews,
1156
- },
1157
- {
1158
- key: 'github_actions',
1159
- workLocality: 'externally_observable',
1160
- startupStrategy: 'reconcile',
1161
- terminalStatus: null,
1162
- failureCode: null,
1163
- updatesDomainRecords: false,
1164
- pausesAutomation: false,
1165
- reconcile: () => reconcileGitHubActionsRuns({
1166
- getConfig: input.getGitHubConfig,
1167
- startPolling: false,
1168
- }),
1169
- },
1170
- ];
1217
+ ? (await import('./checkAutoFix.js')).reconcileCheckAutoFixLoops()
1218
+ : { interrupted: 0 },
1219
+ },
1220
+ {
1221
+ key: 'check_repair',
1222
+ workLocality: 'durable_orchestration',
1223
+ startupStrategy: 'reconcile',
1224
+ terminalStatus: null,
1225
+ failureCode: null,
1226
+ updatesDomainRecords: true,
1227
+ pausesAutomation: true,
1228
+ reconcile: async () => (await import('./checkRepairReconciliation.js')).reconcileCheckRepairs(),
1229
+ },
1230
+ {
1231
+ key: 'repository_graph_scan',
1232
+ workLocality: 'local_process',
1233
+ startupStrategy: 'interrupt',
1234
+ terminalStatus: 'interrupted',
1235
+ failureCode: 'server_restarted',
1236
+ updatesDomainRecords: true,
1237
+ pausesAutomation: false,
1238
+ reconcile: reconcileRepositoryGraphScans,
1239
+ },
1240
+ {
1241
+ key: 'automation',
1242
+ workLocality: 'durable_orchestration',
1243
+ startupStrategy: 'reconcile',
1244
+ terminalStatus: null,
1245
+ failureCode: null,
1246
+ updatesDomainRecords: true,
1247
+ pausesAutomation: true,
1248
+ reconcile: reconcileAutomationExecutions,
1249
+ },
1250
+ {
1251
+ key: 'build_sequence',
1252
+ workLocality: 'durable_orchestration',
1253
+ startupStrategy: 'reconcile',
1254
+ terminalStatus: 'interrupted',
1255
+ failureCode: 'server_restarted',
1256
+ updatesDomainRecords: true,
1257
+ pausesAutomation: false,
1258
+ reconcile: () => reconcileActiveBuildSequences({ interruptActiveChild: interruptSequenceBuild }),
1259
+ },
1260
+ {
1261
+ key: 'build_open_question_batch',
1262
+ workLocality: 'durable_orchestration',
1263
+ startupStrategy: 'reconcile',
1264
+ terminalStatus: 'interrupted',
1265
+ failureCode: 'server_restarted',
1266
+ updatesDomainRecords: true,
1267
+ pausesAutomation: true,
1268
+ reconcile: reconcileBuildOpenQuestionBatches,
1269
+ },
1270
+ ...directWorkflowPolicies.map(policy => ({
1271
+ key: policy.kind,
1272
+ workLocality: 'local_process',
1273
+ startupStrategy: 'interrupt',
1274
+ terminalStatus: 'interrupted',
1275
+ failureCode: 'server_restarted',
1276
+ updatesDomainRecords: Boolean(policy.domainParent),
1277
+ pausesAutomation: false,
1278
+ reconcile: () => reconcileDirectWorkflow(policy),
1279
+ })),
1280
+ {
1281
+ key: 'build_setup',
1282
+ workLocality: 'durable_orchestration',
1283
+ startupStrategy: 'reconcile',
1284
+ terminalStatus: 'failed',
1285
+ failureCode: 'server_restarted',
1286
+ updatesDomainRecords: true,
1287
+ pausesAutomation: false,
1288
+ reconcile: async () => db.prepare(`
1289
+ SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'build_setup_evidence'
1290
+ `).get()
1291
+ ? (await import('./buildSetupAuthority.js')).reconcileBuildSetupEvidence()
1292
+ : { failed: 0 },
1293
+ },
1294
+ {
1295
+ key: 'spec_review',
1296
+ workLocality: 'local_process',
1297
+ startupStrategy: 'interrupt',
1298
+ terminalStatus: 'interrupted',
1299
+ failureCode: 'server_restarted',
1300
+ updatesDomainRecords: true,
1301
+ pausesAutomation: false,
1302
+ reconcile: reconcileSpecReviews,
1303
+ },
1304
+ {
1305
+ key: 'github_actions',
1306
+ workLocality: 'externally_observable',
1307
+ startupStrategy: 'reconcile',
1308
+ terminalStatus: null,
1309
+ failureCode: null,
1310
+ updatesDomainRecords: false,
1311
+ pausesAutomation: false,
1312
+ reconcile: () => reconcileGitHubActionsRuns({
1313
+ getConfig: input.getGitHubConfig,
1314
+ startPolling: false,
1315
+ }),
1316
+ },
1317
+ ];
1318
+ };
1171
1319
  export const reconcileStartupExecutions = async (input) => {
1172
1320
  const policies = input.policies ?? createExecutionReconciliationPolicies(input);
1173
1321
  const summary = {};