@telora/daemon 0.22.85 → 0.22.91

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 (317) hide show
  1. package/build-info.json +4 -3
  2. package/dist/backends/index.d.ts +1 -1
  3. package/dist/backends/index.d.ts.map +1 -1
  4. package/dist/backends/index.js +1 -1
  5. package/dist/backends/index.js.map +1 -1
  6. package/dist/backends/resolve.d.ts +37 -8
  7. package/dist/backends/resolve.d.ts.map +1 -1
  8. package/dist/backends/resolve.js +62 -28
  9. package/dist/backends/resolve.js.map +1 -1
  10. package/dist/ci/ci-escalation.d.ts +17 -1
  11. package/dist/ci/ci-escalation.d.ts.map +1 -1
  12. package/dist/ci/ci-escalation.js +51 -3
  13. package/dist/ci/ci-escalation.js.map +1 -1
  14. package/dist/ci/ci-red-attribution.d.ts +85 -0
  15. package/dist/ci/ci-red-attribution.d.ts.map +1 -0
  16. package/dist/ci/ci-red-attribution.js +108 -0
  17. package/dist/ci/ci-red-attribution.js.map +1 -0
  18. package/dist/ci/ci-sensor-loop.d.ts +84 -80
  19. package/dist/ci/ci-sensor-loop.d.ts.map +1 -1
  20. package/dist/ci/ci-sensor-loop.js +132 -129
  21. package/dist/ci/ci-sensor-loop.js.map +1 -1
  22. package/dist/cli/connect/config-write.d.ts +7 -4
  23. package/dist/cli/connect/config-write.d.ts.map +1 -1
  24. package/dist/cli/connect/config-write.js +13 -7
  25. package/dist/cli/connect/config-write.js.map +1 -1
  26. package/dist/cli/connect.d.ts.map +1 -1
  27. package/dist/cli/connect.js +11 -1
  28. package/dist/cli/connect.js.map +1 -1
  29. package/dist/cli/update-commands.d.ts.map +1 -1
  30. package/dist/cli/update-commands.js +2 -1
  31. package/dist/cli/update-commands.js.map +1 -1
  32. package/dist/completion/coverage-stabilized.d.ts.map +1 -1
  33. package/dist/completion/coverage-stabilized.js +1 -1
  34. package/dist/completion/coverage-stabilized.js.map +1 -1
  35. package/dist/completion/escalations.d.ts +40 -5
  36. package/dist/completion/escalations.d.ts.map +1 -1
  37. package/dist/completion/escalations.js +19 -11
  38. package/dist/completion/escalations.js.map +1 -1
  39. package/dist/completion/event-escalations.d.ts +12 -1
  40. package/dist/completion/event-escalations.d.ts.map +1 -1
  41. package/dist/completion/event-escalations.js +73 -37
  42. package/dist/completion/event-escalations.js.map +1 -1
  43. package/dist/completion/event.d.ts +4 -1
  44. package/dist/completion/event.d.ts.map +1 -1
  45. package/dist/completion/event.js +1 -1
  46. package/dist/completion/event.js.map +1 -1
  47. package/dist/completion/human-only-act.d.ts +1 -1
  48. package/dist/completion/human-only-act.js +1 -1
  49. package/dist/completion/machine-remediable-demotion.d.ts +28 -1
  50. package/dist/completion/machine-remediable-demotion.d.ts.map +1 -1
  51. package/dist/completion/machine-remediable-demotion.js +9 -6
  52. package/dist/completion/machine-remediable-demotion.js.map +1 -1
  53. package/dist/completion/read-only-pristine.d.ts.map +1 -1
  54. package/dist/completion/read-only-pristine.js +4 -0
  55. package/dist/completion/read-only-pristine.js.map +1 -1
  56. package/dist/completion/review-exit-deps.js +1 -1
  57. package/dist/completion/review-exit-deps.js.map +1 -1
  58. package/dist/completion/review-exit-escalations.d.ts +10 -0
  59. package/dist/completion/review-exit-escalations.d.ts.map +1 -1
  60. package/dist/completion/review-exit-escalations.js +38 -3
  61. package/dist/completion/review-exit-escalations.js.map +1 -1
  62. package/dist/completion/review-exit-phase.d.ts.map +1 -1
  63. package/dist/completion/review-exit-phase.js +8 -7
  64. package/dist/completion/review-exit-phase.js.map +1 -1
  65. package/dist/completion/review-exit-shortfall.d.ts +5 -1
  66. package/dist/completion/review-exit-shortfall.d.ts.map +1 -1
  67. package/dist/completion/review-exit-shortfall.js +8 -3
  68. package/dist/completion/review-exit-shortfall.js.map +1 -1
  69. package/dist/completion/status-advance-phase.d.ts +2 -0
  70. package/dist/completion/status-advance-phase.d.ts.map +1 -1
  71. package/dist/completion/status-advance-phase.js +2 -1
  72. package/dist/completion/status-advance-phase.js.map +1 -1
  73. package/dist/completion/terminated-work-reconcile.js +2 -2
  74. package/dist/completion/terminated-work-reconcile.js.map +1 -1
  75. package/dist/delivery/delivery-advance-block.d.ts +3 -3
  76. package/dist/delivery/delivery-advance-block.d.ts.map +1 -1
  77. package/dist/delivery/delivery-advance-block.js +13 -8
  78. package/dist/delivery/delivery-advance-block.js.map +1 -1
  79. package/dist/delivery/delivery-lifecycle.d.ts +10 -0
  80. package/dist/delivery/delivery-lifecycle.d.ts.map +1 -1
  81. package/dist/delivery/delivery-lifecycle.js +12 -6
  82. package/dist/delivery/delivery-lifecycle.js.map +1 -1
  83. package/dist/delivery/delivery-merge-ops.d.ts.map +1 -1
  84. package/dist/delivery/delivery-merge-ops.js +39 -21
  85. package/dist/delivery/delivery-merge-ops.js.map +1 -1
  86. package/dist/delivery/external-hold.d.ts +8 -2
  87. package/dist/delivery/external-hold.d.ts.map +1 -1
  88. package/dist/delivery/external-hold.js +12 -5
  89. package/dist/delivery/external-hold.js.map +1 -1
  90. package/dist/delivery/operator-hold.d.ts +6 -1
  91. package/dist/delivery/operator-hold.d.ts.map +1 -1
  92. package/dist/delivery/operator-hold.js +31 -10
  93. package/dist/delivery/operator-hold.js.map +1 -1
  94. package/dist/delivery/paused-revival.d.ts +6 -0
  95. package/dist/delivery/paused-revival.d.ts.map +1 -1
  96. package/dist/delivery/paused-revival.js +3 -1
  97. package/dist/delivery/paused-revival.js.map +1 -1
  98. package/dist/directive/directive-dispatch.d.ts.map +1 -1
  99. package/dist/directive/directive-dispatch.js +6 -3
  100. package/dist/directive/directive-dispatch.js.map +1 -1
  101. package/dist/directive/directive-queue.d.ts.map +1 -1
  102. package/dist/directive/directive-queue.js +4 -1
  103. package/dist/directive/directive-queue.js.map +1 -1
  104. package/dist/drift/staleness-sensor.d.ts +20 -0
  105. package/dist/drift/staleness-sensor.d.ts.map +1 -1
  106. package/dist/drift/staleness-sensor.js +54 -21
  107. package/dist/drift/staleness-sensor.js.map +1 -1
  108. package/dist/drift/staleness.d.ts +18 -5
  109. package/dist/drift/staleness.d.ts.map +1 -1
  110. package/dist/drift/staleness.js +28 -15
  111. package/dist/drift/staleness.js.map +1 -1
  112. package/dist/focus/focus-executor.d.ts.map +1 -1
  113. package/dist/focus/focus-executor.js +6 -8
  114. package/dist/focus/focus-executor.js.map +1 -1
  115. package/dist/focus/focus-loop-registry.d.ts.map +1 -1
  116. package/dist/focus/focus-loop-registry.js +23 -13
  117. package/dist/focus/focus-loop-registry.js.map +1 -1
  118. package/dist/focus/focus-respawn-guard.d.ts.map +1 -1
  119. package/dist/focus/focus-respawn-guard.js +21 -4
  120. package/dist/focus/focus-respawn-guard.js.map +1 -1
  121. package/dist/focus/listener-auto-advance.d.ts.map +1 -1
  122. package/dist/focus/listener-auto-advance.js +10 -6
  123. package/dist/focus/listener-auto-advance.js.map +1 -1
  124. package/dist/focus/verify-open-issues.d.ts.map +1 -1
  125. package/dist/focus/verify-open-issues.js +21 -1
  126. package/dist/focus/verify-open-issues.js.map +1 -1
  127. package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts.map +1 -1
  128. package/dist/focus-stage-lifecycle/wedge-watchdog.js +35 -14
  129. package/dist/focus-stage-lifecycle/wedge-watchdog.js.map +1 -1
  130. package/dist/loops/index.d.ts.map +1 -1
  131. package/dist/loops/index.js +2 -1
  132. package/dist/loops/index.js.map +1 -1
  133. package/dist/merge/focus-merge.d.ts +1 -1
  134. package/dist/merge/focus-merge.d.ts.map +1 -1
  135. package/dist/merge/focus-merge.js +5 -1
  136. package/dist/merge/focus-merge.js.map +1 -1
  137. package/dist/merge/merge-back-loop.d.ts +2 -1
  138. package/dist/merge/merge-back-loop.d.ts.map +1 -1
  139. package/dist/merge/merge-back-loop.js +15 -12
  140. package/dist/merge/merge-back-loop.js.map +1 -1
  141. package/dist/merge/merge-conflict-escalation.d.ts.map +1 -1
  142. package/dist/merge/merge-conflict-escalation.js +4 -0
  143. package/dist/merge/merge-conflict-escalation.js.map +1 -1
  144. package/dist/nexus/return.d.ts +14 -15
  145. package/dist/nexus/return.d.ts.map +1 -1
  146. package/dist/nexus/return.js +41 -31
  147. package/dist/nexus/return.js.map +1 -1
  148. package/dist/prompt-sections/execution-sections.d.ts +13 -5
  149. package/dist/prompt-sections/execution-sections.d.ts.map +1 -1
  150. package/dist/prompt-sections/execution-sections.js +78 -18
  151. package/dist/prompt-sections/execution-sections.js.map +1 -1
  152. package/dist/prompt-sections/routing-decider-sections.d.ts +60 -0
  153. package/dist/prompt-sections/routing-decider-sections.d.ts.map +1 -0
  154. package/dist/prompt-sections/routing-decider-sections.js +197 -0
  155. package/dist/prompt-sections/routing-decider-sections.js.map +1 -0
  156. package/dist/queries/agent-resolvable-aging.d.ts +4 -3
  157. package/dist/queries/agent-resolvable-aging.d.ts.map +1 -1
  158. package/dist/queries/agent-resolvable-aging.js +5 -4
  159. package/dist/queries/agent-resolvable-aging.js.map +1 -1
  160. package/dist/queries/agent-resolvable-promotion.d.ts +172 -124
  161. package/dist/queries/agent-resolvable-promotion.d.ts.map +1 -1
  162. package/dist/queries/agent-resolvable-promotion.js +332 -307
  163. package/dist/queries/agent-resolvable-promotion.js.map +1 -1
  164. package/dist/queries/agent-resolvable-retirement.d.ts +105 -0
  165. package/dist/queries/agent-resolvable-retirement.d.ts.map +1 -0
  166. package/dist/queries/agent-resolvable-retirement.js +227 -0
  167. package/dist/queries/agent-resolvable-retirement.js.map +1 -0
  168. package/dist/queries/cause-key.d.ts +109 -0
  169. package/dist/queries/cause-key.d.ts.map +1 -1
  170. package/dist/queries/cause-key.js +93 -1
  171. package/dist/queries/cause-key.js.map +1 -1
  172. package/dist/queries/deliveries.d.ts +14 -0
  173. package/dist/queries/deliveries.d.ts.map +1 -1
  174. package/dist/queries/deliveries.js +20 -2
  175. package/dist/queries/deliveries.js.map +1 -1
  176. package/dist/queries/delivery-gate-state.d.ts +130 -5
  177. package/dist/queries/delivery-gate-state.d.ts.map +1 -1
  178. package/dist/queries/delivery-gate-state.js +111 -2
  179. package/dist/queries/delivery-gate-state.js.map +1 -1
  180. package/dist/queries/escalation-consumption.d.ts +95 -15
  181. package/dist/queries/escalation-consumption.d.ts.map +1 -1
  182. package/dist/queries/escalation-consumption.js +162 -24
  183. package/dist/queries/escalation-consumption.js.map +1 -1
  184. package/dist/queries/escalation-dispositions.d.ts +19 -7
  185. package/dist/queries/escalation-dispositions.d.ts.map +1 -1
  186. package/dist/queries/escalation-dispositions.js +7 -0
  187. package/dist/queries/escalation-dispositions.js.map +1 -1
  188. package/dist/queries/escalation-suppression.d.ts +63 -19
  189. package/dist/queries/escalation-suppression.d.ts.map +1 -1
  190. package/dist/queries/escalation-suppression.js +64 -35
  191. package/dist/queries/escalation-suppression.js.map +1 -1
  192. package/dist/queries/escalations.d.ts +127 -70
  193. package/dist/queries/escalations.d.ts.map +1 -1
  194. package/dist/queries/escalations.js +170 -188
  195. package/dist/queries/escalations.js.map +1 -1
  196. package/dist/queries/filing-observation.d.ts +82 -0
  197. package/dist/queries/filing-observation.d.ts.map +1 -0
  198. package/dist/queries/filing-observation.js +157 -0
  199. package/dist/queries/filing-observation.js.map +1 -0
  200. package/dist/queries/holds.d.ts +69 -2
  201. package/dist/queries/holds.d.ts.map +1 -1
  202. package/dist/queries/holds.js +106 -11
  203. package/dist/queries/holds.js.map +1 -1
  204. package/dist/queries/incident-correlation.d.ts +291 -0
  205. package/dist/queries/incident-correlation.d.ts.map +1 -0
  206. package/dist/queries/incident-correlation.js +408 -0
  207. package/dist/queries/incident-correlation.js.map +1 -0
  208. package/dist/queries/routing-measurement.d.ts +48 -1
  209. package/dist/queries/routing-measurement.d.ts.map +1 -1
  210. package/dist/queries/routing-measurement.js +64 -2
  211. package/dist/queries/routing-measurement.js.map +1 -1
  212. package/dist/queries/routing-quality.d.ts +8 -0
  213. package/dist/queries/routing-quality.d.ts.map +1 -1
  214. package/dist/queries/routing-quality.js +12 -3
  215. package/dist/queries/routing-quality.js.map +1 -1
  216. package/dist/queries/shared.js +1 -1
  217. package/dist/shell/crash-recovery.d.ts.map +1 -1
  218. package/dist/shell/crash-recovery.js +6 -6
  219. package/dist/shell/crash-recovery.js.map +1 -1
  220. package/dist/shell/heartbeat-metadata.d.ts +18 -0
  221. package/dist/shell/heartbeat-metadata.d.ts.map +1 -1
  222. package/dist/shell/heartbeat-metadata.js +24 -0
  223. package/dist/shell/heartbeat-metadata.js.map +1 -1
  224. package/dist/shell/heartbeat.d.ts.map +1 -1
  225. package/dist/shell/heartbeat.js +6 -17
  226. package/dist/shell/heartbeat.js.map +1 -1
  227. package/dist/shell/install-probe-tick.d.ts.map +1 -1
  228. package/dist/shell/install-probe-tick.js +3 -1
  229. package/dist/shell/install-probe-tick.js.map +1 -1
  230. package/dist/shell/model-resolution.d.ts +9 -4
  231. package/dist/shell/model-resolution.d.ts.map +1 -1
  232. package/dist/shell/model-resolution.js +46 -7
  233. package/dist/shell/model-resolution.js.map +1 -1
  234. package/dist/shell/unified-init.d.ts +24 -0
  235. package/dist/shell/unified-init.d.ts.map +1 -1
  236. package/dist/shell/unified-init.js +22 -17
  237. package/dist/shell/unified-init.js.map +1 -1
  238. package/dist/spawn/model-id-guards.d.ts +50 -0
  239. package/dist/spawn/model-id-guards.d.ts.map +1 -0
  240. package/dist/spawn/model-id-guards.js +110 -0
  241. package/dist/spawn/model-id-guards.js.map +1 -0
  242. package/dist/spawn/model-routing.d.ts +1 -43
  243. package/dist/spawn/model-routing.d.ts.map +1 -1
  244. package/dist/spawn/model-routing.js +3 -94
  245. package/dist/spawn/model-routing.js.map +1 -1
  246. package/dist/spawn/respawn-cycle-escalation.d.ts +10 -2
  247. package/dist/spawn/respawn-cycle-escalation.d.ts.map +1 -1
  248. package/dist/spawn/respawn-cycle-escalation.js +16 -2
  249. package/dist/spawn/respawn-cycle-escalation.js.map +1 -1
  250. package/dist/spawn/routing-decider-spawn.d.ts +93 -0
  251. package/dist/spawn/routing-decider-spawn.d.ts.map +1 -0
  252. package/dist/spawn/routing-decider-spawn.js +185 -0
  253. package/dist/spawn/routing-decider-spawn.js.map +1 -0
  254. package/dist/spawn/routing-decider.d.ts +42 -0
  255. package/dist/spawn/routing-decider.d.ts.map +1 -0
  256. package/dist/spawn/routing-decider.js +95 -0
  257. package/dist/spawn/routing-decider.js.map +1 -0
  258. package/dist/spawn/shared.d.ts +8 -0
  259. package/dist/spawn/shared.d.ts.map +1 -1
  260. package/dist/spawn/shared.js +1 -1
  261. package/dist/spawn/shared.js.map +1 -1
  262. package/dist/spawn/spawn-decision.d.ts +24 -16
  263. package/dist/spawn/spawn-decision.d.ts.map +1 -1
  264. package/dist/spawn/spawn-decision.js +28 -11
  265. package/dist/spawn/spawn-decision.js.map +1 -1
  266. package/dist/spawn/worktree-session.d.ts +23 -7
  267. package/dist/spawn/worktree-session.d.ts.map +1 -1
  268. package/dist/spawn/worktree-session.js +54 -22
  269. package/dist/spawn/worktree-session.js.map +1 -1
  270. package/dist/testing/fixtures/index.js +1 -1
  271. package/dist/types/index.js +1 -1
  272. package/dist/update/auto-update.d.ts +20 -3
  273. package/dist/update/auto-update.d.ts.map +1 -1
  274. package/dist/update/auto-update.js +8 -24
  275. package/dist/update/auto-update.js.map +1 -1
  276. package/dist/update/self-update.d.ts +43 -4
  277. package/dist/update/self-update.d.ts.map +1 -1
  278. package/dist/update/self-update.js +61 -38
  279. package/dist/update/self-update.js.map +1 -1
  280. package/dist/update/version-check.d.ts +29 -45
  281. package/dist/update/version-check.d.ts.map +1 -1
  282. package/dist/update/version-check.js +48 -79
  283. package/dist/update/version-check.js.map +1 -1
  284. package/dist/verify/failure-signature.d.ts +91 -0
  285. package/dist/verify/failure-signature.d.ts.map +1 -0
  286. package/dist/verify/failure-signature.js +233 -0
  287. package/dist/verify/failure-signature.js.map +1 -0
  288. package/dist/verify/shortfall-repeat.d.ts +12 -9
  289. package/dist/verify/shortfall-repeat.d.ts.map +1 -1
  290. package/dist/verify/shortfall-repeat.js +38 -14
  291. package/dist/verify/shortfall-repeat.js.map +1 -1
  292. package/dist/verify/stall-detectors.d.ts +19 -43
  293. package/dist/verify/stall-detectors.d.ts.map +1 -1
  294. package/dist/verify/stall-detectors.js +26 -101
  295. package/dist/verify/stall-detectors.js.map +1 -1
  296. package/dist/verify/unreviewed-settle-detector.d.ts +8 -0
  297. package/dist/verify/unreviewed-settle-detector.d.ts.map +1 -1
  298. package/dist/verify/unreviewed-settle-detector.js +15 -1
  299. package/dist/verify/unreviewed-settle-detector.js.map +1 -1
  300. package/dist/verify/verification-deps-default.d.ts.map +1 -1
  301. package/dist/verify/verification-deps-default.js +14 -2
  302. package/dist/verify/verification-deps-default.js.map +1 -1
  303. package/dist/verify/verification-evidence-unavailable-feedback.d.ts.map +1 -1
  304. package/dist/verify/verification-evidence-unavailable-feedback.js +8 -2
  305. package/dist/verify/verification-evidence-unavailable-feedback.js.map +1 -1
  306. package/dist/verify/verification-feedback.d.ts +62 -14
  307. package/dist/verify/verification-feedback.d.ts.map +1 -1
  308. package/dist/verify/verification-feedback.js +101 -28
  309. package/dist/verify/verification-feedback.js.map +1 -1
  310. package/dist/verify/verify-gate-stall.d.ts +116 -0
  311. package/dist/verify/verify-gate-stall.d.ts.map +1 -0
  312. package/dist/verify/verify-gate-stall.js +169 -0
  313. package/dist/verify/verify-gate-stall.js.map +1 -0
  314. package/dist/verify/verify-restatement.d.ts.map +1 -1
  315. package/dist/verify/verify-restatement.js +2 -1
  316. package/dist/verify/verify-restatement.js.map +1 -1
  317. package/package.json +3 -3
