@straiffi/archon 1.7.3 → 1.7.5

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 (136) hide show
  1. package/dist/client/assets/ActivityView-CT9iCPyc.js +1 -0
  2. package/dist/client/assets/GitHubActionsDialog-BA6xyQeX.js +1 -0
  3. package/dist/client/assets/TestsDialog-D2qrW5Ug.js +4 -0
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-BzzGotac.js → architectureDiagram-3BPJPVTR-R6v7w0Nl.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-DIcqLHKg.js → blockDiagram-GPEHLZMM-CHlBlnJq.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-Hc1Sqmhs.js → c4Diagram-AAUBKEIU-BvZKqUc6.js} +1 -1
  7. package/dist/client/assets/channel-Ckv8cPSf.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-CWSwjsgk.js → chunk-2J33WTMH-2gsIkBeO.js} +1 -1
  9. package/dist/client/assets/{chunk-3OPIFGDE-BnX3DljN.js → chunk-3OPIFGDE-CcqPBjqx.js} +1 -1
  10. package/dist/client/assets/{chunk-5ZQYHXKU-EyyJlI-d.js → chunk-5ZQYHXKU-DX_wK5Rp.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-Ci6CfxWj.js → chunk-727SXJPM-B9WUxzJL.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-RKZNsJTm.js → chunk-AQP2D5EJ-BG0s75_H.js} +1 -1
  13. package/dist/client/assets/{chunk-CSCIHK7Q-DBf0Euoj.js → chunk-CSCIHK7Q-CyNcvvqW.js} +1 -1
  14. package/dist/client/assets/{chunk-KSCS5N6A-BmLQI_2S.js → chunk-KSCS5N6A-CWVy_Wi0.js} +1 -1
  15. package/dist/client/assets/{chunk-L5ZTLDWV-DKwVAb8G.js → chunk-L5ZTLDWV-3dsXRB0X.js} +1 -1
  16. package/dist/client/assets/{chunk-LZXEDZCA-axotKkbz.js → chunk-LZXEDZCA-B09QSB9z.js} +2 -2
  17. package/dist/client/assets/{chunk-ND2GUHAM-BBlJ4nXC.js → chunk-ND2GUHAM-BxzAdxTN.js} +1 -1
  18. package/dist/client/assets/{chunk-NZK2D7GU-BKJGoZZt.js → chunk-NZK2D7GU-D4ZfFwbB.js} +1 -1
  19. package/dist/client/assets/{chunk-O5CBEL6O-C-Ghigo1.js → chunk-O5CBEL6O-BxQULuKS.js} +1 -1
  20. package/dist/client/assets/{chunk-WU5MYG2G-CCzVWYcd.js → chunk-WU5MYG2G-CEJwp0Ry.js} +1 -1
  21. package/dist/client/assets/classDiagram-4FO5ZUOK-B-N-arsi.js +1 -0
  22. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-PTX7YrlL.js +1 -0
  23. package/dist/client/assets/{dagre-BM42HDAG-rwIdPcjI.js → dagre-BM42HDAG-Dx_V7OMy.js} +1 -1
  24. package/dist/client/assets/{diagram-2AECGRRQ-DJWxOQsg.js → diagram-2AECGRRQ-Cc3VTg9P.js} +1 -1
  25. package/dist/client/assets/{diagram-5GNKFQAL-C-mQeFtd.js → diagram-5GNKFQAL-mJ1xhs2G.js} +1 -1
  26. package/dist/client/assets/{diagram-KO2AKTUF-BAu6u4sR.js → diagram-KO2AKTUF-CLyT9ac8.js} +1 -1
  27. package/dist/client/assets/{diagram-LMA3HP47-U6CbvBn0.js → diagram-LMA3HP47-CVr47yij.js} +1 -1
  28. package/dist/client/assets/{diagram-OG6HWLK6-DNTwFcs1.js → diagram-OG6HWLK6-Dp5TJ8lM.js} +1 -1
  29. package/dist/client/assets/{erDiagram-TEJ5UH35-DaHX6Xw5.js → erDiagram-TEJ5UH35-BBxwyu0r.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-I6XJVG4X-B9hKqL2l.js → flowDiagram-I6XJVG4X-CuFYzVQ-.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-6RSMTGT7-BQ1HPHVO.js → ganttDiagram-6RSMTGT7-aRQarcQY.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-BBRcsmMK.js → gitGraphDiagram-PVQCEYII-B3mHSkBv.js} +1 -1
  33. package/dist/client/assets/index-BJGHMaMz.css +2 -0
  34. package/dist/client/assets/index-Oz0S7bcT.js +273 -0
  35. package/dist/client/assets/{infoDiagram-5YYISTIA-DUoNiSGC.js → infoDiagram-5YYISTIA-BSyryQJ9.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-DJN1y2w8.js → ishikawaDiagram-YF4QCWOH-BAGbrtSG.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-JHISSGLW-BxLLMT70.js → journeyDiagram-JHISSGLW-CDTht6k6.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-UN3LZRKU-BdCPistJ.js → kanban-definition-UN3LZRKU-BDtuMp0H.js} +1 -1
  39. package/dist/client/assets/{line-4mIgXsZd.js → line-n0aNicmA.js} +1 -1
  40. package/dist/client/assets/{mermaid-parser.core-Pu1QGWJ-.js → mermaid-parser.core-CdE4vZ4k.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-DnH9DrgZ.js → mermaid.core-C546CQj1.js} +3 -3
  42. package/dist/client/assets/{mindmap-definition-RKZ34NQL-DqE4ba3C.js → mindmap-definition-RKZ34NQL-BwO779am.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-4H26LBE5-Crjy9ejf.js → pieDiagram-4H26LBE5-DU1k71kn.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BUSZ2VB7.js → quadrantDiagram-W4KKPZXB-CvM5lyQg.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-4Y6WPE33-CBhFKEoV.js → requirementDiagram-4Y6WPE33-DDBdSq0B.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CgdbY41v.js → sankeyDiagram-5OEKKPKP-ELM2mu81.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-hkp_H7AE.js → sequenceDiagram-3UESZ5HK-D7uF-nRR.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-AJRCARHV-C6x45RWp.js → stateDiagram-AJRCARHV-IhaGRCbG.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-ceHFQMIT.js +1 -0
  50. package/dist/client/assets/{timeline-definition-PNZ67QCA-CjnZWttQ.js → timeline-definition-PNZ67QCA-zHJGFvZ8.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-CIIHVFJN-DU5ZyYju.js → vennDiagram-CIIHVFJN-EG1HFfD7.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-CaCEnCB0.js → wardleyDiagram-YWT4CUSO-DdQevxXg.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-2RQKCTM6-Ce3xa37U.js → xychartDiagram-2RQKCTM6-BjihNG0E.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/server/db.js +283 -0
  56. package/dist/server/db.js.map +1 -1
  57. package/dist/server/index.js +390 -117
  58. package/dist/server/index.js.map +1 -1
  59. package/dist/server/lib/attachments.js +40 -0
  60. package/dist/server/lib/attachments.js.map +1 -1
  61. package/dist/server/lib/buildOpenQuestionOperations.js +17 -2
  62. package/dist/server/lib/buildOpenQuestionOperations.js.map +1 -1
  63. package/dist/server/lib/buildSequences.js +25 -0
  64. package/dist/server/lib/buildSequences.js.map +1 -1
  65. package/dist/server/lib/bundleReviewOperations.js +9 -0
  66. package/dist/server/lib/bundleReviewOperations.js.map +1 -1
  67. package/dist/server/lib/chatOperations.js +40 -16
  68. package/dist/server/lib/chatOperations.js.map +1 -1
  69. package/dist/server/lib/executionArtifactCleanupState.js +12 -0
  70. package/dist/server/lib/executionArtifactCleanupState.js.map +1 -0
  71. package/dist/server/lib/executionArtifactRetention.js +405 -0
  72. package/dist/server/lib/executionArtifactRetention.js.map +1 -0
  73. package/dist/server/lib/executionArtifacts.js +705 -0
  74. package/dist/server/lib/executionArtifacts.js.map +1 -0
  75. package/dist/server/lib/executionRetry.js +702 -0
  76. package/dist/server/lib/executionRetry.js.map +1 -0
  77. package/dist/server/lib/executionRetryContext.js +510 -0
  78. package/dist/server/lib/executionRetryContext.js.map +1 -0
  79. package/dist/server/lib/executionRetryPredicates.js +74 -0
  80. package/dist/server/lib/executionRetryPredicates.js.map +1 -0
  81. package/dist/server/lib/executionRuns.js +203 -40
  82. package/dist/server/lib/executionRuns.js.map +1 -1
  83. package/dist/server/lib/executionTimeline.js +73 -0
  84. package/dist/server/lib/executionTimeline.js.map +1 -0
  85. package/dist/server/lib/lightweightPrompt.js +2 -2
  86. package/dist/server/lib/lightweightPrompt.js.map +1 -1
  87. package/dist/server/lib/projects.js +16 -3
  88. package/dist/server/lib/projects.js.map +1 -1
  89. package/dist/server/lib/pullRequestContent.js +148 -25
  90. package/dist/server/lib/pullRequestContent.js.map +1 -1
  91. package/dist/server/lib/queue.js +1 -0
  92. package/dist/server/lib/queue.js.map +1 -1
  93. package/dist/server/lib/runtimePaths.js +7 -0
  94. package/dist/server/lib/runtimePaths.js.map +1 -1
  95. package/dist/server/lib/specOperations.js +51 -16
  96. package/dist/server/lib/specOperations.js.map +1 -1
  97. package/dist/server/lib/specReviewContext.js +10 -4
  98. package/dist/server/lib/specReviewContext.js.map +1 -1
  99. package/dist/server/lib/specReviewOperations.js +33 -5
  100. package/dist/server/lib/specReviewOperations.js.map +1 -1
  101. package/dist/server/lib/testSessions.js +2 -5
  102. package/dist/server/lib/testSessions.js.map +1 -1
  103. package/dist/server/lib/ticketAutomationCommit.js +11 -4
  104. package/dist/server/lib/ticketAutomationCommit.js.map +1 -1
  105. package/dist/server/lib/ticketFollowUpOperations.js +74 -27
  106. package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
  107. package/dist/server/lib/ticketMessages.js +16 -0
  108. package/dist/server/lib/ticketMessages.js.map +1 -1
  109. package/dist/server/lib/types.js +12 -0
  110. package/dist/server/lib/types.js.map +1 -1
  111. package/dist/server/workers/build.js +1 -1
  112. package/dist/server/workers/build.js.map +1 -1
  113. package/dist/server/workers/chat.js +26 -20
  114. package/dist/server/workers/chat.js.map +1 -1
  115. package/dist/server/workers/followUp.js +118 -15
  116. package/dist/server/workers/followUp.js.map +1 -1
  117. package/dist/server/workers/plan.js +23 -4
  118. package/dist/server/workers/plan.js.map +1 -1
  119. package/dist/server/workers/planFollowUp.js +12 -9
  120. package/dist/server/workers/planFollowUp.js.map +1 -1
  121. package/dist/server/workers/review.js +2 -2
  122. package/dist/server/workers/review.js.map +1 -1
  123. package/dist/server/workers/specReview.js +2 -2
  124. package/dist/server/workers/specReview.js.map +1 -1
  125. package/dist/server/workers/specSession.js +29 -22
  126. package/dist/server/workers/specSession.js.map +1 -1
  127. package/package.json +1 -1
  128. package/dist/client/assets/ActivityView-DTEenQas.js +0 -1
  129. package/dist/client/assets/GitHubActionsDialog-BKWHonUS.js +0 -1
  130. package/dist/client/assets/TestsDialog-cBaoffCs.js +0 -4
  131. package/dist/client/assets/channel-Cym17waa.js +0 -1
  132. package/dist/client/assets/classDiagram-4FO5ZUOK-BcigvvIC.js +0 -1
  133. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-3AdSuE0N.js +0 -1
  134. package/dist/client/assets/index-CEMaJCPS.js +0 -273
  135. package/dist/client/assets/index-lWftq65U.css +0 -2
  136. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DVzPjRPN.js +0 -1
@@ -0,0 +1,74 @@
1
+ export const RETRYABLE_AGENT_STATUSES = new Set(['failed', 'interrupted']);
2
+ export const RETRYABLE_TEST_STATUSES = new Set(['failed', 'cancelled', 'interrupted']);
3
+ export const RETRYABLE_EXECUTION_KINDS = new Set([
4
+ 'test',
5
+ 'plan',
6
+ 'build',
7
+ 'review',
8
+ 'build_follow_up',
9
+ 'plan_follow_up',
10
+ 'chat_turn',
11
+ 'spec_turn',
12
+ 'spec_review',
13
+ ]);
14
+ export const LATER_RUN_SUPERSEDED_KINDS = new Set([
15
+ 'plan',
16
+ 'build',
17
+ 'review',
18
+ 'build_follow_up',
19
+ 'plan_follow_up',
20
+ 'chat_turn',
21
+ 'spec_turn',
22
+ ]);
23
+ export const isLaterRunSupersededKind = (kind) => LATER_RUN_SUPERSEDED_KINDS.has(kind);
24
+ export const isRetryableExecutionStatus = (kind, status) => ((kind === 'test' ? RETRYABLE_TEST_STATUSES : RETRYABLE_AGENT_STATUSES).has(status));
25
+ export const parseExecutionRetryTestSelection = (value) => {
26
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
27
+ return null;
28
+ }
29
+ const selection = value;
30
+ if (selection.kind === 'all') {
31
+ return { kind: 'all' };
32
+ }
33
+ if (selection.kind === 'file' && typeof selection.path === 'string') {
34
+ return { kind: 'file', path: selection.path };
35
+ }
36
+ if (selection.kind === 'case' && typeof selection.path === 'string' && typeof selection.name === 'string') {
37
+ return { kind: 'case', path: selection.path, name: selection.name };
38
+ }
39
+ return null;
40
+ };
41
+ export const resolveExecutionRetryRunConfig = (source) => {
42
+ const tool = source.metadata?.retry_tool;
43
+ const model = source.metadata?.retry_model;
44
+ const variant = source.metadata?.retry_variant;
45
+ const skills = source.metadata?.retry_skills;
46
+ if ((tool !== 'opencode' && tool !== 'claude')
47
+ || (model !== null && typeof model !== 'string')
48
+ || (variant !== null && typeof variant !== 'string')
49
+ || !Array.isArray(skills)
50
+ || skills.some(skill => typeof skill !== 'string')) {
51
+ return null;
52
+ }
53
+ return { tool: tool, model, variant, skills: skills };
54
+ };
55
+ export const resolveExecutionRetrySessionMode = (source) => {
56
+ const mode = source.metadata?.retry_mode;
57
+ if (source.kind === 'chat_turn') {
58
+ return mode === 'plan' || mode === 'build' ? mode : null;
59
+ }
60
+ if (source.kind === 'spec_turn') {
61
+ return mode === 'standard' || mode === 'grill' ? mode : null;
62
+ }
63
+ return null;
64
+ };
65
+ export const hasExecutionRetryAutomationOwnership = ({ source, parentKind, automationPausedStageRunId, }) => {
66
+ if (source.initiated_by !== 'user' || source.metadata?.build_open_question_batch_id) {
67
+ return true;
68
+ }
69
+ if (source.parent_run_id && (parentKind === 'automation_progression' || parentKind === 'automation_review_fix')) {
70
+ return true;
71
+ }
72
+ return automationPausedStageRunId === source.id;
73
+ };
74
+ //# sourceMappingURL=executionRetryPredicates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executionRetryPredicates.js","sourceRoot":"","sources":["../../../server/lib/executionRetryPredicates.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,12 @@ const GENERIC_RETRY_DENIED_KINDS = new Set([
17
17
  'automation_push',
18
18
  'automation_pull_request',
19
19
  ]);
