@straiffi/archon 1.10.8 → 1.10.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/ActivityView-CRkM5TeN.js +1 -0
- package/dist/client/assets/GitHubActionsDialog-KFUiR00H.js +1 -0
- package/dist/client/assets/VerificationDialog-BIDttgiW.js +4 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-CzUKSWJZ.js → architectureDiagram-3BPJPVTR-u0PSZlNP.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-DoNm6BnS.js → blockDiagram-GPEHLZMM-DFXLHRIk.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-C7ocs3wQ.js → c4Diagram-AAUBKEIU-Dvic8tCt.js} +1 -1
- package/dist/client/assets/channel-Dhv_uSKx.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-DUmaxCq8.js → chunk-2J33WTMH-t5CxhRXJ.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-CX2MPsMQ.js → chunk-3OPIFGDE-YHT1dP8Q.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-B103kU_y.js → chunk-5ZQYHXKU-DcxuI7lb.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-BmLJ4h5X.js → chunk-727SXJPM-D1F6DmrT.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-Cnv1b4Vd.js → chunk-AQP2D5EJ-BIx6Ohbk.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-CkIUhpY9.js → chunk-CSCIHK7Q-B355h6HA.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-wmkCuZ5U.js → chunk-KSCS5N6A-CLHGYOLQ.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-D9AdL5mv.js → chunk-L5ZTLDWV-DFLYcMx8.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-rMyTvR3J.js → chunk-LZXEDZCA-DtljFujX.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-19HTozu8.js → chunk-ND2GUHAM-CIuUQ86K.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-DBHgwKcS.js → chunk-NZK2D7GU-CIwqlxek.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-4WpSG06s.js → chunk-O5CBEL6O-W42BEPu4.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-loqCl7ki.js → chunk-WU5MYG2G-C_zuaNxV.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-9XKvSSLq.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Dbr5_49Y.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-FVpIr0qZ.js → dagre-BM42HDAG-UKfIUJTP.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-Dgj8FaDO.js → diagram-2AECGRRQ-Xn6yojqq.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-DNpYKz_a.js → diagram-5GNKFQAL-BzpY1ZEl.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-N7R7efvN.js → diagram-KO2AKTUF-Dyrvzvsg.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-hoOvd_tG.js → diagram-LMA3HP47-AqImKwAr.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-ze25ZIy6.js → diagram-OG6HWLK6-CaQQ46mi.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-780zvNNZ.js → erDiagram-TEJ5UH35-Dkw4Aa5u.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-x4UB_M_4.js → flowDiagram-I6XJVG4X-CJTuGX9h.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-B5Wl0y91.js → ganttDiagram-6RSMTGT7-CidgOdS-.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CZtVHpBH.js → gitGraphDiagram-PVQCEYII-Bj02mIQj.js} +1 -1
- package/dist/client/assets/index-ClRB9YWJ.css +2 -0
- package/dist/client/assets/index-DBMdsTvy.js +276 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-BOzLwsnA.js → infoDiagram-5YYISTIA-GwJWfo_r.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-ButBhNg6.js → ishikawaDiagram-YF4QCWOH-rbrlWbUn.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-CO0Db0Gn.js → journeyDiagram-JHISSGLW-BLi_uzQM.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-B6KT8yln.js → kanban-definition-UN3LZRKU-ynlywm-_.js} +1 -1
- package/dist/client/assets/{line-Cv-WaOjt.js → line-BNyTZBB4.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-DgJy4Qlx.js → mermaid-parser.core-CzUOsZAD.js} +1 -1
- package/dist/client/assets/{mermaid.core-YpvipwZo.js → mermaid.core-K0QR6SzN.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-DGgtT98Y.js → mindmap-definition-RKZ34NQL-BKcgqXm0.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-CgqnLewO.js → pieDiagram-4H26LBE5-Bjx0lvDI.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Ci_0SzdR.js → quadrantDiagram-W4KKPZXB-CmUlgyUv.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-wJl0DP74.js → requirementDiagram-4Y6WPE33-Dj2mzg1y.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CE7aDnzF.js → sankeyDiagram-5OEKKPKP-DC--H6qf.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-CW0bF1i6.js → sequenceDiagram-3UESZ5HK-BkNrt6Ra.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-Ccl0RXsJ.js → stateDiagram-AJRCARHV-BuxxRu1Y.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Cyo85GPN.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-TuF6SUnb.js → timeline-definition-PNZ67QCA-Db3pCVSp.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-D8OvSROI.js → vennDiagram-CIIHVFJN-BgBSGThQ.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DXW3R5NJ.js → wardleyDiagram-YWT4CUSO-BJhPdjYS.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-qdiUQDW9.js → xychartDiagram-2RQKCTM6-BY4Ra0kX.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +341 -4
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +197 -270
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/agentRuns.js +14 -1
- package/dist/server/lib/agentRuns.js.map +1 -1
- package/dist/server/lib/automationLoopProjection.js +55 -12
- package/dist/server/lib/automationLoopProjection.js.map +1 -1
- package/dist/server/lib/automationProgression.js +2 -5
- package/dist/server/lib/automationProgression.js.map +1 -1
- package/dist/server/lib/automationResumeDecision.js +19 -1
- package/dist/server/lib/automationResumeDecision.js.map +1 -1
- package/dist/server/lib/boundedProcess.js +129 -0
- package/dist/server/lib/boundedProcess.js.map +1 -0
- package/dist/server/lib/buildControlEligibility.js +4 -0
- package/dist/server/lib/buildControlEligibility.js.map +1 -0
- package/dist/server/lib/buildExecutionAdmission.js +76 -0
- package/dist/server/lib/buildExecutionAdmission.js.map +1 -0
- package/dist/server/lib/buildSequenceResume.js +79 -0
- package/dist/server/lib/buildSequenceResume.js.map +1 -0
- package/dist/server/lib/buildWorkload.js +11 -0
- package/dist/server/lib/buildWorkload.js.map +1 -0
- package/dist/server/lib/bundleReviewOperations.js +7 -0
- package/dist/server/lib/bundleReviewOperations.js.map +1 -1
- package/dist/server/lib/chats.js +12 -0
- package/dist/server/lib/chats.js.map +1 -1
- package/dist/server/lib/checkAutoFixDetails.js +36 -1
- package/dist/server/lib/checkAutoFixDetails.js.map +1 -1
- package/dist/server/lib/executionRetry.js +13 -1
- package/dist/server/lib/executionRetry.js.map +1 -1
- package/dist/server/lib/executionRunAuthority.js +2 -2
- package/dist/server/lib/executionRunAuthority.js.map +1 -1
- package/dist/server/lib/executionTargets.js +7 -4
- package/dist/server/lib/executionTargets.js.map +1 -1
- package/dist/server/lib/executionTimeline.js +8 -13
- package/dist/server/lib/executionTimeline.js.map +1 -1
- package/dist/server/lib/executionTimelineHierarchy.js +83 -0
- package/dist/server/lib/executionTimelineHierarchy.js.map +1 -0
- package/dist/server/lib/failedBuildSequenceRecovery.js +133 -0
- package/dist/server/lib/failedBuildSequenceRecovery.js.map +1 -0
- package/dist/server/lib/git.js +89 -48
- package/dist/server/lib/git.js.map +1 -1
- package/dist/server/lib/hostRunSession.js +62 -0
- package/dist/server/lib/hostRunSession.js.map +1 -0
- package/dist/server/lib/hostServiceRecovery.js +159 -0
- package/dist/server/lib/hostServiceRecovery.js.map +1 -0
- package/dist/server/lib/hostServiceRunProvider.js +32 -0
- package/dist/server/lib/hostServiceRunProvider.js.map +1 -0
- package/dist/server/lib/integrations/github.js +49 -105
- package/dist/server/lib/integrations/github.js.map +1 -1
- package/dist/server/lib/mobileAccess.js +6 -2
- package/dist/server/lib/mobileAccess.js.map +1 -1
- package/dist/server/lib/projectAutoConfig.js +5 -2
- package/dist/server/lib/projectAutoConfig.js.map +1 -1
- package/dist/server/lib/projectTargets.js +8 -2
- package/dist/server/lib/projectTargets.js.map +1 -1
- package/dist/server/lib/projects.js +25 -1
- package/dist/server/lib/projects.js.map +1 -1
- package/dist/server/lib/reviewFindingReconciliation.js +9 -2
- package/dist/server/lib/reviewFindingReconciliation.js.map +1 -1
- package/dist/server/lib/reviewFindings.js +113 -23
- package/dist/server/lib/reviewFindings.js.map +1 -1
- package/dist/server/lib/reviewRepairAssessmentRecords.js +41 -0
- package/dist/server/lib/reviewRepairAssessmentRecords.js.map +1 -0
- package/dist/server/lib/reviewRepairCompletion.js +78 -18
- package/dist/server/lib/reviewRepairCompletion.js.map +1 -1
- package/dist/server/lib/reviewRepairIterationRecords.js +4 -2
- package/dist/server/lib/reviewRepairIterationRecords.js.map +1 -1
- package/dist/server/lib/reviewRepairIterations.js +8 -2
- package/dist/server/lib/reviewRepairIterations.js.map +1 -1
- package/dist/server/lib/reviewRepairProgress.js +177 -0
- package/dist/server/lib/reviewRepairProgress.js.map +1 -0
- package/dist/server/lib/reviewRepairReconciliation.js +119 -140
- package/dist/server/lib/reviewRepairReconciliation.js.map +1 -1
- package/dist/server/lib/reviewRepairResumeOverride.js +55 -0
- package/dist/server/lib/reviewRepairResumeOverride.js.map +1 -0
- package/dist/server/lib/run.js +523 -504
- package/dist/server/lib/run.js.map +1 -1
- package/dist/server/lib/runProcessIdentity.js +71 -0
- package/dist/server/lib/runProcessIdentity.js.map +1 -0
- package/dist/server/lib/runProcesses.js +261 -0
- package/dist/server/lib/runProcesses.js.map +1 -0
- package/dist/server/lib/runServices.js +221 -0
- package/dist/server/lib/runServices.js.map +1 -0
- package/dist/server/lib/runSessionLifecycle.js +68 -0
- package/dist/server/lib/runSessionLifecycle.js.map +1 -0
- package/dist/server/lib/runStatus.js +7 -0
- package/dist/server/lib/runStatus.js.map +1 -0
- package/dist/server/lib/serviceRuns.js +273 -16
- package/dist/server/lib/serviceRuns.js.map +1 -1
- package/dist/server/lib/ticketAutomation.js +4 -27
- package/dist/server/lib/ticketAutomation.js.map +1 -1
- package/dist/server/lib/ticketRunOperations.js +103 -34
- package/dist/server/lib/ticketRunOperations.js.map +1 -1
- package/dist/server/lib/tickets.js +46 -21
- package/dist/server/lib/tickets.js.map +1 -1
- package/dist/server/lib/types.js.map +1 -1
- package/dist/server/workers/build.js +44 -69
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/plan.js +0 -7
- package/dist/server/workers/plan.js.map +1 -1
- package/dist/server/workers/review.js +95 -146
- package/dist/server/workers/review.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/ActivityView-PGEex8sS.js +0 -1
- package/dist/client/assets/GitHubActionsDialog-CwsZRevc.js +0 -1
- package/dist/client/assets/VerificationDialog-BYE1M9T2.js +0 -4
- package/dist/client/assets/channel-b-KoJfeL.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-2c5Acffy.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-ClrW9LEB.js +0 -1
- package/dist/client/assets/index-BXsDja_y.css +0 -2
- package/dist/client/assets/index-Cy3ZoenG.js +0 -276
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-k65NSsdn.js +0 -1
- package/dist/server/lib/automationDeliveryDecision.js +0 -34
- package/dist/server/lib/automationDeliveryDecision.js.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { registerApprovalRoutes } from './lib/approvalRoutes.js';
|
|
|
18
18
|
import { admitForcePush, finalizeGitPushApproval, resumeGitPushApproval } from './lib/gitPushApprovals.js';
|
|
19
19
|
import { reconcileApprovalRequests } from './lib/approvalReconciliation.js';
|
|
20
20
|
import { dockerSandboxAdapter, dockerSandboxRunner, dockerSandboxRuntimeController, } from './lib/dockerSandboxEnvironmentOperations.js';
|
|
21
|
-
import { deleteChatSession, getChatSession, listChatSessions } from './lib/chats.js';
|
|
21
|
+
import { deleteChatSession, getChatSession, hasActiveBundleChatSession, listChatSessions } from './lib/chats.js';
|
|
22
22
|
import { deleteSpecSession, getCreatedTicketIdsForSnapshot, getSpecSession, getSpecSnapshot, listSpecSessions } from './lib/specs.js';
|
|
23
23
|
import { getSpecSnapshotRepositoryEvidenceFreshness } from './lib/specSnapshotEvidence.js';
|
|
24
24
|
import { projectSpecSnapshotForActionPrompt } from './lib/specRepositoryEvidenceFormatting.js';
|
|
@@ -32,7 +32,7 @@ import { resolveSessionTargetForExecution } from './lib/sessionTargets.js';
|
|
|
32
32
|
import { createChatMessage, getChatMessage, updateChatMessageContent } from './lib/chatMessages.js';
|
|
33
33
|
import { attachChatTicketProposalsToMessageContent } from './lib/chatTicketProposals.js';
|
|
34
34
|
import { createCorsOriginResolver, parseCorsOrigins } from './lib/cors.js';
|
|
35
|
-
import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteLocalBranch, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffDataAsync, getGitDiffFiles, getGitStatus, getGitStatusAsync, getGitUnifiedDiff, getGitUnifiedDiffAsync, getLocalProjectBranch, getProjectBranch, getProjectBranchSuggestions, getReviewDiffDataAsync, getSelectedGitUnifiedDiff, getUnpushedCommitDiffForCwd, getUnpushedCommitSummaryForCwd, getUnpushedCommitSummaryForCwdAsync, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, squashUnpushedCommitsForCwd, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd, UnpushedCommitDiffSelectionError } from './lib/git.js';
|
|
35
|
+
import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteLocalBranch, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffDataAsync, getGitDiffFiles, getGitDiffFilesAsync, getGitStatus, getGitStatusAsync, getGitUnifiedDiff, getGitUnifiedDiffAsync, getLocalProjectBranch, getProjectBranch, getProjectBranchAsync, getProjectBranchSuggestions, getReviewDiffDataAsync, getSelectedGitUnifiedDiff, getUnpushedCommitDiffForCwd, getUnpushedCommitSummaryForCwd, getUnpushedCommitSummaryForCwdAsync, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, squashUnpushedCommitsForCwd, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd, UnpushedCommitDiffSelectionError } from './lib/git.js';
|
|
36
36
|
import config from './lib/config.js';
|
|
37
37
|
import { autoConfigProject, ProjectAutoConfigError } from './lib/projectAutoConfig.js';
|
|
38
38
|
import { analyzeRepoPath, createInitialCommit, ensureInitialCommitForWorktrees, initializeGitRepo, isInitialCommitRequiredError, } from './lib/projectRepos.js';
|
|
@@ -70,8 +70,8 @@ import { abandonAutomationPause, completeAutomationProgression, completeAutomati
|
|
|
70
70
|
import { reconcileAutomationStage } from './lib/automationReconciliation.js';
|
|
71
71
|
import { runAutomationReviewGateOrPause } from './lib/automationReviewGate.js';
|
|
72
72
|
import { pausedRunBelongsToProgression, resolveAutomationResumeDecision } from './lib/automationResumeDecision.js';
|
|
73
|
-
import {
|
|
74
|
-
import {
|
|
73
|
+
import { isLegacyReviewRepairMigrationPause, recoverCompletedReviewRepairVerification } from './lib/reviewRepairReconciliation.js';
|
|
74
|
+
import { overrideNoProgressReviewRepairInTransaction } from './lib/reviewRepairResumeOverride.js';
|
|
75
75
|
import { getAutomationStageDetails, } from './lib/automationStageDetails.js';
|
|
76
76
|
import { buildAutomationFixInstruction, clearAutomationPause, countBlockingFindings, depthAtLeast, getBundleAutomationState, isAutomationActive, isAutomationDepth, recordAutomationPause, resetBundleAutomationLoop, resolveAutomationConfig, } from './lib/ticketAutomation.js';
|
|
77
77
|
import { resolveAutomationCheckGateResume, runAutomationCommitChain, runAutomationCommitOperation } from './lib/ticketAutomationCommit.js';
|
|
@@ -80,7 +80,7 @@ import { continueAfterBuildCheckpoint } from './lib/automationBuildContinuation.
|
|
|
80
80
|
import { getDiscoveredSkills, normalizeSkillNames } from './lib/skills.js';
|
|
81
81
|
import { clearAllPreviewProxies } from './lib/previewProxy.js';
|
|
82
82
|
import { createExecutionRun, addExecutionRunLink, addExecutionRunLinks, getExecutionRun, getLifecycleEvent, hasActiveBuildExecutionRunForTicket, getGitHubActionsExecutionDetails, getTestExecutionDetails, GitHubActionsDispatchConflictError, listExecutionRunLinks, listExecutionRuns, listLifecycleEvents, listProjectLifecycleEvents, toCanonicalLifecycleEvent, transitionExecutionRun, withExecutionRunTransaction, } from './lib/executionRuns.js';
|
|
83
|
-
import { appendReviewRepairIterationDecisionInTransaction,
|
|
83
|
+
import { appendReviewRepairIterationDecisionInTransaction, attachReviewRepairActionEnvelopeInTransaction, attachReviewRepairActionInTransaction, claimReviewRepairIterationInTransaction, assertReviewRepairIterationDeletionEligible, deleteTerminalReviewRepairIterationsForTicketInTransaction, getActiveReviewRepairIteration, getReviewRepairIteration, pauseReviewRepairIteration, rewindStaleReviewRepairVerification, replaceReviewRepairActionEnvelopeRetryInTransaction, replaceReviewRepairActionRetryInTransaction, replaceReviewRepairVerificationRetryInTransaction, resetReviewRepairGateForRetry, resumePausedReviewRepairIteration, terminalizeReviewRepairIteration, terminalizeReviewRepairIterationInTransaction, } from './lib/reviewRepairIterations.js';
|
|
84
84
|
import { dispatchExecutionRetry, getExecutionRetryEligibility, resolveExecutionRetry } from './lib/executionRetry.js';
|
|
85
85
|
import { listExecutionTimeline } from './lib/executionTimeline.js';
|
|
86
86
|
import { loadReviewAutomationLoopDetails } from './lib/automationLoopProjection.js';
|
|
@@ -105,7 +105,9 @@ import { refreshIfNeeded as refreshAssignedIssues, clearCachedAssignedIssues } f
|
|
|
105
105
|
import { buildCommitMessagePrompt, normalizeGeneratedCommitMessage } from './lib/commitMessage.js';
|
|
106
106
|
import { BUNDLE_PULL_REQUEST_SYNC_TTL_MS, getBundlePullRequestRecord, hasOpenBundlePullRequest, serializeBundlePullRequest, shouldSyncBundlePullRequest, syncTrackedBundlePullRequestIfNeeded, upsertBundlePullRequest, upsertBundlePullRequestFromGitHub, } from './lib/bundlePullRequests.js';
|
|
107
107
|
import { countInboundProjectLinks, getEffectiveProject, getProjectCheckDefinitions, getProjectById, getProjectTestCommands, getProjectTicketStats, hasProjectRunIde, listInterruptedProjectDeletions, listProjects, releaseInterruptedProjectDeletionClaims, replaceProjectLinks, serializeProject, updateProjectActiveTarget, validateProjectRequiredCheckPolicyReferences, validateProjectPayload, } from './lib/projects.js';
|
|
108
|
-
import { getActiveRunStatus, getActiveRunStatusForProject, getPreviewStatusForProject, hasRunConfig, isRunning, openIde, openTicketIdeInWorkspace, resolveProjectRunContextKey, runTicketInWorkspace, stopAllTickets, stopTicket } from './lib/run.js';
|
|
108
|
+
import { getActiveRunStatus, getActiveRunStatusForProject, getLastRunStatusForProject, getPreviewStatusForProject, hasRunConfig, isRunning, openIde, openTicketIdeInWorkspace, resolveProjectRunContextKey, runTicketInWorkspace, stopAllTickets, stopTicket } from './lib/run.js';
|
|
109
|
+
import { hasActiveProjectServiceRun } from './lib/serviceRuns.js';
|
|
110
|
+
import { projectRunStatus } from './lib/runStatus.js';
|
|
109
111
|
import { clearAllTestSessions, createTestSession, deleteTestSession, discoverTestsForSession, doesTestFileExistForSession, getTestSession, isTestSelectionSupported, startTestSessionRun, stopTestSessionRun } from './lib/testSessions.js';
|
|
110
112
|
import { closeCheckRunnerAdmissions, finalizeProductionCheckApproval, getCheckBatchAttempts, openCheckRunnerAdmissions, rerunCheckBatchAndSchedule, resumeProductionCheckApproval, shutdownCheckRunner, startCheckBatch, stopCheckBatch } from './lib/checkRunner.js';
|
|
111
113
|
import { CheckRepairError, getCheckRepairCandidateState, getCheckRepairDetails, getLatestCheckRepairForBatch, retryCheckRepair, startCheckRepair, stopCheckRepair } from './lib/checkRepair.js';
|
|
@@ -421,7 +423,7 @@ const getCheckBatchRerunEligibility = (project, batch, selection) => {
|
|
|
421
423
|
workspaceAvailable: !('error' in workspace),
|
|
422
424
|
});
|
|
423
425
|
};
|
|
424
|
-
const buildExecutionRunDetailResponse = async (project, run) => {
|
|
426
|
+
const buildExecutionRunDetailResponse = async (project, run, scope) => {
|
|
425
427
|
const checkBatch = run.kind === 'check_batch' ? getCheckBatchDetails(project.id, run.id) : null;
|
|
426
428
|
const retryEligibility = checkBatch
|
|
427
429
|
? getCheckBatchRerunEligibility(project, checkBatch)
|
|
@@ -463,7 +465,7 @@ const buildExecutionRunDetailResponse = async (project, run) => {
|
|
|
463
465
|
? {
|
|
464
466
|
kind: 'check_auto_fix',
|
|
465
467
|
available: true,
|
|
466
|
-
check_auto_fix: getCheckAutoFixDetails(project.id, run.id),
|
|
468
|
+
check_auto_fix: getCheckAutoFixDetails(project.id, run.id, scope),
|
|
467
469
|
retry_eligibility: retryEligibility,
|
|
468
470
|
action_kind: null,
|
|
469
471
|
}
|
|
@@ -490,7 +492,7 @@ const buildExecutionRunDetailResponse = async (project, run) => {
|
|
|
490
492
|
...artifacts,
|
|
491
493
|
environment_context: getExecutionEnvironmentContextForRun(project.id, run.id),
|
|
492
494
|
automation_loops: run.kind === 'automation_progression'
|
|
493
|
-
? loadReviewAutomationLoopDetails(project.id, run.id)
|
|
495
|
+
? loadReviewAutomationLoopDetails(project.id, run.id, scope)
|
|
494
496
|
: [],
|
|
495
497
|
};
|
|
496
498
|
};
|
|
@@ -576,7 +578,26 @@ const cleanupWorktreeExecutionTarget = async ({ project, targetKind, targetKey,
|
|
|
576
578
|
}
|
|
577
579
|
return { branchWarning: buildBranchCleanupWarning(branch, deleteLocalBranch(branch, project)) };
|
|
578
580
|
};
|
|
581
|
+
const assertTicketServiceTargetMutable = (ticketId) => {
|
|
582
|
+
db.transaction(() => {
|
|
583
|
+
const authority = db.prepare(`SELECT 1 FROM active_project_service_authorities active
|
|
584
|
+
JOIN execution_runs run ON run.id = active.execution_run_id
|
|
585
|
+
WHERE run.source_kind = 'ticket' AND run.source_id = ? LIMIT 1`).get(ticketId);
|
|
586
|
+
if (authority) {
|
|
587
|
+
throw new HttpError(409, 'Ticket has active service authority. Stop the service session and verify cleanup before changing or deleting its target.');
|
|
588
|
+
}
|
|
589
|
+
}).immediate();
|
|
590
|
+
};
|
|
579
591
|
const deleteTicketRecord = async (ticket) => {
|
|
592
|
+
try {
|
|
593
|
+
assertTicketServiceTargetMutable(ticket.id);
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
if (error instanceof HttpError) {
|
|
597
|
+
return { ok: false, status: error.status, error: error.message };
|
|
598
|
+
}
|
|
599
|
+
throw error;
|
|
600
|
+
}
|
|
580
601
|
if (ticket.project_id) {
|
|
581
602
|
try {
|
|
582
603
|
assertReviewRepairIterationDeletionEligible(ticket.project_id, ticket.id);
|
|
@@ -667,6 +688,7 @@ const deleteTicketRecord = async (ticket) => {
|
|
|
667
688
|
}
|
|
668
689
|
try {
|
|
669
690
|
withExecutionRunTransaction(transaction => {
|
|
691
|
+
assertTicketServiceTargetMutable(ticket.id);
|
|
670
692
|
if (ticket.project_id) {
|
|
671
693
|
deleteTerminalReviewRepairIterationsForTicketInTransaction(transaction, ticket.project_id, ticket.id);
|
|
672
694
|
}
|
|
@@ -1467,8 +1489,8 @@ const getWorkspaceFileTargetUnavailableMessage = (targetKind) => {
|
|
|
1467
1489
|
const targetName = targetKind === 'repo_root' ? 'Repository' : targetKind === 'bundle' ? 'Bundle' : 'Ticket';
|
|
1468
1490
|
return `${targetName} target is unavailable`;
|
|
1469
1491
|
};
|
|
1470
|
-
const getProjectBranchStateForFooter = (project, options = {}) => {
|
|
1471
|
-
const branchState =
|
|
1492
|
+
const getProjectBranchStateForFooter = async (project, options = {}) => {
|
|
1493
|
+
const branchState = await getProjectBranchAsync(project, {
|
|
1472
1494
|
refreshRemote: options.refreshRemote,
|
|
1473
1495
|
includeDiffStats: project.active_target_kind !== 'bundle',
|
|
1474
1496
|
});
|
|
@@ -1484,7 +1506,7 @@ const getProjectBranchStateForFooter = (project, options = {}) => {
|
|
|
1484
1506
|
}
|
|
1485
1507
|
return {
|
|
1486
1508
|
...branchState,
|
|
1487
|
-
diff_stats: summarizeTicketDiffFiles(
|
|
1509
|
+
diff_stats: summarizeTicketDiffFiles(await getGitDiffFilesAsync(workspace.cwd)),
|
|
1488
1510
|
};
|
|
1489
1511
|
};
|
|
1490
1512
|
const listBundleTickets = (projectId, bundleId) => {
|
|
@@ -1613,22 +1635,20 @@ const getBundleReviewState = (projectId, tickets) => {
|
|
|
1613
1635
|
has_completed_review: false,
|
|
1614
1636
|
has_follow_up_since_last_review: false,
|
|
1615
1637
|
can_re_review: false,
|
|
1638
|
+
re_review_eligibility_reason: 'no_completed_review',
|
|
1616
1639
|
};
|
|
1617
1640
|
}
|
|
1618
|
-
const
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
worktreeBundleId: representativeTicket.worktree_bundle_id,
|
|
1623
|
-
},
|
|
1624
|
-
isContextRunning: tickets.some(ticket => ticket.agent_status === 'running'),
|
|
1625
|
-
currentWorkspaceSignature: null,
|
|
1626
|
-
currentDiffSignature: null,
|
|
1627
|
-
});
|
|
1641
|
+
const activityRunning = tickets.some(ticket => ticket.agent_status === 'running') || Boolean(representativeTicket.worktree_bundle_id
|
|
1642
|
+
&& hasActiveBundleChatSession(projectId, representativeTicket.worktree_bundle_id));
|
|
1643
|
+
const eligibleTicket = tickets.find(ticket => ticket.can_re_review === true);
|
|
1644
|
+
const fallbackReason = tickets.find(ticket => ticket.re_review_eligibility_reason)?.re_review_eligibility_reason;
|
|
1628
1645
|
return {
|
|
1629
|
-
has_completed_review:
|
|
1630
|
-
has_follow_up_since_last_review:
|
|
1631
|
-
can_re_review:
|
|
1646
|
+
has_completed_review: tickets.some(ticket => ticket.has_completed_review === true),
|
|
1647
|
+
has_follow_up_since_last_review: tickets.some(ticket => ticket.has_follow_up_since_last_review === true),
|
|
1648
|
+
can_re_review: !activityRunning && Boolean(eligibleTicket),
|
|
1649
|
+
re_review_eligibility_reason: activityRunning
|
|
1650
|
+
? 'activity_running'
|
|
1651
|
+
: eligibleTicket?.re_review_eligibility_reason ?? fallbackReason ?? 'no_completed_review',
|
|
1632
1652
|
};
|
|
1633
1653
|
};
|
|
1634
1654
|
const BUNDLE_PULL_REQUEST_DISCOVERY_MISS_COOLDOWN_MS = 10 * 60 * 1000;
|
|
@@ -1977,6 +1997,7 @@ const serializeBundleDetails = (project, bundle, extras = {}) => {
|
|
|
1977
1997
|
has_completed_review: bundleReviewState.has_completed_review,
|
|
1978
1998
|
has_follow_up_since_last_review: bundleReviewState.has_follow_up_since_last_review,
|
|
1979
1999
|
can_re_review: bundleReviewState.can_re_review,
|
|
2000
|
+
re_review_eligibility_reason: bundleReviewState.re_review_eligibility_reason,
|
|
1980
2001
|
review_recovery_available: getBundleAutomationState(project.id, bundle.id)?.automation_paused_reason
|
|
1981
2002
|
? false
|
|
1982
2003
|
: getBundleReviewRecovery(project.id, bundle.id) !== null,
|
|
@@ -2071,7 +2092,7 @@ const rerunTicketReview = (ticketId) => {
|
|
|
2071
2092
|
return { ok: false, status: 409, error: 'Ticket is already running' };
|
|
2072
2093
|
}
|
|
2073
2094
|
if (!ticket.can_re_review) {
|
|
2074
|
-
return { ok: false, status: 409, error: 'Review can only be re-run after
|
|
2095
|
+
return { ok: false, status: 409, error: 'Review can only be re-run after the workspace changes' };
|
|
2075
2096
|
}
|
|
2076
2097
|
const scheduling = rerunTicketReviewOperation(ticket, io);
|
|
2077
2098
|
if (!scheduling.ok) {
|
|
@@ -2296,6 +2317,7 @@ const getBundleReviewFindings = async (projectId, bundleId) => {
|
|
|
2296
2317
|
isContextRunning: false,
|
|
2297
2318
|
currentWorkspaceSignature: null,
|
|
2298
2319
|
currentDiffSignature: null,
|
|
2320
|
+
isBundleChatRunning: hasActiveBundleChatSession(projectId, bundleId),
|
|
2299
2321
|
});
|
|
2300
2322
|
}
|
|
2301
2323
|
const context = {
|
|
@@ -2319,6 +2341,7 @@ const getBundleReviewFindings = async (projectId, bundleId) => {
|
|
|
2319
2341
|
: null,
|
|
2320
2342
|
currentDiffText: currentDiffState?.diffText ?? null,
|
|
2321
2343
|
currentChangeScope: currentDiffState?.changeScope ?? null,
|
|
2344
|
+
isBundleChatRunning: hasActiveBundleChatSession(projectId, bundleId),
|
|
2322
2345
|
});
|
|
2323
2346
|
};
|
|
2324
2347
|
const getTicketReviewFindings = async (ticketId) => {
|
|
@@ -2347,6 +2370,9 @@ const getTicketReviewFindings = async (ticketId) => {
|
|
|
2347
2370
|
: null,
|
|
2348
2371
|
currentDiffText: currentDiffState?.diffText ?? null,
|
|
2349
2372
|
currentChangeScope: currentDiffState?.changeScope ?? null,
|
|
2373
|
+
isBundleChatRunning: Boolean(ticket.project_id
|
|
2374
|
+
&& ticket.worktree_bundle_id
|
|
2375
|
+
&& hasActiveBundleChatSession(ticket.project_id, ticket.worktree_bundle_id)),
|
|
2350
2376
|
});
|
|
2351
2377
|
};
|
|
2352
2378
|
const undoBundleReviewTickets = (projectId, bundleId) => {
|
|
@@ -2795,11 +2821,9 @@ app.get('/run-config', (req, res) => {
|
|
|
2795
2821
|
});
|
|
2796
2822
|
app.get('/run-status', (req, res) => {
|
|
2797
2823
|
const requestedProjectId = getRequestedProjectId(req);
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
: getActiveRunStatus(),
|
|
2802
|
-
});
|
|
2824
|
+
const activeRun = requestedProjectId ? getActiveRunStatusForProject(requestedProjectId) : getActiveRunStatus();
|
|
2825
|
+
const durableAuthority = hasActiveProjectServiceRun(requestedProjectId);
|
|
2826
|
+
return res.json(projectRunStatus(activeRun, requestedProjectId ? getLastRunStatusForProject(requestedProjectId) : null, durableAuthority));
|
|
2803
2827
|
});
|
|
2804
2828
|
app.get('/preview-status', (req, res) => {
|
|
2805
2829
|
const projectId = getRequestedProjectId(req);
|
|
@@ -4588,7 +4612,25 @@ app.post('/bundles/:id/undo-review', (req, res) => {
|
|
|
4588
4612
|
}
|
|
4589
4613
|
return res.json(undoResult.details);
|
|
4590
4614
|
});
|
|
4591
|
-
const
|
|
4615
|
+
const parseServiceRunControl = (body) => {
|
|
4616
|
+
if (body === undefined) {
|
|
4617
|
+
return {};
|
|
4618
|
+
}
|
|
4619
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
4620
|
+
return { error: 'Run control body must be an object' };
|
|
4621
|
+
}
|
|
4622
|
+
const input = body;
|
|
4623
|
+
for (const field of ['service_id', 'session_id']) {
|
|
4624
|
+
if (input[field] !== undefined && (typeof input[field] !== 'string' || !input[field].trim())) {
|
|
4625
|
+
return { error: `${field} must be a non-empty string` };
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
return {
|
|
4629
|
+
serviceId: typeof input.service_id === 'string' ? input.service_id.trim() : undefined,
|
|
4630
|
+
sessionId: typeof input.session_id === 'string' ? input.session_id.trim() : undefined,
|
|
4631
|
+
};
|
|
4632
|
+
};
|
|
4633
|
+
const parseServiceExecutionProviderChoice = (body, allowServiceControl = false) => {
|
|
4592
4634
|
if (body === undefined || body === null) {
|
|
4593
4635
|
return 'project_default';
|
|
4594
4636
|
}
|
|
@@ -4596,7 +4638,8 @@ const parseServiceExecutionProviderChoice = (body) => {
|
|
|
4596
4638
|
return null;
|
|
4597
4639
|
}
|
|
4598
4640
|
const request = body;
|
|
4599
|
-
if (Object.keys(request).some(key => key !== 'execution_provider'
|
|
4641
|
+
if (Object.keys(request).some(key => key !== 'execution_provider'
|
|
4642
|
+
&& !(allowServiceControl && (key === 'service_id' || key === 'session_id')))) {
|
|
4600
4643
|
return null;
|
|
4601
4644
|
}
|
|
4602
4645
|
if (request.execution_provider === undefined || request.execution_provider === 'project_default') {
|
|
@@ -4655,8 +4698,17 @@ app.post('/bundles/:id/run', (req, res) => {
|
|
|
4655
4698
|
return res.status(409).json(activeRunConflict);
|
|
4656
4699
|
}
|
|
4657
4700
|
const profileSelection = createServiceExecutionProfileSelection(project.id, executionProvider);
|
|
4701
|
+
try {
|
|
4702
|
+
void runTicketInWorkspace(targetTicket, io, project, workspace.cwd, profileSelection)
|
|
4703
|
+
.catch(error => console.error('[run] admitted bundle service startup failed:', error));
|
|
4704
|
+
}
|
|
4705
|
+
catch (error) {
|
|
4706
|
+
return res.status(409).json({
|
|
4707
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4708
|
+
code: error instanceof Error && 'code' in error ? error.code : 'service_start_rejected',
|
|
4709
|
+
});
|
|
4710
|
+
}
|
|
4658
4711
|
emitRunStateForContext(targetTicket, true);
|
|
4659
|
-
runTicketInWorkspace(targetTicket, io, project, workspace.cwd, profileSelection);
|
|
4660
4712
|
return res.json(serializeBundleDetails(result.project, bundle));
|
|
4661
4713
|
});
|
|
4662
4714
|
app.post('/bundles/:id/stop', async (req, res) => {
|
|
@@ -4696,52 +4748,6 @@ app.post('/bundles/:id/follow-up', (req, res) => {
|
|
|
4696
4748
|
});
|
|
4697
4749
|
const AUTOMATION_SIDE_EFFECT_KINDS = new Set(['automation_commit', 'automation_push', 'automation_pull_request']);
|
|
4698
4750
|
const AUTOMATION_WORKER_PAUSE_KINDS = new Set(['build', 'review', 'build_follow_up', 'automation_review_fix', 'check_auto_fix', 'check_repair', 'check_batch']);
|
|
4699
|
-
app.post('/bundles/:id/continue-delivery', async (req, res) => {
|
|
4700
|
-
const result = getRequiredProject(req);
|
|
4701
|
-
if ('error' in result) {
|
|
4702
|
-
return res.status(400).json({ error: result.error });
|
|
4703
|
-
}
|
|
4704
|
-
const bundle = getBundle(req.params.id, result.project.id);
|
|
4705
|
-
if (!bundle) {
|
|
4706
|
-
return res.status(404).json({ error: 'Bundle not found' });
|
|
4707
|
-
}
|
|
4708
|
-
const state = getBundleAutomationState(result.project.id, bundle.id);
|
|
4709
|
-
const tickets = listDetailedBundleTickets(result.project.id, bundle.id);
|
|
4710
|
-
if (!state) {
|
|
4711
|
-
return res.status(409).json({ error: 'Bundle delivery is not ready to continue' });
|
|
4712
|
-
}
|
|
4713
|
-
const automationConfig = resolveAutomationConfig(bundle, result.project);
|
|
4714
|
-
const decision = resolveAutomationDeliveryDecision({
|
|
4715
|
-
projectId: result.project.id,
|
|
4716
|
-
bundleId: bundle.id,
|
|
4717
|
-
state,
|
|
4718
|
-
config: automationConfig,
|
|
4719
|
-
tickets,
|
|
4720
|
-
});
|
|
4721
|
-
if (!decision) {
|
|
4722
|
-
return res.status(409).json({ error: 'Bundle delivery is not ready to continue' });
|
|
4723
|
-
}
|
|
4724
|
-
const owner = tickets.find(ticket => ticket.id === decision.owner_ticket_id);
|
|
4725
|
-
const reviewStageRunId = state.automation_delivery_ready_stage_run_id;
|
|
4726
|
-
if (!owner || !reviewStageRunId) {
|
|
4727
|
-
return res.status(409).json({ error: 'Bundle delivery owner is no longer available' });
|
|
4728
|
-
}
|
|
4729
|
-
try {
|
|
4730
|
-
const progression = ensureAutomationProgression({
|
|
4731
|
-
projectId: result.project.id,
|
|
4732
|
-
bundleId: bundle.id,
|
|
4733
|
-
ticketId: owner.id,
|
|
4734
|
-
ticketTitle: owner.title,
|
|
4735
|
-
parentRunId: reviewStageRunId,
|
|
4736
|
-
broadcaster: io,
|
|
4737
|
-
});
|
|
4738
|
-
await runAutomationCommitChain(owner, result.project, owner.bundle ?? bundle, automationConfig, io, progression.runId, undefined, reviewStageRunId);
|
|
4739
|
-
}
|
|
4740
|
-
catch (error) {
|
|
4741
|
-
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
4742
|
-
}
|
|
4743
|
-
return res.json(serializeBundleDetails(result.project, bundle));
|
|
4744
|
-
});
|
|
4745
4751
|
app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
4746
4752
|
const result = getRequiredProject(req);
|
|
4747
4753
|
if ('error' in result) {
|
|
@@ -4781,7 +4787,7 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4781
4787
|
: 'Bundle automation cannot be resumed in its current state';
|
|
4782
4788
|
return res.status(409).json({ error, automation_resume_decision: initialDecision });
|
|
4783
4789
|
}
|
|
4784
|
-
|
|
4790
|
+
const decision = initialDecision;
|
|
4785
4791
|
const revalidateDecision = () => {
|
|
4786
4792
|
const current = resolveCurrentDecision();
|
|
4787
4793
|
return current?.status === 'enabled'
|
|
@@ -4997,9 +5003,21 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4997
5003
|
}
|
|
4998
5004
|
const automationConfig = resolveAutomationConfig(owner.bundle ?? null, project);
|
|
4999
5005
|
const legacyReviewRepairPause = isLegacyReviewRepairMigrationPause(result.project.id, bundle.id, progressionRunId);
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5006
|
+
const completedVerificationIteration = getActiveReviewRepairIteration(result.project.id, progressionRunId);
|
|
5007
|
+
if (completedVerificationIteration
|
|
5008
|
+
&& ['paused', 'verification_active'].includes(completedVerificationIteration.phase)
|
|
5009
|
+
&& completedVerificationIteration.stop_reason !== 'no_progress'
|
|
5010
|
+
&& completedVerificationIteration.resulting_review_execution_run_id
|
|
5011
|
+
&& getExecutionRun(result.project.id, completedVerificationIteration.resulting_review_execution_run_id)?.status === 'completed') {
|
|
5012
|
+
try {
|
|
5013
|
+
await recoverCompletedReviewRepairVerification(result.project.id, completedVerificationIteration.id, io, { resume: true });
|
|
5014
|
+
return res.json(serializeBundleDetails(result.project, bundle));
|
|
5015
|
+
}
|
|
5016
|
+
catch (error) {
|
|
5017
|
+
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
5018
|
+
}
|
|
5019
|
+
}
|
|
5020
|
+
if (completedVerificationIteration?.phase === 'paused' && completedVerificationIteration.stop_reason !== 'no_progress') {
|
|
5003
5021
|
withExecutionRunTransaction(transaction => {
|
|
5004
5022
|
appendReviewRepairIterationDecisionInTransaction(transaction, {
|
|
5005
5023
|
projectId: result.project.id,
|
|
@@ -5010,165 +5028,6 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
5010
5028
|
});
|
|
5011
5029
|
}, io, 'immediate');
|
|
5012
5030
|
}
|
|
5013
|
-
if (completedVerificationIteration?.phase === 'paused') {
|
|
5014
|
-
const verificationRunId = completedVerificationIteration.resulting_review_execution_run_id;
|
|
5015
|
-
const verificationRun = verificationRunId ? getExecutionRun(result.project.id, verificationRunId) : null;
|
|
5016
|
-
if (verificationRun?.status === 'completed') {
|
|
5017
|
-
try {
|
|
5018
|
-
completedVerificationIteration = resumePausedReviewRepairIteration({
|
|
5019
|
-
projectId: result.project.id,
|
|
5020
|
-
iterationId: completedVerificationIteration.id,
|
|
5021
|
-
expectedCurrentRunId: verificationRunId,
|
|
5022
|
-
broadcaster: io,
|
|
5023
|
-
});
|
|
5024
|
-
}
|
|
5025
|
-
catch (error) {
|
|
5026
|
-
const current = getReviewRepairIteration(result.project.id, completedVerificationIteration.id);
|
|
5027
|
-
if (current?.resulting_review_execution_run_id === verificationRunId
|
|
5028
|
-
&& ['verification_active', 'terminal'].includes(current.phase)) {
|
|
5029
|
-
return res.json(serializeBundleDetails(result.project, bundle));
|
|
5030
|
-
}
|
|
5031
|
-
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
5032
|
-
}
|
|
5033
|
-
}
|
|
5034
|
-
}
|
|
5035
|
-
if (completedVerificationIteration?.phase === 'verification_active'
|
|
5036
|
-
&& completedVerificationIteration.resulting_review_execution_run_id) {
|
|
5037
|
-
const verificationRunId = completedVerificationIteration.resulting_review_execution_run_id;
|
|
5038
|
-
const verificationRun = getExecutionRun(result.project.id, verificationRunId);
|
|
5039
|
-
const resultingReview = verificationRun?.status === 'completed'
|
|
5040
|
-
? db.prepare(`
|
|
5041
|
-
SELECT review.id
|
|
5042
|
-
FROM review_runs review
|
|
5043
|
-
JOIN execution_run_links result_link
|
|
5044
|
-
ON result_link.execution_run_id = ?
|
|
5045
|
-
AND result_link.project_id = review.project_id
|
|
5046
|
-
AND result_link.entity_type = 'review_run'
|
|
5047
|
-
AND result_link.entity_id = review.id
|
|
5048
|
-
AND result_link.relationship = 'result'
|
|
5049
|
-
WHERE review.project_id = ? AND review.ticket_id = ?
|
|
5050
|
-
AND review.worktree_bundle_id = ? AND review.status = 'completed'
|
|
5051
|
-
LIMIT 1
|
|
5052
|
-
`).get(verificationRunId, result.project.id, owner.id, bundle.id)
|
|
5053
|
-
: undefined;
|
|
5054
|
-
if (resultingReview) {
|
|
5055
|
-
reconciledSourceReviewRunId = resultingReview.id;
|
|
5056
|
-
const blockingCount = countBlockingFindings(listReviewFindings(resultingReview.id), automationConfig);
|
|
5057
|
-
const terminal = !isAutomationActive(automationConfig)
|
|
5058
|
-
? { outcome: 'interrupted', stopReason: 'configuration_changed' }
|
|
5059
|
-
: blockingCount === 0
|
|
5060
|
-
? { outcome: 'repaired', stopReason: 'no_blocking_findings' }
|
|
5061
|
-
: automationConfig.reviewFixEnabled
|
|
5062
|
-
? completedVerificationIteration.ordinal < automationConfig.reviewMaxIterations
|
|
5063
|
-
? { outcome: 'continuing', stopReason: null }
|
|
5064
|
-
: { outcome: 'exhausted', stopReason: 'max_iterations' }
|
|
5065
|
-
: { outcome: 'interrupted', stopReason: 'automation_disabled' };
|
|
5066
|
-
try {
|
|
5067
|
-
withExecutionRunTransaction(transaction => {
|
|
5068
|
-
if (!completedVerificationIteration.resulting_review_run_id) {
|
|
5069
|
-
attachReviewRepairResultInTransaction(transaction, {
|
|
5070
|
-
projectId: result.project.id,
|
|
5071
|
-
iterationId: completedVerificationIteration.id,
|
|
5072
|
-
expectedPhase: 'verification_active',
|
|
5073
|
-
expectedVerificationRunId: verificationRunId,
|
|
5074
|
-
resultingReviewRunId: resultingReview.id,
|
|
5075
|
-
});
|
|
5076
|
-
}
|
|
5077
|
-
appendReviewRepairIterationDecisionInTransaction(transaction, {
|
|
5078
|
-
projectId: result.project.id,
|
|
5079
|
-
iterationId: completedVerificationIteration.id,
|
|
5080
|
-
decisionPoint: 'verification_completion',
|
|
5081
|
-
effectiveAutomationConfig: automationConfig,
|
|
5082
|
-
decisionCode: 'verification_reconciled_on_resume',
|
|
5083
|
-
operationRunId: verificationRunId,
|
|
5084
|
-
});
|
|
5085
|
-
terminalizeReviewRepairIterationInTransaction(transaction, {
|
|
5086
|
-
projectId: result.project.id,
|
|
5087
|
-
iterationId: completedVerificationIteration.id,
|
|
5088
|
-
expectedPhase: 'verification_active',
|
|
5089
|
-
expectedCurrentRunId: verificationRunId,
|
|
5090
|
-
outcome: terminal.outcome,
|
|
5091
|
-
stopReason: terminal.stopReason,
|
|
5092
|
-
resultingBlockingCount: blockingCount,
|
|
5093
|
-
});
|
|
5094
|
-
if (automationConfig.depth === 'manual') {
|
|
5095
|
-
completeAutomationProgressionInTransaction(transaction, {
|
|
5096
|
-
projectId: result.project.id,
|
|
5097
|
-
bundleId: bundle.id,
|
|
5098
|
-
progressionRunId,
|
|
5099
|
-
domainStatus: 'manual_boundary_reached',
|
|
5100
|
-
});
|
|
5101
|
-
}
|
|
5102
|
-
if (terminal.outcome === 'continuing') {
|
|
5103
|
-
claimReviewRepairIterationInTransaction(transaction, {
|
|
5104
|
-
projectId: result.project.id,
|
|
5105
|
-
worktreeBundleId: bundle.id,
|
|
5106
|
-
ownerTicketId: owner.id,
|
|
5107
|
-
automationProgressionRunId: progressionRunId,
|
|
5108
|
-
triggeringReviewExecutionRunId: verificationRunId,
|
|
5109
|
-
triggeringReviewRunId: resultingReview.id,
|
|
5110
|
-
triggeringBlockingCount: blockingCount,
|
|
5111
|
-
effectiveAutomationConfig: automationConfig,
|
|
5112
|
-
decisionCode: 'resume_verification_repair_admitted',
|
|
5113
|
-
});
|
|
5114
|
-
}
|
|
5115
|
-
}, io, 'immediate');
|
|
5116
|
-
}
|
|
5117
|
-
catch (error) {
|
|
5118
|
-
const current = getReviewRepairIteration(result.project.id, completedVerificationIteration.id);
|
|
5119
|
-
if (current?.phase === 'terminal'
|
|
5120
|
-
&& current.resulting_review_execution_run_id === verificationRunId
|
|
5121
|
-
&& current.resulting_review_run_id === resultingReview.id) {
|
|
5122
|
-
return res.json(serializeBundleDetails(result.project, bundle));
|
|
5123
|
-
}
|
|
5124
|
-
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
5125
|
-
}
|
|
5126
|
-
if (terminal.outcome === 'continuing') {
|
|
5127
|
-
const continuationDecision = resolveCurrentDecision();
|
|
5128
|
-
if (!continuationDecision || continuationDecision.status !== 'enabled') {
|
|
5129
|
-
return res.status(409).json({ error: 'Bundle automation state changed before repair continuation could start' });
|
|
5130
|
-
}
|
|
5131
|
-
decision = continuationDecision;
|
|
5132
|
-
}
|
|
5133
|
-
else if (terminal.outcome === 'repaired') {
|
|
5134
|
-
resetBundleAutomationLoop(result.project.id, bundle.id);
|
|
5135
|
-
runAutomationProgression({
|
|
5136
|
-
projectId: result.project.id,
|
|
5137
|
-
bundleId: bundle.id,
|
|
5138
|
-
progressionRunId,
|
|
5139
|
-
broadcaster: io,
|
|
5140
|
-
});
|
|
5141
|
-
if (depthAtLeast(automationConfig.depth, 'auto_commit')) {
|
|
5142
|
-
await runAutomationCommitChain(owner, project, owner.bundle ?? null, automationConfig, io, progressionRunId, undefined, verificationRunId);
|
|
5143
|
-
}
|
|
5144
|
-
else {
|
|
5145
|
-
completeAutomationProgression({
|
|
5146
|
-
projectId: result.project.id,
|
|
5147
|
-
bundleId: bundle.id,
|
|
5148
|
-
progressionRunId,
|
|
5149
|
-
broadcaster: io,
|
|
5150
|
-
});
|
|
5151
|
-
}
|
|
5152
|
-
return res.json(serializeBundleDetails(result.project, bundle));
|
|
5153
|
-
}
|
|
5154
|
-
else if (automationConfig.depth !== 'manual') {
|
|
5155
|
-
waitAutomationProgression({
|
|
5156
|
-
projectId: result.project.id,
|
|
5157
|
-
bundleId: bundle.id,
|
|
5158
|
-
progressionRunId,
|
|
5159
|
-
pauseCode: 'review',
|
|
5160
|
-
pauseReason: 'Blocking Review findings remain after repair verification.',
|
|
5161
|
-
reasonCode: 'precondition_failed',
|
|
5162
|
-
pausedStageRunId: verificationRunId,
|
|
5163
|
-
broadcaster: io,
|
|
5164
|
-
});
|
|
5165
|
-
return res.json(serializeBundleDetails(result.project, bundle));
|
|
5166
|
-
}
|
|
5167
|
-
else {
|
|
5168
|
-
return res.json(serializeBundleDetails(result.project, bundle));
|
|
5169
|
-
}
|
|
5170
|
-
}
|
|
5171
|
-
}
|
|
5172
5031
|
const restoreWaitingProgression = () => {
|
|
5173
5032
|
waitAutomationProgression({
|
|
5174
5033
|
projectId: result.project.id,
|
|
@@ -5361,7 +5220,16 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
5361
5220
|
}
|
|
5362
5221
|
const workflowContext = validateWorkflowContext(progression.metadata);
|
|
5363
5222
|
let existingIteration = getActiveReviewRepairIteration(result.project.id, progressionRunId);
|
|
5364
|
-
const
|
|
5223
|
+
const overridingNoProgress = existingIteration?.phase === 'paused' && existingIteration.stop_reason === 'no_progress';
|
|
5224
|
+
if (overridingNoProgress) {
|
|
5225
|
+
existingIteration = withExecutionRunTransaction(transaction => {
|
|
5226
|
+
if (!revalidateDecision()) {
|
|
5227
|
+
throw new Error('Bundle automation state changed before Resume override could start');
|
|
5228
|
+
}
|
|
5229
|
+
return overrideNoProgressReviewRepairInTransaction(transaction, result.project.id, existingIteration.id, io);
|
|
5230
|
+
}, io, 'immediate');
|
|
5231
|
+
}
|
|
5232
|
+
const sourceReviewRunId = existingIteration?.triggering_review_run_id ?? null;
|
|
5365
5233
|
if (!sourceReviewRunId) {
|
|
5366
5234
|
throw new Error('The completed review findings to resume are unavailable');
|
|
5367
5235
|
}
|
|
@@ -5370,12 +5238,14 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
5370
5238
|
review_finding_run_id: sourceReviewRunId,
|
|
5371
5239
|
review_severity: automationConfig.reviewSeverity,
|
|
5372
5240
|
});
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5241
|
+
if (!overridingNoProgress) {
|
|
5242
|
+
runAutomationProgression({
|
|
5243
|
+
projectId: result.project.id,
|
|
5244
|
+
bundleId: bundle.id,
|
|
5245
|
+
progressionRunId,
|
|
5246
|
+
broadcaster: io,
|
|
5247
|
+
});
|
|
5248
|
+
}
|
|
5379
5249
|
if (existingIteration?.phase === 'paused') {
|
|
5380
5250
|
existingIteration = resumePausedReviewRepairIteration({
|
|
5381
5251
|
projectId: result.project.id,
|
|
@@ -5646,9 +5516,7 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
5646
5516
|
});
|
|
5647
5517
|
}, io, 'immediate');
|
|
5648
5518
|
}
|
|
5649
|
-
|
|
5650
|
-
resetBundleAutomationLoop(result.project.id, bundle.id);
|
|
5651
|
-
}
|
|
5519
|
+
resetBundleAutomationLoop(result.project.id, bundle.id);
|
|
5652
5520
|
}
|
|
5653
5521
|
runAutomationProgression({
|
|
5654
5522
|
projectId: result.project.id,
|
|
@@ -7677,7 +7545,7 @@ app.post('/projects/:id/memory/suggestions/:suggestionId/supersede', (req, res)
|
|
|
7677
7545
|
}
|
|
7678
7546
|
return res.json(superseded);
|
|
7679
7547
|
});
|
|
7680
|
-
app.get('/projects/:id/branch', (req, res) => {
|
|
7548
|
+
app.get('/projects/:id/branch', async (req, res) => {
|
|
7681
7549
|
const project = getProjectById(req.params.id);
|
|
7682
7550
|
if (!project) {
|
|
7683
7551
|
return res.status(404).json({ error: 'Not found' });
|
|
@@ -7685,7 +7553,7 @@ app.get('/projects/:id/branch', (req, res) => {
|
|
|
7685
7553
|
ensureProjectRootBundle(project.id);
|
|
7686
7554
|
try {
|
|
7687
7555
|
const refreshRemote = req.query.refresh_remote === 'true';
|
|
7688
|
-
return res.json(getProjectBranchStateForFooter(project, { refreshRemote }));
|
|
7556
|
+
return res.json(await getProjectBranchStateForFooter(project, { refreshRemote }));
|
|
7689
7557
|
}
|
|
7690
7558
|
catch (error) {
|
|
7691
7559
|
console.error(`Failed to read branch for project ${project.id}:`, error);
|
|
@@ -8197,14 +8065,6 @@ app.patch('/projects/:id', async (req, res) => {
|
|
|
8197
8065
|
if ('error' in roleModelValidation) {
|
|
8198
8066
|
return res.status(400).json({ error: roleModelValidation.error });
|
|
8199
8067
|
}
|
|
8200
|
-
if (hasOwn(validation.values, 'repo_path') && validation.values.repo_path !== project.repo_path) {
|
|
8201
|
-
const stats = getProjectTicketStats(project.id);
|
|
8202
|
-
if (stats.ticketCount > 0 || stats.worktreeCount > 0 || stats.bundleCount > 0) {
|
|
8203
|
-
return res.status(409).json({
|
|
8204
|
-
error: 'repo_path cannot be changed after tickets, bundles, or worktrees exist for this project',
|
|
8205
|
-
});
|
|
8206
|
-
}
|
|
8207
|
-
}
|
|
8208
8068
|
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, helper_models_json, reviewer_model, reviewer_variant, reviewer_models_json, 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;
|
|
8209
8069
|
const updateProject = db.prepare(`
|
|
8210
8070
|
UPDATE projects SET
|
|
@@ -8262,7 +8122,7 @@ app.patch('/projects/:id', async (req, res) => {
|
|
|
8262
8122
|
const nextReviewerVariant = nextReviewerModel
|
|
8263
8123
|
? (hasOwn(validation.values, 'reviewer_variant') ? reviewer_variant ?? null : project.reviewer_variant)
|
|
8264
8124
|
: null;
|
|
8265
|
-
const
|
|
8125
|
+
const applyUpdate = db.transaction(() => {
|
|
8266
8126
|
const currentProject = getProjectById(project.id);
|
|
8267
8127
|
if (!currentProject || JSON.stringify(currentProject) !== JSON.stringify(project)) {
|
|
8268
8128
|
return {
|
|
@@ -8270,6 +8130,15 @@ app.patch('/projects/:id', async (req, res) => {
|
|
|
8270
8130
|
status: 409,
|
|
8271
8131
|
};
|
|
8272
8132
|
}
|
|
8133
|
+
if (hasOwn(validation.values, 'repo_path') && repo_path !== currentProject.repo_path) {
|
|
8134
|
+
if (currentProject.deletion_started_at || hasActiveProjectServiceRun(project.id) || getActiveRunStatusForProject(project.id)) {
|
|
8135
|
+
return { error: 'Cannot change project repository while service authority or deletion is active', status: 409 };
|
|
8136
|
+
}
|
|
8137
|
+
const stats = getProjectTicketStats(project.id);
|
|
8138
|
+
if (stats.ticketCount > 0 || stats.worktreeCount > 0 || stats.bundleCount > 0) {
|
|
8139
|
+
return { error: 'repo_path cannot be changed after tickets, bundles, or worktrees exist for this project', status: 409 };
|
|
8140
|
+
}
|
|
8141
|
+
}
|
|
8273
8142
|
const policyReferences = validateProjectRequiredCheckPolicyReferences({
|
|
8274
8143
|
projectId: project.id,
|
|
8275
8144
|
checkDefinitionsJson: nextCheckDefinitions,
|
|
@@ -8301,7 +8170,17 @@ app.patch('/projects/:id', async (req, res) => {
|
|
|
8301
8170
|
replaceProjectLinks(project.id, linked_project_ids ?? []);
|
|
8302
8171
|
}
|
|
8303
8172
|
return { ok: true };
|
|
8304
|
-
})
|
|
8173
|
+
});
|
|
8174
|
+
let updateResult;
|
|
8175
|
+
try {
|
|
8176
|
+
updateResult = applyUpdate.immediate();
|
|
8177
|
+
}
|
|
8178
|
+
catch (error) {
|
|
8179
|
+
if (error instanceof Error && 'code' in error && error.code === 'SQLITE_CONSTRAINT_TRIGGER') {
|
|
8180
|
+
return res.status(409).json({ error: error.message });
|
|
8181
|
+
}
|
|
8182
|
+
throw error;
|
|
8183
|
+
}
|
|
8305
8184
|
if ('error' in updateResult) {
|
|
8306
8185
|
const status = 'status' in updateResult && typeof updateResult.status === 'number'
|
|
8307
8186
|
? updateResult.status
|
|
@@ -8312,6 +8191,9 @@ app.patch('/projects/:id', async (req, res) => {
|
|
|
8312
8191
|
res.json(serializeProject(updated ?? null));
|
|
8313
8192
|
});
|
|
8314
8193
|
const assertProjectDeletionEligible = (projectId, claimId) => {
|
|
8194
|
+
if (hasActiveProjectServiceRun(projectId) || getActiveRunStatusForProject(projectId)) {
|
|
8195
|
+
throw new ProjectDeletionError(409, 'Cannot delete a project while services are active');
|
|
8196
|
+
}
|
|
8315
8197
|
const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(projectId);
|
|
8316
8198
|
if (!project) {
|
|
8317
8199
|
throw new ProjectDeletionError(404, 'Not found');
|
|
@@ -8359,6 +8241,7 @@ const assertProjectDeletionEligible = (projectId, claimId) => {
|
|
|
8359
8241
|
}
|
|
8360
8242
|
};
|
|
8361
8243
|
const completeClaimedProjectDeletion = async (projectId, claimId) => {
|
|
8244
|
+
assertProjectDeletionEligible(projectId, claimId);
|
|
8362
8245
|
const marked = db.prepare(`
|
|
8363
8246
|
UPDATE projects
|
|
8364
8247
|
SET deletion_cleanup_started_at = COALESCE(deletion_cleanup_started_at, CURRENT_TIMESTAMP)
|
|
@@ -8595,6 +8478,17 @@ app.patch('/tickets/:id', async (req, res) => {
|
|
|
8595
8478
|
const nextProjectId = hasProjectField ? project_id : ticket.project_id;
|
|
8596
8479
|
const isProjectChanging = nextProjectId !== ticket.project_id;
|
|
8597
8480
|
const hasStandaloneWorktree = typeof ticket.branch === 'string' && ticket.branch.trim() !== '';
|
|
8481
|
+
if (isProjectChanging || (hasBundleField && req.body.worktree_bundle_id !== ticket.worktree_bundle_id)) {
|
|
8482
|
+
try {
|
|
8483
|
+
assertTicketServiceTargetMutable(ticket.id);
|
|
8484
|
+
}
|
|
8485
|
+
catch (error) {
|
|
8486
|
+
if (error instanceof HttpError) {
|
|
8487
|
+
return res.status(error.status).json({ error: error.message });
|
|
8488
|
+
}
|
|
8489
|
+
throw error;
|
|
8490
|
+
}
|
|
8491
|
+
}
|
|
8598
8492
|
if (isProjectChanging && hasStandaloneWorktree) {
|
|
8599
8493
|
return res.status(409).json({ error: 'project_id cannot be changed after a worktree has been created' });
|
|
8600
8494
|
}
|
|
@@ -8704,10 +8598,23 @@ app.patch('/tickets/:id', async (req, res) => {
|
|
|
8704
8598
|
}
|
|
8705
8599
|
}
|
|
8706
8600
|
}
|
|
8707
|
-
|
|
8708
|
-
|
|
8601
|
+
try {
|
|
8602
|
+
db.transaction(() => {
|
|
8603
|
+
if (bundleChanged) {
|
|
8604
|
+
assertTicketServiceTargetMutable(ticket.id);
|
|
8605
|
+
}
|
|
8606
|
+
if (isProjectChanging) {
|
|
8607
|
+
deleteTicketDependenciesForTicket(ticket.id);
|
|
8608
|
+
}
|
|
8609
|
+
db.prepare('UPDATE tickets SET title = ?, description = ?, model = ?, variant = ?, skills_json = ?, worktree_bundle_id = ?, project_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(title?.trim() ?? ticket.title, description ?? ticket.description, resolvedModel, resolvedVariant, JSON.stringify(resolvedSkills), resolvedBundleId, nextProjectId, req.params.id);
|
|
8610
|
+
}).immediate();
|
|
8611
|
+
}
|
|
8612
|
+
catch (error) {
|
|
8613
|
+
if (error instanceof HttpError) {
|
|
8614
|
+
return res.status(error.status).json({ error: error.message });
|
|
8615
|
+
}
|
|
8616
|
+
throw error;
|
|
8709
8617
|
}
|
|
8710
|
-
db.prepare('UPDATE tickets SET title = ?, description = ?, model = ?, variant = ?, skills_json = ?, worktree_bundle_id = ?, project_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(title?.trim() ?? ticket.title, description ?? ticket.description, resolvedModel, resolvedVariant, JSON.stringify(resolvedSkills), resolvedBundleId, nextProjectId, req.params.id);
|
|
8711
8618
|
upsertTicketDescriptionMessage(req.params.id, description ?? ticket.description);
|
|
8712
8619
|
const updated = getTicket(req.params.id);
|
|
8713
8620
|
if (bundleChanged && previousBundleId && previousProject) {
|
|
@@ -9129,7 +9036,12 @@ app.post('/tickets/:id/undo', (req, res) => {
|
|
|
9129
9036
|
return res.json(result.ticket);
|
|
9130
9037
|
});
|
|
9131
9038
|
app.post('/tickets/:id/run', (req, res) => {
|
|
9039
|
+
const control = parseServiceRunControl(req.body);
|
|
9040
|
+
if ('error' in control) {
|
|
9041
|
+
return res.status(400).json({ error: control.error });
|
|
9042
|
+
}
|
|
9132
9043
|
const result = runTicketRun({
|
|
9044
|
+
...control,
|
|
9133
9045
|
ticketId: req.params.id,
|
|
9134
9046
|
requestedProjectId: getRequestedProjectId(req),
|
|
9135
9047
|
broadcaster: io,
|
|
@@ -9390,11 +9302,16 @@ app.post('/projects/:id/open-ide', (req, res) => {
|
|
|
9390
9302
|
return res.json({ ok: true });
|
|
9391
9303
|
});
|
|
9392
9304
|
app.post('/projects/:id/run', (req, res) => {
|
|
9393
|
-
const
|
|
9305
|
+
const control = parseServiceRunControl(req.body);
|
|
9306
|
+
if ('error' in control) {
|
|
9307
|
+
return res.status(400).json({ error: control.error });
|
|
9308
|
+
}
|
|
9309
|
+
const executionProvider = parseServiceExecutionProviderChoice(req.body, true);
|
|
9394
9310
|
if (!executionProvider) {
|
|
9395
9311
|
return res.status(400).json({ error: 'execution_provider must be project_default or host' });
|
|
9396
9312
|
}
|
|
9397
9313
|
const result = runProjectTarget({
|
|
9314
|
+
...control,
|
|
9398
9315
|
projectId: req.params.id,
|
|
9399
9316
|
executionProvider,
|
|
9400
9317
|
});
|
|
@@ -9879,7 +9796,7 @@ app.get('/projects/:id/execution-timeline/:runId', async (req, res) => {
|
|
|
9879
9796
|
if (scope.scopeEntity && !links.some(link => link.entity_type === scope.scopeEntity?.type && link.entity_id === scope.scopeEntity.id)) {
|
|
9880
9797
|
return res.status(404).json({ error: 'Execution run not found' });
|
|
9881
9798
|
}
|
|
9882
|
-
return res.json(await buildExecutionRunDetailResponse(project, run));
|
|
9799
|
+
return res.json(await buildExecutionRunDetailResponse(project, run, scope.scopeEntity));
|
|
9883
9800
|
});
|
|
9884
9801
|
app.get('/projects/:id/execution-runs/:runId', async (req, res) => {
|
|
9885
9802
|
const project = getProjectById(req.params.id);
|
|
@@ -10127,7 +10044,12 @@ app.post('/terminals/:sessionId/destroy', (req, res) => {
|
|
|
10127
10044
|
return res.status(204).send();
|
|
10128
10045
|
});
|
|
10129
10046
|
app.post('/tickets/:id/stop', async (req, res) => {
|
|
10047
|
+
const control = parseServiceRunControl(req.body);
|
|
10048
|
+
if ('error' in control) {
|
|
10049
|
+
return res.status(400).json({ error: control.error });
|
|
10050
|
+
}
|
|
10130
10051
|
const result = await stopTicketRun({
|
|
10052
|
+
...control,
|
|
10131
10053
|
ticketId: req.params.id,
|
|
10132
10054
|
requestedProjectId: getRequestedProjectId(req),
|
|
10133
10055
|
broadcaster: io,
|
|
@@ -10138,7 +10060,12 @@ app.post('/tickets/:id/stop', async (req, res) => {
|
|
|
10138
10060
|
return res.status(result.status).json(result.value);
|
|
10139
10061
|
});
|
|
10140
10062
|
app.post('/projects/:id/stop-run', async (req, res) => {
|
|
10063
|
+
const control = parseServiceRunControl(req.body);
|
|
10064
|
+
if ('error' in control) {
|
|
10065
|
+
return res.status(400).json({ error: control.error });
|
|
10066
|
+
}
|
|
10141
10067
|
const result = await stopProjectTargetRun({
|
|
10068
|
+
...control,
|
|
10142
10069
|
projectId: req.params.id,
|
|
10143
10070
|
});
|
|
10144
10071
|
if (!result.ok) {
|