@straiffi/archon 1.10.0 → 1.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/dist/client/assets/ActivityView-CdDT5tgr.js +1 -0
  2. package/dist/client/assets/{GitHubActionsDialog-GWVxWGPK.js → GitHubActionsDialog-NdYtgMhg.js} +1 -1
  3. package/dist/client/assets/{VerificationDialog-C4RMuI4y.js → VerificationDialog-BfIKtVjt.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-CeM6gbLN.js → architectureDiagram-3BPJPVTR-C4zoWnXM.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-BP4hta96.js → blockDiagram-GPEHLZMM-ZhRwV8ma.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-5aJScvhn.js → c4Diagram-AAUBKEIU-CA2USxCJ.js} +1 -1
  7. package/dist/client/assets/channel-C4I-VlJu.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-BKc2IelV.js → chunk-2J33WTMH-CY32Brnt.js} +1 -1
  9. package/dist/client/assets/{chunk-3OPIFGDE-DCc15la5.js → chunk-3OPIFGDE-H70UH7rH.js} +1 -1
  10. package/dist/client/assets/{chunk-5ZQYHXKU-DSD9HbTp.js → chunk-5ZQYHXKU-DC1dllSt.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-DrAjnKiA.js → chunk-727SXJPM-DVIgI7hM.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-DxKnpHfQ.js → chunk-AQP2D5EJ-gwSYEPou.js} +1 -1
  13. package/dist/client/assets/{chunk-CSCIHK7Q-DLejrAB_.js → chunk-CSCIHK7Q-Z1cfABPc.js} +1 -1
  14. package/dist/client/assets/{chunk-KSCS5N6A-BWRNmF8S.js → chunk-KSCS5N6A-DFoW6dtq.js} +1 -1
  15. package/dist/client/assets/{chunk-L5ZTLDWV-D48i966f.js → chunk-L5ZTLDWV-CmQbbP8b.js} +1 -1
  16. package/dist/client/assets/{chunk-LZXEDZCA-DLhg6Bok.js → chunk-LZXEDZCA-Ba16nX-R.js} +2 -2
  17. package/dist/client/assets/{chunk-ND2GUHAM-B4-7cOEq.js → chunk-ND2GUHAM-Bc7u1Zqm.js} +1 -1
  18. package/dist/client/assets/{chunk-NZK2D7GU-D2ttXA_d.js → chunk-NZK2D7GU-DNXYx7JX.js} +1 -1
  19. package/dist/client/assets/{chunk-O5CBEL6O-BrWrOVCq.js → chunk-O5CBEL6O-Ba00cuZ5.js} +1 -1
  20. package/dist/client/assets/{chunk-WU5MYG2G-CLcPwxPe.js → chunk-WU5MYG2G-SJ_ny5dU.js} +1 -1
  21. package/dist/client/assets/classDiagram-4FO5ZUOK-Ca3KxBnW.js +1 -0
  22. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CSpMessw.js +1 -0
  23. package/dist/client/assets/{dagre-BM42HDAG-DIXN8_VX.js → dagre-BM42HDAG-CL2dKbj9.js} +1 -1
  24. package/dist/client/assets/{diagram-2AECGRRQ-D2wjdCij.js → diagram-2AECGRRQ-DQq1qZ0l.js} +1 -1
  25. package/dist/client/assets/{diagram-5GNKFQAL-E1Og_PHE.js → diagram-5GNKFQAL-Zf0YQuRe.js} +1 -1
  26. package/dist/client/assets/{diagram-KO2AKTUF-B6P1Ud8a.js → diagram-KO2AKTUF-DcUxL9SG.js} +1 -1
  27. package/dist/client/assets/{diagram-LMA3HP47-C3Jxq56v.js → diagram-LMA3HP47-BAIixEu7.js} +1 -1
  28. package/dist/client/assets/{diagram-OG6HWLK6-Do9LVhdL.js → diagram-OG6HWLK6-Dhhjm84p.js} +1 -1
  29. package/dist/client/assets/{erDiagram-TEJ5UH35-DLb5E4-f.js → erDiagram-TEJ5UH35-BcQOacBP.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-I6XJVG4X-CcTs_wU9.js → flowDiagram-I6XJVG4X-BZIJqLp3.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-6RSMTGT7-q9ez5fri.js → ganttDiagram-6RSMTGT7-DFC6qgrY.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-BcaKiz4c.js → gitGraphDiagram-PVQCEYII-Ds0nwSU0.js} +1 -1
  33. package/dist/client/assets/index-Bg3l17L3.css +2 -0
  34. package/dist/client/assets/index-D_KmViSu.js +276 -0
  35. package/dist/client/assets/{infoDiagram-5YYISTIA-Cu0w4frE.js → infoDiagram-5YYISTIA-DfDrR5Mz.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CJ47Z-ak.js → ishikawaDiagram-YF4QCWOH-B2v4Byji.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-JHISSGLW-fAL_Zcvs.js → journeyDiagram-JHISSGLW-CO6laklO.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-UN3LZRKU-BTNIybMC.js → kanban-definition-UN3LZRKU-BziFO199.js} +1 -1
  39. package/dist/client/assets/{line-BvsadeZR.js → line-Vb0A8-Oe.js} +1 -1
  40. package/dist/client/assets/{mermaid-parser.core-C0EkH2y_.js → mermaid-parser.core-Bm3Xv0Zt.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-BU5QIIbY.js → mermaid.core-DXBRp0XG.js} +3 -3
  42. package/dist/client/assets/{mindmap-definition-RKZ34NQL-CWbSYK8f.js → mindmap-definition-RKZ34NQL-4GueJvPT.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-4H26LBE5-t4w2cB-y.js → pieDiagram-4H26LBE5-Djy0sVJR.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Cqasvrpa.js → quadrantDiagram-W4KKPZXB-3rWAi4K5.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-4Y6WPE33-_l-mY-tb.js → requirementDiagram-4Y6WPE33-D7-WCnjH.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Cr9YrZ-Y.js → sankeyDiagram-5OEKKPKP-B4-3BUQ2.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DvzZhQpb.js → sequenceDiagram-3UESZ5HK-DCWomuSc.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-AJRCARHV-cFjPpAO3.js → stateDiagram-AJRCARHV-DpsWWFaO.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-BE_IAuYt.js +1 -0
  50. package/dist/client/assets/{timeline-definition-PNZ67QCA-BjRNrEJY.js → timeline-definition-PNZ67QCA-B0Mh4MHF.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-CIIHVFJN-xs9afjrw.js → vennDiagram-CIIHVFJN-Dpxr8LTG.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DH68ZYti.js → wardleyDiagram-YWT4CUSO-BdAz5ERd.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DLW5ktTU.js → xychartDiagram-2RQKCTM6-BV9Ah-jk.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/server/db.js +640 -0
  56. package/dist/server/db.js.map +1 -1
  57. package/dist/server/index.js +795 -125
  58. package/dist/server/index.js.map +1 -1
  59. package/dist/server/lib/agentCatalog.js +35 -0
  60. package/dist/server/lib/agentCatalog.js.map +1 -0
  61. package/dist/server/lib/agentModelConfig.js +151 -0
  62. package/dist/server/lib/agentModelConfig.js.map +1 -0
  63. package/dist/server/lib/agentRuns.js +1 -0
  64. package/dist/server/lib/agentRuns.js.map +1 -1
  65. package/dist/server/lib/automationCheckGate.js +98 -9
  66. package/dist/server/lib/automationCheckGate.js.map +1 -1
  67. package/dist/server/lib/automationLoopAgentEvidence.js +42 -0
  68. package/dist/server/lib/automationLoopAgentEvidence.js.map +1 -0
  69. package/dist/server/lib/automationLoopProjection.js +339 -0
  70. package/dist/server/lib/automationLoopProjection.js.map +1 -0
  71. package/dist/server/lib/automationProgression.js +115 -6
  72. package/dist/server/lib/automationProgression.js.map +1 -1
  73. package/dist/server/lib/automationReconciliation.js +27 -11
  74. package/dist/server/lib/automationReconciliation.js.map +1 -1
  75. package/dist/server/lib/automationResumeDecision.js +55 -15
  76. package/dist/server/lib/automationResumeDecision.js.map +1 -1
  77. package/dist/server/lib/automationReviewGate.js +352 -26
  78. package/dist/server/lib/automationReviewGate.js.map +1 -1
  79. package/dist/server/lib/buildExecutionEnvironment.js +7 -4
  80. package/dist/server/lib/buildExecutionEnvironment.js.map +1 -1
  81. package/dist/server/lib/buildFlow.js +12 -0
  82. package/dist/server/lib/buildFlow.js.map +1 -1
  83. package/dist/server/lib/bundleReviewOperations.js +40 -23
  84. package/dist/server/lib/bundleReviewOperations.js.map +1 -1
  85. package/dist/server/lib/chatOperations.js +26 -4
  86. package/dist/server/lib/chatOperations.js.map +1 -1
  87. package/dist/server/lib/checkAutoFix.js +5 -4
  88. package/dist/server/lib/checkAutoFix.js.map +1 -1
  89. package/dist/server/lib/checkAutoFixDetails.js +54 -9
  90. package/dist/server/lib/checkAutoFixDetails.js.map +1 -1
  91. package/dist/server/lib/checkGateEvaluations.js +6 -1
  92. package/dist/server/lib/checkGateEvaluations.js.map +1 -1
  93. package/dist/server/lib/checkRepair.js +30 -0
  94. package/dist/server/lib/checkRepair.js.map +1 -1
  95. package/dist/server/lib/claudeAgentProvider.js +19 -0
  96. package/dist/server/lib/claudeAgentProvider.js.map +1 -1
  97. package/dist/server/lib/claudeValidationOutput.js +4 -4
  98. package/dist/server/lib/claudeValidationOutput.js.map +1 -1
  99. package/dist/server/lib/dockerSandboxExecutionEnvironment.js +48 -7
  100. package/dist/server/lib/dockerSandboxExecutionEnvironment.js.map +1 -1
  101. package/dist/server/lib/executionEnvironmentValidation.js +19 -10
  102. package/dist/server/lib/executionEnvironmentValidation.js.map +1 -1
  103. package/dist/server/lib/executionRunAuthority.js +30 -0
  104. package/dist/server/lib/executionRunAuthority.js.map +1 -1
  105. package/dist/server/lib/executionRuns.js +17 -3
  106. package/dist/server/lib/executionRuns.js.map +1 -1
  107. package/dist/server/lib/executionTimeline.js +3 -0
  108. package/dist/server/lib/executionTimeline.js.map +1 -1
  109. package/dist/server/lib/lightweightPrompt.js +13 -40
  110. package/dist/server/lib/lightweightPrompt.js.map +1 -1
  111. package/dist/server/lib/mobileAccess.js +47 -3
  112. package/dist/server/lib/mobileAccess.js.map +1 -1
  113. package/dist/server/lib/modelResolutionErrors.js +24 -0
  114. package/dist/server/lib/modelResolutionErrors.js.map +1 -0
  115. package/dist/server/lib/models.js +9 -9
  116. package/dist/server/lib/models.js.map +1 -1
  117. package/dist/server/lib/postBuildCheckRuns.js +79 -2
  118. package/dist/server/lib/postBuildCheckRuns.js.map +1 -1
  119. package/dist/server/lib/projectAutoConfig.js +15 -52
  120. package/dist/server/lib/projectAutoConfig.js.map +1 -1
  121. package/dist/server/lib/projectMemoryScan.js +15 -9
  122. package/dist/server/lib/projectMemoryScan.js.map +1 -1
  123. package/dist/server/lib/projects.js +59 -4
  124. package/dist/server/lib/projects.js.map +1 -1
  125. package/dist/server/lib/reviewRepairCompletion.js +126 -0
  126. package/dist/server/lib/reviewRepairCompletion.js.map +1 -0
  127. package/dist/server/lib/reviewRepairIterationRecords.js +149 -0
  128. package/dist/server/lib/reviewRepairIterationRecords.js.map +1 -0
  129. package/dist/server/lib/reviewRepairIterations.js +590 -0
  130. package/dist/server/lib/reviewRepairIterations.js.map +1 -0
  131. package/dist/server/lib/reviewRepairReconciliation.js +588 -0
  132. package/dist/server/lib/reviewRepairReconciliation.js.map +1 -0
  133. package/dist/server/lib/reviewerConfig.js +18 -7
  134. package/dist/server/lib/reviewerConfig.js.map +1 -1
  135. package/dist/server/lib/run.js +7 -2
  136. package/dist/server/lib/run.js.map +1 -1
  137. package/dist/server/lib/specArtifact.js +1 -1
  138. package/dist/server/lib/specArtifact.js.map +1 -1
  139. package/dist/server/lib/specGraphFeature.js +2 -2
  140. package/dist/server/lib/specGraphFeature.js.map +1 -1
  141. package/dist/server/lib/specOperations.js +26 -4
  142. package/dist/server/lib/specOperations.js.map +1 -1
  143. package/dist/server/lib/specRepositoryEvidenceFormatting.js +2 -2
  144. package/dist/server/lib/specRepositoryEvidenceFormatting.js.map +1 -1
  145. package/dist/server/lib/specRepositoryImpact.js +3 -3
  146. package/dist/server/lib/specRepositoryImpact.js.map +1 -1
  147. package/dist/server/lib/specReviewContext.js +7 -1
  148. package/dist/server/lib/specReviewContext.js.map +1 -1
  149. package/dist/server/lib/specReviewOperations.js +20 -7
  150. package/dist/server/lib/specReviewOperations.js.map +1 -1
  151. package/dist/server/lib/specSnapshotEvidence.js +3 -3
  152. package/dist/server/lib/specSnapshotEvidence.js.map +1 -1
  153. package/dist/server/lib/ticketAutomation.js +9 -11
  154. package/dist/server/lib/ticketAutomation.js.map +1 -1
  155. package/dist/server/lib/ticketAutomationCommit.js +21 -11
  156. package/dist/server/lib/ticketAutomationCommit.js.map +1 -1
  157. package/dist/server/lib/ticketFollowUpOperations.js +25 -7
  158. package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
  159. package/dist/server/lib/ticketSettingsOperations.js +9 -0
  160. package/dist/server/lib/ticketSettingsOperations.js.map +1 -1
  161. package/dist/server/lib/ticketWorkflowOperations.js +2 -6
  162. package/dist/server/lib/ticketWorkflowOperations.js.map +1 -1
  163. package/dist/server/lib/tickets.js +16 -6
  164. package/dist/server/lib/tickets.js.map +1 -1
  165. package/dist/server/lib/types.js +45 -0
  166. package/dist/server/lib/types.js.map +1 -1
  167. package/dist/server/workers/build.js +111 -1
  168. package/dist/server/workers/build.js.map +1 -1
  169. package/dist/server/workers/followUp.js +109 -22
  170. package/dist/server/workers/followUp.js.map +1 -1
  171. package/dist/server/workers/review.js +292 -60
  172. package/dist/server/workers/review.js.map +1 -1
  173. package/dist/server/workers/specReview.js +17 -8
  174. package/dist/server/workers/specReview.js.map +1 -1
  175. package/dist/server/workers/specSession.js +33 -23
  176. package/dist/server/workers/specSession.js.map +1 -1
  177. package/package.json +1 -1
  178. package/dist/client/assets/ActivityView-Y5IaJiim.js +0 -1
  179. package/dist/client/assets/channel-B3DjZXbm.js +0 -1
  180. package/dist/client/assets/classDiagram-4FO5ZUOK-DproSkn2.js +0 -1
  181. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-JVMMzJxJ.js +0 -1
  182. package/dist/client/assets/index-1CokwSFm.css +0 -2
  183. package/dist/client/assets/index-CSOKbu3w.js +0 -276
  184. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-btHMH5Cg.js +0 -1
@@ -23,17 +23,21 @@ import { captureStageBaseline, captureStageSuccess } from '../lib/ticketUndo.js'
23
23
  import { beginTicketThinkingRun, bindTicketThinkingMessage } from '../lib/thinkingState.js';
24
24
  import { autoParkTicketIfStale, getTicket, resolveTicketBranch, resolveTicketTool, resolveTicketWorkerKey } from '../lib/tickets.js';
25
25
  import { createAgentRun, emitAgentRunsUpdated, emitExecutionAgentRunsUpdated, getAgentRun, listAgentRunsForExecutionRun, terminalizeAgentRunsForExecutionRun, } from '../lib/agentRuns.js';
26
- import { buildAutomationFixInstruction, buildAutomationFixStatusMessage, decideReviewAutomation, depthAtLeast, getBundleAutomationState, resetBundleAutomationLoop, resolveAutomationConfig, upsertBundleAutomationState, } from '../lib/ticketAutomation.js';
26
+ import { buildAutomationFixInstruction, buildAutomationFixStatusMessage, countBlockingFindings, decideReviewAutomation, depthAtLeast, getBundleAutomationState, resetBundleAutomationLoop, resolveAutomationConfig, } from '../lib/ticketAutomation.js';
27
27
  import { runAutomationCommitChain } from '../lib/ticketAutomationCommit.js';
28
28
  import { submitTicketFollowUp } from '../lib/ticketFollowUpOperations.js';
29
- import { completeAutomationProgression, createAutomationReviewFix, ensureAutomationProgression, finishAutomationReviewFix, pauseAutomationProgression, resolveProgressionForChild, waitAutomationProgression, } from '../lib/automationProgression.js';
29
+ import { completeAutomationProgressionInTransaction, completeAutomationProgression, completeAutomationReviewFixActionInTransaction, createAutomationReviewFixInTransaction, ensureAutomationProgression, finishAutomationReviewFix, pauseAutomationProgression, resolveProgressionForChild, waitAutomationProgression, } from '../lib/automationProgression.js';
30
+ import { assertAutomationAuthorizationCurrent, assertAutomationBoundaryNotRequired, AutomationCheckGateError, } from '../lib/automationCheckGate.js';
31
+ import { attachReviewRepairActionEnvelopeInTransaction, attachReviewRepairActionInTransaction, appendReviewRepairIterationDecisionInTransaction, attachReviewRepairResultInTransaction, claimReviewRepairIterationInTransaction, getReviewRepairIteration, getReviewRepairIterationByVerification, pauseReviewRepairIteration, pauseReviewRepairIterationInTransaction, terminalizeReviewRepairIterationInTransaction, } from '../lib/reviewRepairIterations.js';
30
32
  import { formatBuildOpenQuestionAnswersPrompt, listAnsweredBuildOpenQuestionsForBundle } from '../lib/buildOpenQuestions.js';
31
33
  import { validateWorkflowContext } from '../lib/types.js';
32
- import { isOpencodeModelResolutionError } from '../lib/opencodeErrors.js';
34
+ import { isModelResolutionError } from '../lib/modelResolutionErrors.js';
33
35
  import { assertProviderSessionAuthorityMatches, createExecutionProfileSelectionMetadata } from '../lib/executionRunAuthority.js';
34
36
  import { classifyTicketExecutionTarget, resolveExecutionTarget } from '../lib/executionTargets.js';
35
37
  import { resolveWorkloadExecutionEnvironment } from '../lib/workloadExecutionEnvironment.js';
36
38
  import { getModelProvider } from '../lib/models.js';
39
+ import { captureCheckWorkspaceIdentity } from '../lib/checkWorkspaceIdentity.js';
40
+ import { persistReviewCompletionAutomationInTransaction, } from '../lib/reviewRepairCompletion.js';
37
41
  const getErrorMessage = (error) => {
38
42
  return error instanceof Error ? error.message : String(error);
39
43
  };
@@ -368,7 +372,7 @@ Every finding must set \`file_path\` to a path in \`<review_scope>\` and set at
368
372
 
369
373
  Do not include planning text, implementation steps, or commentary about changing modes.`, ticket.skills);
370
374
  };