20
+ const ARTIFACT_AUDIT_EVENT_TYPES = new Set([
21
+ 'artifact_created',
22
+ 'artifact_availability_changed',
23
+ 'artifact_deletion_failed',
24
+ 'artifact_expired',
25
+ ]);
20
26
  export class GitHubActionsDispatchConflictError extends Error {
21
27
  executionRunId;
22
28
  constructor(executionRunId) {
@@ -27,7 +33,7 @@ export class GitHubActionsDispatchConflictError extends Error {
27
33
  }
28
34
  const getRunStatement = db.prepare('SELECT * FROM execution_runs WHERE id = ? AND project_id = ?');
29
35
  const getRunByIdStatement = db.prepare('SELECT * FROM execution_runs WHERE id = ?');
30
- const projectExistsStatement = db.prepare('SELECT 1 FROM projects WHERE id = ?');
36
+ const projectExistsStatement = db.prepare('SELECT 1 FROM projects WHERE id = ? AND deletion_started_at IS NULL');
31
37
  const insertRunStatement = db.prepare(`
32
38
  INSERT INTO execution_runs (
33
39
  id, project_id, kind, status, domain_status, source_kind, source_id, source_label, source_url,
@@ -62,6 +68,44 @@ const getRunLinkStatement = db.prepare(`
62
68
  WHERE execution_run_id = ? AND project_id = ? AND entity_type = ? AND entity_id = ? AND relationship = ?
63
69
  `);
64
70
  const getDirectRetryChildStatement = db.prepare('SELECT id FROM execution_runs WHERE retry_of_run_id = ? LIMIT 1');
71
+ const hasLaterRunForPrimaryEntityStatement = db.prepare(`
72
+ SELECT 1
73
+ FROM execution_runs candidate
74
+ INNER JOIN execution_run_links candidate_primary
75
+ ON candidate_primary.execution_run_id = candidate.id
76
+ AND candidate_primary.project_id = candidate.project_id
77
+ AND candidate_primary.relationship = 'primary'
78
+ INNER JOIN execution_runs source ON source.id = ? AND source.project_id = ?
79
+ WHERE candidate.project_id = ?
80
+ AND candidate.kind = ?
81
+ AND candidate_primary.entity_type = ?
82
+ AND candidate_primary.entity_id = ?
83
+ AND candidate.rowid > source.rowid
84
+ AND candidate.retry_of_run_id IS NOT source.id
85
+ LIMIT 1
86
+ `);
87
+ const hasLaterSpecReviewForSnapshotStatement = db.prepare(`
88
+ SELECT 1
89
+ FROM execution_runs candidate
90
+ INNER JOIN execution_run_links candidate_primary
91
+ ON candidate_primary.execution_run_id = candidate.id
92
+ AND candidate_primary.project_id = candidate.project_id
93
+ AND candidate_primary.relationship = 'primary'
94
+ AND candidate_primary.entity_type = 'spec_session'
95
+ INNER JOIN execution_run_links candidate_snapshot
96
+ ON candidate_snapshot.execution_run_id = candidate.id
97
+ AND candidate_snapshot.project_id = candidate.project_id
98
+ AND candidate_snapshot.relationship = 'context'
99
+ AND candidate_snapshot.entity_type = 'spec_snapshot'
100
+ INNER JOIN execution_runs source ON source.id = ? AND source.project_id = ?
101
+ WHERE candidate.project_id = ?
102
+ AND candidate.kind = 'spec_review'
103
+ AND candidate_primary.entity_id = ?
104
+ AND candidate_snapshot.entity_id = ?
105
+ AND candidate.rowid > source.rowid
106
+ AND candidate.retry_of_run_id IS NOT source.id
107
+ LIMIT 1
108
+ `);
65
109
  const getEventStatement = db.prepare('SELECT * FROM lifecycle_events WHERE id = ? AND project_id = ?');
66
110
  const insertTestDetailsStatement = db.prepare(`
67
111
  INSERT INTO test_execution_details (
@@ -183,26 +227,34 @@ const boundTestOutput = (output, alreadyTruncated = false) => {
183
227
  outputTruncated: true,
184
228
  };
185
229
  };
186
- const isSourceAvailable = (row) => {
187
- if (!row.source_kind || !row.source_id) {
188
- return false;
189
- }
190
- if (row.source_kind === 'ticket') {
191
- return Boolean(db.prepare('SELECT 1 FROM tickets WHERE id = ? AND project_id = ?').get(row.source_id, row.project_id));
192
- }
193
- if (row.source_kind === 'project') {
194
- return Boolean(projectExistsStatement.get(row.source_id));
195
- }
196
- if (row.source_kind === 'bundle') {
197
- return Boolean(db.prepare('SELECT 1 FROM worktree_bundles WHERE id = ? AND project_id = ?').get(row.source_id, row.project_id));
198
- }
199
- if (row.source_kind === 'chat_session') {
200
- return Boolean(db.prepare('SELECT 1 FROM chat_sessions WHERE id = ? AND project_id = ?').get(row.source_id, row.project_id));
201
- }
202
- if (row.source_kind === 'spec_session') {
203
- return Boolean(db.prepare('SELECT 1 FROM spec_sessions WHERE id = ? AND project_id = ?').get(row.source_id, row.project_id));
230
+ const SOURCE_TABLES = {
231
+ ticket: 'tickets',
232
+ project: 'projects',
233
+ bundle: 'worktree_bundles',
234
+ chat_session: 'chat_sessions',
235
+ spec_session: 'spec_sessions',
236
+ };
237
+ const loadSourceAvailability = (rows) => {
238
+ const availability = new Map();
239
+ for (const [sourceKind, table] of Object.entries(SOURCE_TABLES)) {
240
+ const sourceRows = rows.filter(row => row.source_kind === sourceKind && row.source_id);
241
+ const ids = [...new Set(sourceRows.map(row => row.source_id))];
242
+ if (ids.length === 0) {
243
+ continue;
244
+ }
245
+ const projectIds = [...new Set(sourceRows.map(row => row.project_id))];
246
+ const records = db.prepare(`
247
+ SELECT id, ${sourceKind === 'project' ? 'id' : 'project_id'} AS project_id
248
+ FROM ${table}
249
+ WHERE id IN (${ids.map(() => '?').join(', ')})
250
+ AND ${sourceKind === 'project' ? 'id' : 'project_id'} IN (${projectIds.map(() => '?').join(', ')})
251
+ `).all(...ids, ...projectIds);
252
+ const availableKeys = new Set(records.map(record => `${record.project_id}:${sourceKind}:${record.id}`));
253
+ for (const row of sourceRows) {
254
+ availability.set(`${row.project_id}:${sourceKind}:${row.source_id}`, availableKeys.has(`${row.project_id}:${sourceKind}:${row.source_id}`));
255
+ }
204
256
  }
205
- return Boolean(safeSnapshotUrl(row.source_url));
257
+ return availability;
206
258
  };
207
259
  const safeSnapshotUrl = (value) => {
208
260
  if (!value) {
@@ -216,7 +268,7 @@ const safeSnapshotUrl = (value) => {
216
268
  return null;
217
269
  }
218
270
  };
219
- const serializeRun = (row) => ({
271
+ const serializeRun = (row, sourceAvailability) => ({
220
272
  id: row.id,
221
273
  project_id: row.project_id,
222
274
  kind: row.kind,
@@ -227,7 +279,7 @@ const serializeRun = (row) => ({
227
279
  kind: row.source_kind,
228
280
  id: row.source_id,
229
281
  label: row.source_label,
230
- available: isSourceAvailable(row),
282
+ available: sourceAvailability?.get(`${row.project_id}:${row.source_kind}:${row.source_id}`) ?? loadSourceAvailability([row]).get(`${row.project_id}:${row.source_kind}:${row.source_id}`) ?? Boolean(safeSnapshotUrl(row.source_url)),
231
283
  url: safeSnapshotUrl(row.source_url),
232
284
  }
233
285
  : null,
@@ -255,10 +307,12 @@ const serializeEvent = (row) => ({
255
307
  metadata: parseStoredObject(row.metadata_json),
256
308
  });
257
309
  const PUBLIC_EVENT_METADATA_KEYS = new Set([
310
+ 'artifact_id',
258
311
  'candidate_run_ids',
259
312
  'conclusion',
260
313
  'input_names',
261
314
  'provider_run_id',
315
+ 'prior_availability',
262
316
  'ref',
263
317
  'resolution_attempts',
264
318
  'trigger',
@@ -266,7 +320,18 @@ const PUBLIC_EVENT_METADATA_KEYS = new Set([
266
320
  ]);
267
321
  export const toCanonicalLifecycleEvent = (event) => {
268
322
  const metadata = event.metadata
269
- ? Object.fromEntries(Object.entries(event.metadata).filter(([key]) => PUBLIC_EVENT_METADATA_KEYS.has(key)))
323
+ ? Object.fromEntries(Object.entries(event.metadata).filter(([key, value]) => {
324
+ if (!PUBLIC_EVENT_METADATA_KEYS.has(key)) {
325
+ return false;
326
+ }
327
+ if (key === 'artifact_id') {
328
+ return typeof value === 'string' && value.length > 0 && value.length <= 255;
329
+ }
330
+ if (key === 'prior_availability') {
331
+ return value === 'available' || value === 'missing';
332
+ }
333
+ return true;
334
+ }))
270
335
  : null;
271
336
  return {
272
337
  ...event,
@@ -567,6 +632,12 @@ const assertTestTarget = (projectId, details) => {
567
632
  }
568
633
  };
569
634
  export const hasDirectRetryChild = (runId) => Boolean(getDirectRetryChildStatement.get(runId));
635
+ export const getDirectRetryChild = (projectId, runId) => {
636
+ const child = getDirectRetryChildStatement.get(runId);
637
+ return child ? getExecutionRun(projectId, child.id) : null;
638
+ };
639
+ export const hasLaterExecutionRunForPrimaryEntity = ({ projectId, sourceRunId, kind, entityType, entityId, }) => Boolean(hasLaterRunForPrimaryEntityStatement.get(sourceRunId, projectId, projectId, kind, entityType, entityId));
640
+ export const hasLaterSpecReviewForSnapshot = ({ projectId, sourceRunId, sessionId, snapshotId, }) => Boolean(hasLaterSpecReviewForSnapshotStatement.get(sourceRunId, projectId, projectId, sessionId, snapshotId));
570
641
  const insertTestDetails = (runId, details) => {
571
642
  const boundedOutput = boundTestOutput(details.output ?? '', details.outputTruncated);
572
643
  insertTestDetailsStatement.run(runId, details.commandId ?? null, details.commandName ?? null, details.commandRunner ?? null, details.commandSnapshot ?? null, details.activeCommand ?? null, details.targetKind ?? null, details.targetBundleId ?? null, details.targetLabel ?? null, details.ticketId ?? null, serializeBoundedObject(details.selection, 'Test selection'), serializeBoundedObject(details.summary, 'Test summary'), details.exitCode ?? null, details.durationMs ?? null, boundedOutput.output, boundedOutput.outputTruncated ? 1 : 0, Buffer.byteLength(boundedOutput.output, 'utf8'));
@@ -747,7 +818,33 @@ const createExecutionRunInTransaction = (input, allowAutomationSideEffectRetry =
747
818
  metadataJson: serializeBoundedObject(input.eventMetadata, 'Lifecycle event metadata'),
748
819
  createdAt: now,
749
820
  });
750
- return { run: serializeRun(getRunRow(input.projectId, id)), event };
821
+ const retryEvents = retrySource
822
+ ? [
823
+ createEvent({
824
+ projectId: input.projectId,
825
+ runId: retrySource.id,
826
+ eventType: 'retry_requested',
827
+ fromStatus: retrySource.status,
828
+ toStatus: retrySource.status,
829
+ domainStatus: retrySource.domain_status,
830
+ metadataJson: serializeBoundedObject({ replacement_run_id: id }, 'Lifecycle event metadata'),
831
+ createdAt: now,
832
+ }),
833
+ ...(event.event_type === 'retry_started'
834
+ ? []
835
+ : [createEvent({
836
+ projectId: input.projectId,
837
+ runId: id,
838
+ eventType: 'retry_started',
839
+ fromStatus: 'queued',
840
+ toStatus: 'queued',
841
+ domainStatus: input.domainStatus ?? null,
842
+ metadataJson: serializeBoundedObject({ retry_of_run_id: retrySource.id }, 'Lifecycle event metadata'),
843
+ createdAt: now,
844
+ })]),
845
+ ]
846
+ : [];
847
+ return { run: serializeRun(getRunRow(input.projectId, id)), event, retryEvents };
751
848
  };
752
849
  const insertRunLink = (input) => {
753
850
  if (input.link.relationship === 'primary') {
@@ -861,11 +958,17 @@ export const withExecutionRunTransaction = (callback, emitter) => {
861
958
  createExecutionRun: input => {
862
959
  const result = createExecutionRunInTransaction(input);
863
960
  enqueueRunAndEvent(notifications, result.run, result.event, emitter);
961
+ for (const retryEvent of result.retryEvents) {
962
+ notifications.push({ event: 'lifecycle-event:created', payload: retryEvent, emitter });
963
+ }
864
964
  return result.run;
865
965
  },
866
966
  createAutomationRetryExecutionRun: input => {
867
967
  const result = createExecutionRunInTransaction(input, true);
868
968
  enqueueRunAndEvent(notifications, result.run, result.event, emitter);
969
+ for (const retryEvent of result.retryEvents) {
970
+ notifications.push({ event: 'lifecycle-event:created', payload: retryEvent, emitter });
971
+ }
869
972
  return result.run;
870
973
  },
871
974
  transitionExecutionRun: input => {
@@ -931,6 +1034,18 @@ export const getExecutionRun = (projectId, runId) => {
931
1034
  const row = getRunStatement.get(runId, projectId);
932
1035
  return row ? serializeRun(row) : null;
933
1036
  };
1037
+ export const getExecutionRunsByIds = (projectId, runIds) => {
1038
+ const ids = [...new Set(runIds)];
1039
+ if (ids.length === 0) {
1040
+ return [];
1041
+ }
1042
+ const rows = db.prepare(`
1043
+ SELECT * FROM execution_runs
1044
+ WHERE project_id = ? AND id IN (${ids.map(() => '?').join(', ')})
1045
+ `).all(projectId, ...ids);
1046
+ const sourceAvailability = loadSourceAvailability(rows);
1047
+ return rows.map(row => serializeRun(row, sourceAvailability));
1048
+ };
934
1049
  export const getQueuedSpecReviewExecutionRun = (projectId, snapshotId) => {
935
1050
  const row = db.prepare(`
936
1051
  SELECT execution_runs.id
@@ -989,20 +1104,23 @@ export const hasActiveBuildExecutionRunForTicket = (projectId, ticketId) => {
989
1104
  LIMIT 1
990
1105
  `).get(projectId, ticketId));
991
1106
  };
992
- const purgeTestExecutionRunTransaction = db.transaction((projectId, runId) => {
993
- return db.prepare(`
994
- DELETE FROM execution_runs
995
- WHERE id = ? AND project_id = ? AND kind = 'test'
996
- `).run(runId, projectId).changes > 0;
997
- });
998
- export const purgeTestExecutionRun = (projectId, runId) => {
999
- return purgeTestExecutionRunTransaction(projectId, runId);
1000
- };
1001
1107
  export const listExecutionRunLinks = (projectId, runId) => {
1002
1108
  getRunRow(projectId, runId);
1003
1109
  const links = getRunLinksStatement.all(runId, projectId);
1004
1110
  return links.map(link => ({ ...link, url_snapshot: safeSnapshotUrl(link.url_snapshot) }));
1005
1111
  };
1112
+ export const listExecutionRunLinksForRuns = (projectId, runIds) => {
1113
+ const ids = [...new Set(runIds)];
1114
+ if (ids.length === 0) {
1115
+ return [];
1116
+ }
1117
+ const links = db.prepare(`
1118
+ SELECT * FROM execution_run_links
1119
+ WHERE project_id = ? AND execution_run_id IN (${ids.map(() => '?').join(', ')})
1120
+ ORDER BY execution_run_id ASC, created_at ASC, entity_type ASC, entity_id ASC, relationship ASC
1121
+ `).all(projectId, ...ids);
1122
+ return links.map(link => ({ ...link, url_snapshot: safeSnapshotUrl(link.url_snapshot) }));
1123
+ };
1006
1124
  const testDetailsSelect = `
1007
1125
  SELECT td.*, er.project_id, er.status AS execution_status
1008
1126
  FROM test_execution_details td
@@ -1175,30 +1293,41 @@ export const listExecutionRuns = (input) => {
1175
1293
  }
1176
1294
  const statuses = [...new Set(input.statuses ?? [])];
1177
1295
  const cursor = input.cursor ? decodeCursor(input.cursor) : null;
1178
- const clauses = ['project_id = ?'];
1296
+ const clauses = ['er.project_id = ?'];
1179
1297
  const parameters = [input.projectId];
1180
1298
  if (input.kind) {
1181
- clauses.push('kind = ?');
1299
+ clauses.push('er.kind = ?');
1182
1300
  parameters.push(input.kind);
1183
1301
  }
1184
1302
  if (statuses.length > 0) {
1185
- clauses.push(`status IN (${statuses.map(() => '?').join(', ')})`);
1303
+ clauses.push(`er.status IN (${statuses.map(() => '?').join(', ')})`);
1186
1304
  parameters.push(...statuses);
1187
1305
  }
1188
1306
  if (cursor) {
1189
- clauses.push('(created_at < ? OR (created_at = ? AND id < ?))');
1307
+ clauses.push('(er.created_at < ? OR (er.created_at = ? AND er.id < ?))');
1190
1308
  parameters.push(cursor.created_at, cursor.created_at, cursor.id);
1191
1309
  }
1310
+ if (input.scopeEntity) {
1311
+ clauses.push(`EXISTS (
1312
+ SELECT 1 FROM execution_run_links scope_link
1313
+ WHERE scope_link.execution_run_id = er.id
1314
+ AND scope_link.project_id = er.project_id
1315
+ AND scope_link.entity_type = ?
1316
+ AND scope_link.entity_id = ?
1317
+ )`);
1318
+ parameters.push(input.scopeEntity.type, input.scopeEntity.id);
1319
+ }
1192
1320
  const rows = db.prepare(`
1193
- SELECT * FROM execution_runs
1321
+ SELECT er.* FROM execution_runs er
1194
1322
  WHERE ${clauses.join(' AND ')}
1195
- ORDER BY created_at DESC, id DESC
1323
+ ORDER BY er.created_at DESC, er.id DESC
1196
1324
  LIMIT ?
1197
1325
  `).all(...parameters, limit + 1);
1198
1326
  const pageRows = rows.slice(0, limit);
1327
+ const sourceAvailability = loadSourceAvailability(pageRows);
1199
1328
  const lastRow = pageRows.at(-1);
1200
1329
  return {
1201
- runs: pageRows.map(serializeRun),
1330
+ runs: pageRows.map(row => serializeRun(row, sourceAvailability)),
1202
1331
  next_cursor: rows.length > limit && lastRow
1203
1332
  ? encodeCursor({ created_at: lastRow.created_at, id: lastRow.id })
1204
1333
  : null,
@@ -1213,6 +1342,40 @@ export const listLifecycleEvents = (projectId, runId) => {
1213
1342
  `).all(projectId, runId);
1214
1343
  return rows.map(serializeEvent);
1215
1344
  };
1345
+ export const listLatestLifecycleEventsForRuns = (projectId, runIds) => {
1346
+ const ids = [...new Set(runIds)];
1347
+ if (ids.length === 0) {
1348
+ return [];
1349
+ }
1350
+ const rows = db.prepare(`
1351
+ SELECT * FROM (
1352
+ SELECT lifecycle_events.*,
1353
+ ROW_NUMBER() OVER (PARTITION BY execution_run_id ORDER BY sequence DESC, id DESC) AS timeline_rank
1354
+ FROM lifecycle_events
1355
+ WHERE project_id = ? AND execution_run_id IN (${ids.map(() => '?').join(', ')})
1356
+ )
1357
+ WHERE timeline_rank = 1
1358
+ `).all(projectId, ...ids);
1359
+ return rows.map(({ timeline_rank: _timelineRank, ...row }) => serializeEvent(row));
1360
+ };
1361
+ export const listLatestMeaningfulLifecycleEventsForRuns = (projectId, runIds) => {
1362
+ const ids = [...new Set(runIds)];
1363
+ if (ids.length === 0) {
1364
+ return [];
1365
+ }
1366
+ const excludedEventTypes = [...ARTIFACT_AUDIT_EVENT_TYPES];
1367
+ const rows = db.prepare(`
1368
+ SELECT * FROM (
1369
+ SELECT lifecycle_events.*,
1370
+ ROW_NUMBER() OVER (PARTITION BY execution_run_id ORDER BY sequence DESC, id DESC) AS timeline_rank
1371
+ FROM lifecycle_events
1372
+ WHERE project_id = ? AND execution_run_id IN (${ids.map(() => '?').join(', ')})
1373
+ AND event_type NOT IN (${excludedEventTypes.map(() => '?').join(', ')})
1374
+ )
1375
+ WHERE timeline_rank = 1
1376
+ `).all(projectId, ...ids, ...excludedEventTypes);
1377
+ return rows.map(({ timeline_rank: _timelineRank, ...row }) => serializeEvent(row));
1378
+ };
1216
1379
  export const listProjectLifecycleEvents = (input) => {
1217
1380
  const limit = input.limit ?? 50;
1218
1381
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 200) {