@telora/daemon 0.23.1 → 0.23.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (298) hide show
  1. package/build-info.json +6 -3
  2. package/dist/completion/review-exit/review-exit-deps.d.ts +2 -1
  3. package/dist/completion/review-exit/review-exit-deps.d.ts.map +1 -1
  4. package/dist/completion/review-exit/review-exit-phase.d.ts.map +1 -1
  5. package/dist/completion/review-exit/review-exit-phase.js +5 -4
  6. package/dist/completion/review-exit/review-exit-phase.js.map +1 -1
  7. package/dist/completion/review-exit-holds/review-exit-verdict-gate.d.ts +2 -1
  8. package/dist/completion/review-exit-holds/review-exit-verdict-gate.d.ts.map +1 -1
  9. package/dist/completion/review-exit-holds/review-exit-verdict-gate.js +8 -5
  10. package/dist/completion/review-exit-holds/review-exit-verdict-gate.js.map +1 -1
  11. package/dist/completion/state-cascade/review-next-step.d.ts.map +1 -1
  12. package/dist/completion/state-cascade/review-next-step.js +15 -8
  13. package/dist/completion/state-cascade/review-next-step.js.map +1 -1
  14. package/dist/completion/team-exit/status-advance-phase.d.ts.map +1 -1
  15. package/dist/completion/team-exit/status-advance-phase.js +6 -6
  16. package/dist/completion/team-exit/status-advance-phase.js.map +1 -1
  17. package/dist/completion/team-liveness/supervision-firing-record.d.ts +6 -0
  18. package/dist/completion/team-liveness/supervision-firing-record.d.ts.map +1 -1
  19. package/dist/completion/team-liveness/supervision-firing-record.js +10 -1
  20. package/dist/completion/team-liveness/supervision-firing-record.js.map +1 -1
  21. package/dist/completion/team-outcome/decide-close-rows.d.ts +9 -0
  22. package/dist/completion/team-outcome/decide-close-rows.d.ts.map +1 -1
  23. package/dist/completion/team-outcome/decide-close-rows.js +18 -4
  24. package/dist/completion/team-outcome/decide-close-rows.js.map +1 -1
  25. package/dist/composition/completion-event-context.js +2 -2
  26. package/dist/composition/completion-event-context.js.map +1 -1
  27. package/dist/composition/daemon-contexts.d.ts.map +1 -1
  28. package/dist/composition/daemon-contexts.js +5 -1
  29. package/dist/composition/daemon-contexts.js.map +1 -1
  30. package/dist/composition/engine-start-context.d.ts +3 -0
  31. package/dist/composition/engine-start-context.d.ts.map +1 -0
  32. package/dist/composition/engine-start-context.js +24 -0
  33. package/dist/composition/engine-start-context.js.map +1 -0
  34. package/dist/composition/execution-spawn-context.d.ts.map +1 -1
  35. package/dist/composition/execution-spawn-context.js +13 -1
  36. package/dist/composition/execution-spawn-context.js.map +1 -1
  37. package/dist/composition/runtime-delivery-advance-context.js +3 -3
  38. package/dist/composition/runtime-delivery-advance-context.js.map +1 -1
  39. package/dist/composition/runtime-heartbeat-context.d.ts.map +1 -1
  40. package/dist/composition/runtime-heartbeat-context.js +3 -0
  41. package/dist/composition/runtime-heartbeat-context.js.map +1 -1
  42. package/dist/composition/runtime-poll-context.d.ts +29 -2
  43. package/dist/composition/runtime-poll-context.d.ts.map +1 -1
  44. package/dist/composition/runtime-poll-context.js +51 -4
  45. package/dist/composition/runtime-poll-context.js.map +1 -1
  46. package/dist/composition/runtime-prd-context.d.ts.map +1 -1
  47. package/dist/composition/runtime-prd-context.js +25 -1
  48. package/dist/composition/runtime-prd-context.js.map +1 -1
  49. package/dist/composition/runtime-spawn-pass-context.js +1 -1
  50. package/dist/composition/runtime-spawn-pass-context.js.map +1 -1
  51. package/dist/composition/spawn-pass-deps.d.ts +1 -1
  52. package/dist/composition/spawn-pass-deps.d.ts.map +1 -1
  53. package/dist/composition/spawn-pass-deps.js +3 -0
  54. package/dist/composition/spawn-pass-deps.js.map +1 -1
  55. package/dist/composition/standing-approval-context.d.ts.map +1 -1
  56. package/dist/composition/standing-approval-context.js +2 -1
  57. package/dist/composition/standing-approval-context.js.map +1 -1
  58. package/dist/composition/state-cascade-context.js +1 -1
  59. package/dist/composition/state-cascade-context.js.map +1 -1
  60. package/dist/composition/tower-pass-context.d.ts +9 -0
  61. package/dist/composition/tower-pass-context.d.ts.map +1 -0
  62. package/dist/composition/tower-pass-context.js +53 -0
  63. package/dist/composition/tower-pass-context.js.map +1 -0
  64. package/dist/composition/verify-rework-context.d.ts +26 -2
  65. package/dist/composition/verify-rework-context.d.ts.map +1 -1
  66. package/dist/composition/verify-rework-context.js +34 -10
  67. package/dist/composition/verify-rework-context.js.map +1 -1
  68. package/dist/config/live-daemon-config.d.ts.map +1 -1
  69. package/dist/config/live-daemon-config.js +1 -19
  70. package/dist/config/live-daemon-config.js.map +1 -1
  71. package/dist/daemon-state/awaiting-slot.d.ts.map +1 -1
  72. package/dist/daemon-state/awaiting-slot.js +2 -1
  73. package/dist/daemon-state/awaiting-slot.js.map +1 -1
  74. package/dist/daemon-state/focus-team-state.js +1 -1
  75. package/dist/daemon-state/intake-control.d.ts.map +1 -1
  76. package/dist/daemon-state/intake-control.js +2 -1
  77. package/dist/daemon-state/intake-control.js.map +1 -1
  78. package/dist/delivery/recorded-concerns.d.ts +19 -2
  79. package/dist/delivery/recorded-concerns.d.ts.map +1 -1
  80. package/dist/delivery/recorded-concerns.js +32 -6
  81. package/dist/delivery/recorded-concerns.js.map +1 -1
  82. package/dist/directive/directive-queue.js +1 -1
  83. package/dist/focus/closed-review-window.d.ts +12 -0
  84. package/dist/focus/closed-review-window.d.ts.map +1 -1
  85. package/dist/focus/closed-review-window.js +18 -0
  86. package/dist/focus/closed-review-window.js.map +1 -1
  87. package/dist/focus/control-state.d.ts +30 -26
  88. package/dist/focus/control-state.d.ts.map +1 -1
  89. package/dist/focus/control-state.js +60 -46
  90. package/dist/focus/control-state.js.map +1 -1
  91. package/dist/focus/daemon-contexts.d.ts +23 -0
  92. package/dist/focus/daemon-contexts.d.ts.map +1 -1
  93. package/dist/focus/daemon-contexts.js +1 -1
  94. package/dist/focus/daemon-contexts.js.map +1 -1
  95. package/dist/focus/engine-ready-pass.d.ts +11 -0
  96. package/dist/focus/engine-ready-pass.d.ts.map +1 -0
  97. package/dist/focus/engine-ready-pass.js +25 -0
  98. package/dist/focus/engine-ready-pass.js.map +1 -0
  99. package/dist/focus/failed-head-wedge.d.ts +95 -0
  100. package/dist/focus/failed-head-wedge.d.ts.map +1 -0
  101. package/dist/focus/failed-head-wedge.js +85 -0
  102. package/dist/focus/failed-head-wedge.js.map +1 -0
  103. package/dist/focus/focus-engine.d.ts.map +1 -1
  104. package/dist/focus/focus-engine.js +20 -20
  105. package/dist/focus/focus-engine.js.map +1 -1
  106. package/dist/focus/focus-loop-registry.d.ts.map +1 -1
  107. package/dist/focus/focus-loop-registry.js +2 -1
  108. package/dist/focus/focus-loop-registry.js.map +1 -1
  109. package/dist/focus/focus-poller-auto-advance.d.ts.map +1 -1
  110. package/dist/focus/focus-poller-auto-advance.js +4 -4
  111. package/dist/focus/focus-poller-auto-advance.js.map +1 -1
  112. package/dist/focus/focus-poller-spawn.d.ts +42 -49
  113. package/dist/focus/focus-poller-spawn.d.ts.map +1 -1
  114. package/dist/focus/focus-poller-spawn.js +129 -83
  115. package/dist/focus/focus-poller-spawn.js.map +1 -1
  116. package/dist/focus/focus-poller-state.d.ts.map +1 -1
  117. package/dist/focus/focus-poller-state.js +1 -19
  118. package/dist/focus/focus-poller-state.js.map +1 -1
  119. package/dist/focus/focus-poller.d.ts.map +1 -1
  120. package/dist/focus/focus-poller.js +2 -1
  121. package/dist/focus/focus-poller.js.map +1 -1
  122. package/dist/focus/focus-respawn-guard.d.ts +7 -0
  123. package/dist/focus/focus-respawn-guard.d.ts.map +1 -1
  124. package/dist/focus/focus-respawn-guard.js +20 -1
  125. package/dist/focus/focus-respawn-guard.js.map +1 -1
  126. package/dist/focus/focus-spawn-decision.d.ts +11 -3
  127. package/dist/focus/focus-spawn-decision.d.ts.map +1 -1
  128. package/dist/focus/focus-spawn-decision.js +3 -1
  129. package/dist/focus/focus-spawn-decision.js.map +1 -1
  130. package/dist/focus/heartbeat-metadata.d.ts +3 -0
  131. package/dist/focus/heartbeat-metadata.d.ts.map +1 -1
  132. package/dist/focus/heartbeat-metadata.js +6 -1
  133. package/dist/focus/heartbeat-metadata.js.map +1 -1
  134. package/dist/focus/heartbeat.d.ts.map +1 -1
  135. package/dist/focus/heartbeat.js +2 -0
  136. package/dist/focus/heartbeat.js.map +1 -1
  137. package/dist/focus/pending-directive-gates.d.ts +47 -0
  138. package/dist/focus/pending-directive-gates.d.ts.map +1 -0
  139. package/dist/focus/pending-directive-gates.js +76 -0
  140. package/dist/focus/pending-directive-gates.js.map +1 -0
  141. package/dist/focus/pending-review-approved-head.d.ts +2 -1
  142. package/dist/focus/pending-review-approved-head.d.ts.map +1 -1
  143. package/dist/focus/pending-review-approved-head.js +3 -4
  144. package/dist/focus/pending-review-approved-head.js.map +1 -1
  145. package/dist/focus/pending-spawn-pass-deps.d.ts +5 -0
  146. package/dist/focus/pending-spawn-pass-deps.d.ts.map +1 -1
  147. package/dist/focus/pending-spawn-pass-deps.js +4 -1
  148. package/dist/focus/pending-spawn-pass-deps.js.map +1 -1
  149. package/dist/focus/pending-spawn-verify-hold.d.ts +13 -0
  150. package/dist/focus/pending-spawn-verify-hold.d.ts.map +1 -0
  151. package/dist/focus/pending-spawn-verify-hold.js +30 -0
  152. package/dist/focus/pending-spawn-verify-hold.js.map +1 -0
  153. package/dist/focus/ready-focus-spawn-action.d.ts +15 -2
  154. package/dist/focus/ready-focus-spawn-action.d.ts.map +1 -1
  155. package/dist/focus/ready-focus-spawn-action.js +25 -8
  156. package/dist/focus/ready-focus-spawn-action.js.map +1 -1
  157. package/dist/focus/spawn-demand-probe.d.ts +45 -0
  158. package/dist/focus/spawn-demand-probe.d.ts.map +1 -0
  159. package/dist/focus/spawn-demand-probe.js +74 -0
  160. package/dist/focus/spawn-demand-probe.js.map +1 -0
  161. package/dist/focus/stranded-review-reroute.d.ts.map +1 -1
  162. package/dist/focus/stranded-review-reroute.js +5 -3
  163. package/dist/focus/stranded-review-reroute.js.map +1 -1
  164. package/dist/focus/verify-routing-passes.d.ts +8 -3
  165. package/dist/focus/verify-routing-passes.d.ts.map +1 -1
  166. package/dist/focus/verify-routing-passes.js +1 -0
  167. package/dist/focus/verify-routing-passes.js.map +1 -1
  168. package/dist/git/focus-branch-containment.d.ts +39 -0
  169. package/dist/git/focus-branch-containment.d.ts.map +1 -0
  170. package/dist/git/focus-branch-containment.js +47 -0
  171. package/dist/git/focus-branch-containment.js.map +1 -0
  172. package/dist/git/types.d.ts.map +1 -1
  173. package/dist/git/types.js +1 -0
  174. package/dist/git/types.js.map +1 -1
  175. package/dist/poll-trigger.js +1 -1
  176. package/dist/poll-trigger.js.map +1 -1
  177. package/dist/prd/arm-readiness.d.ts.map +1 -1
  178. package/dist/prd/arm-readiness.js +2 -1
  179. package/dist/prd/arm-readiness.js.map +1 -1
  180. package/dist/prd/controller-shared.d.ts +32 -8
  181. package/dist/prd/controller-shared.d.ts.map +1 -1
  182. package/dist/prd/controller-shared.js +34 -18
  183. package/dist/prd/controller-shared.js.map +1 -1
  184. package/dist/prd/controller.d.ts.map +1 -1
  185. package/dist/prd/controller.js +3 -8
  186. package/dist/prd/controller.js.map +1 -1
  187. package/dist/prd/done-sweep.d.ts.map +1 -1
  188. package/dist/prd/done-sweep.js +2 -7
  189. package/dist/prd/done-sweep.js.map +1 -1
  190. package/dist/prd/frontier-arming.d.ts +13 -1
  191. package/dist/prd/frontier-arming.d.ts.map +1 -1
  192. package/dist/prd/frontier-arming.js +50 -2
  193. package/dist/prd/frontier-arming.js.map +1 -1
  194. package/dist/prd/index.d.ts.map +1 -1
  195. package/dist/prd/index.js +2 -1
  196. package/dist/prd/index.js.map +1 -1
  197. package/dist/queries/api-client/shared.d.ts +1 -0
  198. package/dist/queries/api-client/shared.d.ts.map +1 -1
  199. package/dist/queries/api-client/shared.js +16 -1
  200. package/dist/queries/api-client/shared.js.map +1 -1
  201. package/dist/queries/deliveries/issues.d.ts.map +1 -1
  202. package/dist/queries/deliveries/issues.js +3 -0
  203. package/dist/queries/deliveries/issues.js.map +1 -1
  204. package/dist/queries/focuses/focus-discovery.d.ts +15 -0
  205. package/dist/queries/focuses/focus-discovery.d.ts.map +1 -1
  206. package/dist/queries/focuses/focus-discovery.js +10 -1
  207. package/dist/queries/focuses/focus-discovery.js.map +1 -1
  208. package/dist/queries/prd/prd.d.ts.map +1 -1
  209. package/dist/queries/prd/prd.js +2 -1
  210. package/dist/queries/prd/prd.js.map +1 -1
  211. package/dist/queries/tower/tower-state.d.ts +56 -0
  212. package/dist/queries/tower/tower-state.d.ts.map +1 -0
  213. package/dist/queries/tower/tower-state.js +76 -0
  214. package/dist/queries/tower/tower-state.js.map +1 -0
  215. package/dist/queries/wire-schemas/schemas.d.ts +12 -0
  216. package/dist/queries/wire-schemas/schemas.d.ts.map +1 -1
  217. package/dist/queries/wire-schemas/schemas.js +11 -0
  218. package/dist/queries/wire-schemas/schemas.js.map +1 -1
  219. package/dist/spawn/lead-continuation.d.ts +9 -1
  220. package/dist/spawn/lead-continuation.d.ts.map +1 -1
  221. package/dist/spawn/lead-continuation.js +37 -11
  222. package/dist/spawn/lead-continuation.js.map +1 -1
  223. package/dist/spawn/spawn-decision.d.ts.map +1 -1
  224. package/dist/spawn/spawn-decision.js +4 -1
  225. package/dist/spawn/spawn-decision.js.map +1 -1
  226. package/dist/supervision/spawn-cooldown.js +1 -1
  227. package/dist/supervision/stop-rows-team-session.d.ts.map +1 -1
  228. package/dist/supervision/stop-rows-team-session.js +11 -10
  229. package/dist/supervision/stop-rows-team-session.js.map +1 -1
  230. package/dist/supervision/team-work.d.ts.map +1 -1
  231. package/dist/supervision/team-work.js +3 -1
  232. package/dist/supervision/team-work.js.map +1 -1
  233. package/dist/testing/fixtures/api-client.js +1 -1
  234. package/dist/testing/fixtures/approved-head-inert-deps.js +1 -1
  235. package/dist/testing/fixtures/approved-head-inert-deps.js.map +1 -1
  236. package/dist/testing/fixtures/index.js +1 -1
  237. package/dist/testing/fixtures/pending-spawn-world.d.ts.map +1 -1
  238. package/dist/testing/fixtures/pending-spawn-world.js +3 -1
  239. package/dist/testing/fixtures/pending-spawn-world.js.map +1 -1
  240. package/dist/testing/fixtures/review-exit-deps.js +1 -1
  241. package/dist/testing/fixtures/review-exit-deps.js.map +1 -1
  242. package/dist/testing/fixtures/verification-feedback-deps.d.ts.map +1 -1
  243. package/dist/testing/fixtures/verification-feedback-deps.js +2 -0
  244. package/dist/testing/fixtures/verification-feedback-deps.js.map +1 -1
  245. package/dist/tower/focus-landed.d.ts +4 -0
  246. package/dist/tower/focus-landed.d.ts.map +1 -0
  247. package/dist/tower/focus-landed.js +45 -0
  248. package/dist/tower/focus-landed.js.map +1 -0
  249. package/dist/tower/tower-admission.d.ts +91 -0
  250. package/dist/tower/tower-admission.d.ts.map +1 -0
  251. package/dist/tower/tower-admission.js +137 -0
  252. package/dist/tower/tower-admission.js.map +1 -0
  253. package/dist/tower/tower-graph.d.ts +23 -0
  254. package/dist/tower/tower-graph.d.ts.map +1 -0
  255. package/dist/tower/tower-graph.js +53 -0
  256. package/dist/tower/tower-graph.js.map +1 -0
  257. package/dist/tower/tower-heartbeat-status.d.ts +28 -0
  258. package/dist/tower/tower-heartbeat-status.d.ts.map +1 -0
  259. package/dist/tower/tower-heartbeat-status.js +11 -0
  260. package/dist/tower/tower-heartbeat-status.js.map +1 -0
  261. package/dist/tower/tower-mode.d.ts +9 -0
  262. package/dist/tower/tower-mode.d.ts.map +1 -0
  263. package/dist/tower/tower-mode.js +16 -0
  264. package/dist/tower/tower-mode.js.map +1 -0
  265. package/dist/tower/tower-pass-standing.d.ts +8 -0
  266. package/dist/tower/tower-pass-standing.d.ts.map +1 -0
  267. package/dist/tower/tower-pass-standing.js +50 -0
  268. package/dist/tower/tower-pass-standing.js.map +1 -0
  269. package/dist/tower/tower-pass.d.ts +55 -0
  270. package/dist/tower/tower-pass.d.ts.map +1 -0
  271. package/dist/tower/tower-pass.js +59 -0
  272. package/dist/tower/tower-pass.js.map +1 -0
  273. package/dist/tower/tower-production-pass.d.ts +3 -0
  274. package/dist/tower/tower-production-pass.d.ts.map +1 -0
  275. package/dist/tower/tower-production-pass.js +24 -0
  276. package/dist/tower/tower-production-pass.js.map +1 -0
  277. package/dist/tower/tower-spawn-intent.d.ts +72 -0
  278. package/dist/tower/tower-spawn-intent.d.ts.map +1 -0
  279. package/dist/tower/tower-spawn-intent.js +138 -0
  280. package/dist/tower/tower-spawn-intent.js.map +1 -0
  281. package/dist/tower/tower-verdicts.d.ts +81 -0
  282. package/dist/tower/tower-verdicts.d.ts.map +1 -0
  283. package/dist/tower/tower-verdicts.js +139 -0
  284. package/dist/tower/tower-verdicts.js.map +1 -0
  285. package/dist/types/index.js +1 -1
  286. package/dist/types/issue-status.d.ts +14 -0
  287. package/dist/types/issue-status.d.ts.map +1 -1
  288. package/dist/types/issue-status.js +14 -0
  289. package/dist/types/issue-status.js.map +1 -1
  290. package/dist/verify/gate-verdicts/commit-verdict-record.js +1 -1
  291. package/dist/verify/outcome-routing/verification-rework-issue.d.ts +44 -5
  292. package/dist/verify/outcome-routing/verification-rework-issue.d.ts.map +1 -1
  293. package/dist/verify/outcome-routing/verification-rework-issue.js +85 -27
  294. package/dist/verify/outcome-routing/verification-rework-issue.js.map +1 -1
  295. package/dist/worktree/focus-worktree-state.d.ts.map +1 -1
  296. package/dist/worktree/focus-worktree-state.js +1 -0
  297. package/dist/worktree/focus-worktree-state.js.map +1 -1
  298. package/package.json +3 -3
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Spawn admission through the tower: what the spawn passes ask before they
3
+ * start a coding or review team for an armed focus.
4
+ *
5
+ * THE RULE (docs/runbook-tower.md):
6
+ * off -- the tower is not consulted; spawning is exactly as before it.
7
+ * observe -- the verdict is read and never obeyed; a spawn decision enforce
8
+ * would have made differently is LOGGED (focus, verdict, the
9
+ * decision taken), so an operator judges the verdicts on real
10
+ * foci before enforcing.
11
+ * enforce -- a holding verdict that is FRESH holds the spawn.
12
+ *
13
+ * FAIL OPEN. Only an available, holding verdict ever holds, and only while
14
+ * this daemon's tower pass for the focus's product is standing
15
+ * (tower-pass-standing.ts): a pass that failed, or has not yet succeeded,
16
+ * leaves every verdict of that product unobeyed at once. A verdict that is
17
+ * missing, malformed, older than the bounded age or written under an earlier
18
+ * arm is no verdict (daemon-core readTowerVerdict). Either way the focus
19
+ * spawns as it would with the tower off, and the daemon says so. Admission fails open while
20
+ * the merge gates fail closed because the failures are not symmetric: a wrongly
21
+ * blocked merge is visible (a held delivery, a red gate), a wrongly held spawn
22
+ * is SILENT (a focus that simply never runs).
23
+ *
24
+ * The decision is pure (decideTowerAdmission); noteTowerAdmission logs it and
25
+ * records the focus's spawn intent once the spawn gate's final action is known.
26
+ */
27
+ import { type TowerMode, type TowerVerdictReading } from '@telora/daemon-core';
28
+ import { type SpawnIntentSource } from './tower-spawn-intent.js';
29
+ import type { TowerPassStanding } from './tower-pass-standing.js';
30
+ /** The focus row fields admission reads (ready and active focus rows both carry them). */
31
+ export interface TowerAdmissionFocus {
32
+ focus_id: string;
33
+ focus_name: string;
34
+ product_id?: string | null;
35
+ agent_assigned_at?: string | null;
36
+ tower_verdict?: unknown;
37
+ }
38
+ export interface TowerAdmission {
39
+ mode: TowerMode;
40
+ /** The spawn is held (enforce + an available holding verdict). */
41
+ holds: boolean;
42
+ /** What enforce would do with this verdict: hold. */
43
+ enforceWouldHold: boolean;
44
+ /** The verdict as read; null in off mode (the tower is not consulted). */
45
+ reading: TowerVerdictReading | null;
46
+ /** The product's pass is not standing: every verdict is unobeyed (fail open). Null when it stands. */
47
+ passNotStanding: Exclude<TowerPassStanding, 'ok'> | null;
48
+ }
49
+ /** What admission reads of the tower each pass (the spawn passes' deps; composition/spawn-pass-deps.ts). */
50
+ export interface TowerAdmissionDeps {
51
+ /** TELORA_TOWER_MODE. */
52
+ mode: () => TowerMode;
53
+ /** This daemon's pass standing for a product (tower-pass-standing.ts). */
54
+ passStanding: (productId: string | null | undefined) => TowerPassStanding;
55
+ }
56
+ export declare function decideTowerAdmission(focus: TowerAdmissionFocus, tower: TowerAdmissionDeps, nowMs: number): TowerAdmission;
57
+ /** The outcome the spawn gate reached for a focus, after the tower was consulted. */
58
+ export type TowerGateOutcome = 'spawn' | 'tower_hold' | 'capacity' | 'not_wanting';
59
+ /** The line a decision produces, or null when it is not worth a line. Exported for tests. */
60
+ export declare function towerAdmissionMessage(focus: TowerAdmissionFocus, admission: TowerAdmission, outcome: TowerGateOutcome): string | null;
61
+ /**
62
+ * Log the admission (once per distinct line) and record the focus's spawn
63
+ * intent for the tower's next pass. Never throws: admission bookkeeping must
64
+ * not cost a spawn.
65
+ */
66
+ export declare function noteTowerAdmission(focus: TowerAdmissionFocus, admission: TowerAdmission, outcome: TowerGateOutcome, nowMs?: number, source?: SpawnIntentSource): void;
67
+ /**
68
+ * The order the spawn pass offers ready foci to the host governor: in enforce,
69
+ * focus rank order (lowest priority_rank first, stable); otherwise the order the
70
+ * API returned them, exactly as before the tower.
71
+ */
72
+ export declare function orderForTower<T extends {
73
+ priority_rank?: number | null;
74
+ }>(foci: readonly T[], mode: TowerMode): T[];
75
+ /**
76
+ * A spawn pass's offer: opens the pass's demand record over every focus it
77
+ * offers (tower-spawn-intent.ts beginSpawnIntentPass -- a focus some earlier
78
+ * gate stops is committed as not wanting) and returns them in orderForTower's
79
+ * order. The pass's runner commits it (focus/focus-poller-spawn.ts).
80
+ */
81
+ export declare function offerForTower<T extends {
82
+ priority_rank?: number | null;
83
+ }>(source: SpawnIntentSource, foci: readonly T[], idOf: (f: T) => string, mode: TowerMode): T[];
84
+ /**
85
+ * The pending-spawn pass's tower check (a queued directive -- e.g. a review --
86
+ * on an armed focus): consulted after the cooldown and before the host cap,
87
+ * exactly where the ready pass consults it. True = held; the directive stays
88
+ * queued.
89
+ */
90
+ export declare function pendingSpawnHeldByTower(focus: TowerAdmissionFocus, atHostCapacity: boolean, tower: TowerAdmissionDeps, nowMs: number): boolean;
91
+ //# sourceMappingURL=tower-admission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-admission.d.ts","sourceRoot":"","sources":["../../src/tower/tower-admission.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAA2C,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC1G,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAIlE,0FAA0F;AAC1F,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,kEAAkE;IAClE,KAAK,EAAE,OAAO,CAAC;IACf,qDAAqD;IACrD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,0EAA0E;IAC1E,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACpC,sGAAsG;IACtG,eAAe,EAAE,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC;CAC1D;AAED,4GAA4G;AAC5G,MAAM,WAAW,kBAAkB;IACjC,yBAAyB;IACzB,IAAI,EAAE,MAAM,SAAS,CAAC;IACtB,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,iBAAiB,CAAC;CAC3E;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,CAQzH;AAED,qFAAqF;AACrF,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,YAAY,GAAG,UAAU,GAAG,aAAa,CAAC;AAYnF,6FAA6F;AAC7F,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,mBAAmB,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAsBrI;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,mBAAmB,EAC1B,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,gBAAgB,EACzB,KAAK,GAAE,MAAmB,EAC1B,MAAM,GAAE,iBAA2B,GAClC,IAAI,CAWN;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,CAAC,EAAE,CAInH;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACvE,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,SAAS,CAAC,EAAE,EAClB,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,EACtB,IAAI,EAAE,SAAS,GACd,CAAC,EAAE,CAGL;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,mBAAmB,EAC1B,cAAc,EAAE,OAAO,EACvB,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,MAAM,GACZ,OAAO,CAIT"}
@@ -0,0 +1,137 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/tower/tower-pass.test.ts
3
+ // PINNED-BY-END
4
+ /**
5
+ * Spawn admission through the tower: what the spawn passes ask before they
6
+ * start a coding or review team for an armed focus.
7
+ *
8
+ * THE RULE (docs/runbook-tower.md):
9
+ * off -- the tower is not consulted; spawning is exactly as before it.
10
+ * observe -- the verdict is read and never obeyed; a spawn decision enforce
11
+ * would have made differently is LOGGED (focus, verdict, the
12
+ * decision taken), so an operator judges the verdicts on real
13
+ * foci before enforcing.
14
+ * enforce -- a holding verdict that is FRESH holds the spawn.
15
+ *
16
+ * FAIL OPEN. Only an available, holding verdict ever holds, and only while
17
+ * this daemon's tower pass for the focus's product is standing
18
+ * (tower-pass-standing.ts): a pass that failed, or has not yet succeeded,
19
+ * leaves every verdict of that product unobeyed at once. A verdict that is
20
+ * missing, malformed, older than the bounded age or written under an earlier
21
+ * arm is no verdict (daemon-core readTowerVerdict). Either way the focus
22
+ * spawns as it would with the tower off, and the daemon says so. Admission fails open while
23
+ * the merge gates fail closed because the failures are not symmetric: a wrongly
24
+ * blocked merge is visible (a held delivery, a red gate), a wrongly held spawn
25
+ * is SILENT (a focus that simply never runs).
26
+ *
27
+ * The decision is pure (decideTowerAdmission); noteTowerAdmission logs it and
28
+ * records the focus's spawn intent once the spawn gate's final action is known.
29
+ */
30
+ import { describeTowerHold, describeTowerUnavailable, readTowerVerdict, towerReadingHolds, } from '@telora/daemon-core';
31
+ import { createLogger } from '../log/daemon-log.js';
32
+ import { beginSpawnIntentPass, recordSpawnIntent } from './tower-spawn-intent.js';
33
+ const log = createLogger({ module: 'tower' });
34
+ export function decideTowerAdmission(focus, tower, nowMs) {
35
+ const mode = tower.mode();
36
+ if (mode === 'off')
37
+ return { mode, holds: false, enforceWouldHold: false, reading: null, passNotStanding: null };
38
+ const reading = readTowerVerdict(focus.tower_verdict ?? null, { armedAt: focus.agent_assigned_at ?? null }, nowMs);
39
+ const standing = tower.passStanding(focus.product_id);
40
+ if (standing !== 'ok')
41
+ return { mode, holds: false, enforceWouldHold: false, reading, passNotStanding: standing };
42
+ const enforceWouldHold = towerReadingHolds(reading);
43
+ return { mode, holds: mode === 'enforce' && enforceWouldHold, enforceWouldHold, reading, passNotStanding: null };
44
+ }
45
+ /** One log line per focus per distinct message (a held focus is not re-logged every poll). */
46
+ const lastLogged = new Map();
47
+ function logOnce(focusId, message, level) {
48
+ if (lastLogged.get(focusId) === message)
49
+ return;
50
+ lastLogged.set(focusId, message);
51
+ if (level === 'warn')
52
+ log.warn(message);
53
+ else
54
+ log.info(message);
55
+ }
56
+ /** The line a decision produces, or null when it is not worth a line. Exported for tests. */
57
+ export function towerAdmissionMessage(focus, admission, outcome) {
58
+ const { reading } = admission;
59
+ if (!reading || outcome === 'not_wanting')
60
+ return null;
61
+ const name = `"${focus.focus_name}" (${focus.focus_id.slice(0, 8)})`;
62
+ if (admission.passNotStanding) {
63
+ if (admission.mode !== 'enforce')
64
+ return null;
65
+ const why = admission.passNotStanding === 'failed' ? 'its product\'s tower pass failed' : 'its product\'s tower pass has not succeeded yet';
66
+ return `[tower] ${name}: ${why} -- failing open, spawning as with the tower off`;
67
+ }
68
+ if (reading.status === 'unavailable') {
69
+ if (admission.mode !== 'enforce')
70
+ return null;
71
+ return `[tower] ${name}: ${describeTowerUnavailable(reading.why)} -- failing open, spawning as with the tower off`;
72
+ }
73
+ const v = reading.verdict;
74
+ if (v.state !== 'holding')
75
+ return null;
76
+ const why = `${describeTowerHold(v)} since ${v.since}`;
77
+ if (outcome === 'tower_hold')
78
+ return `[tower] holding ${name}: ${why}`;
79
+ if (admission.mode === 'observe') {
80
+ const taken = outcome === 'spawn' ? 'spawned' : 'left waiting for a host slot';
81
+ return `[tower] observe: enforce would hold ${name} (${why}); decision taken: ${taken}`;
82
+ }
83
+ return null;
84
+ }
85
+ /**
86
+ * Log the admission (once per distinct line) and record the focus's spawn
87
+ * intent for the tower's next pass. Never throws: admission bookkeeping must
88
+ * not cost a spawn.
89
+ */
90
+ export function noteTowerAdmission(focus, admission, outcome, nowMs = Date.now(), source = 'ready') {
91
+ try {
92
+ // Open demand only: a spawned focus's demand is met (its team holds the slot).
93
+ recordSpawnIntent(focus.focus_id, outcome === 'tower_hold' || outcome === 'capacity', nowMs, source);
94
+ const message = towerAdmissionMessage(focus, admission, outcome);
95
+ const failingOpen = admission.passNotStanding !== null || admission.reading?.status === 'unavailable';
96
+ if (message)
97
+ logOnce(focus.focus_id, message, failingOpen ? 'warn' : 'info');
98
+ else
99
+ lastLogged.delete(focus.focus_id);
100
+ }
101
+ catch {
102
+ // Bookkeeping only.
103
+ }
104
+ }
105
+ /**
106
+ * The order the spawn pass offers ready foci to the host governor: in enforce,
107
+ * focus rank order (lowest priority_rank first, stable); otherwise the order the
108
+ * API returned them, exactly as before the tower.
109
+ */
110
+ export function orderForTower(foci, mode) {
111
+ if (mode !== 'enforce')
112
+ return [...foci];
113
+ const rank = (f) => f.priority_rank ?? Number.MAX_SAFE_INTEGER;
114
+ return [...foci].map((f, i) => ({ f, i })).sort((a, b) => rank(a.f) - rank(b.f) || a.i - b.i).map((x) => x.f);
115
+ }
116
+ /**
117
+ * A spawn pass's offer: opens the pass's demand record over every focus it
118
+ * offers (tower-spawn-intent.ts beginSpawnIntentPass -- a focus some earlier
119
+ * gate stops is committed as not wanting) and returns them in orderForTower's
120
+ * order. The pass's runner commits it (focus/focus-poller-spawn.ts).
121
+ */
122
+ export function offerForTower(source, foci, idOf, mode) {
123
+ beginSpawnIntentPass(source, foci.map(idOf));
124
+ return orderForTower(foci, mode);
125
+ }
126
+ /**
127
+ * The pending-spawn pass's tower check (a queued directive -- e.g. a review --
128
+ * on an armed focus): consulted after the cooldown and before the host cap,
129
+ * exactly where the ready pass consults it. True = held; the directive stays
130
+ * queued.
131
+ */
132
+ export function pendingSpawnHeldByTower(focus, atHostCapacity, tower, nowMs) {
133
+ const admission = decideTowerAdmission(focus, tower, nowMs);
134
+ noteTowerAdmission(focus, admission, admission.holds ? 'tower_hold' : atHostCapacity ? 'capacity' : 'spawn', nowMs, 'pending');
135
+ return admission.holds;
136
+ }
137
+ //# sourceMappingURL=tower-admission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-admission.js","sourceRoot":"","sources":["../../src/tower/tower-admission.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,0DAA0D;AAC1D,gBAAgB;AAEhB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,gBAAgB,EAChB,iBAAiB,GAGlB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAA0B,MAAM,yBAAyB,CAAC;AAG1G,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AA+B9C,MAAM,UAAU,oBAAoB,CAAC,KAA0B,EAAE,KAAyB,EAAE,KAAa;IACvG,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1B,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;IACjH,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,CAAC,aAAa,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,iBAAiB,IAAI,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;IACnH,MAAM,QAAQ,GAAG,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACtD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC;IAClH,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACpD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,SAAS,IAAI,gBAAgB,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;AACnH,CAAC;AAKD,8FAA8F;AAC9F,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;AAE7C,SAAS,OAAO,CAAC,OAAe,EAAE,OAAe,EAAE,KAAsB;IACvE,IAAI,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,OAAO;QAAE,OAAO;IAChD,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,MAAM;QAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;;QACnC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,qBAAqB,CAAC,KAA0B,EAAE,SAAyB,EAAE,OAAyB;IACpH,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;IAC9B,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,aAAa;QAAE,OAAO,IAAI,CAAC;IACvD,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,UAAU,MAAM,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;IACrE,IAAI,SAAS,CAAC,eAAe,EAAE,CAAC;QAC9B,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QAC9C,MAAM,GAAG,GAAG,SAAS,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,iDAAiD,CAAC;QAC5I,OAAO,WAAW,IAAI,KAAK,GAAG,kDAAkD,CAAC;IACnF,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACrC,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QAC9C,OAAO,WAAW,IAAI,KAAK,wBAAwB,CAAC,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC;IACrH,CAAC;IACD,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAC1B,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,GAAG,GAAG,GAAG,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,CAAC;IACvD,IAAI,OAAO,KAAK,YAAY;QAAE,OAAO,mBAAmB,IAAI,KAAK,GAAG,EAAE,CAAC;IACvE,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,8BAA8B,CAAC;QAC/E,OAAO,uCAAuC,IAAI,KAAK,GAAG,sBAAsB,KAAK,EAAE,CAAC;IAC1F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAA0B,EAC1B,SAAyB,EACzB,OAAyB,EACzB,QAAgB,IAAI,CAAC,GAAG,EAAE,EAC1B,SAA4B,OAAO;IAEnC,IAAI,CAAC;QACH,+EAA+E;QAC/E,iBAAiB,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,KAAK,YAAY,IAAI,OAAO,KAAK,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACrG,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QACjE,MAAM,WAAW,GAAG,SAAS,CAAC,eAAe,KAAK,IAAI,IAAI,SAAS,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,CAAC;QACtG,IAAI,OAAO;YAAE,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;;YACxE,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,oBAAoB;IACtB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAA8C,IAAkB,EAAE,IAAe;IAC5G,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,CAAC,CAAI,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,IAAI,MAAM,CAAC,gBAAgB,CAAC;IAClE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAyB,EACzB,IAAkB,EAClB,IAAsB,EACtB,IAAe;IAEf,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,OAAO,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACnC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAA0B,EAC1B,cAAuB,EACvB,KAAyB,EACzB,KAAa;IAEb,MAAM,SAAS,GAAG,oBAAoB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5D,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IAC/H,OAAO,SAAS,CAAC,KAAK,CAAC;AACzB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The tower graph, judged for one product's pass: which armed foci wait on an
3
+ * unlanded depends_on predecessor, which are joined by an excludes edge to
4
+ * which other foci, and which cannot be judged at all.
5
+ *
6
+ * depends_on (from -> to): `to` waits until `from` has LANDED
7
+ * (tower/focus-landed.ts, the definition the PRD frontier shares). A
8
+ * predecessor that has definitely not landed holds the successor with
9
+ * `waits_on`; one whose landed state git could not answer -- with none
10
+ * definitely unlanded -- leaves the successor UNAVAILABLE (no verdict, fail
11
+ * open), never holding.
12
+ * excludes (a <-> b): each side lists the other with the edge's evidence;
13
+ * tower-verdicts.ts holds whichever is reached second while the first runs or
14
+ * is cleared.
15
+ *
16
+ * Git is read only for a predecessor whose deliveries say it could have landed.
17
+ */
18
+ import type { FocusBranchContainment } from '../git/focus-branch-containment.js';
19
+ import type { TowerProductState } from '../queries/tower/tower-state.js';
20
+ import type { TowerComputeInput } from './tower-verdicts.js';
21
+ export type TowerGraphInputs = Pick<TowerComputeInput, 'waitsOn' | 'excludes' | 'unavailableFocusIds'>;
22
+ export declare function judgeTowerGraph(state: Pick<TowerProductState, 'foci' | 'edges' | 'graphFoci'>, containmentOf: (focus: TowerProductState['graphFoci'][number]) => FocusBranchContainment): TowerGraphInputs;
23
+ //# sourceMappingURL=tower-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-graph.d.ts","sourceRoot":"","sources":["../../src/tower/tower-graph.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,OAAO,KAAK,EAA4C,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAEvG,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,UAAU,GAAG,qBAAqB,CAAC,CAAC;AAEvG,wBAAgB,eAAe,CAC7B,KAAK,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC,EAC9D,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,KAAK,sBAAsB,GACvF,gBAAgB,CA0ClB"}
@@ -0,0 +1,53 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/tower/focus-landed.test.ts, packages/daemon/src/tower/tower-pass.test.ts,
3
+ // packages/daemon/src/tower/tower-state-ingestion.test.ts
4
+ // PINNED-BY-END
5
+ import { judgeFocusLanded } from './focus-landed.js';
6
+ export function judgeTowerGraph(state, containmentOf) {
7
+ const info = new Map(state.graphFoci.map((f) => [f.focusId, f]));
8
+ const landedCache = new Map();
9
+ const landed = (focusId) => {
10
+ const cached = landedCache.get(focusId);
11
+ if (cached)
12
+ return cached;
13
+ const f = info.get(focusId);
14
+ let result;
15
+ if (!f)
16
+ result = 'unknown';
17
+ else if (judgeFocusLanded(f.deliveryStatuses, 'contained') !== 'landed')
18
+ result = 'not_landed';
19
+ else
20
+ result = judgeFocusLanded(f.deliveryStatuses, containmentOf(f));
21
+ landedCache.set(focusId, result);
22
+ return result;
23
+ };
24
+ const nameOf = (focusId) => info.get(focusId)?.focusName ?? focusId.slice(0, 8);
25
+ const waitsOn = new Map();
26
+ const excludes = new Map();
27
+ const unavailableFocusIds = new Set();
28
+ const armed = new Set(state.foci.map((f) => f.focusId));
29
+ for (const focusId of armed) {
30
+ const predecessors = state.edges
31
+ .filter((e) => e.kind === 'depends_on' && e.toFocusId === focusId)
32
+ .map((e) => e.fromFocusId)
33
+ .sort((a, b) => nameOf(a).localeCompare(nameOf(b)));
34
+ const unlanded = predecessors.find((p) => landed(p) === 'not_landed');
35
+ if (unlanded)
36
+ waitsOn.set(focusId, { focusId: unlanded, focusName: nameOf(unlanded) });
37
+ else if (predecessors.some((p) => landed(p) === 'unknown'))
38
+ unavailableFocusIds.add(focusId);
39
+ }
40
+ for (const e of state.edges) {
41
+ if (e.kind !== 'excludes')
42
+ continue;
43
+ const add = (self, other) => {
44
+ const list = excludes.get(self) ?? [];
45
+ list.push({ otherFocusId: other, otherFocusName: nameOf(other), evidence: e.evidence });
46
+ excludes.set(self, list);
47
+ };
48
+ add(e.fromFocusId, e.toFocusId);
49
+ add(e.toFocusId, e.fromFocusId);
50
+ }
51
+ return { waitsOn, excludes, unavailableFocusIds };
52
+ }
53
+ //# sourceMappingURL=tower-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-graph.js","sourceRoot":"","sources":["../../src/tower/tower-graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The tower's status per product, published on the daemon heartbeat as
3
+ * `metadata.tower` ({ [productId]: { mode, pass } }; product-scoped,
4
+ * docs/heartbeat-metadata-audit.md) -- what the dashboard needs to say whether
5
+ * the product's tower is running at all:
6
+ *
7
+ * mode -- TELORA_TOWER_MODE on this daemon. `off`: there is no tower; the
8
+ * dashboard shows no tower line.
9
+ * pass -- this daemon's pass standing for the product (tower-pass-standing.ts).
10
+ * Anything but `ok` means admission fails open on every focus of the
11
+ * product, whatever verdicts they carry -- the dashboard says so even
12
+ * when NO focus carries a verdict (the all-missing case after a start
13
+ * or a failed pass).
14
+ *
15
+ * Pure over its reads; bound at the composition root
16
+ * (composition/runtime-heartbeat-context.ts).
17
+ */
18
+ import type { TowerAdmissionDeps } from './tower-admission.js';
19
+ import type { TowerPassStanding } from './tower-pass-standing.js';
20
+ import type { TowerMode } from '@telora/daemon-core';
21
+ export interface TowerHeartbeatStatus {
22
+ mode: TowerMode;
23
+ pass: TowerPassStanding;
24
+ }
25
+ export declare function buildTowerStatusByProduct(products: ReadonlyArray<{
26
+ id: string;
27
+ }>, tower: TowerAdmissionDeps): Record<string, TowerHeartbeatStatus>;
28
+ //# sourceMappingURL=tower-heartbeat-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-heartbeat-status.d.ts","sourceRoot":"","sources":["../../src/tower/tower-heartbeat-status.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,aAAa,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,EACvC,KAAK,EAAE,kBAAkB,GACxB,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAKtC"}
@@ -0,0 +1,11 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/tower/tower-heartbeat-status.test.ts
3
+ // PINNED-BY-END
4
+ export function buildTowerStatusByProduct(products, tower) {
5
+ const mode = tower.mode();
6
+ const out = {};
7
+ for (const p of products)
8
+ out[p.id] = { mode, pass: tower.passStanding(p.id) };
9
+ return out;
10
+ }
11
+ //# sourceMappingURL=tower-heartbeat-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-heartbeat-status.js","sourceRoot":"","sources":["../../src/tower/tower-heartbeat-status.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,sEAAsE;AACtE,gBAAgB;AA6BhB,MAAM,UAAU,yBAAyB,CACvC,QAAuC,EACvC,KAAyB;IAEzB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1B,MAAM,GAAG,GAAyC,EAAE,CAAC;IACrD,KAAK,MAAM,CAAC,IAAI,QAAQ;QAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAC/E,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The tower's rollout mode, TELORA_TOWER_MODE (declared, typed and validated in
3
+ * @telora/daemon-core env-config.ts): off | observe | enforce, default observe.
4
+ * See docs/runbook-tower.md for what each mode does and how a host moves from
5
+ * observe to enforce.
6
+ */
7
+ import { type TowerMode } from '@telora/daemon-core';
8
+ export declare function resolveTowerMode(env?: NodeJS.ProcessEnv): TowerMode;
9
+ //# sourceMappingURL=tower-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-mode.d.ts","sourceRoot":"","sources":["../../src/tower/tower-mode.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEjF,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,CAIhF"}
@@ -0,0 +1,16 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/tower/tower-production-pass.test.ts
3
+ // PINNED-BY-END
4
+ /**
5
+ * The tower's rollout mode, TELORA_TOWER_MODE (declared, typed and validated in
6
+ * @telora/daemon-core env-config.ts): off | observe | enforce, default observe.
7
+ * See docs/runbook-tower.md for what each mode does and how a host moves from
8
+ * observe to enforce.
9
+ */
10
+ import { isTowerMode, loadEnvConfig } from '@telora/daemon-core';
11
+ export function resolveTowerMode(env = process.env) {
12
+ // env-config validates the value; the guard narrows its inferred string type.
13
+ const mode = loadEnvConfig(env).TELORA_TOWER_MODE;
14
+ return isTowerMode(mode) ? mode : 'observe';
15
+ }
16
+ //# sourceMappingURL=tower-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-mode.js","sourceRoot":"","sources":["../../src/tower/tower-mode.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,qEAAqE;AACrE,gBAAgB;AAEhB;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,aAAa,EAAkB,MAAM,qBAAqB,CAAC;AAEjF,MAAM,UAAU,gBAAgB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACnE,8EAA8E;IAC9E,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAC;IAClD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type TowerPassStanding = 'ok' | 'failed' | 'not_run';
2
+ /** Record one product pass's outcome (tower-pass.ts runTowerPass). */
3
+ export declare function recordTowerPassOutcome(productId: string, ok: boolean, nowMs?: number): void;
4
+ /** The product's pass standing now. A focus with no product cannot be judged: not_run. */
5
+ export declare function towerPassStanding(productId: string | null | undefined, nowMs?: number): TowerPassStanding;
6
+ /** Test-only reset. */
7
+ export declare function resetTowerPassStanding(): void;
8
+ //# sourceMappingURL=tower-pass-standing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-pass-standing.d.ts","sourceRoot":"","sources":["../../src/tower/tower-pass-standing.ts"],"names":[],"mappings":"AAgCA,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,QAAQ,GAAG,SAAS,CAAC;AAI5D,sEAAsE;AACtE,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,GAAE,MAAmB,GAAG,IAAI,CAEvG;AAED,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,GAAE,MAAmB,GAAG,iBAAiB,CAMrH;AAED,uBAAuB;AACvB,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
@@ -0,0 +1,50 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/tower/tower-entrypoints.test.ts,
3
+ // packages/daemon/src/tower/tower-heartbeat-status.test.ts, packages/daemon/src/tower/tower-pass.test.ts,
4
+ // packages/daemon/src/tower/tower-prd-arm.test.ts,
5
+ // packages/daemon/src/tower/tower-ready-row-ingestion.test.ts,
6
+ // packages/daemon/src/tower/tower-spawn-modes.test.ts
7
+ // PINNED-BY-END
8
+ /**
9
+ * Whether this daemon's tower pass for a product is STANDING -- the condition
10
+ * under which admission may obey a stored verdict at all.
11
+ *
12
+ * A stored verdict was written by some pass; it is only as good as the pass
13
+ * that keeps confirming it. When this process's most recent pass for the
14
+ * product failed -- or no pass for it has succeeded yet -- a still-fresh
15
+ * holding verdict in the database is a leftover, not a decision: admission
16
+ * fails open on it IMMEDIATELY (tower-admission.ts), rather than enforcing it
17
+ * until it ages out of the freshness bound. Another product's standing is
18
+ * untouched.
19
+ *
20
+ * ok -- the most recent pass for the product succeeded, within the
21
+ * freshness bound (TOWER_VERDICT_MAX_AGE_MS).
22
+ * failed -- the most recent pass for the product threw.
23
+ * not_run -- no pass for the product has succeeded within the bound (daemon
24
+ * start before the first pass, or a pass that stopped running).
25
+ *
26
+ * A leaf: in-process state, written by tower-pass.ts and read by admission
27
+ * through the spawn passes' deps (composition/spawn-pass-deps.ts).
28
+ */
29
+ import { TOWER_VERDICT_MAX_AGE_MS } from '@telora/daemon-core';
30
+ const lastOutcome = new Map();
31
+ /** Record one product pass's outcome (tower-pass.ts runTowerPass). */
32
+ export function recordTowerPassOutcome(productId, ok, nowMs = Date.now()) {
33
+ lastOutcome.set(productId, { ok, atMs: nowMs });
34
+ }
35
+ /** The product's pass standing now. A focus with no product cannot be judged: not_run. */
36
+ export function towerPassStanding(productId, nowMs = Date.now()) {
37
+ if (!productId)
38
+ return 'not_run';
39
+ const last = lastOutcome.get(productId);
40
+ if (!last)
41
+ return 'not_run';
42
+ if (!last.ok)
43
+ return 'failed';
44
+ return nowMs - last.atMs > TOWER_VERDICT_MAX_AGE_MS ? 'not_run' : 'ok';
45
+ }
46
+ /** Test-only reset. */
47
+ export function resetTowerPassStanding() {
48
+ lastOutcome.clear();
49
+ }
50
+ //# sourceMappingURL=tower-pass-standing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-pass-standing.js","sourceRoot":"","sources":["../../src/tower/tower-pass-standing.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,kEAAkE;AAClE,4GAA4G;AAC5G,qDAAqD;AACrD,iEAAiE;AACjE,wDAAwD;AACxD,gBAAgB;AAEhB;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAI/D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAyC,CAAC;AAErE,sEAAsE;AACtE,MAAM,UAAU,sBAAsB,CAAC,SAAiB,EAAE,EAAW,EAAE,QAAgB,IAAI,CAAC,GAAG,EAAE;IAC/F,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,iBAAiB,CAAC,SAAoC,EAAE,QAAgB,IAAI,CAAC,GAAG,EAAE;IAChG,IAAI,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC9B,OAAO,KAAK,GAAG,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACzE,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,sBAAsB;IACpC,WAAW,CAAC,KAAK,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The tower pass: one pass per configured product, the SOLE writer of
3
+ * product_focuses.tower_verdict.
4
+ *
5
+ * It runs at the head of every ready-focus pass (so on daemon start and every
6
+ * poll), reads the product's limit and armed foci, computes every verdict
7
+ * (tower-verdicts.ts) from the armed set, the limit, focus rank, team presence
8
+ * and the spawn passes' last record of demand, and writes only what changed --
9
+ * or what is aging toward the bounded freshness age. So any change to those
10
+ * inputs is reflected on the next poll, and a verdict the pass keeps confirming
11
+ * never goes stale under the spawn pass.
12
+ *
13
+ * ISOLATION. A pass that throws for one product is logged and recorded
14
+ * (tower-pass-standing.ts): admission fails open on that product's foci at
15
+ * once, whatever verdicts they still carry, until a pass for it succeeds. It
16
+ * never stops another product's pass. Mode `off` runs no pass at all.
17
+ *
18
+ * The tower never arms or un-arms: nothing here writes assigned_agent_role_id.
19
+ */
20
+ import type { TowerMode } from '@telora/daemon-core';
21
+ import type { TowerProductState } from '../queries/tower/tower-state.js';
22
+ import { type TowerVerdictWrite } from './tower-verdicts.js';
23
+ import type { TowerGraphInputs } from './tower-graph.js';
24
+ export interface TowerPassDeps {
25
+ products: ReadonlyArray<{
26
+ id: string;
27
+ }>;
28
+ mode: TowerMode;
29
+ readState: (productId: string) => Promise<TowerProductState>;
30
+ writeVerdicts: (productId: string, writes: readonly TowerVerdictWrite[]) => Promise<void>;
31
+ /** Foci holding a live coding or review team now. */
32
+ liveTeamFocusIds: () => ReadonlySet<string>;
33
+ /** Foci the spawn passes last found wanting a team (null = not yet known). */
34
+ wantingFocusIds: () => ReadonlySet<string> | null;
35
+ /**
36
+ * The product's graph inputs (depends_on / excludes / landed), judged from the
37
+ * state read (tower/tower-graph.ts over the product's git). Throwing fails the
38
+ * product's pass, which fails open.
39
+ */
40
+ judgeGraph: (productId: string, state: TowerProductState) => TowerGraphInputs;
41
+ now: () => Date;
42
+ }
43
+ /** The pass's deps over the running daemon, or null before it has a live config (composition/tower-pass-context.ts). */
44
+ export type TowerPassDepsSource = () => TowerPassDeps | null;
45
+ export interface TowerProductOutcome {
46
+ productId: string;
47
+ ok: boolean;
48
+ writes: number;
49
+ error?: string;
50
+ }
51
+ /** Run one product's pass. Throws on a read/compute/write failure (the caller isolates it). */
52
+ export declare function runTowerProductPass(productId: string, deps: TowerPassDeps): Promise<number>;
53
+ /** Run every product's pass; one product's failure never stops another's. */
54
+ export declare function runTowerPass(deps: TowerPassDeps): Promise<TowerProductOutcome[]>;
55
+ //# sourceMappingURL=tower-pass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-pass.d.ts","sourceRoot":"","sources":["../../src/tower/tower-pass.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAMzD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxC,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC7D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,iBAAiB,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1F,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,8EAA8E;IAC9E,eAAe,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAClD;;;;OAIG;IACH,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,KAAK,gBAAgB,CAAC;IAC9E,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAED,wHAAwH;AACxH,MAAM,MAAM,mBAAmB,GAAG,MAAM,aAAa,GAAG,IAAI,CAAC;AAE7D,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,+FAA+F;AAC/F,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBjG;AAED,6EAA6E;AAC7E,wBAAsB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAmBtF"}
@@ -0,0 +1,59 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/tower/tower-entrypoints.test.ts,
3
+ // packages/daemon/src/tower/tower-pass.test.ts, packages/daemon/src/tower/tower-prd-arm.test.ts,
4
+ // packages/daemon/src/tower/tower-spawn-modes.test.ts,
5
+ // packages/daemon/src/tower/tower-state-ingestion.test.ts
6
+ // PINNED-BY-END
7
+ import { createLogger } from '../log/daemon-log.js';
8
+ import { computeTowerVerdicts, planTowerWrites, } from './tower-verdicts.js';
9
+ import { recordTowerPassOutcome } from './tower-pass-standing.js';
10
+ const log = createLogger({ module: 'tower' });
11
+ /** Run one product's pass. Throws on a read/compute/write failure (the caller isolates it). */
12
+ export async function runTowerProductPass(productId, deps) {
13
+ if (deps.mode === 'off')
14
+ return 0;
15
+ const state = await deps.readState(productId);
16
+ const foci = state.foci.map((f) => ({
17
+ focusId: f.focusId,
18
+ focusName: f.focusName,
19
+ priorityRank: f.priorityRank,
20
+ armedAt: f.agentAssignedAt ?? null,
21
+ previousVerdict: f.towerVerdict ?? null,
22
+ }));
23
+ const graph = deps.judgeGraph(productId, state);
24
+ const now = deps.now();
25
+ const computed = computeTowerVerdicts({
26
+ foci,
27
+ limit: state.concurrencyLimit ?? null,
28
+ liveTeamFocusIds: deps.liveTeamFocusIds(),
29
+ wantingFocusIds: deps.wantingFocusIds(),
30
+ ...graph,
31
+ mode: deps.mode,
32
+ now: now.toISOString(),
33
+ });
34
+ const writes = planTowerWrites(foci, computed, state.unarmedWithVerdictFocusIds ?? [], now.getTime());
35
+ await deps.writeVerdicts(productId, writes);
36
+ return writes.length;
37
+ }
38
+ /** Run every product's pass; one product's failure never stops another's. */
39
+ export async function runTowerPass(deps) {
40
+ if (deps.mode === 'off')
41
+ return [];
42
+ const outcomes = [];
43
+ for (const product of deps.products) {
44
+ try {
45
+ const writes = await runTowerProductPass(product.id, deps);
46
+ recordTowerPassOutcome(product.id, true, deps.now().getTime());
47
+ outcomes.push({ productId: product.id, ok: true, writes });
48
+ }
49
+ catch (err) {
50
+ const error = err instanceof Error ? err.message : String(err);
51
+ recordTowerPassOutcome(product.id, false, deps.now().getTime());
52
+ log.warn(`[tower] pass failed for product ${product.id.slice(0, 8)}: ${error} -- admission fails open on its foci ` +
53
+ `until a pass for it succeeds (tower-pass-standing.ts)`);
54
+ outcomes.push({ productId: product.id, ok: false, writes: 0, error });
55
+ }
56
+ }
57
+ return outcomes;
58
+ }
59
+ //# sourceMappingURL=tower-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-pass.js","sourceRoot":"","sources":["../../src/tower/tower-pass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type TowerPassDepsSource } from './tower-pass.js';
2
+ export declare function runTowerPassSafely(source: TowerPassDepsSource): Promise<void>;
3
+ //# sourceMappingURL=tower-production-pass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tower-production-pass.d.ts","sourceRoot":"","sources":["../../src/tower/tower-production-pass.ts"],"names":[],"mappings":"AAaA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAIzE,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOnF"}