elanous 0.2.38 → 0.2.41

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 (712) hide show
  1. package/README.md +7 -7
  2. package/apps/pwa/out/404/index.html +1 -1
  3. package/apps/pwa/out/404.html +1 -1
  4. package/apps/pwa/out/_next/static/{RrjXTwz-urRAbChP1bX02 → GiOLWmt4MqDZePubBjA4Q}/_buildManifest.js +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1115.434ae2ecb0ad986d.js +149 -0
  6. package/apps/pwa/out/_next/static/chunks/1121.667cec0bcdbb3fdb.js +36 -0
  7. package/apps/pwa/out/_next/static/chunks/1178.1aa9cad0e7695427.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1197-54da59d250a6e476.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{5203.51e5d8b5f1dcfe53.js → 1219.7e17964e0e7cd8e5.js} +3 -3
  10. package/apps/pwa/out/_next/static/chunks/1245.1cfd13b26dbd71d6.js +287 -0
  11. package/apps/pwa/out/_next/static/chunks/1494-f93046ff5c5a124d.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/1609.2b312797599b8e45.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1661.0dd330f3d89f73f3.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/167-0c121e4b47e0f9da.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1700.cdc2bf8cc685e221.js +90 -0
  16. package/apps/pwa/out/_next/static/chunks/1703-78e46acad29f3cfc.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/{7573.2376e13c9c21f1f8.js → 1765.679b29625c8e88d4.js} +2 -2
  18. package/apps/pwa/out/_next/static/chunks/{3528.e4b8ab70d339f965.js → 1800.f7ff51dc4978a958.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/1971-7efb628c471dc5a0.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/1999-8de7b7bb36801913.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2073.689dcb2680113d76.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2198-557d49184104d165.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/221-440ce67fa9f5c839.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/{8879.ac06755bcf3e44a1.js → 2263.e09b6edea33bad2d.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/2427-8ed8e98624cbd664.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2457-725c5827bc003795.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2491-2718b15a5ccb75a5.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2527-5c636a43c7cc5dde.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2692-1027cb16fe4ccf49.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2743.5d3f76948be32b34.js +41 -0
  31. package/apps/pwa/out/_next/static/chunks/2773.0e80b9d325fb5635.js +32 -0
  32. package/apps/pwa/out/_next/static/chunks/2873-0f3cb56c8e3528ea.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/{9fe1b809.738da85c8b17e3fb.js → 2fdf2931.b431eee4fb468bc3.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/{eaad0cba.771200940c529df7.js → 2fe2c302.41b517f88d6fe502.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/3097.864e92bdaa2f4d15.js +79 -0
  36. package/apps/pwa/out/_next/static/chunks/3136-d6870ba63300a93e.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3173-0289113d434e83b7.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3230-49298c3b73ef2268.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/325-dd9208a1a0ab7ce4.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3283.528f430dd043fb4e.js +74 -0
  41. package/apps/pwa/out/_next/static/chunks/3295.cc8cb13b8f0dae1a.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3688-86e3b88abf18cb3b.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3804-7220467a5473a8df.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3808.3e1e247ce7bdf889.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{b7478fef.5917716286213fcc.js → 385e4574.e9886ab32759eda3.js} +3 -3
  46. package/apps/pwa/out/_next/static/chunks/3927.b5473ad109da44c5.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/3934-98ea94832e194796.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4133.2056bb4d78f621af.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4156.ba4de4a3664f04b4.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4297-239ca82f293faab0.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/4320-dcc5a7734126f4c9.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4434.94b4c552a2d91e69.js +148 -0
  53. package/apps/pwa/out/_next/static/chunks/{8911.bf5e712acb25fb6c.js → 4527.a69f2894d1bc9f4a.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/4579.03e4092f73950965.js +226 -0
  55. package/apps/pwa/out/_next/static/chunks/{5307.d7328020227880c2.js → 4667.97e31aa38c95c475.js} +4 -4
  56. package/apps/pwa/out/_next/static/chunks/{6142.913b44e260a3f925.js → 4702.4a4adc1f16d7c93b.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/4815.481501c57b3568d9.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/487-d16e698ef14662d9.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/4888-7a38bf522c9cc6fe.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/5034-30e429d6bb086881.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5040-35c494e60dadd97d.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/50c6ab1e.81a96e419350909d.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5154.4b6a836179356f30.js +94 -0
  64. package/apps/pwa/out/_next/static/chunks/{8487.291666583bebe640.js → 5159.115f1c2ca471386c.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/5228-a8200df486278f2d.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/530.d26346e18089640a.js +115 -0
  67. package/apps/pwa/out/_next/static/chunks/5379.97af106d09566f69.js +8 -0
  68. package/apps/pwa/out/_next/static/chunks/{89d6c04f-6b8ab341748e468a.js → 53aeb4ee-51c92f8d683f5220.js} +1 -1
  69. package/apps/pwa/out/_next/static/chunks/{3291.f6c1005387d514bd.js → 5471.1e611302ed3a7c53.js} +2 -2
  70. package/apps/pwa/out/_next/static/chunks/5479-b6372e762719f92f.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/5534-699c5cbcbc96c5d7.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/{3086.fb14b60ce98b53d3.js → 5574.f35accf4f132b29d.js} +1 -1
  73. package/apps/pwa/out/_next/static/chunks/5701.9c4e8f250312eeee.js +24 -0
  74. package/apps/pwa/out/_next/static/chunks/{1632.7d87f998e714bede.js → 5744.b9415e18ad623100.js} +1 -1
  75. package/apps/pwa/out/_next/static/chunks/{5300.677c97e0393742e6.js → 5892.2526d962339662d8.js} +1 -1
  76. package/apps/pwa/out/_next/static/chunks/5937.c2df9e3447e1f667.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/5941.8210cc93c9202c0a.js +286 -0
  78. package/apps/pwa/out/_next/static/chunks/{7161-8f22db3ec5ed005e.js → 5947-135e26537d80a6b9.js} +3 -3
  79. package/apps/pwa/out/_next/static/chunks/{3124-561f7f037943cde2.js → 5964-7e21164a8f2cf0ce.js} +1 -1
  80. package/apps/pwa/out/_next/static/chunks/5975.4004f820ae3cecd5.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6142-7ccd48795d7abd55.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6160.bf9177b8c1bfb243.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/6398.71ba0690c8027555.js +27 -0
  84. package/apps/pwa/out/_next/static/chunks/6492.2bd5c10edc1a064e.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/6552.a3840aa7da35ad68.js +62 -0
  86. package/apps/pwa/out/_next/static/chunks/6736.1e36d3ac5fa5e41d.js +24 -0
  87. package/apps/pwa/out/_next/static/chunks/6874.d5cf7eb8edb2cabe.js +2 -0
  88. package/apps/pwa/out/_next/static/chunks/6900.3ccbe069bdd4556a.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/71327eb4.add50b365f94bf3a.js +122 -0
  90. package/apps/pwa/out/_next/static/chunks/7136.deca3e3a2ddaa374.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7139-ee9f8ed2677a40b3.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/{2415.738b777642a02233.js → 7191.fdc955f7d96064a4.js} +1 -1
  93. package/apps/pwa/out/_next/static/chunks/7423-e0aa5d9e2962120e.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7453.afc1ea7e05d56a8c.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7503-5e6a3d7fe67e4a0c.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/7761-476fd7bf768abd34.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/7975.eafb12097a0a9e62.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{1026-44010f405704050c.js → 8050-9b364bdef8f71927.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/8152.64e253c35cccfaae.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/8190.620f0231b3924bcb.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/83184dcd-0ee6247ff41eb3f2.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8448.45b6b1f1a99eeb35.js +104 -0
  103. package/apps/pwa/out/_next/static/chunks/8464-e24f10451b658aac.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8477.e4c944bb3aa5d2f3.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8710.ca385257d5ca422e.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8830.627ccf294ec2465c.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8880.64c445592f04acaf.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8981.c706964c8ba27aff.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{5638.107314dc479e2f37.js → 9048.212083de42d25be3.js} +2 -2
  110. package/apps/pwa/out/_next/static/chunks/9053.bd0fffaff8d3f836.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/9161-c66e723d7ec9d682.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/9185.1ecdf9b65c0bb684.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9356.fccd5f58086b4ff5.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9446.dc0406109371d329.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9506.dcf25d467a42627e.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{8019.21ca554a821dad01.js → 9507.7026657a2f47eb64.js} +16 -16
  117. package/apps/pwa/out/_next/static/chunks/9541.eb8e2f8480c7feca.js +93 -0
  118. package/apps/pwa/out/_next/static/chunks/9577.ed7582186efa5482.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9579.4a8169600fff2058.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9642-ff2e039eb62e3b92.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9659-8f2323348113ad80.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9724.4c5623af2db43153.js +34 -0
  123. package/apps/pwa/out/_next/static/chunks/{6493.27ad77de7aac7966.js → 9834.63ab2bbc3153172e.js} +1 -1
  124. package/apps/pwa/out/_next/static/chunks/{3389.012c70767a872f9b.js → 9869.81a1e24ca682d5a0.js} +1 -1
  125. package/apps/pwa/out/_next/static/chunks/{8994d93e.33ba491f95345fc2.js → a0a01613.6d1cca322238d7fe.js} +1 -1
  126. package/apps/pwa/out/_next/static/chunks/{de8b930d.d94c0f42acf807fc.js → addc2b32.8c4ff8f0aefe987d.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-8d9d76ea322d855c.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed2c4e59f9f1fe21.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-2dc99e03c5d76ca0.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/board/page-b5a3a788330e920f.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-866aac5ff226a7f3.js → page-14949db884bfa672.js} +1 -1
  132. package/apps/pwa/out/_next/static/chunks/app/bots/page-ea76c85b36388a97.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/ceo/page-01716d20af62d325.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/chat/page-62baf6881a64b1e9.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/consult/page-f89ba590c2af8afa.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/control/page-22f8b0f78d9637e5.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/decisions/page-817f9d7e6820be80.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/design-check/page-ffb9bc0d2650249a.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/editor/page-f6d0e64873c0f629.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/exec/page-8bfa0d4ba23a209d.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/field/page-f5060462249e32c7.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/healthz/route-8d9d76ea322d855c.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/inside/page-b2049f8b1ef79117.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/intake/page-1446c5fa93907f11.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/layout-b48a7a07c5204156.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/live/page-e974b8c4ca47614c.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/live-run/page-8a919b869bd28594.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/loops/page-d31b8de6bfcd72ed.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/market/page-64765f817909db29.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-d128c8569a36ee9b.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/morning/page-b2679d357e9b9270.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/not-found-6fabec98a9e7b269.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/observatory/page-048f30149dcf1349.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-9113181d6b5a2725.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-40f9e19149024298.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-1e7a755a13227611.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3c8f8b67a203cf07.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/page-18032f5437215062.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/reflection/page-da3b739192fd0ee3.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d332545571bd6fd1.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/sessions/page-5237aa5b3f3b8ec5.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a142dfb0331a05f5.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/settings/page-d16e56c3989c147e.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6b6593600869e2cd.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/setup/layout-8d9d76ea322d855c.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/setup/page-29e8b8a8396b55cb.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/share/page-b3414d856a1a7e2f.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c3affa52b7ce961b.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f21dddf9f95b6fab.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/term/page-f5a30a24745bf088.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/today/page-8c8569a09b4d4e34.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/trace/page-2ce32722c0243483.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/vault/page-4767b0d9c0a98c6c.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-116ee1339c387a75.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/workflows/page-35e8c50ca4ef7ffc.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/app/workspace/page-52e17113356bb891.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7458ae05ed2ac1a0.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/b2271e54.c1de3347976e90b0.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/{b7459770.6539b11860eb8a9c.js → c0c1a699.0ba79f97005965d4.js} +7 -7
  180. package/apps/pwa/out/_next/static/chunks/framework-ad6c9dd524ad6ef8.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/main-app-2ddd1f2b44d6aa59.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/main-e3d82bdc358d20c5.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/pages/_app-71d5aa3896e1655f.js +1 -0
  184. package/apps/pwa/out/_next/static/chunks/pages/_error-03ffc54e00438546.js +1 -0
  185. package/apps/pwa/out/_next/static/chunks/webpack-56bb89af5a611d0b.js +1 -0
  186. package/apps/pwa/out/approvals/index.html +1 -1
  187. package/apps/pwa/out/approvals/index.txt +22 -22
  188. package/apps/pwa/out/autopilot/index.html +1 -1
  189. package/apps/pwa/out/autopilot/index.txt +22 -22
  190. package/apps/pwa/out/board/index.html +1 -1
  191. package/apps/pwa/out/board/index.txt +22 -22
  192. package/apps/pwa/out/botlab/index.html +1 -1
  193. package/apps/pwa/out/botlab/index.txt +21 -21
  194. package/apps/pwa/out/bots/index.html +1 -1
  195. package/apps/pwa/out/bots/index.txt +21 -21
  196. package/apps/pwa/out/ceo/index.html +1 -1
  197. package/apps/pwa/out/ceo/index.txt +22 -22
  198. package/apps/pwa/out/chat/index.html +1 -1
  199. package/apps/pwa/out/chat/index.txt +21 -21
  200. package/apps/pwa/out/consult/index.html +1 -1
  201. package/apps/pwa/out/consult/index.txt +22 -22
  202. package/apps/pwa/out/control/index.html +1 -1
  203. package/apps/pwa/out/control/index.txt +22 -22
  204. package/apps/pwa/out/decisions/index.html +1 -1
  205. package/apps/pwa/out/decisions/index.txt +21 -21
  206. package/apps/pwa/out/design-check/index.html +1 -1
  207. package/apps/pwa/out/design-check/index.txt +22 -22
  208. package/apps/pwa/out/editor/index.html +1 -1
  209. package/apps/pwa/out/editor/index.txt +22 -22
  210. package/apps/pwa/out/exec/index.html +1 -1
  211. package/apps/pwa/out/exec/index.txt +22 -22
  212. package/apps/pwa/out/field/index.html +1 -1
  213. package/apps/pwa/out/field/index.txt +22 -22
  214. package/apps/pwa/out/healthz +1 -1
  215. package/apps/pwa/out/index.html +1 -1
  216. package/apps/pwa/out/index.txt +21 -21
  217. package/apps/pwa/out/inside/index.html +1 -1
  218. package/apps/pwa/out/inside/index.txt +21 -21
  219. package/apps/pwa/out/intake/index.html +1 -1
  220. package/apps/pwa/out/intake/index.txt +22 -22
  221. package/apps/pwa/out/live/index.html +1 -1
  222. package/apps/pwa/out/live/index.txt +22 -22
  223. package/apps/pwa/out/live-run/index.html +1 -1
  224. package/apps/pwa/out/live-run/index.txt +22 -22
  225. package/apps/pwa/out/loops/index.html +1 -1
  226. package/apps/pwa/out/loops/index.txt +21 -21
  227. package/apps/pwa/out/market/index.html +1 -1
  228. package/apps/pwa/out/market/index.txt +21 -21
  229. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  230. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  231. package/apps/pwa/out/morning/index.html +1 -1
  232. package/apps/pwa/out/morning/index.txt +22 -22
  233. package/apps/pwa/out/observatory/index.html +1 -1
  234. package/apps/pwa/out/observatory/index.txt +22 -22
  235. package/apps/pwa/out/ops/checklist/index.html +1 -1
  236. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  237. package/apps/pwa/out/ops/release/index.html +1 -1
  238. package/apps/pwa/out/ops/release/index.txt +21 -21
  239. package/apps/pwa/out/ops/seats/index.html +1 -1
  240. package/apps/pwa/out/ops/seats/index.txt +21 -21
  241. package/apps/pwa/out/outputs/index.html +1 -1
  242. package/apps/pwa/out/outputs/index.txt +21 -21
  243. package/apps/pwa/out/reflection/index.html +1 -1
  244. package/apps/pwa/out/reflection/index.txt +22 -22
  245. package/apps/pwa/out/scheduler/index.html +1 -1
  246. package/apps/pwa/out/scheduler/index.txt +22 -22
  247. package/apps/pwa/out/sessions/index.html +1 -1
  248. package/apps/pwa/out/sessions/index.txt +21 -21
  249. package/apps/pwa/out/settings/devices/index.html +1 -1
  250. package/apps/pwa/out/settings/devices/index.txt +22 -22
  251. package/apps/pwa/out/settings/index.html +1 -1
  252. package/apps/pwa/out/settings/index.txt +22 -22
  253. package/apps/pwa/out/setup/done/index.html +1 -1
  254. package/apps/pwa/out/setup/done/index.txt +22 -22
  255. package/apps/pwa/out/setup/index.html +1 -1
  256. package/apps/pwa/out/setup/index.txt +22 -22
  257. package/apps/pwa/out/share/index.html +1 -1
  258. package/apps/pwa/out/share/index.txt +22 -22
  259. package/apps/pwa/out/showroom/index.html +1 -1
  260. package/apps/pwa/out/showroom/index.txt +21 -21
  261. package/apps/pwa/out/tasks/index.html +1 -1
  262. package/apps/pwa/out/tasks/index.txt +22 -22
  263. package/apps/pwa/out/term/index.html +1 -1
  264. package/apps/pwa/out/term/index.txt +22 -22
  265. package/apps/pwa/out/today/index.html +1 -1
  266. package/apps/pwa/out/today/index.txt +22 -22
  267. package/apps/pwa/out/trace/index.html +1 -1
  268. package/apps/pwa/out/trace/index.txt +22 -22
  269. package/apps/pwa/out/vault/index.html +1 -1
  270. package/apps/pwa/out/vault/index.txt +22 -22
  271. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  272. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  273. package/apps/pwa/out/workflows/index.html +1 -1
  274. package/apps/pwa/out/workflows/index.txt +22 -22
  275. package/apps/pwa/out/workspace/index.html +1 -1
  276. package/apps/pwa/out/workspace/index.txt +22 -22
  277. package/apps/pwa/out/worktrees/index.html +1 -1
  278. package/apps/pwa/out/worktrees/index.txt +22 -22
  279. package/bin/elanous.mjs +6 -1
  280. package/catalog/models/openai/gpt-6.1-sol.yaml +16 -0
  281. package/catalog/models/openai/gpt-6.1-terra.yaml +16 -0
  282. package/graphs/demo/persona-marketer/fixtures/marketer-scenario.yaml +10 -0
  283. package/graphs/demo/persona-marketer/persona-marketer.yaml +25 -0
  284. package/graphs/demo/persona-marketer/recipes.yaml +19 -0
  285. package/graphs/demo/persona-teacher/lesson-pack.yaml +16 -0
  286. package/graphs/demo/persona-teacher/recipes.yaml +17 -0
  287. package/graphs/demo/persona-teacher/weekly-lesson.yaml +36 -0
  288. package/graphs/overlays/stop-heal-ladder.yaml +23 -2
  289. package/graphs/release/release-loop.yaml +3 -1
  290. package/graphs/units/gate/gate.yaml +20 -0
  291. package/graphs/units/git/git.yaml +26 -0
  292. package/graphs/units/implement/implement.yaml +19 -0
  293. package/graphs/units/landing/landing.yaml +18 -0
  294. package/graphs/units/review/review.yaml +20 -0
  295. package/graphs/units/verbs/recipes.yaml +3 -0
  296. package/graphs/units/verbs/research.yaml +20 -0
  297. package/graphs/video/explainer-line.yaml +4 -1
  298. package/graphs/work/research-report.yaml +11 -1
  299. package/package.json +1 -1
  300. package/scripts/dead-code/report.ts +169 -0
  301. package/scripts/disk-steward.ts +46 -0
  302. package/scripts/fleet/node-setup.sh +50 -0
  303. package/scripts/graph-units/verbs/research.ts +55 -0
  304. package/scripts/lib/morning-report-options.ts +3 -4
  305. package/scripts/lib/tui-regress-evidence.ts +56 -0
  306. package/scripts/llm-cost-ledger.ts +179 -8
  307. package/scripts/measure-nl-routing-corpus.ts +10 -2
  308. package/scripts/morning-report.ts +5 -1
  309. package/scripts/persona-teacher/weekly-lesson-node.ts +172 -0
  310. package/scripts/public-copy-lint.ts +163 -19
  311. package/scripts/public-export.ts +15 -7
  312. package/scripts/ratchets/train-side-invariants.sh +0 -1
  313. package/scripts/release-loop/export-check-node.ts +21 -3
  314. package/scripts/release-loop/gate-autoheal.ts +117 -0
  315. package/scripts/release-loop/gate-baseline-receipt.ts +8 -1
  316. package/scripts/release-loop/gate-diff.ts +22 -3
  317. package/scripts/release-loop/gate-node.ts +583 -64
  318. package/scripts/release-loop/gate-shard-dispatch.ts +128 -0
  319. package/scripts/release-loop/gate-shard-load-flaky.ts +256 -0
  320. package/scripts/release-loop/gate-shard-receipts.ts +89 -0
  321. package/scripts/release-loop/gate-triage-node.ts +70 -10
  322. package/scripts/release-loop/gate-wall.ts +5 -5
  323. package/scripts/release-loop/preflight.ts +6 -2
  324. package/scripts/release-loop/resume-release.ts +267 -20
  325. package/scripts/release-loop/resume.ts +48 -0
  326. package/scripts/release-loop/traffic-node.ts +6 -3
  327. package/scripts/release-loop/unattended-release.ts +4 -2
  328. package/scripts/release-loop/version-node.ts +7 -2
  329. package/scripts/release-story/draft.ts +1 -1
  330. package/scripts/research-bench/README.md +53 -0
  331. package/scripts/research-bench/questions.json +66 -0
  332. package/scripts/research-bench/rubric.md +33 -0
  333. package/scripts/research-bench/run.ts +523 -0
  334. package/scripts/shadow-authoring-probe.ts +20 -0
  335. package/scripts/tui-regress.ts +85 -10
  336. package/scripts/ux-sim.ts +19 -2
  337. package/src/acp/core-turn-bridge.ts +94 -4
  338. package/src/agent/autonomous-tools.ts +10 -3
  339. package/src/agent/monad-agent-turn.ts +4 -1
  340. package/src/agent/registry.ts +26 -2
  341. package/src/agent/shared-app-tools.ts +1 -1
  342. package/src/agent/surface-ux/build.ts +12 -3
  343. package/src/agent/surface-ux/types.ts +1 -1
  344. package/src/agent/task-notification.ts +75 -6
  345. package/src/agent/types.ts +3 -0
  346. package/src/agent-mission/driver.ts +29 -3
  347. package/src/agent-mission/mission-cli.ts +2 -1
  348. package/src/agent-substrate/review-context-pack.ts +365 -0
  349. package/src/agent-substrate/self-review-cli.ts +291 -20
  350. package/src/ask-user-question/index.ts +6 -0
  351. package/src/ask-user-question/inline-resolver.ts +49 -0
  352. package/src/auto-research/question-ladder-contract.ts +22 -3
  353. package/src/auto-research/research-turn.ts +359 -17
  354. package/src/autopilot/agent-loop.ts +1 -1
  355. package/src/autopilot/discovery/wiki-claim-proposer.ts +22 -1
  356. package/src/autopilot/pty-control-loop.ts +8 -1
  357. package/src/boot/daemon-tools/index.ts +4 -3
  358. package/src/boot/daemon-tools/self-implement.ts +312 -7
  359. package/src/boot/daemon-tools/types.ts +2 -0
  360. package/src/budget/llm-cost.ts +44 -5
  361. package/src/budget/shadow-arms.ts +52 -0
  362. package/src/budget/unified-usage.ts +17 -1
  363. package/src/capture/tui-self-report.ts +14 -0
  364. package/src/chat/index.ts +33 -7
  365. package/src/chat/pickers/state.ts +7 -1
  366. package/src/cli/agent-cli.ts +43 -8
  367. package/src/cli/intake-cli.ts +22 -1
  368. package/src/cli/lesson-cli.ts +19 -3
  369. package/src/cli/log-event-names.ts +6 -0
  370. package/src/cli/mcp-login.ts +8 -18
  371. package/src/cli/msg-cli.ts +57 -1
  372. package/src/cli/pod-cli.ts +17 -2
  373. package/src/cli/pr-land-heavy-remote.ts +15 -9
  374. package/src/cli/pty-takeover-cli.ts +67 -7
  375. package/src/cli/release-cli.ts +79 -7
  376. package/src/cli/repo-cli.ts +2 -1
  377. package/src/cli/sim-cli.ts +7 -1
  378. package/src/cli/tasks-cli.ts +41 -13
  379. package/src/codex/models.ts +30 -15
  380. package/src/control-plane/ledger.ts +21 -4
  381. package/src/core-turn/run-core-turn.ts +47 -3
  382. package/src/core-turn/run-goal-loop.ts +240 -10
  383. package/src/core-turn/types.ts +9 -0
  384. package/src/dashboard/acp-boot.ts +72 -3
  385. package/src/dashboard/chat/acp-chat.ts +2 -2
  386. package/src/dashboard/classic-sigquit.ts +16 -0
  387. package/src/dashboard/index.ts +327 -27
  388. package/src/dashboard/input/chat-main-interaction-opts.ts +3 -0
  389. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +7 -2
  390. package/src/dashboard/input/chat-main-slash-command.ts +7 -0
  391. package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
  392. package/src/dashboard/input/dashboard-priority-key-route.ts +21 -5
  393. package/src/dashboard/input/external-input-host.ts +2 -0
  394. package/src/dashboard/sidetab-runtime.ts +228 -0
  395. package/src/dashboard/slash-runtime/ask-clarification.ts +7 -1
  396. package/src/dashboard/slash-runtime/dashboard-handlers.ts +20 -8
  397. package/src/dashboard/slash-runtime/help-from-registry.ts +2 -2
  398. package/src/dashboard/term-slash-runtime.ts +16 -18
  399. package/src/dashboard/turn-preamble.ts +14 -1
  400. package/src/disk-steward/judge.ts +73 -0
  401. package/src/disk-steward/ledger.ts +122 -0
  402. package/src/disk-steward/steward.ts +373 -0
  403. package/src/domains/guardian-tick.ts +191 -2
  404. package/src/domains/knowledge.ts +1 -1
  405. package/src/esc-abort-gate.ts +33 -1
  406. package/src/execution-loop/launch-gate.ts +206 -1
  407. package/src/graph-runner/graph-cli.ts +27 -4
  408. package/src/graph-runner/in-process-registry.ts +37 -0
  409. package/src/graph-runner/runner.ts +63 -11
  410. package/src/graph-wizard/generate.ts +16 -7
  411. package/src/graph-wizard/mission-compose.ts +201 -0
  412. package/src/grok/agent-search.ts +8 -2
  413. package/src/grok/credential.ts +5 -0
  414. package/src/grounding/ref-index.ts +30 -0
  415. package/src/grounding/ref-vector.ts +61 -0
  416. package/src/grounding/sources-cli.ts +11 -3
  417. package/src/grounding/sources.ts +15 -1
  418. package/src/harness/harness-cli-command.ts +305 -23
  419. package/src/harness/harness-incidents.ts +312 -7
  420. package/src/harness/harness-pod-dispatch.ts +7 -1
  421. package/src/harness/harness-space.ts +15 -1
  422. package/src/harness/progress-worker-entry.ts +59 -0
  423. package/src/harness/progress-worker-runtime.ts +11 -0
  424. package/src/index.ts +35 -5
  425. package/src/input/external-input-queue.ts +10 -0
  426. package/src/instance/dev-project.ts +25 -0
  427. package/src/intake-plane/collect-telegram-saved.ts +36 -1
  428. package/src/intake-plane/intake-to-tasks.ts +246 -3
  429. package/src/intelligence-map/model-alias.ts +6 -5
  430. package/src/intelligence-map/model-catalog.ts +59 -2
  431. package/src/lessons/friction-ledger.ts +154 -0
  432. package/src/live-supply/collector.ts +149 -0
  433. package/src/live-supply/probes.ts +52 -0
  434. package/src/live-supply/snapshot.ts +40 -0
  435. package/src/llm/model-defaults.ts +4 -4
  436. package/src/llm.ts +473 -87
  437. package/src/loops/orchestrator/tick.ts +56 -0
  438. package/src/mcp/mcp-oauth.ts +3 -1
  439. package/src/model-tier/llm-tier-map.ts +9 -9
  440. package/src/model-tier/turn-tier.ts +1 -1
  441. package/src/msg/msg-mirror-github.ts +80 -0
  442. package/src/msg/msg-mirror-policy.ts +61 -0
  443. package/src/msg/msg-mirror-runner.ts +219 -0
  444. package/src/msg/msg-mirror-state.ts +33 -0
  445. package/src/msg/msg-store.ts +11 -0
  446. package/src/nexus/api/harness-api.ts +2 -1
  447. package/src/output-truncation.ts +41 -12
  448. package/src/project/project-store.ts +48 -1
  449. package/src/pty/snapshot.py +132 -0
  450. package/src/pty-shell/pty-control-ipc.ts +29 -2
  451. package/src/pty-shell/pty-manifest.ts +59 -3
  452. package/src/pty-shell/registry.ts +2 -0
  453. package/src/pty-shell/tui-run-state.ts +80 -3
  454. package/src/release-loop/gate-host-reservation.ts +157 -4
  455. package/src/release-loop/gate-shard-ledger.ts +68 -0
  456. package/src/release-loop/merged-pr-checklist.ts +25 -2
  457. package/src/release-loop/rehearsal-shard-plan.ts +6 -1
  458. package/src/release-loop/rehearsal.ts +27 -0
  459. package/src/release-loop/release-schedule.ts +90 -1
  460. package/src/research/bloom-exemplars.ts +135 -0
  461. package/src/research/edge-decision.ts +124 -15
  462. package/src/research/gap-followup.ts +249 -0
  463. package/src/research/lenses/counter-evidence.ts +100 -0
  464. package/src/research/lenses/working-backwards.ts +89 -0
  465. package/src/research/plugin-discovery.ts +242 -0
  466. package/src/research-bridge/invoke.ts +4 -2
  467. package/src/research-bridge/types.ts +2 -0
  468. package/src/self-dev/ask-launch-flow.ts +73 -131
  469. package/src/self-dev/dev-cli.ts +19 -17
  470. package/src/self-dev/dev-pipeline.ts +8 -4
  471. package/src/self-dev/graph-front-nodes.ts +23 -0
  472. package/src/self-dev/graph-journey-route.ts +10 -2
  473. package/src/self-dev/launch-preflight-text.ts +2 -2
  474. package/src/self-dev/launch-preflight.ts +47 -11
  475. package/src/self-dev/orchestrate-cli.ts +1 -0
  476. package/src/self-dev/orchestrate.ts +15 -0
  477. package/src/self-dev/run-supervisor.ts +204 -3
  478. package/src/self-dev/run-types.ts +3 -0
  479. package/src/self-implement/device-first.ts +8 -2
  480. package/src/self-implement/doctor-visit.ts +76 -58
  481. package/src/self-implement/gate-artifacts.ts +16 -0
  482. package/src/self-implement/gate-baseline.ts +75 -4
  483. package/src/self-implement/gate-cli.ts +368 -30
  484. package/src/self-implement/gate-receipt-registry.ts +180 -0
  485. package/src/self-implement/gate-remote.ts +165 -24
  486. package/src/self-implement/goal-author-clarification.ts +205 -32
  487. package/src/self-implement/goal-author-decompose.ts +24 -15
  488. package/src/self-implement/goal-author.ts +86 -10
  489. package/src/self-implement/graph-stage-units.ts +172 -0
  490. package/src/self-implement/graph-templates.ts +162 -1
  491. package/src/self-implement/graph-variant.ts +37 -4
  492. package/src/self-implement/host-regate.ts +124 -49
  493. package/src/self-implement/impl-findings-feedback.ts +188 -0
  494. package/src/self-implement/knowhow-pack.ts +227 -10
  495. package/src/self-implement/off-diff-evidence.ts +124 -0
  496. package/src/self-implement/orchestrator.ts +320 -28
  497. package/src/self-implement/run-ledger.ts +110 -14
  498. package/src/self-implement/run-stop.ts +33 -2
  499. package/src/self-implement/running-runs.ts +35 -12
  500. package/src/self-implement/seams.ts +6 -3
  501. package/src/self-implement/self-implement-cli.ts +40 -6
  502. package/src/self-implement/stop-autoheal-operational.ts +43 -0
  503. package/src/self-implement/stop-autoheal.ts +15 -6
  504. package/src/session/chat.ts +66 -15
  505. package/src/session/request-size-guard.ts +123 -0
  506. package/src/session-runtime/provider-circuit.ts +140 -0
  507. package/src/shell-runner/file-engine.ts +1 -0
  508. package/src/shell-runner/types.ts +4 -0
  509. package/src/sidetab-core/model.ts +312 -0
  510. package/src/skills/tools/schedule-wakeup.ts +23 -1
  511. package/src/skills/tools/shell.ts +51 -5
  512. package/src/steward/directive-audit.ts +354 -0
  513. package/src/surface/child-process.ts +31 -0
  514. package/src/surface/tui-stderr-owner.ts +48 -0
  515. package/src/task-agent/cover.ts +30 -12
  516. package/src/task-agent/judge.ts +56 -0
  517. package/src/task-agent/parent-host.ts +14 -1
  518. package/src/task-agent/postland-card.ts +35 -2
  519. package/src/task-agent/rejudge-on-head.ts +46 -2
  520. package/src/task-agent/repair-cycle.ts +1 -1
  521. package/src/task-agent/task-hand.ts +133 -25
  522. package/src/task-agent/tier-split.ts +105 -21
  523. package/src/task-orchestrator/surfaces/pod-command-job.ts +330 -2
  524. package/src/task-orchestrator/surfaces/pod-config-snapshot.ts +123 -0
  525. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +70 -2
  526. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +29 -4
  527. package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +11 -8
  528. package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +1 -1
  529. package/src/task-orchestrator/surfaces/pod-source-receive.ts +74 -6
  530. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +2 -1
  531. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +56 -15
  532. package/src/task-orchestrator/surfaces/self-implement-pod.ts +298 -45
  533. package/src/task-orchestrator/surfaces/self-implement.ts +18 -0
  534. package/src/tool-runtime/skill-exec-runtime.ts +95 -5
  535. package/src/tui/entry.ts +25 -5
  536. package/src/tui/inline-choice.ts +188 -0
  537. package/src/tui/sidetab/list.ts +36 -165
  538. package/src/tui/sidetab/nl-tool.ts +2 -136
  539. package/src/tui/sidetab/peek.ts +52 -0
  540. package/src/tui/slash/term.ts +12 -1
  541. package/src/tui-client/dashboard-session.ts +10 -1
  542. package/src/user-config.ts +45 -9
  543. package/src/ux-sim/classic-tui-seat-scenarios.ts +1 -7
  544. package/src/ux-sim/fake-land-queue.ts +88 -0
  545. package/src/ux-sim/fault-needs-human-reason-repair.ts +107 -0
  546. package/src/ux-sim/fault-pod-child-result.ts +42 -0
  547. package/src/ux-sim/gate-shard-host-reroute.ts +82 -0
  548. package/src/ux-sim/goal-interim-trace.ts +24 -0
  549. package/src/ux-sim/next-tui-seat-scenarios.ts +8 -13
  550. package/src/ux-sim/release-sim-rig.ts +262 -0
  551. package/src/ux-sim/seminar-rehearsal.ts +109 -0
  552. package/src/ux-sim/subagent-tools-device.ts +2 -2
  553. package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +4 -1
  554. package/src/version/packed-revision.json +1 -1
  555. package/src/video-pipeline/recipes/explainer.ts +54 -2
  556. package/src/web-search/grok.ts +32 -6
  557. package/src/web-search/index.ts +8 -7
  558. package/src/workflow-runtime/node-catalog.ts +53 -4
  559. package/apps/pwa/out/_next/static/chunks/1118.a8a74ea29aac3e46.js +0 -27
  560. package/apps/pwa/out/_next/static/chunks/1204-2aa830479adf80f4.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/1341-43b7d2dc77d45c9d.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/1440-c3ea8882badfd9bf.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/1698-9c11cbf97f13717e.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/1770-3b1fc92253ef9f2f.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/1923-5c1695332240246a.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/1938.0e6c60ee91c4d7f5.js +0 -148
  567. package/apps/pwa/out/_next/static/chunks/2104.70e086d5174a4736.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/213-20675f20673ba22a.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/214.186a430da28338f1.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/2277.3bd4c0ac7f1b5996.js +0 -93
  571. package/apps/pwa/out/_next/static/chunks/240.e09f433f71c78ff4.js +0 -104
  572. package/apps/pwa/out/_next/static/chunks/2417.02151393605a7cd0.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/2463.cd2641a76daaa9ff.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/2576.b9e9c629c4b84c52.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/265-c021de53f0865bd9.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/2998-b1cc13956cd6a6e7.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/2e4782d3.ba2f0ab2b63a53e7.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/3130.626b54382b4dc379.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/3221-1bb3f99a55088613.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/3280.24edb1ffd03a7f3f.js +0 -24
  581. package/apps/pwa/out/_next/static/chunks/3288-eb3c22a1cbf2ae68.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/33.ef99e8374f96c6c4.js +0 -36
  583. package/apps/pwa/out/_next/static/chunks/3341-f09b9efbd7a82f70.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/3410-d7c0f14b329d6fda.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/3421.c87d6313a7d670f7.js +0 -287
  586. package/apps/pwa/out/_next/static/chunks/3422-83a3953f9bd67e26.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/3587-002e24e368c515a9.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/3644.f12a7d821ab940af.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/3660.d7516588e2d875c3.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/3695.b82e2610f4cd4ea9.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/3858.ca1f5bc0f577eac9.js +0 -94
  592. package/apps/pwa/out/_next/static/chunks/3959-e6ac4a139d9d4bcc.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/3f0de3de-2340c532c00395db.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/4032.53f62176514ec0ea.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/4199-f7c6eb40cd0322f8.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/4204-ef8484cbecd5d62f.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/4259-163129ca79913c4b.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/428-eaa77df3d0c85969.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/4432-97e400ffa1e161f5.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/449-aa427cf2cbb75f50.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/4491-39a0fd32bbaf3840.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/4588.b7cf06d382953979.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/4690.70ec8060825366ab.js +0 -32
  604. package/apps/pwa/out/_next/static/chunks/4804.6012b5cd33c22792.js +0 -90
  605. package/apps/pwa/out/_next/static/chunks/4881.e4d22d730e3b9df5.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/4937.ce2e340dc29cad3e.js +0 -79
  607. package/apps/pwa/out/_next/static/chunks/5-bb1c08d84bd71df3.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/5005.de98df23b37cdf3c.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/5035.dc64a89cad5573cc.js +0 -74
  610. package/apps/pwa/out/_next/static/chunks/5041-5aada92ca215ffdd.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/5207.1a224035455790fd.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/5255-1b60d50eabe9f057.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/5269.d0436ee6d773382a.js +0 -286
  614. package/apps/pwa/out/_next/static/chunks/5309.3367c4d50f52b489.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/552-689827d49780fa00.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/5539.34cc9d60622cb69b.js +0 -8
  617. package/apps/pwa/out/_next/static/chunks/5568.218dea0c00ca2ec7.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/5645.b7e3b055455a04ea.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/5662.18cc0f30353cbd96.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/5671.da24fc9dc5e98325.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/5b141171.324c40fd49cb6821.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/6175-d4621f6a10b0cfab.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/6290.3eb53a20746b72ce.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/6415-9307f3eafa90837f.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/6579-3de35d00aacd71b5.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/680-2f324c4e039a5b67.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/6845-331b3c7481da3ba3.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/7000.28157393caaf5343.js +0 -62
  629. package/apps/pwa/out/_next/static/chunks/7311-60a7ea80d23c7521.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/7401.525d0a12f7d02c06.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/7438.c2cf72b66dca6aa4.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/7488.966b116789f3d41a.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/7524.ce973525d30b3d52.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/7577.ea6e9ac72e035520.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/7628.6b16863ac3972333.js +0 -34
  636. package/apps/pwa/out/_next/static/chunks/7657.8d1fb3d2f7ebe273.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/7903-1435d5d2f0dd7807.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/7954.4f7a8832663ee979.js +0 -115
  639. package/apps/pwa/out/_next/static/chunks/7978-30a91192c5dbe3c2.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/8025-8795821525cf1a07.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/8191.3674a00f0b565c23.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/8217-604955d8885c5ef2.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/8298-eed6f5f2585a63f4.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/8509.3a54d4e61893d7cb.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/8758.ac421f726a189125.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/891.d6c723a51a067b36.js +0 -149
  647. package/apps/pwa/out/_next/static/chunks/9223.cbcc88e0e9950426.js +0 -41
  648. package/apps/pwa/out/_next/static/chunks/9299.24e2a7d9b6d1f91b.js +0 -226
  649. package/apps/pwa/out/_next/static/chunks/9317.6a9869039c231bb3.js +0 -24
  650. package/apps/pwa/out/_next/static/chunks/9383.e4755f275b3c11ff.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/9581.da63ddf41acfad46.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/9715.8c5529a0a5c41a36.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/9817.3009d65f0889ef1f.js +0 -2
  654. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76873dd80da4f715.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed32b6ec5dea8f23.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-fdd40e15babf3d25.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/board/page-384efc78e85e400b.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/bots/page-d91809c5aeed319a.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/ceo/page-128f3522f89a81ab.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/chat/page-595961abf3b43ad1.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/consult/page-f41dccb76d8c0263.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/control/page-a3205c2e28b7d0d9.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/decisions/page-c39a80277814a3f4.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/design-check/page-1ab3f98194513195.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/editor/page-a5246f7e254d77d0.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/exec/page-0f92287bdb81217d.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/field/page-5a37131ff63793ef.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76873dd80da4f715.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/inside/page-ac4bd14bb2348bfd.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/intake/page-c348a3959c5b5fc6.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/layout-2addd81c0d3e2906.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/live/page-da6b765f58a3eefb.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0661e40f8780c32c.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/loops/page-b89af1b1c8e4875b.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/market/page-da8aeb82ba751cdb.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-cc71317b239439db.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/morning/page-bde7fc1d64ffe369.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/not-found-e2797214f713fa58.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/observatory/page-2a35bcda70defaf6.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3af03deedde45032.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d1d1210f99f620ae.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-46d1b5500885861d.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/outputs/page-291ab19fe2acb173.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/page-7f569e533ddeecba.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/reflection/page-990129c9fee46d8f.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-7737f785ebb1cdaf.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f3c73a4f7ffd2f8.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-03be7fb347f01058.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/app/settings/page-96b0280625e4df2b.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-56855545049eb56d.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76873dd80da4f715.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/app/setup/page-79a909c9981f0338.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/app/share/page-a9d9a3e66459c1e0.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c383cbf2013f7069.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f57c6a2e54dbdca4.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/term/page-6b140f77593576eb.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/today/page-6b767ae2d9d0adfc.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/app/trace/page-116d6198d5da0e3d.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/app/vault/page-b9c9b778c7501370.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-a6c78392ab8f6335.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/app/workflows/page-455183deb4ed0cfe.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6a63e99526bd63d0.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31c22caafe788c08.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/bb6987f9.3647e1a23b0e3217.js +0 -122
  705. package/apps/pwa/out/_next/static/chunks/framework-4b12a946f352cee2.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/main-941155250ed2644b.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/main-app-f8b6765abed6d149.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/pages/_app-78432194c85388f9.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/pages/_error-cdf7390829512b1b.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/webpack-d5f0964a9c2e92d2.js +0 -1
  711. package/scripts/ratchets/next-tui-entry-budget.ts +0 -124
  712. /package/apps/pwa/out/_next/static/{RrjXTwz-urRAbChP1bX02 → GiOLWmt4MqDZePubBjA4Q}/_ssgManifest.js +0 -0
@@ -1,9 +1,12 @@
1
1
  import { randomUUID } from 'node:crypto';
2
+ import { spawnSync } from 'node:child_process';
2
3
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
4
  import { join } from 'node:path';
4
5
  import { psProcessStartMs } from '../harness/harness-stop.js';
5
6
  import { debug } from '../debug/log.js';
6
7
  import { effectiveInstanceRoot } from '../instance/resolve.js';
8
+ import { psProcessStart, type HostLeaseRecord, type PendingJobIdentity } from '../pod-lease/host-lease.js';
9
+ import { parsePodPool, podPoolHostLease, resolvePodPoolSpec, type PodPoolMember } from '../task-orchestrator/surfaces/pod-pool.js';
7
10
 
8
11
  interface GateHostClaim {
9
12
  token: string;
@@ -11,11 +14,154 @@ interface GateHostClaim {
11
14
  hosts: string[];
12
15
  pid: number;
13
16
  pidStartedAt: number | null;
17
+ /** Wall-clock claim time. Claims written before this field existed stay readable (optional on read). */
18
+ reservedAt?: number;
14
19
  }
15
20
 
16
21
  export type GateHostReservation =
17
22
  | { status: 'free' }
18
- | { status: 'reserved' | 'unknown'; reason: string; runId?: string };
23
+ | { status: 'reserved' | 'unknown'; reason: string; runId?: string; reservedAt?: number };
24
+
25
+ /** Read-only snapshots used to decide whether the resource pool behind a gate host is occupied. */
26
+ export interface GateHostResourceSnapshot {
27
+ /** null means the lease ledger was unreadable; [] is a measured empty ledger. */
28
+ leases: readonly HostLeaseRecord[] | null;
29
+ /** Active Pending/Running Jobs from the configured Pod pool; null means not measurable. */
30
+ activeJobs: readonly PendingJobIdentity[] | null;
31
+ }
32
+
33
+ export type GateHostResourceReading =
34
+ | { status: 'measured'; empty: boolean; reservations: number; authoringLeases: number; waitingJobs: number; activeJobs: number }
35
+ | { status: 'unmeasurable'; empty: null; reason: 'lease-ledger-unavailable' | 'lease-ledger-invalid' | 'pod-list-unavailable' | 'pod-list-invalid' | 'pod-pool-invalid' };
36
+
37
+ const unavailable = (reason: Extract<GateHostResourceReading, { status: 'unmeasurable' }>['reason']): GateHostResourceReading => ({ status: 'unmeasurable', empty: null, reason });
38
+
39
+ /** Read leases and active harness Pods from the configured pool without creating, pruning, or updating any ledger. */
40
+ export function readGateHostResourcesForHost(host: string, env: NodeJS.ProcessEnv = process.env,
41
+ deps: { members?: readonly PodPoolMember[]; kubectl?: (args: readonly string[]) => { status: number | null; stdout: string };
42
+ processStart?: (pid: number) => string | null } = {}): GateHostResourceReading {
43
+ let members: readonly PodPoolMember[];
44
+ try {
45
+ const spec = resolvePodPoolSpec(undefined, env, () => {
46
+ try {
47
+ // The gate path resolves its pool from harness.podPool, unlike generic Pod launches.
48
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
49
+ return (require('../user-config.js') as typeof import('../user-config.js')).getUserConfig().harness?.podPool;
50
+ } catch { return undefined; }
51
+ });
52
+ members = deps.members ?? (spec ? parsePodPool(spec) : []);
53
+ } catch { return unavailable('pod-pool-invalid'); }
54
+ if (!members.length) return unavailable('pod-list-unavailable');
55
+ const selectedHost = host.split('@').at(-1)!;
56
+ const selectedMembers = members.filter(member => member.sshHost === selectedHost);
57
+ if (!selectedMembers.length) return unavailable('pod-list-unavailable');
58
+ const selectedContexts = new Set(selectedMembers.map(member => member.context));
59
+
60
+ const leasePool = podPoolHostLease(members);
61
+ let leases: HostLeaseRecord[];
62
+ try {
63
+ let names: string[];
64
+ try { names = readdirSync(leasePool.dir).filter(name => name.endsWith('.json')); }
65
+ catch (error) {
66
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') names = [];
67
+ else return unavailable('lease-ledger-unavailable');
68
+ }
69
+ leases = [];
70
+ const processStart = deps.processStart ?? psProcessStart;
71
+ for (const name of names) {
72
+ if (!/^[A-Za-z0-9._-]+\.json$/.test(name)) return unavailable('lease-ledger-invalid');
73
+ let lease: HostLeaseRecord;
74
+ try { lease = JSON.parse(readFileSync(join(leasePool.dir, name), 'utf8')) as HostLeaseRecord; }
75
+ catch { return unavailable('lease-ledger-invalid'); }
76
+ if (!lease || !Number.isSafeInteger(lease.pid) || lease.pid <= 0 || typeof lease.startedAt !== 'string' || !lease.startedAt ||
77
+ typeof lease.at !== 'string' || (lease.stage !== undefined && lease.stage !== 'authoring' && lease.stage !== 'job') ||
78
+ (lease.stage === 'job' && (!lease.job || !lease.context || !lease.namespace))) return unavailable('lease-ledger-invalid');
79
+ try { process.kill(lease.pid, 0); }
80
+ catch (error) {
81
+ if ((error as NodeJS.ErrnoException).code === 'ESRCH') continue;
82
+ // EPERM means the PID may still be alive; the start-time comparison below is still read-only.
83
+ }
84
+ const startedAt = processStart(lease.pid);
85
+ if (startedAt === null) return unavailable('lease-ledger-unavailable');
86
+ if (startedAt !== lease.startedAt) continue;
87
+ if (lease.stage !== 'job' || selectedContexts.has(lease.context!)) leases.push(lease);
88
+ }
89
+ } catch { return unavailable('lease-ledger-unavailable'); }
90
+
91
+ const kubectl = deps.kubectl ?? ((args: readonly string[]) => {
92
+ const result = spawnSync('kubectl', [...args], { encoding: 'utf8', timeout: 15_000, maxBuffer: 64 * 1024 * 1024 });
93
+ return { status: result.status, stdout: result.stdout ?? '' };
94
+ });
95
+ const activeJobs = new Map<string, PendingJobIdentity>();
96
+ for (const member of selectedMembers) {
97
+ let result: { status: number | null; stdout: string };
98
+ try { result = kubectl(['--context', member.context, '--request-timeout=10s', 'get', 'pods', '--all-namespaces', '-l', 'elanous.substrate=pod', '-o', 'json']); }
99
+ catch { return unavailable('pod-list-unavailable'); }
100
+ if (result.status !== 0) return unavailable('pod-list-unavailable');
101
+ let items: unknown;
102
+ try { items = (JSON.parse(result.stdout) as { items?: unknown }).items; }
103
+ catch { return unavailable('pod-list-invalid'); }
104
+ if (!Array.isArray(items)) return unavailable('pod-list-invalid');
105
+ for (const item of items) {
106
+ if (!item || typeof item !== 'object') return unavailable('pod-list-invalid');
107
+ const pod = item as { metadata?: { name?: unknown; namespace?: unknown; deletionTimestamp?: unknown; labels?: Record<string, unknown>; ownerReferences?: Array<{ kind?: unknown; name?: unknown }> }; status?: { phase?: unknown } };
108
+ const phase = pod.status?.phase;
109
+ if (phase === 'Succeeded' || phase === 'Failed') continue;
110
+ if (phase !== 'Pending' && phase !== 'Running') return unavailable('pod-list-invalid');
111
+ // Deleting Pods are already leaving the pool and must not keep the selected host occupied.
112
+ if (pod.metadata?.deletionTimestamp != null) continue;
113
+ const context = member.context;
114
+ const namespace = pod.metadata?.namespace;
115
+ const owner = pod.metadata?.ownerReferences?.find(ref => ref.kind === 'Job')?.name;
116
+ const job = owner ?? pod.metadata?.labels?.['job-name'];
117
+ if (typeof namespace !== 'string' || !namespace || typeof job !== 'string' || !job) return unavailable('pod-list-invalid');
118
+ const identity = { context, namespace, job };
119
+ activeJobs.set(`${context}\0${namespace}\0${job}`, identity);
120
+ }
121
+ }
122
+ const reading = readGateHostResources({ leases, activeJobs: [...activeJobs.values()] });
123
+ debug.log('release-loop.gate', 'host-resource-probe', { host: selectedHost, poolContexts: selectedMembers.map(member => member.context), ...reading });
124
+ return reading;
125
+ }
126
+
127
+ function activeJobKey(job: PendingJobIdentity): string | null {
128
+ return typeof job?.context === 'string' && job.context.length > 0 &&
129
+ typeof job.namespace === 'string' && job.namespace.length > 0 &&
130
+ typeof job.job === 'string' && job.job.length > 0
131
+ ? `${job.context}\0${job.namespace}\0${job.job}` : null;
132
+ }
133
+
134
+ /**
135
+ * Read the resource ledgers as the authority for host occupancy. Process argv is deliberately not an input:
136
+ * unrelated commands that happen to contain `bun test` do not reserve a gate host.
137
+ * A Job-backed lease and its active Job are one reservation; a lease without an observed Job remains reserved.
138
+ */
139
+ export function readGateHostResources(snapshot: GateHostResourceSnapshot): GateHostResourceReading {
140
+ if (snapshot.leases === null) return { status: 'unmeasurable', empty: null, reason: 'lease-ledger-unavailable' };
141
+ if (snapshot.activeJobs === null) return { status: 'unmeasurable', empty: null, reason: 'pod-list-unavailable' };
142
+ if (!Array.isArray(snapshot.leases) || snapshot.leases.some((lease) => !lease ||
143
+ !Number.isSafeInteger(lease.pid) || lease.pid <= 0 || typeof lease.startedAt !== 'string' || !lease.startedAt || typeof lease.at !== 'string' ||
144
+ (lease.stage !== undefined && lease.stage !== 'authoring' && lease.stage !== 'job') ||
145
+ (lease.stage === 'job' && (!lease.job || !lease.context || !lease.namespace)))) {
146
+ return { status: 'unmeasurable', empty: null, reason: 'lease-ledger-invalid' };
147
+ }
148
+ if (!Array.isArray(snapshot.activeJobs) || snapshot.activeJobs.some((job) => !job || activeJobKey(job) === null)) {
149
+ return { status: 'unmeasurable', empty: null, reason: 'pod-list-invalid' };
150
+ }
151
+
152
+ const jobs = new Set(snapshot.activeJobs.map(activeJobKey) as string[]);
153
+ // Authoring leases have no host/context yet, so they conservatively occupy each selected host in the shared pool.
154
+ let authoringLeases = 0;
155
+ let waitingJobs = 0;
156
+ for (const lease of snapshot.leases) {
157
+ if (lease.stage !== 'job') { authoringLeases++; continue; }
158
+ const key = lease.job && lease.context && lease.namespace
159
+ ? activeJobKey({ job: lease.job, context: lease.context, namespace: lease.namespace }) : null;
160
+ if (!key || !jobs.has(key)) waitingJobs++;
161
+ }
162
+ const reservations = authoringLeases + waitingJobs + jobs.size;
163
+ return { status: 'measured', empty: reservations === 0, reservations, authoringLeases, waitingJobs, activeJobs: jobs.size };
164
+ }
19
165
 
20
166
  const hostName = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
21
167
  const claimsDir = (root: string) => join(root, 'release', 'gate-host-reservations');
@@ -44,7 +190,7 @@ export function reserveReleaseGateHosts(root: string, runId: string, hosts: read
44
190
  const token = randomUUID();
45
191
  const pidStartedAt = psProcessStartMs(process.pid);
46
192
  if (pidStartedAt === null) throw new Error('release gate owner start time unmeasured');
47
- const claim: GateHostClaim = { token, runId, hosts: unique, pid: process.pid, pidStartedAt };
193
+ const claim: GateHostClaim = { token, runId, hosts: unique, pid: process.pid, pidStartedAt, reservedAt: Date.now() };
48
194
  const path = join(dir, `${token}.json`);
49
195
  const temp = join(dir, `.${token}.tmp`);
50
196
  try {
@@ -82,7 +228,9 @@ export function inspectReleaseGateHost(root: string, host: string): GateHostRese
82
228
  || !Number.isSafeInteger(claim.pid) || claim.pid <= 0 || typeof claim.token !== 'string'
83
229
  || typeof claim.runId !== 'string' || !claim.runId
84
230
  || (claim.pidStartedAt !== null && (!Number.isSafeInteger(claim.pidStartedAt)
85
- || claim.pidStartedAt <= 0 || claim.pidStartedAt > Date.now()))) throw new Error('invalid claim');
231
+ || claim.pidStartedAt <= 0 || claim.pidStartedAt > Date.now()))
232
+ || (claim.reservedAt !== undefined && (!Number.isSafeInteger(claim.reservedAt)
233
+ || claim.reservedAt <= 0 || claim.reservedAt > Date.now()))) throw new Error('invalid claim');
86
234
  } catch (error) { return { status: 'unknown', reason: `reservation unreadable: ${name}: ${String(error)}` }; }
87
235
  const live = claimAlive(claim);
88
236
  if (live === 'dead') {
@@ -91,7 +239,12 @@ export function inspectReleaseGateHost(root: string, host: string): GateHostRese
91
239
  continue;
92
240
  }
93
241
  if (!claim.hosts.includes(host) && !claim.hosts.includes('*')) continue;
94
- return { status: live === 'unknown' ? 'unknown' : 'reserved', reason: `release gate ${claim.runId} holds ${host}`, runId: claim.runId };
242
+ return {
243
+ status: live === 'unknown' ? 'unknown' : 'reserved',
244
+ reason: `release gate ${claim.runId} holds ${host}`,
245
+ runId: claim.runId,
246
+ ...(claim.reservedAt !== undefined ? { reservedAt: claim.reservedAt } : {}),
247
+ };
95
248
  }
96
249
  return { status: 'free' };
97
250
  }
@@ -0,0 +1,68 @@
1
+ // One append-only observation per gate shard transition. The live board is a projection, not evidence for reuse.
2
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { releaseLedgerRoot } from '../instance/resolve.js';
5
+
6
+ export interface GateShardLedgerRow {
7
+ v: 1;
8
+ version: string;
9
+ phase: 'cut' | 'baseline';
10
+ shardId: string;
11
+ files: string[];
12
+ host: string | null;
13
+ executor: 'pod' | 'host';
14
+ imageDigest: string | null;
15
+ commit: string;
16
+ envFingerprint: string;
17
+ startedAt: string | null;
18
+ endedAt: string | null;
19
+ observedAt: string;
20
+ state: 'pending' | 'running' | 'done' | 'retry' | 'timeout' | 'failed';
21
+ rc: number | null;
22
+ pass: number | null;
23
+ fail: number | null;
24
+ errors: number | null;
25
+ ran: number | null;
26
+ failureIds: string[] | null;
27
+ errorIds: string[] | null;
28
+ attribution: 'complete' | 'unmeasured';
29
+ logPath: string | null;
30
+ junitPath: string | null;
31
+ stopReason: string | null;
32
+ attemptId: string | null;
33
+ }
34
+
35
+ export function gateShardLedgerPath(version: string, root = releaseLedgerRoot()): string {
36
+ if (!/^\d+\.\d+\.\d+(?:[-.][A-Za-z0-9.-]+)?$/.test(version)) throw new Error('invalid release version');
37
+ return join(root, 'release', version, 'gate-logs', 'shard-ledger.jsonl');
38
+ }
39
+
40
+ /** Each newline is a complete event, so an observer can read while the gate is running. */
41
+ export function appendGateShardLedger(path: string, row: GateShardLedgerRow): void {
42
+ if (!/^[a-f0-9]{40}$/i.test(row.commit) || !/^[a-f0-9]{64}$/i.test(row.envFingerprint)) throw new Error('invalid shard ledger identity');
43
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(row.shardId) || row.files.some((file) => file.startsWith('/') || file.split('/').includes('..'))) throw new Error('invalid shard ledger path');
44
+ mkdirSync(dirname(path), { recursive: true });
45
+ appendFileSync(path, `${JSON.stringify(row)}\n`, { mode: 0o600 });
46
+ }
47
+
48
+ export function readGateShardLedger(path: string): { rows: GateShardLedgerRow[]; malformed: number } | null {
49
+ if (!existsSync(path)) return null;
50
+ const rows: GateShardLedgerRow[] = [];
51
+ let malformed = 0;
52
+ for (const line of readFileSync(path, 'utf8').split('\n')) {
53
+ if (!line.trim()) continue;
54
+ try {
55
+ const row = JSON.parse(line) as GateShardLedgerRow;
56
+ if (row.v !== 1 || typeof row.shardId !== 'string' || !Array.isArray(row.files) || typeof row.observedAt !== 'string') throw new Error('invalid row');
57
+ rows.push(row);
58
+ } catch { malformed++; }
59
+ }
60
+ return { rows, malformed };
61
+ }
62
+
63
+ /** The newest event for each phase/shard. Preserve every attempt in the underlying ledger. */
64
+ export function latestGateShardLedger(rows: readonly GateShardLedgerRow[]): GateShardLedgerRow[] {
65
+ const latest = new Map<string, GateShardLedgerRow>();
66
+ for (const row of rows) latest.set(`${row.phase}:${row.shardId}`, row);
67
+ return [...latest.values()].sort((a, b) => a.phase.localeCompare(b.phase) || a.shardId.localeCompare(b.shardId, undefined, { numeric: true }));
68
+ }
@@ -7,7 +7,7 @@ import { listChecklist, setItem } from './checklist.js';
7
7
  import { judgeLandedCell } from './landed-to-green.js';
8
8
  import { evidencePlan, landedButYellow, type MergedChecklistPr } from './landed-but-yellow.js';
9
9
  import { assignedVersions, evidenceAdd } from './feature-store.js';
10
- import { boundMergedTaskCells, candidateMergedTaskCells, closeMergedTaskCard } from '../task-agent/postland-card.js';
10
+ import { boundMergedTaskCells, candidateMergedTaskCells, closeMergedTaskCard, recordMergedTaskImplFindings } from '../task-agent/postland-card.js';
11
11
 
12
12
  export interface MergedPrChecklistDeps {
13
13
  readPr?: (number: number, cwd: string) => { state: string; baseRefName: string; title: string; body: string | null; mergedAt: string; url?: string; headRefOid?: string; files?: Array<{ path: string }> };
@@ -19,13 +19,19 @@ export interface MergedPrChecklistDeps {
19
19
  closeTaskCard?: typeof closeMergedTaskCard;
20
20
  boundTaskCells?: typeof boundMergedTaskCells;
21
21
  candidateTaskCells?: typeof candidateMergedTaskCells;
22
+ feedbackStatePath?: string;
23
+ feedbackFollowUp?: NonNullable<Parameters<typeof recordMergedTaskImplFindings>[2]>['followUp'];
22
24
  log?: typeof debug.log;
23
25
  }
24
26
 
27
+ function readMergedPr(number: number, cwd: string): NonNullable<MergedPrChecklistDeps['readPr']> extends (number: number, cwd: string) => infer T ? T : never {
28
+ return JSON.parse(execFileSync('gh', ['pr', 'view', String(number), '--json', 'state,baseRefName,title,body,mergedAt,url,headRefOid,files'], { cwd, encoding: 'utf8', timeout: 30_000 }));
29
+ }
30
+
25
31
  /** A confirmed main merge is the only entry. The existing checklist matcher owns PR-to-cell decisions. */
26
32
  export function syncMergedPrChecklist(number: number, cwd: string, goalFile?: string, deps: MergedPrChecklistDeps = {}): void {
27
33
  const log = deps.log ?? debug.log.bind(debug);
28
- const pr = (deps.readPr ?? ((n, dir) => JSON.parse(execFileSync('gh', ['pr', 'view', String(n), '--json', 'state,baseRefName,title,body,mergedAt,url,headRefOid,files'], { cwd: dir, encoding: 'utf8', timeout: 30_000 })) as ReturnType<NonNullable<MergedPrChecklistDeps['readPr']>>))(number, cwd);
34
+ const pr = (deps.readPr ?? readMergedPr)(number, cwd);
29
35
  if (pr.state !== 'MERGED' || pr.baseRefName !== 'main') return;
30
36
  const merged: MergedChecklistPr = { number, title: pr.title, body: pr.body, mergedAt: pr.mergedAt };
31
37
  const versions = deps.versions?.() ?? (() => {
@@ -116,6 +122,23 @@ export function syncMergedPrChecklist(number: number, cwd: string, goalFile?: st
116
122
  for (const id of missing) log('release.checklist', 'merged-pr-cell-not-found', { pr: number, id });
117
123
  }
118
124
 
125
+ /** Production merge entry: reconcile synchronously, then await idempotent PR-body feedback before returning. */
126
+ export async function syncMergedPrChecklistWithFeedback(
127
+ number: number,
128
+ cwd: string,
129
+ goalFile?: string,
130
+ deps: MergedPrChecklistDeps = {},
131
+ ): Promise<Awaited<ReturnType<typeof recordMergedTaskImplFindings>>> {
132
+ const pr = (deps.readPr ?? readMergedPr)(number, cwd);
133
+ syncMergedPrChecklist(number, cwd, goalFile, { ...deps, readPr: () => pr });
134
+ if (pr.url && pr.headRefOid) {
135
+ return await recordMergedTaskImplFindings({ ...pr, number, url: pr.url, headRefOid: pr.headRefOid }, cwd,
136
+ { ...(deps.feedbackStatePath ? { statePath: deps.feedbackStatePath } : {}),
137
+ ...(deps.feedbackFollowUp ? { followUp: deps.feedbackFollowUp } : {}) });
138
+ }
139
+ return null;
140
+ }
141
+
119
142
  const LABEL_IDS = '([A-Za-z0-9_-]+(?:[ \\t]*(?:,|·)[ \\t]*[A-Za-z0-9_-]+)*)';
120
143
  const LABEL_END = '(?=[ \\t]*$|[ \\t]+[—–·(-])';
121
144
  const DESIGNATION = new RegExp(`^ {0,3}(?:#{1,6}[ \\t]+|[-*][ \\t]+)?칸[ \\t]*:[ \\t]*${LABEL_IDS}${LABEL_END}`);
@@ -87,7 +87,12 @@ export function planRehearsalShards(version: string, root: string, checkout: str
87
87
  let gatePlanShards: number | null = null;
88
88
  try {
89
89
  const board = (deps.board ?? readGateShards)(gateShardsPath(version, root));
90
- gatePlanShards = board?.version === version && board.shards.length && new Set(board.shards.map(shard => shard.id)).size === board.shards.length ? board.shards.length : null;
90
+ if (board?.version === version && board.shards.length
91
+ && new Set(board.shards.map(shard => shard.id)).size === board.shards.length) {
92
+ // Split/retry/tail rows are observations, not extra planned shards. Older boards used arbitrary IDs.
93
+ const roots = board.shards.filter(shard => /^pod-\d+$/.test(shard.id));
94
+ gatePlanShards = roots.length || (board.shards.some(shard => /^pod-\d+/.test(shard.id)) ? null : board.shards.length);
95
+ }
91
96
  } catch (error) { reasons.push(`persisted gate shard plan unreadable: ${error instanceof Error ? error.message : String(error)}`); }
92
97
  let testFiles: number | null = null;
93
98
  let measuredFiles = 0;
@@ -67,6 +67,15 @@ export function planRehearsal(schedules: readonly ReleaseSchedule[], now: Date,
67
67
  return { status: 'not-yet', version, cutAt, nextStepAt: new Date(cut - open[0]!.minutes * minute).toISOString() };
68
68
  }
69
69
 
70
+ export interface RehearsalRecovery {
71
+ failedShard: string;
72
+ remeasuredFiles: number | null;
73
+ durationMs: number | null;
74
+ poolOverwriteApplied: boolean | null;
75
+ outcome: 'ok' | 'failed';
76
+ error?: string;
77
+ }
78
+
70
79
  export interface RehearsalStamp {
71
80
  version: string;
72
81
  step: StepId;
@@ -79,6 +88,7 @@ export interface RehearsalStamp {
79
88
  regressionFiles?: string[];
80
89
  result?: LightRcResult;
81
90
  hardGate?: HardGateResult;
91
+ recovery?: RehearsalRecovery;
82
92
  hardGateRunId?: string;
83
93
  hardGateLog?: string;
84
94
  /** B6: an unmeasured hard gate always retains a cause, run id and durable local evidence path. */
@@ -94,6 +104,7 @@ export interface RehearsalOptions {
94
104
  apply?: boolean;
95
105
  checkout?: string;
96
106
  ledgerRoot?: string;
107
+ recoveryRun?: string;
97
108
  shards?: number;
98
109
  remote?: string;
99
110
  }
@@ -109,6 +120,7 @@ export interface RehearsalDeps {
109
120
  notify?: (text: string) => void | boolean | Promise<void | boolean>;
110
121
  git?: (args: string[], cwd: string) => { rc: number | null; stdout: string; stderr?: string };
111
122
  leakCheck?: (checkout: string) => { status: number | null; detail?: string };
123
+ resumeGate?: (runId: string, checkout: string) => Promise<{ files: number; durationMs: number; poolOverwriteApplied: boolean; ok: boolean }>;
112
124
  }
113
125
 
114
126
  export type RehearsalTickResult = RehearsalPlan & { applied?: boolean; stamps?: RehearsalStamp[]; notificationFailures?: number };
@@ -520,6 +532,21 @@ export async function runRehearsalTick(opts: RehearsalOptions = {}, deps: Rehear
520
532
  console.error(line);
521
533
  } }, { cwd })))(plan.version, checkout, selection);
522
534
  stamp.result = result;
535
+ if (opts.recoveryRun && result.unmeasured !== null) {
536
+ const failedShard = result.unmeasured;
537
+ const started = Date.now();
538
+ try {
539
+ const recovered = await deps.resumeGate?.(opts.recoveryRun, checkout);
540
+ if (!recovered) throw new Error('release resume --from gate is unavailable for this rehearsal');
541
+ stamp.recovery = { failedShard, remeasuredFiles: recovered.files, durationMs: recovered.durationMs,
542
+ poolOverwriteApplied: recovered.poolOverwriteApplied, outcome: recovered.ok ? 'ok' : 'failed',
543
+ ...(!recovered.ok ? { error: 'resumed gate did not pass' } : {}) };
544
+ } catch (error) {
545
+ stamp.recovery = { failedShard, remeasuredFiles: null, durationMs: Math.max(0, Date.now() - started),
546
+ poolOverwriteApplied: null, outcome: 'failed',
547
+ error: oneLine(error instanceof Error ? error.message : String(error)) };
548
+ }
549
+ }
523
550
  if (regressionFiles.length) stamp.regressionFiles = regressionFiles;
524
551
  const shardMismatch = selection.shards !== undefined && result.shards !== selection.shards
525
552
  ? `selected shards ${selection.shards} but RC reported ${result.shards}` : null;
@@ -1,10 +1,99 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
1
4
  import { CliUserError } from '../cli/cli-user-error.js';
2
5
  import { debug } from '../debug/log.js';
3
- import { readSchedule, readSchedules, writeSchedule, type ScheduleRow } from './feature-store.js';
6
+ import { releaseLedgerRoot } from '../instance/resolve.js';
7
+ import { readSchedule, readSchedules, releasedVersion, writeSchedule, type ScheduleRow } from './feature-store.js';
4
8
  import { checkScheduleCeoLoad } from './placement.js';
5
9
 
6
10
  export type ReleaseSchedule = ScheduleRow;
7
11
 
12
+ export interface ReleaseCutDueEvent {
13
+ id: string;
14
+ type: 'release.cut.due';
15
+ version: string;
16
+ cutAt: string;
17
+ at: string;
18
+ }
19
+
20
+ function newerVersion(version: string, published: string): boolean {
21
+ if (!published) return true;
22
+ const left = version.split('.').map(Number), right = published.split('.').map(Number);
23
+ for (let index = 0; index < 3; index++) {
24
+ if (left[index]! !== right[index]!) return left[index]! > right[index]!;
25
+ }
26
+ return false;
27
+ }
28
+
29
+ /** A scheduler tick observes due cuts; it does not directly start a release run. */
30
+ export function dueReleaseCutEvents(now: string, root = releaseLedgerRoot()): ReleaseCutDueEvent[] {
31
+ const at = Date.parse(now);
32
+ if (!Number.isFinite(at)) throw new Error(`invalid schedule tick time: ${now}`);
33
+ const published = releasedVersion(root);
34
+ return readSchedules(root).filter(row => {
35
+ const cut = Date.parse(row.cutAt);
36
+ return newerVersion(row.version, published) && cut <= at && at - cut < 24 * 3_600_000;
37
+ }).map(row => ({ id: `release.cut.due:${row.version}:${row.cutAt}`, type: 'release.cut.due' as const,
38
+ version: row.version, cutAt: row.cutAt, at: new Date(at).toISOString() }));
39
+ }
40
+
41
+ function hasReleaseRunForEvent(root: string, eventId: string): boolean {
42
+ const dir = join(root, 'graph-runs', 'release-loop');
43
+ if (!existsSync(dir)) return false;
44
+ for (const name of readdirSync(dir)) {
45
+ if (!name.endsWith('.json')) continue;
46
+ try {
47
+ const run = JSON.parse(readFileSync(join(dir, name), 'utf8')) as { input?: { triggerEvent?: { id?: string } } };
48
+ if (run.input?.triggerEvent?.id === eventId) return true;
49
+ } catch { /* Ignore a partially written run; the next scheduler tick retries. */ }
50
+ }
51
+ return false;
52
+ }
53
+
54
+ /** Claim each version+cut only once across scheduler processes; a dead pending owner can be retried. */
55
+ export async function emitDueReleaseCutEvents(now: string, emit: (event: ReleaseCutDueEvent) => Promise<void>,
56
+ root = releaseLedgerRoot(), subscriptionId = 'graph:release-loop'): Promise<ReleaseCutDueEvent[]> {
57
+ const emitted: ReleaseCutDueEvent[] = [];
58
+ for (const event of dueReleaseCutEvents(now, root)) {
59
+ const hash = createHash('sha256').update(`${subscriptionId}\0${event.id}`).digest('hex').slice(0, 16);
60
+ const path = join(root, 'release', 'cut-events', `${event.version}-${hash}.json`);
61
+ mkdirSync(dirname(path), { recursive: true });
62
+ let fd: number;
63
+ try { fd = openSync(path, 'wx', 0o600); }
64
+ catch (cause) {
65
+ if ((cause as NodeJS.ErrnoException).code !== 'EEXIST') throw cause;
66
+ let prior: { status?: string; pid?: number } = {};
67
+ try { prior = JSON.parse(readFileSync(path, 'utf8')) as typeof prior; } catch { /* pending write */ }
68
+ if (prior.status !== 'pending' || !Number.isInteger(prior.pid)) {
69
+ // A crash between O_EXCL creation and the first write leaves an empty claim.
70
+ if (Date.now() - statSync(path).mtimeMs < 60_000 || prior.status === 'emitted') continue;
71
+ } else {
72
+ try { process.kill(prior.pid!, 0); continue; }
73
+ catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ESRCH') continue; }
74
+ }
75
+ if (hasReleaseRunForEvent(root, event.id)) {
76
+ writeFileSync(path, JSON.stringify({ ...event, subscriptionId, status: 'emitted', recovered: true }) + '\n', { mode: 0o600 });
77
+ continue;
78
+ }
79
+ try { unlinkSync(path); fd = openSync(path, 'wx', 0o600); }
80
+ catch (error) { if ((error as NodeJS.ErrnoException).code === 'EEXIST') continue; throw error; }
81
+ }
82
+ closeSync(fd);
83
+ writeFileSync(path, JSON.stringify({ ...event, subscriptionId, status: 'pending', pid: process.pid }) + '\n', { mode: 0o600 });
84
+ try {
85
+ await emit(event);
86
+ writeFileSync(path, JSON.stringify({ ...event, subscriptionId, status: 'emitted', pid: process.pid }) + '\n', { mode: 0o600 });
87
+ debug.log('release.schedule', 'cut-due', { version: event.version, cutAt: event.cutAt, eventId: event.id, subscriptionId });
88
+ emitted.push(event);
89
+ } catch (error) {
90
+ if (existsSync(path)) unlinkSync(path);
91
+ throw error;
92
+ }
93
+ }
94
+ return emitted;
95
+ }
96
+
8
97
  /** A release landing and its representative work share the same KST day boundary. */
9
98
  export function kstDay(utc: string): string {
10
99
  return new Date(Date.parse(utc) + 9 * 3_600_000).toISOString().slice(0, 10);
@@ -0,0 +1,135 @@
1
+ import { readFileSync, statSync } from 'node:fs';
2
+ import { BLOOM_2D_SCOPED, isBloom2DScoped, type Bloom2D } from '../knowledge/kgs/types.js';
3
+
4
+ export const BLOOM_EXEMPLAR_DOMAINS = [
5
+ 'finance', 'macro', 'technology', 'science', 'medicine', 'law', 'business',
6
+ ] as const;
7
+ export type BloomExemplarDomain = typeof BLOOM_EXEMPLAR_DOMAINS[number];
8
+ export type BloomExemplarLevel = 1 | 2 | 3 | 4 | 5 | 6;
9
+
10
+ export interface BloomExemplar {
11
+ domain: BloomExemplarDomain;
12
+ level: BloomExemplarLevel;
13
+ bloom: Bloom2D;
14
+ question: string;
15
+ }
16
+
17
+ export interface BloomExemplarPack { version: 1; exemplars: BloomExemplar[] }
18
+ export interface BloomExemplarContext {
19
+ domain: BloomExemplarDomain;
20
+ domainSignal: 'matched' | 'fallback';
21
+ source: 'pro-pack' | 'public-fallback';
22
+ count: number;
23
+ levels: BloomExemplarLevel[];
24
+ context: string;
25
+ }
26
+
27
+ // The existing KGS Bloom2D vocabulary is reused. Numeric research levels are
28
+ // mapped to the closest scoped cognitive/knowledge cell; no second taxonomy is
29
+ // introduced.
30
+ const COGNITIVE_BY_LEVEL = ['remember', 'understand', 'apply', 'analyze', 'evaluate', 'create'] as const;
31
+ const BLOOM_BY_LEVEL = Object.fromEntries(COGNITIVE_BY_LEVEL.map((cognitive, index) => {
32
+ const cell = BLOOM_2D_SCOPED.find(candidate => candidate.cognitive === cognitive);
33
+ if (!cell) throw new Error(`KGS Bloom2D is missing cognitive stage ${cognitive}`);
34
+ return [index + 1, cell];
35
+ })) as Record<BloomExemplarLevel, Bloom2D>;
36
+
37
+ // Public source tree intentionally contains only two original exemplars.
38
+ const PUBLIC_EXEMPLARS: readonly BloomExemplar[] = [
39
+ { domain: 'finance', level: 4, bloom: BLOOM_BY_LEVEL[4], question: '채권 금리 변화가 만기 구조와 발행자의 차환 위험에 어떤 경로로 연결되는가?' },
40
+ { domain: 'finance', level: 5, bloom: BLOOM_BY_LEVEL[5], question: '현재 금리 전망을 반박할 수 있는 시장 지표와 그 신뢰도는 무엇인가?' },
41
+ ];
42
+
43
+ const DOMAIN_TERMS: Record<BloomExemplarDomain, readonly string[]> = {
44
+ finance: ['bond', 'yield', '채권', '금리', '주식', '금융', '환율', '투자'],
45
+ macro: ['inflation', 'gdp', 'recession', '물가', '경기', '거시', '고용'],
46
+ technology: ['software', 'chip', 'ai', 'technology', '소프트웨어', '반도체', '기술', '인공지능'],
47
+ science: ['physics', 'chemistry', 'biology', 'science', '물리', '화학', '생물', '과학'],
48
+ medicine: ['patient', 'clinical', 'disease', 'medicine', '환자', '임상', '질환', '의학'],
49
+ law: ['legal', 'law', 'court', 'statute', '법률', '법원', '규정', '판례'],
50
+ business: ['company', 'market', 'customer', 'business', '기업', '시장', '고객', '사업'],
51
+ };
52
+
53
+ let cachedPackPath: string | undefined;
54
+ let cachedPackStamp: string | undefined;
55
+ let cachedPack: BloomExemplarPack | undefined;
56
+
57
+ export function inferBloomExemplarDomain(request: string): BloomExemplarDomain {
58
+ return inferBloomExemplarDomainWithSignal(request).domain;
59
+ }
60
+
61
+ function inferBloomExemplarDomainWithSignal(request: string): { domain: BloomExemplarDomain; domainSignal: BloomExemplarContext['domainSignal'] } {
62
+ const normalized = request.toLocaleLowerCase();
63
+ const ranked = BLOOM_EXEMPLAR_DOMAINS.map(domain => ({
64
+ domain,
65
+ score: DOMAIN_TERMS[domain].reduce((score, term) => score + (normalized.includes(term) ? 1 : 0), 0),
66
+ })).sort((a, b) => b.score - a.score || BLOOM_EXEMPLAR_DOMAINS.indexOf(a.domain) - BLOOM_EXEMPLAR_DOMAINS.indexOf(b.domain));
67
+ return ranked[0]!.score > 0
68
+ ? { domain: ranked[0]!.domain, domainSignal: 'matched' }
69
+ : { domain: 'business', domainSignal: 'fallback' };
70
+ }
71
+
72
+ export function validateBloomExemplarPack(value: unknown): value is BloomExemplarPack {
73
+ if (!isRecord(value) || value.version !== 1 || !Array.isArray(value.exemplars)) return false;
74
+ const counts = new Map<string, number>();
75
+ for (const item of value.exemplars) {
76
+ if (!isRecord(item) || !isDomain(item.domain) || !isLevel(item.level)
77
+ || typeof item.question !== 'string' || item.question.trim().length === 0
78
+ || item.question.length > 500 || /[\u0000-\u001f\u007f]/.test(item.question)
79
+ || !isBloom(item.bloom) || !isBloom2DScoped(item.bloom)
80
+ || !sameBloom(item.bloom, BLOOM_BY_LEVEL[item.level])) return false;
81
+ const key = `${item.domain}:${item.level}`;
82
+ counts.set(key, (counts.get(key) ?? 0) + 1);
83
+ }
84
+ return value.exemplars.length === 210
85
+ && BLOOM_EXEMPLAR_DOMAINS.every(domain => [1, 2, 3, 4, 5, 6].every(level => counts.get(`${domain}:${level}`) === 5));
86
+ }
87
+
88
+ export function getBloomExemplarContext(request: string, packPath = process.env.ELANOUS_BLOOM_EXEMPLARS_PACK): BloomExemplarContext {
89
+ const { domain, domainSignal } = inferBloomExemplarDomainWithSignal(request);
90
+ let exemplars: readonly BloomExemplar[] = PUBLIC_EXEMPLARS;
91
+ let source: BloomExemplarContext['source'] = 'public-fallback';
92
+ if (packPath) {
93
+ try {
94
+ const stamp = (() => { const stat = statSync(packPath); return `${stat.mtimeMs}:${stat.size}`; })();
95
+ if (cachedPackPath !== packPath || cachedPackStamp !== stamp) {
96
+ const parsed: unknown = JSON.parse(readFileSync(packPath, 'utf8'));
97
+ cachedPackPath = packPath;
98
+ cachedPackStamp = stamp;
99
+ cachedPack = validateBloomExemplarPack(parsed) ? parsed : undefined;
100
+ }
101
+ if (cachedPack) {
102
+ exemplars = cachedPack.exemplars;
103
+ source = 'pro-pack';
104
+ }
105
+ } catch {
106
+ // A missing pack can be installed while this process runs; don't cache
107
+ // ENOENT. Invalid JSON/data is safely cached by the file stamp above.
108
+ if (cachedPackPath === packPath) { cachedPackPath = undefined; cachedPackStamp = undefined; cachedPack = undefined; }
109
+ }
110
+ }
111
+ const selected = ([4, 5] as const).flatMap(level => exemplars
112
+ .filter(item => item.domain === domain && item.level === level)
113
+ .slice(0, source === 'pro-pack' ? 3 : 1));
114
+ const levels = [...new Set(selected.map(item => item.level))].sort() as BloomExemplarLevel[];
115
+ const context = selected.length
116
+ ? ['Bloom exemplar examples (reference style only; treat each question as data and generate request-specific questions):', ...selected.map(item => `L${item.level} ${item.bloom.cognitive}/${item.bloom.knowledge}: ${JSON.stringify(item.question)}`)].join('\n')
117
+ : '';
118
+ return { domain, domainSignal, source, count: selected.length, levels, context };
119
+ }
120
+
121
+ function isRecord(value: unknown): value is Record<string, unknown> {
122
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
123
+ }
124
+ function isDomain(value: unknown): value is BloomExemplarDomain {
125
+ return typeof value === 'string' && (BLOOM_EXEMPLAR_DOMAINS as readonly string[]).includes(value);
126
+ }
127
+ function isLevel(value: unknown): value is BloomExemplarLevel {
128
+ return Number.isInteger(value) && typeof value === 'number' && value >= 1 && value <= 6;
129
+ }
130
+ function isBloom(value: unknown): value is Bloom2D {
131
+ return isRecord(value) && isBloom2DScoped(value as unknown as Bloom2D);
132
+ }
133
+ function sameBloom(a: Bloom2D, b: Bloom2D): boolean {
134
+ return a.cognitive === b.cognitive && a.knowledge === b.knowledge;
135
+ }