@straiffi/archon 1.7.7 → 1.7.8
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-DwfDSF4q.js +1 -0
- package/dist/client/assets/GitHubActionsDialog-3dMCYCSZ.js +1 -0
- package/dist/client/assets/VerificationDialog-CoTfXMyb.js +4 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-DVbmGv0b.js → architectureDiagram-3BPJPVTR-Bdnc97XK.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-BsxPuJKK.js → blockDiagram-GPEHLZMM-boxM3She.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-9Jjpu2R1.js → c4Diagram-AAUBKEIU-CJDdF7xd.js} +1 -1
- package/dist/client/assets/channel-BGqdAcIT.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-Dzljq_5a.js → chunk-2J33WTMH-ClpcO-Om.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-CiBHaLdV.js → chunk-3OPIFGDE-CcuL_65J.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-DZFrI-_H.js → chunk-5ZQYHXKU-CUGYZXkH.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-u519MeJ7.js → chunk-727SXJPM-CApeLDyK.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DC91Nw9K.js → chunk-AQP2D5EJ-8FdSvF2D.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-CR7cwH-D.js → chunk-CSCIHK7Q-CZoIOF3a.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-DJwQEfa_.js → chunk-KSCS5N6A-BigFQI5y.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-w6TWSOTT.js → chunk-L5ZTLDWV-DIi3SHaT.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-Dfw_jEjR.js → chunk-LZXEDZCA-B-Z0BcQy.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-DHdJgCA7.js → chunk-ND2GUHAM-EBeba0mg.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-GvOuUiUy.js → chunk-NZK2D7GU-DJBI2y53.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O--r1VQgDc.js → chunk-O5CBEL6O-BqxdXhMd.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-Xgc6shf5.js → chunk-WU5MYG2G-Coe1_Cbb.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-D5VuKRcj.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B9GNqpRr.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-IcfTsQ6f.js → dagre-BM42HDAG-tuTmeA0Y.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-D8Z4_NSQ.js → diagram-2AECGRRQ-ILGCpG2F.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-CIjC8QzH.js → diagram-5GNKFQAL-C8qBZsgK.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-BBbYFygn.js → diagram-KO2AKTUF-BXlcm_Zo.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-lZstJdXv.js → diagram-LMA3HP47-B8V5EVbD.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-Cnqij60i.js → diagram-OG6HWLK6-CWsdOqcP.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-1DAJwSbR.js → erDiagram-TEJ5UH35-DW8pgly2.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-DIQkR95e.js → flowDiagram-I6XJVG4X-Djn7fOVR.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-mDGbRM53.js → ganttDiagram-6RSMTGT7-DZ421MEy.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-BQirGZuE.js → gitGraphDiagram-PVQCEYII-CCNzLPuP.js} +1 -1
- package/dist/client/assets/index-BRc02lbW.js +273 -0
- package/dist/client/assets/index-CZO9HL1Z.css +2 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-CljoXByt.js → infoDiagram-5YYISTIA-Bd1gYgMp.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-ouY7AiSo.js → ishikawaDiagram-YF4QCWOH-txyMoatJ.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-BiI9xI0r.js → journeyDiagram-JHISSGLW-DhT3bmX6.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-CvuW7aYT.js → kanban-definition-UN3LZRKU-BBHt2hfC.js} +1 -1
- package/dist/client/assets/{line-CoE97hT8.js → line-KGRcT7Qz.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-BHswQsmF.js → mermaid-parser.core-CDzt4_zR.js} +1 -1
- package/dist/client/assets/{mermaid.core-a9PzZlL1.js → mermaid.core-DD_-k1aB.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-CIkEQqLV.js → mindmap-definition-RKZ34NQL-DoctVTDl.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-BIvcv2M5.js → pieDiagram-4H26LBE5-DFlLEi70.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BWCVll7J.js → quadrantDiagram-W4KKPZXB-3YHOQgl7.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-w5l0CZn2.js → requirementDiagram-4Y6WPE33-X4fZVL9M.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BuKReL9w.js → sankeyDiagram-5OEKKPKP-BUPnMXeG.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Br5KpukA.js → sequenceDiagram-3UESZ5HK-D6QU3M64.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-BfFCsPCI.js → stateDiagram-AJRCARHV-c35bu4qZ.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ymy1_EHr.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-BrDP2iO0.js → timeline-definition-PNZ67QCA-CX2y6e_b.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-C7lGYnr9.js → vennDiagram-CIIHVFJN-BDaWltjF.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-BRKJRuEF.js → wardleyDiagram-YWT4CUSO-BpBRL2sb.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-BjaB0hQ8.js → xychartDiagram-2RQKCTM6-Bqw7X0UT.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +458 -2
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +324 -25
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/activeAutomationChild.js +31 -0
- package/dist/server/lib/activeAutomationChild.js.map +1 -0
- package/dist/server/lib/automationCheckGate.js +266 -0
- package/dist/server/lib/automationCheckGate.js.map +1 -0
- package/dist/server/lib/automationProgression.js +20 -21
- package/dist/server/lib/automationProgression.js.map +1 -1
- package/dist/server/lib/automationReconciliation.js +16 -2
- package/dist/server/lib/automationReconciliation.js.map +1 -1
- package/dist/server/lib/automationResumeDecision.js +4 -1
- package/dist/server/lib/automationResumeDecision.js.map +1 -1
- package/dist/server/lib/automationStageDetails.js +173 -41
- package/dist/server/lib/automationStageDetails.js.map +1 -1
- package/dist/server/lib/checkDefinitions.js +278 -0
- package/dist/server/lib/checkDefinitions.js.map +1 -0
- package/dist/server/lib/checkGateEvaluations.js +294 -0
- package/dist/server/lib/checkGateEvaluations.js.map +1 -0
- package/dist/server/lib/checkRunner.js +674 -0
- package/dist/server/lib/checkRunner.js.map +1 -0
- package/dist/server/lib/checkRuns.js +736 -0
- package/dist/server/lib/checkRuns.js.map +1 -0
- package/dist/server/lib/checkWorkspaceIdentity.js +207 -0
- package/dist/server/lib/checkWorkspaceIdentity.js.map +1 -0
- package/dist/server/lib/executionReconciliation.js +79 -2
- package/dist/server/lib/executionReconciliation.js.map +1 -1
- package/dist/server/lib/executionRetry.js +14 -6
- package/dist/server/lib/executionRetry.js.map +1 -1
- package/dist/server/lib/executionRetryContext.js +3 -3
- package/dist/server/lib/executionRetryContext.js.map +1 -1
- package/dist/server/lib/executionRunRetention.js +41 -3
- package/dist/server/lib/executionRunRetention.js.map +1 -1
- package/dist/server/lib/executionRuns.js +135 -2
- package/dist/server/lib/executionRuns.js.map +1 -1
- package/dist/server/lib/executionTimeline.js +52 -1
- package/dist/server/lib/executionTimeline.js.map +1 -1
- package/dist/server/lib/projectAutoConfig.js +47 -65
- package/dist/server/lib/projectAutoConfig.js.map +1 -1
- package/dist/server/lib/projects.js +87 -7
- package/dist/server/lib/projects.js.map +1 -1
- package/dist/server/lib/requiredCheckPolicy.js +68 -0
- package/dist/server/lib/requiredCheckPolicy.js.map +1 -0
- package/dist/server/lib/shutdown.js +4 -4
- package/dist/server/lib/shutdown.js.map +1 -1
- package/dist/server/lib/testCommandRunner.js +3 -2
- package/dist/server/lib/testCommandRunner.js.map +1 -1
- package/dist/server/lib/testSessions.js +2 -6
- package/dist/server/lib/testSessions.js.map +1 -1
- package/dist/server/lib/ticketAutomationCommit.js +185 -22
- package/dist/server/lib/ticketAutomationCommit.js.map +1 -1
- package/dist/server/lib/tickets.js +21 -1
- package/dist/server/lib/tickets.js.map +1 -1
- package/dist/server/lib/types.js +2 -0
- package/dist/server/lib/types.js.map +1 -1
- package/dist/server/workers/review.js +2 -10
- package/dist/server/workers/review.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/ActivityView-Btpsv-5U.js +0 -1
- package/dist/client/assets/GitHubActionsDialog-DRlIMgSB.js +0 -1
- package/dist/client/assets/TestsDialog-DSWDkKPV.js +0 -4
- package/dist/client/assets/channel-Lic13vb5.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DACLwQIJ.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CKi6c1d9.js +0 -1
- package/dist/client/assets/index-DQt4dcwc.css +0 -2
- package/dist/client/assets/index-W9OCqkMU.js +0 -273
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-lhIQ8FTe.js +0 -1
package/dist/server/index.js
CHANGED
|
@@ -52,7 +52,7 @@ import { reconcileAutomationStage } from './lib/automationReconciliation.js';
|
|
|
52
52
|
import { pausedRunBelongsToProgression, resolveAutomationResumeDecision } from './lib/automationResumeDecision.js';
|
|
53
53
|
import { getAutomationStageDetails, } from './lib/automationStageDetails.js';
|
|
54
54
|
import { buildAutomationFixInstruction, clearAutomationPause, countBlockingFindings, depthAtLeast, getBundleAutomationState, isAutomationDepth, recordAutomationPause, resolveAutomationConfig, upsertBundleAutomationState, } from './lib/ticketAutomation.js';
|
|
55
|
-
import { runAutomationCommitChain } from './lib/ticketAutomationCommit.js';
|
|
55
|
+
import { resolveAutomationCheckGateResume, runAutomationCommitChain } from './lib/ticketAutomationCommit.js';
|
|
56
56
|
import { getDiscoveredSkills, normalizeSkillNames } from './lib/skills.js';
|
|
57
57
|
import { clearAllPreviewProxies } from './lib/previewProxy.js';
|
|
58
58
|
import { createExecutionRun, addExecutionRunLink, addExecutionRunLinks, getExecutionRun, hasActiveBuildExecutionRunForTicket, getGitHubActionsExecutionDetails, getTestExecutionDetails, GitHubActionsDispatchConflictError, listExecutionRunLinks, listExecutionRuns, listLifecycleEvents, listProjectLifecycleEvents, toCanonicalLifecycleEvent, transitionExecutionRun, withExecutionRunTransaction, } from './lib/executionRuns.js';
|
|
@@ -61,7 +61,7 @@ import { listExecutionTimeline } from './lib/executionTimeline.js';
|
|
|
61
61
|
import { loadExecutionArtifactSummariesForRuns, openExecutionArtifactForRetrieval, } from './lib/executionArtifacts.js';
|
|
62
62
|
import { cleanupAllExecutionEvidence, EXECUTION_EVIDENCE_CLEANUP_INTERVAL_MS, resolveExecutionEvidenceRetentionDays, } from './lib/executionRunRetention.js';
|
|
63
63
|
import { ExecutionArtifactCleanupError, reconcileExecutionArtifactRegistrationIntents, reconcileFinalizedExecutionArtifacts, reconcileProjectExecutionArtifacts, resolveArtifactCleanupIntervalMs, } from './lib/executionArtifactRetention.js';
|
|
64
|
-
import { shutdownRealtimeServer } from './lib/shutdown.js';
|
|
64
|
+
import { closeHttpServer, shutdownRealtimeServer } from './lib/shutdown.js';
|
|
65
65
|
import { resolveClientBuildPaths, shouldServeClientApp, STATIC_CLIENT_DIRECTORY_OPTIONS, STATIC_CLIENT_INDEX_OPTIONS, } from './lib/staticClient.js';
|
|
66
66
|
import { deleteIntegrationConnection, getGitHubConnectionConfig, getJiraConnectionConfig, listIntegrationConnections, upsertIntegrationConnection, } from './lib/integrations/index.js';
|
|
67
67
|
import { createGitHubPullRequest, createOrFindGitHubPullRequest, findOpenGitHubPullRequest, findOpenGitHubPullRequestByHeadBranch, getGitHubPullRequestComments, getGitHubPullRequestReviews, getGitHubPullRequest, GitHubApiError, hasGitHubRemoteBranch, resolveGitHubRepository, validateGitHubConnection, } from './lib/integrations/github.js';
|
|
@@ -75,9 +75,14 @@ import { PlanningReferenceError, resolveExternalPlanningDescription } from './li
|
|
|
75
75
|
import { refreshIfNeeded as refreshAssignedIssues, clearCachedAssignedIssues } from './lib/integrations/assignedIssuesCache.js';
|
|
76
76
|
import { buildCommitMessagePrompt, normalizeGeneratedCommitMessage } from './lib/commitMessage.js';
|
|
77
77
|
import { BUNDLE_PULL_REQUEST_SYNC_TTL_MS, getBundlePullRequestRecord, hasOpenBundlePullRequest, serializeBundlePullRequest, shouldSyncBundlePullRequest, syncTrackedBundlePullRequestIfNeeded, upsertBundlePullRequest, upsertBundlePullRequestFromGitHub, } from './lib/bundlePullRequests.js';
|
|
78
|
-
import { countInboundProjectLinks, getEffectiveProject, getProjectById, getProjectTicketStats, hasProjectRunIde, listInterruptedProjectDeletions, listProjects, releaseInterruptedProjectDeletionClaims, replaceProjectLinks, serializeProject, updateProjectActiveTarget, validateProjectPayload, } from './lib/projects.js';
|
|
78
|
+
import { countInboundProjectLinks, getEffectiveProject, getProjectCheckDefinitions, getProjectById, getProjectTestCommands, getProjectTicketStats, hasProjectRunIde, listInterruptedProjectDeletions, listProjects, releaseInterruptedProjectDeletionClaims, replaceProjectLinks, serializeProject, updateProjectActiveTarget, validateProjectPayload, } from './lib/projects.js';
|
|
79
79
|
import { getActiveRunStatus, getActiveRunStatusForProject, getPreviewStatusForProject, hasRunConfig, isRunning, openIde, openTicketIdeInWorkspace, resolveProjectRunContextKey, runSetupCommandsInWorkspace, runTicketInWorkspace, stopAllTickets, stopTicket } from './lib/run.js';
|
|
80
|
-
import { clearAllTestSessions, createTestSession, deleteTestSession, discoverTestsForSession, doesTestFileExistForSession,
|
|
80
|
+
import { clearAllTestSessions, createTestSession, deleteTestSession, discoverTestsForSession, doesTestFileExistForSession, getTestSession, isTestSelectionSupported, startTestSessionRun, stopTestSessionRun } from './lib/testSessions.js';
|
|
81
|
+
import { closeCheckRunnerAdmissions, getCheckBatchAttempts, openCheckRunnerAdmissions, rerunCheckBatchAndSchedule, shutdownCheckRunner, startCheckBatch, stopCheckBatch } from './lib/checkRunner.js';
|
|
82
|
+
import { findActiveCheckBatchForTarget, getCheckAttemptDetails, getCheckBatchDetails, hasActiveProjectCheckBatch, isCheckAttemptCurrent, resolveCheckBatchRerunEligibility } from './lib/checkRuns.js';
|
|
83
|
+
import { getCheckDefinitionFingerprint } from './lib/checkDefinitions.js';
|
|
84
|
+
import { getCheckGateEvaluation } from './lib/checkGateEvaluations.js';
|
|
85
|
+
import { captureCheckWorkspaceIdentity } from './lib/checkWorkspaceIdentity.js';
|
|
81
86
|
import { getBundleBuildBlockerResponse, shouldStartBundleBuildNow, transitionTicketState, } from './lib/ticketWorkflowOperations.js';
|
|
82
87
|
import { submitPlanTicketFollowUp, submitTicketFollowUp, } from './lib/ticketFollowUpOperations.js';
|
|
83
88
|
import { closePendingTicketPlanQuestionSet, listPendingTicketPlanQuestionSetMessageIds, resolveTicketPlanQuestionSet, restoreClosedTicketPlanQuestionSets, } from './lib/planQuestionOperations.js';
|
|
@@ -292,15 +297,90 @@ const serializeGitHubActionsExecutionDetail = (details) => {
|
|
|
292
297
|
jobs_count: details.jobs_count,
|
|
293
298
|
};
|
|
294
299
|
};
|
|
295
|
-
const
|
|
296
|
-
const
|
|
300
|
+
const serializeCheckAttemptExecutionDetail = (projectId, runId) => {
|
|
301
|
+
const attempt = getCheckAttemptDetails(projectId, runId);
|
|
302
|
+
return {
|
|
303
|
+
kind: 'check_attempt',
|
|
304
|
+
available: true,
|
|
305
|
+
attempt,
|
|
306
|
+
batch: attempt ? getCheckBatchDetails(projectId, attempt.batch_run_id) : null,
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
const hydrateCheckAttemptCurrency = async (project, batch, attempts) => {
|
|
310
|
+
const terminalAttempts = attempts.filter(attempt => attempt.outcome !== null);
|
|
311
|
+
if (terminalAttempts.length === 0) {
|
|
312
|
+
return attempts.map(attempt => ({ ...attempt, currency: 'pending' }));
|
|
313
|
+
}
|
|
314
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, batch.target_kind, batch.target_bundle_id);
|
|
315
|
+
if ('error' in workspace) {
|
|
316
|
+
return attempts.map(attempt => ({
|
|
317
|
+
...attempt,
|
|
318
|
+
currency: attempt.outcome === null ? 'pending' : 'unavailable',
|
|
319
|
+
}));
|
|
320
|
+
}
|
|
321
|
+
const currentIdentity = await captureCheckWorkspaceIdentity(workspace.cwd);
|
|
322
|
+
const currentProject = getProjectById(project.id);
|
|
323
|
+
const definitionsById = new Map(getProjectCheckDefinitions(currentProject).map(definition => [definition.id, definition]));
|
|
324
|
+
return attempts.map(attempt => {
|
|
325
|
+
if (attempt.outcome === null) {
|
|
326
|
+
return { ...attempt, currency: 'pending' };
|
|
327
|
+
}
|
|
328
|
+
const definition = definitionsById.get(attempt.definition_id);
|
|
329
|
+
if (!definition || getCheckDefinitionFingerprint(definition) !== attempt.definition_fingerprint) {
|
|
330
|
+
return { ...attempt, currency: 'stale' };
|
|
331
|
+
}
|
|
332
|
+
const currency = currentIdentity.state === 'unavailable'
|
|
333
|
+
? 'unavailable'
|
|
334
|
+
: isCheckAttemptCurrent(attempt, currentIdentity) ? 'current' : 'stale';
|
|
335
|
+
return { ...attempt, currency };
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
const getCheckBatchRerunEligibility = (project, batch, selection) => {
|
|
339
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, batch.target_kind, batch.target_bundle_id);
|
|
340
|
+
return resolveCheckBatchRerunEligibility({
|
|
341
|
+
projectId: project.id,
|
|
342
|
+
batchRunId: batch.execution_run_id,
|
|
343
|
+
availableDefinitions: getProjectCheckDefinitions(project),
|
|
344
|
+
selection,
|
|
345
|
+
targetAvailable: !('error' in workspace && typeof workspace.error === 'string' && workspace.error.includes('no longer exists')),
|
|
346
|
+
workspaceAvailable: !('error' in workspace),
|
|
347
|
+
});
|
|
348
|
+
};
|
|
349
|
+
const buildExecutionRunDetailResponse = async (project, run) => {
|
|
350
|
+
const checkBatch = run.kind === 'check_batch' ? getCheckBatchDetails(project.id, run.id) : null;
|
|
351
|
+
const retryEligibility = checkBatch
|
|
352
|
+
? getCheckBatchRerunEligibility(project, checkBatch)
|
|
353
|
+
: getExecutionRetryEligibility(project.id, run.id);
|
|
354
|
+
const checkBatchAttempts = checkBatch
|
|
355
|
+
? await hydrateCheckAttemptCurrency(project, checkBatch, getCheckBatchAttempts(project.id, run.id))
|
|
356
|
+
: [];
|
|
357
|
+
const checkAttemptDetail = run.kind === 'check_attempt' ? serializeCheckAttemptExecutionDetail(project.id, run.id) : null;
|
|
358
|
+
const checkAttempt = checkAttemptDetail?.attempt && checkAttemptDetail.batch
|
|
359
|
+
? (await hydrateCheckAttemptCurrency(project, checkAttemptDetail.batch, [checkAttemptDetail.attempt]))[0]
|
|
360
|
+
: checkAttemptDetail?.attempt ?? null;
|
|
297
361
|
const details = run.kind === 'test'
|
|
298
362
|
? serializeTestExecutionDetail(getTestExecutionDetails(project.id, run.id), retryEligibility)
|
|
299
|
-
: run.kind === '
|
|
300
|
-
? {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
:
|
|
363
|
+
: run.kind === 'check_batch'
|
|
364
|
+
? {
|
|
365
|
+
kind: 'check_batch',
|
|
366
|
+
available: true,
|
|
367
|
+
batch: checkBatch,
|
|
368
|
+
attempts: checkBatchAttempts,
|
|
369
|
+
retry_eligibility: retryEligibility,
|
|
370
|
+
action_kind: retryEligibility.eligible ? 'rerun' : null,
|
|
371
|
+
}
|
|
372
|
+
: run.kind === 'check_attempt'
|
|
373
|
+
? {
|
|
374
|
+
...checkAttemptDetail,
|
|
375
|
+
attempt: checkAttempt,
|
|
376
|
+
retry_eligibility: retryEligibility,
|
|
377
|
+
action_kind: null,
|
|
378
|
+
}
|
|
379
|
+
: run.kind === 'github_actions'
|
|
380
|
+
? { ...serializeGitHubActionsExecutionDetail(getGitHubActionsExecutionDetails(project.id, run.id)), retry_eligibility: retryEligibility }
|
|
381
|
+
: ['automation_commit', 'automation_push', 'automation_pull_request'].includes(run.kind)
|
|
382
|
+
? { kind: run.kind, available: true, automation_stage: getAutomationStageDetails(project.id, run.id), retry_eligibility: retryEligibility }
|
|
383
|
+
: { kind: run.kind, available: true, retry_eligibility: retryEligibility };
|
|
304
384
|
const artifacts = loadExecutionArtifactSummariesForRuns(project.id, [run.id], MAX_ARTIFACTS_PER_RUN).get(run.id)
|
|
305
385
|
?? { artifacts: [], artifact_count: 0 };
|
|
306
386
|
return {
|
|
@@ -3628,6 +3708,9 @@ app.delete('/bundles/:id', (req, res) => {
|
|
|
3628
3708
|
if (result.project.active_target_kind === 'bundle' && result.project.active_target_bundle_id === bundle.id) {
|
|
3629
3709
|
return res.status(409).json({ error: 'Cannot delete the currently selected target bundle' });
|
|
3630
3710
|
}
|
|
3711
|
+
if (findActiveCheckBatchForTarget(result.project.id, 'bundle', bundle.id)) {
|
|
3712
|
+
return res.status(409).json({ error: 'Cannot delete a bundle while checks are running' });
|
|
3713
|
+
}
|
|
3631
3714
|
const affectedChatSessionIds = listChatSessions(result.project.id)
|
|
3632
3715
|
.filter(chatSession => chatSession.target_bundle_id === bundle.id)
|
|
3633
3716
|
.map(chatSession => chatSession.id);
|
|
@@ -3976,7 +4059,7 @@ app.post('/bundles/:id/follow-up', (req, res) => {
|
|
|
3976
4059
|
return res.status(response.status).json(response.value);
|
|
3977
4060
|
});
|
|
3978
4061
|
const AUTOMATION_SIDE_EFFECT_KINDS = new Set(['automation_commit', 'automation_push', 'automation_pull_request']);
|
|
3979
|
-
const AUTOMATION_WORKER_PAUSE_KINDS = new Set(['build', 'review', 'build_follow_up', 'automation_review_fix']);
|
|
4062
|
+
const AUTOMATION_WORKER_PAUSE_KINDS = new Set(['build', 'review', 'build_follow_up', 'automation_review_fix', 'check_batch']);
|
|
3980
4063
|
app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
3981
4064
|
const result = getRequiredProject(req);
|
|
3982
4065
|
if ('error' in result) {
|
|
@@ -4272,7 +4355,7 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4272
4355
|
return res.status(followUpResult.status).json({ error: followUpResult.error });
|
|
4273
4356
|
}
|
|
4274
4357
|
}
|
|
4275
|
-
else if (decision.outcome === 'continue_commit_chain') {
|
|
4358
|
+
else if (decision.outcome === 'continue_commit_chain' || decision.outcome === 'rerun_required_checks') {
|
|
4276
4359
|
try {
|
|
4277
4360
|
if (!revalidateDecision()) {
|
|
4278
4361
|
return res.status(409).json({ error: 'Bundle automation state changed before Resume could start' });
|
|
@@ -4283,7 +4366,10 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
4283
4366
|
progressionRunId,
|
|
4284
4367
|
broadcaster: io,
|
|
4285
4368
|
});
|
|
4286
|
-
|
|
4369
|
+
const checkGateResume = decision.outcome === 'rerun_required_checks'
|
|
4370
|
+
? resolveAutomationCheckGateResume(result.project.id, progressionRunId)
|
|
4371
|
+
: undefined;
|
|
4372
|
+
await runAutomationCommitChain(owner, project, owner.bundle ?? null, automationConfig, io, progressionRunId, checkGateResume);
|
|
4287
4373
|
}
|
|
4288
4374
|
catch (error) {
|
|
4289
4375
|
return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
|
|
@@ -5495,7 +5581,7 @@ app.post('/projects', (req, res) => {
|
|
|
5495
5581
|
return res.status(400).json({ error: validation.error });
|
|
5496
5582
|
}
|
|
5497
5583
|
const projectCount = Number(db.prepare('SELECT COUNT(*) AS count FROM projects').get()?.count ?? 0);
|
|
5498
|
-
const { name, repo_path, linked_project_ids, run_setup, run_services, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, 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;
|
|
5584
|
+
const { name, repo_path, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, 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;
|
|
5499
5585
|
if (!repo_path) {
|
|
5500
5586
|
return res.status(400).json({ error: 'repo_path must be a non-empty string' });
|
|
5501
5587
|
}
|
|
@@ -5515,6 +5601,8 @@ app.post('/projects', (req, res) => {
|
|
|
5515
5601
|
repo_path,
|
|
5516
5602
|
run_setup,
|
|
5517
5603
|
run_services,
|
|
5604
|
+
check_definitions,
|
|
5605
|
+
required_check_definition_ids,
|
|
5518
5606
|
test_commands,
|
|
5519
5607
|
run_ide,
|
|
5520
5608
|
preview_service_name,
|
|
@@ -5535,8 +5623,8 @@ app.post('/projects', (req, res) => {
|
|
|
5535
5623
|
automation_review_severity,
|
|
5536
5624
|
automation_review_max_iterations
|
|
5537
5625
|
)
|
|
5538
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5539
|
-
`).run(id, name, normalizedRepoPath, run_setup, run_services, 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, 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);
|
|
5626
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5627
|
+
`).run(id, name, normalizedRepoPath, run_setup, run_services, check_definitions, required_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, 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);
|
|
5540
5628
|
replaceProjectLinks(id, linked_project_ids ?? []);
|
|
5541
5629
|
ensureProjectRootBundle(id);
|
|
5542
5630
|
if (projectCount === 0) {
|
|
@@ -6702,13 +6790,15 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
6702
6790
|
});
|
|
6703
6791
|
}
|
|
6704
6792
|
}
|
|
6705
|
-
const { name, repo_path, linked_project_ids, run_setup, run_services, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, 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;
|
|
6793
|
+
const { name, repo_path, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, 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;
|
|
6706
6794
|
db.prepare(`
|
|
6707
6795
|
UPDATE projects SET
|
|
6708
6796
|
name = ?,
|
|
6709
6797
|
repo_path = ?,
|
|
6710
6798
|
run_setup = ?,
|
|
6711
6799
|
run_services = ?,
|
|
6800
|
+
check_definitions = ?,
|
|
6801
|
+
required_check_definition_ids = ?,
|
|
6712
6802
|
test_commands = ?,
|
|
6713
6803
|
run_ide = ?,
|
|
6714
6804
|
preview_service_name = ?,
|
|
@@ -6730,7 +6820,7 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
6730
6820
|
automation_review_max_iterations = ?,
|
|
6731
6821
|
updated_at = CURRENT_TIMESTAMP
|
|
6732
6822
|
WHERE id = ?
|
|
6733
|
-
`).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, 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, '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);
|
|
6823
|
+
`).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, hasOwn(validation.values, 'check_definitions') ? check_definitions : project.check_definitions, hasOwn(validation.values, 'required_check_definition_ids') ? required_check_definition_ids : project.required_check_definition_ids, 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, '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);
|
|
6734
6824
|
if (hasOwn(validation.values, 'linked_project_ids')) {
|
|
6735
6825
|
replaceProjectLinks(project.id, linked_project_ids ?? []);
|
|
6736
6826
|
}
|
|
@@ -6759,6 +6849,9 @@ const assertProjectDeletionEligible = (projectId, claimId) => {
|
|
|
6759
6849
|
if (stats.ticketCount > 0 || stats.worktreeCount > 0 || stats.bundleCount > 0) {
|
|
6760
6850
|
throw new ProjectDeletionError(409, 'Cannot delete a project with existing tickets, bundles, or worktrees');
|
|
6761
6851
|
}
|
|
6852
|
+
if (hasActiveProjectCheckBatch(projectId)) {
|
|
6853
|
+
throw new ProjectDeletionError(409, 'Cannot delete a project while checks are active');
|
|
6854
|
+
}
|
|
6762
6855
|
};
|
|
6763
6856
|
const completeClaimedProjectDeletion = async (projectId, claimId) => {
|
|
6764
6857
|
await runExclusiveExecutionArtifactMaintenance(() => reconcileProjectExecutionArtifacts(projectId, {
|
|
@@ -6785,6 +6878,19 @@ const completeClaimedProjectDeletion = async (projectId, claimId) => {
|
|
|
6785
6878
|
db.prepare("DELETE FROM execution_artifacts WHERE project_id = ? AND availability = 'deleted'").run(projectId);
|
|
6786
6879
|
deleteProjectAttachments(projectId);
|
|
6787
6880
|
db.prepare(`
|
|
6881
|
+
DELETE FROM check_gate_evaluation_attempts
|
|
6882
|
+
WHERE evaluation_id IN (SELECT id FROM check_gate_evaluations WHERE project_id = ?)
|
|
6883
|
+
`).run(projectId);
|
|
6884
|
+
db.prepare('DELETE FROM check_gate_evaluations WHERE project_id = ?').run(projectId);
|
|
6885
|
+
db.prepare(`
|
|
6886
|
+
DELETE FROM check_attempt_details
|
|
6887
|
+
WHERE execution_run_id IN (SELECT id FROM execution_runs WHERE project_id = ?)
|
|
6888
|
+
`).run(projectId);
|
|
6889
|
+
db.prepare(`
|
|
6890
|
+
DELETE FROM check_batch_details
|
|
6891
|
+
WHERE execution_run_id IN (SELECT id FROM execution_runs WHERE project_id = ?)
|
|
6892
|
+
`).run(projectId);
|
|
6893
|
+
db.prepare(`
|
|
6788
6894
|
UPDATE execution_runs
|
|
6789
6895
|
SET parent_run_id = NULL, retry_of_run_id = NULL
|
|
6790
6896
|
WHERE project_id = ?
|
|
@@ -7671,6 +7777,186 @@ app.post('/projects/:id/test-sessions', (req, res) => {
|
|
|
7671
7777
|
targetLabel: workspace.target_label,
|
|
7672
7778
|
}));
|
|
7673
7779
|
});
|
|
7780
|
+
app.post('/projects/:id/check-batches', async (req, res) => {
|
|
7781
|
+
const project = getProjectById(req.params.id);
|
|
7782
|
+
if (!project) {
|
|
7783
|
+
return res.status(404).json({ error: 'Not found' });
|
|
7784
|
+
}
|
|
7785
|
+
const body = req.body && typeof req.body === 'object' && !Array.isArray(req.body) ? req.body : null;
|
|
7786
|
+
const targetKind = body?.target_kind === 'bundle'
|
|
7787
|
+
? 'bundle'
|
|
7788
|
+
: body?.target_kind === 'repo_root'
|
|
7789
|
+
? 'repo_root'
|
|
7790
|
+
: null;
|
|
7791
|
+
if (!targetKind) {
|
|
7792
|
+
return res.status(400).json({ error: 'target_kind must be repo_root or bundle' });
|
|
7793
|
+
}
|
|
7794
|
+
const definitionIds = parseCheckDefinitionIds(body?.definition_ids);
|
|
7795
|
+
if (!definitionIds) {
|
|
7796
|
+
return res.status(400).json({ error: 'definition_ids must contain at least one unique check definition ID' });
|
|
7797
|
+
}
|
|
7798
|
+
const definitions = getProjectCheckDefinitions(project);
|
|
7799
|
+
const definitionsById = new Map(definitions.map(definition => [definition.id, definition]));
|
|
7800
|
+
const selectedDefinitions = definitionIds.map(id => definitionsById.get(id)).filter((definition) => Boolean(definition));
|
|
7801
|
+
if (selectedDefinitions.length !== definitionIds.length) {
|
|
7802
|
+
return res.status(409).json({ error: 'One or more selected check definitions are stale or unavailable' });
|
|
7803
|
+
}
|
|
7804
|
+
const targetBundleId = typeof body?.target_bundle_id === 'string' ? body.target_bundle_id.trim() || null : null;
|
|
7805
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, targetKind, targetBundleId);
|
|
7806
|
+
if ('error' in workspace) {
|
|
7807
|
+
return res.status(Number(workspace.status)).json({ error: workspace.error });
|
|
7808
|
+
}
|
|
7809
|
+
try {
|
|
7810
|
+
const batch = await startCheckBatch({
|
|
7811
|
+
projectId: project.id,
|
|
7812
|
+
cwd: workspace.cwd,
|
|
7813
|
+
targetKind: workspace.target_kind,
|
|
7814
|
+
targetBundleId: workspace.target_bundle_id,
|
|
7815
|
+
targetLabel: workspace.target_label,
|
|
7816
|
+
selectedDefinitions,
|
|
7817
|
+
availableDefinitions: definitions,
|
|
7818
|
+
sourceReference: { kind: 'project', id: project.id },
|
|
7819
|
+
}, io);
|
|
7820
|
+
return res.status(201).json({
|
|
7821
|
+
batch,
|
|
7822
|
+
attempts: getCheckBatchAttempts(project.id, batch.execution_run_id),
|
|
7823
|
+
});
|
|
7824
|
+
}
|
|
7825
|
+
catch (error) {
|
|
7826
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
7827
|
+
}
|
|
7828
|
+
});
|
|
7829
|
+
const parseCheckDefinitionIds = (value) => {
|
|
7830
|
+
if (!Array.isArray(value) || value.length === 0 || value.some(id => typeof id !== 'string' || id.trim() === '')) {
|
|
7831
|
+
return null;
|
|
7832
|
+
}
|
|
7833
|
+
const ids = value.map(id => id.trim());
|
|
7834
|
+
return ids.length === new Set(ids).size ? ids : null;
|
|
7835
|
+
};
|
|
7836
|
+
const parseCheckRerunSelection = (body) => {
|
|
7837
|
+
const payload = body && typeof body === 'object' && !Array.isArray(body) ? body : {};
|
|
7838
|
+
if (payload.mode === undefined || payload.mode === 'all') {
|
|
7839
|
+
return { mode: 'all' };
|
|
7840
|
+
}
|
|
7841
|
+
if (payload.mode === 'failed_only') {
|
|
7842
|
+
return { mode: 'failed' };
|
|
7843
|
+
}
|
|
7844
|
+
if (payload.mode !== 'selected') {
|
|
7845
|
+
return { error: 'mode must be all, failed_only, or selected' };
|
|
7846
|
+
}
|
|
7847
|
+
const definitionIds = parseCheckDefinitionIds(payload.definition_ids);
|
|
7848
|
+
if (!definitionIds) {
|
|
7849
|
+
return { error: 'definition_ids must contain at least one unique source definition ID for selected reruns' };
|
|
7850
|
+
}
|
|
7851
|
+
return { mode: 'selected', definitionIds };
|
|
7852
|
+
};
|
|
7853
|
+
const rerunProjectCheckBatch = async (project, sourceBatchRunId, body) => {
|
|
7854
|
+
const sourceBatch = getCheckBatchDetails(project.id, sourceBatchRunId);
|
|
7855
|
+
if (!sourceBatch) {
|
|
7856
|
+
return { ok: false, status: 404, error: 'Check batch not found', reason: 'status_not_retryable' };
|
|
7857
|
+
}
|
|
7858
|
+
const selection = parseCheckRerunSelection(body);
|
|
7859
|
+
if ('error' in selection) {
|
|
7860
|
+
return { ok: false, status: 400, error: selection.error, reason: 'retry_input_unavailable' };
|
|
7861
|
+
}
|
|
7862
|
+
const eligibility = getCheckBatchRerunEligibility(project, sourceBatch, selection);
|
|
7863
|
+
if (!eligibility.eligible) {
|
|
7864
|
+
return {
|
|
7865
|
+
ok: false,
|
|
7866
|
+
status: 409,
|
|
7867
|
+
error: 'Check batch cannot be rerun with its current target and definitions',
|
|
7868
|
+
reason: eligibility.reason,
|
|
7869
|
+
};
|
|
7870
|
+
}
|
|
7871
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, sourceBatch.target_kind, sourceBatch.target_bundle_id);
|
|
7872
|
+
if ('error' in workspace) {
|
|
7873
|
+
return { ok: false, status: Number(workspace.status), error: workspace.error, reason: 'retry_workspace_unavailable' };
|
|
7874
|
+
}
|
|
7875
|
+
try {
|
|
7876
|
+
const value = await rerunCheckBatchAndSchedule({
|
|
7877
|
+
projectId: project.id,
|
|
7878
|
+
sourceBatchRunId,
|
|
7879
|
+
cwd: workspace.cwd,
|
|
7880
|
+
availableDefinitions: getProjectCheckDefinitions(project),
|
|
7881
|
+
selection,
|
|
7882
|
+
}, io);
|
|
7883
|
+
return { ok: true, value };
|
|
7884
|
+
}
|
|
7885
|
+
catch (error) {
|
|
7886
|
+
return { ok: false, status: 409, error: getErrorMessage(error), reason: 'retry_input_unavailable' };
|
|
7887
|
+
}
|
|
7888
|
+
};
|
|
7889
|
+
app.get('/projects/:id/check-batches', (req, res) => {
|
|
7890
|
+
const project = getProjectById(req.params.id);
|
|
7891
|
+
if (!project) {
|
|
7892
|
+
return res.status(404).json({ error: 'Not found' });
|
|
7893
|
+
}
|
|
7894
|
+
const query = parseLifecycleListQuery(req.query);
|
|
7895
|
+
if ('error' in query) {
|
|
7896
|
+
return res.status(400).json({ error: query.error });
|
|
7897
|
+
}
|
|
7898
|
+
try {
|
|
7899
|
+
return res.json(listExecutionTimeline({
|
|
7900
|
+
project,
|
|
7901
|
+
kind: 'check_batch',
|
|
7902
|
+
statuses: query.statuses,
|
|
7903
|
+
cursor: query.cursor,
|
|
7904
|
+
limit: query.limit,
|
|
7905
|
+
}));
|
|
7906
|
+
}
|
|
7907
|
+
catch (error) {
|
|
7908
|
+
if (error instanceof Error && error.message === 'Invalid execution run cursor') {
|
|
7909
|
+
return res.status(400).json({ error: 'cursor must be a valid execution run cursor' });
|
|
7910
|
+
}
|
|
7911
|
+
throw error;
|
|
7912
|
+
}
|
|
7913
|
+
});
|
|
7914
|
+
app.get('/projects/:id/check-batches/:batchRunId', async (req, res) => {
|
|
7915
|
+
const project = getProjectById(req.params.id);
|
|
7916
|
+
if (!project) {
|
|
7917
|
+
return res.status(404).json({ error: 'Not found' });
|
|
7918
|
+
}
|
|
7919
|
+
const batch = getCheckBatchDetails(req.params.id, req.params.batchRunId);
|
|
7920
|
+
if (!batch) {
|
|
7921
|
+
return res.status(404).json({ error: 'Check batch not found' });
|
|
7922
|
+
}
|
|
7923
|
+
return res.json({
|
|
7924
|
+
batch,
|
|
7925
|
+
attempts: await hydrateCheckAttemptCurrency(project, batch, getCheckBatchAttempts(req.params.id, req.params.batchRunId)),
|
|
7926
|
+
});
|
|
7927
|
+
});
|
|
7928
|
+
app.get('/projects/:id/check-gate-evaluations/:evaluationId', (req, res) => {
|
|
7929
|
+
const evaluation = getCheckGateEvaluation(req.params.id, req.params.evaluationId);
|
|
7930
|
+
if (!evaluation) {
|
|
7931
|
+
return res.status(404).json({ error: 'Check gate evaluation not found' });
|
|
7932
|
+
}
|
|
7933
|
+
return res.json(evaluation);
|
|
7934
|
+
});
|
|
7935
|
+
app.post('/projects/:id/check-batches/:batchRunId/stop', async (req, res) => {
|
|
7936
|
+
if (!getCheckBatchDetails(req.params.id, req.params.batchRunId)) {
|
|
7937
|
+
return res.status(404).json({ error: 'Check batch not found' });
|
|
7938
|
+
}
|
|
7939
|
+
try {
|
|
7940
|
+
const batch = await stopCheckBatch(req.params.id, req.params.batchRunId, io);
|
|
7941
|
+
return res.json({
|
|
7942
|
+
batch,
|
|
7943
|
+
attempts: getCheckBatchAttempts(req.params.id, req.params.batchRunId),
|
|
7944
|
+
});
|
|
7945
|
+
}
|
|
7946
|
+
catch (error) {
|
|
7947
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
7948
|
+
}
|
|
7949
|
+
});
|
|
7950
|
+
app.post('/projects/:id/check-batches/:batchRunId/rerun', async (req, res) => {
|
|
7951
|
+
const project = getProjectById(req.params.id);
|
|
7952
|
+
if (!project) {
|
|
7953
|
+
return res.status(404).json({ error: 'Not found' });
|
|
7954
|
+
}
|
|
7955
|
+
const result = await rerunProjectCheckBatch(project, req.params.batchRunId, req.body);
|
|
7956
|
+
return result.ok
|
|
7957
|
+
? res.status(201).json(result.value)
|
|
7958
|
+
: res.status(result.status).json({ error: result.error });
|
|
7959
|
+
});
|
|
7674
7960
|
app.get('/projects/:id/execution-runs', (req, res) => {
|
|
7675
7961
|
const project = getProjectById(req.params.id);
|
|
7676
7962
|
if (!project) {
|
|
@@ -7755,7 +8041,7 @@ app.get('/projects/:id/execution-timeline', (req, res) => {
|
|
|
7755
8041
|
throw error;
|
|
7756
8042
|
}
|
|
7757
8043
|
});
|
|
7758
|
-
app.get('/projects/:id/execution-timeline/:runId', (req, res) => {
|
|
8044
|
+
app.get('/projects/:id/execution-timeline/:runId', async (req, res) => {
|
|
7759
8045
|
const project = getProjectById(req.params.id);
|
|
7760
8046
|
if (!project) {
|
|
7761
8047
|
return res.status(404).json({ error: 'Not found' });
|
|
@@ -7772,9 +8058,9 @@ app.get('/projects/:id/execution-timeline/:runId', (req, res) => {
|
|
|
7772
8058
|
if (scope.scopeEntity && !links.some(link => link.entity_type === scope.scopeEntity?.type && link.entity_id === scope.scopeEntity.id)) {
|
|
7773
8059
|
return res.status(404).json({ error: 'Execution run not found' });
|
|
7774
8060
|
}
|
|
7775
|
-
return res.json(buildExecutionRunDetailResponse(project, run));
|
|
8061
|
+
return res.json(await buildExecutionRunDetailResponse(project, run));
|
|
7776
8062
|
});
|
|
7777
|
-
app.get('/projects/:id/execution-runs/:runId', (req, res) => {
|
|
8063
|
+
app.get('/projects/:id/execution-runs/:runId', async (req, res) => {
|
|
7778
8064
|
const project = getProjectById(req.params.id);
|
|
7779
8065
|
if (!project) {
|
|
7780
8066
|
return res.status(404).json({ error: 'Not found' });
|
|
@@ -7783,7 +8069,7 @@ app.get('/projects/:id/execution-runs/:runId', (req, res) => {
|
|
|
7783
8069
|
if (!run) {
|
|
7784
8070
|
return res.status(404).json({ error: 'Execution run not found' });
|
|
7785
8071
|
}
|
|
7786
|
-
return res.json(buildExecutionRunDetailResponse(project, run));
|
|
8072
|
+
return res.json(await buildExecutionRunDetailResponse(project, run));
|
|
7787
8073
|
});
|
|
7788
8074
|
app.get('/projects/:id/execution-runs/:runId/artifacts/:artifactId', async (req, res) => {
|
|
7789
8075
|
const project = getProjectById(req.params.id);
|
|
@@ -7827,6 +8113,13 @@ app.post('/projects/:id/execution-runs/:runId/retry', async (req, res) => {
|
|
|
7827
8113
|
if (!project) {
|
|
7828
8114
|
return res.status(404).json({ error: 'Not found' });
|
|
7829
8115
|
}
|
|
8116
|
+
const source = getExecutionRun(project.id, req.params.runId);
|
|
8117
|
+
if (source?.kind === 'check_batch') {
|
|
8118
|
+
const result = await rerunProjectCheckBatch(project, source.id, req.body);
|
|
8119
|
+
return result.ok
|
|
8120
|
+
? res.status(201).json(result.value)
|
|
8121
|
+
: res.status(result.status).json({ error: result.error, reason: result.reason });
|
|
8122
|
+
}
|
|
7830
8123
|
const resolution = resolveExecutionRetry(project.id, req.params.runId);
|
|
7831
8124
|
const result = await dispatchExecutionRetry(resolution, io, async (retry) => {
|
|
7832
8125
|
const session = createTestSession({
|
|
@@ -7892,7 +8185,7 @@ app.get('/test-sessions/:id/discovery', (req, res) => {
|
|
|
7892
8185
|
if (!project) {
|
|
7893
8186
|
return res.status(404).json({ error: 'Project not found' });
|
|
7894
8187
|
}
|
|
7895
|
-
command =
|
|
8188
|
+
command = getProjectTestCommands(project).find(item => item.id === commandId) ?? null;
|
|
7896
8189
|
if (!command) {
|
|
7897
8190
|
return res.status(404).json({ error: 'Test command not found' });
|
|
7898
8191
|
}
|
|
@@ -7928,7 +8221,7 @@ app.post('/test-sessions/:id/run', async (req, res) => {
|
|
|
7928
8221
|
if (!selection) {
|
|
7929
8222
|
return res.status(400).json({ error: 'selection must be all, file, or case' });
|
|
7930
8223
|
}
|
|
7931
|
-
const command =
|
|
8224
|
+
const command = getProjectTestCommands(project).find(item => item.id === commandId) ?? null;
|
|
7932
8225
|
if (!command) {
|
|
7933
8226
|
return res.status(404).json({ error: 'Test command not found' });
|
|
7934
8227
|
}
|
|
@@ -8305,6 +8598,7 @@ export const startServer = ({ port = DEFAULT_PORT, host } = {}) => {
|
|
|
8305
8598
|
};
|
|
8306
8599
|
httpServer.once('error', handleError);
|
|
8307
8600
|
const { bindHost } = resolveDesktopServerHost(host);
|
|
8601
|
+
openCheckRunnerAdmissions();
|
|
8308
8602
|
httpServer.listen(port, bindHost, handleListening);
|
|
8309
8603
|
});
|
|
8310
8604
|
}
|
|
@@ -8328,6 +8622,9 @@ export const shutdownServer = async (signal) => {
|
|
|
8328
8622
|
return shutdownServerPromise;
|
|
8329
8623
|
}
|
|
8330
8624
|
shutdownRequested = true;
|
|
8625
|
+
closeCheckRunnerAdmissions();
|
|
8626
|
+
const httpServerClosePromise = httpServer.listening ? closeHttpServer(httpServer) : null;
|
|
8627
|
+
void httpServerClosePromise?.catch(() => undefined);
|
|
8331
8628
|
const pendingStartup = startServerPromise;
|
|
8332
8629
|
shutdownServerPromise = (async () => {
|
|
8333
8630
|
await Promise.all([
|
|
@@ -8344,6 +8641,7 @@ export const shutdownServer = async (signal) => {
|
|
|
8344
8641
|
}
|
|
8345
8642
|
await executionArtifactCleanupPromise;
|
|
8346
8643
|
await stopMobileAccess();
|
|
8644
|
+
await shutdownCheckRunner();
|
|
8347
8645
|
await clearAllTestSessions(io);
|
|
8348
8646
|
await clearAllGitHubActionsSessions();
|
|
8349
8647
|
await shutdownRealtimeServer({
|
|
@@ -8356,6 +8654,7 @@ export const shutdownServer = async (signal) => {
|
|
|
8356
8654
|
stopAllReviewAgents,
|
|
8357
8655
|
stopAllPlanAgents,
|
|
8358
8656
|
stopAllTickets,
|
|
8657
|
+
httpServerClosePromise,
|
|
8359
8658
|
});
|
|
8360
8659
|
})().finally(() => {
|
|
8361
8660
|
shutdownRequested = false;
|