@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
@@ -11,6 +11,10 @@ import db from './db.js';
11
11
  import { pruneAllRepositoryGraphScans } from './lib/repositoryGraphRetention.js';
12
12
  import { registerRepositoryGraphRoutes } from './lib/repositoryGraphRoutes.js';
13
13
  import { registerExecutionEnvironmentRoutes } from './lib/executionEnvironmentRoutes.js';
14
+ import { registerExecutionProfileRoutes } from './lib/executionProfileRoutes.js';
15
+ import { registerApprovalRoutes } from './lib/approvalRoutes.js';
16
+ import { admitForcePush, finalizeGitPushApproval, resumeGitPushApproval } from './lib/gitPushApprovals.js';
17
+ import { reconcileApprovalRequests } from './lib/approvalReconciliation.js';
14
18
  import { dockerSandboxAdapter, dockerSandboxRunner, dockerSandboxRuntimeController, } from './lib/dockerSandboxEnvironmentOperations.js';
15
19
  import { deleteChatSession, getChatSession, listChatSessions } from './lib/chats.js';
16
20
  import { deleteSpecSession, getCreatedTicketIdsForSnapshot, getSpecSession, getSpecSnapshot, listSpecSessions } from './lib/specs.js';
@@ -26,7 +30,7 @@ import { resolveSessionTargetForExecution } from './lib/sessionTargets.js';
26
30
  import { createChatMessage, getChatMessage, updateChatMessageContent } from './lib/chatMessages.js';
27
31
  import { attachChatTicketProposalsToMessageContent } from './lib/chatTicketProposals.js';
28
32
  import { createCorsOriginResolver, parseCorsOrigins } from './lib/cors.js';
29
- import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffDataAsync, getGitDiffFiles, getGitStatus, getGitStatusAsync, getGitUnifiedDiff, getGitUnifiedDiffAsync, getLocalProjectBranch, getProjectBranch, getProjectBranchSuggestions, getSelectedGitUnifiedDiff, getUnpushedCommitDiffForCwd, getUnpushedCommitSummaryForCwd, getUnpushedCommitSummaryForCwdAsync, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, squashUnpushedCommitsForCwd, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd, UnpushedCommitDiffSelectionError } from './lib/git.js';
33
+ import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteLocalBranch, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffDataAsync, getGitDiffFiles, getGitStatus, getGitStatusAsync, getGitUnifiedDiff, getGitUnifiedDiffAsync, getLocalProjectBranch, getProjectBranch, getProjectBranchSuggestions, getSelectedGitUnifiedDiff, getUnpushedCommitDiffForCwd, getUnpushedCommitSummaryForCwd, getUnpushedCommitSummaryForCwdAsync, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, squashUnpushedCommitsForCwd, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd, UnpushedCommitDiffSelectionError } from './lib/git.js';
30
34
  import config from './lib/config.js';
31
35
  import { autoConfigProject, ProjectAutoConfigError } from './lib/projectAutoConfig.js';
32
36
  import { analyzeRepoPath, createInitialCommit, ensureInitialCommitForWorktrees, initializeGitRepo, isInitialCommitRequiredError, } from './lib/projectRepos.js';
@@ -37,7 +41,8 @@ import { validateDesktopMobileAccessRequest } from './lib/mobileAccessSecurity.j
37
41
  import { refreshChatSessionUsageSnapshot, refreshSpecSessionUsageSnapshot, stopAllBuildAgents, stopAllReviewAgents, stopBuildAgent, stopReviewAgent } from './lib/agent.js';
38
42
  import { runLightweightPrompt } from './lib/lightweightPrompt.js';
39
43
  import { advanceBuildChain, startBuildChain, stopBuildChain } from './lib/buildChains.js';
40
- import { activateQueuedBuildSequenceTicket, compensateBuildSequenceAcceptance, enqueueBundledBuild, getDependencyOrderedBundleTicketIds, getBuildSequenceIdForTicket, hasActiveSequenceForBundle, removeTicketFromBuildSequence, stopBuildSequence, } from './lib/buildSequences.js';
44
+ import { activateQueuedBuildSequenceTicket, bindBuildSequenceTicketExecutionRun, compensateBuildSequenceAcceptance, enqueueBundledBuild, getDependencyOrderedBundleTicketIds, getBuildSequenceIdForTicket, getBuildSequenceTicketExecutionProfileSelection, hasActiveSequenceForBundle, releaseQueuedBuildSequenceTicketExecutionRunForRetry, removeTicketFromBuildSequence, stopBuildSequence, } from './lib/buildSequences.js';
45
+ import { createExecutionProfileSelection, executionProfileSelectionMetadata, validateExecutionProfileSelection } from './lib/executionRunAuthority.js';
41
46
  import { prepareTicketForBuild } from './lib/buildFlow.js';