package/build-info.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "commitSha": "4dbe9ac08",
3
- "builtAt": "2026-09-12T22:47:37.560Z",
2
+ "commitSha": "21c04539d",
3
+ "builtAt": "2026-09-13T21:01:40.572Z",
4
4
  "expectedMigrations": [
5
5
  "20250829113330_create_org_nodes_table.sql",
6
6
  "20250829113402_fix_function_security_search_path.sql",
@@ -785,6 +785,7 @@
785
785
  "20260909203246_review_complete_stamps_reviewed_through_sha.sql",
786
786
  "20260911015223_add_created_by_session_id_to_issues.sql",
787
787
  "20260911215819_org_default_pass_models.sql",
788
- "20260911232237_org_default_pass_models_setter.sql"
788
+ "20260911232237_org_default_pass_models_setter.sql",
789
+ "20260913005752_agent_escalations_resolution_class_floor.sql"
789
790
  ]
790
791
  }
@@ -2,6 +2,6 @@ export { type AgentBackend, type AgentSpawnContext, type AgentResumeContext, typ
2
2
  export { DEFAULT_BACKEND_ID, registerBackend, getBackend, hasBackend, listBackends, _resetRegistry, } from './registry.js';
3
3
  export { claudeBackend } from './claude/claude-backend.js';
4
4
  export { codexBackend } from './codex/codex-backend.js';
5
- export { resolveBackend, type EnginePass, type EngineOverride, type EngineSource, type ResolvedEngine, type ResolveBackendArgs, } from './resolve.js';
5
+ export { resolveBackend, formatEngineSource, formatEngineLog, type EnginePass, type EngineOverride, type EngineSource, type ResolvedEngine, type ResolveBackendArgs, } from './resolve.js';
6
6
  export { probeAllBackends, _resetAvailabilityCache, type BackendAvailabilityReport, } from './availability.js';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":"AA8BA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,EACL,cAAc,EACd,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":"AA8BA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC"}
@@ -27,6 +27,6 @@ registerBackend(codexBackend);
27
27
  export { DEFAULT_BACKEND_ID, registerBackend, getBackend, hasBackend, listBackends, _resetRegistry, } from './registry.js';
28
28
  export { claudeBackend } from './claude/claude-backend.js';
29
29
  export { codexBackend } from './codex/codex-backend.js';
30
- export { resolveBackend, } from './resolve.js';
30
+ export { resolveBackend, formatEngineSource, formatEngineLog, } from './resolve.js';
31
31
  export { probeAllBackends, _resetAvailabilityCache, } from './availability.js';
32
32
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,iEAAiE;AACjE,+DAA+D;AAC/D,0DAA0D;AAC1D,uEAAuE;AACvE,+DAA+D;AAC/D,6DAA6D;AAC7D,+DAA+D;AAC/D,4DAA4D;AAC5D,0DAA0D;AAC1D,oEAAoE;AACpE,gBAAgB;AAEhB;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,6EAA6E;AAC7E,8EAA8E;AAC9E,6DAA6D;AAC7D,eAAe,CAAC,aAAa,CAAC,CAAC;AAC/B,eAAe,CAAC,YAAY,CAAC,CAAC;AAS9B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,EACL,cAAc,GAMf,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,GAExB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,iEAAiE;AACjE,+DAA+D;AAC/D,0DAA0D;AAC1D,uEAAuE;AACvE,+DAA+D;AAC/D,6DAA6D;AAC7D,+DAA+D;AAC/D,4DAA4D;AAC5D,0DAA0D;AAC1D,oEAAoE;AACpE,gBAAgB;AAEhB;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,6EAA6E;AAC7E,8EAA8E;AAC9E,6DAA6D;AAC7D,eAAe,CAAC,aAAa,CAAC,CAAC;AAC/B,eAAe,CAAC,YAAY,CAAC,CAAC;AAS9B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,eAAe,GAMhB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,GAExB,MAAM,mBAAmB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { type PassModelSource, type ResolvedPassModel } from '@telora/daemon-core';
1
2
  /** A spawn "pass" -- the coding vs review lineage. */
2
3
  export type EnginePass = 'coding' | 'review';
3
4
  /** Per-focus engine override: an engine id per pass; absent/null = inherit. */
@@ -10,6 +11,10 @@ export type EngineSource = 'focus-override' | 'stage-directive' | 'product-defau
10
11
  export interface ResolvedEngine {
11
12
  engineId: string;
12
13
  source: EngineSource;
14
+ /** Set only when source is 'model-implied': the model-ladder layer that selected it. */
15
+ modelLayer?: PassModelSource;
16
+ /** Set only when source is 'model-implied': the selected model that implied the engine. */
17
+ model?: string;
13
18
  }
14
19
  export interface ResolveBackendArgs {
15
20
  /** Per-focus override map (null/absent = inherit the stage/product/org default). */
@@ -27,18 +32,42 @@ export interface ResolveBackendArgs {
27
32
  /** Org-level default engine id (null/absent = the global default). */
28
33
  orgDefault?: string | null;
29
34
  /**
30
- * The effective per-pass model resolved for this spawn (the five-layer
31
- * precedence result). When NO engine is pinned by any layer (focus / stage /
32
- * product / org), the engine is DERIVED from this model -- so an MCP-set
33
- * gpt-5.6-terra review resolves to codex instead of the Claude default. Null =
34
- * no model, no derivation. An explicitly-pinned engine always wins over it.
35
+ * The pass's resolved model AND the model-ladder layer it came from
36
+ * (`resolvePassModel`'s result). Its layer is compared against the engine
37
+ * ladder's layer by resolvePassEngine. Null/absent = no model selected; the
38
+ * engine ladder alone decides.
35
39
  */
36
- resolvedModel?: string | null;
40
+ resolvedModel?: ResolvedPassModel | null;
41
+ /**
42
+ * The resolved workflow's builtin flag -- ranks a stage-directive engine pin
43
+ * (org-authored above the org default, builtin below it). Unknown = builtin.
44
+ */
45
+ workflowIsBuiltin?: boolean | null;
46
+ /**
47
+ * True when this pass selects a local model (focus local_model_overrides[pass]
48
+ * or the stage directive's localModel). Holds a codex ladder engine against
49
+ * the cloud model axis so local-model routing is unchanged.
50
+ */
51
+ localModelSelected?: boolean;
37
52
  }
38
53
  /**
39
54
  * Resolve the engine id (and its source) for a spawn pass. Pure + deterministic.
40
- * Precedence is single-sourced in `model-resolution.resolveEngineId`; this wraps
41
- * it with the never-spawn-on-an-unregistered-engine guard.
55
+ * Engine-ladder precedence is single-sourced in `model-resolution.resolveEngineId`
56
+ * and the cross-axis rule in daemon-core `resolvePassEngine`; this wraps them
57
+ * with the local-model hold and the never-spawn-on-an-unregistered-engine guard.
42
58
  */
43
59
  export declare function resolveBackend(args: ResolveBackendArgs): ResolvedEngine;
60
+ /**
61
+ * The source label for the spawn engine log line. Names the winning layer: an
62
+ * engine-ladder result reads as its layer (`org-default`); a model-implied engine
63
+ * names the model-ladder layer and the model that selected it
64
+ * (`model-implied from org-default, model "gpt-5.6-sol"`). Pure.
65
+ */
66
+ export declare function formatEngineSource(resolved: ResolvedEngine): string;
67
+ /** The no-invisible-cascading spawn engine log line for a pass. Pure. */
68
+ export declare function formatEngineLog(args: {
69
+ focusName: string;
70
+ enginePass: EnginePass;
71
+ resolvedEngine: ResolvedEngine;
72
+ }): string;
44
73
  //# sourceMappingURL=resolve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/backends/resolve.ts"],"names":[],"mappings":"AAsCA,sDAAsD;AACtD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE7C,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GACpB,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,aAAa,GACb,eAAe,GACf,SAAS,GACT,UAAU,CAAC;AAEf,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,oFAAoF;IACpF,aAAa,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,8BAA8B;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CA+BvE"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/backends/resolve.ts"],"names":[],"mappings":"AA+CA,OAAO,EAAqB,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAMtG,sDAAsD;AACtD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE7C,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GACpB,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,aAAa,GACb,eAAe,GACf,SAAS,GACT,UAAU,CAAC;AAEf,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,wFAAwF;IACxF,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,2FAA2F;IAC3F,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,oFAAoF;IACpF,aAAa,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,8BAA8B;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACzC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CA+BvE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAKnE;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,UAAU,CAAC;IAAC,cAAc,EAAE,cAAc,CAAA;CAAE,GAAG,MAAM,CAG3H"}
@@ -5,63 +5,97 @@
5
5
  * Per-spawn engine resolution -- the single place the daemon decides WHICH
6
6
  * engine a spawn runs on.
7
7
  *
8
- * Selection is one knob (the engine) with a default + one override layer,
9
- * most-specific wins, resolution always logged (the no-invisible-cascading
10
- * principle). Mirrors the centralization of `shouldRunLoop()` in
11
- * focus-engine.ts: one exported pure function, no side effects, easy to test.
8
+ * A pass has two selection axes: the ENGINE ladder and the MODEL ladder. Both
9
+ * are resolved elsewhere and reconciled here, resolution always logged (the
10
+ * no-invisible-cascading principle). Mirrors the centralization of
11
+ * `shouldRunLoop()` in focus-engine.ts: one exported pure function, no side
12
+ * effects, easy to test.
12
13
  *
13
- * Resolution order for a pass (coding | review), most-specific wins:
14
+ * Engine ladder for a pass (coding | review), most-specific wins -- single-sourced
15
+ * in `model-resolution.resolveEngineId`:
14
16
  * 1. per-focus override for that pass (focusOverride[pass], if a truthy id)
15
17
  * 2. workflow stage directive engine (stageOverride, if a truthy id)
16
18
  * 3. per-product default (productDefault, if a truthy id)
17
19
  * 4. org-level default (orgDefault, if a truthy id)
18
20
  * 5. the global default (DEFAULT_BACKEND_ID = 'claude')
19
21
  *
20
- * The Focus > Workflow head mirrors the cloud-model and local-model axes; the
21
- * precedence itself is single-sourced in `model-resolution.resolveEngineId`,
22
- * which this function wraps with the registered-backend guard. The engine axis
23
- * keeps the product -> org -> global tail the other two axes do not have.
22
+ * Cross-axis rule -- single-sourced in `@telora/daemon-core` resolvePassEngine: a
23
+ * selected model whose engine is known wins when its layer is AT LEAST AS
24
+ * SPECIFIC as the engine ladder's winning layer (focus-override >
25
+ * org-authored-stage > org-default > product-default > builtin-stage > default).
26
+ * So an org default gpt-5.6-sol review runs on codex even when the org or
27
+ * product default engine is claude, and a bare engine choice decides only when
28
+ * no model is selected at or above its layer. `isEngineAvailable` here is
29
+ * registration (hasBackend), not the install probe: a model-implied engine whose
30
+ * CLI is missing on this host fails its spawn loudly instead of silently running
31
+ * another model.
32
+ *
33
+ * Local-model hold: a pass that selects a LOCAL model (focus
34
+ * local_model_overrides[pass] / stage localModel) routes through codex. When the
35
+ * engine ladder already resolved codex for it, a cloud model (e.g. a Claude id on
36
+ * the focus) must not flip the engine back to claude and strand the local route,
37
+ * so the model axis is not consulted.
24
38
  *
25
39
  * Guard: a resolved id that is not a registered backend (typo, or an engine the
26
- * host lacks) falls back to the default rather than spawning on a bogus engine.
27
- * With no override and no org default, this returns 'claude' -- behavior parity.
40
+ * host lacks) falls back to the default rather than spawning on a bogus engine;
41
+ * a model-implied engine that is not registered is ignored (the ladder stands).
42
+ * With no override, no org default, and no model, this returns 'claude'.
28
43
  */
