@straiffi/archon 1.7.10 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/{ActivityView-IUWr92uj.js → ActivityView-CbdDNsdO.js} +1 -1
- package/dist/client/assets/{GitHubActionsDialog-0YtWvbFk.js → GitHubActionsDialog-Cmb9Yebr.js} +1 -1
- package/dist/client/assets/VerificationDialog-YUi8g2ou.js +4 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-LLMyOR-6.js → architectureDiagram-3BPJPVTR-BgoQ_BIc.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-aa1HitBJ.js → blockDiagram-GPEHLZMM-Xdp2x8BW.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-BpSt5sdj.js → c4Diagram-AAUBKEIU-B220zS5a.js} +1 -1
- package/dist/client/assets/channel-CZ1HsfsD.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-D8pf3RNr.js → chunk-2J33WTMH-tYM2mjFx.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-_yx1N2iT.js → chunk-3OPIFGDE-CTITOUvV.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-CYURGSbL.js → chunk-5ZQYHXKU-D45jJDNN.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-Ci2Mu1zz.js → chunk-727SXJPM-X90EdJX4.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-CqVgrKze.js → chunk-AQP2D5EJ-PEoVUnQP.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-CT8aPk-o.js → chunk-CSCIHK7Q-CLWG2R5F.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-yM1i0J-p.js → chunk-KSCS5N6A-BamDWXh_.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-BKMGr31Z.js → chunk-L5ZTLDWV-05TaQDcG.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-A7oUDETP.js → chunk-LZXEDZCA-CjsbWN3c.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-Cagc-XAG.js → chunk-ND2GUHAM-BVmPBEeD.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-pe8tKLWI.js → chunk-NZK2D7GU-DNZ6WouC.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-BahwghWW.js → chunk-O5CBEL6O-Dfonikgz.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-BfSAFufr.js → chunk-WU5MYG2G-CyiTxcGh.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-D4DcLL7k.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Benwuous.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-DCma6-hS.js → dagre-BM42HDAG-9jv2zUKk.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-DUeggZrO.js → diagram-2AECGRRQ-DmF0Td45.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-CdjOKGtl.js → diagram-5GNKFQAL-DJNMQ7tI.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-CYjXhgh_.js → diagram-KO2AKTUF-EfcoL9E4.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DYBCh4cm.js → diagram-LMA3HP47-C77Lzbhk.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-D3HPQt3z.js → diagram-OG6HWLK6-7GPyDhpX.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-mG8Yztg-.js → erDiagram-TEJ5UH35-B-mOpWb2.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-4FvjuoSZ.js → flowDiagram-I6XJVG4X-Bbte_qNo.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-ByCQI8ft.js → ganttDiagram-6RSMTGT7-DC4ltCss.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CzEUA7nB.js → gitGraphDiagram-PVQCEYII-4F87Sxzr.js} +1 -1
- package/dist/client/assets/index-D4qr0sQx.css +2 -0
- package/dist/client/assets/index-DCCSprul.js +274 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-Q9CqnXO1.js → infoDiagram-5YYISTIA-CHQaSUgi.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CO8BToGf.js → ishikawaDiagram-YF4QCWOH-CuXLE_L1.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-DtOyWvDJ.js → journeyDiagram-JHISSGLW-Dkj3B7DF.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-DIn9bYpk.js → kanban-definition-UN3LZRKU-DAH_WodG.js} +1 -1
- package/dist/client/assets/{line-H-k0M9_s.js → line-CxewTz3a.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-CZOUS5zK.js → mermaid-parser.core-W88fOlNl.js} +1 -1
- package/dist/client/assets/{mermaid.core-Ck71Qr7h.js → mermaid.core-CjJzzlTK.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-BRFEcJw_.js → mindmap-definition-RKZ34NQL-BdCMvjnB.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-CwihXXcJ.js → pieDiagram-4H26LBE5-HeTRmhyt.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-moiRw4Cm.js → quadrantDiagram-W4KKPZXB-CLXv5YTL.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-tshvM_vV.js → requirementDiagram-4Y6WPE33-DB4DS6CS.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-rtKIuBEp.js → sankeyDiagram-5OEKKPKP-BjUVvwEU.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-lkTCo4wE.js → sequenceDiagram-3UESZ5HK-FH39XKuz.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CFQhVUSd.js → stateDiagram-AJRCARHV-U8DIBjJj.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Cw8SkdDx.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-BmlQ7Wiy.js → timeline-definition-PNZ67QCA-cuO_PRfp.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-CZyZepcT.js → vennDiagram-CIIHVFJN-BR7Vk6hi.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-C2b52gni.js → wardleyDiagram-YWT4CUSO-C443pGVk.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-FqnROUHq.js → xychartDiagram-2RQKCTM6-BtjwBwmu.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +503 -9
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +374 -201
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/activeAutomationChild.js +6 -1
- package/dist/server/lib/activeAutomationChild.js.map +1 -1
- package/dist/server/lib/automationCheckGate.js +183 -50
- package/dist/server/lib/automationCheckGate.js.map +1 -1
- package/dist/server/lib/automationProgression.js +76 -9
- package/dist/server/lib/automationProgression.js.map +1 -1
- package/dist/server/lib/automationReconciliation.js +65 -0
- package/dist/server/lib/automationReconciliation.js.map +1 -1
- package/dist/server/lib/automationResumeDecision.js +69 -32
- package/dist/server/lib/automationResumeDecision.js.map +1 -1
- package/dist/server/lib/automationReviewGate.js +6 -1
- package/dist/server/lib/automationReviewGate.js.map +1 -1
- package/dist/server/lib/bundleActivity.js +20 -0
- package/dist/server/lib/bundleActivity.js.map +1 -1
- package/dist/server/lib/bundleReviewOperations.js +36 -5
- package/dist/server/lib/bundleReviewOperations.js.map +1 -1
- package/dist/server/lib/chatOperations.js +9 -0
- package/dist/server/lib/chatOperations.js.map +1 -1
- package/dist/server/lib/chatTicketProposals.js +2 -1
- package/dist/server/lib/chatTicketProposals.js.map +1 -1
- package/dist/server/lib/chats.js +2 -0
- package/dist/server/lib/chats.js.map +1 -1
- package/dist/server/lib/checkDiagnosticParsers.js +301 -0
- package/dist/server/lib/checkDiagnosticParsers.js.map +1 -0
- package/dist/server/lib/checkDiagnostics.js +142 -0
- package/dist/server/lib/checkDiagnostics.js.map +1 -0
- package/dist/server/lib/checkGateEvaluations.js +89 -1
- package/dist/server/lib/checkGateEvaluations.js.map +1 -1
- package/dist/server/lib/checkRepair.js +1031 -0
- package/dist/server/lib/checkRepair.js.map +1 -0
- package/dist/server/lib/checkRepairLifecycle.js +56 -0
- package/dist/server/lib/checkRepairLifecycle.js.map +1 -0
- package/dist/server/lib/checkRepairReconciliation.js +145 -0
- package/dist/server/lib/checkRepairReconciliation.js.map +1 -0
- package/dist/server/lib/checkRunner.js +122 -23
- package/dist/server/lib/checkRunner.js.map +1 -1
- package/dist/server/lib/checkRuns.js +60 -6
- package/dist/server/lib/checkRuns.js.map +1 -1
- package/dist/server/lib/executionReconciliation.js +21 -11
- package/dist/server/lib/executionReconciliation.js.map +1 -1
- package/dist/server/lib/executionRetryPredicates.js +3 -1
- package/dist/server/lib/executionRetryPredicates.js.map +1 -1
- package/dist/server/lib/executionRunRetention.js +24 -3
- package/dist/server/lib/executionRunRetention.js.map +1 -1
- package/dist/server/lib/executionRuns.js +15 -3
- package/dist/server/lib/executionRuns.js.map +1 -1
- package/dist/server/lib/git.js +57 -7
- package/dist/server/lib/git.js.map +1 -1
- package/dist/server/lib/planModeQuestions.js +2 -0
- package/dist/server/lib/planModeQuestions.js.map +1 -1
- package/dist/server/lib/planQuestions.js +39 -7
- package/dist/server/lib/planQuestions.js.map +1 -1
- package/dist/server/lib/postBuildCheckRuns.js +43 -0
- package/dist/server/lib/postBuildCheckRuns.js.map +1 -0
- package/dist/server/lib/postBuildChecks.js +122 -0
- package/dist/server/lib/postBuildChecks.js.map +1 -0
- package/dist/server/lib/projectMemorySuggestions.js +2 -1
- package/dist/server/lib/projectMemorySuggestions.js.map +1 -1
- package/dist/server/lib/projects.js +24 -25
- package/dist/server/lib/projects.js.map +1 -1
- package/dist/server/lib/pullRequestContent.js +14 -0
- package/dist/server/lib/pullRequestContent.js.map +1 -1
- package/dist/server/lib/requiredCheckPolicy.js +13 -49
- package/dist/server/lib/requiredCheckPolicy.js.map +1 -1
- package/dist/server/lib/requiredCheckPolicyPrompt.js +46 -0
- package/dist/server/lib/requiredCheckPolicyPrompt.js.map +1 -0
- package/dist/server/lib/specDraft.js +6 -0
- package/dist/server/lib/specDraft.js.map +1 -1
- package/dist/server/lib/specResources.js +42 -0
- package/dist/server/lib/specResources.js.map +1 -0
- package/dist/server/lib/specs.js +9 -5
- package/dist/server/lib/specs.js.map +1 -1
- package/dist/server/lib/ticketAutomation.js +10 -3
- package/dist/server/lib/ticketAutomation.js.map +1 -1
- package/dist/server/lib/ticketAutomationCommit.js +36 -11
- package/dist/server/lib/ticketAutomationCommit.js.map +1 -1
- package/dist/server/lib/ticketFollowUpOperations.js +33 -9
- package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
- package/dist/server/lib/tickets.js +11 -32
- package/dist/server/lib/tickets.js.map +1 -1
- package/dist/server/lib/types.js +1 -0
- package/dist/server/lib/types.js.map +1 -1
- package/dist/server/workers/build.js +42 -12
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/chat.js +18 -3
- package/dist/server/workers/chat.js.map +1 -1
- package/dist/server/workers/followUp.js +70 -17
- package/dist/server/workers/followUp.js.map +1 -1
- package/dist/server/workers/plan.js +5 -3
- package/dist/server/workers/plan.js.map +1 -1
- package/dist/server/workers/planFollowUp.js +5 -6
- package/dist/server/workers/planFollowUp.js.map +1 -1
- package/dist/server/workers/review.js +38 -6
- package/dist/server/workers/review.js.map +1 -1
- package/dist/server/workers/specSession.js +10 -4
- package/dist/server/workers/specSession.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/VerificationDialog-B4Pyz1LV.js +0 -4
- package/dist/client/assets/channel-DWPRC-jg.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DBZaZLLc.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B7HsVFoJ.js +0 -1
- package/dist/client/assets/index-D-miHTTb.js +0 -273
- package/dist/client/assets/index-TAQIlNdJ.css +0 -2
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-rrN4DgWp.js +0 -1
package/dist/server/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { resolveChatSessionTarget } from './lib/chatTargets.js';
|
|
|
18
18
|
import { createChatMessage, getChatMessage, updateChatMessageContent } from './lib/chatMessages.js';
|
|
19
19
|
import { attachChatTicketProposalsToMessageContent } from './lib/chatTicketProposals.js';
|
|
20
20
|
import { createCorsOriginResolver, parseCorsOrigins } from './lib/cors.js';
|
|
21
|
-
import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffData, getGitDiffFiles, getGitStatus, getGitUnifiedDiff, getLocalProjectBranch, getProjectBranch, getProjectBranchSuggestions, getSelectedGitUnifiedDiff, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd } from './lib/git.js';
|
|
21
|
+
import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffData, getGitDiffFiles, getGitStatus, getGitUnifiedDiff, getLocalProjectBranch, getProjectBranch, getProjectBranchSuggestions, getSelectedGitUnifiedDiff, getUnpushedCommitDiffForCwd, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd, UnpushedCommitDiffSelectionError } from './lib/git.js';
|
|
22
22
|
import config from './lib/config.js';
|
|
23
23
|
import { autoConfigProject, ProjectAutoConfigError } from './lib/projectAutoConfig.js';
|
|
24
24
|
import { analyzeRepoPath, createInitialCommit, ensureInitialCommitForWorktrees, initializeGitRepo, isInitialCommitRequiredError, } from './lib/projectRepos.js';
|
|
@@ -31,7 +31,7 @@ import { runLightweightPrompt } from './lib/lightweightPrompt.js';
|
|
|
31
31
|
import { advanceBuildChain, startBuildChain, stopBuildChain } from './lib/buildChains.js';
|
|
32
32
|
import { activateQueuedBuildSequenceTicket, compensateBuildSequenceAcceptance, enqueueBundledBuild, getDependencyOrderedBundleTicketIds, getBuildSequenceIdForTicket, hasActiveSequenceForBundle, removeTicketFromBuildSequence, stopBuildSequence, } from './lib/buildSequences.js';
|
|
33
33
|
import { prepareTicketForBuild } from './lib/buildFlow.js';
|
|
34
|
-
import { getBundlePlanMutationBlockerById, shouldBlockPlanForBundleMutationById, } from './lib/bundleActivity.js';
|
|
34
|
+
import { getActiveBundleRepairBlocker, getBundlePlanMutationBlockerById, shouldBlockPlanForBundleMutationById, } from './lib/bundleActivity.js';
|
|
35
35
|
import { getDiscoveredModels, normalizeModelId, normalizeTicketModelId } from './lib/models.js';
|
|
36
36
|
import { upgradeTool, UpgradeInProgressError } from './lib/toolUpgrade.js';
|
|
37
37
|
import { acceptProjectMemorySuggestion, applyProjectMemorySuggestionToDecision, applyProjectMemorySuggestionToConvention, archiveProjectConvention, archiveProjectDecision, createProjectConvention, createProjectDecision, dismissProjectMemorySuggestion, getProjectMemoryById, getProjectMemorySuggestionById, isProjectMemoryEnabled, listProjectConventions, listLatestSuccessfulProjectContextArtifacts, listProjectDecisions, listProjectMemories, listProjectMemorySuggestions, restoreProjectConvention, restoreProjectDecision, serializeProjectMemoryFromConventionValue, serializeProjectMemoryFromDecisionValue, serializeProjectMemoryFromSuggestionValue, supersedeProjectDecision, supersedeProjectDecisionFromSuggestion, updateProjectConvention, updateProjectDecision, validateProjectConventionPayload, validateProjectDecisionPayload, } from './lib/projectMemory.js';
|
|
@@ -47,7 +47,8 @@ import { exportProjectMemoryArchive, importProjectMemoryArchive } from './lib/pr
|
|
|
47
47
|
import { buildReviewFindingsResponse, computeDiffSignature, getLatestReviewRunForContext, listReviewFindings } from './lib/reviewFindings.js';
|
|
48
48
|
import { formatBuildOpenQuestionAnswersPrompt, getLatestBuildOpenQuestionBatchStatus, hasActiveBuildOpenQuestionBatch, listBuildOpenQuestionsForBundle, loadBuildOpenQuestionsByBundleIds, } from './lib/buildOpenQuestions.js';
|
|
49
49
|
import { acceptBuildOpenQuestionResolution } from './lib/buildOpenQuestionOperations.js';
|
|
50
|
-
import {
|
|
50
|
+
import { areBundleTicketsReadyForAutomaticReview } from './lib/bundleAutomationReadiness.js';
|
|
51
|
+
import { abandonAutomationPause, completeAutomationProgression, createAutomationReviewFix, ensureAutomationProgression, finishAutomationReviewFix, getAutomationProgression, recoverLegacyAutomationProgression, recoverStoppedSideEffectProgression, runAutomationProgression, stopAutomationProgression, waitAutomationProgression } from './lib/automationProgression.js';
|
|
51
52
|
import { reconcileAutomationStage } from './lib/automationReconciliation.js';
|
|
52
53
|
import { runAutomationReviewGateOrPause } from './lib/automationReviewGate.js';
|
|
53
54
|
import { pausedRunBelongsToProgression, resolveAutomationResumeDecision } from './lib/automationResumeDecision.js';
|
|
@@ -82,11 +83,13 @@ import { countInboundProjectLinks, getEffectiveProject, getProjectCheckDefinitio
|
|
|
82
83
|
import { getActiveRunStatus, getActiveRunStatusForProject, getPreviewStatusForProject, hasRunConfig, isRunning, openIde, openTicketIdeInWorkspace, resolveProjectRunContextKey, runSetupCommandsInWorkspace, runTicketInWorkspace, stopAllTickets, stopTicket } from './lib/run.js';
|
|
83
84
|
import { clearAllTestSessions, createTestSession, deleteTestSession, discoverTestsForSession, doesTestFileExistForSession, getTestSession, isTestSelectionSupported, startTestSessionRun, stopTestSessionRun } from './lib/testSessions.js';
|
|
84
85
|
import { closeCheckRunnerAdmissions, getCheckBatchAttempts, openCheckRunnerAdmissions, rerunCheckBatchAndSchedule, shutdownCheckRunner, startCheckBatch, stopCheckBatch } from './lib/checkRunner.js';
|
|
86
|
+
import { CheckRepairError, getCheckRepairCandidateState, getCheckRepairDetails, getLatestCheckRepairForBatch, retryCheckRepair, startCheckRepair, stopCheckRepair } from './lib/checkRepair.js';
|
|
85
87
|
import { findActiveCheckBatchForTarget, getCheckAttemptDetails, getCheckBatchDetails, hasActiveProjectCheckBatch, isCheckAttemptCurrent, resolveCheckBatchRerunEligibility } from './lib/checkRuns.js';
|
|
86
|
-
import {
|
|
87
|
-
import {
|
|
88
|
+
import { getCheckAttemptDiagnosticResponse, getCheckBatchDiagnosticResponse } from './lib/checkDiagnostics.js';
|
|
89
|
+
import { getCheckDefinitionFingerprint } from './lib/checkDefinitions.js';
|
|
90
|
+
import { getCheckGateEvaluation, hydrateCheckGatePresentApplicability } from './lib/checkGateEvaluations.js';
|
|
88
91
|
import { captureCheckWorkspaceIdentity } from './lib/checkWorkspaceIdentity.js';
|
|
89
|
-
import {
|
|
92
|
+
import { resolveEffectiveRequiredCheckPolicyMembership, resolveRequiredCheckPolicy } from './lib/requiredCheckPolicy.js';
|
|
90
93
|
import { getBundleBuildBlockerResponse, shouldStartBundleBuildNow, transitionTicketState, } from './lib/ticketWorkflowOperations.js';
|
|
91
94
|
import { submitPlanTicketFollowUp, submitTicketFollowUp, } from './lib/ticketFollowUpOperations.js';
|
|
92
95
|
import { closePendingTicketPlanQuestionSet, listPendingTicketPlanQuestionSetMessageIds, resolveTicketPlanQuestionSet, restoreClosedTicketPlanQuestionSets, } from './lib/planQuestionOperations.js';
|
|
@@ -103,9 +106,9 @@ import { HttpError } from './lib/httpError.js';
|
|
|
103
106
|
import { parseIncomingExternalSource } from './lib/externalSource.js';
|
|
104
107
|
import { materializeTicketsWithDependencies } from './lib/ticketBatch.js';
|
|
105
108
|
import { IMPLEMENTATION_READY_DESCRIPTION_SECTION_HEADINGS } from './lib/ticketDescriptionContract.js';
|
|
106
|
-
import { buildSpecImplementationDraft, buildVisualizationAppendix } from './lib/specDraft.js';
|
|
109
|
+
import { buildImplementationResourcesAppendix, buildSpecImplementationDraft, buildVisualizationAppendix } from './lib/specDraft.js';
|
|
107
110
|
import { exportSpecToDocs, checkSpecExportCollision } from './lib/specExport.js';
|
|
108
|
-
import { createTicketDependency, deleteTicketDependenciesForTicket, deleteTicketDependency, getTicketDependency, isTicketRunSetupReady, listTicketDependencies, validateCreateTicketDependency, } from './lib/ticketDependencies.js';
|
|
111
|
+
import { createTicketDependency, deleteTicketDependenciesForTicket, deleteTicketDependency, getTicketDependency, isTicketBuildComplete, isTicketRunSetupReady, listTicketDependencies, validateCreateTicketDependency, } from './lib/ticketDependencies.js';
|
|
109
112
|
import { startBuild } from './workers/build.js';
|
|
110
113
|
import { expandPlanningTicketOutline, generatePlanningTicketOutline, generatePlanText, prepareLinkedPlanningResearch, startPlan, stopAllPlanAgents, } from './workers/plan.js';
|
|
111
114
|
import { EXECUTION_RUN_KINDS, MAX_ARTIFACTS_PER_RUN, validateWorkflowContext } from './lib/types.js';
|
|
@@ -135,6 +138,34 @@ const runExclusiveExecutionArtifactMaintenance = async (task) => {
|
|
|
135
138
|
const getErrorMessage = (error) => {
|
|
136
139
|
return error instanceof Error ? error.message : String(error);
|
|
137
140
|
};
|
|
141
|
+
const getCommitHashQuery = (req) => {
|
|
142
|
+
if (req.query.commit === undefined) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return typeof req.query.commit === 'string' ? req.query.commit : '';
|
|
146
|
+
};
|
|
147
|
+
const sendUnpushedCommitDiff = (res, workspace, commitHash) => {
|
|
148
|
+
try {
|
|
149
|
+
const diffData = getUnpushedCommitDiffForCwd({
|
|
150
|
+
cwd: workspace.cwd,
|
|
151
|
+
branch: workspace.branch,
|
|
152
|
+
project: workspace.project,
|
|
153
|
+
commitHash,
|
|
154
|
+
});
|
|
155
|
+
return res.json({
|
|
156
|
+
stats: summarizeTicketDiffFiles(diffData.files),
|
|
157
|
+
files: diffData.files,
|
|
158
|
+
diff: diffData.diff,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
if (error instanceof UnpushedCommitDiffSelectionError) {
|
|
163
|
+
return res.status(400).json({ error: error.message });
|
|
164
|
+
}
|
|
165
|
+
console.error(`Failed to load committed diff for ${workspace.cwd}:`, error);
|
|
166
|
+
return res.status(500).json({ error: getErrorMessage(error) || 'Unable to load the committed diff.' });
|
|
167
|
+
}
|
|
168
|
+
};
|
|
138
169
|
const getProjectMemoryDisabledError = () => 'Project memory is disabled for this project';
|
|
139
170
|
const sanitizeProjectMemoryExportFileName = (projectName) => {
|
|
140
171
|
const normalized = projectName
|
|
@@ -380,11 +411,19 @@ const buildExecutionRunDetailResponse = async (project, run) => {
|
|
|
380
411
|
retry_eligibility: retryEligibility,
|
|
381
412
|
action_kind: null,
|
|
382
413
|
}
|
|
383
|
-
: run.kind === '
|
|
384
|
-
? {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
:
|
|
414
|
+
: run.kind === 'check_repair'
|
|
415
|
+
? {
|
|
416
|
+
kind: 'check_repair',
|
|
417
|
+
available: true,
|
|
418
|
+
check_repair: getCheckRepairDetails(project.id, run.id),
|
|
419
|
+
retry_eligibility: retryEligibility,
|
|
420
|
+
action_kind: null,
|
|
421
|
+
}
|
|
422
|
+
: run.kind === 'github_actions'
|
|
423
|
+
? { ...serializeGitHubActionsExecutionDetail(getGitHubActionsExecutionDetails(project.id, run.id)), retry_eligibility: retryEligibility }
|
|
424
|
+
: ['automation_commit', 'automation_push', 'automation_pull_request'].includes(run.kind)
|
|
425
|
+
? { kind: run.kind, available: true, automation_stage: getAutomationStageDetails(project.id, run.id), retry_eligibility: retryEligibility }
|
|
426
|
+
: { kind: run.kind, available: true, retry_eligibility: retryEligibility };
|
|
388
427
|
const artifacts = loadExecutionArtifactSummariesForRuns(project.id, [run.id], MAX_ARTIFACTS_PER_RUN).get(run.id)
|
|
389
428
|
?? { artifacts: [], artifact_count: 0 };
|
|
390
429
|
return {
|
|
@@ -444,7 +483,14 @@ const getParkTicketError = (ticket) => {
|
|
|
444
483
|
const getBundlePlanBlockerError = () => {
|
|
445
484
|
return 'AI planning is blocked because this bundle worktree is currently being modified.';
|
|
446
485
|
};
|
|
486
|
+
const hasActiveBundleCheckWork = (projectId, bundleId) => Boolean(getActiveBundleRepairBlocker(bundleId)
|
|
487
|
+
|| findActiveCheckBatchForTarget(projectId, 'bundle', bundleId));
|
|
447
488
|
const deleteTicketRecord = async (ticket) => {
|
|
489
|
+
if (ticket.project_id
|
|
490
|
+
&& ticket.worktree_bundle_id
|
|
491
|
+
&& hasActiveBundleCheckWork(ticket.project_id, ticket.worktree_bundle_id)) {
|
|
492
|
+
return { ok: false, status: 409, error: 'Cannot delete a ticket while its bundle has active check work' };
|
|
493
|
+
}
|
|
448
494
|
if (ticket.agent_status === 'running'
|
|
449
495
|
|| (ticket.project_id && hasActiveBuildExecutionRunForTicket(ticket.project_id, ticket.id))) {
|
|
450
496
|
return { ok: false, status: 409, error: 'Cannot delete a ticket while the agent is running' };
|
|
@@ -812,6 +858,9 @@ const cleanupBundleIfEmpty = (bundleId, project) => {
|
|
|
812
858
|
if (isProjectRootBundle(bundle)) {
|
|
813
859
|
return;
|
|
814
860
|
}
|
|
861
|
+
if (hasActiveBundleCheckWork(project.id, bundle.id)) {
|
|
862
|
+
throw new HttpError(409, 'Cannot delete a bundle while check work is active');
|
|
863
|
+
}
|
|
815
864
|
try {
|
|
816
865
|
deleteWorktree(bundle.branch, project);
|
|
817
866
|
}
|
|
@@ -1464,10 +1513,7 @@ const serializeBundleRow = (projectId, bundle, openQuestions = bundle ? loadBuil
|
|
|
1464
1513
|
const hasPreparedWorktree = bundle.kind === 'project_root'
|
|
1465
1514
|
? true
|
|
1466
1515
|
: Boolean(project && resolveExistingWorktreePath(bundle.branch, project));
|
|
1467
|
-
const
|
|
1468
|
-
? { value: null }
|
|
1469
|
-
: parseBundleRequiredCheckPolicyOverride(bundle.required_check_policy_override);
|
|
1470
|
-
const effectivePolicy = resolveEffectiveRequiredCheckPolicyMembership(project, bundle);
|
|
1516
|
+
const effectivePolicy = resolveEffectiveRequiredCheckPolicyMembership(project);
|
|
1471
1517
|
return {
|
|
1472
1518
|
...bundle,
|
|
1473
1519
|
has_prepared_worktree: hasPreparedWorktree,
|
|
@@ -1475,7 +1521,6 @@ const serializeBundleRow = (projectId, bundle, openQuestions = bundle ? loadBuil
|
|
|
1475
1521
|
? serializeBundlePullRequest(getBundlePullRequestRecord(bundle.id, projectId))
|
|
1476
1522
|
: null,
|
|
1477
1523
|
automation_config: resolveAutomationConfig(bundle, project),
|
|
1478
|
-
required_check_policy_override: 'error' in parsedOverride ? null : parsedOverride.value,
|
|
1479
1524
|
required_check_policy: 'error' in effectivePolicy
|
|
1480
1525
|
? undefined
|
|
1481
1526
|
: {
|
|
@@ -1483,7 +1528,6 @@ const serializeBundleRow = (projectId, bundle, openQuestions = bundle ? loadBuil
|
|
|
1483
1528
|
required_check_definition_ids: effectivePolicy.required_check_definition_ids,
|
|
1484
1529
|
pre_review_check_definition_ids: effectivePolicy.pre_review_check_definition_ids,
|
|
1485
1530
|
},
|
|
1486
|
-
required_check_policy_error: 'error' in parsedOverride ? parsedOverride.error : ('error' in effectivePolicy ? effectivePolicy.error : null),
|
|
1487
1531
|
open_questions: openQuestions,
|
|
1488
1532
|
build_open_question_batch_status: getLatestBuildOpenQuestionBatchStatus(projectId, bundle.id),
|
|
1489
1533
|
};
|
|
@@ -3049,11 +3093,14 @@ app.post('/projects/:id/spec-sessions/:sessionId/snapshots/:snapshotId/create-ti
|
|
|
3049
3093
|
? createAutoBundleForName(bundleName, project.id)
|
|
3050
3094
|
: createAutoPlanningBundle(project.id);
|
|
3051
3095
|
const ticketsForMaterialization = snapshot.tickets.map(ticket => {
|
|
3052
|
-
const
|
|
3096
|
+
const appendices = [
|
|
3097
|
+
buildVisualizationAppendix(ticket.relevant_visualizations ?? [], snapshot),
|
|
3098
|
+
buildImplementationResourcesAppendix(snapshot.resource_urls),
|
|
3099
|
+
].filter(Boolean);
|
|
3053
3100
|
return {
|
|
3054
3101
|
key: ticket.key,
|
|
3055
3102
|
title: ticket.title,
|
|
3056
|
-
description:
|
|
3103
|
+
description: appendices.length > 0 ? `${ticket.description}\n\n${appendices.join('\n\n')}` : ticket.description,
|
|
3057
3104
|
};
|
|
3058
3105
|
});
|
|
3059
3106
|
const { createdTickets, createdDependencies } = materializeTicketsWithDependencies({
|
|
@@ -3295,6 +3342,20 @@ app.get('/tickets/:id/diff', (req, res) => {
|
|
|
3295
3342
|
diff: diffData.diff,
|
|
3296
3343
|
});
|
|
3297
3344
|
});
|
|
3345
|
+
app.get('/tickets/:id/committed-diff', (req, res) => {
|
|
3346
|
+
const ticket = getTicketRouteContext(req.params.id);
|
|
3347
|
+
if (!ticket) {
|
|
3348
|
+
return res.status(404).json({ error: 'Not found' });
|
|
3349
|
+
}
|
|
3350
|
+
if (!matchesTicketProjectContext(ticket, req)) {
|
|
3351
|
+
return res.status(404).json({ error: 'Not found' });
|
|
3352
|
+
}
|
|
3353
|
+
const workspace = resolveTicketDiffWorkspaceFromContext(ticket);
|
|
3354
|
+
if ('error' in workspace) {
|
|
3355
|
+
return res.status(workspace.status).json({ error: workspace.error });
|
|
3356
|
+
}
|
|
3357
|
+
return sendUnpushedCommitDiff(res, workspace, getCommitHashQuery(req));
|
|
3358
|
+
});
|
|
3298
3359
|
app.get('/tickets/:id/git-status', (req, res) => {
|
|
3299
3360
|
const ticket = getTicketRouteContext(req.params.id);
|
|
3300
3361
|
if (!ticket) {
|
|
@@ -3719,15 +3780,15 @@ app.delete('/bundles/:id', (req, res) => {
|
|
|
3719
3780
|
if (isProjectRootBundle(bundle)) {
|
|
3720
3781
|
return res.status(409).json({ error: 'Project root cannot be deleted' });
|
|
3721
3782
|
}
|
|
3783
|
+
if (hasActiveBundleCheckWork(result.project.id, bundle.id)) {
|
|
3784
|
+
return res.status(409).json({ error: 'Cannot delete a bundle while check work is active' });
|
|
3785
|
+
}
|
|
3722
3786
|
if (countBundleTickets(bundle.id, result.project.id) > 0) {
|
|
3723
3787
|
return res.status(409).json({ error: 'Cannot delete a bundle that is still used by tickets' });
|
|
3724
3788
|
}
|
|
3725
3789
|
if (result.project.active_target_kind === 'bundle' && result.project.active_target_bundle_id === bundle.id) {
|
|
3726
3790
|
return res.status(409).json({ error: 'Cannot delete the currently selected target bundle' });
|
|
3727
3791
|
}
|
|
3728
|
-
if (findActiveCheckBatchForTarget(result.project.id, 'bundle', bundle.id)) {
|
|
3729
|
-
return res.status(409).json({ error: 'Cannot delete a bundle while checks are running' });
|
|
3730
|
-
}
|
|
3731
3792
|
const affectedChatSessionIds = listChatSessions(result.project.id)
|
|
3732
3793
|
.filter(chatSession => chatSession.target_bundle_id === bundle.id)
|
|
3733
3794
|
.map(chatSession => chatSession.id);
|
|
@@ -3758,14 +3819,14 @@ app.patch('/bundles/:id', (req, res) => {
|
|
|
3758
3819
|
return res.status(404).json({ error: 'Bundle not found' });
|
|
3759
3820
|
}
|
|
3760
3821
|
const hasNameUpdate = hasOwn(req.body, 'name');
|
|
3761
|
-
|
|
3822
|
+
if (hasOwn(req.body, 'required_check_policy_override')) {
|
|
3823
|
+
return res.status(400).json({ error: 'Bundle required-check policy overrides are no longer supported; configure required checks on the project' });
|
|
3824
|
+
}
|
|
3762
3825
|
if (isProjectRootBundle(bundle)) {
|
|
3763
3826
|
return res.status(409).json({
|
|
3764
3827
|
error: hasNameUpdate
|
|
3765
3828
|
? 'Project root bundles cannot be renamed'
|
|
3766
|
-
:
|
|
3767
|
-
? 'Project root bundles cannot override required-check policy'
|
|
3768
|
-
: 'Project root bundle automation cannot be overridden',
|
|
3829
|
+
: 'Project root bundle automation cannot be overridden',
|
|
3769
3830
|
});
|
|
3770
3831
|
}
|
|
3771
3832
|
let nextName = bundle.name;
|
|
@@ -3838,39 +3899,6 @@ app.patch('/bundles/:id', (req, res) => {
|
|
|
3838
3899
|
return res.status(400).json({ error: 'automation_pause_on_open_questions must be a boolean' });
|
|
3839
3900
|
}
|
|
3840
3901
|
}
|
|
3841
|
-
let requiredCheckPolicyOverride;
|
|
3842
|
-
let requiredCheckPolicyOverrideValue;
|
|
3843
|
-
if (hasRequiredCheckPolicyOverrideUpdate) {
|
|
3844
|
-
const value = req.body.required_check_policy_override;
|
|
3845
|
-
if (value == null) {
|
|
3846
|
-
requiredCheckPolicyOverride = null;
|
|
3847
|
-
requiredCheckPolicyOverrideValue = null;
|
|
3848
|
-
}
|
|
3849
|
-
else {
|
|
3850
|
-
const result = canonicalizeBundleRequiredCheckPolicyOverride(value);
|
|
3851
|
-
if ('error' in result) {
|
|
3852
|
-
return res.status(400).json({ error: result.error });
|
|
3853
|
-
}
|
|
3854
|
-
requiredCheckPolicyOverrideValue = result.value;
|
|
3855
|
-
requiredCheckPolicyOverride = result.value ? JSON.stringify(result.value) : null;
|
|
3856
|
-
}
|
|
3857
|
-
}
|
|
3858
|
-
if (requiredCheckPolicyOverrideValue) {
|
|
3859
|
-
const definitions = getProjectCheckDefinitions(result.project);
|
|
3860
|
-
const definitionIds = new Set(definitions.map(definition => definition.id));
|
|
3861
|
-
const missingId = [
|
|
3862
|
-
...requiredCheckPolicyOverrideValue.required_check_definition_ids,
|
|
3863
|
-
...requiredCheckPolicyOverrideValue.pre_review_check_definition_ids,
|
|
3864
|
-
].find(id => !definitionIds.has(id));
|
|
3865
|
-
if (missingId) {
|
|
3866
|
-
return res.status(400).json({ error: `required_check_policy_override references missing check definition ${missingId}` });
|
|
3867
|
-
}
|
|
3868
|
-
const requiredDefinitions = definitions.filter(definition => requiredCheckPolicyOverrideValue.required_check_definition_ids.includes(definition.id));
|
|
3869
|
-
const policySnapshotSizeError = validateRequiredCheckPolicySnapshotSize(requiredCheckPolicyOverrideValue.required_check_definition_ids, requiredDefinitions);
|
|
3870
|
-
if (policySnapshotSizeError) {
|
|
3871
|
-
return res.status(400).json({ error: policySnapshotSizeError });
|
|
3872
|
-
}
|
|
3873
|
-
}
|
|
3874
3902
|
let updated = null;
|
|
3875
3903
|
try {
|
|
3876
3904
|
updated = db.transaction(() => {
|
|
@@ -3888,7 +3916,6 @@ app.patch('/bundles/:id', (req, res) => {
|
|
|
3888
3916
|
automationDepth,
|
|
3889
3917
|
automationReviewFixEnabled,
|
|
3890
3918
|
automationPauseOnOpenQuestions,
|
|
3891
|
-
requiredCheckPolicyOverride,
|
|
3892
3919
|
});
|
|
3893
3920
|
})();
|
|
3894
3921
|
}
|
|
@@ -3928,7 +3955,7 @@ app.post('/bundles/:id/review', (req, res) => {
|
|
|
3928
3955
|
}
|
|
3929
3956
|
return res.json(reviewResult.details);
|
|
3930
3957
|
});
|
|
3931
|
-
app.post('/bundles/:id/build-open-questions/resolve', (req, res) => {
|
|
3958
|
+
app.post('/bundles/:id/build-open-questions/resolve', async (req, res) => {
|
|
3932
3959
|
const result = getRequiredProject(req);
|
|
3933
3960
|
if ('error' in result) {
|
|
3934
3961
|
return res.status(400).json({ error: result.error });
|
|
@@ -3971,54 +3998,70 @@ app.post('/bundles/:id/build-open-questions/resolve', (req, res) => {
|
|
|
3971
3998
|
}
|
|
3972
3999
|
if (accepted.kind === 'dismissed') {
|
|
3973
4000
|
const automationConfig = resolveAutomationConfig(bundle, result.project);
|
|
3974
|
-
|
|
3975
|
-
? transitionBundleToReview(result.project.id, bundle.id, io, {
|
|
3976
|
-
automatic: true,
|
|
3977
|
-
initiation: {
|
|
3978
|
-
causalParentRunId: accepted.progressionRunId,
|
|
3979
|
-
workflowContext: validateWorkflowContext({ build_open_question_batch_id: accepted.batchId }),
|
|
3980
|
-
},
|
|
3981
|
-
})
|
|
3982
|
-
: null;
|
|
3983
|
-
if (!reviewResult) {
|
|
4001
|
+
if (!depthAtLeast(automationConfig.depth, 'auto_review')) {
|
|
3984
4002
|
completeAutomationProgression({
|
|
3985
4003
|
projectId: result.project.id,
|
|
3986
4004
|
bundleId: bundle.id,
|
|
3987
4005
|
progressionRunId: accepted.progressionRunId,
|
|
3988
4006
|
broadcaster: io,
|
|
3989
4007
|
});
|
|
4008
|
+
for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
|
|
4009
|
+
io.emit('ticket:updated', ticket);
|
|
4010
|
+
}
|
|
4011
|
+
return res.json(serializeBundleDetails(result.project, bundle));
|
|
3990
4012
|
}
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
});
|
|
4002
|
-
for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
|
|
4003
|
-
io.emit('ticket:updated', ticket);
|
|
4004
|
-
}
|
|
4005
|
-
return res.json(serializeBundleDetails(result.project, bundle));
|
|
4013
|
+
const ticketsBeforeReview = listDetailedBundleTickets(result.project.id, bundle.id);
|
|
4014
|
+
if (!areBundleTicketsReadyForAutomaticReview(ticketsBeforeReview, bundle.id)) {
|
|
4015
|
+
completeAutomationProgression({
|
|
4016
|
+
projectId: result.project.id,
|
|
4017
|
+
bundleId: bundle.id,
|
|
4018
|
+
progressionRunId: accepted.progressionRunId,
|
|
4019
|
+
broadcaster: io,
|
|
4020
|
+
});
|
|
4021
|
+
for (const ticket of ticketsBeforeReview) {
|
|
4022
|
+
io.emit('ticket:updated', ticket);
|
|
4006
4023
|
}
|
|
4024
|
+
return res.json(serializeBundleDetails(result.project, bundle));
|
|
4025
|
+
}
|
|
4026
|
+
let reviewStartFailureStatus = null;
|
|
4027
|
+
let reviewStartFailureError = null;
|
|
4028
|
+
const reviewStarted = await runAutomationReviewGateOrPause({
|
|
4029
|
+
projectId: result.project.id,
|
|
4030
|
+
bundleId: bundle.id,
|
|
4031
|
+
progressionRunId: accepted.progressionRunId,
|
|
4032
|
+
broadcaster: io,
|
|
4033
|
+
pausedStageRunId: null,
|
|
4034
|
+
initiation: {
|
|
4035
|
+
workflowContext: validateWorkflowContext({ build_open_question_batch_id: accepted.batchId }),
|
|
4036
|
+
},
|
|
4037
|
+
onReviewStartFailure: failure => {
|
|
4038
|
+
reviewStartFailureStatus = failure.status;
|
|
4039
|
+
reviewStartFailureError = failure.error;
|
|
4040
|
+
},
|
|
4041
|
+
});
|
|
4042
|
+
if (reviewStartFailureStatus !== null && reviewStartFailureError !== null) {
|
|
4007
4043
|
waitAutomationProgression({
|
|
4008
4044
|
projectId: result.project.id,
|
|
4009
4045
|
bundleId: bundle.id,
|
|
4010
4046
|
progressionRunId: accepted.progressionRunId,
|
|
4011
4047
|
pauseCode: 'review',
|
|
4012
|
-
pauseReason:
|
|
4048
|
+
pauseReason: reviewStartFailureError,
|
|
4013
4049
|
reasonCode: 'stage_failed',
|
|
4014
4050
|
pausedStageRunId: null,
|
|
4015
4051
|
broadcaster: io,
|
|
4016
4052
|
});
|
|
4017
|
-
return res.status(
|
|
4053
|
+
return res.status(reviewStartFailureStatus).json({ error: reviewStartFailureError });
|
|
4054
|
+
}
|
|
4055
|
+
const updatedTickets = listDetailedBundleTickets(result.project.id, bundle.id);
|
|
4056
|
+
for (const ticket of updatedTickets) {
|
|
4057
|
+
io.emit('ticket:updated', ticket);
|
|
4018
4058
|
}
|
|
4059
|
+
const reviewOwner = reviewStarted
|
|
4060
|
+
? updatedTickets.find(ticket => ticket.state === 'review' && ticket.parked_at === null)
|
|
4061
|
+
: null;
|
|
4019
4062
|
return res.json(serializeBundleDetails(result.project, bundle, {
|
|
4020
|
-
review_owner_ticket_id:
|
|
4021
|
-
target_ticket_id:
|
|
4063
|
+
review_owner_ticket_id: reviewOwner?.id,
|
|
4064
|
+
target_ticket_id: reviewOwner?.id,
|
|
4022
4065
|
}));
|
|
4023
4066
|
}
|
|
4024
4067
|
for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
|
|
@@ -4125,7 +4168,7 @@ app.post('/bundles/:id/follow-up', (req, res) => {
|
|
|
4125
4168
|
return res.status(response.status).json(response.value);
|
|
4126
4169
|
});
|
|
4127
4170
|
const AUTOMATION_SIDE_EFFECT_KINDS = new Set(['automation_commit', 'automation_push', 'automation_pull_request']);
|
|
4128
|
-
const AUTOMATION_WORKER_PAUSE_KINDS = new Set(['build', 'review', 'build_follow_up', 'automation_review_fix', 'check_batch']);
|
|
4171
|
+
const AUTOMATION_WORKER_PAUSE_KINDS = new Set(['build', 'review', 'build_follow_up', 'automation_review_fix', 'check_repair', 'check_batch']);
|
|
4129
4172
|
app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
4130
4173
|
const result = getRequiredProject(req);
|
|
4131
4174
|
if ('error' in result) {
|
|
@@ -4208,7 +4251,11 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4208
4251
|
});
|
|
4209
4252
|
if (checkpointLineage) {
|
|
4210
4253
|
const owner = getTicket(decision.owner_ticket_id);
|
|
4211
|
-
if (!owner
|
|
4254
|
+
if (!owner
|
|
4255
|
+
|| owner.id !== checkpointLineage.ticket.id
|
|
4256
|
+
|| owner.state !== 'build'
|
|
4257
|
+
|| owner.parked_at !== null
|
|
4258
|
+
|| !isTicketBuildComplete(owner)) {
|
|
4212
4259
|
return res.status(409).json({ error: 'Checkpoint progression owner is no longer the active Build ticket' });
|
|
4213
4260
|
}
|
|
4214
4261
|
let retryOfRunId;
|
|
@@ -4463,6 +4510,7 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4463
4510
|
pauseReason: bundleState.automation_paused_reason ?? 'Automation paused',
|
|
4464
4511
|
reasonCode: bundleState.automation_pause_reason_code ?? 'manual_transition',
|
|
4465
4512
|
pausedStageRunId: bundleState.automation_paused_stage_run_id,
|
|
4513
|
+
pausedBoundaryPurpose: bundleState.automation_paused_boundary_purpose,
|
|
4466
4514
|
broadcaster: io,
|
|
4467
4515
|
});
|
|
4468
4516
|
};
|
|
@@ -4500,7 +4548,62 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4500
4548
|
}
|
|
4501
4549
|
return res.json(serializeBundleDetails(result.project, bundle));
|
|
4502
4550
|
}
|
|
4503
|
-
if (
|
|
4551
|
+
if (decision.outcome === 'complete_manual_boundary') {
|
|
4552
|
+
if (!revalidateDecision()) {
|
|
4553
|
+
return res.status(409).json({ error: 'Bundle automation state changed before Resume could start' });
|
|
4554
|
+
}
|
|
4555
|
+
completeAutomationProgression({
|
|
4556
|
+
projectId: result.project.id,
|
|
4557
|
+
bundleId: bundle.id,
|
|
4558
|
+
progressionRunId,
|
|
4559
|
+
domainStatus: 'manual_boundary_reached',
|
|
4560
|
+
broadcaster: io,
|
|
4561
|
+
});
|
|
4562
|
+
return res.json(serializeBundleDetails(result.project, bundle));
|
|
4563
|
+
}
|
|
4564
|
+
if ((decision.outcome === 'start_review' || decision.outcome === 'rerun_review_checks')
|
|
4565
|
+
&& owner.parked_at === null
|
|
4566
|
+
&& isTicketBuildComplete(owner)
|
|
4567
|
+
&& (owner.state === 'build' || (decision.outcome === 'rerun_review_checks' && owner.state === 'review'))) {
|
|
4568
|
+
if (!revalidateDecision()) {
|
|
4569
|
+
return res.status(409).json({ error: 'Bundle automation state changed before Resume could start' });
|
|
4570
|
+
}
|
|
4571
|
+
const progression = getAutomationProgression(result.project.id, bundle.id);
|
|
4572
|
+
if (!progression || progression.id !== progressionRunId) {
|
|
4573
|
+
return res.status(409).json({ error: 'Automation progression ownership is invalid' });
|
|
4574
|
+
}
|
|
4575
|
+
const workflowContext = validateWorkflowContext(progression.metadata);
|
|
4576
|
+
let reviewStartFailureStatus = null;
|
|
4577
|
+
let reviewStartFailureError = null;
|
|
4578
|
+
try {
|
|
4579
|
+
runAutomationProgression({
|
|
4580
|
+
projectId: result.project.id,
|
|
4581
|
+
bundleId: bundle.id,
|
|
4582
|
+
progressionRunId,
|
|
4583
|
+
broadcaster: io,
|
|
4584
|
+
});
|
|
4585
|
+
await runAutomationReviewGateOrPause({
|
|
4586
|
+
projectId: result.project.id,
|
|
4587
|
+
bundleId: bundle.id,
|
|
4588
|
+
progressionRunId,
|
|
4589
|
+
broadcaster: io,
|
|
4590
|
+
pausedStageRunId: bundleState.automation_paused_stage_run_id,
|
|
4591
|
+
initiation: { workflowContext },
|
|
4592
|
+
onReviewStartFailure: failure => {
|
|
4593
|
+
reviewStartFailureStatus = failure.status;
|
|
4594
|
+
reviewStartFailureError = failure.error;
|
|
4595
|
+
restoreWaitingProgression();
|
|
4596
|
+
},
|
|
4597
|
+
});
|
|
4598
|
+
}
|
|
4599
|
+
catch (error) {
|
|
4600
|
+
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
4601
|
+
}
|
|
4602
|
+
if (reviewStartFailureStatus !== null && reviewStartFailureError !== null) {
|
|
4603
|
+
return res.status(reviewStartFailureStatus).json({ error: reviewStartFailureError });
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4606
|
+
else if (owner.state === 'review' && owner.agent_status !== 'running') {
|
|
4504
4607
|
const iteration = bundleState.review_iteration_count ?? 0;
|
|
4505
4608
|
if (decision.outcome === 'start_review_fix') {
|
|
4506
4609
|
let reviewFixRunId = null;
|
|
@@ -4610,7 +4713,7 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4610
4713
|
broadcaster: io,
|
|
4611
4714
|
});
|
|
4612
4715
|
const checkGateResume = decision.outcome === 'rerun_required_checks'
|
|
4613
|
-
? resolveAutomationCheckGateResume(result.project.id, progressionRunId)
|
|
4716
|
+
? resolveAutomationCheckGateResume(result.project.id, progressionRunId, owner.bundle?.kind === 'project_root' ? project.repo_path : resolveWorktreePath(bundle.branch, project))
|
|
4614
4717
|
: undefined;
|
|
4615
4718
|
await runAutomationCommitChain(owner, project, owner.bundle ?? null, automationConfig, io, progressionRunId, checkGateResume);
|
|
4616
4719
|
}
|
|
@@ -4635,45 +4738,6 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4635
4738
|
return res.status(409).json({ error: 'The selected Resume outcome no longer matches the bundle state' });
|
|
4636
4739
|
}
|
|
4637
4740
|
}
|
|
4638
|
-
else if (owner.state === 'build' && owner.agent_status === 'done' && (decision.outcome === 'start_review' || decision.outcome === 'rerun_review_checks')) {
|
|
4639
|
-
if (!revalidateDecision()) {
|
|
4640
|
-
return res.status(409).json({ error: 'Bundle automation state changed before Resume could start' });
|
|
4641
|
-
}
|
|
4642
|
-
const progression = getAutomationProgression(result.project.id, bundle.id);
|
|
4643
|
-
if (!progression || progression.id !== progressionRunId) {
|
|
4644
|
-
return res.status(409).json({ error: 'Automation progression ownership is invalid' });
|
|
4645
|
-
}
|
|
4646
|
-
const workflowContext = validateWorkflowContext(progression.metadata);
|
|
4647
|
-
let reviewStartFailureStatus = null;
|
|
4648
|
-
let reviewStartFailureError = null;
|
|
4649
|
-
try {
|
|
4650
|
-
runAutomationProgression({
|
|
4651
|
-
projectId: result.project.id,
|
|
4652
|
-
bundleId: bundle.id,
|
|
4653
|
-
progressionRunId,
|
|
4654
|
-
broadcaster: io,
|
|
4655
|
-
});
|
|
4656
|
-
await runAutomationReviewGateOrPause({
|
|
4657
|
-
projectId: result.project.id,
|
|
4658
|
-
bundleId: bundle.id,
|
|
4659
|
-
progressionRunId,
|
|
4660
|
-
broadcaster: io,
|
|
4661
|
-
pausedStageRunId: bundleState.automation_paused_stage_run_id,
|
|
4662
|
-
initiation: { workflowContext },
|
|
4663
|
-
onReviewStartFailure: failure => {
|
|
4664
|
-
reviewStartFailureStatus = failure.status;
|
|
4665
|
-
reviewStartFailureError = failure.error;
|
|
4666
|
-
restoreWaitingProgression();
|
|
4667
|
-
},
|
|
4668
|
-
});
|
|
4669
|
-
}
|
|
4670
|
-
catch (error) {
|
|
4671
|
-
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
4672
|
-
}
|
|
4673
|
-
if (reviewStartFailureStatus !== null && reviewStartFailureError !== null) {
|
|
4674
|
-
return res.status(reviewStartFailureStatus).json({ error: reviewStartFailureError });
|
|
4675
|
-
}
|
|
4676
|
-
}
|
|
4677
4741
|
else {
|
|
4678
4742
|
return res.status(409).json({ error: 'Ticket is not in a resumable state' });
|
|
4679
4743
|
}
|
|
@@ -4704,72 +4768,31 @@ app.post('/bundles/:id/dismiss-automation-pause', async (req, res) => {
|
|
|
4704
4768
|
&& listBuildOpenQuestionsForBundle(result.project.id, bundle.id).length > 0) {
|
|
4705
4769
|
return res.status(409).json({ error: 'Resolve or dismiss all Build open questions before dismissing this pause' });
|
|
4706
4770
|
}
|
|
4707
|
-
|
|
4708
|
-
|
|
4771
|
+
const progressionRunId = bundleState.automation_progression_run_id;
|
|
4772
|
+
const checkpointLineage = progressionRunId
|
|
4773
|
+
? resolveBuildTicketCommitCheckpointLineage({
|
|
4774
|
+
projectId: result.project.id,
|
|
4775
|
+
bundleId: bundle.id,
|
|
4776
|
+
progressionRunId,
|
|
4777
|
+
})
|
|
4778
|
+
: null;
|
|
4779
|
+
if (progressionRunId) {
|
|
4709
4780
|
try {
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
: recoverLegacyAutomationProgression({
|
|
4717
|
-
projectId: result.project.id,
|
|
4718
|
-
bundleId: bundle.id,
|
|
4719
|
-
broadcaster: io,
|
|
4720
|
-
})).runId;
|
|
4721
|
-
}
|
|
4722
|
-
catch (error) {
|
|
4723
|
-
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
4724
|
-
}
|
|
4725
|
-
}
|
|
4726
|
-
try {
|
|
4727
|
-
if (getAutomationProgression(result.project.id, bundle.id)?.id !== progressionRunId) {
|
|
4728
|
-
return res.status(409).json({ error: 'Automation progression ownership is invalid' });
|
|
4729
|
-
}
|
|
4730
|
-
}
|
|
4731
|
-
catch (error) {
|
|
4732
|
-
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
4733
|
-
}
|
|
4734
|
-
const checkpointLineage = resolveBuildTicketCommitCheckpointLineage({
|
|
4735
|
-
projectId: result.project.id,
|
|
4736
|
-
bundleId: bundle.id,
|
|
4737
|
-
progressionRunId,
|
|
4738
|
-
});
|
|
4739
|
-
if (bundleState.automation_paused_stage_run_id) {
|
|
4740
|
-
const stage = getExecutionRun(result.project.id, bundleState.automation_paused_stage_run_id);
|
|
4741
|
-
if (!stage
|
|
4742
|
-
|| (!AUTOMATION_SIDE_EFFECT_KINDS.has(stage.kind) && !AUTOMATION_WORKER_PAUSE_KINDS.has(stage.kind))
|
|
4743
|
-
|| !pausedRunBelongsToProgression(result.project.id, bundle.id, progressionRunId, stage.id)) {
|
|
4744
|
-
return res.status(409).json({ error: 'The paused automation stage does not belong to the active progression' });
|
|
4781
|
+
await stopAutomationProgression({
|
|
4782
|
+
projectId: result.project.id,
|
|
4783
|
+
bundleId: bundle.id,
|
|
4784
|
+
broadcaster: io,
|
|
4785
|
+
stopLocalWorker: worker => (worker.kind === 'review' ? stopReviewAgent(worker.ticket_id) : stopBuildAgent(worker.ticket_id)),
|
|
4786
|
+
});
|
|
4745
4787
|
}
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
try {
|
|
4749
|
-
reconciled = await reconcileAutomationStage({
|
|
4750
|
-
projectId: result.project.id,
|
|
4751
|
-
bundleId: bundle.id,
|
|
4752
|
-
progressionRunId,
|
|
4753
|
-
stageRunId: stage.id,
|
|
4754
|
-
});
|
|
4755
|
-
}
|
|
4756
|
-
catch (error) {
|
|
4757
|
-
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
4758
|
-
}
|
|
4759
|
-
if (reconciled.domainStatus === 'ambiguous') {
|
|
4760
|
-
return res.status(409).json({ error: 'The paused automation side effect is ambiguous and cannot be dismissed safely.' });
|
|
4761
|
-
}
|
|
4762
|
-
if (!(reconciled.status === 'completed' && reconciled.domainStatus === 'reconciled_completed')
|
|
4763
|
-
&& !(reconciled.status === 'interrupted' && reconciled.domainStatus === 'reconciled_absent')) {
|
|
4764
|
-
return res.status(409).json({ error: 'The paused automation side effect has no safely dismissible outcome.' });
|
|
4765
|
-
}
|
|
4788
|
+
catch {
|
|
4789
|
+
// Explicit abandonment also repairs a stale progression pointer.
|
|
4766
4790
|
}
|
|
4767
4791
|
}
|
|
4768
|
-
|
|
4792
|
+
abandonAutomationPause({
|
|
4769
4793
|
projectId: result.project.id,
|
|
4770
4794
|
bundleId: bundle.id,
|
|
4771
4795
|
broadcaster: io,
|
|
4772
|
-
stopLocalWorker: worker => (worker.kind === 'review' ? stopReviewAgent(worker.ticket_id) : stopBuildAgent(worker.ticket_id)),
|
|
4773
4796
|
});
|
|
4774
4797
|
if (checkpointLineage) {
|
|
4775
4798
|
const stoppedSequence = stopBuildSequence(checkpointLineage.ticket.id);
|
|
@@ -5859,7 +5882,7 @@ app.post('/projects', (req, res) => {
|
|
|
5859
5882
|
return res.status(400).json({ error: validation.error });
|
|
5860
5883
|
}
|
|
5861
5884
|
const projectCount = Number(db.prepare('SELECT COUNT(*) AS count FROM projects').get()?.count ?? 0);
|
|
5862
|
-
const { name, repo_path, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, pre_review_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_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;
|
|
5885
|
+
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, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_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;
|
|
5863
5886
|
if (!repo_path) {
|
|
5864
5887
|
return res.status(400).json({ error: 'repo_path must be a non-empty string' });
|
|
5865
5888
|
}
|
|
@@ -5882,6 +5905,7 @@ app.post('/projects', (req, res) => {
|
|
|
5882
5905
|
check_definitions,
|
|
5883
5906
|
required_check_definition_ids,
|
|
5884
5907
|
pre_review_check_definition_ids,
|
|
5908
|
+
post_build_check_definition_ids,
|
|
5885
5909
|
test_commands,
|
|
5886
5910
|
run_ide,
|
|
5887
5911
|
preview_service_name,
|
|
@@ -5903,8 +5927,8 @@ app.post('/projects', (req, res) => {
|
|
|
5903
5927
|
automation_review_severity,
|
|
5904
5928
|
automation_review_max_iterations
|
|
5905
5929
|
)
|
|
5906
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5907
|
-
`).run(id, name, normalizedRepoPath, run_setup, run_services, check_definitions, required_check_definition_ids ?? '[]', pre_review_check_definition_ids ?? '[]', test_commands, run_ide ?? null, preview_service_name ?? null, preview_path ?? null, preview_capability_mode ?? null, helper_model ?? null, helper_variant ?? null, commit_message_rules ?? null, commit_after_each_ticket ?? 0, auto_park_stale_tickets ?? 0, memory_enabled ?? 0, scan_include_globs_json ?? null, scan_exclude_globs_json ?? null, worktree_sync ?? null, spec_aware_code_review_enabled ?? 0, automation_depth ?? 'manual', automation_review_fix_enabled ?? 0, automation_pause_on_open_questions ?? 0, automation_review_severity ?? 'medium', automation_review_max_iterations ?? 3);
|
|
5930
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5931
|
+
`).run(id, name, normalizedRepoPath, run_setup, run_services, check_definitions, required_check_definition_ids ?? '[]', pre_review_check_definition_ids ?? '[]', post_build_check_definition_ids ?? '[]', test_commands, run_ide ?? null, preview_service_name ?? null, preview_path ?? null, preview_capability_mode ?? null, helper_model ?? null, helper_variant ?? null, commit_message_rules ?? null, commit_after_each_ticket ?? 0, auto_park_stale_tickets ?? 0, memory_enabled ?? 0, scan_include_globs_json ?? null, scan_exclude_globs_json ?? null, worktree_sync ?? null, spec_aware_code_review_enabled ?? 0, automation_depth ?? 'manual', automation_review_fix_enabled ?? 0, automation_pause_on_open_questions ?? 0, automation_review_severity ?? 'medium', automation_review_max_iterations ?? 3);
|
|
5908
5932
|
replaceProjectLinks(id, linked_project_ids ?? []);
|
|
5909
5933
|
ensureProjectRootBundle(id);
|
|
5910
5934
|
if (projectCount === 0) {
|
|
@@ -6814,6 +6838,17 @@ app.get('/projects/:id/diff', (req, res) => {
|
|
|
6814
6838
|
diff: diffData.diff,
|
|
6815
6839
|
});
|
|
6816
6840
|
});
|
|
6841
|
+
app.get('/projects/:id/committed-diff', (req, res) => {
|
|
6842
|
+
const project = getProjectById(req.params.id);
|
|
6843
|
+
if (!project) {
|
|
6844
|
+
return res.status(404).json({ error: 'Not found' });
|
|
6845
|
+
}
|
|
6846
|
+
const workspace = resolveProjectTargetGitWorkspace(project);
|
|
6847
|
+
if ('error' in workspace) {
|
|
6848
|
+
return res.status(workspace.status).json({ error: workspace.error });
|
|
6849
|
+
}
|
|
6850
|
+
return sendUnpushedCommitDiff(res, workspace, getCommitHashQuery(req));
|
|
6851
|
+
});
|
|
6817
6852
|
app.get('/projects/:id/git-status', (req, res) => {
|
|
6818
6853
|
const project = getProjectById(req.params.id);
|
|
6819
6854
|
if (!project) {
|
|
@@ -7070,7 +7105,7 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
7070
7105
|
});
|
|
7071
7106
|
}
|
|
7072
7107
|
}
|
|
7073
|
-
const { name, repo_path, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, pre_review_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_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;
|
|
7108
|
+
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, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_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;
|
|
7074
7109
|
const updateProject = db.prepare(`
|
|
7075
7110
|
UPDATE projects SET
|
|
7076
7111
|
name = ?,
|
|
@@ -7080,6 +7115,7 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
7080
7115
|
check_definitions = ?,
|
|
7081
7116
|
required_check_definition_ids = ?,
|
|
7082
7117
|
pre_review_check_definition_ids = ?,
|
|
7118
|
+
post_build_check_definition_ids = ?,
|
|
7083
7119
|
test_commands = ?,
|
|
7084
7120
|
run_ide = ?,
|
|
7085
7121
|
preview_service_name = ?,
|
|
@@ -7110,17 +7146,21 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
7110
7146
|
const nextPreReviewCheckDefinitionIds = hasOwn(validation.values, 'pre_review_check_definition_ids')
|
|
7111
7147
|
? pre_review_check_definition_ids ?? '[]'
|
|
7112
7148
|
: project.pre_review_check_definition_ids;
|
|
7149
|
+
const nextPostBuildCheckDefinitionIds = hasOwn(validation.values, 'post_build_check_definition_ids')
|
|
7150
|
+
? post_build_check_definition_ids ?? '[]'
|
|
7151
|
+
: project.post_build_check_definition_ids ?? '[]';
|
|
7113
7152
|
const updateResult = db.transaction(() => {
|
|
7114
7153
|
const policyReferences = validateProjectRequiredCheckPolicyReferences({
|
|
7115
7154
|
projectId: project.id,
|
|
7116
7155
|
checkDefinitionsJson: nextCheckDefinitions,
|
|
7117
7156
|
requiredCheckDefinitionIdsJson: nextRequiredCheckDefinitionIds,
|
|
7118
7157
|
preReviewCheckDefinitionIdsJson: nextPreReviewCheckDefinitionIds,
|
|
7158
|
+
postBuildCheckDefinitionIdsJson: nextPostBuildCheckDefinitionIds,
|
|
7119
7159
|
});
|
|
7120
7160
|
if ('error' in policyReferences) {
|
|
7121
7161
|
return policyReferences;
|
|
7122
7162
|
}
|
|
7123
|
-
updateProject.run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, nextCheckDefinitions, nextRequiredCheckDefinitionIds, nextPreReviewCheckDefinitionIds, 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, 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);
|
|
7163
|
+
updateProject.run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, nextCheckDefinitions, nextRequiredCheckDefinitionIds, nextPreReviewCheckDefinitionIds, nextPostBuildCheckDefinitionIds, 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, 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);
|
|
7124
7164
|
if (hasOwn(validation.values, 'linked_project_ids')) {
|
|
7125
7165
|
replaceProjectLinks(project.id, linked_project_ids ?? []);
|
|
7126
7166
|
}
|
|
@@ -7187,6 +7227,12 @@ const completeClaimedProjectDeletion = async (projectId, claimId) => {
|
|
|
7187
7227
|
WHERE evaluation_id IN (SELECT id FROM check_gate_evaluations WHERE project_id = ?)
|
|
7188
7228
|
`).run(projectId);
|
|
7189
7229
|
db.prepare('DELETE FROM check_gate_evaluations WHERE project_id = ?').run(projectId);
|
|
7230
|
+
if (db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'check_repair_details'").get()) {
|
|
7231
|
+
db.prepare(`
|
|
7232
|
+
DELETE FROM check_repair_details
|
|
7233
|
+
WHERE execution_run_id IN (SELECT id FROM execution_runs WHERE project_id = ?)
|
|
7234
|
+
`).run(projectId);
|
|
7235
|
+
}
|
|
7190
7236
|
db.prepare(`
|
|
7191
7237
|
DELETE FROM check_attempt_details
|
|
7192
7238
|
WHERE execution_run_id IN (SELECT id FROM execution_runs WHERE project_id = ?)
|
|
@@ -8131,6 +8177,98 @@ app.post('/projects/:id/check-batches', async (req, res) => {
|
|
|
8131
8177
|
return res.status(409).json({ error: getErrorMessage(error) });
|
|
8132
8178
|
}
|
|
8133
8179
|
});
|
|
8180
|
+
app.post('/projects/:id/check-repairs', async (req, res) => {
|
|
8181
|
+
const project = getProjectById(req.params.id);
|
|
8182
|
+
if (!project) {
|
|
8183
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8184
|
+
}
|
|
8185
|
+
const body = req.body && typeof req.body === 'object' && !Array.isArray(req.body)
|
|
8186
|
+
? req.body
|
|
8187
|
+
: null;
|
|
8188
|
+
if (!body || Object.keys(body).some(key => !['diagnostic_ids', 'raw_output_attempt_run_ids', 'ticket_id'].includes(key))) {
|
|
8189
|
+
return res.status(400).json({ error: 'Only diagnostic_ids, raw_output_attempt_run_ids, and optional ticket_id are accepted' });
|
|
8190
|
+
}
|
|
8191
|
+
const diagnosticIds = body.diagnostic_ids === undefined
|
|
8192
|
+
? []
|
|
8193
|
+
: Array.isArray(body.diagnostic_ids)
|
|
8194
|
+
&& body.diagnostic_ids.length <= 32
|
|
8195
|
+
&& body.diagnostic_ids.every(id => typeof id === 'string' && id.trim().length > 0 && id.trim().length <= 100)
|
|
8196
|
+
? body.diagnostic_ids.map(id => id.trim())
|
|
8197
|
+
: null;
|
|
8198
|
+
const rawOutputAttemptRunIds = body.raw_output_attempt_run_ids === undefined
|
|
8199
|
+
? []
|
|
8200
|
+
: Array.isArray(body.raw_output_attempt_run_ids)
|
|
8201
|
+
&& body.raw_output_attempt_run_ids.length <= 8
|
|
8202
|
+
&& body.raw_output_attempt_run_ids.every(id => typeof id === 'string' && id.trim().length > 0 && id.trim().length <= 100)
|
|
8203
|
+
? body.raw_output_attempt_run_ids.map(id => id.trim())
|
|
8204
|
+
: null;
|
|
8205
|
+
if (!diagnosticIds
|
|
8206
|
+
|| diagnosticIds.length !== new Set(diagnosticIds).size
|
|
8207
|
+
|| !rawOutputAttemptRunIds
|
|
8208
|
+
|| rawOutputAttemptRunIds.length !== new Set(rawOutputAttemptRunIds).size
|
|
8209
|
+
|| diagnosticIds.length + rawOutputAttemptRunIds.length === 0) {
|
|
8210
|
+
return res.status(400).json({ error: 'At least one unique diagnostic_ids or raw_output_attempt_run_ids evidence reference is required' });
|
|
8211
|
+
}
|
|
8212
|
+
if (body.ticket_id !== undefined && (typeof body.ticket_id !== 'string'
|
|
8213
|
+
|| body.ticket_id.trim().length === 0
|
|
8214
|
+
|| body.ticket_id.trim().length > 100)) {
|
|
8215
|
+
return res.status(400).json({ error: 'ticket_id must be a non-empty opaque ticket ID' });
|
|
8216
|
+
}
|
|
8217
|
+
try {
|
|
8218
|
+
return res.status(202).json(await startCheckRepair({
|
|
8219
|
+
projectId: project.id,
|
|
8220
|
+
diagnosticIds,
|
|
8221
|
+
rawOutputAttemptRunIds,
|
|
8222
|
+
ticketId: typeof body.ticket_id === 'string' ? body.ticket_id.trim() : null,
|
|
8223
|
+
io,
|
|
8224
|
+
}));
|
|
8225
|
+
}
|
|
8226
|
+
catch (error) {
|
|
8227
|
+
if (error instanceof CheckRepairError) {
|
|
8228
|
+
return res.status(error.status).json({ error: error.message });
|
|
8229
|
+
}
|
|
8230
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
8231
|
+
}
|
|
8232
|
+
});
|
|
8233
|
+
app.get('/projects/:id/check-repairs/:repairRunId', (req, res) => {
|
|
8234
|
+
const project = getProjectById(req.params.id);
|
|
8235
|
+
if (!project) {
|
|
8236
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8237
|
+
}
|
|
8238
|
+
const repair = getCheckRepairDetails(project.id, req.params.repairRunId);
|
|
8239
|
+
return repair ? res.json(repair) : res.status(404).json({ error: 'Check repair not found' });
|
|
8240
|
+
});
|
|
8241
|
+
app.post('/projects/:id/check-repairs/:repairRunId/stop', async (req, res) => {
|
|
8242
|
+
if (!getProjectById(req.params.id)) {
|
|
8243
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8244
|
+
}
|
|
8245
|
+
try {
|
|
8246
|
+
return res.json(await stopCheckRepair(req.params.id, req.params.repairRunId, io));
|
|
8247
|
+
}
|
|
8248
|
+
catch (error) {
|
|
8249
|
+
if (error instanceof CheckRepairError) {
|
|
8250
|
+
return res.status(error.status).json({ error: error.message });
|
|
8251
|
+
}
|
|
8252
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
8253
|
+
}
|
|
8254
|
+
});
|
|
8255
|
+
app.post('/projects/:id/check-repairs/:repairRunId/retry', async (req, res) => {
|
|
8256
|
+
if (!getProjectById(req.params.id)) {
|
|
8257
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8258
|
+
}
|
|
8259
|
+
if (req.body && typeof req.body === 'object' && Object.keys(req.body).length > 0) {
|
|
8260
|
+
return res.status(400).json({ error: 'Check repair retries do not accept client-supplied repair context' });
|
|
8261
|
+
}
|
|
8262
|
+
try {
|
|
8263
|
+
return res.status(202).json(await retryCheckRepair(req.params.id, req.params.repairRunId, io));
|
|
8264
|
+
}
|
|
8265
|
+
catch (error) {
|
|
8266
|
+
if (error instanceof CheckRepairError) {
|
|
8267
|
+
return res.status(error.status).json({ error: error.message });
|
|
8268
|
+
}
|
|
8269
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
8270
|
+
}
|
|
8271
|
+
});
|
|
8134
8272
|
const parseCheckDefinitionIds = (value) => {
|
|
8135
8273
|
if (!Array.isArray(value) || value.length === 0 || value.some(id => typeof id !== 'string' || id.trim() === '')) {
|
|
8136
8274
|
return null;
|
|
@@ -8230,12 +8368,47 @@ app.get('/projects/:id/check-batches/:batchRunId', async (req, res) => {
|
|
|
8230
8368
|
attempts: await hydrateCheckAttemptCurrency(project, batch, getCheckBatchAttempts(req.params.id, req.params.batchRunId)),
|
|
8231
8369
|
});
|
|
8232
8370
|
});
|
|
8233
|
-
app.get('/projects/:id/check-
|
|
8371
|
+
app.get('/projects/:id/check-batches/:batchRunId/diagnostics', async (req, res) => {
|
|
8372
|
+
const project = getProjectById(req.params.id);
|
|
8373
|
+
if (!project) {
|
|
8374
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8375
|
+
}
|
|
8376
|
+
const diagnostics = getCheckBatchDiagnosticResponse(project.id, req.params.batchRunId);
|
|
8377
|
+
if (!diagnostics) {
|
|
8378
|
+
return res.status(404).json({ error: 'Check batch not found' });
|
|
8379
|
+
}
|
|
8380
|
+
return res.json({
|
|
8381
|
+
...diagnostics,
|
|
8382
|
+
repair: await getCheckRepairCandidateState(project.id, req.params.batchRunId),
|
|
8383
|
+
latest_repair: getLatestCheckRepairForBatch(project.id, req.params.batchRunId),
|
|
8384
|
+
});
|
|
8385
|
+
});
|
|
8386
|
+
app.get('/projects/:id/check-attempts/:attemptRunId/diagnostics', (req, res) => {
|
|
8387
|
+
const project = getProjectById(req.params.id);
|
|
8388
|
+
if (!project) {
|
|
8389
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8390
|
+
}
|
|
8391
|
+
const diagnostics = getCheckAttemptDiagnosticResponse(project.id, req.params.attemptRunId);
|
|
8392
|
+
if (!diagnostics) {
|
|
8393
|
+
return res.status(404).json({ error: 'Check attempt diagnostics not found' });
|
|
8394
|
+
}
|
|
8395
|
+
return res.json(diagnostics);
|
|
8396
|
+
});
|
|
8397
|
+
app.get('/projects/:id/check-gate-evaluations/:evaluationId', async (req, res) => {
|
|
8398
|
+
const project = getProjectById(req.params.id);
|
|
8399
|
+
if (!project) {
|
|
8400
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8401
|
+
}
|
|
8234
8402
|
const evaluation = getCheckGateEvaluation(req.params.id, req.params.evaluationId);
|
|
8235
8403
|
if (!evaluation) {
|
|
8236
8404
|
return res.status(404).json({ error: 'Check gate evaluation not found' });
|
|
8237
8405
|
}
|
|
8238
|
-
|
|
8406
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, evaluation.target.targetKind, evaluation.target.targetBundleId);
|
|
8407
|
+
const currentWorkspaceIdentity = 'error' in workspace
|
|
8408
|
+
? null
|
|
8409
|
+
: await captureCheckWorkspaceIdentity(workspace.cwd);
|
|
8410
|
+
const currentPolicyResolution = resolveRequiredCheckPolicy(project, { boundaryKind: evaluation.boundaryKind });
|
|
8411
|
+
return res.json(hydrateCheckGatePresentApplicability(evaluation, currentWorkspaceIdentity, currentPolicyResolution));
|
|
8239
8412
|
});
|
|
8240
8413
|
app.post('/projects/:id/check-batches/:batchRunId/stop', async (req, res) => {
|
|
8241
8414
|
if (!getCheckBatchDetails(req.params.id, req.params.batchRunId)) {
|