42
47
  import { getActiveBundleRepairBlocker, getBundlePlanMutationBlockerById, shouldBlockPlanForBundleMutationById, } from './lib/bundleActivity.js';
43
48
  import { getDiscoveredModels, normalizeModelId, normalizeTicketModelId } from './lib/models.js';
@@ -92,15 +97,16 @@ import { refreshIfNeeded as refreshAssignedIssues, clearCachedAssignedIssues } f
92
97
  import { buildCommitMessagePrompt, normalizeGeneratedCommitMessage } from './lib/commitMessage.js';
93
98
  import { BUNDLE_PULL_REQUEST_SYNC_TTL_MS, getBundlePullRequestRecord, hasOpenBundlePullRequest, serializeBundlePullRequest, shouldSyncBundlePullRequest, syncTrackedBundlePullRequestIfNeeded, upsertBundlePullRequest, upsertBundlePullRequestFromGitHub, } from './lib/bundlePullRequests.js';
94
99
  import { countInboundProjectLinks, getEffectiveProject, getProjectCheckDefinitions, getProjectById, getProjectTestCommands, getProjectTicketStats, hasProjectRunIde, listInterruptedProjectDeletions, listProjects, releaseInterruptedProjectDeletionClaims, replaceProjectLinks, serializeProject, updateProjectActiveTarget, validateProjectRequiredCheckPolicyReferences, validateProjectPayload, } from './lib/projects.js';
95
- import { getActiveRunStatus, getActiveRunStatusForProject, getPreviewStatusForProject, hasRunConfig, isRunning, openIde, openTicketIdeInWorkspace, resolveProjectRunContextKey, runSetupCommandsInWorkspace, runTicketInWorkspace, stopAllTickets, stopTicket } from './lib/run.js';
100
+ import { getActiveRunStatus, getActiveRunStatusForProject, getPreviewStatusForProject, hasRunConfig, isRunning, openIde, openTicketIdeInWorkspace, resolveProjectRunContextKey, runTicketInWorkspace, stopAllTickets, stopTicket } from './lib/run.js';
96
101
  import { clearAllTestSessions, createTestSession, deleteTestSession, discoverTestsForSession, doesTestFileExistForSession, getTestSession, isTestSelectionSupported, startTestSessionRun, stopTestSessionRun } from './lib/testSessions.js';
97
- import { closeCheckRunnerAdmissions, getCheckBatchAttempts, openCheckRunnerAdmissions, rerunCheckBatchAndSchedule, shutdownCheckRunner, startCheckBatch, stopCheckBatch } from './lib/checkRunner.js';
102
+ import { closeCheckRunnerAdmissions, finalizeProductionCheckApproval, getCheckBatchAttempts, openCheckRunnerAdmissions, rerunCheckBatchAndSchedule, resumeProductionCheckApproval, shutdownCheckRunner, startCheckBatch, stopCheckBatch } from './lib/checkRunner.js';
98
103
  import { CheckRepairError, getCheckRepairCandidateState, getCheckRepairDetails, getLatestCheckRepairForBatch, retryCheckRepair, startCheckRepair, stopCheckRepair } from './lib/checkRepair.js';
99
104
  import { getCheckAutoFixDetails, stopCheckAutoFix } from './lib/checkAutoFix.js';
100
105
  import { findActiveCheckAutoFixForTarget, findActiveCheckBatchForTarget, getCheckAttemptDetails, getCheckBatchDetails, getLatestCheckBatchRunIdForTarget, hasActiveProjectCheckBatch, isCheckAttemptCurrent, resolveCheckBatchRerunEligibility } from './lib/checkRuns.js';
101
106
  import { getCheckAttemptDiagnosticResponse, getCheckBatchDiagnosticResponse } from './lib/checkDiagnostics.js';
102
107
  import { getCheckDefinitionFingerprint } from './lib/checkDefinitions.js';
103
108
  import { getCheckGateEvaluation, hydrateCheckGatePresentApplicability } from './lib/checkGateEvaluations.js';
109
+ import { getBuildSetupEvidenceForRun } from './lib/buildSetupAuthority.js';
104
110
  import { captureCheckWorkspaceIdentity } from './lib/checkWorkspaceIdentity.js';
105
111
  import { hasActiveBundleRepositoryGraphScan, hasActiveProjectRepositoryGraphScan } from './lib/repositoryGraphStore.js';
106
112
  import { resolveEffectiveRequiredCheckPolicyMembership, resolveRequiredCheckPolicy } from './lib/requiredCheckPolicy.js';
@@ -231,6 +237,7 @@ const parseMultiValueQuery = (value, allowedValues, fieldName) => {
231
237
  };
232
238
  };
233
239
  const EXECUTION_RUN_STATUSES = ['queued', 'running', 'waiting', 'completed', 'failed', 'cancelled', 'interrupted'];
