issue-flow 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/README.md +43 -97
  2. package/dist/agent-4DHRKZ2Z.js +32 -0
  3. package/dist/{analyze-Q4ONIWRZ.js → analyze-UEJ2R3H5.js} +42 -41
  4. package/dist/analyze-UEJ2R3H5.js.map +1 -0
  5. package/dist/{apply-EEFPY2W4.js → apply-QDYNQMUX.js} +22 -24
  6. package/dist/apply-QDYNQMUX.js.map +1 -0
  7. package/dist/artifacts-AMJQJFFV.js +82 -0
  8. package/dist/artifacts-AMJQJFFV.js.map +1 -0
  9. package/dist/{bench-WWWXLTFH.js → bench-RNRXVEFK.js} +42 -42
  10. package/dist/{chunk-RYOUZ6EK.js → chunk-23VHVVKW.js} +3 -3
  11. package/dist/chunk-2EYLOS5I.js +99 -0
  12. package/dist/chunk-2EYLOS5I.js.map +1 -0
  13. package/dist/{chunk-C5TL7T4V.js → chunk-2YM2VH6R.js} +2 -2
  14. package/dist/{chunk-XUEMATVY.js → chunk-37O2FMAN.js} +28 -25
  15. package/dist/chunk-37O2FMAN.js.map +1 -0
  16. package/dist/chunk-44RF3VFG.js +717 -0
  17. package/dist/chunk-44RF3VFG.js.map +1 -0
  18. package/dist/chunk-6AOZCWN2.js +21 -0
  19. package/dist/chunk-6AOZCWN2.js.map +1 -0
  20. package/dist/chunk-7MJGYPHF.js +1571 -0
  21. package/dist/chunk-7MJGYPHF.js.map +1 -0
  22. package/dist/{chunk-SA6HXL4Y.js → chunk-7WBUEBD5.js} +9 -9
  23. package/dist/chunk-7WBUEBD5.js.map +1 -0
  24. package/dist/chunk-7ZZUSMBJ.js +35 -0
  25. package/dist/chunk-7ZZUSMBJ.js.map +1 -0
  26. package/dist/{chunk-QJ44VG5G.js → chunk-ADH6RJNX.js} +3 -3
  27. package/dist/{chunk-34OCSKOB.js → chunk-AKL5UPR6.js} +159 -114
  28. package/dist/chunk-AKL5UPR6.js.map +1 -0
  29. package/dist/{chunk-7F7LGMVO.js → chunk-BTBJ5UOO.js} +12 -12
  30. package/dist/{chunk-7F7LGMVO.js.map → chunk-BTBJ5UOO.js.map} +1 -1
  31. package/dist/{chunk-HOAHMERN.js → chunk-CLZ6RRZL.js} +21 -21
  32. package/dist/chunk-DBLXLKGU.js +1201 -0
  33. package/dist/chunk-DBLXLKGU.js.map +1 -0
  34. package/dist/{chunk-DUS5ZNKL.js → chunk-DIMDAYZS.js} +7 -6
  35. package/dist/chunk-DZ3DZ3EN.js +278 -0
  36. package/dist/chunk-DZ3DZ3EN.js.map +1 -0
  37. package/dist/{chunk-CRGIQFUQ.js → chunk-E2H3JV2E.js} +3 -1
  38. package/dist/chunk-E2H3JV2E.js.map +1 -0
  39. package/dist/{chunk-45WZ6GMI.js → chunk-EORDSKWZ.js} +17 -17
  40. package/dist/chunk-EORDSKWZ.js.map +1 -0
  41. package/dist/{chunk-LTH6XYZ3.js → chunk-EYKPWHT7.js} +40 -30
  42. package/dist/chunk-EYKPWHT7.js.map +1 -0
  43. package/dist/{chunk-OODXFCQV.js → chunk-FU2FQ4JE.js} +14 -16
  44. package/dist/{chunk-OODXFCQV.js.map → chunk-FU2FQ4JE.js.map} +1 -1
  45. package/dist/{chunk-V7QOIQYC.js → chunk-G442RDFX.js} +9 -23
  46. package/dist/{chunk-V7QOIQYC.js.map → chunk-G442RDFX.js.map} +1 -1
  47. package/dist/{chunk-QPGEFLMY.js → chunk-I64N6RL6.js} +7 -7
  48. package/dist/{chunk-KRAGAUKD.js → chunk-JBYXJOJJ.js} +201 -64
  49. package/dist/chunk-JBYXJOJJ.js.map +1 -0
  50. package/dist/{chunk-SOM4QA6C.js → chunk-JCR5C5A7.js} +93 -18
  51. package/dist/chunk-JCR5C5A7.js.map +1 -0
  52. package/dist/{chunk-RNFUVKC3.js → chunk-K5NM26NV.js} +30 -13
  53. package/dist/chunk-K5NM26NV.js.map +1 -0
  54. package/dist/{chunk-YVPZUN6X.js → chunk-KUA6KBBD.js} +335 -38
  55. package/dist/chunk-KUA6KBBD.js.map +1 -0
  56. package/dist/{chunk-KMCS76ZW.js → chunk-KUL64XUF.js} +44 -12
  57. package/dist/chunk-KUL64XUF.js.map +1 -0
  58. package/dist/{chunk-HAKDLR6N.js → chunk-MAJ6QDRL.js} +65 -78
  59. package/dist/chunk-MAJ6QDRL.js.map +1 -0
  60. package/dist/{chunk-DGRTJD5R.js → chunk-MML2G7IC.js} +7 -7
  61. package/dist/chunk-NP47HRX2.js +136 -0
  62. package/dist/chunk-NP47HRX2.js.map +1 -0
  63. package/dist/{chunk-3R27MJ5R.js → chunk-PFWHHYEO.js} +3 -3
  64. package/dist/{chunk-ECU7YO7S.js → chunk-PM5YWJYO.js} +22 -6
  65. package/dist/chunk-PM5YWJYO.js.map +1 -0
  66. package/dist/{chunk-E4I6MMHD.js → chunk-PXPTIXJF.js} +154 -101
  67. package/dist/chunk-PXPTIXJF.js.map +1 -0
  68. package/dist/{chunk-CWSQVHVV.js → chunk-QJHZ3HYJ.js} +248 -102
  69. package/dist/chunk-QJHZ3HYJ.js.map +1 -0
  70. package/dist/{chunk-LI35PIIS.js → chunk-RDPK7LYK.js} +20 -117
  71. package/dist/chunk-RDPK7LYK.js.map +1 -0
  72. package/dist/{chunk-PMLE7KAQ.js → chunk-RMIBLFG3.js} +30 -14
  73. package/dist/chunk-RMIBLFG3.js.map +1 -0
  74. package/dist/{chunk-62GVVZQO.js → chunk-T736DSZI.js} +32 -316
  75. package/dist/chunk-T736DSZI.js.map +1 -0
  76. package/dist/{chunk-TZYINKCC.js → chunk-UDUUUFQF.js} +57 -29
  77. package/dist/chunk-UDUUUFQF.js.map +1 -0
  78. package/dist/{chunk-NEWNXZ65.js → chunk-V7GETODQ.js} +36 -28
  79. package/dist/{chunk-NEWNXZ65.js.map → chunk-V7GETODQ.js.map} +1 -1
  80. package/dist/{chunk-PPZ3K4YM.js → chunk-VHQMMY2D.js} +2 -2
  81. package/dist/{chunk-ZVZMWNCG.js → chunk-VJ3PF6B7.js} +50 -16
  82. package/dist/chunk-VJ3PF6B7.js.map +1 -0
  83. package/dist/{chunk-L6HSKTQE.js → chunk-W7LZLJAF.js} +4 -4
  84. package/dist/{chunk-6K2LCFG6.js → chunk-WB4FA2EH.js} +70 -81
  85. package/dist/chunk-WB4FA2EH.js.map +1 -0
  86. package/dist/{chunk-LGHND47K.js → chunk-WQSGL5LX.js} +22 -16
  87. package/dist/chunk-WQSGL5LX.js.map +1 -0
  88. package/dist/{chunk-ICM463EO.js → chunk-XNK5UN2D.js} +5 -649
  89. package/dist/chunk-XNK5UN2D.js.map +1 -0
  90. package/dist/{chunk-7UMMFOL2.js → chunk-Y2KOWABB.js} +39 -24
  91. package/dist/chunk-Y2KOWABB.js.map +1 -0
  92. package/dist/{chunk-QF7VEL3M.js → chunk-YC24E66P.js} +568 -158
  93. package/dist/chunk-YC24E66P.js.map +1 -0
  94. package/dist/{chunk-EG2MGDP5.js → chunk-Z6RCKCVB.js} +52 -46
  95. package/dist/chunk-Z6RCKCVB.js.map +1 -0
  96. package/dist/{chunk-YUNBOUQH.js → chunk-ZPGNVBWY.js} +8 -8
  97. package/dist/cli.js +132 -62
  98. package/dist/cli.js.map +1 -1
  99. package/dist/compat-OJG76J4I.js +24 -0
  100. package/dist/{config-LEBZZCJP.js → config-6YTWPDBK.js} +20 -17
  101. package/dist/{contract-JH2WBIO6.js → contract-HWLFFL3F.js} +2 -2
  102. package/dist/{conventions-XOT6YW5L.js → conventions-5UPOTU42.js} +20 -16
  103. package/dist/{conventions-XOT6YW5L.js.map → conventions-5UPOTU42.js.map} +1 -1
  104. package/dist/db-QAZG4LQD.js +264 -0
  105. package/dist/db-QAZG4LQD.js.map +1 -0
  106. package/dist/diagnostics-YEFB42E5.js +25 -0
  107. package/dist/execute-QCT64D37.js +38 -0
  108. package/dist/{generate-JGW5JSKB.js → generate-QATMDKOK.js} +46 -43
  109. package/dist/generate-QATMDKOK.js.map +1 -0
  110. package/dist/{git-A2IP32PQ.js → git-XGOECXWG.js} +2 -3
  111. package/dist/history-NBPUZVGQ.js +101 -0
  112. package/dist/history-NBPUZVGQ.js.map +1 -0
  113. package/dist/init-4LB2YKWP.js +28 -0
  114. package/dist/{operations-O4FORFIH.js → operations-RUQP2Y2F.js} +78 -52
  115. package/dist/operations-RUQP2Y2F.js.map +1 -0
  116. package/dist/{permissions-G4XCAESF.js → permissions-QZPUUBSO.js} +5 -6
  117. package/dist/plan-2NMSHTMD.js +38 -0
  118. package/dist/{policy-U5NBFAO5.js → policy-RKKFF5A2.js} +17 -15
  119. package/dist/{policy-U5NBFAO5.js.map → policy-RKKFF5A2.js.map} +1 -1
  120. package/dist/pr-QKFO5HI3.js +42 -0
  121. package/dist/pr-review-H2BFD6F2.js +33 -0
  122. package/dist/prd-ET6XKYRS.js +37 -0
  123. package/dist/{ps-76L6ZAZL.js → ps-UIKSOUAQ.js} +26 -15
  124. package/dist/ps-UIKSOUAQ.js.map +1 -0
  125. package/dist/{recorder-2BAKPBRA.js → recorder-LOU3PTIE.js} +11 -7
  126. package/dist/registry-HV7M6MAH.js +17 -0
  127. package/dist/repository-RCRR6RDE.js +80 -0
  128. package/dist/resume-BWJZGBLZ.js +278 -0
  129. package/dist/resume-BWJZGBLZ.js.map +1 -0
  130. package/dist/review-2MTZLNSS.js +38 -0
  131. package/dist/routing-RM5ABHHD.js +35 -0
  132. package/dist/run-XMACQBRJ.js +61 -0
  133. package/dist/{session-publisher-KAJQRHNZ.js → session-publisher-Q5TNEZPX.js} +3 -3
  134. package/dist/session-publisher-Q5TNEZPX.js.map +1 -0
  135. package/dist/{state-manager-UPHO2EA2.js → state-manager-SRN4VJSX.js} +9 -4
  136. package/dist/state-manager-SRN4VJSX.js.map +1 -0
  137. package/dist/{usage-KTMYXAPF.js → usage-JOODVE5O.js} +69 -63
  138. package/dist/usage-JOODVE5O.js.map +1 -0
  139. package/dist/web-TLMWJ2BM.js +205 -0
  140. package/dist/web-TLMWJ2BM.js.map +1 -0
  141. package/package.json +22 -8
  142. package/prompts/analyze.md +16 -19
  143. package/prompts/execute.md +73 -144
  144. package/prompts/generate.md +23 -10
  145. package/prompts/plan.md +32 -60
  146. package/prompts/pr-review.md +58 -40
  147. package/prompts/pr.md +50 -16
  148. package/prompts/prd.md +26 -19
  149. package/prompts/review.md +41 -18
  150. package/web/public/app.js +111 -112
  151. package/dist/agent-HMZTXCRT.js +0 -30
  152. package/dist/analyze-Q4ONIWRZ.js.map +0 -1
  153. package/dist/apply-EEFPY2W4.js.map +0 -1
  154. package/dist/chunk-34OCSKOB.js.map +0 -1
  155. package/dist/chunk-45WZ6GMI.js.map +0 -1
  156. package/dist/chunk-62GVVZQO.js.map +0 -1
  157. package/dist/chunk-6K2LCFG6.js.map +0 -1
  158. package/dist/chunk-7UMMFOL2.js.map +0 -1
  159. package/dist/chunk-CRGIQFUQ.js.map +0 -1
  160. package/dist/chunk-CWSQVHVV.js.map +0 -1
  161. package/dist/chunk-E4I6MMHD.js.map +0 -1
  162. package/dist/chunk-ECU7YO7S.js.map +0 -1
  163. package/dist/chunk-EG2MGDP5.js.map +0 -1
  164. package/dist/chunk-HAKDLR6N.js.map +0 -1
  165. package/dist/chunk-ICM463EO.js.map +0 -1
  166. package/dist/chunk-KMCS76ZW.js.map +0 -1
  167. package/dist/chunk-KRAGAUKD.js.map +0 -1
  168. package/dist/chunk-LGHND47K.js.map +0 -1
  169. package/dist/chunk-LI35PIIS.js.map +0 -1
  170. package/dist/chunk-LTH6XYZ3.js.map +0 -1
  171. package/dist/chunk-PMLE7KAQ.js.map +0 -1
  172. package/dist/chunk-QF7VEL3M.js.map +0 -1
  173. package/dist/chunk-RNFUVKC3.js.map +0 -1
  174. package/dist/chunk-SA6HXL4Y.js.map +0 -1
  175. package/dist/chunk-SOM4QA6C.js.map +0 -1
  176. package/dist/chunk-TZYINKCC.js.map +0 -1
  177. package/dist/chunk-WCCZJL7W.js +0 -141
  178. package/dist/chunk-WCCZJL7W.js.map +0 -1
  179. package/dist/chunk-XEYH6ZZI.js +0 -289
  180. package/dist/chunk-XEYH6ZZI.js.map +0 -1
  181. package/dist/chunk-XUEMATVY.js.map +0 -1
  182. package/dist/chunk-XZUEFTUP.js +0 -243
  183. package/dist/chunk-XZUEFTUP.js.map +0 -1
  184. package/dist/chunk-YVPZUN6X.js.map +0 -1
  185. package/dist/chunk-ZVZMWNCG.js.map +0 -1
  186. package/dist/diagnostics-HTCWF7F4.js +0 -23
  187. package/dist/execute-LELOSR5T.js +0 -37
  188. package/dist/generate-JGW5JSKB.js.map +0 -1
  189. package/dist/init-TO6MUXO4.js +0 -26
  190. package/dist/operations-O4FORFIH.js.map +0 -1
  191. package/dist/plan-OXAT344Q.js +0 -34
  192. package/dist/pr-UZHNKKZC.js +0 -39
  193. package/dist/pr-review-Q4ACAWDM.js +0 -32
  194. package/dist/prd-6CYEMLDY.js +0 -33
  195. package/dist/ps-76L6ZAZL.js.map +0 -1
  196. package/dist/registry-XLHHYLTR.js +0 -15
  197. package/dist/resume-URCU6CIJ.js +0 -244
  198. package/dist/resume-URCU6CIJ.js.map +0 -1
  199. package/dist/review-LVZT2TMW.js +0 -35
  200. package/dist/routing-EK6RBZM4.js +0 -33
  201. package/dist/run-2RXJCJKT.js +0 -56
  202. package/dist/usage-KTMYXAPF.js.map +0 -1
  203. package/dist/web-B5UPHHR5.js +0 -172
  204. package/dist/web-B5UPHHR5.js.map +0 -1
  205. /package/dist/{agent-HMZTXCRT.js.map → agent-4DHRKZ2Z.js.map} +0 -0
  206. /package/dist/{bench-WWWXLTFH.js.map → bench-RNRXVEFK.js.map} +0 -0
  207. /package/dist/{chunk-RYOUZ6EK.js.map → chunk-23VHVVKW.js.map} +0 -0
  208. /package/dist/{chunk-C5TL7T4V.js.map → chunk-2YM2VH6R.js.map} +0 -0
  209. /package/dist/{chunk-QJ44VG5G.js.map → chunk-ADH6RJNX.js.map} +0 -0
  210. /package/dist/{chunk-HOAHMERN.js.map → chunk-CLZ6RRZL.js.map} +0 -0
  211. /package/dist/{chunk-DUS5ZNKL.js.map → chunk-DIMDAYZS.js.map} +0 -0
  212. /package/dist/{chunk-QPGEFLMY.js.map → chunk-I64N6RL6.js.map} +0 -0
  213. /package/dist/{chunk-DGRTJD5R.js.map → chunk-MML2G7IC.js.map} +0 -0
  214. /package/dist/{chunk-3R27MJ5R.js.map → chunk-PFWHHYEO.js.map} +0 -0
  215. /package/dist/{chunk-PPZ3K4YM.js.map → chunk-VHQMMY2D.js.map} +0 -0
  216. /package/dist/{chunk-L6HSKTQE.js.map → chunk-W7LZLJAF.js.map} +0 -0
  217. /package/dist/{chunk-YUNBOUQH.js.map → chunk-ZPGNVBWY.js.map} +0 -0
  218. /package/dist/{config-LEBZZCJP.js.map → compat-OJG76J4I.js.map} +0 -0
  219. /package/dist/{contract-JH2WBIO6.js.map → config-6YTWPDBK.js.map} +0 -0
  220. /package/dist/{diagnostics-HTCWF7F4.js.map → contract-HWLFFL3F.js.map} +0 -0
  221. /package/dist/{execute-LELOSR5T.js.map → diagnostics-YEFB42E5.js.map} +0 -0
  222. /package/dist/{git-A2IP32PQ.js.map → execute-QCT64D37.js.map} +0 -0
  223. /package/dist/{init-TO6MUXO4.js.map → git-XGOECXWG.js.map} +0 -0
  224. /package/dist/{permissions-G4XCAESF.js.map → init-4LB2YKWP.js.map} +0 -0
  225. /package/dist/{plan-OXAT344Q.js.map → permissions-QZPUUBSO.js.map} +0 -0
  226. /package/dist/{pr-UZHNKKZC.js.map → plan-2NMSHTMD.js.map} +0 -0
  227. /package/dist/{pr-review-Q4ACAWDM.js.map → pr-QKFO5HI3.js.map} +0 -0
  228. /package/dist/{prd-6CYEMLDY.js.map → pr-review-H2BFD6F2.js.map} +0 -0
  229. /package/dist/{recorder-2BAKPBRA.js.map → prd-ET6XKYRS.js.map} +0 -0
  230. /package/dist/{registry-XLHHYLTR.js.map → recorder-LOU3PTIE.js.map} +0 -0
  231. /package/dist/{review-LVZT2TMW.js.map → registry-HV7M6MAH.js.map} +0 -0
  232. /package/dist/{routing-EK6RBZM4.js.map → repository-RCRR6RDE.js.map} +0 -0
  233. /package/dist/{run-2RXJCJKT.js.map → review-2MTZLNSS.js.map} +0 -0
  234. /package/dist/{session-publisher-KAJQRHNZ.js.map → routing-RM5ABHHD.js.map} +0 -0
  235. /package/dist/{state-manager-UPHO2EA2.js.map → run-XMACQBRJ.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/execution/plan.ts","../src/execution/containers.ts","../src/issues/graph.ts","../src/execution/order.ts","../src/core/pipeline.ts"],"sourcesContent":["import { mkdir, readFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { ZodError } from 'zod';\nimport { isoNow } from '../core/state-manager.js';\nimport type { DependencyGraph } from '../issues/graph.js';\nimport type { Issue } from '../issues/types.js';\nimport { executionPlanSchema } from '../storage/schemas.js';\nimport type { LastError, PullRequestRef } from '../types.js';\nimport { writeFileAtomic } from '../utils/fs.js';\nimport { isContainer } from './containers.js';\nimport { priorityOf } from './order.js';\nimport type {\n ExecutionPlan,\n ExecutionPlanExcluded,\n ExecutionPlanIssue,\n QueueIssueRole,\n QueueIssueStatus,\n QueueStatus,\n} from './types.js';\n\n/**\n * Building, reading, writing and advancing the execution plan of a queue.\n *\n * The plan is the only state a multi-issue run adds to the storage tree, and it\n * is written **only** for a queue with more than one Issue — a single-issue run\n * leaves the layout exactly as it was.\n */\n\nexport interface BuildExecutionPlanInput {\n /** Project id of the global storage tree. */\n projectId: string;\n /** Identifiers the user asked for, in order; the first one names the queue. */\n requested: readonly string[];\n graph: DependencyGraph;\n /** Ids to run, already ordered by `computeExecutionOrder`. */\n order: readonly string[];\n noBranch?: boolean;\n prReview?: boolean;\n /** Injectable clock, so tests can assert `createdAt` vs `updatedAt`. */\n now?: () => string;\n}\n\nfunction parentOf(graph: DependencyGraph, id: string): string | null {\n for (const node of graph.nodes.values()) {\n if (node.relations.children.includes(id)) return node.id;\n }\n return null;\n}\n\n/** Title/url/number of a node, falling back to what the id alone tells us. */\nfunction describeIssue(\n issue: Issue | null,\n id: string,\n): {\n number: number | null;\n title: string;\n url: string | null;\n source: string;\n} {\n const parsed = Number.parseInt(id, 10);\n return {\n number: issue?.number ?? (Number.isNaN(parsed) ? null : parsed),\n title: issue?.title ?? '',\n url: issue?.remoteRef ?? null,\n source: issue?.source ?? 'github',\n };\n}\n\n/**\n * Turn a graph plus an order into the persisted plan.\n *\n * Every Issue of the graph that is *not* in the order becomes an `excluded`\n * entry: the consolidated Pull Request reports them as known pending work, so a\n * hierarchy the user chose to trim never disappears silently.\n */\nexport function buildExecutionPlan(input: BuildExecutionPlanInput): ExecutionPlan {\n const now = input.now ?? isoNow;\n const at = now();\n const scheduled = new Set(input.order);\n const requested = new Set(input.requested);\n\n const issues: ExecutionPlanIssue[] = input.order.map((id, index) => {\n const node = input.graph.nodes.get(id);\n const relations = node?.relations;\n const described = describeIssue(node?.issue ?? null, id);\n const children = relations?.children ?? [];\n const role: QueueIssueRole = isContainer({\n issue: node?.issue ?? null,\n children,\n })\n ? 'container'\n : 'executable';\n\n return {\n id,\n number: described.number,\n title: described.title,\n url: described.url,\n source: described.source,\n position: index + 1,\n status: 'pending',\n origin: requested.has(id) ? 'requested' : 'discovered',\n role,\n externalDependencies: (relations?.blockedBy ?? []).filter(\n (blocker) =>\n !scheduled.has(blocker) && input.graph.nodes.get(blocker)?.issue?.state !== 'closed',\n ),\n // Only the constraints that survive inside the queue: a blocker left out\n // of it is reported in `excluded`, not enforced as a dependency.\n dependsOn: (relations?.blockedBy ?? []).filter((blocker) => scheduled.has(blocker)),\n parent: relations?.parent ?? parentOf(input.graph, id),\n priority: priorityOf(node?.issue?.labels ?? []),\n heuristic: (relations?.heuristic.length ?? 0) > 0,\n failedPhase: null,\n lastError: null,\n attempts: 0,\n blockedReason: null,\n startedAt: null,\n completedAt: null,\n };\n });\n\n const excluded: ExecutionPlanExcluded[] = [];\n for (const node of input.graph.nodes.values()) {\n if (scheduled.has(node.id)) continue;\n const described = describeIssue(node.issue, node.id);\n excluded.push({\n id: node.id,\n number: described.number,\n title: described.title,\n url: described.url,\n reason:\n node.issue?.state === 'closed'\n ? 'Already closed before this run'\n : 'Discovered in the hierarchy but not selected for this run',\n });\n }\n\n return {\n schemaVersion: 1,\n id: input.requested[0] ?? (input.order[0] as string),\n project: input.projectId,\n requested: [...input.requested],\n branchName: null,\n noBranch: input.noBranch ?? false,\n prReview: input.prReview ?? false,\n status: 'pending',\n createdAt: at,\n updatedAt: at,\n truncated: input.graph.truncated,\n issues,\n excluded,\n };\n}\n\n/** Read a persisted plan. Throws with the offending fields on invalid content. */\nexport async function loadExecutionPlan(path: string): Promise<ExecutionPlan> {\n const raw: unknown = JSON.parse(await readFile(path, 'utf-8'));\n\n try {\n return executionPlanSchema.parse(raw) as ExecutionPlan;\n } catch (err) {\n if (err instanceof ZodError) {\n const issues = err.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid execution-plan.json at ${path}:\\n${issues}`);\n }\n throw err;\n }\n}\n\n/**\n * Persist a plan atomically, creating the queue directory on first write.\n *\n * `updatedAt` is stamped here so no call site can forget it — the file is the\n * only record of how far a queue got, and a stale timestamp on it is worse than\n * none.\n */\nexport async function saveExecutionPlan(\n path: string,\n plan: ExecutionPlan,\n now: () => string = isoNow,\n): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n const stamped: ExecutionPlan = { ...plan, updatedAt: now() };\n await writeFileAtomic(path, `${JSON.stringify(stamped, null, 2)}\\n`);\n}\n\n/**\n * The Issue the queue should work on next, or `null` when every one of them is\n * completed.\n *\n * The order of the lookups *is* the resume policy: an interrupted issue\n * (`in_progress`) and a failed one are retried before the queue moves on, and\n * each of them picks up from its own `tasks.json` (phase-level resume) rather\n * than from the start. A `completed` issue is never revisited — that is the\n * whole point of persisting the queue.\n */\nexport interface NextQueueIssueOptions {\n /**\n * Ids this invocation is done with, however they ended. A queue that skips a\n * failing Issue and comes back to it needs a way to say \"not this one again\",\n * or \"come back later\" becomes \"come back forever\".\n */\n exclude?: ReadonlySet<string>;\n}\n\n/**\n * Whether every dependency this Issue declares *inside the queue* is finished.\n *\n * The execution order already places dependencies first, so this only bites\n * once an entry stops being `completed` — skipped, blocked or failed. Handing\n * out an Issue whose blocker was skipped is how a queue produces work that\n * cannot compile.\n */\nfunction dependenciesSatisfied(plan: ExecutionPlan, entry: ExecutionPlanIssue): boolean {\n return entry.dependsOn.every((id) => {\n const dependency = plan.issues.find((candidate) => candidate.id === id);\n if (dependency === undefined) return true;\n // A container is not work: waiting on it would deadlock the children\n // that complete it.\n if (dependency.role === 'container') return true;\n return dependency.status === 'completed';\n });\n}\n\n/**\n * The Issue to work on next.\n *\n * The order of the four lookups is the resumption policy:\n * an interrupted Issue first (it was mid-flight), then one that failed on a\n * previous invocation, then the untouched ones, and only at the end the ones\n * this queue deliberately set aside — which is what \"go on with the\n * independent work and come back to it\" means in one line.\n *\n * `blocked` is absent on purpose: it needs a human, and no ordering of the\n * queue changes that.\n */\nexport function nextQueueIssue(\n plan: ExecutionPlan,\n options: NextQueueIssueOptions = {},\n): ExecutionPlanIssue | null {\n const exclude = options.exclude ?? new Set<string>();\n const eligible = (status: QueueIssueStatus) => (entry: ExecutionPlanIssue) =>\n entry.role !== 'container' &&\n entry.status === status &&\n !exclude.has(entry.id) &&\n dependenciesSatisfied(plan, entry);\n\n return (\n plan.issues.find(eligible('in_progress')) ??\n plan.issues.find(eligible('failed')) ??\n plan.issues.find(eligible('pending')) ??\n plan.issues.find(eligible('skipped')) ??\n null\n );\n}\n\n/**\n * Status of the queue derived from its entries.\n *\n * `skipped` is *not* terminal — the queue means to come back to it — so a queue\n * holding one is still `in_progress`. `blocked` is: nothing the queue can do\n * moves it, so a queue that ends with one ends `failed`, which is what a person\n * needs to see.\n */\nexport function queueStatus(plan: ExecutionPlan): QueueStatus {\n if (plan.issues.some((entry) => entry.status === 'failed' || entry.status === 'blocked')) {\n return 'failed';\n }\n if (plan.issues.every((entry) => entry.status === 'completed')) return 'completed';\n if (plan.issues.some((entry) => entry.status !== 'pending')) return 'in_progress';\n return 'pending';\n}\n\n/** Whether every Issue of the queue has been resolved. */\nexport function isQueueComplete(plan: ExecutionPlan): boolean {\n return plan.issues.every((entry) => entry.status === 'completed');\n}\n\n/** Apply `patch` to one entry and recompute the queue status. Pure. */\nfunction updateIssue(\n plan: ExecutionPlan,\n id: string,\n patch: Partial<ExecutionPlanIssue>,\n): ExecutionPlan {\n const issues = plan.issues.map((entry) => (entry.id === id ? { ...entry, ...patch } : entry));\n const next: ExecutionPlan = { ...plan, issues };\n return { ...next, status: queueStatus(next) };\n}\n\nexport function markQueueIssueInProgress(\n plan: ExecutionPlan,\n id: string,\n now: () => string = isoNow,\n): ExecutionPlan {\n const current = plan.issues.find((entry) => entry.id === id);\n return updateIssue(plan, id, {\n status: 'in_progress',\n // Preserved across a resume: the queue reports when the work on this Issue\n // really began, not when it was last picked up.\n startedAt: current?.startedAt ?? now(),\n failedPhase: null,\n lastError: null,\n });\n}\n\nfunction completeResolvedContainers(plan: ExecutionPlan, now: () => string): ExecutionPlan {\n let next = plan;\n let changed = true;\n while (changed) {\n changed = false;\n for (const entry of next.issues) {\n if (entry.role !== 'container' || entry.status === 'completed') continue;\n const children = next.issues.filter((candidate) => candidate.parent === entry.id);\n if (children.length === 0) continue;\n if (children.every((child) => child.status === 'completed')) {\n next = updateIssue(next, entry.id, {\n status: 'completed',\n completedAt: now(),\n failedPhase: null,\n lastError: null,\n });\n changed = true;\n }\n }\n }\n return next;\n}\n\nexport function markQueueIssueCompleted(\n plan: ExecutionPlan,\n id: string,\n now: () => string = isoNow,\n): ExecutionPlan {\n return completeResolvedContainers(\n updateIssue(plan, id, {\n status: 'completed',\n completedAt: now(),\n failedPhase: null,\n lastError: null,\n }),\n now,\n );\n}\n\nexport function markQueueIssueFailed(\n plan: ExecutionPlan,\n id: string,\n failure: { phase: string | null; error: LastError | null },\n): ExecutionPlan {\n const current = plan.issues.find((entry) => entry.id === id);\n return updateIssue(plan, id, {\n status: 'failed',\n failedPhase: failure.phase,\n lastError: failure.error,\n attempts: (current?.attempts ?? 0) + 1,\n });\n}\n\n/**\n * Set an Issue aside and come back to it later.\n *\n * The difference from `failed` is the whole point of `--on-issue-failure skip`:\n * a failure that stopped this attempt is not a verdict on the eleven other\n * Issues in the queue, and `skipped` is the status that says \"not now\" rather\n * than \"not at all\". `attempts` is incremented here because the counter is what\n * keeps \"come back later\" from becoming \"come back forever\".\n */\nexport function markQueueIssueSkipped(\n plan: ExecutionPlan,\n id: string,\n failure: { phase: string | null; error: LastError | null },\n): ExecutionPlan {\n const current = plan.issues.find((entry) => entry.id === id);\n return updateIssue(plan, id, {\n status: 'skipped',\n failedPhase: failure.phase,\n lastError: failure.error,\n attempts: (current?.attempts ?? 0) + 1,\n });\n}\n\n/**\n * Stop working on an Issue until a person looks at it.\n *\n * `blocked` is never handed back out by `nextQueueIssue`: waiting cannot fix\n * a missing credential or a repository mid-rebase, and a queue that could\n * unblock itself would spin on the same cause forever.\n */\nexport function markQueueIssueBlocked(\n plan: ExecutionPlan,\n id: string,\n reason: string,\n failure: { phase: string | null; error: LastError | null },\n): ExecutionPlan {\n const current = plan.issues.find((entry) => entry.id === id);\n return updateIssue(plan, id, {\n status: 'blocked',\n blockedReason: reason,\n failedPhase: failure.phase,\n lastError: failure.error,\n attempts: (current?.attempts ?? 0) + 1,\n });\n}\n\n/** Record the branch every Issue of the queue shares. */\nexport function setQueueBranch(plan: ExecutionPlan, branchName: string): ExecutionPlan {\n return { ...plan, branchName };\n}\n\n/** Record the single Pull Request that consolidated the queue. */\nexport function setQueuePullRequest(\n plan: ExecutionPlan,\n pullRequest: PullRequestRef,\n): ExecutionPlan {\n return { ...plan, pullRequest };\n}\n","import type { DependencyGraph } from '../issues/graph.js';\nimport type { Issue } from '../issues/types.js';\n\nexport const CONTAINER_SIGNALS = ['children', 'issue-type', 'label', 'title-prefix'] as const;\n\nexport type ContainerSignal = (typeof CONTAINER_SIGNALS)[number];\n\nexport interface ContainerConfig {\n detect: readonly ContainerSignal[];\n issueTypes: readonly string[];\n labels: readonly string[];\n titlePrefixes: readonly string[];\n}\n\n/** Unconfigured repos: only the universal signal. */\nexport const DEFAULT_CONTAINER_CONFIG: ContainerConfig = {\n detect: ['children'],\n issueTypes: ['Epic'],\n labels: ['epic'],\n titlePrefixes: ['[Epic]'],\n};\n\nexport interface ContainerProbe {\n issue: Pick<Issue, 'title' | 'labels' | 'type'> | null;\n children: readonly string[];\n}\n\nfunction enabled(config: ContainerConfig, signal: ContainerSignal): boolean {\n return config.detect.includes(signal);\n}\n\nfunction hasTitlePrefix(title: string, prefixes: readonly string[]): boolean {\n const trimmed = title.trim();\n return prefixes.some((prefix) => trimmed.toLowerCase().startsWith(prefix.toLowerCase()));\n}\n\n/**\n * Precedence: children > issue type > label > title prefix.\n * Having children always wins when that signal is enabled.\n */\nexport function isContainer(\n probe: ContainerProbe,\n config: ContainerConfig = DEFAULT_CONTAINER_CONFIG,\n): boolean {\n if (enabled(config, 'children') && probe.children.length > 0) return true;\n\n const issue = probe.issue;\n if (issue === null) return false;\n\n if (enabled(config, 'issue-type')) {\n const type = issue.type?.trim();\n if (\n type !== undefined &&\n type !== '' &&\n config.issueTypes.some((name) => name.toLowerCase() === type.toLowerCase())\n ) {\n return true;\n }\n }\n\n if (enabled(config, 'label')) {\n const labels = new Set(issue.labels.map((label) => label.toLowerCase()));\n if (config.labels.some((label) => labels.has(label.toLowerCase()))) return true;\n }\n\n if (enabled(config, 'title-prefix') && hasTitlePrefix(issue.title, config.titlePrefixes)) {\n return true;\n }\n\n return false;\n}\n\n/** Root plus every descendant reached through `children`. */\nexport function collectCascadeIds(graph: DependencyGraph, roots: readonly string[]): string[] {\n const ids = new Set<string>();\n const walk = (id: string): void => {\n if (ids.has(id)) return;\n ids.add(id);\n for (const child of graph.nodes.get(id)?.relations.children ?? []) {\n walk(child);\n }\n };\n for (const root of roots) walk(root);\n return [...ids];\n}\n","import { emptyRelations, uniqueIds } from './relations.js';\nimport type { Issue, IssueRelations } from './types.js';\n\n/**\n * Dependency graph of a set of Issues, built by walking the relations every\n * provider exposes through `fetchRelations`.\n *\n * The traversal is deliberately *structural*: it follows hierarchy\n * (parent/children) and dependencies (blockedBy/blocking) and stops there. Plain\n * mentions are recorded on each node but never expanded — a body citing \"see\n * also #12\" would otherwise drag an unrelated Issue into an execution plan the\n * user is about to confirm.\n */\n\nexport interface DependencyGraphNode {\n id: string;\n relations: IssueRelations;\n /** Distance from the closest root, in traversal hops. */\n depth: number;\n /** The Issue itself, when the caller supplied a way to read it. */\n issue: Issue | null;\n /** True when the node was requested by the user rather than discovered. */\n root: boolean;\n}\n\nexport interface DependencyGraph {\n /** Ids the traversal started from, in the order they were requested. */\n roots: string[];\n /** Every discovered node, keyed by id, in discovery order. */\n nodes: Map<string, DependencyGraphNode>;\n /**\n * Dependency cycles found among the discovered nodes, each listed once,\n * starting at its smallest member. Reported rather than thrown: discovery\n * must still answer, and it is the ordering layer that refuses to run.\n */\n cycles: string[][];\n /** True when a limit stopped the traversal before it was exhausted. */\n truncated: boolean;\n limits: { maxNodes: number; maxDepth: number };\n}\n\n/**\n * Conservative defaults: a real hierarchy is a handful of Issues, and each node\n * costs several API calls. A malformed (or simply huge) graph must not turn a\n * `run` into a rate-limit incident.\n */\nexport const DEFAULT_MAX_NODES = 25;\nexport const DEFAULT_MAX_DEPTH = 3;\n\nexport interface BuildDependencyGraphOptions {\n maxNodes?: number;\n maxDepth?: number;\n /** Issues the caller already resolved, so the traversal does not re-read them. */\n known?: Iterable<Issue>;\n /** Reader for the Issues the traversal discovers. Optional. */\n fetchIssue?: (id: string) => Promise<Issue | null>;\n /** Warning sink for a source that failed. Silent by default. */\n warn?: (message: string) => void;\n}\n\n/** Edge `from` → `to`, meaning \"`from` must be resolved before `to`\". */\nexport interface DependencyEdge {\n from: string;\n to: string;\n}\n\n/**\n * Ordering constraints of a graph: only hierarchy-free, explicit dependencies.\n *\n * `blockedBy`/`blocking` are the two spellings of the same fact, so both are\n * normalized into a single direction here. Parent/child is **not** a constraint\n * — it is a presentation and tie-breaking concern (see `computeExecutionOrder`),\n * because a parent is usually an umbrella that neither blocks nor is blocked by\n * its children.\n *\n * Edges pointing outside the graph are dropped: an Issue the traversal never\n * reached cannot be scheduled, so a constraint against it is unenforceable.\n */\nexport function dependencyEdges(graph: DependencyGraph): DependencyEdge[] {\n const edges: DependencyEdge[] = [];\n const seen = new Set<string>();\n\n const add = (from: string, to: string): void => {\n if (from === to || !graph.nodes.has(from) || !graph.nodes.has(to)) return;\n const key = `${from}->${to}`;\n if (seen.has(key)) return;\n seen.add(key);\n edges.push({ from, to });\n };\n\n for (const node of graph.nodes.values()) {\n for (const blocker of node.relations.blockedBy) {\n add(blocker, node.id);\n }\n for (const blocked of node.relations.blocking) {\n add(node.id, blocked);\n }\n }\n\n return edges;\n}\n\n/** Every cycle of the dependency edges, each reported once. */\nexport function findCycles(graph: DependencyGraph): string[][] {\n const adjacency = new Map<string, string[]>();\n for (const { from, to } of dependencyEdges(graph)) {\n const list = adjacency.get(from);\n if (list === undefined) {\n adjacency.set(from, [to]);\n } else {\n list.push(to);\n }\n }\n\n const cycles: string[][] = [];\n const reported = new Set<string>();\n const state = new Map<string, 'visiting' | 'done'>();\n const stack: string[] = [];\n\n const record = (cycle: string[]): void => {\n // Rotate to the smallest member so the same cycle found from two entry\n // points is reported once.\n let pivot = 0;\n for (let i = 1; i < cycle.length; i++) {\n if (compareIds(cycle[i] as string, cycle[pivot] as string) < 0) pivot = i;\n }\n const normalized = [...cycle.slice(pivot), ...cycle.slice(0, pivot)];\n const key = normalized.join('->');\n if (reported.has(key)) return;\n reported.add(key);\n cycles.push(normalized);\n };\n\n const visit = (id: string): void => {\n state.set(id, 'visiting');\n stack.push(id);\n\n for (const next of adjacency.get(id) ?? []) {\n const status = state.get(next);\n if (status === 'visiting') {\n record(stack.slice(stack.indexOf(next)));\n continue;\n }\n if (status === undefined) {\n visit(next);\n }\n }\n\n stack.pop();\n state.set(id, 'done');\n };\n\n for (const id of graph.nodes.keys()) {\n if (!state.has(id)) visit(id);\n }\n\n return cycles;\n}\n\n/** Numeric when both ids are numbers, lexicographic otherwise. */\nexport function compareIds(a: string, b: string): number {\n const na = Number(a);\n const nb = Number(b);\n if (Number.isInteger(na) && Number.isInteger(nb)) {\n return na - nb;\n }\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/** Ids a node links to structurally, in traversal order. */\nfunction neighbours(relations: IssueRelations): string[] {\n return uniqueIds(\n [\n ...(relations.parent === null ? [] : [relations.parent]),\n ...relations.children,\n ...relations.blockedBy,\n ...relations.blocking,\n ],\n relations.id,\n );\n}\n\n/**\n * Walk the relations of `roots` breadth-first and return the whole structure.\n *\n * Failures never abort the walk: a node whose relations cannot be read enters\n * the graph with empty relations (and a warning), exactly like an Issue that\n * genuinely relates to nothing. That keeps a partially unavailable API from\n * costing the user the Issues that *were* discovered.\n *\n * Breadth-first is what makes `depth` meaningful under a limit: when the walk is\n * truncated, what is missing is the far edge of the graph, not a random subset.\n */\nexport async function buildDependencyGraph(\n roots: readonly string[],\n fetchRelations: (id: string) => Promise<IssueRelations | null>,\n options: BuildDependencyGraphOptions = {},\n): Promise<DependencyGraph> {\n const maxNodes = options.maxNodes ?? DEFAULT_MAX_NODES;\n const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;\n const warn = options.warn ?? ((): void => {});\n\n const known = new Map<string, Issue>();\n for (const issue of options.known ?? []) {\n known.set(issue.id, issue);\n }\n\n const rootIds = uniqueIds(roots);\n const nodes = new Map<string, DependencyGraphNode>();\n const queued = new Set<string>(rootIds);\n let truncated = false;\n\n let frontier: { id: string; depth: number }[] = rootIds.map((id) => ({ id, depth: 0 }));\n\n while (frontier.length > 0) {\n const next: { id: string; depth: number }[] = [];\n\n for (const { id, depth } of frontier) {\n if (nodes.size >= maxNodes) {\n truncated = true;\n break;\n }\n\n let relations: IssueRelations | null = null;\n try {\n relations = await fetchRelations(id);\n } catch (err) {\n warn(\n `Could not read the relations of issue '${id}': ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n let issue = known.get(id) ?? null;\n if (issue === null && options.fetchIssue !== undefined) {\n try {\n issue = await options.fetchIssue(id);\n } catch (err) {\n warn(`Could not read issue '${id}': ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n const resolved = relations ?? emptyRelations(id);\n nodes.set(id, { id, relations: resolved, depth, issue, root: rootIds.includes(id) });\n\n if (depth >= maxDepth) {\n // Only a node that actually has unexplored neighbours truncates the\n // graph; a leaf at max depth explored everything there was.\n if (neighbours(resolved).some((neighbour) => !queued.has(neighbour))) {\n truncated = true;\n }\n continue;\n }\n\n for (const neighbour of neighbours(resolved)) {\n if (queued.has(neighbour)) continue;\n queued.add(neighbour);\n next.push({ id: neighbour, depth: depth + 1 });\n }\n }\n\n if (nodes.size >= maxNodes && next.length > 0) {\n truncated = true;\n break;\n }\n frontier = next;\n }\n\n const graph: DependencyGraph = {\n roots: rootIds,\n nodes,\n cycles: [],\n truncated,\n limits: { maxNodes, maxDepth },\n };\n\n linkReferencedBy(graph);\n graph.cycles = findCycles(graph);\n\n return graph;\n}\n\n/**\n * Complete `referencedBy` with the inverse of `references` among the discovered\n * nodes.\n *\n * The origin only reports the citations it can see (GitHub's timeline covers\n * cross-references but not, for instance, a mention added by the textual\n * heuristic); inverting what the graph already holds costs no API call and\n * keeps the two directions consistent inside it.\n */\nfunction linkReferencedBy(graph: DependencyGraph): void {\n for (const node of graph.nodes.values()) {\n for (const cited of node.relations.references) {\n const target = graph.nodes.get(cited);\n if (target === undefined || target.id === node.id) continue;\n target.relations.referencedBy = uniqueIds(\n [...target.relations.referencedBy, node.id],\n target.id,\n );\n }\n }\n}\n","import { compareIds, type DependencyGraph, dependencyEdges, findCycles } from '../issues/graph.js';\nimport type { IssuePriority } from './types.js';\n\n/**\n * Turning a dependency graph into the order the queue runs in.\n *\n * The criteria are applied in the exact precedence the initiative specifies:\n *\n * 1. **dependencies and blocks** — hard constraints; an Issue never starts\n * before something it depends on has finished. This is the topological part\n * and the only one that can make the computation fail (a cycle);\n * 2. **hierarchy** — with two Issues both ready to run, the ancestor goes\n * first, so an umbrella Issue frames the work of its sub-issues;\n * 3. **priority** — the GitHub labels `high`, `medium` and `low`;\n * 4. **logical order** — the identifier, numerically when it is a number.\n *\n * Everything below the first item is a *tie-break among Issues that are already\n * free to run*, never a constraint: a `high` Issue that depends on a `low` one\n * still runs second, which is the whole point of the feature.\n */\n\nconst PRIORITY_LABELS: Record<string, IssuePriority> = {\n high: 'high',\n medium: 'medium',\n low: 'low',\n};\n\n/** Ranking of a priority; an Issue with no label sorts after every labelled one. */\nconst PRIORITY_RANK: Record<IssuePriority, number> = { high: 0, medium: 1, low: 2 };\nconst NO_PRIORITY_RANK = 3;\n\n/**\n * Priority carried by a label set, or `null` when there is none.\n *\n * Both the bare labels used by this repository (`high`) and the qualified\n * spellings other projects use (`priority: high`, `priority/high`, `P-high`)\n * are accepted; anything else is simply not a priority. The absence of a label\n * is never an error — most Issues have none.\n */\nexport function priorityOf(labels: readonly string[]): IssuePriority | null {\n for (const label of labels) {\n const normalized = label.trim().toLowerCase();\n const direct = PRIORITY_LABELS[normalized];\n if (direct !== undefined) return direct;\n\n const qualified = /^(?:priority|prio|p)\\s*[:/-]\\s*(high|medium|low)$/.exec(normalized);\n const matched = qualified?.[1];\n if (matched !== undefined) return PRIORITY_LABELS[matched] as IssuePriority;\n }\n return null;\n}\n\nexport interface ExecutionOrderOptions {\n /**\n * Subset of the graph to schedule. Defaults to every node — the \"run the\n * whole hierarchy\" answer; the \"only the ones I asked for\" answer passes the\n * requested ids.\n */\n include?: readonly string[];\n}\n\nexport type ExecutionOrderResult =\n | { ok: true; order: string[] }\n | { ok: false; cycles: string[][] };\n\n/** Number of ancestors of `id` inside the selection: 0 for a top-level Issue. */\nfunction hierarchyDepth(\n id: string,\n parentOf: Map<string, string | null>,\n selected: Set<string>,\n): number {\n let depth = 0;\n let current = parentOf.get(id) ?? null;\n const seen = new Set<string>([id]);\n\n while (current !== null && selected.has(current) && !seen.has(current)) {\n depth++;\n seen.add(current);\n current = parentOf.get(current) ?? null;\n }\n\n return depth;\n}\n\n/**\n * Order the selected Issues of a graph.\n *\n * Kahn's algorithm with a deterministic tie-break, so the same graph always\n * produces the same order — a plan the user confirmed must not reshuffle itself\n * on the next run.\n *\n * A cycle is reported, never worked around: an arbitrary order would silently\n * implement an Issue before its dependency, which is exactly the failure this\n * whole feature exists to prevent. Constraints pointing outside the selection\n * are dropped — an Issue that is not going to run cannot block anything.\n */\nexport function computeExecutionOrder(\n graph: DependencyGraph,\n options: ExecutionOrderOptions = {},\n): ExecutionOrderResult {\n const selected = new Set(\n (options.include ?? [...graph.nodes.keys()]).filter((id) => graph.nodes.has(id)),\n );\n\n const parentOf = new Map<string, string | null>();\n const priority = new Map<string, IssuePriority | null>();\n for (const id of selected) {\n const node = graph.nodes.get(id);\n parentOf.set(id, node?.relations.parent ?? null);\n priority.set(id, priorityOf(node?.issue?.labels ?? []));\n }\n\n const incoming = new Map<string, Set<string>>();\n const outgoing = new Map<string, string[]>();\n for (const id of selected) {\n incoming.set(id, new Set());\n outgoing.set(id, []);\n }\n for (const { from, to } of dependencyEdges(graph)) {\n if (!selected.has(from) || !selected.has(to) || from === to) continue;\n incoming.get(to)?.add(from);\n outgoing.get(from)?.push(to);\n }\n\n const depth = new Map<string, number>();\n for (const id of selected) {\n depth.set(id, hierarchyDepth(id, parentOf, selected));\n }\n\n const rank = (id: string): number => {\n const value = priority.get(id) ?? null;\n return value === null ? NO_PRIORITY_RANK : PRIORITY_RANK[value];\n };\n\n const better = (a: string, b: string): number => {\n const byHierarchy = (depth.get(a) ?? 0) - (depth.get(b) ?? 0);\n if (byHierarchy !== 0) return byHierarchy;\n const byPriority = rank(a) - rank(b);\n if (byPriority !== 0) return byPriority;\n return compareIds(a, b);\n };\n\n const ready = [...selected].filter((id) => (incoming.get(id)?.size ?? 0) === 0);\n const order: string[] = [];\n\n while (ready.length > 0) {\n ready.sort(better);\n const next = ready.shift() as string;\n order.push(next);\n\n for (const dependent of outgoing.get(next) ?? []) {\n const pending = incoming.get(dependent);\n if (pending === undefined) continue;\n pending.delete(next);\n if (pending.size === 0) {\n ready.push(dependent);\n }\n }\n }\n\n if (order.length !== selected.size) {\n // Prefer the cycles the graph already recorded; recompute only if the\n // selection is what closed the loop.\n const cycles = graph.cycles.length > 0 ? graph.cycles : findCycles(graph);\n const relevant = cycles.filter((cycle) => cycle.every((id) => selected.has(id)));\n return { ok: false, cycles: relevant.length > 0 ? relevant : cycles };\n }\n\n return { ok: true, order };\n}\n\n/** One-line description of the cycles, for an error message. */\nexport function describeCycles(cycles: readonly string[][]): string {\n return cycles.map((cycle) => [...cycle, cycle[0]].map((id) => `#${id}`).join(' → ')).join('; ');\n}\n","import type { PipelineState, TaskPlan } from '../types.js';\nimport { loadTaskPlan, saveTaskPlan } from './state-manager.js';\n\n/**\n * Ordered pipeline phases. Each phase must complete before the next can start.\n *\n * `pr-review` is deliberately NOT here: it is opt-in via `--pr-review`. Adding\n * it to the default set would make every existing `tasks.json` (which has no\n * `prReviewCompleted`) look unfinished, so `getNextPhase()` would resume into a\n * phase the user never asked for.\n */\nexport const PIPELINE_PHASES = ['init', 'prd', 'plan', 'execute', 'review', 'pr'] as const;\n\n/**\n * The default set plus the opt-in `pr-review` phase, used when the user passes\n * `--pr-review` (or the flag was persisted in `plan.prReview.enabled`).\n */\nexport const PIPELINE_PHASES_WITH_PR_REVIEW = [...PIPELINE_PHASES, 'pr-review'] as const;\n\nexport type PipelinePhase = (typeof PIPELINE_PHASES_WITH_PR_REVIEW)[number];\n\n/**\n * Pipeline phases excluding the 'pr' phase, for --no-branch mode.\n * `pr-review` is excluded too: without a branch there is no PR to review.\n */\nexport const PIPELINE_PHASES_NO_BRANCH = PIPELINE_PHASES.filter(\n (p): p is Exclude<PipelinePhase, 'pr' | 'pr-review'> => p !== 'pr',\n);\n\n/**\n * Map pipeline phases to their corresponding PipelineState field.\n * 'init' has no persisted state — it's a runtime-only check.\n */\nconst PHASE_TO_FIELD: Record<PipelinePhase, keyof PipelineState | null> = {\n init: null,\n prd: 'prdCompleted',\n plan: 'jsonCompleted',\n execute: 'executionCompleted',\n review: 'reviewCompleted',\n pr: 'prCreated',\n 'pr-review': 'prReviewCompleted',\n};\n\nexport class PipelineManager {\n private tasksJsonPath: string;\n private plan: TaskPlan;\n private activePhases: readonly PipelinePhase[];\n\n constructor(plan: TaskPlan, tasksJsonPath: string, activePhases?: readonly PipelinePhase[]) {\n this.plan = plan;\n this.tasksJsonPath = tasksJsonPath;\n this.activePhases = activePhases ?? PIPELINE_PHASES;\n }\n\n /**\n * Reload state from disk.\n */\n async reload(): Promise<void> {\n this.plan = await loadTaskPlan(this.tasksJsonPath);\n }\n\n /**\n * Check if a phase is complete.\n */\n isPhaseComplete(phase: PipelinePhase): boolean {\n const field = PHASE_TO_FIELD[phase];\n if (field === null) return true; // init is always \"complete\" after running\n return this.plan.pipeline?.[field] ?? false;\n }\n\n /**\n * Get the first incomplete phase in the pipeline.\n * Returns null if all phases are complete.\n */\n getNextPhase(): PipelinePhase | null {\n for (const phase of this.activePhases) {\n if (!this.isPhaseComplete(phase)) {\n return phase;\n }\n }\n return null;\n }\n\n /**\n * Get the current phase (alias for getNextPhase).\n */\n getCurrentPhase(): PipelinePhase | null {\n return this.getNextPhase();\n }\n\n /**\n * Check whether we can resume from a specific phase.\n * All prerequisite phases must be complete.\n */\n canResume(fromPhase: PipelinePhase): boolean {\n const idx = this.activePhases.indexOf(fromPhase);\n if (idx < 0) return false;\n\n // All active phases before fromPhase must be complete\n for (let i = 0; i < idx; i++) {\n if (!this.isPhaseComplete(this.activePhases[i])) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Mark a phase as complete and persist to disk.\n */\n async markPhaseComplete(phase: PipelinePhase): Promise<void> {\n const field = PHASE_TO_FIELD[phase];\n if (field === null) return; // init has no persisted state\n\n this.plan = {\n ...this.plan,\n pipeline: {\n ...(this.plan.pipeline ?? {\n prdCompleted: false,\n jsonCompleted: false,\n executionCompleted: false,\n reviewCompleted: false,\n prCreated: false,\n }),\n [field]: true,\n },\n };\n\n await saveTaskPlan(this.tasksJsonPath, this.plan);\n }\n}\n"],"mappings":"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","names":["parentOf"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/telemetry/aggregate.ts"],"sourcesContent":["import type { TaskPlan } from '../types.js';\nimport type { ExecutionRecord, ExecutionStatus, ExecutionSummary } from './types.js';\n\nfunction emptySummary(discarded = 0): ExecutionSummary {\n return {\n count: 0,\n discarded,\n byStatus: {},\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheCreationTokens: 0,\n reasoningTokens: 0,\n },\n totalCost: { reported: 0, estimated: 0, unknownExecutions: 0 },\n };\n}\n\nfunction add(into: ExecutionSummary, record: ExecutionRecord): void {\n into.count += 1;\n into.byStatus[record.status] = (into.byStatus[record.status] ?? 0) + 1;\n if (record.usage !== null) {\n into.usage.inputTokens += record.usage.inputTokens ?? 0;\n into.usage.outputTokens += record.usage.outputTokens ?? 0;\n into.usage.cacheReadTokens += record.usage.cacheReadTokens ?? 0;\n into.usage.cacheCreationTokens += record.usage.cacheCreationTokens ?? 0;\n into.usage.reasoningTokens += record.usage.reasoningTokens ?? 0;\n }\n if (record.cost.status === 'reported') {\n into.totalCost.reported += record.cost.amount;\n } else if (record.cost.status === 'estimated') {\n into.totalCost.estimated += record.cost.amount;\n } else {\n into.totalCost.unknownExecutions += 1;\n }\n}\n\nfunction isPlan(value: TaskPlan | readonly ExecutionRecord[]): value is TaskPlan {\n return !Array.isArray(value) && 'userStories' in value;\n}\n\nexport function summarize(\n input: TaskPlan | readonly ExecutionRecord[],\n discarded = 0,\n): ExecutionSummary {\n const records = isPlan(input) ? (input.executions ?? []) : input;\n const summary = emptySummary(discarded);\n for (const record of records) add(summary, record);\n return summary;\n}\n\nexport type GroupKey = 'harness' | 'provider' | 'model' | 'purpose' | 'status';\n\nfunction keyOf(record: ExecutionRecord, key: GroupKey): string {\n switch (key) {\n case 'harness':\n return record.agent.harness;\n case 'provider':\n return record.agent.provider ?? 'unknown';\n case 'model':\n return record.agent.model.resolved ?? record.agent.model.requested ?? 'unknown';\n case 'purpose':\n return record.purpose;\n case 'status':\n return record.status;\n default: {\n const _exhaustive: never = key;\n return _exhaustive;\n }\n }\n}\n\nexport function groupBy(\n records: readonly ExecutionRecord[],\n key: GroupKey,\n): Map<string, ExecutionSummary> {\n const groups = new Map<string, ExecutionRecord[]>();\n for (const record of records) {\n const name = keyOf(record, key);\n const bucket = groups.get(name);\n if (bucket === undefined) groups.set(name, [record]);\n else bucket.push(record);\n }\n const result = new Map<string, ExecutionSummary>();\n for (const [name, bucket] of groups) {\n result.set(name, summarize(bucket));\n }\n return result;\n}\n\nexport type { ExecutionStatus };\n"],"mappings":";;;AAGA,SAAS,aAAa,YAAY,GAAqB;AACrD,SAAO;AAAA,IACL,OAAO;AAAA,IACP;AAAA,IACA,UAAU,CAAC;AAAA,IACX,OAAO;AAAA,MACL,aAAa;AAAA,MACb,cAAc;AAAA,MACd,iBAAiB;AAAA,MACjB,qBAAqB;AAAA,MACrB,iBAAiB;AAAA,IACnB;AAAA,IACA,WAAW,EAAE,UAAU,GAAG,WAAW,GAAG,mBAAmB,EAAE;AAAA,EAC/D;AACF;AAEA,SAAS,IAAI,MAAwB,QAA+B;AAClE,OAAK,SAAS;AACd,OAAK,SAAS,OAAO,MAAM,KAAK,KAAK,SAAS,OAAO,MAAM,KAAK,KAAK;AACrE,MAAI,OAAO,UAAU,MAAM;AACzB,SAAK,MAAM,eAAe,OAAO,MAAM,eAAe;AACtD,SAAK,MAAM,gBAAgB,OAAO,MAAM,gBAAgB;AACxD,SAAK,MAAM,mBAAmB,OAAO,MAAM,mBAAmB;AAC9D,SAAK,MAAM,uBAAuB,OAAO,MAAM,uBAAuB;AACtE,SAAK,MAAM,mBAAmB,OAAO,MAAM,mBAAmB;AAAA,EAChE;AACA,MAAI,OAAO,KAAK,WAAW,YAAY;AACrC,SAAK,UAAU,YAAY,OAAO,KAAK;AAAA,EACzC,WAAW,OAAO,KAAK,WAAW,aAAa;AAC7C,SAAK,UAAU,aAAa,OAAO,KAAK;AAAA,EAC1C,OAAO;AACL,SAAK,UAAU,qBAAqB;AAAA,EACtC;AACF;AAEA,SAAS,OAAO,OAAiE;AAC/E,SAAO,CAAC,MAAM,QAAQ,KAAK,KAAK,iBAAiB;AACnD;AAEO,SAAS,UACd,OACA,YAAY,GACM;AAClB,QAAM,UAAU,OAAO,KAAK,IAAK,MAAM,cAAc,CAAC,IAAK;AAC3D,QAAM,UAAU,aAAa,SAAS;AACtC,aAAW,UAAU,QAAS,KAAI,SAAS,MAAM;AACjD,SAAO;AACT;AAIA,SAAS,MAAM,QAAyB,KAAuB;AAC7D,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,aAAO,OAAO,MAAM;AAAA,IACtB,KAAK;AACH,aAAO,OAAO,MAAM,YAAY;AAAA,IAClC,KAAK;AACH,aAAO,OAAO,MAAM,MAAM,YAAY,OAAO,MAAM,MAAM,aAAa;AAAA,IACxE,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,SAAS;AACP,YAAM,cAAqB;AAC3B,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,QACd,SACA,KAC+B;AAC/B,QAAM,SAAS,oBAAI,IAA+B;AAClD,aAAW,UAAU,SAAS;AAC5B,UAAM,OAAO,MAAM,QAAQ,GAAG;AAC9B,UAAM,SAAS,OAAO,IAAI,IAAI;AAC9B,QAAI,WAAW,OAAW,QAAO,IAAI,MAAM,CAAC,MAAM,CAAC;AAAA,QAC9C,QAAO,KAAK,MAAM;AAAA,EACzB;AACA,QAAM,SAAS,oBAAI,IAA8B;AACjD,aAAW,CAAC,MAAM,MAAM,KAAK,QAAQ;AACnC,WAAO,IAAI,MAAM,UAAU,MAAM,CAAC;AAAA,EACpC;AACA,SAAO;AACT;","names":[]}