@straiffi/archon 1.8.3 → 1.8.4

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 (108) hide show
  1. package/dist/client/assets/{ActivityView-_WBeUWWL.js → ActivityView-CZwuxu-k.js} +1 -1
  2. package/dist/client/assets/{GitHubActionsDialog-CBb9kFXm.js → GitHubActionsDialog-m0WCUbsY.js} +1 -1
  3. package/dist/client/assets/VerificationDialog-D0LL2bcG.js +4 -0
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-YAcjb-IA.js → architectureDiagram-3BPJPVTR-Da0b3yts.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-BCPGaDad.js → blockDiagram-GPEHLZMM-DvzsxBUN.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-fJhOu4Ij.js → c4Diagram-AAUBKEIU-BEKrFtx2.js} +1 -1
  7. package/dist/client/assets/channel-CHuNNrYD.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-C-_Ugy45.js → chunk-2J33WTMH-BAKgBD7I.js} +1 -1
  9. package/dist/client/assets/{chunk-3OPIFGDE-D23LnWDf.js → chunk-3OPIFGDE-BKPTlfa5.js} +1 -1
  10. package/dist/client/assets/{chunk-5ZQYHXKU-Cc_biXgw.js → chunk-5ZQYHXKU-BDqDj4QJ.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-CSF9LkHi.js → chunk-727SXJPM-Cs2r0JtD.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-DHliNbxB.js → chunk-AQP2D5EJ-DQbaJPN-.js} +1 -1
  13. package/dist/client/assets/{chunk-CSCIHK7Q-BW11cpQ7.js → chunk-CSCIHK7Q-CxuNtNrM.js} +1 -1
  14. package/dist/client/assets/{chunk-KSCS5N6A-CKaTqnRs.js → chunk-KSCS5N6A-Cw9Q8JPi.js} +1 -1
  15. package/dist/client/assets/{chunk-L5ZTLDWV-DZwb33Ik.js → chunk-L5ZTLDWV-CtuZjEe8.js} +1 -1
  16. package/dist/client/assets/{chunk-LZXEDZCA-_cTPpjHZ.js → chunk-LZXEDZCA-BQQddDdh.js} +2 -2
  17. package/dist/client/assets/{chunk-ND2GUHAM-D0HwGHDO.js → chunk-ND2GUHAM-AK6DBTJe.js} +1 -1
  18. package/dist/client/assets/{chunk-NZK2D7GU-BZvw0sGh.js → chunk-NZK2D7GU-CWRA29FQ.js} +1 -1
  19. package/dist/client/assets/{chunk-O5CBEL6O-D4CkvYFL.js → chunk-O5CBEL6O-X-Oio9FM.js} +1 -1
  20. package/dist/client/assets/{chunk-WU5MYG2G-D8hEcq7j.js → chunk-WU5MYG2G-CETO3v_f.js} +1 -1
  21. package/dist/client/assets/classDiagram-4FO5ZUOK-BZQ7jv-A.js +1 -0
  22. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-7EFkByjs.js +1 -0
  23. package/dist/client/assets/{dagre-BM42HDAG-C8BK1hX6.js → dagre-BM42HDAG-Dt0-rccy.js} +1 -1
  24. package/dist/client/assets/{diagram-2AECGRRQ-BuY39sUj.js → diagram-2AECGRRQ-BWRTEjaU.js} +1 -1
  25. package/dist/client/assets/{diagram-5GNKFQAL-C0rXzbL5.js → diagram-5GNKFQAL-C2H5QcoG.js} +1 -1
  26. package/dist/client/assets/{diagram-KO2AKTUF-DLdOz8VS.js → diagram-KO2AKTUF-RBK3VSKM.js} +1 -1
  27. package/dist/client/assets/{diagram-LMA3HP47-C8VMfBnH.js → diagram-LMA3HP47-RGh2ZH4s.js} +1 -1
  28. package/dist/client/assets/{diagram-OG6HWLK6-B5Rk5j3I.js → diagram-OG6HWLK6-DRZPfcn0.js} +1 -1
  29. package/dist/client/assets/{erDiagram-TEJ5UH35-D8zk9anG.js → erDiagram-TEJ5UH35-BMjeuzHP.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-I6XJVG4X-yHoB6qvF.js → flowDiagram-I6XJVG4X-DoLSPv4x.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-6RSMTGT7-CdYAAP7d.js → ganttDiagram-6RSMTGT7-By8_6nMe.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-ZW6nKwWo.js → gitGraphDiagram-PVQCEYII-2rHM4CiD.js} +1 -1
  33. package/dist/client/assets/{index-CXhEwHXf.js → index-3dSPyX_Q.js} +104 -104
  34. package/dist/client/assets/index-DZqqZx53.css +2 -0
  35. package/dist/client/assets/{infoDiagram-5YYISTIA-CsCvEEDm.js → infoDiagram-5YYISTIA-DWOg8hUJ.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-L6YgNJNe.js → ishikawaDiagram-YF4QCWOH-C9YznvSm.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-JHISSGLW-CaDdypZa.js → journeyDiagram-JHISSGLW-DSQDDTNI.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-UN3LZRKU-CLgMcoKM.js → kanban-definition-UN3LZRKU-BkmCufHa.js} +1 -1
  39. package/dist/client/assets/{line-MwAlVJyR.js → line-cufAthH2.js} +1 -1
  40. package/dist/client/assets/{mermaid-parser.core-YnCNioX9.js → mermaid-parser.core-BzbhogG7.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-C-vjuXJ6.js → mermaid.core-CsQKe9Kh.js} +3 -3
  42. package/dist/client/assets/{mindmap-definition-RKZ34NQL-DRwJsFL4.js → mindmap-definition-RKZ34NQL-Ci6JrMb6.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-4H26LBE5-D8-qF3PX.js → pieDiagram-4H26LBE5-CU9I_3Q_.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BMhgWKrx.js → quadrantDiagram-W4KKPZXB-DytyOGrA.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-4Y6WPE33-rHi9GD9i.js → requirementDiagram-4Y6WPE33-CnrtyW9-.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BkXWM9ao.js → sankeyDiagram-5OEKKPKP-CGAC1tbH.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-YEoJ-ZTV.js → sequenceDiagram-3UESZ5HK-Cqlp3Fns.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-AJRCARHV-Cj-qFZz1.js → stateDiagram-AJRCARHV-DspbWEGS.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Bcxu99EH.js +1 -0
  50. package/dist/client/assets/{timeline-definition-PNZ67QCA-BSOzNHtd.js → timeline-definition-PNZ67QCA-Bd0SWRDD.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-CIIHVFJN-Bg8Uivxc.js → vennDiagram-CIIHVFJN-BZduwhis.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DU13oR2A.js → wardleyDiagram-YWT4CUSO-D6LrBEnX.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-2RQKCTM6-Bj3Qm_Tg.js → xychartDiagram-2RQKCTM6-CoLKir5a.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/server/db.js +10 -0
  56. package/dist/server/db.js.map +1 -1
  57. package/dist/server/index.js +57 -94
  58. package/dist/server/index.js.map +1 -1
  59. package/dist/server/lib/automationBuildContinuation.js +141 -0
  60. package/dist/server/lib/automationBuildContinuation.js.map +1 -0
  61. package/dist/server/lib/chatOperations.js +23 -5
  62. package/dist/server/lib/chatOperations.js.map +1 -1
  63. package/dist/server/lib/chatTargets.js +3 -129
  64. package/dist/server/lib/chatTargets.js.map +1 -1
  65. package/dist/server/lib/chats.js +4 -40
  66. package/dist/server/lib/chats.js.map +1 -1
  67. package/dist/server/lib/checkRepair.js +28 -1
  68. package/dist/server/lib/checkRepair.js.map +1 -1
  69. package/dist/server/lib/executionRetry.js +14 -6
  70. package/dist/server/lib/executionRetry.js.map +1 -1
  71. package/dist/server/lib/executionRetryContext.js +11 -3
  72. package/dist/server/lib/executionRetryContext.js.map +1 -1
  73. package/dist/server/lib/git.js +2 -2
  74. package/dist/server/lib/git.js.map +1 -1
  75. package/dist/server/lib/projectTargets.js +5 -0
  76. package/dist/server/lib/projectTargets.js.map +1 -1
  77. package/dist/server/lib/pullRequestContent.js +8 -4
  78. package/dist/server/lib/pullRequestContent.js.map +1 -1
  79. package/dist/server/lib/sessionTargets.js +166 -0
  80. package/dist/server/lib/sessionTargets.js.map +1 -0
  81. package/dist/server/lib/specOperations.js +6 -0
  82. package/dist/server/lib/specOperations.js.map +1 -1
  83. package/dist/server/lib/specReviewOperations.js +5 -0
  84. package/dist/server/lib/specReviewOperations.js.map +1 -1
  85. package/dist/server/lib/specWorkspaceActivity.js +35 -0
  86. package/dist/server/lib/specWorkspaceActivity.js.map +1 -0
  87. package/dist/server/lib/specs.js +15 -4
  88. package/dist/server/lib/specs.js.map +1 -1
  89. package/dist/server/lib/ticketAutomationCommit.js +1 -4
  90. package/dist/server/lib/ticketAutomationCommit.js.map +1 -1
  91. package/dist/server/lib/tickets.js +7 -4
  92. package/dist/server/lib/tickets.js.map +1 -1
  93. package/dist/server/lib/types.js.map +1 -1
  94. package/dist/server/workers/build.js +3 -1
  95. package/dist/server/workers/build.js.map +1 -1
  96. package/dist/server/workers/plan.js +1 -1
  97. package/dist/server/workers/plan.js.map +1 -1
  98. package/dist/server/workers/specReview.js +21 -4
  99. package/dist/server/workers/specReview.js.map +1 -1
  100. package/dist/server/workers/specSession.js +42 -19
  101. package/dist/server/workers/specSession.js.map +1 -1
  102. package/package.json +1 -1
  103. package/dist/client/assets/VerificationDialog-C5N4ftKi.js +0 -4
  104. package/dist/client/assets/channel-9ISsn-ke.js +0 -1
  105. package/dist/client/assets/classDiagram-4FO5ZUOK-Cb62gXr_.js +0 -1
  106. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CmX033GA.js +0 -1
  107. package/dist/client/assets/index-BcJjyvZL.css +0 -2
  108. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-C3hkJar3.js +0 -1
@@ -15,6 +15,7 @@ import { createProjectChatSession, resolveChatTool, resolveProjectChatPlanQuesti
15
15
  import { createProjectSpecSession, deleteProjectSpecSession, resolveProjectSpecPlanQuestionSet, stopProjectSpecSessionResponse, submitProjectSpecSessionMessage, updateProjectSpecSessionMode, } from './lib/specOperations.js';
16
16
  import { buildProjectSpecReviewRevisionDraft, getProjectSpecReviewFindings, startProjectSpecReview, stopProjectSpecReview, } from './lib/specReviewOperations.js';
17
17
  import { resolveChatSessionTarget } from './lib/chatTargets.js';
18
+ import { resolveSessionTargetForExecution } from './lib/sessionTargets.js';
18
19
  import { createChatMessage, getChatMessage, updateChatMessageContent } from './lib/chatMessages.js';
19
20
  import { attachChatTicketProposalsToMessageContent } from './lib/chatTicketProposals.js';
20
21
  import { createCorsOriginResolver, parseCorsOrigins } from './lib/cors.js';
@@ -43,6 +44,7 @@ import { decodeProjectMemoryCursor } from './lib/projectMemoryCursor.js';
43
44
  import { getProjectFileSuggestions } from './lib/projectFileSuggestions.js';
44
45
  import { getProjectFileManifest, ProjectFileContentError, ProjectFileTargetUnavailableError, readProjectFileContent } from './lib/projectFiles.js';
45
46
  import { updateProjectTargetSelection } from './lib/projectTargets.js';
47
+ import { hasActiveBundleSpecWork } from './lib/specWorkspaceActivity.js';
46
48
  import { exportProjectMemoryArchive, importProjectMemoryArchive } from './lib/projectMemoryTransfer.js';
47
49
  import { buildReviewFindingsResponse, computeDiffSignature, getLatestReviewRunForContext, listReviewFindings } from './lib/reviewFindings.js';
48
50
  import { formatBuildOpenQuestionAnswersPrompt, getLatestBuildOpenQuestionBatchStatus, hasActiveBuildOpenQuestionBatch, listBuildOpenQuestionsForBundle, loadBuildOpenQuestionsByBundleIds, } from './lib/buildOpenQuestions.js';
@@ -55,8 +57,8 @@ import { pausedRunBelongsToProgression, resolveAutomationResumeDecision } from '
55
57
  import { getAutomationStageDetails, } from './lib/automationStageDetails.js';
56
58
  import { buildAutomationFixInstruction, clearAutomationPause, countBlockingFindings, depthAtLeast, getBundleAutomationState, isAutomationDepth, recordAutomationPause, resolveAutomationConfig, upsertBundleAutomationState, } from './lib/ticketAutomation.js';
57
59
  import { resolveAutomationCheckGateResume, runAutomationCommitChain, runAutomationCommitOperation } from './lib/ticketAutomationCommit.js';
58
- import { canMutateBuildTicketCommitCheckpoint, resolveBuildTicketCommitCheckpointLineage } from './lib/buildTicketCommit.js';
59
- import { advanceSuccessfulBuildTicketAfterCheckpoint, compensateBuildSequenceTicketActivation } from './lib/buildCompletionContinuation.js';
60
+ import { resolveBuildTicketCommitCheckpointLineage, canMutateBuildTicketCommitCheckpoint } from './lib/buildTicketCommit.js';
61
+ import { continueAfterBuildCheckpoint } from './lib/automationBuildContinuation.js';
60
62
  import { getDiscoveredSkills, normalizeSkillNames } from './lib/skills.js';
61
63
  import { clearAllPreviewProxies } from './lib/previewProxy.js';
62
64
  import { createExecutionRun, addExecutionRunLink, addExecutionRunLinks, getExecutionRun, getLifecycleEvent, hasActiveBuildExecutionRunForTicket, getGitHubActionsExecutionDetails, getTestExecutionDetails, GitHubActionsDispatchConflictError, listExecutionRunLinks, listExecutionRuns, listLifecycleEvents, listProjectLifecycleEvents, toCanonicalLifecycleEvent, transitionExecutionRun, withExecutionRunTransaction, } from './lib/executionRuns.js';
@@ -814,7 +816,7 @@ const uniqueBundleBranchName = (name, projectId, options = {}) => {
814
816
  }
815
817
  return branch;
816
818
  };
817
- const createAutoPlanningBundle = (projectId) => {
819
+ const createAutoPlanningBundle = (projectId, baseBranch = null) => {
818
820
  const name = uniqueAutoBundleName(projectId);
819
821
  const branch = uniqueBundleBranchName(name, projectId);
820
822
  if (!branch) {
@@ -823,6 +825,7 @@ const createAutoPlanningBundle = (projectId) => {
823
825
  const bundle = createBundle({
824
826
  name,
825
827
  branch,
828
+ baseBranch,
826
829
  projectId,
827
830
  });
828
831
  if (!bundle) {
@@ -830,7 +833,7 @@ const createAutoPlanningBundle = (projectId) => {
830
833
  }
831
834
  return bundle;
832
835
  };
833
- const createAutoBundleForName = (name, projectId) => {
836
+ const createAutoBundleForName = (name, projectId, baseBranch = null) => {
834
837
  const trimmedName = name.trim();
835
838
  const requestedName = trimmedName || uniqueAutoBundleName(projectId);
836
839
  const branch = uniqueBundleBranchName(requestedName, projectId);
@@ -840,6 +843,7 @@ const createAutoBundleForName = (name, projectId) => {
840
843
  const bundle = createBundle({
841
844
  name: branch,
842
845
  branch,
846
+ baseBranch,
843
847
  projectId,
844
848
  });
845
849
  if (!bundle) {
@@ -2370,7 +2374,11 @@ const createPlanningAgentTickets = db.transaction(({ draft, initiativePrompt, pr
2370
2374
  return { tickets: createdTickets, dependencies: createdDependencies };
2371
2375
  });
2372
2376
  const createChatProposedTickets = db.transaction(({ project, chatSession, proposals, }) => {
2373
- const bundle = createAutoPlanningBundle(project.id);
2377
+ const targetResult = resolveSessionTargetForExecution(chatSession, project, 'Chat session');
2378
+ if (!targetResult.ok) {
2379
+ throw new HttpError(409, targetResult.error);
2380
+ }
2381
+ const bundle = createAutoPlanningBundle(project.id, targetResult.target.branch);
2374
2382
  const { createdTickets, createdDependencies, ticketIdsByKey } = materializeTicketsWithDependencies({
2375
2383
  options: {
2376
2384
  projectId: project.id,
@@ -3087,11 +3095,21 @@ app.post('/projects/:id/spec-sessions/:sessionId/snapshots/:snapshotId/create-ti
3087
3095
  error: `Spec tickets must include testing or verification guidance in their descriptions before they can be created on the board. Missing guidance in: ${ticketKeys}`,
3088
3096
  });
3089
3097
  }
3098
+ let generatedBundleId = null;
3090
3099
  try {
3100
+ const targetResult = resolveSessionTargetForExecution(specSession, project, 'Spec session');
3101
+ if (!targetResult.ok) {
3102
+ throw new HttpError(409, targetResult.error);
3103
+ }
3104
+ const target = targetResult.target;
3091
3105
  const bundleName = specSession.external_source?.external_key ?? '';
3092
3106
  const bundle = bundleName
3093
- ? createAutoBundleForName(bundleName, project.id)
3094
- : createAutoPlanningBundle(project.id);
3107
+ ? createAutoBundleForName(bundleName, project.id, target.branch)
3108
+ : createAutoPlanningBundle(project.id, target.branch);
3109
+ if (!bundle || isProjectRootBundle(bundle)) {
3110
+ throw new HttpError(409, 'Unable to create a bundle for this Spec snapshot.');
3111
+ }
3112
+ generatedBundleId = bundle.id;
3095
3113
  const ticketsForMaterialization = snapshot.tickets.map(ticket => {
3096
3114
  const appendices = [
3097
3115
  buildVisualizationAppendix(ticket.relevant_visualizations ?? [], snapshot),
@@ -3130,6 +3148,9 @@ app.post('/projects/:id/spec-sessions/:sessionId/snapshots/:snapshotId/create-ti
3130
3148
  });
3131
3149
  }
3132
3150
  catch (error) {
3151
+ if (generatedBundleId) {
3152
+ cleanupBundleIfEmpty(generatedBundleId, project);
3153
+ }
3133
3154
  if (error instanceof HttpError) {
3134
3155
  return res.status(error.status).json({ error: error.message });
3135
3156
  }
@@ -3149,6 +3170,10 @@ app.post('/projects/:id/spec-sessions/:sessionId/snapshots/:snapshotId/implement
3149
3170
  if (!snapshot || snapshot.spec_session_id !== specSession.id) {
3150
3171
  return res.status(404).json({ error: 'Not found' });
3151
3172
  }
3173
+ const targetResult = resolveSessionTargetForExecution(specSession, project, 'Spec session');
3174
+ if (!targetResult.ok) {
3175
+ return res.status(409).json({ error: targetResult.error });
3176
+ }
3152
3177
  const draftText = buildSpecImplementationDraft({ snapshot, specSession });
3153
3178
  return res.status(200).json({
3154
3179
  draft_text: draftText,
@@ -3169,11 +3194,15 @@ app.post('/projects/:id/spec-sessions/:sessionId/snapshots/:snapshotId/export',
3169
3194
  if (!snapshot || snapshot.spec_session_id !== specSession.id) {
3170
3195
  return res.status(404).json({ error: 'Not found' });
3171
3196
  }
3197
+ const targetResult = resolveSessionTargetForExecution(specSession, project, 'Spec session');
3198
+ if (!targetResult.ok) {
3199
+ return res.status(409).json({ error: targetResult.error });
3200
+ }
3172
3201
  try {
3173
3202
  const overwrite = req.body?.overwrite === true;
3174
3203
  const collision = checkSpecExportCollision({
3175
3204
  specSession,
3176
- repoPath: project.repo_path,
3205
+ repoPath: targetResult.target.cwd,
3177
3206
  });
3178
3207
  if (collision && !overwrite) {
3179
3208
  return res.status(409).json({
@@ -3184,7 +3213,7 @@ app.post('/projects/:id/spec-sessions/:sessionId/snapshots/:snapshotId/export',
3184
3213
  const result = exportSpecToDocs({
3185
3214
  snapshot,
3186
3215
  specSession,
3187
- repoPath: project.repo_path,
3216
+ repoPath: targetResult.target.cwd,
3188
3217
  });
3189
3218
  return res.status(201).json({
3190
3219
  path: result.filePath,
@@ -3748,6 +3777,7 @@ app.post('/bundles', async (req, res) => {
3748
3777
  const bundle = createBundle({
3749
3778
  name,
3750
3779
  branch,
3780
+ baseBranch: requestedBaseBranch || null,
3751
3781
  projectId: result.project.id,
3752
3782
  });
3753
3783
  if (!bundle) {
@@ -3783,6 +3813,9 @@ app.delete('/bundles/:id', (req, res) => {
3783
3813
  if (hasActiveBundleCheckWork(result.project.id, bundle.id)) {
3784
3814
  return res.status(409).json({ error: 'Cannot delete a bundle while check work is active' });
3785
3815
  }
3816
+ if (hasActiveBundleSpecWork(result.project.id, bundle.id)) {
3817
+ return res.status(409).json({ error: 'Cannot delete a bundle while Spec work is active' });
3818
+ }
3786
3819
  if (countBundleTickets(bundle.id, result.project.id) > 0) {
3787
3820
  return res.status(409).json({ error: 'Cannot delete a bundle that is still used by tickets' });
3788
3821
  }
@@ -3792,6 +3825,9 @@ app.delete('/bundles/:id', (req, res) => {
3792
3825
  const affectedChatSessionIds = listChatSessions(result.project.id)
3793
3826
  .filter(chatSession => chatSession.target_bundle_id === bundle.id)
3794
3827
  .map(chatSession => chatSession.id);
3828
+ const affectedSpecSessionIds = listSpecSessions(result.project.id)
3829
+ .filter(specSession => specSession.target_bundle_id === bundle.id)
3830
+ .map(specSession => specSession.id);
3795
3831
  try {
3796
3832
  deleteWorktree(bundle.branch, result.project);
3797
3833
  }
@@ -3807,6 +3843,12 @@ app.delete('/bundles/:id', (req, res) => {
3807
3843
  io.emit('chat-session:updated', updatedChatSession);
3808
3844
  }
3809
3845
  });
3846
+ affectedSpecSessionIds.forEach(specSessionId => {
3847
+ const updatedSpecSession = getProjectSpecSession(result.project.id, specSessionId);
3848
+ if (updatedSpecSession) {
3849
+ io.emit('spec-session:updated', updatedSpecSession);
3850
+ }
3851
+ });
3810
3852
  res.status(204).end();
3811
3853
  });
3812
3854
  app.patch('/bundles/:id', (req, res) => {
@@ -4345,94 +4387,15 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
4345
4387
  return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
4346
4388
  }
4347
4389
  const workflowContext = validateWorkflowContext(checkpointLineage.progression.metadata);
4348
- const continuation = advanceSuccessfulBuildTicketAfterCheckpoint({
4390
+ await continueAfterBuildCheckpoint({
4349
4391
  ticket: owner,
4392
+ project: result.project,
4393
+ parentRunId: checkpointLineage.parent.id,
4394
+ workflowContext,
4395
+ io,
4350
4396
  emitUpdatedTickets,
4397
+ commitCheckpoint: false,
4351
4398
  });
4352
- if (continuation.nextBuild) {
4353
- const nextBuild = continuation.nextBuild;
4354
- const nextSequenceId = continuation.nextSequenceId;
4355
- const scheduling = startBuild(nextBuild, io, {
4356
- initiation: {
4357
- initiator: 'automation',
4358
- causalParentRunId: checkpointLineage.parent.id,
4359
- workflowContext,
4360
- },
4361
- sequenceId: nextSequenceId,
4362
- accept: () => {
4363
- const transition = prepareTicketForBuild(nextBuild.id, {
4364
- ignoredBlockerTicketIds: continuation.ignoredBlockerTicketIds,
4365
- });
4366
- if (!transition.ok) {
4367
- throw new Error(transition.error);
4368
- }
4369
- if (!nextSequenceId) {
4370
- throw new Error('Build sequence no longer exists');
4371
- }
4372
- activateQueuedBuildSequenceTicket(nextSequenceId, transition.ticket.id);
4373
- return {
4374
- ticket: transition.ticket,
4375
- sequenceId: nextSequenceId,
4376
- compensate: () => compensateBuildSequenceTicketActivation(nextSequenceId, transition.ticket.id),
4377
- };
4378
- },
4379
- });
4380
- if (!scheduling.accepted) {
4381
- const stoppedSequence = stopBuildSequence(nextBuild.id);
4382
- emitUpdatedTickets(stoppedSequence.affectedTicketIds);
4383
- }
4384
- }
4385
- else if (!continuation.nextTicketId && owner.project_id && owner.worktree_bundle_id) {
4386
- const resumedOwnerForReview = getTicket(owner.id) ?? owner;
4387
- const liveProject = getEffectiveProject(resumedOwnerForReview) ?? result.project;
4388
- const automationConfig = resolveAutomationConfig(resumedOwnerForReview.bundle ?? null, liveProject);
4389
- const autoReviewEnabled = depthAtLeast(automationConfig.depth, 'auto_review');
4390
- const latestBundleState = getBundleAutomationState(owner.project_id, owner.worktree_bundle_id);
4391
- const openQuestions = listBuildOpenQuestionsForBundle(owner.project_id, owner.worktree_bundle_id);
4392
- const shouldPauseForOpenQuestions = autoReviewEnabled
4393
- && automationConfig.pauseOnOpenQuestions
4394
- && openQuestions.length > 0;
4395
- if (shouldPauseForOpenQuestions) {
4396
- const reviewProgression = ensureAutomationProgression({
4397
- projectId: owner.project_id,
4398
- bundleId: owner.worktree_bundle_id,
4399
- ticketId: owner.id,
4400
- ticketTitle: owner.title,
4401
- parentRunId: checkpointLineage.parent.id,
4402
- workflowContext,
4403
- broadcaster: io,
4404
- });
4405
- waitAutomationProgression({
4406
- projectId: owner.project_id,
4407
- bundleId: owner.worktree_bundle_id,
4408
- progressionRunId: reviewProgression.runId,
4409
- pauseCode: 'open_questions',
4410
- pauseReason: `${openQuestions.length} unresolved Build open question${openQuestions.length === 1 ? '' : 's'}`,
4411
- reasonCode: 'precondition_failed',
4412
- pausedStageRunId: checkpointLineage.parent.id,
4413
- broadcaster: io,
4414
- });
4415
- }
4416
- else if (autoReviewEnabled && (!latestBundleState?.automation_paused_reason || latestBundleState.automation_paused_code === 'open_questions')) {
4417
- const reviewProgression = ensureAutomationProgression({
4418
- projectId: owner.project_id,
4419
- bundleId: owner.worktree_bundle_id,
4420
- ticketId: owner.id,
4421
- ticketTitle: owner.title,
4422
- parentRunId: checkpointLineage.parent.id,
4423
- workflowContext,
4424
- broadcaster: io,
4425
- });
4426
- await runAutomationReviewGateOrPause({
4427
- projectId: owner.project_id,
4428
- bundleId: owner.worktree_bundle_id,
4429
- progressionRunId: reviewProgression.runId,
4430
- broadcaster: io,
4431
- pausedStageRunId: checkpointLineage.parent.id,
4432
- initiation: { workflowContext },
4433
- });
4434
- }
4435
- }
4436
4399
  const resumedOwner = getTicket(owner.id);
4437
4400
  if (resumedOwner) {
4438
4401
  io.emit('ticket:updated', resumedOwner);