@straiffi/archon 1.7.10 → 1.8.0
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-EVy76nes.js} +1 -1
- package/dist/client/assets/{GitHubActionsDialog-0YtWvbFk.js → GitHubActionsDialog-D53aA0Fv.js} +1 -1
- package/dist/client/assets/VerificationDialog-Dm5DcY8F.js +4 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-LLMyOR-6.js → architectureDiagram-3BPJPVTR-CmMRkze6.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-aa1HitBJ.js → blockDiagram-GPEHLZMM-BgLsRgxM.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-BpSt5sdj.js → c4Diagram-AAUBKEIU-C3WJl13c.js} +1 -1
- package/dist/client/assets/channel-xqaERdN6.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-D8pf3RNr.js → chunk-2J33WTMH-BfH4_PkQ.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-_yx1N2iT.js → chunk-3OPIFGDE-CkVGPLM4.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-CYURGSbL.js → chunk-5ZQYHXKU-CDTwT753.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-Ci2Mu1zz.js → chunk-727SXJPM-CKEhBrr_.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-CqVgrKze.js → chunk-AQP2D5EJ-D5QPK_k7.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-CT8aPk-o.js → chunk-CSCIHK7Q-BU4Ysr_2.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-yM1i0J-p.js → chunk-KSCS5N6A-B9lHLGg1.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-BKMGr31Z.js → chunk-L5ZTLDWV-BrNsmxtD.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-A7oUDETP.js → chunk-LZXEDZCA-CVgZc1nv.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-Cagc-XAG.js → chunk-ND2GUHAM-DWWdi42a.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-pe8tKLWI.js → chunk-NZK2D7GU-46-aRMvO.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-BahwghWW.js → chunk-O5CBEL6O-BW6wHmzD.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-BfSAFufr.js → chunk-WU5MYG2G-Wfk15Qtf.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BdwFrsZE.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DxAuXA1L.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-DCma6-hS.js → dagre-BM42HDAG-CZ_BDahz.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-DUeggZrO.js → diagram-2AECGRRQ-CQKXQvjO.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-CdjOKGtl.js → diagram-5GNKFQAL-Dgsh82oA.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-CYjXhgh_.js → diagram-KO2AKTUF-DUwmm3sV.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DYBCh4cm.js → diagram-LMA3HP47-DLGz0g10.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-D3HPQt3z.js → diagram-OG6HWLK6-CjS6v8Zz.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-mG8Yztg-.js → erDiagram-TEJ5UH35-DHaCFHn9.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-4FvjuoSZ.js → flowDiagram-I6XJVG4X-E4LSVqs6.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-ByCQI8ft.js → ganttDiagram-6RSMTGT7-BWywa_xn.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CzEUA7nB.js → gitGraphDiagram-PVQCEYII-BK4kWKuB.js} +1 -1
- package/dist/client/assets/index-CS2arNNP.js +274 -0
- package/dist/client/assets/index-Cb_ZVYaw.css +2 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-Q9CqnXO1.js → infoDiagram-5YYISTIA-BX7HzDWX.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CO8BToGf.js → ishikawaDiagram-YF4QCWOH-CEnWCNes.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-DtOyWvDJ.js → journeyDiagram-JHISSGLW-MQPDgPxD.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-DIn9bYpk.js → kanban-definition-UN3LZRKU-PwVLKAnN.js} +1 -1
- package/dist/client/assets/{line-H-k0M9_s.js → line-BJMnG1sl.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-CZOUS5zK.js → mermaid-parser.core-Bwv8qN83.js} +1 -1
- package/dist/client/assets/{mermaid.core-Ck71Qr7h.js → mermaid.core-DpFOXZmb.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-BRFEcJw_.js → mindmap-definition-RKZ34NQL-Dt-9JT_z.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-CwihXXcJ.js → pieDiagram-4H26LBE5-aD3cg6m3.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-moiRw4Cm.js → quadrantDiagram-W4KKPZXB-DUh4hgmW.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-tshvM_vV.js → requirementDiagram-4Y6WPE33-DOgz-iLT.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-rtKIuBEp.js → sankeyDiagram-5OEKKPKP-BqpDXW6-.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-lkTCo4wE.js → sequenceDiagram-3UESZ5HK-BM0Gvqia.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CFQhVUSd.js → stateDiagram-AJRCARHV-XZMyDtEy.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-C0Qe8FiL.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-BmlQ7Wiy.js → timeline-definition-PNZ67QCA-gqiDwxVu.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-CZyZepcT.js → vennDiagram-CIIHVFJN-MxeqqqyG.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-C2b52gni.js → wardleyDiagram-YWT4CUSO-pMgGO6cB.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-FqnROUHq.js → xychartDiagram-2RQKCTM6-_VK_NZDn.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +452 -7
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +309 -195
- 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/chatTicketProposals.js +2 -1
- package/dist/server/lib/chatTicketProposals.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 +115 -21
- package/dist/server/lib/checkRunner.js.map +1 -1
- package/dist/server/lib/checkRuns.js +37 -3
- 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 +5 -2
- package/dist/server/lib/executionRuns.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/projectMemorySuggestions.js +2 -1
- package/dist/server/lib/projectMemorySuggestions.js.map +1 -1
- package/dist/server/lib/projects.js +1 -26
- 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 +24 -9
- package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
- package/dist/server/lib/tickets.js +9 -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 +34 -12
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/chat.js +4 -3
- package/dist/server/workers/chat.js.map +1 -1
- package/dist/server/workers/followUp.js +60 -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
|
@@ -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';
|
|
@@ -380,11 +383,19 @@ const buildExecutionRunDetailResponse = async (project, run) => {
|
|
|
380
383
|
retry_eligibility: retryEligibility,
|
|
381
384
|
action_kind: null,
|
|
382
385
|
}
|
|
383
|
-
: run.kind === '
|
|
384
|
-
? {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
:
|
|
386
|
+
: run.kind === 'check_repair'
|
|
387
|
+
? {
|
|
388
|
+
kind: 'check_repair',
|
|
389
|
+
available: true,
|
|
390
|
+
check_repair: getCheckRepairDetails(project.id, run.id),
|
|
391
|
+
retry_eligibility: retryEligibility,
|
|
392
|
+
action_kind: null,
|
|
393
|
+
}
|
|
394
|
+
: run.kind === 'github_actions'
|
|
395
|
+
? { ...serializeGitHubActionsExecutionDetail(getGitHubActionsExecutionDetails(project.id, run.id)), retry_eligibility: retryEligibility }
|
|
396
|
+
: ['automation_commit', 'automation_push', 'automation_pull_request'].includes(run.kind)
|
|
397
|
+
? { kind: run.kind, available: true, automation_stage: getAutomationStageDetails(project.id, run.id), retry_eligibility: retryEligibility }
|
|
398
|
+
: { kind: run.kind, available: true, retry_eligibility: retryEligibility };
|
|
388
399
|
const artifacts = loadExecutionArtifactSummariesForRuns(project.id, [run.id], MAX_ARTIFACTS_PER_RUN).get(run.id)
|
|
389
400
|
?? { artifacts: [], artifact_count: 0 };
|
|
390
401
|
return {
|
|
@@ -444,7 +455,14 @@ const getParkTicketError = (ticket) => {
|
|
|
444
455
|
const getBundlePlanBlockerError = () => {
|
|
445
456
|
return 'AI planning is blocked because this bundle worktree is currently being modified.';
|
|
446
457
|
};
|
|
458
|
+
const hasActiveBundleCheckWork = (projectId, bundleId) => Boolean(getActiveBundleRepairBlocker(bundleId)
|
|
459
|
+
|| findActiveCheckBatchForTarget(projectId, 'bundle', bundleId));
|
|
447
460
|
const deleteTicketRecord = async (ticket) => {
|
|
461
|
+
if (ticket.project_id
|
|
462
|
+
&& ticket.worktree_bundle_id
|
|
463
|
+
&& hasActiveBundleCheckWork(ticket.project_id, ticket.worktree_bundle_id)) {
|
|
464
|
+
return { ok: false, status: 409, error: 'Cannot delete a ticket while its bundle has active check work' };
|
|
465
|
+
}
|
|
448
466
|
if (ticket.agent_status === 'running'
|
|
449
467
|
|| (ticket.project_id && hasActiveBuildExecutionRunForTicket(ticket.project_id, ticket.id))) {
|
|
450
468
|
return { ok: false, status: 409, error: 'Cannot delete a ticket while the agent is running' };
|
|
@@ -812,6 +830,9 @@ const cleanupBundleIfEmpty = (bundleId, project) => {
|
|
|
812
830
|
if (isProjectRootBundle(bundle)) {
|
|
813
831
|
return;
|
|
814
832
|
}
|
|
833
|
+
if (hasActiveBundleCheckWork(project.id, bundle.id)) {
|
|
834
|
+
throw new HttpError(409, 'Cannot delete a bundle while check work is active');
|
|
835
|
+
}
|
|
815
836
|
try {
|
|
816
837
|
deleteWorktree(bundle.branch, project);
|
|
817
838
|
}
|
|
@@ -1464,10 +1485,7 @@ const serializeBundleRow = (projectId, bundle, openQuestions = bundle ? loadBuil
|
|
|
1464
1485
|
const hasPreparedWorktree = bundle.kind === 'project_root'
|
|
1465
1486
|
? true
|
|
1466
1487
|
: Boolean(project && resolveExistingWorktreePath(bundle.branch, project));
|
|
1467
|
-
const
|
|
1468
|
-
? { value: null }
|
|
1469
|
-
: parseBundleRequiredCheckPolicyOverride(bundle.required_check_policy_override);
|
|
1470
|
-
const effectivePolicy = resolveEffectiveRequiredCheckPolicyMembership(project, bundle);
|
|
1488
|
+
const effectivePolicy = resolveEffectiveRequiredCheckPolicyMembership(project);
|
|
1471
1489
|
return {
|
|
1472
1490
|
...bundle,
|
|
1473
1491
|
has_prepared_worktree: hasPreparedWorktree,
|
|
@@ -1475,7 +1493,6 @@ const serializeBundleRow = (projectId, bundle, openQuestions = bundle ? loadBuil
|
|
|
1475
1493
|
? serializeBundlePullRequest(getBundlePullRequestRecord(bundle.id, projectId))
|
|
1476
1494
|
: null,
|
|
1477
1495
|
automation_config: resolveAutomationConfig(bundle, project),
|
|
1478
|
-
required_check_policy_override: 'error' in parsedOverride ? null : parsedOverride.value,
|
|
1479
1496
|
required_check_policy: 'error' in effectivePolicy
|
|
1480
1497
|
? undefined
|
|
1481
1498
|
: {
|
|
@@ -1483,7 +1500,6 @@ const serializeBundleRow = (projectId, bundle, openQuestions = bundle ? loadBuil
|
|
|
1483
1500
|
required_check_definition_ids: effectivePolicy.required_check_definition_ids,
|
|
1484
1501
|
pre_review_check_definition_ids: effectivePolicy.pre_review_check_definition_ids,
|
|
1485
1502
|
},
|
|
1486
|
-
required_check_policy_error: 'error' in parsedOverride ? parsedOverride.error : ('error' in effectivePolicy ? effectivePolicy.error : null),
|
|
1487
1503
|
open_questions: openQuestions,
|
|
1488
1504
|
build_open_question_batch_status: getLatestBuildOpenQuestionBatchStatus(projectId, bundle.id),
|
|
1489
1505
|
};
|
|
@@ -3049,11 +3065,14 @@ app.post('/projects/:id/spec-sessions/:sessionId/snapshots/:snapshotId/create-ti
|
|
|
3049
3065
|
? createAutoBundleForName(bundleName, project.id)
|
|
3050
3066
|
: createAutoPlanningBundle(project.id);
|
|
3051
3067
|
const ticketsForMaterialization = snapshot.tickets.map(ticket => {
|
|
3052
|
-
const
|
|
3068
|
+
const appendices = [
|
|
3069
|
+
buildVisualizationAppendix(ticket.relevant_visualizations ?? [], snapshot),
|
|
3070
|
+
buildImplementationResourcesAppendix(snapshot.resource_urls),
|
|
3071
|
+
].filter(Boolean);
|
|
3053
3072
|
return {
|
|
3054
3073
|
key: ticket.key,
|
|
3055
3074
|
title: ticket.title,
|
|
3056
|
-
description:
|
|
3075
|
+
description: appendices.length > 0 ? `${ticket.description}\n\n${appendices.join('\n\n')}` : ticket.description,
|
|
3057
3076
|
};
|
|
3058
3077
|
});
|
|
3059
3078
|
const { createdTickets, createdDependencies } = materializeTicketsWithDependencies({
|
|
@@ -3719,15 +3738,15 @@ app.delete('/bundles/:id', (req, res) => {
|
|
|
3719
3738
|
if (isProjectRootBundle(bundle)) {
|
|
3720
3739
|
return res.status(409).json({ error: 'Project root cannot be deleted' });
|
|
3721
3740
|
}
|
|
3741
|
+
if (hasActiveBundleCheckWork(result.project.id, bundle.id)) {
|
|
3742
|
+
return res.status(409).json({ error: 'Cannot delete a bundle while check work is active' });
|
|
3743
|
+
}
|
|
3722
3744
|
if (countBundleTickets(bundle.id, result.project.id) > 0) {
|
|
3723
3745
|
return res.status(409).json({ error: 'Cannot delete a bundle that is still used by tickets' });
|
|
3724
3746
|
}
|
|
3725
3747
|
if (result.project.active_target_kind === 'bundle' && result.project.active_target_bundle_id === bundle.id) {
|
|
3726
3748
|
return res.status(409).json({ error: 'Cannot delete the currently selected target bundle' });
|
|
3727
3749
|
}
|
|
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
3750
|
const affectedChatSessionIds = listChatSessions(result.project.id)
|
|
3732
3751
|
.filter(chatSession => chatSession.target_bundle_id === bundle.id)
|
|
3733
3752
|
.map(chatSession => chatSession.id);
|
|
@@ -3758,14 +3777,14 @@ app.patch('/bundles/:id', (req, res) => {
|
|
|
3758
3777
|
return res.status(404).json({ error: 'Bundle not found' });
|
|
3759
3778
|
}
|
|
3760
3779
|
const hasNameUpdate = hasOwn(req.body, 'name');
|
|
3761
|
-
|
|
3780
|
+
if (hasOwn(req.body, 'required_check_policy_override')) {
|
|
3781
|
+
return res.status(400).json({ error: 'Bundle required-check policy overrides are no longer supported; configure required checks on the project' });
|
|
3782
|
+
}
|
|
3762
3783
|
if (isProjectRootBundle(bundle)) {
|
|
3763
3784
|
return res.status(409).json({
|
|
3764
3785
|
error: hasNameUpdate
|
|
3765
3786
|
? 'Project root bundles cannot be renamed'
|
|
3766
|
-
:
|
|
3767
|
-
? 'Project root bundles cannot override required-check policy'
|
|
3768
|
-
: 'Project root bundle automation cannot be overridden',
|
|
3787
|
+
: 'Project root bundle automation cannot be overridden',
|
|
3769
3788
|
});
|
|
3770
3789
|
}
|
|
3771
3790
|
let nextName = bundle.name;
|
|
@@ -3838,39 +3857,6 @@ app.patch('/bundles/:id', (req, res) => {
|
|
|
3838
3857
|
return res.status(400).json({ error: 'automation_pause_on_open_questions must be a boolean' });
|
|
3839
3858
|
}
|
|
3840
3859
|
}
|
|
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
3860
|
let updated = null;
|
|
3875
3861
|
try {
|
|
3876
3862
|
updated = db.transaction(() => {
|
|
@@ -3888,7 +3874,6 @@ app.patch('/bundles/:id', (req, res) => {
|
|
|
3888
3874
|
automationDepth,
|
|
3889
3875
|
automationReviewFixEnabled,
|
|
3890
3876
|
automationPauseOnOpenQuestions,
|
|
3891
|
-
requiredCheckPolicyOverride,
|
|
3892
3877
|
});
|
|
3893
3878
|
})();
|
|
3894
3879
|
}
|
|
@@ -3928,7 +3913,7 @@ app.post('/bundles/:id/review', (req, res) => {
|
|
|
3928
3913
|
}
|
|
3929
3914
|
return res.json(reviewResult.details);
|
|
3930
3915
|
});
|
|
3931
|
-
app.post('/bundles/:id/build-open-questions/resolve', (req, res) => {
|
|
3916
|
+
app.post('/bundles/:id/build-open-questions/resolve', async (req, res) => {
|
|
3932
3917
|
const result = getRequiredProject(req);
|
|
3933
3918
|
if ('error' in result) {
|
|
3934
3919
|
return res.status(400).json({ error: result.error });
|
|
@@ -3971,54 +3956,70 @@ app.post('/bundles/:id/build-open-questions/resolve', (req, res) => {
|
|
|
3971
3956
|
}
|
|
3972
3957
|
if (accepted.kind === 'dismissed') {
|
|
3973
3958
|
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) {
|
|
3959
|
+
if (!depthAtLeast(automationConfig.depth, 'auto_review')) {
|
|
3984
3960
|
completeAutomationProgression({
|
|
3985
3961
|
projectId: result.project.id,
|
|
3986
3962
|
bundleId: bundle.id,
|
|
3987
3963
|
progressionRunId: accepted.progressionRunId,
|
|
3988
3964
|
broadcaster: io,
|
|
3989
3965
|
});
|
|
3966
|
+
for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
|
|
3967
|
+
io.emit('ticket:updated', ticket);
|
|
3968
|
+
}
|
|
3969
|
+
return res.json(serializeBundleDetails(result.project, bundle));
|
|
3990
3970
|
}
|
|
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));
|
|
3971
|
+
const ticketsBeforeReview = listDetailedBundleTickets(result.project.id, bundle.id);
|
|
3972
|
+
if (!areBundleTicketsReadyForAutomaticReview(ticketsBeforeReview, bundle.id)) {
|
|
3973
|
+
completeAutomationProgression({
|
|
3974
|
+
projectId: result.project.id,
|
|
3975
|
+
bundleId: bundle.id,
|
|
3976
|
+
progressionRunId: accepted.progressionRunId,
|
|
3977
|
+
broadcaster: io,
|
|
3978
|
+
});
|
|
3979
|
+
for (const ticket of ticketsBeforeReview) {
|
|
3980
|
+
io.emit('ticket:updated', ticket);
|
|
4006
3981
|
}
|
|
3982
|
+
return res.json(serializeBundleDetails(result.project, bundle));
|
|
3983
|
+
}
|
|
3984
|
+
let reviewStartFailureStatus = null;
|
|
3985
|
+
let reviewStartFailureError = null;
|
|
3986
|
+
const reviewStarted = await runAutomationReviewGateOrPause({
|
|
3987
|
+
projectId: result.project.id,
|
|
3988
|
+
bundleId: bundle.id,
|
|
3989
|
+
progressionRunId: accepted.progressionRunId,
|
|
3990
|
+
broadcaster: io,
|
|
3991
|
+
pausedStageRunId: null,
|
|
3992
|
+
initiation: {
|
|
3993
|
+
workflowContext: validateWorkflowContext({ build_open_question_batch_id: accepted.batchId }),
|
|
3994
|
+
},
|
|
3995
|
+
onReviewStartFailure: failure => {
|
|
3996
|
+
reviewStartFailureStatus = failure.status;
|
|
3997
|
+
reviewStartFailureError = failure.error;
|
|
3998
|
+
},
|
|
3999
|
+
});
|
|
4000
|
+
if (reviewStartFailureStatus !== null && reviewStartFailureError !== null) {
|
|
4007
4001
|
waitAutomationProgression({
|
|
4008
4002
|
projectId: result.project.id,
|
|
4009
4003
|
bundleId: bundle.id,
|
|
4010
4004
|
progressionRunId: accepted.progressionRunId,
|
|
4011
4005
|
pauseCode: 'review',
|
|
4012
|
-
pauseReason:
|
|
4006
|
+
pauseReason: reviewStartFailureError,
|
|
4013
4007
|
reasonCode: 'stage_failed',
|
|
4014
4008
|
pausedStageRunId: null,
|
|
4015
4009
|
broadcaster: io,
|
|
4016
4010
|
});
|
|
4017
|
-
return res.status(
|
|
4011
|
+
return res.status(reviewStartFailureStatus).json({ error: reviewStartFailureError });
|
|
4012
|
+
}
|
|
4013
|
+
const updatedTickets = listDetailedBundleTickets(result.project.id, bundle.id);
|
|
4014
|
+
for (const ticket of updatedTickets) {
|
|
4015
|
+
io.emit('ticket:updated', ticket);
|
|
4018
4016
|
}
|
|
4017
|
+
const reviewOwner = reviewStarted
|
|
4018
|
+
? updatedTickets.find(ticket => ticket.state === 'review' && ticket.parked_at === null)
|
|
4019
|
+
: null;
|
|
4019
4020
|
return res.json(serializeBundleDetails(result.project, bundle, {
|
|
4020
|
-
review_owner_ticket_id:
|
|
4021
|
-
target_ticket_id:
|
|
4021
|
+
review_owner_ticket_id: reviewOwner?.id,
|
|
4022
|
+
target_ticket_id: reviewOwner?.id,
|
|
4022
4023
|
}));
|
|
4023
4024
|
}
|
|
4024
4025
|
for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
|
|
@@ -4125,7 +4126,7 @@ app.post('/bundles/:id/follow-up', (req, res) => {
|
|
|
4125
4126
|
return res.status(response.status).json(response.value);
|
|
4126
4127
|
});
|
|
4127
4128
|
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']);
|
|
4129
|
+
const AUTOMATION_WORKER_PAUSE_KINDS = new Set(['build', 'review', 'build_follow_up', 'automation_review_fix', 'check_repair', 'check_batch']);
|
|
4129
4130
|
app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
4130
4131
|
const result = getRequiredProject(req);
|
|
4131
4132
|
if ('error' in result) {
|
|
@@ -4208,7 +4209,11 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4208
4209
|
});
|
|
4209
4210
|
if (checkpointLineage) {
|
|
4210
4211
|
const owner = getTicket(decision.owner_ticket_id);
|
|
4211
|
-
if (!owner
|
|
4212
|
+
if (!owner
|
|
4213
|
+
|| owner.id !== checkpointLineage.ticket.id
|
|
4214
|
+
|| owner.state !== 'build'
|
|
4215
|
+
|| owner.parked_at !== null
|
|
4216
|
+
|| !isTicketBuildComplete(owner)) {
|
|
4212
4217
|
return res.status(409).json({ error: 'Checkpoint progression owner is no longer the active Build ticket' });
|
|
4213
4218
|
}
|
|
4214
4219
|
let retryOfRunId;
|
|
@@ -4463,6 +4468,7 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4463
4468
|
pauseReason: bundleState.automation_paused_reason ?? 'Automation paused',
|
|
4464
4469
|
reasonCode: bundleState.automation_pause_reason_code ?? 'manual_transition',
|
|
4465
4470
|
pausedStageRunId: bundleState.automation_paused_stage_run_id,
|
|
4471
|
+
pausedBoundaryPurpose: bundleState.automation_paused_boundary_purpose,
|
|
4466
4472
|
broadcaster: io,
|
|
4467
4473
|
});
|
|
4468
4474
|
};
|
|
@@ -4500,7 +4506,62 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4500
4506
|
}
|
|
4501
4507
|
return res.json(serializeBundleDetails(result.project, bundle));
|
|
4502
4508
|
}
|
|
4503
|
-
if (
|
|
4509
|
+
if (decision.outcome === 'complete_manual_boundary') {
|
|
4510
|
+
if (!revalidateDecision()) {
|
|
4511
|
+
return res.status(409).json({ error: 'Bundle automation state changed before Resume could start' });
|
|
4512
|
+
}
|
|
4513
|
+
completeAutomationProgression({
|
|
4514
|
+
projectId: result.project.id,
|
|
4515
|
+
bundleId: bundle.id,
|
|
4516
|
+
progressionRunId,
|
|
4517
|
+
domainStatus: 'manual_boundary_reached',
|
|
4518
|
+
broadcaster: io,
|
|
4519
|
+
});
|
|
4520
|
+
return res.json(serializeBundleDetails(result.project, bundle));
|
|
4521
|
+
}
|
|
4522
|
+
if ((decision.outcome === 'start_review' || decision.outcome === 'rerun_review_checks')
|
|
4523
|
+
&& owner.parked_at === null
|
|
4524
|
+
&& isTicketBuildComplete(owner)
|
|
4525
|
+
&& (owner.state === 'build' || (decision.outcome === 'rerun_review_checks' && owner.state === 'review'))) {
|
|
4526
|
+
if (!revalidateDecision()) {
|
|
4527
|
+
return res.status(409).json({ error: 'Bundle automation state changed before Resume could start' });
|
|
4528
|
+
}
|
|
4529
|
+
const progression = getAutomationProgression(result.project.id, bundle.id);
|
|
4530
|
+
if (!progression || progression.id !== progressionRunId) {
|
|
4531
|
+
return res.status(409).json({ error: 'Automation progression ownership is invalid' });
|
|
4532
|
+
}
|
|
4533
|
+
const workflowContext = validateWorkflowContext(progression.metadata);
|
|
4534
|
+
let reviewStartFailureStatus = null;
|
|
4535
|
+
let reviewStartFailureError = null;
|
|
4536
|
+
try {
|
|
4537
|
+
runAutomationProgression({
|
|
4538
|
+
projectId: result.project.id,
|
|
4539
|
+
bundleId: bundle.id,
|
|
4540
|
+
progressionRunId,
|
|
4541
|
+
broadcaster: io,
|
|
4542
|
+
});
|
|
4543
|
+
await runAutomationReviewGateOrPause({
|
|
4544
|
+
projectId: result.project.id,
|
|
4545
|
+
bundleId: bundle.id,
|
|
4546
|
+
progressionRunId,
|
|
4547
|
+
broadcaster: io,
|
|
4548
|
+
pausedStageRunId: bundleState.automation_paused_stage_run_id,
|
|
4549
|
+
initiation: { workflowContext },
|
|
4550
|
+
onReviewStartFailure: failure => {
|
|
4551
|
+
reviewStartFailureStatus = failure.status;
|
|
4552
|
+
reviewStartFailureError = failure.error;
|
|
4553
|
+
restoreWaitingProgression();
|
|
4554
|
+
},
|
|
4555
|
+
});
|
|
4556
|
+
}
|
|
4557
|
+
catch (error) {
|
|
4558
|
+
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
4559
|
+
}
|
|
4560
|
+
if (reviewStartFailureStatus !== null && reviewStartFailureError !== null) {
|
|
4561
|
+
return res.status(reviewStartFailureStatus).json({ error: reviewStartFailureError });
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
else if (owner.state === 'review' && owner.agent_status !== 'running') {
|
|
4504
4565
|
const iteration = bundleState.review_iteration_count ?? 0;
|
|
4505
4566
|
if (decision.outcome === 'start_review_fix') {
|
|
4506
4567
|
let reviewFixRunId = null;
|
|
@@ -4610,7 +4671,7 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4610
4671
|
broadcaster: io,
|
|
4611
4672
|
});
|
|
4612
4673
|
const checkGateResume = decision.outcome === 'rerun_required_checks'
|
|
4613
|
-
? resolveAutomationCheckGateResume(result.project.id, progressionRunId)
|
|
4674
|
+
? resolveAutomationCheckGateResume(result.project.id, progressionRunId, owner.bundle?.kind === 'project_root' ? project.repo_path : resolveWorktreePath(bundle.branch, project))
|
|
4614
4675
|
: undefined;
|
|
4615
4676
|
await runAutomationCommitChain(owner, project, owner.bundle ?? null, automationConfig, io, progressionRunId, checkGateResume);
|
|
4616
4677
|
}
|
|
@@ -4635,45 +4696,6 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4635
4696
|
return res.status(409).json({ error: 'The selected Resume outcome no longer matches the bundle state' });
|
|
4636
4697
|
}
|
|
4637
4698
|
}
|
|
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
4699
|
else {
|
|
4678
4700
|
return res.status(409).json({ error: 'Ticket is not in a resumable state' });
|
|
4679
4701
|
}
|
|
@@ -4704,72 +4726,31 @@ app.post('/bundles/:id/dismiss-automation-pause', async (req, res) => {
|
|
|
4704
4726
|
&& listBuildOpenQuestionsForBundle(result.project.id, bundle.id).length > 0) {
|
|
4705
4727
|
return res.status(409).json({ error: 'Resolve or dismiss all Build open questions before dismissing this pause' });
|
|
4706
4728
|
}
|
|
4707
|
-
|
|
4708
|
-
|
|
4729
|
+
const progressionRunId = bundleState.automation_progression_run_id;
|
|
4730
|
+
const checkpointLineage = progressionRunId
|
|
4731
|
+
? resolveBuildTicketCommitCheckpointLineage({
|
|
4732
|
+
projectId: result.project.id,
|
|
4733
|
+
bundleId: bundle.id,
|
|
4734
|
+
progressionRunId,
|
|
4735
|
+
})
|
|
4736
|
+
: null;
|
|
4737
|
+
if (progressionRunId) {
|
|
4709
4738
|
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' });
|
|
4739
|
+
await stopAutomationProgression({
|
|
4740
|
+
projectId: result.project.id,
|
|
4741
|
+
bundleId: bundle.id,
|
|
4742
|
+
broadcaster: io,
|
|
4743
|
+
stopLocalWorker: worker => (worker.kind === 'review' ? stopReviewAgent(worker.ticket_id) : stopBuildAgent(worker.ticket_id)),
|
|
4744
|
+
});
|
|
4745
4745
|
}
|
|
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
|
-
}
|
|
4746
|
+
catch {
|
|
4747
|
+
// Explicit abandonment also repairs a stale progression pointer.
|
|
4766
4748
|
}
|
|
4767
4749
|
}
|
|
4768
|
-
|
|
4750
|
+
abandonAutomationPause({
|
|
4769
4751
|
projectId: result.project.id,
|
|
4770
4752
|
bundleId: bundle.id,
|
|
4771
4753
|
broadcaster: io,
|
|
4772
|
-
stopLocalWorker: worker => (worker.kind === 'review' ? stopReviewAgent(worker.ticket_id) : stopBuildAgent(worker.ticket_id)),
|
|
4773
4754
|
});
|
|
4774
4755
|
if (checkpointLineage) {
|
|
4775
4756
|
const stoppedSequence = stopBuildSequence(checkpointLineage.ticket.id);
|
|
@@ -7187,6 +7168,12 @@ const completeClaimedProjectDeletion = async (projectId, claimId) => {
|
|
|
7187
7168
|
WHERE evaluation_id IN (SELECT id FROM check_gate_evaluations WHERE project_id = ?)
|
|
7188
7169
|
`).run(projectId);
|
|
7189
7170
|
db.prepare('DELETE FROM check_gate_evaluations WHERE project_id = ?').run(projectId);
|
|
7171
|
+
if (db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'check_repair_details'").get()) {
|
|
7172
|
+
db.prepare(`
|
|
7173
|
+
DELETE FROM check_repair_details
|
|
7174
|
+
WHERE execution_run_id IN (SELECT id FROM execution_runs WHERE project_id = ?)
|
|
7175
|
+
`).run(projectId);
|
|
7176
|
+
}
|
|
7190
7177
|
db.prepare(`
|
|
7191
7178
|
DELETE FROM check_attempt_details
|
|
7192
7179
|
WHERE execution_run_id IN (SELECT id FROM execution_runs WHERE project_id = ?)
|
|
@@ -8131,6 +8118,98 @@ app.post('/projects/:id/check-batches', async (req, res) => {
|
|
|
8131
8118
|
return res.status(409).json({ error: getErrorMessage(error) });
|
|
8132
8119
|
}
|
|
8133
8120
|
});
|
|
8121
|
+
app.post('/projects/:id/check-repairs', async (req, res) => {
|
|
8122
|
+
const project = getProjectById(req.params.id);
|
|
8123
|
+
if (!project) {
|
|
8124
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8125
|
+
}
|
|
8126
|
+
const body = req.body && typeof req.body === 'object' && !Array.isArray(req.body)
|
|
8127
|
+
? req.body
|
|
8128
|
+
: null;
|
|
8129
|
+
if (!body || Object.keys(body).some(key => !['diagnostic_ids', 'raw_output_attempt_run_ids', 'ticket_id'].includes(key))) {
|
|
8130
|
+
return res.status(400).json({ error: 'Only diagnostic_ids, raw_output_attempt_run_ids, and optional ticket_id are accepted' });
|
|
8131
|
+
}
|
|
8132
|
+
const diagnosticIds = body.diagnostic_ids === undefined
|
|
8133
|
+
? []
|
|
8134
|
+
: Array.isArray(body.diagnostic_ids)
|
|
8135
|
+
&& body.diagnostic_ids.length <= 32
|
|
8136
|
+
&& body.diagnostic_ids.every(id => typeof id === 'string' && id.trim().length > 0 && id.trim().length <= 100)
|
|
8137
|
+
? body.diagnostic_ids.map(id => id.trim())
|
|
8138
|
+
: null;
|
|
8139
|
+
const rawOutputAttemptRunIds = body.raw_output_attempt_run_ids === undefined
|
|
8140
|
+
? []
|
|
8141
|
+
: Array.isArray(body.raw_output_attempt_run_ids)
|
|
8142
|
+
&& body.raw_output_attempt_run_ids.length <= 8
|
|
8143
|
+
&& body.raw_output_attempt_run_ids.every(id => typeof id === 'string' && id.trim().length > 0 && id.trim().length <= 100)
|
|
8144
|
+
? body.raw_output_attempt_run_ids.map(id => id.trim())
|
|
8145
|
+
: null;
|
|
8146
|
+
if (!diagnosticIds
|
|
8147
|
+
|| diagnosticIds.length !== new Set(diagnosticIds).size
|
|
8148
|
+
|| !rawOutputAttemptRunIds
|
|
8149
|
+
|| rawOutputAttemptRunIds.length !== new Set(rawOutputAttemptRunIds).size
|
|
8150
|
+
|| diagnosticIds.length + rawOutputAttemptRunIds.length === 0) {
|
|
8151
|
+
return res.status(400).json({ error: 'At least one unique diagnostic_ids or raw_output_attempt_run_ids evidence reference is required' });
|
|
8152
|
+
}
|
|
8153
|
+
if (body.ticket_id !== undefined && (typeof body.ticket_id !== 'string'
|
|
8154
|
+
|| body.ticket_id.trim().length === 0
|
|
8155
|
+
|| body.ticket_id.trim().length > 100)) {
|
|
8156
|
+
return res.status(400).json({ error: 'ticket_id must be a non-empty opaque ticket ID' });
|
|
8157
|
+
}
|
|
8158
|
+
try {
|
|
8159
|
+
return res.status(202).json(await startCheckRepair({
|
|
8160
|
+
projectId: project.id,
|
|
8161
|
+
diagnosticIds,
|
|
8162
|
+
rawOutputAttemptRunIds,
|
|
8163
|
+
ticketId: typeof body.ticket_id === 'string' ? body.ticket_id.trim() : null,
|
|
8164
|
+
io,
|
|
8165
|
+
}));
|
|
8166
|
+
}
|
|
8167
|
+
catch (error) {
|
|
8168
|
+
if (error instanceof CheckRepairError) {
|
|
8169
|
+
return res.status(error.status).json({ error: error.message });
|
|
8170
|
+
}
|
|
8171
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
8172
|
+
}
|
|
8173
|
+
});
|
|
8174
|
+
app.get('/projects/:id/check-repairs/:repairRunId', (req, res) => {
|
|
8175
|
+
const project = getProjectById(req.params.id);
|
|
8176
|
+
if (!project) {
|
|
8177
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8178
|
+
}
|
|
8179
|
+
const repair = getCheckRepairDetails(project.id, req.params.repairRunId);
|
|
8180
|
+
return repair ? res.json(repair) : res.status(404).json({ error: 'Check repair not found' });
|
|
8181
|
+
});
|
|
8182
|
+
app.post('/projects/:id/check-repairs/:repairRunId/stop', async (req, res) => {
|
|
8183
|
+
if (!getProjectById(req.params.id)) {
|
|
8184
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8185
|
+
}
|
|
8186
|
+
try {
|
|
8187
|
+
return res.json(await stopCheckRepair(req.params.id, req.params.repairRunId, io));
|
|
8188
|
+
}
|
|
8189
|
+
catch (error) {
|
|
8190
|
+
if (error instanceof CheckRepairError) {
|
|
8191
|
+
return res.status(error.status).json({ error: error.message });
|
|
8192
|
+
}
|
|
8193
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
8194
|
+
}
|
|
8195
|
+
});
|
|
8196
|
+
app.post('/projects/:id/check-repairs/:repairRunId/retry', async (req, res) => {
|
|
8197
|
+
if (!getProjectById(req.params.id)) {
|
|
8198
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8199
|
+
}
|
|
8200
|
+
if (req.body && typeof req.body === 'object' && Object.keys(req.body).length > 0) {
|
|
8201
|
+
return res.status(400).json({ error: 'Check repair retries do not accept client-supplied repair context' });
|
|
8202
|
+
}
|
|
8203
|
+
try {
|
|
8204
|
+
return res.status(202).json(await retryCheckRepair(req.params.id, req.params.repairRunId, io));
|
|
8205
|
+
}
|
|
8206
|
+
catch (error) {
|
|
8207
|
+
if (error instanceof CheckRepairError) {
|
|
8208
|
+
return res.status(error.status).json({ error: error.message });
|
|
8209
|
+
}
|
|
8210
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
8211
|
+
}
|
|
8212
|
+
});
|
|
8134
8213
|
const parseCheckDefinitionIds = (value) => {
|
|
8135
8214
|
if (!Array.isArray(value) || value.length === 0 || value.some(id => typeof id !== 'string' || id.trim() === '')) {
|
|
8136
8215
|
return null;
|
|
@@ -8230,12 +8309,47 @@ app.get('/projects/:id/check-batches/:batchRunId', async (req, res) => {
|
|
|
8230
8309
|
attempts: await hydrateCheckAttemptCurrency(project, batch, getCheckBatchAttempts(req.params.id, req.params.batchRunId)),
|
|
8231
8310
|
});
|
|
8232
8311
|
});
|
|
8233
|
-
app.get('/projects/:id/check-
|
|
8312
|
+
app.get('/projects/:id/check-batches/:batchRunId/diagnostics', async (req, res) => {
|
|
8313
|
+
const project = getProjectById(req.params.id);
|
|
8314
|
+
if (!project) {
|
|
8315
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8316
|
+
}
|
|
8317
|
+
const diagnostics = getCheckBatchDiagnosticResponse(project.id, req.params.batchRunId);
|
|
8318
|
+
if (!diagnostics) {
|
|
8319
|
+
return res.status(404).json({ error: 'Check batch not found' });
|
|
8320
|
+
}
|
|
8321
|
+
return res.json({
|
|
8322
|
+
...diagnostics,
|
|
8323
|
+
repair: await getCheckRepairCandidateState(project.id, req.params.batchRunId),
|
|
8324
|
+
latest_repair: getLatestCheckRepairForBatch(project.id, req.params.batchRunId),
|
|
8325
|
+
});
|
|
8326
|
+
});
|
|
8327
|
+
app.get('/projects/:id/check-attempts/:attemptRunId/diagnostics', (req, res) => {
|
|
8328
|
+
const project = getProjectById(req.params.id);
|
|
8329
|
+
if (!project) {
|
|
8330
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8331
|
+
}
|
|
8332
|
+
const diagnostics = getCheckAttemptDiagnosticResponse(project.id, req.params.attemptRunId);
|
|
8333
|
+
if (!diagnostics) {
|
|
8334
|
+
return res.status(404).json({ error: 'Check attempt diagnostics not found' });
|
|
8335
|
+
}
|
|
8336
|
+
return res.json(diagnostics);
|
|
8337
|
+
});
|
|
8338
|
+
app.get('/projects/:id/check-gate-evaluations/:evaluationId', async (req, res) => {
|
|
8339
|
+
const project = getProjectById(req.params.id);
|
|
8340
|
+
if (!project) {
|
|
8341
|
+
return res.status(404).json({ error: 'Not found' });
|
|
8342
|
+
}
|
|
8234
8343
|
const evaluation = getCheckGateEvaluation(req.params.id, req.params.evaluationId);
|
|
8235
8344
|
if (!evaluation) {
|
|
8236
8345
|
return res.status(404).json({ error: 'Check gate evaluation not found' });
|
|
8237
8346
|
}
|
|
8238
|
-
|
|
8347
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, evaluation.target.targetKind, evaluation.target.targetBundleId);
|
|
8348
|
+
const currentWorkspaceIdentity = 'error' in workspace
|
|
8349
|
+
? null
|
|
8350
|
+
: await captureCheckWorkspaceIdentity(workspace.cwd);
|
|
8351
|
+
const currentPolicyResolution = resolveRequiredCheckPolicy(project, { boundaryKind: evaluation.boundaryKind });
|
|
8352
|
+
return res.json(hydrateCheckGatePresentApplicability(evaluation, currentWorkspaceIdentity, currentPolicyResolution));
|
|
8239
8353
|
});
|
|
8240
8354
|
app.post('/projects/:id/check-batches/:batchRunId/stop', async (req, res) => {
|
|
8241
8355
|
if (!getCheckBatchDetails(req.params.id, req.params.batchRunId)) {
|