371
- const runReviewCompletionAutomation = async (ticket, project, reviewRunId, parsedStatus, findings, io, executionContext, initiation) => {
375
+ const runReviewCompletionAutomation = async (ticket, project, reviewRunId, parsedStatus, findings, io, executionContext, initiation, persistedAutomation) => {
372
376
  if (!ticket.worktree_bundle_id || !ticket.project_id) {
373
377
  return false;
374
378
  }
@@ -376,12 +380,74 @@ const runReviewCompletionAutomation = async (ticket, project, reviewRunId, parse
376
380
  const bundleId = ticket.worktree_bundle_id;
377
381
  const liveTicket = getTicket(ticket.id) ?? ticket;
378
382
  const liveProject = getEffectiveProject(liveTicket) ?? project;
379
- const config = resolveAutomationConfig(liveTicket.bundle ?? null, liveProject);
383
+ const config = persistedAutomation?.config ?? resolveAutomationConfig(liveTicket.bundle ?? null, liveProject);
380
384
  const bundleState = getBundleAutomationState(projectId, bundleId);
381
- const decision = decideReviewAutomation(config, bundleState, reviewRunId, parsedStatus, findings);
382
- const progression = initiation.initiator === 'automation'
383
- ? resolveProgressionForChild({ projectId, bundleId, childRunId: executionContext.runId })
384
- : null;
385
+ const decision = persistedAutomation?.decision
386
+ ?? decideReviewAutomation(config, bundleState, reviewRunId, parsedStatus, findings);
387
+ const progressionRunId = persistedAutomation?.progressionRunId
388
+ ?? (initiation.initiator === 'automation'
389
+ ? resolveProgressionForChild({ projectId, bundleId, childRunId: executionContext.runId }).id
390
+ : null);
391
+ const progression = progressionRunId ? { id: progressionRunId } : null;
392
+ if (progression) {
393
+ const iteration = getReviewRepairIterationByVerification(projectId, executionContext.runId);
394
+ if (iteration?.phase === 'verification_active'
395
+ && iteration.resulting_review_execution_run_id === executionContext.runId) {
396
+ const resultingBlockingCount = parsedStatus === 'completed'
397
+ ? countBlockingFindings(findings, config)
398
+ : null;
399
+ withExecutionRunTransaction(transaction => {
400
+ if (parsedStatus === 'completed' && reviewRunId) {
401
+ attachReviewRepairResultInTransaction(transaction, {
402
+ projectId,
403
+ iterationId: iteration.id,
404
+ expectedPhase: 'verification_active',
405
+ expectedVerificationRunId: executionContext.runId,
406
+ resultingReviewRunId: reviewRunId,
407
+ });
408
+ }
409
+ appendReviewRepairIterationDecisionInTransaction(transaction, {
410
+ projectId,
411
+ iterationId: iteration.id,
412
+ decisionPoint: 'verification_completion',
413
+ effectiveAutomationConfig: config,
414
+ decisionCode: parsedStatus === 'completed' ? 'verification_completed' : 'verification_failed',
415
+ operationRunId: executionContext.runId,
416
+ });
417
+ const terminal = decision.kind === 'fix_iteration'
418
+ ? { outcome: 'continuing', stopReason: null }
419
+ : decision.kind === 'clean'
420
+ ? { outcome: 'repaired', stopReason: 'no_blocking_findings' }
421
+ : decision.kind === 'exhausted'
422
+ ? { outcome: 'exhausted', stopReason: 'max_iterations' }
423
+ : decision.kind === 'pause_blocking'
424
+ ? { outcome: 'interrupted', stopReason: 'automation_disabled' }
425
+ : parsedStatus !== 'completed'
426
+ ? { outcome: 'failed', stopReason: 'verification_failed' }
427
+ : { outcome: 'interrupted', stopReason: 'configuration_changed' };
428
+ if (parsedStatus !== 'completed') {
429
+ pauseReviewRepairIterationInTransaction(transaction, {
430
+ projectId,
431
+ iterationId: iteration.id,
432
+ expectedPhase: 'verification_active',
433
+ expectedCurrentRunId: executionContext.runId,
434
+ stopReason: 'verification_failed',
435
+ });
436
+ }
437
+ else {
438
+ terminalizeReviewRepairIterationInTransaction(transaction, {
439
+ projectId,
440
+ iterationId: iteration.id,
441
+ expectedPhase: 'verification_active',
442
+ expectedCurrentRunId: executionContext.runId,
443
+ outcome: terminal.outcome,
444
+ stopReason: terminal.stopReason,
445
+ resultingBlockingCount,
446
+ });
447
+ }
448
+ }, asGenericEmitter(io), 'immediate');
449
+ }
450
+ }
385
451
  switch (decision.kind) {
386
452
  case 'manual':
387
453
  if (progression) {
@@ -431,41 +497,66 @@ const runReviewCompletionAutomation = async (ticket, project, reviewRunId, parse
431
497
  if (!reviewRunId) {
432
498
  throw new Error('Review fix cannot start without persisted review findings');
433
499
  }
434
- const previousIteration = bundleState?.review_iteration_count ?? 0;
435
- const previousFixLoopActive = bundleState?.fix_loop_active ?? 0;
436
- upsertBundleAutomationState(projectId, bundleId, {
437
- review_iteration_count: decision.iteration,
438
- fix_loop_active: 1,
439
- });
440
500
  const instruction = buildAutomationFixInstruction(config.reviewSeverity);
441
501
  const reviewFixWorkflowContext = validateWorkflowContext({
442
502
  ...(initiation.workflowContext ?? {}),
443
503
  review_finding_run_id: reviewRunId,
444
504
  review_severity: config.reviewSeverity,
445
505
  });
446
- let reviewFix = null;
447
- try {
448
- reviewFix = progression
449
- ? createAutomationReviewFix({
506
+ const repairProgressionRunId = progression?.id ?? ensureAutomationProgression({
507
+ projectId,
508
+ bundleId,
509
+ ticketId: ticket.id,
510
+ ticketTitle: ticket.title,
511
+ parentRunId: executionContext.runId,
512
+ workflowContext: initiation.workflowContext,
513
+ broadcaster: asGenericEmitter(io),
514
+ }).runId;
515
+ let reviewFix = persistedAutomation?.repair?.reviewFix ?? null;
516
+ let iterationId = persistedAutomation?.repair?.iterationId ?? null;
517
+ if (persistedAutomation && !persistedAutomation.repair) {
518
+ return true;
519
+ }
520
+ if (!reviewFix) {
521
+ const claimed = withExecutionRunTransaction(transaction => {
522
+ const iteration = claimReviewRepairIterationInTransaction(transaction, {
523
+ projectId,
524
+ worktreeBundleId: bundleId,
525
+ ownerTicketId: ticket.id,
526
+ automationProgressionRunId: repairProgressionRunId,
527
+ triggeringReviewExecutionRunId: executionContext.runId,
528
+ triggeringReviewRunId: reviewRunId,
529
+ triggeringBlockingCount: countBlockingFindings(findings, config),
530
+ effectiveAutomationConfig: config,
531
+ decisionCode: 'repair_admitted',
532
+ });
533
+ if (iteration.phase !== 'observed') {
534
+ return { iteration, reviewFix: null };
535
+ }
536
+ const createdReviewFix = createAutomationReviewFixInTransaction(transaction, {
450
537
  projectId,
451
538
  bundleId,
452
539
  ticketId: ticket.id,
453
540
  ticketTitle: ticket.title,
454
- progressionRunId: progression.id,
541
+ progressionRunId: repairProgressionRunId,
455
542
  workflowContext: reviewFixWorkflowContext,
456
543
  triggeringReviewRunId: executionContext.runId,
457
544
  reviewFindingRunId: reviewRunId,
458
545
  reviewSeverity: config.reviewSeverity,
459
- broadcaster: asGenericEmitter(io),
460
- })
461
- : null;
546
+ });
547
+ attachReviewRepairActionEnvelopeInTransaction(transaction, {
548
+ projectId,
549
+ iterationId: iteration.id,
550
+ expectedPhase: 'observed',
551
+ runId: createdReviewFix.runId,
552
+ });
553
+ return { iteration, reviewFix: createdReviewFix };
554
+ }, asGenericEmitter(io), 'immediate');
555
+ iterationId = claimed.iteration.id;
556
+ reviewFix = claimed.reviewFix;
462
557
  }
463
- catch (error) {
464
- upsertBundleAutomationState(projectId, bundleId, {
465
- review_iteration_count: previousIteration,
466
- fix_loop_active: previousFixLoopActive,
467
- });
468
- throw error;
558
+ if (!reviewFix) {
559
+ return true;
469
560
  }
470
561
  const started = submitTicketFollowUp({
471
562
  ticketId: ticket.id,
@@ -475,16 +566,30 @@ const runReviewCompletionAutomation = async (ticket, project, reviewRunId, parse
475
566
  persistTriggerMessage: false,
476
567
  initiation: {
477
568
  initiator: 'automation',
478
- causalParentRunId: reviewFix?.runId ?? executionContext.runId,
569
+ causalParentRunId: reviewFix.runId,
479
570
  workflowContext: reviewFixWorkflowContext,
480
571
  },
572
+ onExecutionCreated: (createdContext, transaction) => {
573
+ attachReviewRepairActionInTransaction(transaction, {
574
+ projectId,
575
+ iterationId: iterationId,
576
+ expectedPhase: 'action_claimed',
577
+ runId: createdContext.runId,
578
+ });
579
+ },
580
+ onExecutionCompleted: (completedContext, transaction) => {
581
+ const currentTicket = getTicket(ticket.id) ?? ticket;
582
+ const currentProject = getEffectiveProject(currentTicket) ?? project;
583
+ const currentConfig = resolveAutomationConfig(currentTicket.bundle ?? null, currentProject);
584
+ completeAutomationReviewFixActionInTransaction(transaction, {
585
+ projectId,
586
+ actionRunId: completedContext.runId,
587
+ effectiveAutomationConfig: currentConfig,
588
+ });
589
+ },
481
590
  });
482
591
  if (!started.ok) {
483
- upsertBundleAutomationState(projectId, bundleId, {
484
- review_iteration_count: previousIteration,
485
- fix_loop_active: previousFixLoopActive,
486
- });
487
- if (reviewFix && progression) {
592
+ if (reviewFix) {
488
593
  finishAutomationReviewFix({
489
594
  projectId,
490
595
  reviewFixRunId: reviewFix.runId,
@@ -494,24 +599,27 @@ const runReviewCompletionAutomation = async (ticket, project, reviewRunId, parse
494
599
  waitAutomationProgression({
495
600
  projectId,
496
601
  bundleId,
497
- progressionRunId: progression.id,
602
+ progressionRunId: repairProgressionRunId,
498
603
  pauseCode: 'review',
499
604
  pauseReason: `fix follow-up could not start: ${started.error}`,
500
605
  reasonCode: 'stage_failed',
501
606
  pausedStageRunId: reviewFix.runId,
502
607
  broadcaster: asGenericEmitter(io),
503
608
  });
504
- }
505
- else {
506
- pauseAutomationProgression({
507
- projectId,
508
- bundleId,
509
- pauseCode: 'review',
510
- pauseReason: `fix follow-up could not start: ${started.error}`,
511
- reasonCode: 'stage_failed',
512
- pausedStageRunId: null,
513
- broadcaster: asGenericEmitter(io),
514
- });
609
+ const failedIteration = getReviewRepairIteration(projectId, iterationId);
610
+ if (failedIteration && !['paused', 'terminal'].includes(failedIteration.phase)) {
611
+ pauseReviewRepairIteration({
612
+ projectId,
613
+ iterationId: failedIteration.id,
614
+ expectedPhase: failedIteration.phase,
615
+ expectedCurrentRunId: failedIteration.resulting_review_execution_run_id
616
+ ?? failedIteration.gate_batch_run_id
617
+ ?? failedIteration.action_run_id
618
+ ?? failedIteration.action_envelope_run_id,
619
+ stopReason: 'action_failed',
620
+ broadcaster: asGenericEmitter(io),
621
+ });
622
+ }
515
623
  }
516
624
  const pausedTicket = getTicket(ticket.id);
517
625
  if (pausedTicket) {
@@ -557,6 +665,36 @@ const runReviewCompletionAutomation = async (ticket, project, reviewRunId, parse
557
665
  }
558
666
  return true;
559
667
  }
668
+ case 'exhausted': {
669
+ if (progression) {
670
+ waitAutomationProgression({
671
+ projectId,
672
+ bundleId,
673
+ progressionRunId: progression.id,
674
+ pauseCode: 'review',
675
+ pauseReason: 'Blocking Review findings remain after repair verification.',
676
+ reasonCode: 'precondition_failed',
677
+ pausedStageRunId: executionContext.runId,
678
+ broadcaster: asGenericEmitter(io),
679
+ });
680
+ }
681
+ else {
682
+ pauseAutomationProgression({
683
+ projectId,
684
+ bundleId,
685
+ pauseCode: 'review',
686
+ pauseReason: 'Blocking Review findings remain after repair verification.',
687
+ reasonCode: 'precondition_failed',
688
+ pausedStageRunId: null,
689
+ broadcaster: asGenericEmitter(io),
690
+ });
691
+ }
692
+ const pausedTicket = getTicket(ticket.id);
693
+ if (pausedTicket) {
694
+ io.emit('ticket:updated', pausedTicket);
695
+ }
696
+ return true;
697
+ }
560
698
  case 'clean': {
561
699
  resetBundleAutomationLoop(projectId, bundleId);
562
700
  if (depthAtLeast(config.depth, 'auto_commit')) {
@@ -617,15 +755,10 @@ export function startReview(ticket, io, options) {
617
755
  compensateAcceptance = acceptance.compensate;
618
756
  }
619
757
  const acceptedTool = resolveTicketTool(acceptedTicket, config.tool ?? 'opencode');
620
- const acceptedReviewer = acceptedTool === 'opencode'
621
- ? resolveReviewerConfig(acceptedTicket.bundle, project, {
622
- model: acceptedTicket.model,
623
- variant: acceptedTicket.variant,
624
- })
625
- : {
626
- model: acceptedTicket.model,
627
- variant: acceptedTicket.model ? acceptedTicket.variant : null,
628
- };
758
+ const acceptedReviewer = resolveReviewerConfig(acceptedTicket.bundle, project, {
759
+ model: acceptedTicket.model,
760
+ variant: acceptedTicket.variant,
761
+ }, acceptedTool);
629
762
  acceptedReviewExecution = {
630
763
  tool: acceptedTool,
631
764
  model: acceptedReviewer.model,
@@ -677,12 +810,14 @@ export function startReview(ticket, io, options) {
677
810
  },
678
811
  eventType: initiation.retrySourceRunId ? 'retry_started' : 'review_created',
679
812
  });
680
- return {
813
+ const createdContext = {
681
814
  runId: executionRun.id,
682
815
  correlationId: executionRun.correlation_id,
683
816
  projectId: executionRun.project_id,
684
817
  kind: executionRun.kind,
685
818
  };
819
+ options?.onExecutionCreated?.(createdContext, transaction);
820
+ return createdContext;
686
821
  }, asGenericEmitter(io));
687
822
  }
688
823
  catch (error) {
@@ -716,6 +851,93 @@ export function startReview(ticket, io, options) {
716
851
  throw new Error('The queued Review has no immutable execution configuration');
717
852
  }
718
853
  const cwd = currentTicket.bundle?.kind === 'project_root' ? project.repo_path : resolveWorktreePath(branch, project);
854
+ const verificationIteration = getReviewRepairIterationByVerification(project.id, executionContext.runId);
855
+ if (verificationIteration) {
856
+ const admittedIdentity = verificationIteration.workspace_evidence?.workspaceIdentity;
857
+ const currentIdentity = await captureCheckWorkspaceIdentity(cwd);
858
+ const liveConfig = resolveAutomationConfig(currentTicket.bundle ?? null, project);
859
+ if (liveConfig.depth === 'manual') {
860
+ withExecutionRunTransaction(transaction => {
861
+ terminalizeReviewRepairIterationInTransaction(transaction, {
862
+ projectId: project.id,
863
+ iterationId: verificationIteration.id,
864
+ expectedPhase: 'verification_active',
865
+ expectedCurrentRunId: executionContext.runId,
866
+ outcome: 'interrupted',
867
+ stopReason: 'configuration_changed',
868
+ });
869
+ completeAutomationProgressionInTransaction(transaction, {
870
+ projectId: project.id,
871
+ bundleId: verificationIteration.worktree_bundle_id,
872
+ progressionRunId: verificationIteration.automation_progression_run_id,
873
+ domainStatus: 'manual_boundary_reached',
874
+ });
875
+ }, asGenericEmitter(io), 'immediate');
876
+ throw new Error('Automated Review was disabled before verification started');
877
+ }
878
+ if (admittedIdentity?.state !== 'available'
879
+ || currentIdentity.state !== 'available'
880
+ || admittedIdentity.fingerprint !== currentIdentity.fingerprint) {
881
+ pauseReviewRepairIteration({
882
+ projectId: project.id,
883
+ iterationId: verificationIteration.id,
884
+ expectedPhase: 'verification_active',
885
+ expectedCurrentRunId: executionContext.runId,
886
+ stopReason: 'stale_evidence',
887
+ workspaceEvidence: {
888
+ ...verificationIteration.workspace_evidence,
889
+ startupWorkspaceIdentity: currentIdentity,
890
+ },
891
+ broadcaster: asGenericEmitter(io),
892
+ });
893
+ throw new Error('Automated Review workspace is stale relative to gate admission');
894
+ }
895
+ if (!currentTicket.bundle) {
896
+ throw new Error('Automated Review bundle no longer exists');
897
+ }
898
+ try {
899
+ if (verificationIteration.gate_evaluation_id) {
900
+ await assertAutomationAuthorizationCurrent({
901
+ project,
902
+ bundle: currentTicket.bundle,
903
+ progressionRunId: verificationIteration.automation_progression_run_id,
904
+ cwd,
905
+ evaluationId: verificationIteration.gate_evaluation_id,
906
+ boundaryKind: 'automation_review',
907
+ });
908
+ }
909
+ else if (typeof verificationIteration.workspace_evidence?.policyFingerprint === 'string') {
910
+ assertAutomationBoundaryNotRequired({
911
+ project,
912
+ bundle: currentTicket.bundle,
913
+ progressionRunId: verificationIteration.automation_progression_run_id,
914
+ policyFingerprint: verificationIteration.workspace_evidence.policyFingerprint,
915
+ boundaryKind: 'automation_review',
916
+ });
917
+ }
918
+ else {
919
+ throw new AutomationCheckGateError('invalid_lineage', 'Automated Review gate evidence is unavailable');
920
+ }
921
+ }
922
+ catch (error) {
923
+ if (!(error instanceof AutomationCheckGateError)) {
924
+ throw error;
925
+ }
926
+ pauseReviewRepairIteration({
927
+ projectId: project.id,
928
+ iterationId: verificationIteration.id,
929
+ expectedPhase: 'verification_active',
930
+ expectedCurrentRunId: executionContext.runId,
931
+ stopReason: 'stale_evidence',
932
+ workspaceEvidence: {
933
+ ...verificationIteration.workspace_evidence,
934
+ startupWorkspaceIdentity: currentIdentity,
935
+ },
936
+ broadcaster: asGenericEmitter(io),
937
+ });
938
+ throw new Error(`Automated Review gate authorization is stale: ${error.message}`);
939
+ }
940
+ }
719
941
  const { tool, skills } = acceptedReviewExecution;
720
942
  const reviewerConfig = {
721
943
  model: acceptedReviewExecution.model,
@@ -907,6 +1129,15 @@ export function startReview(ticket, io, options) {
907
1129
  expectedParent: { entityType: 'ticket', entityId: currentTicket.id },
908
1130
  },
909
1131
  });
1132
+ if (verificationIteration) {
1133
+ attachReviewRepairResultInTransaction(transaction, {
1134
+ projectId: executionContext.projectId,
1135
+ iterationId: verificationIteration.id,
1136
+ expectedPhase: 'verification_active',
1137
+ expectedVerificationRunId: executionContext.runId,
1138
+ resultingReviewRunId: activeReviewRunId,
1139
+ });
1140
+ }
910
1141
  }, asGenericEmitter(io));
911
1142
  beginTicketThinkingRun(ticket.id);
912
1143
  const blindRunId = createAgentRun({
@@ -1025,7 +1256,7 @@ export function startReview(ticket, io, options) {
1025
1256
  });
1026
1257
  }
1027
1258
  catch (error) {
1028
- if (tool === 'opencode' && reviewerConfig.model && isOpencodeModelResolutionError(error)) {
1259
+ if (reviewerConfig.model && isModelResolutionError(error, tool)) {
1029
1260
  throw getReviewerExecutionError(error, reviewerConfig.model, reviewerConfig.variant);
1030
1261
  }
1031
1262
  throw error;
@@ -1195,7 +1426,8 @@ export function startReview(ticket, io, options) {
1195
1426
  ...(parseFailed ? { failure: { code: 'parse_failed', message: 'Review output could not be parsed' } } : {}),
1196
1427
  eventType: parseFailed ? 'review_parse_failed' : 'review_completed',
1197
1428
  });
1198
- return { messageId, reviewRunId: activeReviewRunId };
1429
+ const persistedAutomation = persistReviewCompletionAutomationInTransaction(transaction, currentTicket, project, activeReviewRunId, parsedStatus, parsedFindings, executionContext, initiation);
1430
+ return { messageId, reviewRunId: activeReviewRunId, persistedAutomation };
1199
1431
  }, asGenericEmitter(io));
1200
1432
  persistedMessageId = persisted.messageId;
1201
1433
  reviewRunId = persisted.reviewRunId;
@@ -1232,7 +1464,7 @@ export function startReview(ticket, io, options) {
1232
1464
  captureStageSuccess(ticket.id, 'review');
1233
1465
  let handled = false;
1234
1466
  try {
1235
- handled = await runReviewCompletionAutomation(updated, project, reviewRunId, parsedStatus, parsedFindings, io, executionContext, initiation);
1467
+ handled = await runReviewCompletionAutomation(updated, project, reviewRunId, parsedStatus, parsedFindings, io, executionContext, initiation, persisted.persistedAutomation);
1236
1468
  }
1237
1469
  catch (automationError) {
1238
1470
  if (ticket.project_id && ticket.worktree_bundle_id) {