29
44
  import { DEFAULT_BACKEND_ID, hasBackend } from './registry.js';
30
45
  import { resolveEngineId } from '../shell/model-resolution.js';
31
- import { modelImpliesEngine } from '@telora/daemon-core';
46
+ import { resolvePassEngine } from '@telora/daemon-core';
32
47
  import { createLogger } from '../shell/log.js';
33
48
  const log = createLogger({ module: 'engine' });
34
49
  /**
35
50
  * Resolve the engine id (and its source) for a spawn pass. Pure + deterministic.
36
- * Precedence is single-sourced in `model-resolution.resolveEngineId`; this wraps
37
- * it with the never-spawn-on-an-unregistered-engine guard.
51
+ * Engine-ladder precedence is single-sourced in `model-resolution.resolveEngineId`
52
+ * and the cross-axis rule in daemon-core `resolvePassEngine`; this wraps them
53
+ * with the local-model hold and the never-spawn-on-an-unregistered-engine guard.
38
54
  */
39
55
  export function resolveBackend(args) {
40
- const { engineId: candidate, source } = resolveEngineId({
56
+ const ladder = resolveEngineId({
41
57
  focusOverrideEngine: args.focusOverride?.[args.pass],
42
58
  stageDirectiveEngine: args.stageOverride,
43
59
  productDefault: args.productDefault,
44
60
  orgDefault: args.orgDefault,
45
61
  globalDefault: DEFAULT_BACKEND_ID,
46
62
  });
47
- // Derive the engine from the resolved model ONLY when no layer pinned an
48
- // engine (source 'default' => globalDefault). A model that implies a
49
- // registered engine wins over the bare global default, so a focus/org/stage
50
- // that selects gpt-5.6-terra with no engine override runs on codex. An
51
- // explicitly-pinned engine (any non-'default' source) is untouched -- this
52
- // preserves engine behavior for an org that set default_coding_engine.
53
- if (source === 'default' && args.resolvedModel) {
54
- const implied = modelImpliesEngine(args.resolvedModel);
55
- if (implied && hasBackend(implied)) {
56
- return { engineId: implied, source: 'model-implied' };
63
+ const localModelHoldsCodex = args.localModelSelected === true && ladder.engineId === 'codex';
64
+ if (args.resolvedModel && !localModelHoldsCodex) {
65
+ const decided = resolvePassEngine({
66
+ model: args.resolvedModel,
67
+ engine: ladder,
68
+ workflowIsBuiltin: args.workflowIsBuiltin,
69
+ isEngineAvailable: hasBackend,
70
+ });
71
+ if (decided.source === 'model-implied') {
72
+ return { engineId: decided.engineId, source: 'model-implied', modelLayer: decided.layer, model: decided.model };
57
73
  }
58
74
  }
59
75
  // Never spawn on an unregistered engine -- fall back to the global default.
60
- if (!hasBackend(candidate) && candidate !== DEFAULT_BACKEND_ID) {
61
- log.warn(`[engine] Resolved engine "${candidate}" (source: ${source}) is not a registered backend; ` +
76
+ if (!hasBackend(ladder.engineId) && ladder.engineId !== DEFAULT_BACKEND_ID) {
77
+ log.warn(`[engine] Resolved engine "${ladder.engineId}" (source: ${ladder.source}) is not a registered backend; ` +
62
78
  `falling back to "${DEFAULT_BACKEND_ID}".`);
63
79
  return { engineId: DEFAULT_BACKEND_ID, source: 'fallback' };
64
80
  }
65
- return { engineId: candidate, source };
81
+ return { engineId: ladder.engineId, source: ladder.source };
82
+ }
83
+ /**
84
+ * The source label for the spawn engine log line. Names the winning layer: an
85
+ * engine-ladder result reads as its layer (`org-default`); a model-implied engine
86
+ * names the model-ladder layer and the model that selected it
87
+ * (`model-implied from org-default, model "gpt-5.6-sol"`). Pure.
88
+ */
89
+ export function formatEngineSource(resolved) {
90
+ if (resolved.source !== 'model-implied')
91
+ return resolved.source;
92
+ const layer = resolved.modelLayer ? ` from ${resolved.modelLayer}` : '';
93
+ const model = resolved.model ? `, model "${resolved.model}"` : '';
94
+ return `model-implied${layer}${model}`;
95
+ }
96
+ /** The no-invisible-cascading spawn engine log line for a pass. Pure. */
97
+ export function formatEngineLog(args) {
98
+ return `[focus-executor] Engine for "${args.focusName}" pass=${args.enginePass}: ` +
99
+ `${args.resolvedEngine.engineId} (source: ${formatEngineSource(args.resolvedEngine)})`;
66
100
  }
67
101
  //# sourceMappingURL=resolve.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/backends/resolve.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,0FAA0F;AAC1F,gBAAgB;AAEhB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AAoD/C;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAwB;IACrD,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,eAAe,CAAC;QACtD,mBAAmB,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;QACpD,oBAAoB,EAAE,IAAI,CAAC,aAAa;QACxC,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,aAAa,EAAE,kBAAkB;KAClC,CAAC,CAAC;IAEH,yEAAyE;IACzE,qEAAqE;IACrE,4EAA4E;IAC5E,uEAAuE;IACvE,2EAA2E;IAC3E,uEAAuE;IACvE,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QAC/C,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACvD,IAAI,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACnC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;QACxD,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,SAAS,KAAK,kBAAkB,EAAE,CAAC;QAC/D,GAAG,CAAC,IAAI,CACN,6BAA6B,SAAS,cAAc,MAAM,iCAAiC;YAC3F,oBAAoB,kBAAkB,IAAI,CAC3C,CAAC;QACF,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC9D,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/backends/resolve.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,5 @@
1
+ import { createEscalation } from '../queries/escalations.js';
2
+ import { NO_HUMAN_ACT, type HumanAct } from '../queries/filing-observation.js';
1
3
  export declare const CD_PUSH_FAILED_KIND = "cd_push_failed";
2
4
  /**
3
5
  * Returns true when a CD push escalation should be filed for `result`.
@@ -15,6 +17,18 @@ export declare function shouldFileCdPushEscalation(pushToRemote: boolean, result
15
17
  success: boolean;
16
18
  error?: string;
17
19
  }): boolean;
20
+ /**
21
+ * Pure: the act only a human can perform for a CD push failure, or NO_HUMAN_ACT.
22
+ *
23
+ * Branch protection, the daemon host's git credential, and the host checkout's
24
+ * remote configuration all live outside the repo and outside any agent session,
25
+ * so each names a specific act. Every other push failure (non-fast-forward, a
26
+ * network blip, an unrecognized error) is retry / rebase work the machine owns:
27
+ * NO_HUMAN_ACT, so the filing chokepoint files it as an observation. Policy is
28
+ * checked before auth because a protection refusal commonly also prints
29
+ * "Permission denied".
30
+ */
31
+ export declare function cdPushHumanAct(pushError: string): HumanAct | typeof NO_HUMAN_ACT;
18
32
  export interface CdPushEscalationParams {
19
33
  organizationId: string;
20
34
  sessionId: string;
@@ -27,5 +41,7 @@ export interface CdPushEscalationParams {
27
41
  * File a `cd_push_failed` escalation. Best-effort — swallow the failure
28
42
  * so the caller's finally/cleanup path isn't disrupted by a logging hiccup.
29
43
  */
30
- export declare function fileCdPushEscalation(params: CdPushEscalationParams): Promise<void>;
44
+ export declare function fileCdPushEscalation(params: CdPushEscalationParams, deps?: {
45
+ createEscalation: typeof createEscalation;
46
+ }): Promise<void>;
31
47
  //# sourceMappingURL=ci-escalation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ci-escalation.d.ts","sourceRoot":"","sources":["../../src/ci/ci-escalation.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,OAAO,EACrB,MAAM,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3C,OAAO,CAET;AAED,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAoCf"}
1
+ {"version":3,"file":"ci-escalation.d.ts","sourceRoot":"","sources":["../../src/ci/ci-escalation.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAM/E,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,OAAO,EACrB,MAAM,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3C,OAAO,CAET;AAaD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,YAAY,CA0BhF;AAED,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,sBAAsB,EAC9B,IAAI,GAAE;IAAE,gBAAgB,EAAE,OAAO,gBAAgB,CAAA;CAAyB,GACzE,OAAO,CAAC,IAAI,CAAC,CAsCf"}
@@ -8,9 +8,14 @@
8
8
  * the push fails for any reason — no remote configured, auth failure,
9
9
  * non-fast-forward, network — the failure must surface as a visible
10
10
  * escalation rather than disappearing into the log.
11
+ *
12
+ * Routing: the filing reaches a human only when the push error names an act no
13
+ * agent session can perform (cdPushHumanAct). Otherwise it is filed as an
14
+ * observation the machine owns (see queries/filing-observation.ts).
11
15
  */
12
16
  import { ESCALATION_REASONS, ESCALATION_DISPOSITIONS } from '@telora/daemon-core';
13
17
  import { createEscalation } from '../queries/escalations.js';
18
+ import { NO_HUMAN_ACT } from '../queries/filing-observation.js';
14
19
  import { createLogger } from '../shell/log.js';
15
20
  const log = createLogger({ module: 'CD' });
16
21
  export const CD_PUSH_FAILED_KIND = 'cd_push_failed';
@@ -29,20 +34,63 @@ export const CD_PUSH_FAILED_KIND = 'cd_push_failed';
29
34
  export function shouldFileCdPushEscalation(pushToRemote, result) {
30
35
  return pushToRemote && !result.success && !!result.error && /push/i.test(result.error);
31
36
  }
37
+ /** The remote refused the push by POLICY (branch protection / a server-side hook). */
38
+ const PUSH_REFUSED_BY_POLICY_RE = /protected branch|branch \S+ is protected|branch protection|GH006|pre-receive hook declined|not allowed to push/i;
39
+ /** The remote refused the push by IDENTITY (the daemon host's credential). */
40
+ const PUSH_REFUSED_BY_AUTH_RE = /permission denied|authentication failed|could not read (?:username|password)|access denied|publickey|invalid credentials|not authorized|\b40[13]\b/i;
41
+ /** There is no remote to push to at all. */
42
+ const PUSH_NO_REMOTE_RE = /no origin remote configured/i;
43
+ /**
44
+ * Pure: the act only a human can perform for a CD push failure, or NO_HUMAN_ACT.
45
+ *
46
+ * Branch protection, the daemon host's git credential, and the host checkout's
47
+ * remote configuration all live outside the repo and outside any agent session,
48
+ * so each names a specific act. Every other push failure (non-fast-forward, a
49
+ * network blip, an unrecognized error) is retry / rebase work the machine owns:
50
+ * NO_HUMAN_ACT, so the filing chokepoint files it as an observation. Policy is
51
+ * checked before auth because a protection refusal commonly also prints
52
+ * "Permission denied".
53
+ */
54
+ export function cdPushHumanAct(pushError) {
55
+ if (PUSH_REFUSED_BY_POLICY_RE.test(pushError)) {
56
+ return {
57
+ kind: 'unreachable_capability',
58
+ act: "Allow the daemon's CD push to main through the remote's branch protection " +
59
+ '(or turn off cd.enabled for this product) -- the remote refused the push by policy.',
60
+ };
61
+ }
62
+ if (PUSH_REFUSED_BY_AUTH_RE.test(pushError)) {
63
+ return {
64
+ kind: 'unreachable_capability',
65
+ act: "Grant the daemon host's git credential push access to main on the remote -- " +
66
+ 'the push was rejected as unauthenticated or unauthorized.',
67
+ };
68
+ }
69
+ if (PUSH_NO_REMOTE_RE.test(pushError)) {
70
+ return {
71
+ kind: 'unreachable_capability',
72
+ act: "Configure an origin remote for this repository's checkout on the daemon host " +
73
+ '(or turn off cd.enabled for this product) -- CD has no remote to push to.',
74
+ };
75
+ }
76
+ return NO_HUMAN_ACT;
77
+ }
32
78
  /**
33
79
  * File a `cd_push_failed` escalation. Best-effort — swallow the failure
34
80
  * so the caller's finally/cleanup path isn't disrupted by a logging hiccup.
35
81
  */
36
- export async function fileCdPushEscalation(params) {
82
+ export async function fileCdPushEscalation(params, deps = { createEscalation }) {
37
83
  const { organizationId, sessionId, focusId, focusName, integrationBranch, pushError } = params;
38
84
  try {
39
- await createEscalation({
85
+ await deps.createEscalation({
40
86
  organizationId,
41
87
  sessionId,
42
88
  issueId: null,
43
89
  reasonType: ESCALATION_REASONS.ERROR_ENCOUNTERED,
44
- // Operator triage: auth / branch protection / network at the remote.
45
90
  disposition: ESCALATION_DISPOSITIONS.NEEDS_OPERATOR,
91
+ // A named act only for a remote-policy / credential / remote-config refusal;
92
+ // any other push failure is machine retry work (observation).
93
+ humanAct: cdPushHumanAct(pushError),
46
94
  anchor: { focusId, branch: integrationBranch },
47
95
  description: `CD push to remote failed after merging ${integrationBranch} into main.\n\n` +
48
96
  `**Focus:** ${focusName}\n` +
@@ -1 +1 @@
1
- {"version":3,"file":"ci-escalation.js","sourceRoot":"","sources":["../../src/ci/ci-escalation.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,0DAA0D;AAC1D,gBAAgB;AAEhB;;;;;;;GAOG;AAEH,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAG3C,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,0BAA0B,CACxC,YAAqB,EACrB,MAA4C;IAE5C,OAAO,YAAY,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzF,CAAC;AAWD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAA8B;IAE9B,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAC/F,IAAI,CAAC;QACH,MAAM,gBAAgB,CAAC;YACrB,cAAc;YACd,SAAS;YACT,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,kBAAkB,CAAC,iBAAiB;YAChD,qEAAqE;YACrE,WAAW,EAAE,uBAAuB,CAAC,cAAc;YACnD,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE;YAC9C,WAAW,EACT,0CAA0C,iBAAiB,iBAAiB;gBAC5E,cAAc,SAAS,IAAI;gBAC3B,iBAAiB,OAAO,IAAI;gBAC5B,cAAc,SAAS,MAAM;gBAC7B,oFAAoF;gBACpF,6DAA6D;YAC/D,YAAY,EAAE,+CAA+C;YAC7D,UAAU,EACR,uEAAuE;gBACvE,0DAA0D;YAC5D,cAAc,EAAE,mBAAmB;YACnC,2EAA2E;YAC3E,QAAQ,EAAE;gBACR,kBAAkB,EAAE,iBAAiB;gBACrC,UAAU,EAAE,SAAS;aACtB;SACF,CAAC,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,6CAA6C,OAAO,MAAM,SAAS,IAAI,CAAC,CAAC;IACpF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,IAAI,CACN,2DAA2D,OAAO,GAAG,EACpE,GAAa,CAAC,OAAO,CACvB,CAAC;IACJ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"ci-escalation.js","sourceRoot":"","sources":["../../src/ci/ci-escalation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Attribution of a red integration run to the focus (and delivery) whose merge
3
+ * INTRODUCED the red tip -- or to nobody, when that cannot be determined.
4
+ *
5
+ * The red run's anchor is what the ci_run_red filing routes to and what the
6
+ * incident reducer groups it under, so a WRONG anchor is worse than none: it
7
+ * rides the fix into an unrelated focus's coding pass and correlates the red
8
+ * with that focus's unrelated symptoms. Hence the rule: attribute only when the
9
+ * tip's introduced work resolves to exactly ONE focus; otherwise return the
10
+ * unattributed anchor and let the caller file branch-only. There is no "any
11
+ * active focus" fallback -- that fallback was a guess.
12
+ *
13
+ * Which commits did the tip introduce? For a non-merge tip, the tip itself (its
14
+ * own `Telora-Delivery` trailer). For a MERGE tip, the commits reachable from the
15
+ * tip and not from its INTEGRATION-SIDE parent. Git records the branch a merge was
16
+ * made ON as the first parent, and integration receives merges in two shapes:
17
+ * - a merge made ON integration (mergeToIntegration's `--no-ff` "Merge agent
18
+ * work", the main->integration sync): ^1 is integration, the work is ^2's side.
19
+ * - a MERGE-BACK made on a focus branch (`git merge origin/integration`) that
20
+ * integration then FAST-FORWARDED to (mergeToIntegration tries `--ff-only`
21
+ * first): ^1 is the FOCUS branch, ^2 is the old integration tip, and the work
22
+ * is ^1's side.
23
+ * The merge-back shape is the common one (38 of 43 merge tips on integration from
24
+ * 2026-08-20 to 2026-09-12), and the former resolver walked `^1...tip` for it --
25
+ * i.e. the OTHER foci's integration commits the focus had just merged back -- so
26
+ * a red run was attributed to whichever focus last merged before the one that
27
+ * turned it red (e.g. tip d58280129 resolved to the migration-gate focus's
28
+ * 094e94351 instead of its own 4cf454a73). The merge-back shape is recognized by
29
+ * git's default merge subject naming the integration branch as the merged-in ref.
30
+ *
31
+ * Best-effort: any git/DB failure degrades to the unattributed anchor, never throws.
32
+ *
33
+ * @module ci-red-attribution
34
+ */
35
+ import type { DaemonConfig } from '../types/index.js';
36
+ /** The anchor a red integration run is filed against. Both null = branch-only. */
37
+ export interface CiRedRunAnchor {
38
+ focusId: string | null;
39
+ /** Set only when the introduced work of the attributed focus is ONE delivery. */
40
+ deliveryId: string | null;
41
+ }
42
+ /** The branch-only anchor: the red run could not be attributed to one focus. */
43
+ export declare const UNATTRIBUTED_RED_RUN: Readonly<CiRedRunAnchor>;
44
+ /** Injected IO for red-run attribution: git reads + the delivery->focus DB lookup. */
45
+ export interface RedRunAttributionDeps {
46
+ /** The commit's raw `Telora-Delivery` trailer value ('' when absent). */
47
+ readTrailer: (repoPath: string, sha: string) => string;
48
+ /** The commit's parent shas, in order (empty on failure). */
49
+ readParents: (repoPath: string, sha: string) => string[];
50
+ /** The commit's subject line ('' on failure). */
51
+ readSubject: (repoPath: string, sha: string) => string;
52
+ /** `git rev-list <range>` (empty on failure). */
53
+ listCommits: (repoPath: string, range: string) => string[];
54
+ /** delivery id -> owning focus id (null when unknown). */
55
+ focusForDelivery: (deliveryId: string) => Promise<string | null>;
56
+ }
57
+ export declare function readCommitParents(repoPath: string, sha: string): string[];
58
+ export declare function readCommitSubject(repoPath: string, sha: string): string;
59
+ /**
60
+ * Whether a merge commit's subject is git's default message for merging the
61
+ * INTEGRATION branch in (`Merge branch 'integration' ...` / `Merge
62
+ * remote-tracking branch 'origin/integration' ...`) -- the merge-back shape,
63
+ * whose SECOND parent is the integration side. PURE.
64
+ */
65
+ export declare function mergedInIntegration(subject: string, integrationBranch: string): boolean;
66
+ /**
67
+ * The integration-side parent of a merge tip: ^2 for a merge-back that integration
68
+ * fast-forwarded to, ^1 otherwise. Null for a non-merge. PURE.
69
+ */
70
+ export declare function integrationSideParent(parents: readonly string[], subject: string, integrationBranch: string): string | null;
71
+ /**
72
+ * Decide the anchor from the deliveries the tip introduced (delivery -> owning
73
+ * focus, null when unknown). Exactly one focus -> that focus, plus the delivery
74
+ * when it is the only one of that focus; zero or several foci -> unattributed.
75
+ * A delivery whose focus is unknown is ignored rather than guessed at. PURE.
76
+ */
77
+ export declare function decideRedRunAnchor(focusByDelivery: ReadonlyMap<string, string | null>): CiRedRunAnchor;
78
+ /**
79
+ * Resolve the anchor for a red run on `sha` (the integration tip). Order: (1) the
80
+ * tip's own trailer; (2) for a merge tip, the deliveries of the commits it
81
+ * introduced relative to its integration-side parent. Unattributable -> the
82
+ * branch-only anchor (never a fallback focus).
83
+ */
84
+ export declare function resolveRedRunAnchor(config: DaemonConfig, sha: string, deps?: RedRunAttributionDeps): Promise<CiRedRunAnchor>;
85
+ //# sourceMappingURL=ci-red-attribution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-red-attribution.d.ts","sourceRoot":"","sources":["../../src/ci/ci-red-attribution.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAQtD,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,iFAAiF;IACjF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,cAAc,CAAsD,CAAC;AAEjH,sFAAsF;AACtF,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACvD,6DAA6D;IAC7D,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACzD,iDAAiD;IACjD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACvD,iDAAiD;IACjD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAC3D,0DAA0D;IAC1D,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAClE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAIzE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGvE;AAsBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAKvF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,EAAE,MAAM,EACf,iBAAiB,EAAE,MAAM,GACxB,MAAM,GAAG,IAAI,CAGf;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,cAAc,CAQtG;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,qBAAmC,GACxC,OAAO,CAAC,cAAc,CAAC,CAwBzB"}
@@ -0,0 +1,108 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/ci/ci-owning-focus-resolve.test.ts
3
+ // PINNED-BY-END
4
+ import { runGitSync } from '../git/exec.js';
5
+ import { getDeliveryContext } from '../queries/deliveries.js';
6
+ import { readDeliveryTrailer, listRangeCommits } from '../resolvers/shared/git-diff.js';
7
+ import { createLogger } from '../shell/log.js';
8
+ const log = createLogger({ module: 'ci-red-attribution' });
9
+ /** The branch-only anchor: the red run could not be attributed to one focus. */
10
+ export const UNATTRIBUTED_RED_RUN = Object.freeze({ focusId: null, deliveryId: null });
11
+ export function readCommitParents(repoPath, sha) {
12
+ const result = runGitSync(['rev-list', '--parents', '-n', '1', `${sha}^{commit}`], repoPath);
13
+ if (!result.success)
14
+ return [];
15
+ return result.output.trim().split(/\s+/).filter((p) => p !== '').slice(1);
16
+ }
17
+ export function readCommitSubject(repoPath, sha) {
18
+ const result = runGitSync(['show', '-s', '--format=%s', `${sha}^{commit}`], repoPath);
19
+ return result.success ? result.output.trim() : '';
20
+ }
21
+ const defaultDeps = {
22
+ readTrailer: readDeliveryTrailer,
23
+ readParents: readCommitParents,
24
+ readSubject: readCommitSubject,
25
+ listCommits: listRangeCommits,
26
+ focusForDelivery: async (deliveryId) => (await getDeliveryContext(deliveryId))?.focus?.id ?? null,
27
+ };
28
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
29
+ /** A trailer value that names a delivery (the `none` sentinel and junk do not). */
30
+ function asDeliveryId(trailer) {
31
+ const value = trailer.trim();
32
+ return UUID_RE.test(value) ? value : null;
33
+ }
34
+ function escapeRegExp(value) {
35
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
36
+ }
37
+ /**
38
+ * Whether a merge commit's subject is git's default message for merging the
39
+ * INTEGRATION branch in (`Merge branch 'integration' ...` / `Merge
40
+ * remote-tracking branch 'origin/integration' ...`) -- the merge-back shape,
41
+ * whose SECOND parent is the integration side. PURE.
42
+ */
43
+ export function mergedInIntegration(subject, integrationBranch) {
44
+ const branch = escapeRegExp(integrationBranch);
45
+ // The ref prefix is a REMOTE name, so it is allowed only in the remote-tracking
46
+ // form; a local `Merge branch 'agent/integration'` is some other branch.
47
+ return new RegExp(`^Merge (?:remote-tracking branch '[^'/]+/${branch}'|branch '${branch}')`).test(subject);
48
+ }
49
+ /**
50
+ * The integration-side parent of a merge tip: ^2 for a merge-back that integration
51
+ * fast-forwarded to, ^1 otherwise. Null for a non-merge. PURE.
52
+ */
53
+ export function integrationSideParent(parents, subject, integrationBranch) {
54
+ if (parents.length < 2)
55
+ return null;
56
+ return (mergedInIntegration(subject, integrationBranch) ? parents[1] : parents[0]) ?? null;
57
+ }
58
+ /**
59
+ * Decide the anchor from the deliveries the tip introduced (delivery -> owning
60
+ * focus, null when unknown). Exactly one focus -> that focus, plus the delivery
61
+ * when it is the only one of that focus; zero or several foci -> unattributed.
62
+ * A delivery whose focus is unknown is ignored rather than guessed at. PURE.
63
+ */
64
+ export function decideRedRunAnchor(focusByDelivery) {
65
+ const foci = new Set();
66
+ for (const focusId of focusByDelivery.values())
67
+ if (focusId)
68
+ foci.add(focusId);
69
+ if (foci.size !== 1)
70
+ return { ...UNATTRIBUTED_RED_RUN };
71
+ const [focusId] = [...foci];
72
+ if (!focusId)
73
+ return { ...UNATTRIBUTED_RED_RUN };
74
+ const deliveries = [...focusByDelivery].filter(([, f]) => f === focusId).map(([d]) => d);
75
+ return { focusId, deliveryId: deliveries.length === 1 ? (deliveries[0] ?? null) : null };
76
+ }
77
+ /**
78
+ * Resolve the anchor for a red run on `sha` (the integration tip). Order: (1) the
79
+ * tip's own trailer; (2) for a merge tip, the deliveries of the commits it
80
+ * introduced relative to its integration-side parent. Unattributable -> the
81
+ * branch-only anchor (never a fallback focus).
82
+ */
83
+ export async function resolveRedRunAnchor(config, sha, deps = defaultDeps) {
84
+ try {
85
+ const own = asDeliveryId(deps.readTrailer(config.repoPath, sha));
86
+ if (own) {
87
+ const focusId = await deps.focusForDelivery(own);
88
+ if (focusId)
89
+ return { focusId, deliveryId: own };
90
+ }
91
+ const base = integrationSideParent(deps.readParents(config.repoPath, sha), deps.readSubject(config.repoPath, sha), config.integrationBranch);
92
+ if (!base)
93
+ return { ...UNATTRIBUTED_RED_RUN };
94
+ const focusByDelivery = new Map();
95
+ for (const commit of deps.listCommits(config.repoPath, `${base}..${sha}`)) {
96
+ const deliveryId = asDeliveryId(deps.readTrailer(config.repoPath, commit));
97
+ if (!deliveryId || focusByDelivery.has(deliveryId))
98
+ continue;
99
+ focusByDelivery.set(deliveryId, await deps.focusForDelivery(deliveryId));
100
+ }
101
+ return decideRedRunAnchor(focusByDelivery);
102
+ }
103
+ catch (err) {
104
+ log.debug(`red-run attribution failed for ${sha.slice(0, 12)}: ${err.message}`);
105
+ return { ...UNATTRIBUTED_RED_RUN };
106
+ }
107
+ }
108
+ //# sourceMappingURL=ci-red-attribution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-red-attribution.js","sourceRoot":"","sources":["../../src/ci/ci-red-attribution.ts"],"names":[],"mappings":"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"}