240
+ const EXECUTION_RUN_DOMAIN_STATUS_FILTERS = ['approval_required'];
234
241
  const parseLifecycleListQuery = (query) => {
235
242
  const kind = parseMultiValueQuery(query.kind, EXECUTION_RUN_KINDS, 'kind');
236
243
  if ('error' in kind) {
@@ -243,6 +250,13 @@ const parseLifecycleListQuery = (query) => {
243
250
  if ('error' in status) {
244
251
  return status;
245
252
  }
253
+ const domainStatus = parseMultiValueQuery(query.domain_status, EXECUTION_RUN_DOMAIN_STATUS_FILTERS, 'domain_status');
254
+ if ('error' in domainStatus) {
255
+ return domainStatus;
256
+ }
257
+ if (domainStatus.values.length > 1) {
258
+ return { error: 'domain_status must include at most one value' };
259
+ }
246
260
  if (query.cursor != null && typeof query.cursor !== 'string') {
247
261
  return { error: 'cursor must be a string' };
248
262
  }
@@ -253,6 +267,7 @@ const parseLifecycleListQuery = (query) => {
253
267
  return {
254
268
  kind: kind.values[0],
255
269
  statuses: status.values,
270
+ domainStatus: domainStatus.values[0],
256
271
  cursor: typeof query.cursor === 'string' && query.cursor !== '' ? query.cursor : null,
257
272
  limit,
258
273
  };
@@ -446,9 +461,16 @@ const buildExecutionRunDetailResponse = async (project, run) => {
446
461
  }
447
462
  : run.kind === 'github_actions'
448
463
  ? { ...serializeGitHubActionsExecutionDetail(getGitHubActionsExecutionDetails(project.id, run.id)), retry_eligibility: retryEligibility }
449
- : ['automation_commit', 'automation_push', 'automation_pull_request'].includes(run.kind)
450
- ? { kind: run.kind, available: true, automation_stage: getAutomationStageDetails(project.id, run.id), retry_eligibility: retryEligibility }
451
- : { kind: run.kind, available: true, retry_eligibility: retryEligibility };
464
+ : run.kind === 'build'
465
+ ? {
466
+ kind: 'build',
467
+ available: true,
468
+ setup_authority: getBuildSetupEvidenceForRun(project.id, run.id),
469
+ retry_eligibility: retryEligibility,
470
+ }
471
+ : ['automation_commit', 'automation_push', 'automation_pull_request'].includes(run.kind)
472
+ ? { kind: run.kind, available: true, automation_stage: getAutomationStageDetails(project.id, run.id), retry_eligibility: retryEligibility }
473
+ : { kind: run.kind, available: true, retry_eligibility: retryEligibility };
452
474
  const artifacts = loadExecutionArtifactSummariesForRuns(project.id, [run.id], MAX_ARTIFACTS_PER_RUN).get(run.id)
453
475
  ?? { artifacts: [], artifact_count: 0 };
454
476
  return {
@@ -467,17 +489,16 @@ const formatWorktreeDeleteError = (target, branch, error) => {
467
489
  const prepareBundleWorkspace = async (bundle, project, options = {}) => {
468
490
  const branch = bundle.branch;
469
491
  const workspacePath = resolveWorktreePath(branch, project);
470
- let setupCompleted = false;
492
+ let workspacePrepared = false;
471
493
  try {
472
494
  ensureInitialCommitForWorktrees(project.repo_path);
473
495
  await createWorktreeAsync(branch, project, { baseBranch: options.baseBranch ?? null });
474
496
  await syncWorktreeFilesAsync(branch, project);
475
- await runSetupCommandsInWorkspace(project, workspacePath);
476
- setupCompleted = true;
497
+ workspacePrepared = true;
477
498
  return workspacePath;
478
499
  }
479
500
  finally {
480
- if (!setupCompleted) {
501
+ if (!workspacePrepared) {
481
502
  deleteWorktree(branch, project);
482
503
  }
483
504
  }
@@ -512,6 +533,18 @@ const getBundlePlanBlockerError = () => {
512
533
  const hasActiveBundleCheckWork = (projectId, bundleId) => Boolean(getActiveBundleRepairBlocker(bundleId)
513
534
  || findActiveCheckBatchForTarget(projectId, 'bundle', bundleId)
514
535
  || findActiveCheckAutoFixForTarget(projectId, 'bundle', bundleId));
536
+ const buildBranchCleanupWarning = (branch, result) => {
537
+ if (result === 'unmerged') {
538
+ return `Git branch "${branch}" was kept because it contains commits that are not merged into its base branch.`;
539
+ }
540
+ if (result === 'checked-out') {
541
+ return `Git branch "${branch}" was kept because it is still checked out in another worktree.`;
542
+ }
543
+ if (result === 'failed') {
544
+ return `Git branch "${branch}" could not be deleted automatically.`;
545
+ }
546
+ return null;
547
+ };
515
548
  const cleanupWorktreeExecutionTarget = async ({ project, targetKind, targetKey, targetLabel, worktreeTarget, branch, }) => {
516
549
  try {
517
550
  await cleanupExecutionEnvironmentsForTarget({
@@ -530,6 +563,7 @@ const cleanupWorktreeExecutionTarget = async ({ project, targetKind, targetKey,
530
563
  catch (error) {
531
564
  throw new HttpError(500, formatWorktreeDeleteError(worktreeTarget, branch, error));
532
565
  }
566
+ return { branchWarning: buildBranchCleanupWarning(branch, deleteLocalBranch(branch, project)) };
533
567
  };
534
568
  const deleteTicketRecord = async (ticket) => {
535
569
  if (ticket.project_id
@@ -1471,6 +1505,14 @@ const resolveBundleLatestBuiltTicket = (tickets) => {
1471
1505
  return right.id.localeCompare(left.id);
1472
1506
  })[0] ?? null;
1473
1507
  };
1508
+ const resolveBundleNewestEligibleTicket = (tickets) => {
1509
+ const eligibleTickets = tickets.filter(ticket => ticket.state !== 'plan' && ticket.parked_at === null);
1510
+ const latestBuiltTicket = resolveBundleLatestBuiltTicket(eligibleTickets);
1511
+ if (latestBuiltTicket) {
1512
+ return latestBuiltTicket;
1513
+ }
1514
+ return sortBundleTicketsByExecutionOrder(eligibleTickets).at(-1) ?? null;
1515
+ };
1474
1516
  const resolveBundleTargetTicket = (tickets, options = {}) => {
1475
1517
  const explicitTargetTicket = options.targetTicketId
1476
1518
  ? tickets.find(ticket => ticket.id === options.targetTicketId) ?? null
@@ -1479,6 +1521,7 @@ const resolveBundleTargetTicket = (tickets, options = {}) => {
1479
1521
  ? tickets.find(ticket => ticket.id === options.reviewOwnerTicketId) ?? null
1480
1522
  : null;
1481
1523
  const runningTicket = tickets.find(ticket => ticket.agent_status === 'running') ?? null;
1524
+ const newestEligibleTicket = resolveBundleNewestEligibleTicket(tickets);
1482
1525
  const latestBuiltTicket = resolveBundleLatestBuiltTicket(tickets);
1483
1526
  const latestSessionTicket = options.latestSessionTicketId
1484
1527
  ? tickets.find(ticket => ticket.id === options.latestSessionTicketId && ticket.session_id) ?? null
@@ -1488,6 +1531,7 @@ const resolveBundleTargetTicket = (tickets, options = {}) => {
1488
1531
  : null;
1489
1532
  return explicitTargetTicket
1490
1533
  ?? reviewOwnerTicket
1534
+ ?? newestEligibleTicket
1491
1535
  ?? runningTicket
1492
1536
  ?? latestBuiltTicket
1493
1537
  ?? latestSessionTicket
@@ -2556,6 +2600,8 @@ registerExecutionEnvironmentRoutes(app, {
2556
2600
  adapter: dockerSandboxAdapter,
2557
2601
  runtimeController: dockerSandboxRuntimeController,
2558
2602
  });
2603
+ registerExecutionProfileRoutes(app);
2604
+ registerApprovalRoutes(app, { emitter: io });
2559
2605
  const requireDesktopMobileAccessRequest = (req, res) => {
2560
2606
  const rejectionReason = validateDesktopMobileAccessRequest(req);
2561
2607
  if (!rejectionReason) {
@@ -3957,8 +4003,9 @@ app.delete('/bundles/:id', async (req, res) => {
3957
4003
  const affectedSpecSessionIds = listSpecSessions(result.project.id)
3958
4004
  .filter(specSession => specSession.target_bundle_id === bundle.id)
3959
4005
  .map(specSession => specSession.id);
4006
+ let cleanupResult;
3960
4007
  try {
3961
- await cleanupWorktreeExecutionTarget({
4008
+ cleanupResult = await cleanupWorktreeExecutionTarget({
3962
4009
  project: result.project,
3963
4010
  targetKind: 'bundle-worktree',
3964
4011
  targetKey: createExecutionTargetKey(result.project.id, {
@@ -3989,6 +4036,9 @@ app.delete('/bundles/:id', async (req, res) => {
3989
4036
  io.emit('spec-session:updated', updatedSpecSession);
3990
4037
  }
3991
4038
  });
4039
+ if (cleanupResult.branchWarning) {
4040
+ return res.status(200).json({ warning: cleanupResult.branchWarning });
4041
+ }
3992
4042
  res.status(204).end();
3993
4043
  });
3994
4044
  app.patch('/bundles/:id', (req, res) => {
@@ -7279,8 +7329,28 @@ app.post('/projects/:id/git-push', async (req, res) => {
7279
7329
  return res.status(workspace.status).json({ error: workspace.error });
7280
7330
  }
7281
7331
  const forceWithLease = req.body?.force_with_lease === true;
7332
+ if (forceWithLease) {
7333
+ try {
7334
+ const admission = await admitForcePush({
7335
+ project: workspace.project,
7336
+ cwd: workspace.cwd,
7337
+ branch: workspace.branch,
7338
+ source: {
7339
+ kind: 'project',
7340
+ id: project.id,
7341
+ targetKind: workspace.target_kind,
7342
+ bundleId: workspace.target_bundle_id,
7343
+ },
7344
+ emitter: io,
7345
+ });
7346
+ return res.status(202).json(admission);
7347
+ }
7348
+ catch (error) {
7349
+ return res.status(409).json({ error: getErrorMessage(error) || 'Unable to request force-push approval right now.' });
7350
+ }
7351
+ }
7282
7352
  try {
7283
- await pushCurrentBranch(workspace.cwd, workspace.branch, { forceWithLease });
7353
+ await pushCurrentBranch(workspace.cwd, workspace.branch, { forceWithLease: false });
7284
7354
  }
7285
7355
  catch (error) {
7286
7356
  return res.status(409).json({ error: getErrorMessage(error) || 'Unable to push the branch right now.' });
@@ -7345,6 +7415,12 @@ app.patch('/projects/:id', (req, res) => {
7345
7415
  if (!project) {
7346
7416
  return res.status(404).json({ error: 'Not found' });
7347
7417
  }
7418
+ if (Object.hasOwn(req.body ?? {}, 'build_execution_provider')) {
7419
+ return res.status(409).json({
7420
+ error: 'Build execution provider changes must use the execution profile endpoint',
7421
+ reason: 'execution_profile_required',
7422
+ });
7423
+ }
7348
7424
  const validation = validateProjectPayload(asProjectPayload(req.body), { partial: true, currentProjectId: project.id });
7349
7425
  if ('error' in validation) {
7350
7426
  return res.status(400).json({ error: validation.error });
@@ -7357,7 +7433,7 @@ app.patch('/projects/:id', (req, res) => {
7357
7433
  });
7358
7434
  }
7359
7435
  }
7360
- const { name, repo_path, build_execution_provider, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, pre_review_check_definition_ids, post_build_check_definition_ids, auto_fix_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, reviewer_model, reviewer_variant, commit_message_rules, commit_after_each_ticket, auto_park_stale_tickets, memory_enabled, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
7436
+ const { name, repo_path, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, pre_review_check_definition_ids, post_build_check_definition_ids, auto_fix_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, reviewer_model, reviewer_variant, commit_message_rules, commit_after_each_ticket, auto_park_stale_tickets, memory_enabled, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
7361
7437
  const updateProject = db.prepare(`
7362
7438
  UPDATE projects SET
7363
7439
  name = ?,
@@ -7424,7 +7500,7 @@ app.patch('/projects/:id', (req, res) => {
7424
7500
  if ('error' in policyReferences) {
7425
7501
  return policyReferences;
7426
7502
  }
7427
- updateProject.run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'build_execution_provider') ? build_execution_provider : project.build_execution_provider, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, nextCheckDefinitions, nextRequiredCheckDefinitionIds, nextPreReviewCheckDefinitionIds, nextPostBuildCheckDefinitionIds, nextAutoFixCheckDefinitionIds, hasOwn(validation.values, 'test_commands') ? test_commands : project.test_commands, hasOwn(validation.values, 'run_ide') ? run_ide : project.run_ide, hasOwn(validation.values, 'preview_service_name') ? preview_service_name : project.preview_service_name, hasOwn(validation.values, 'preview_path') ? preview_path : project.preview_path, hasOwn(validation.values, 'preview_capability_mode') ? preview_capability_mode : project.preview_capability_mode, hasOwn(validation.values, 'helper_model') ? helper_model : project.helper_model, hasOwn(validation.values, 'helper_variant') ? helper_variant : project.helper_variant, nextReviewerModel, nextReviewerVariant, hasOwn(validation.values, 'commit_message_rules') ? commit_message_rules : project.commit_message_rules, hasOwn(validation.values, 'commit_after_each_ticket') ? commit_after_each_ticket : project.commit_after_each_ticket, hasOwn(validation.values, 'auto_park_stale_tickets') ? auto_park_stale_tickets : project.auto_park_stale_tickets, hasOwn(validation.values, 'memory_enabled') ? memory_enabled : project.memory_enabled, hasOwn(validation.values, 'scan_include_globs_json') ? scan_include_globs_json : project.scan_include_globs_json, hasOwn(validation.values, 'scan_exclude_globs_json') ? scan_exclude_globs_json : project.scan_exclude_globs_json, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, hasOwn(validation.values, 'spec_aware_code_review_enabled') ? spec_aware_code_review_enabled : project.spec_aware_code_review_enabled, hasOwn(validation.values, 'automation_depth') ? automation_depth : project.automation_depth, hasOwn(validation.values, 'automation_review_fix_enabled') ? automation_review_fix_enabled : project.automation_review_fix_enabled, hasOwn(validation.values, 'automation_pause_on_open_questions') ? automation_pause_on_open_questions : project.automation_pause_on_open_questions, hasOwn(validation.values, 'automation_review_severity') ? automation_review_severity : project.automation_review_severity, hasOwn(validation.values, 'automation_review_max_iterations') ? automation_review_max_iterations : project.automation_review_max_iterations, req.params.id);
7503
+ updateProject.run(name ?? project.name, repo_path ?? project.repo_path, project.build_execution_provider, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, nextCheckDefinitions, nextRequiredCheckDefinitionIds, nextPreReviewCheckDefinitionIds, nextPostBuildCheckDefinitionIds, nextAutoFixCheckDefinitionIds, hasOwn(validation.values, 'test_commands') ? test_commands : project.test_commands, hasOwn(validation.values, 'run_ide') ? run_ide : project.run_ide, hasOwn(validation.values, 'preview_service_name') ? preview_service_name : project.preview_service_name, hasOwn(validation.values, 'preview_path') ? preview_path : project.preview_path, hasOwn(validation.values, 'preview_capability_mode') ? preview_capability_mode : project.preview_capability_mode, hasOwn(validation.values, 'helper_model') ? helper_model : project.helper_model, hasOwn(validation.values, 'helper_variant') ? helper_variant : project.helper_variant, nextReviewerModel, nextReviewerVariant, hasOwn(validation.values, 'commit_message_rules') ? commit_message_rules : project.commit_message_rules, hasOwn(validation.values, 'commit_after_each_ticket') ? commit_after_each_ticket : project.commit_after_each_ticket, hasOwn(validation.values, 'auto_park_stale_tickets') ? auto_park_stale_tickets : project.auto_park_stale_tickets, hasOwn(validation.values, 'memory_enabled') ? memory_enabled : project.memory_enabled, hasOwn(validation.values, 'scan_include_globs_json') ? scan_include_globs_json : project.scan_include_globs_json, hasOwn(validation.values, 'scan_exclude_globs_json') ? scan_exclude_globs_json : project.scan_exclude_globs_json, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, hasOwn(validation.values, 'spec_aware_code_review_enabled') ? spec_aware_code_review_enabled : project.spec_aware_code_review_enabled, hasOwn(validation.values, 'automation_depth') ? automation_depth : project.automation_depth, hasOwn(validation.values, 'automation_review_fix_enabled') ? automation_review_fix_enabled : project.automation_review_fix_enabled, hasOwn(validation.values, 'automation_pause_on_open_questions') ? automation_pause_on_open_questions : project.automation_pause_on_open_questions, hasOwn(validation.values, 'automation_review_severity') ? automation_review_severity : project.automation_review_severity, hasOwn(validation.values, 'automation_review_max_iterations') ? automation_review_max_iterations : project.automation_review_max_iterations, req.params.id);
7428
7504
  if (hasOwn(validation.values, 'linked_project_ids')) {
7429
7505
  replaceProjectLinks(project.id, linked_project_ids ?? []);
7430
7506
  }
@@ -7471,6 +7547,17 @@ const assertProjectDeletionEligible = (projectId, claimId) => {
7471
7547
  if (!claimId && hasActiveProjectRepositoryGraphScan(projectId)) {
7472
7548
  throw new ProjectDeletionError(409, 'Cannot delete a project while a repository graph scan is active');
7473
7549
  }
7550
+ if (db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'approval_requests'").get() && db.prepare(`
7551
+ SELECT 1 FROM approval_requests request
7552
+ JOIN execution_runs run
7553
+ ON run.id = request.execution_run_id AND run.project_id = request.project_id
7554
+ WHERE request.project_id = ?
7555
+ AND request.status IN ('pending', 'approved', 'consumed')
7556
+ AND run.status IN ('queued', 'running', 'waiting')
7557
+ LIMIT 1
7558
+ `).get(projectId)) {
7559
+ throw new ProjectDeletionError(409, 'Cannot delete a project while consequential action approval is active');
7560
+ }
7474
7561
  };
7475
7562
  const completeClaimedProjectDeletion = async (projectId, claimId) => {
7476
7563
  const marked = db.prepare(`
@@ -8025,7 +8112,7 @@ app.post('/tickets/:id/retry', (req, res) => {
8025
8112
  LIMIT 1
8026
8113
  `).get(ticket.project_id, ticket.id)
8027
8114
  : undefined;
8028
- const acceptRetry = () => {
8115
+ const acceptRetry = (selection) => {
8029
8116
  db.prepare('UPDATE tickets SET agent_status = NULL, agent_log = NULL, agent_message_id = NULL, previous_agent_log = NULL, previous_agent_message_id = NULL, streaming_response = NULL, build_completed_at = NULL, auto_park_dismissed_at = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(req.params.id);
8030
8117
  const updated = getTicket(req.params.id);
8031
8118
  if (!updated) {
@@ -8038,6 +8125,7 @@ app.post('/tickets/:id/retry', (req, res) => {
8038
8125
  projectId: updated.project_id,
8039
8126
  worktreeBundleId: updated.worktree_bundle_id,
8040
8127
  activate: shouldStartBundleBuildNow(updated),
8128
+ selection,
8041
8129
  }).sequenceId;
8042
8130
  }
8043
8131
  return {
@@ -8049,7 +8137,19 @@ app.post('/tickets/:id/retry', (req, res) => {
8049
8137
  if (ticket.project_id && ticket.worktree_bundle_id && hasActiveSequenceForBundle(ticket.project_id, ticket.worktree_bundle_id)) {
8050
8138
  try {
8051
8139
  withExecutionRunTransaction(transaction => {
8052
- const acceptance = acceptRetry();
8140
+ releaseQueuedBuildSequenceTicketExecutionRunForRetry({
8141
+ projectId: ticket.project_id,
8142
+ worktreeBundleId: ticket.worktree_bundle_id,
8143
+ ticketId: ticket.id,
8144
+ cancelExecutionRun: runId => transaction.transitionExecutionRun({
8145
+ projectId: ticket.project_id,
8146
+ runId,
8147
+ status: 'cancelled',
8148
+ domainStatus: 'cancelled',
8149
+ eventType: 'retry_superseded',
8150
+ }),
8151
+ });
8152
+ const acceptance = acceptRetry(createExecutionProfileSelection(ticket.project_id));
8053
8153
  if (!acceptance.sequenceId) {
8054
8154
  throw new Error('Build sequence no longer exists');
8055
8155
  }
@@ -8057,7 +8157,9 @@ app.post('/tickets/:id/retry', (req, res) => {
8057
8157
  if (!project) {
8058
8158
  throw new Error('Build project no longer exists');
8059
8159
  }
8060
- transaction.createExecutionRun({
8160
+ const selection = getBuildSequenceTicketExecutionProfileSelection(acceptance.sequenceId, acceptance.ticket.id);
8161
+ const profileRevision = validateExecutionProfileSelection(ticket.project_id, selection);
8162
+ const executionRun = transaction.createExecutionRun({
8061
8163
  projectId: ticket.project_id,
8062
8164
  kind: 'build',
8063
8165
  domainStatus: 'queued',
@@ -8067,12 +8169,14 @@ app.post('/tickets/:id/retry', (req, res) => {
8067
8169
  { entityType: 'build_sequence', entityId: acceptance.sequenceId, relationship: 'context' },
8068
8170
  ],
8069
8171
  metadata: {
8070
- build_execution_provider: project.build_execution_provider ?? 'host',
8172
+ build_execution_provider: profileRevision.profile.provider_preference,
8173
+ ...executionProfileSelectionMetadata(selection),
8071
8174
  },
8072
8175
  initiatedBy: 'user',
8073
8176
  retryOfRunId: retrySource?.id ?? null,
8074
8177
  eventType: 'build_created',
8075
8178
  });
8179
+ bindBuildSequenceTicketExecutionRun(acceptance.sequenceId, acceptance.ticket.id, executionRun.id);
8076
8180
  }, io);
8077
8181
  }
8078
8182
  catch (error) {
@@ -8353,8 +8457,32 @@ app.post('/tickets/:id/git-push', async (req, res) => {
8353
8457
  }
8354
8458
  const previousTickets = listTicketsInRunContext(ticket);
8355
8459
  const forceWithLease = req.body?.force_with_lease === true;
8460
+ if (forceWithLease) {
8461
+ try {
8462
+ const admission = await admitForcePush({
8463
+ project: workspace.project,
8464
+ cwd: workspace.cwd,
8465
+ branch: workspace.branch,
8466
+ source: {
8467
+ kind: 'ticket',
8468
+ id: ticket.id,
8469
+ targetKind: workspace.kind === 'project_root'
8470
+ ? 'repo_root'
8471
+ : ticket.worktree_bundle_id
8472
+ ? 'bundle'
8473
+ : 'ticket',
8474
+ bundleId: ticket.worktree_bundle_id ?? null,
8475
+ },
8476
+ emitter: io,
8477
+ });
8478
+ return res.status(202).json(admission);
8479
+ }
8480
+ catch (error) {
8481
+ return res.status(409).json({ error: getErrorMessage(error) || 'Unable to request force-push approval right now.' });
8482
+ }
8483
+ }
8356
8484
  try {
8357
- await pushCurrentBranch(workspace.cwd, workspace.branch, { forceWithLease });
8485
+ await pushCurrentBranch(workspace.cwd, workspace.branch, { forceWithLease: false });
8358
8486
  }
8359
8487
  catch (error) {
8360
8488
  return res.status(409).json({ error: getErrorMessage(error) || 'Unable to push the branch right now.' });
@@ -8836,6 +8964,7 @@ app.get('/projects/:id/execution-runs', (req, res) => {
8836
8964
  projectId: project.id,
8837
8965
  kind: query.kind,
8838
8966
  statuses: query.statuses,
8967
+ domainStatus: query.domainStatus,
8839
8968
  cursor: query.cursor,
8840
8969
  limit: query.limit,
8841
8970
  }));
@@ -8905,6 +9034,7 @@ app.get('/projects/:id/execution-timeline', (req, res) => {
8905
9034
  project,
8906
9035
  kind: query.kind,
8907
9036
  statuses: query.statuses,
9037
+ domainStatus: query.domainStatus,
8908
9038
  cursor: query.cursor,
8909
9039
  limit: query.limit,
8910
9040
  scope: scope.scopeEntity,
@@ -9364,8 +9494,11 @@ io.on('connection', (socket) => {
9364
9494
  registerTerminalSocketHandlers(io, socket);
9365
9495
  });
9366
9496
  const DEFAULT_PORT = 3001;
9497
+ const APPROVAL_RECONCILIATION_INTERVAL_MS = 30_000;
9367
9498
  let executionEvidenceCleanupTimer = null;
9368
9499
  let executionArtifactCleanupTimer = null;
9500
+ let approvalReconciliationTimer = null;
9501
+ let approvalReconciliationPromise = null;
9369
9502
  let executionArtifactCleanupPromise = null;
9370
9503
  let startServerPromise = null;
9371
9504
  let shutdownServerPromise = null;
@@ -9441,6 +9574,45 @@ const startExecutionArtifactCleanupTimer = () => {
9441
9574
  }, resolveArtifactCleanupIntervalMs());
9442
9575
  executionArtifactCleanupTimer.unref();
9443
9576
  };
9577
+ const runApprovalReconciliation = () => {
9578
+ if (approvalReconciliationPromise) {
9579
+ return approvalReconciliationPromise;
9580
+ }
9581
+ approvalReconciliationPromise = reconcileApprovalRequests({
9582
+ emitter: io,
9583
+ includeConsumed: false,
9584
+ resumeApproved: async (request) => {
9585
+ if (request.action_kind === 'git_push') {
9586
+ await resumeGitPushApproval(request.project_id, request.id, io);
9587
+ }
9588
+ else {
9589
+ await resumeProductionCheckApproval(request.project_id, request.id, io);
9590
+ }
9591
+ },
9592
+ resolveTerminal: async (request) => {
9593
+ if (request.action_kind === 'git_push') {
9594
+ await finalizeGitPushApproval(request.project_id, request.id, io);
9595
+ }
9596
+ else {
9597
+ finalizeProductionCheckApproval(request.project_id, request.id, io);
9598
+ }
9599
+ },
9600
+ }).then(() => undefined).catch(error => {
9601
+ console.error('Failed to reconcile approval requests', error);
9602
+ }).finally(() => {
9603
+ approvalReconciliationPromise = null;
9604
+ });
9605
+ return approvalReconciliationPromise;
9606
+ };
9607
+ const startApprovalReconciliationTimer = () => {
9608
+ if (approvalReconciliationTimer) {
9609
+ return;
9610
+ }
9611
+ approvalReconciliationTimer = setInterval(() => {
9612
+ void runApprovalReconciliation();
9613
+ }, APPROVAL_RECONCILIATION_INTERVAL_MS);
9614
+ approvalReconciliationTimer.unref();
9615
+ };
9444
9616
  const resolveStartResult = (host) => {
9445
9617
  const address = httpServer.address();
9446
9618
  const { displayHost } = resolveDesktopServerHost(host);
@@ -9510,6 +9682,7 @@ export const startServer = ({ port = DEFAULT_PORT, host } = {}) => {
9510
9682
  throwIfShutdownRequested();
9511
9683
  startExecutionEvidenceCleanupTimer();
9512
9684
  startExecutionArtifactCleanupTimer();
9685
+ startApprovalReconciliationTimer();
9513
9686
  return resolveStartResult(host);
9514
9687
  })().finally(() => {
9515
9688
  startServerPromise = null;
@@ -9538,6 +9711,11 @@ export const shutdownServer = async (signal) => {
9538
9711
  clearInterval(executionArtifactCleanupTimer);
9539
9712
  executionArtifactCleanupTimer = null;
9540
9713
  }
9714
+ if (approvalReconciliationTimer) {
9715
+ clearInterval(approvalReconciliationTimer);
9716
+ approvalReconciliationTimer = null;
9717
+ }
9718
+ await approvalReconciliationPromise;
9541
9719
  await executionArtifactCleanupPromise;
9542
9720
  await stopMobileAccess();
9543
9721
  const repositoryGraphSchemaExists = db.prepare(`