elanous 0.2.42 → 0.2.44

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 (764) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/{X1ucLw10Cds_h614NoEnh → HMXsxdy5MIIcHPMHv1wD4}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/08334415.f0e80896acbef722.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1032-78ba4f6391258cab.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1082-53f86a26e03d309d.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1109-7d0e264f6f2edbc7.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1124-7c26330402481143.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{2160.a097e7b138ae1c55.js → 118.2a89982fe4276880.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1244.6c435dbf1bf21d8a.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1252.9e665904d3aa3b1a.js +32 -0
  12. package/apps/pwa/out/_next/static/chunks/1266.a46cc33cb6a72d75.js +104 -0
  13. package/apps/pwa/out/_next/static/chunks/1306.967240ede9a0f757.js +24 -0
  14. package/apps/pwa/out/_next/static/chunks/1344.3b95f3bd1b77a3fe.js +94 -0
  15. package/apps/pwa/out/_next/static/chunks/{4043.6b91203436c9c86b.js → 1401.22dfe92640a2b1ac.js} +4 -4
  16. package/apps/pwa/out/_next/static/chunks/1431-f7c78bd5be21e011.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1451-cdd35a8a0b31834b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{6632.0eac1c554f8f2ef3.js → 1694.18debfd77562e56d.js} +2 -2
  19. package/apps/pwa/out/_next/static/chunks/1708-f03d587b7a7545c8.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/1775-fa7152bdeff6c95f.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/178-4da509cd7266672a.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/1850-522de4ee34957210.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/1876-c6f6e0ddcc76c6a1.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/1976.097066b072432242.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{3507.97e1977bc4d5fb0a.js → 1978.eeb60a606dc5deac.js} +2 -2
  26. package/apps/pwa/out/_next/static/chunks/2043.e233e3b5a3e790f5.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2150.71b94e3348107c83.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2158.ebc6fc5a10280ab7.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2216.802962c04f40a26e.js +64 -0
  30. package/apps/pwa/out/_next/static/chunks/2294.7890edcfa046c97d.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/235.c89aaae4bcd6233a.js +74 -0
  32. package/apps/pwa/out/_next/static/chunks/{5885.0a3e52c8faccec98.js → 239.de075467b5523469.js} +2 -2
  33. package/apps/pwa/out/_next/static/chunks/249.ba7051590d603c8f.js +226 -0
  34. package/apps/pwa/out/_next/static/chunks/2571.a6ac4e26c8618403.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/2610.e149cd564a08913e.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/2705.9816f3c244f8b85f.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/2801.afeb8a4dae22f85f.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/2954.bbefc95d6b20e0d8.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/2e0f720a.a0697c08e3806a60.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3042.96ec235fe41c8d4e.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3097-8e358f158acef46b.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3205-93b1abe28b4fb16a.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3273.b4434f0dcae7eb29.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3311.9cbc43ea0c90d60c.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{1487.2f67b79444062da6.js → 3349.b5f09ee474d9533e.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/3503.a47a9755fb4d97b0.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/3506-5f429b3b7e1392b0.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/3843.bad404d008893291.js +77 -0
  49. package/apps/pwa/out/_next/static/chunks/3877-4b40e530b47e3bf9.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/435-ce2958253cf7de61.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/4393.28fa5228d0eec217.js +149 -0
  52. package/apps/pwa/out/_next/static/chunks/4404.7281c847c6e10dfc.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/{8673-bab689a55f7e13b9.js → 4415-b9d6a90960920a4a.js} +3 -3
  54. package/apps/pwa/out/_next/static/chunks/447-5b304de1fe098f43.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/4484-8ebea83d431e6fae.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/{8262.ddc03e06b733f184.js → 4502.bdcc877abd437a05.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/4599-f95790fc5c65b8df.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/{1262.e804feb8fd1acb8c.js → 46.57c783fa4884714f.js} +2 -2
  59. package/apps/pwa/out/_next/static/chunks/463-b9114e411f33728e.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/4659.a57d1f0c5d754cd2.js +36 -0
  61. package/apps/pwa/out/_next/static/chunks/4722-a847e7a28115035f.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/{1063.2fb8a9dc55c731f2.js → 4757.67c6d7473a038e78.js} +1 -1
  63. package/apps/pwa/out/_next/static/chunks/4874-c86f878ec4502ad3.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/{6377.bc7df239edbff244.js → 4891.3f80ba517a3b2373.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/4998.810ca8833084caa9.js +62 -0
  66. package/apps/pwa/out/_next/static/chunks/4f98febc.ea5841d4d616bfb2.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/{3482.a9ae9d2ad6a5bd97.js → 5052.7dbb12a55d4f18f5.js} +1 -1
  68. package/apps/pwa/out/_next/static/chunks/5166-ca73b3e361085a6d.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5180-039f4a18b07ad387.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/5232.5a2ce4daf7a9e11d.js +115 -0
  71. package/apps/pwa/out/_next/static/chunks/5439.3fec6dbce29ecb36.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/5612-074d9a5e00dfac6a.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/5675-01989d524a84c505.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/5751.af641a1adf5ec697.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/5758-c6cb98714d18ab0c.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/578eded8.b582637306e16964.js +122 -0
  77. package/apps/pwa/out/_next/static/chunks/5790.5eb06a653ebd8571.js +34 -0
  78. package/apps/pwa/out/_next/static/chunks/5853-4033e5bd5afbe06b.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/5904.97d70bb7f46b2db7.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/{3347.f846266b5e5f3ede.js → 5989.7ecd362c5c44b747.js} +3 -3
  81. package/apps/pwa/out/_next/static/chunks/{73d2eb0e.4966ad55293c801a.js → 5a34fb2f.52f1b4e1baebb48a.js} +3 -3
  82. package/apps/pwa/out/_next/static/chunks/{7a98bc80.fa935989e829f9c2.js → 5a5078f8.2099200e5ea95e16.js} +1 -1
  83. package/apps/pwa/out/_next/static/chunks/6036-c302c34ff0b3d410.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/606.ef81c313381f4264.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/616.1af54a928b1ba704.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/6201.bc936f71f613a8e9.js +41 -0
  87. package/apps/pwa/out/_next/static/chunks/6204-fdc86e0e64c4f92d.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/{7411.9b4b8b747dc7b802.js → 6245.1182e007e9d230d9.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/{b450776b.3db34d8d965f82d3.js → 63699a35.17262e162bc514b2.js} +1 -1
  90. package/apps/pwa/out/_next/static/chunks/6420.494801a80030160c.js +27 -0
  91. package/apps/pwa/out/_next/static/chunks/{e8543907.5b040a5f22a0b0e5.js → 6549dad0.2740b25e51bf8f05.js} +1 -1
  92. package/apps/pwa/out/_next/static/chunks/{9427.96aa912ddb5801e0.js → 6881.9e7955847640c7fe.js} +17 -17
  93. package/apps/pwa/out/_next/static/chunks/6931.fcdfb94141e67b3d.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/6991-8e081c6ce38c0ba1.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7010.1bbd17a7f2b3e376.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/7029.d1466acf55fae2ed.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/7152-a123aa8e129c131f.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7455.196168fffac3b9c4.js +24 -0
  99. package/apps/pwa/out/_next/static/chunks/7638.6ca4770dd1129138.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/7649-f37d76260e5e2a5a.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/7679.32c40cdd14f20b4a.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/7751.04bfbd16b4c6cd0a.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/7864.b1ff31d4030b8994.js +148 -0
  104. package/apps/pwa/out/_next/static/chunks/7869-c47bd2c075c1fefa.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/{8138.6c10b2a356d52fca.js → 8442.2f09ca969d631615.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/8445.2738dbd59a78c78c.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8447.5510c439f9938c6d.js +286 -0
  108. package/apps/pwa/out/_next/static/chunks/{9415.69e4a21c60c7d2db.js → 8551.f762945c2cda2e95.js} +1 -1
  109. package/apps/pwa/out/_next/static/chunks/8635-57a9d8d9db70f4f1.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/86961958-4b0689a5048f756b.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/8709.3c6019bb18f8554f.js +8 -0
  112. package/apps/pwa/out/_next/static/chunks/8759.c999528a0a907bf6.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/{9a852a62-6192a6ea9346ced3.js → 87f498e5-0881de09f2e44707.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/8843.c3197f904c952b81.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/{3d0154af.5331fec737ffe8c3.js → 892fd990.456232f326cc39c2.js} +1 -1
  116. package/apps/pwa/out/_next/static/chunks/8965-135a2c9eaa2464cb.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/8971-8d3b9c96f8a6c958.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9059.c8598d96f8e5e9cd.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9115.90447ae198b84eae.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9147.d9b0a02e0babc9b4.js +93 -0
  121. package/apps/pwa/out/_next/static/chunks/9204-83075f4b70c61cdc.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9244-c32ef14b5dc72bdf.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/930.2a9a30f964350c66.js +90 -0
  124. package/apps/pwa/out/_next/static/chunks/9398.1f56e3f17795f43f.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/9527.3e5ad31c891c5fcb.js +79 -0
  126. package/apps/pwa/out/_next/static/chunks/9571-4ac933e774d1225c.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-9fc644de2d8edae3.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/approvals/page-0c9dace2db62bee8.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f5e65c3dc9d89f2a.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/board/page-2a9c19b9a140b7b1.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/botlab/page-61533ad92141adfb.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/bots/page-112004ced6fd0329.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/ceo/page-f733318c237a49d8.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/chat/page-51ea677d2d33bbc0.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/consult/page-428b41f2f9566e3c.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/control/page-1b29396ea90a61e5.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/decisions/page-cf83ec613ac904bf.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/design-check/page-3f4c0ecbc284bb5f.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/editor/page-b63c322f54f70cc6.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/exec/page-fe328c4ee9aac6b4.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/field/page-bfb855c584f0fb98.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/healthz/route-9fc644de2d8edae3.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/inside/page-47b1710c110eabd7.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/intake/page-42895214458bc6d2.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/layout-d03de0ebd8366e23.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/live/page-3ed6c47e4b1c61c2.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/live-run/page-ae653e9bf261c738.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/loops/page-23f3f0a0209c0ac4.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/market/page-88c691273aae66f9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e187ac52a95538b3.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/morning/page-6e45828daed1295e.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/not-found-edb5d7287bb46186.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/observatory/page-4a7eaacd93c3f6a5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3ca073d43d74cc84.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1e9fee67b1199436.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-df1283ea963c6816.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/outputs/page-19fcf8d8301ccd18.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/page-5ab699e0303bbb40.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/reflection/page-7cde0d29756984c5.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-9d4c598e8ee22f7a.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/sessions/page-cc1609a71f44ec62.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7659a9a1345bfaa9.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/settings/page-0afef60ecc0c4af5.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-a3553949f8f34f5f.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/setup/layout-9fc644de2d8edae3.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/setup/page-057a69702f3c67c7.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/share/page-c0f0e570f3f74c73.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/showroom/page-1825071ae34e3b89.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/tasks/page-6244e3e135d8ccfc.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/term/page-da787e8a10b8a82e.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/today/page-1316d32498e2be42.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/trace/page-4df94572e343de2e.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/vault/page-708969f7475e88c1.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c58add08c919a1dc.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/workflows/page-a24847f45e5d5bf9.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/app/workspace/page-acfec65a3a60e22e.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-3dfc598e6fbde245.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/framework-c658a626118411b7.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-4c73685fd891445b.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-6310f7e5de7dd01d.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-51c326bf48610be1.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-d0beadf754da59b0.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-acd243bfbf3f6732.js +1 -0
  184. package/apps/pwa/out/_next/static/css/d1f8bb401740a549.css +1 -0
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +23 -23
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +23 -23
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +23 -23
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +22 -22
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +22 -22
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +23 -23
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +22 -22
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +23 -23
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +23 -23
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +22 -22
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +23 -23
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +23 -23
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +23 -23
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +23 -23
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +22 -22
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +22 -22
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +23 -23
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +23 -23
  222. package/apps/pwa/out/live-run/index.html +1 -1
  223. package/apps/pwa/out/live-run/index.txt +23 -23
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +22 -22
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +22 -22
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +23 -23
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +23 -23
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +22 -22
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +22 -22
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +22 -22
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +23 -23
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +23 -23
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +22 -22
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +23 -23
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +23 -23
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +23 -23
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +23 -23
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +23 -23
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +22 -22
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +23 -23
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +23 -23
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +23 -23
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +23 -23
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +23 -23
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +23 -23
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +23 -23
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +23 -23
  278. package/catalog/external-commands.yaml +5 -3
  279. package/catalog/meta/catalogVersion.yaml +2 -1
  280. package/catalog/signing/catalog-signing-public.pem +3 -0
  281. package/graphs/intake/intake-daily.yaml +16 -0
  282. package/graphs/intake/news-sources.yaml +1 -0
  283. package/graphs/intake/recipes.yaml +7 -0
  284. package/graphs/overlays/README.md +6 -0
  285. package/graphs/overlays/examples/research-runtime-empty-terminal.yaml +28 -0
  286. package/graphs/profiles/local.yaml +5 -0
  287. package/graphs/profiles/pod.yaml +4 -0
  288. package/graphs/release/release-loop.yaml +33 -17
  289. package/graphs/units/verbs/make-doc.yaml +1 -0
  290. package/package.json +6 -2
  291. package/plugins/demo-absorption-briefing/plugin.json +18 -0
  292. package/plugins/demo-absorption-briefing/skills/demo-absorption-briefing/SKILL.md +8 -0
  293. package/plugins/demo-browser-hand/plugin.json +13 -0
  294. package/plugins/demo-browser-hand/skills/demo-browser-hand/SKILL.md +8 -0
  295. package/plugins/demo-linear-secretary/plugin.json +15 -0
  296. package/plugins/demo-linear-secretary/skills/demo-linear-secretary/SKILL.md +8 -0
  297. package/plugins/demo-plugin-wizard/plugin.json +11 -0
  298. package/plugins/demo-plugin-wizard/skills/demo-plugin-wizard/SKILL.md +8 -0
  299. package/plugins/demo-research/plugin.json +16 -0
  300. package/plugins/demo-research/skills/demo-research/SKILL.md +8 -0
  301. package/plugins/demo-video-briefing/plugin.json +15 -0
  302. package/plugins/demo-video-briefing/skills/demo-video-briefing/SKILL.md +8 -0
  303. package/scripts/bench-metering-plan.ts +229 -0
  304. package/scripts/build-signed-catalog.ts +48 -0
  305. package/scripts/ci-typecheck-main-delta.sh +88 -0
  306. package/scripts/codex-fallback-live-probe.ts +1 -1
  307. package/scripts/demo/demo1104-parallel-host.ts +56 -0
  308. package/scripts/demo/demo1104-parallel-scenes.ts +301 -0
  309. package/scripts/demo/demo1104-parallel.ts +72 -0
  310. package/scripts/demo/demo1104-stage.config.ts +21 -0
  311. package/scripts/demo/demo1104-stage.ts +316 -0
  312. package/scripts/goal-method-audit.ts +17 -1
  313. package/scripts/graph-units/land-enqueue.ts +90 -0
  314. package/scripts/hooks/codex-notify.sh +32 -0
  315. package/scripts/install.sh +10 -38
  316. package/scripts/landing-heal/land-plan.ts +57 -0
  317. package/scripts/public-leak-baseline.txt +0 -1
  318. package/scripts/release-loop/auto-approve-node.ts +1 -1
  319. package/scripts/release-loop/docs-node.ts +4 -2
  320. package/scripts/release-loop/gate-autoheal-sim-evidence.ts +60 -0
  321. package/scripts/release-loop/gate-image-pin.ts +116 -2
  322. package/scripts/release-loop/gate-method.ts +139 -0
  323. package/scripts/release-loop/gate-node.ts +395 -43
  324. package/scripts/release-loop/gate-progress-sim.ts +73 -0
  325. package/scripts/release-loop/gate-traffic-mode.ts +151 -3
  326. package/scripts/release-loop/node-verdict.ts +38 -25
  327. package/scripts/release-loop/notes-repair.ts +18 -1
  328. package/scripts/release-loop/preflight.ts +544 -10
  329. package/scripts/release-loop/render-warm-control-manifest.ts +22 -0
  330. package/scripts/release-loop/resume-release.ts +9 -7
  331. package/scripts/release-loop/resume.ts +13 -4
  332. package/scripts/release-loop/unattended-release.ts +20 -9
  333. package/scripts/release-loop/warm-control-forward.ts +178 -0
  334. package/scripts/retro-cycle.ts +30 -8
  335. package/scripts/rhythm/daily.ts +39 -4
  336. package/scripts/rhythm/linear-progress-sync.ts +311 -0
  337. package/scripts/ux-sim.ts +7 -1
  338. package/scripts/video-pipeline.ts +9 -1
  339. package/scripts/warm-pool-provider-config.ts +35 -7
  340. package/src/acp/agent-manager.ts +1 -0
  341. package/src/acp/codex-app-server-agent.ts +33 -0
  342. package/src/acp/codex-app-server-client.ts +153 -16
  343. package/src/acp/core-turn-bridge.ts +17 -12
  344. package/src/acp/event-router.ts +20 -1
  345. package/src/acp/server.ts +6 -0
  346. package/src/agent-mission/browser-roundtrip-coordinator.ts +85 -0
  347. package/src/agent-mission/claude-headless.ts +4 -1
  348. package/src/agent-mission/codex-exec.ts +105 -0
  349. package/src/agent-mission/driver.ts +192 -11
  350. package/src/agent-mission/grok-auth-status.ts +107 -0
  351. package/src/agent-mission/hand-hooks.ts +101 -0
  352. package/src/agent-mission/mission-cli.ts +56 -21
  353. package/src/agent-mission/mission-hand-reply.ts +118 -0
  354. package/src/agent-mission/mission-line.ts +180 -0
  355. package/src/agent-mission/mission-out.ts +256 -18
  356. package/src/agent-mission/tool-install.ts +122 -11
  357. package/src/agent-substrate/pr-reviewer.ts +147 -11
  358. package/src/agent-substrate/self-review-cli.ts +10 -2
  359. package/src/agents/general-purpose.md +1 -1
  360. package/src/auto-research/research-fanout.ts +221 -0
  361. package/src/auto-research/research-turn.ts +362 -31
  362. package/src/autopilot/mission-codebase-gate.ts +7 -3
  363. package/src/autopilot/pr-manager.ts +31 -3
  364. package/src/boot/daemon-tools/self-implement.ts +3 -1
  365. package/src/boot/daemon-tools/web-search.ts +1 -2
  366. package/src/briefing/brief-items.ts +55 -11
  367. package/src/cli/agent-cli.ts +2 -1
  368. package/src/cli/catalog-cli.ts +31 -0
  369. package/src/cli/connector-cli.ts +16 -0
  370. package/src/cli/control-cli.ts +15 -6
  371. package/src/cli/doctor-fix.ts +2 -2
  372. package/src/cli/intake-source-cli.ts +44 -8
  373. package/src/cli/log-event-names.ts +8 -0
  374. package/src/cli/msg-cli.ts +32 -3
  375. package/src/cli/pr-cli.ts +21 -1
  376. package/src/cli/pr-land-heavy-remote.ts +44 -6
  377. package/src/cli/pr-land-overlap-evidence.ts +13 -1
  378. package/src/cli/pr-land-pod-shadow-job.ts +143 -5
  379. package/src/cli/pr-land-pod-shadow.ts +9 -2
  380. package/src/cli/release-checklist-read-cli.ts +44 -16
  381. package/src/cli/release-cli.ts +23 -3
  382. package/src/cli/research-cli.ts +9 -0
  383. package/src/cli/setup-cli.ts +166 -6
  384. package/src/cli/setup-keys.ts +199 -0
  385. package/src/cli/task-plugin-wizard-host.ts +114 -0
  386. package/src/cli/tasks-cli.ts +54 -9
  387. package/src/cli/tui-test-cli.ts +190 -0
  388. package/src/config.ts +4 -2
  389. package/src/connectors/linear-daily-progress.ts +228 -0
  390. package/src/connectors/linear-projector.ts +34 -12
  391. package/src/connectors/linear.ts +151 -7
  392. package/src/control-plane/ledger.ts +42 -4
  393. package/src/control-plane/member-tokens.ts +25 -2
  394. package/src/control-plane/ports.ts +8 -3
  395. package/src/control-plane/server.ts +26 -5
  396. package/src/core-turn/deep-turn-bloom.ts +67 -0
  397. package/src/core-turn/research-policy.ts +43 -0
  398. package/src/core-turn/run-core-turn.ts +488 -51
  399. package/src/core-turn/types.ts +4 -0
  400. package/src/dashboard/chat/acp-chat.ts +10 -0
  401. package/src/dashboard/classic-sidetab-mission-job.ts +192 -0
  402. package/src/dashboard/classic-sidetab-mission-pair.ts +82 -0
  403. package/src/dashboard/classic-sidetab-mission-trio.ts +242 -0
  404. package/src/dashboard/classic-sidetab-mission.ts +169 -5
  405. package/src/dashboard/index.ts +387 -14
  406. package/src/dashboard/input/chat-main-acp-dispatch.ts +23 -0
  407. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +8 -0
  408. package/src/dashboard/mission-cli-consumer.ts +20 -8
  409. package/src/dashboard/plugin-wizard-runtime.ts +50 -0
  410. package/src/dashboard/plugin-wizard-tool.ts +55 -0
  411. package/src/dashboard/sidetab-runtime.ts +3 -1
  412. package/src/dashboard/terminal-popup-runtime.ts +6 -3
  413. package/src/dashboard/visible-mission-diagnostics.ts +16 -0
  414. package/src/decisions/linear-progress-summary.ts +78 -0
  415. package/src/decisions/linear-progress-sync.ts +152 -0
  416. package/src/domains/outbound-alert.ts +49 -11
  417. package/src/domains/retro-cycle-observation.ts +43 -0
  418. package/src/domains/retro-cycle.ts +13 -2
  419. package/src/domains/retro-proposals.ts +92 -0
  420. package/src/domains/retro-report.ts +6 -1
  421. package/src/domains/telegram-kind-route.ts +16 -9
  422. package/src/graph-runner/arg-allowance.ts +13 -0
  423. package/src/graph-runner/graph-cli.ts +24 -1
  424. package/src/graph-runner/graph-grow-llm.ts +3 -1
  425. package/src/graph-runner/graph-run-api.ts +3 -1
  426. package/src/graph-runner/runner.ts +310 -46
  427. package/src/graph-wizard/mission-compose.ts +407 -12
  428. package/src/harness/dispatch-backpressure.ts +54 -1
  429. package/src/harness/dispatch-shadow-compare.ts +74 -0
  430. package/src/harness/harness-cli-command.ts +15 -3
  431. package/src/harness/harness-pod-dispatch.ts +3 -0
  432. package/src/harness/skill-exec.ts +21 -9
  433. package/src/harness/skill-select.ts +1 -1
  434. package/src/hooks/error-report-cloud.ts +101 -0
  435. package/src/hooks/receiver.ts +29 -2
  436. package/src/index.ts +129 -12
  437. package/src/intake-plane/actions.ts +354 -0
  438. package/src/intake-plane/adapters/telegram-ambient.ts +34 -18
  439. package/src/intake-plane/intake-sources.ts +78 -8
  440. package/src/intake-plane/items.ts +14 -4
  441. package/src/intake-plane/linear-mission-sync.ts +80 -0
  442. package/src/intake-plane/linear-secretary-runtime.ts +178 -0
  443. package/src/intake-plane/linear-wish.ts +227 -1
  444. package/src/life/life-cli.ts +168 -0
  445. package/src/life/life-kinds.ts +160 -0
  446. package/src/live-supply/budget-consumer.ts +78 -0
  447. package/src/live-supply/read.ts +3 -0
  448. package/src/live-supply/snapshot.ts +45 -0
  449. package/src/llm/child-first-llm-observation.ts +112 -0
  450. package/src/llm/provider-credentials.ts +68 -6
  451. package/src/llm/provider-failure-evidence.ts +44 -0
  452. package/src/llm.ts +172 -12
  453. package/src/maturity/feature-maturity.ts +3 -3
  454. package/src/mcp/client.ts +7 -0
  455. package/src/mcp/proxy-runtime.ts +6 -1
  456. package/src/mcp/server.ts +54 -0
  457. package/src/msg/msg-sse-server.ts +3 -3
  458. package/src/msg/msg-store.ts +177 -19
  459. package/src/native-tool-catalog.ts +36 -2
  460. package/src/nexus/api/consult-requests.ts +18 -6
  461. package/src/nexus/api/discord-trigger-bot.ts +6 -6
  462. package/src/nexus/api/graphs-api.ts +4 -2
  463. package/src/nexus/api/grokbot-trigger-bot.ts +206 -0
  464. package/src/nexus/api/http-server.ts +37 -1
  465. package/src/nexus/api/mcp-public-allowlist.ts +2 -0
  466. package/src/nexus/api/telegram-trigger-bot.ts +6 -6
  467. package/src/nexus/boot/register-mcp-clients.ts +114 -8
  468. package/src/nexus/index.ts +22 -2
  469. package/src/nexus/outbound/format.ts +9 -0
  470. package/src/nexus/outbound/grokbot-adapter.ts +71 -0
  471. package/src/nexus/outbound/router.ts +121 -16
  472. package/src/nexus/setup-status.ts +9 -0
  473. package/src/nexus/state/state.ts +3 -0
  474. package/src/oauth/codex-account-store.ts +20 -7
  475. package/src/oauth/codex-owner-mirror.ts +81 -0
  476. package/src/oauth/codex.ts +17 -2
  477. package/src/oauth/fallback-chain.ts +47 -17
  478. package/src/onboarding/first-run-doctor.ts +8 -1
  479. package/src/onboarding/first-run.ts +126 -29
  480. package/src/onboarding/key-setup.ts +103 -0
  481. package/src/onboarding/menu-answer.ts +13 -0
  482. package/src/onboarding/secret-prompt.ts +37 -0
  483. package/src/plugins/install/plugin-install.ts +59 -0
  484. package/src/pr-tool/census.ts +130 -0
  485. package/src/preview/terminal.ts +22 -7
  486. package/src/prompt-enhance/enhance.ts +10 -0
  487. package/src/pty-shell/pty-control-ipc.ts +4 -1
  488. package/src/pty-shell/pty-write-provenance.ts +21 -3
  489. package/src/pty-shell/registry.ts +9 -2
  490. package/src/registry/catalog-server.ts +232 -0
  491. package/src/registry/loader.ts +45 -1
  492. package/src/registry/types.ts +1 -2
  493. package/src/release-loop/proposal-auto-place.ts +83 -0
  494. package/src/research/axis-fanout.ts +79 -29
  495. package/src/research/lenses/second-order.ts +312 -0
  496. package/src/self-dev/ask-launch-flow.ts +72 -1
  497. package/src/self-dev/dev-cli.ts +7 -2
  498. package/src/self-dev/dev-pipeline.ts +5 -0
  499. package/src/self-dev/draft-residue.ts +64 -5
  500. package/src/self-dev/draft-sweep.ts +31 -10
  501. package/src/self-dev/draft-triage-rules.ts +60 -3
  502. package/src/self-dev/graph-front-nodes.ts +2 -1
  503. package/src/self-dev/graph-journey-route.ts +76 -21
  504. package/src/self-dev/launch-preflight.ts +58 -0
  505. package/src/self-dev/orchestrate.ts +1 -1
  506. package/src/self-dev/run-supervisor.ts +152 -2
  507. package/src/self-dev/shard-aggregate.ts +4 -0
  508. package/src/self-dev/shard-run.ts +36 -15
  509. package/src/self-implement/abandoned-classification.ts +12 -3
  510. package/src/self-implement/budget-gate.ts +7 -0
  511. package/src/self-implement/child-llm-preference.ts +10 -1
  512. package/src/self-implement/doctor-visit.ts +30 -8
  513. package/src/self-implement/elanous-tui-spawn.ts +3 -0
  514. package/src/self-implement/fixtures/tdd-green-missing-passing-child-transcript.txt +2 -0
  515. package/src/self-implement/gate-artifacts.ts +2 -1
  516. package/src/self-implement/goal-author-cli.ts +4 -2
  517. package/src/self-implement/goal-author.ts +549 -10
  518. package/src/self-implement/graph-overlay-yaml.ts +181 -4
  519. package/src/self-implement/graph-overlay.ts +3 -1
  520. package/src/self-implement/graph-profile-yaml.ts +107 -0
  521. package/src/self-implement/graph-yaml.ts +94 -5
  522. package/src/self-implement/off-diff-evidence.ts +249 -5
  523. package/src/self-implement/orchestrator.ts +298 -26
  524. package/src/self-implement/run-chart.ts +6 -3
  525. package/src/self-implement/run-ledger.ts +51 -1
  526. package/src/self-implement/runtime-overlay-run-steps.ts +95 -0
  527. package/src/self-implement/tdd-evidence-tags.ts +9 -0
  528. package/src/self-implement/test-scenario-request.ts +37 -6
  529. package/src/self-implement/train-gate-receipt.ts +166 -0
  530. package/src/self-implement/train-gate-remote.ts +62 -18
  531. package/src/session/chat.ts +6 -4
  532. package/src/session/session-fanout.ts +2 -0
  533. package/src/session/streaming/acp-stream-sink.ts +2 -1
  534. package/src/session/streaming/chunk-producer.ts +6 -0
  535. package/src/session/streaming/stream-compositor.ts +3 -2
  536. package/src/session-runtime/index.ts +74 -6
  537. package/src/session-runtime/retry-policy.ts +1 -1
  538. package/src/setup/llm-env-detect.ts +3 -0
  539. package/src/setup/service-key-catalog.ts +46 -0
  540. package/src/setup/service-keys.ts +241 -0
  541. package/src/sidetab-core/model.ts +205 -10
  542. package/src/sidetab-core/visible-agent-mission-pair.ts +51 -0
  543. package/src/sidetab-core/visible-agent-mission.ts +182 -48
  544. package/src/skills/tools/agent.ts +23 -4
  545. package/src/skills/tools/omni-search.ts +127 -27
  546. package/src/task-agent/parent-host.ts +29 -1
  547. package/src/task-agent/recall-inject.ts +98 -0
  548. package/src/task-agent/repair-outcomes.ts +65 -0
  549. package/src/task-agent/task-hand.ts +46 -14
  550. package/src/task-orchestrator/dispatcher.ts +14 -1
  551. package/src/task-orchestrator/linear-capture-events.ts +55 -0
  552. package/src/task-orchestrator/store.ts +16 -5
  553. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +23 -1
  554. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +107 -1
  555. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +113 -14
  556. package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +21 -9
  557. package/src/task-orchestrator/surfaces/pod-source-receive.ts +23 -11
  558. package/src/task-orchestrator/surfaces/pod-warm-switch.ts +2 -0
  559. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +1 -1
  560. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +467 -77
  561. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +45 -3
  562. package/src/task-orchestrator/surfaces/self-implement-pod.ts +210 -14
  563. package/src/task-orchestrator/surfaces/self-implement.ts +27 -3
  564. package/src/task-orchestrator/surfaces/warm-reuse-evidence.ts +169 -0
  565. package/src/task-orchestrator/types.ts +14 -0
  566. package/src/telegram.ts +14 -16
  567. package/src/terminal/coding-agent.ts +14 -5
  568. package/src/terminal/session-registry.ts +1 -1
  569. package/src/tool-hints/gate.ts +46 -14
  570. package/src/tool-hints/signals.ts +1 -1
  571. package/src/tool-hints/types.ts +2 -0
  572. package/src/tool-runtime/agent-runtime.ts +1 -0
  573. package/src/tool-runtime/browser-hand-runtime.ts +391 -0
  574. package/src/tool-runtime/browser-runtime.ts +33 -19
  575. package/src/tool-runtime/elanous-linear-issue-runtime.ts +76 -0
  576. package/src/tool-runtime/elanous-linear-secretary-runtime.ts +173 -0
  577. package/src/tool-runtime/graph-mcp-runtimes.ts +240 -0
  578. package/src/tool-runtime/index.ts +6 -0
  579. package/src/tool-runtime/types.ts +5 -0
  580. package/src/tui/entry.ts +51 -8
  581. package/src/tui/sidetab/attach.ts +252 -11
  582. package/src/tui/sidetab/herdr-portal.ts +225 -0
  583. package/src/tui/sidetab/list.ts +3 -2
  584. package/src/tui-client/next-dashboard-runtime.ts +52 -0
  585. package/src/user-config.ts +34 -9
  586. package/src/user-intent/types.ts +4 -3
  587. package/src/ux-sim/browser-hand.ts +165 -0
  588. package/src/ux-sim/draft-inventory-builder.ts +199 -0
  589. package/src/ux-sim/fake-browser-page.ts +81 -0
  590. package/src/ux-sim/fake-linear-device.ts +31 -0
  591. package/src/ux-sim/graph-engine-sim.ts +47 -0
  592. package/src/ux-sim/linear-secretary.ts +208 -0
  593. package/src/ux-sim/mock-scenario-catalog.json +81 -26
  594. package/src/ux-sim/mock-scenario-catalog.ts +175 -5
  595. package/src/ux-sim/onboarding-first-run-device.ts +31 -21
  596. package/src/ux-sim/release-sim-rig.ts +62 -18
  597. package/src/ux-sim/scenario.ts +70 -26
  598. package/src/ux-sim/service-key-setup.ts +113 -0
  599. package/src/ux-sim/term-slash-device.ts +74 -0
  600. package/src/ux-sim/turn-throw-sim.ts +31 -0
  601. package/src/version/packed-revision.json +1 -1
  602. package/src/web-search/exa.ts +77 -0
  603. package/src/web-search/index.ts +30 -9
  604. package/src/web-search/provider.ts +4 -1
  605. package/src/web-search/x-intent.ts +5 -0
  606. package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +0 -36
  608. package/apps/pwa/out/_next/static/chunks/1147-5196d19c222051b3.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/1319.b9b815ae0f181e06.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/1455-530ca16c7e71baff.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/1574.878c18ecc97b1bd4.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +0 -32
  614. package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/1945.7b54990d578c119b.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +0 -286
  620. package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +0 -34
  623. package/apps/pwa/out/_next/static/chunks/2456.b6de0e1c05f28843.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/2690.0521c738b7749931.js +0 -115
  629. package/apps/pwa/out/_next/static/chunks/2974.595d1e29d040cb6a.js +0 -64
  630. package/apps/pwa/out/_next/static/chunks/3207.a756c95e9ec8c3c8.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +0 -93
  632. package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +0 -41
  633. package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +0 -24
  635. package/apps/pwa/out/_next/static/chunks/3741.9cf76acaa2ee5298.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +0 -77
  641. package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/4276.336d76d858fcdf1b.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/4320.583cbab64db6be02.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/4770.769c9cd9c7034eba.js +0 -148
  647. package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +0 -74
  648. package/apps/pwa/out/_next/static/chunks/50061469.0326382241ef3b2d.js +0 -122
  649. package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +0 -149
  650. package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +0 -27
  652. package/apps/pwa/out/_next/static/chunks/53c73f84.373b6160adfa89a4.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/5431-72a1dbde59400691.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/5440-4236de26c5ea5856.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/6131.348b7d6bdcd267ce.js +0 -226
  664. package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +0 -90
  665. package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +0 -104
  671. package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +0 -94
  672. package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +0 -24
  679. package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +0 -8
  687. package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/8505.fd803c6c33b54cc3.js +0 -79
  689. package/apps/pwa/out/_next/static/chunks/8546-6f03f8ea34dd61d9.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/9211-330b6736a63d4cb3.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/9249.976f1e69a9d80754.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/9633.ce5bddc56784da72.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/9656-374232558fd528d0.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +0 -62
  705. package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0e3d7b9421b79dac.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/app/morning/page-0f291d3c8fd55a3a.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +0 -1
  741. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +0 -1
  744. package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/app/term/page-bf067887b3307423.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +0 -1
  752. package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +0 -1
  753. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +0 -1
  754. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +0 -1
  755. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +0 -1
  756. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +0 -1
  757. package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +0 -1
  758. package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +0 -1
  759. package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +0 -1
  760. package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +0 -1
  761. package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +0 -1
  762. package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +0 -1
  763. package/apps/pwa/out/_next/static/css/be9019f696217521.css +0 -1
  764. /package/apps/pwa/out/_next/static/{X1ucLw10Cds_h614NoEnh → HMXsxdy5MIIcHPMHv1wD4}/_ssgManifest.js +0 -0
@@ -5,8 +5,8 @@
5
5
  // ⛔ 읽기 전용 — 칸을 옮기지도, 동결을 바꾸지도, push 하지도 않는다. 옮길 것은 «명령»으로 제안만 한다.
6
6
  // ⛔ 게이트 규칙을 다시 쓰지 않는다 — checklist-gate 노드 · release run 진입 검사 · 예약 시작 준비 검사(releaseReadiness, #24557)가
7
7
  // 함께 쓰는 단 하나의 판정 cutChecklistGate 를 그대로 부르고, 예약 시작 경로는 releaseReadiness 를 CLI 와 같은 인자로 부른다.
8
- import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, mkdtempSync } from 'node:fs';
9
- import { join } from 'node:path';
8
+ import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, mkdtempSync } from 'node:fs';
9
+ import { dirname, join } from 'node:path';
10
10
  import { tmpdir } from 'node:os';
11
11
  import { spawnSync } from 'node:child_process';
12
12
  import { cutChecklistGate, listChecklist, type ChecklistItem } from '../../src/release-loop/checklist.js';
@@ -16,10 +16,13 @@ import { effectiveInstanceRoot } from '../../src/instance/resolve.js';
16
16
  import { debug } from '../../src/debug/log.js';
17
17
  import { releaseReadiness } from './release-readiness.js';
18
18
  import { latestPublishedPreviousVersion, runUnattendedRelease, type UnattendedReleaseDeps } from './unattended-release.js';
19
- import { loadExportConfig, run as runPublicExport, selectExportFiles } from '../public-export.js';
19
+ import { gitIgnoredExportFiles, loadExportConfig, run as runPublicExport, selectExportFiles } from '../public-export.js';
20
+ import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
21
+ import { NODE_REGISTRY, parsePodPool } from '../../src/task-orchestrator/surfaces/pod-pool.js';
20
22
 
21
23
  export type PreflightLevel = 'ok' | 'warn' | 'block';
22
- export type PreflightCheck = 'checklist' | 'postpub' | 'run-input' | 'freeze' | 'schedule' | 'export';
24
+ export type PreflightCheck = 'checklist' | 'postpub' | 'run-input' | 'freeze' | 'schedule' | 'export' | 'notes' | 'cut-tree' | 'gate-slot' | 'release-notes'
25
+ | 'cutoff-notes' | 'gitignored-export' | 'pwa-build' | 'gate-image';
23
26
  export interface PreflightFinding { level: PreflightLevel; check: PreflightCheck; id?: string; message: string; suggestion?: string }
24
27
  export interface GateMeasure { version: string; runId: string; gateSeconds: number; toPublishSeconds?: number }
25
28
  const compareStable = (a: string, b: string) => { const x = a.split('.').map(Number), y = b.split('.').map(Number); return x[0]! - y[0]! || x[1]! - y[1]! || x[2]! - y[2]!; };
@@ -32,6 +35,14 @@ export interface PreflightResult {
32
35
  findings: PreflightFinding[];
33
36
  schedule: { cutAt: string; landBy: string | null; publishAt: string | null; publishAtSource: 'option' | 'schedule' | 'cadence' | null; exportCheckAt: string } | null;
34
37
  exportCheck: { mainSha: string; files: number; leaks: number; codeLiterals: number } | null;
38
+ cutoffExportCheck: { commit: string; gitIgnoredLeaks: number | null; error?: string } | null;
39
+ /** Read-only T−120 evidence. It describes checks only; it never means a release stage ran. */
40
+ countdownReadiness: {
41
+ status: 'green' | 'red' | 'unmeasured';
42
+ notes: { status: 'green' | 'red' | 'unmeasured'; addedLines: number | null };
43
+ export: { status: 'green' | 'red' | 'unmeasured'; leaks: number | null };
44
+ checklist: { status: 'green' | 'red' | 'unmeasured' };
45
+ };
35
46
  /** The immediately previous release (by version order) whose gate is compared. */
36
47
  previousVersion: string | null;
37
48
  gateMeasure: GateMeasure | null;
@@ -39,8 +50,76 @@ export interface PreflightResult {
39
50
  fallbackGateMeasure: GateMeasure | null;
40
51
  }
41
52
 
53
+ // ── RELEASE-COUNTDOWN-SEQUENCE — T−120 → T0 발사 시퀀스 ─────────────────────────────────────────
54
+ // 누리호 발사 체계의 사상: H0 전 자동 시퀀스 · 단계별 녹색/빨강 한 화면 · 범위 이탈(빨강)이면 자동 중지 ·
55
+ // 최후 수동 취소 구간 · 실패는 «부적합 보고»로 원장에 남겨 다음 판에 반영한다.
56
+ // 단계 → 검사 묶음은 기존 findings 의 check 분류를 «그대로» 재사용한다(게이트 규칙을 다시 쓰지 않는다).
57
+ export type CountdownStage = 'T-120' | 'T-60' | 'T-30' | 'T0';
58
+
59
+ /** What `scripts/release-loop/cutoff.ts` pins at T−60 (`<instance>/release/<version>/manifest.json`). */
60
+ export interface CountdownManifest {
61
+ cutoff?: { sha?: string };
62
+ fragments?: { byPr: number; byNextMd: number; unlinked: number; unknown: number };
63
+ escalate?: Array<{ kind?: string }>;
64
+ }
65
+
66
+ /** The «node-b» gate slot: the dispatcher is paused and the pod target lowered so the gate can take a Pod. */
67
+ export interface CountdownGateSlot { dispatcherPaused?: boolean; podTargetFrom?: number; podTargetTo?: number }
68
+
69
+ export interface CountdownStep {
70
+ stage: CountdownStage;
71
+ /** Minutes before the cut. */
72
+ offsetMinutes: number;
73
+ /** Cut − offset (ISO); null when the release has no stored schedule. */
74
+ at: string | null;
75
+ label: string;
76
+ level: PreflightLevel;
77
+ findings: PreflightFinding[];
78
+ /** Not judged — an earlier step went red and the sequence stopped. */
79
+ skipped: boolean;
80
+ }
81
+
82
+ export interface NonconformanceEntry {
83
+ version: string;
84
+ stage: CountdownStage;
85
+ /** 원인 */
86
+ cause: string;
87
+ /** 수리 */
88
+ repair: string;
89
+ /** 다음 판 반영 */
90
+ nextRelease: string;
91
+ at: string;
92
+ }
93
+
94
+ export interface CountdownResult {
95
+ version: string;
96
+ next: string;
97
+ cutAt: string | null;
98
+ verdict: PreflightLevel;
99
+ steps: CountdownStep[];
100
+ /** The stage that went red and stopped the sequence. */
101
+ haltedAt: CountdownStage | null;
102
+ /** 담당 자리 우편 — sent when the sequence stops. */
103
+ mail: { from: string; to: string; subject: string; body: string } | null;
104
+ /** 최후 수동 취소 구간 — the last seconds before T0 in which a human can still abort. */
105
+ manualCancel: { windowStartAt: string; windowEndAt: string; totalResponsibleSeconds: number; ldSeconds: number } | null;
106
+ nonconformance: NonconformanceEntry[];
107
+ /** The target «부적합 보고» ledger (JSON Lines); dry-run does not append. */
108
+ nonconformanceLedger: string;
109
+ sideEffectsEnabled: boolean;
110
+ preflight: PreflightResult;
111
+ }
112
+ export type ReleaseCountdownStep = {
113
+ id: 'notes-and-readiness' | 'pinned-main-preflight' | 'gate-window' | 'cut';
114
+ offsetMinutes: number;
115
+ at: string;
116
+ action: string;
117
+ observation: 'not-observed-by-countdown';
118
+ };
119
+
42
120
  export interface PreflightDeps {
43
121
  now?: Date;
122
+ countdown?: boolean;
44
123
  /** Planned publish time (ISO). Without it, the October cadence (07:00 · 18:00 KST) after the cut is assumed. */
45
124
  publishAt?: string;
46
125
  runInput?: (version: string) => Promise<{ input: unknown }>;
@@ -52,14 +131,40 @@ export interface PreflightDeps {
52
131
  previousVersion?: string;
53
132
  /** Injectable for deterministic preflight tests. Production pins origin/main and scans a disposable checkout. */
54
133
  exportCheck?: () => Promise<{ mainSha: string; files: number; leaks: number; codeLiterals: number }>;
134
+ /** Release cut −60: build the pinned candidate PWA from a disposable checkout. */
135
+ pwaBuildCheck?: (commit: string) => Promise<{ ok: boolean; detail: string; commit?: string }>;
136
+ /** Release cut −60: count export-gitignored hits against the exact cutoff tree. */
137
+ gitIgnoredExportCheck?: (commit: string) => Promise<number | null>;
138
+ /** Release cut −60: prove the candidate's immutable commit tag exists in at least one configured gate pool. */
139
+ gateImageTagCheck?: (commit: string, poolSpec: string) => { checked: boolean; pools: string[]; detail?: string };
140
+ /** Countdown T−120/T−60/T0: the cut manifest `cutoff.ts` pinned (default `<instance>/release/<v>/manifest.json`). */
141
+ manifest?: (version: string) => CountdownManifest | null;
142
+ /** Countdown T−60: the «node-b» gate-slot record (default `<instance>/release/<v>/gate-slot.json`). */
143
+ gateSlot?: (version: string) => CountdownGateSlot | null;
144
+ /** Countdown seat mail — default posts `eln msg post --from OP --to <담당 자리>`. */
145
+ postMail?: (mail: { from: string; to: string; subject: string; body: string }) => void;
146
+ /** Countdown «부적합 보고» ledger (JSON Lines) — default `<instance>/release/nonconformance.jsonl`. */
147
+ nonconformanceLog?: string;
148
+ /** Injectable read-only comparison of release/next.md against the previous published tag. */
149
+ releaseNotesCheck?: (previousVersion: string | undefined, mainSha: string | undefined) => Promise<number>;
150
+ /** Explicitly enable owner mail and nonconformance ledger append; defaults to dry-run. */
151
+ allowSideEffects?: boolean;
55
152
  }
56
153
 
57
154
  function runGit(cwd: string, args: string[]): string {
58
- const result = spawnSync('git', args, { cwd, encoding: 'utf8', maxBuffer: 16 << 20 });
155
+ const result = spawnSync('git', args, { cwd, encoding: 'utf8', timeout: 60_000, maxBuffer: 16 << 20 });
59
156
  if (result.status !== 0) throw new Error(`git ${args.join(' ')} 실패 rc=${result.status}: ${(result.stderr ?? '').trim().slice(0, 300)}`);
60
157
  return (result.stdout ?? '').trim();
61
158
  }
62
159
 
160
+ /** Avoid partial-clone promisor remotes turning a missing fixture/cut SHA into an implicit network fetch. */
161
+ function hasLocalCommit(repoRoot: string, commit: string): boolean {
162
+ const result = spawnSync('git', ['cat-file', '-e', `${commit}^{commit}`], {
163
+ cwd: repoRoot, env: { ...process.env, GIT_NO_LAZY_FETCH: '1' }, encoding: 'utf8', timeout: 5_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER,
164
+ });
165
+ return result.status === 0;
166
+ }
167
+
63
168
  /** Read-only main snapshot: all checkout and cleanup writes are contained in a disposable temp directory. */
64
169
  export async function scanOriginMainExport(): Promise<{ mainSha: string; files: number; leaks: number; codeLiterals: number }> {
65
170
  const repoRoot = runGit(process.cwd(), ['rev-parse', '--show-toplevel']);
@@ -76,7 +181,7 @@ export async function scanOriginMainExport(): Promise<{ mainSha: string; files:
76
181
  needed.add('release/public-export.yaml');
77
182
  // Read only the paths the pinned manifest can inspect; the full tracked-path index stays at mainSha.
78
183
  const manifestPath = join(snapshot, 'release/public-export.yaml');
79
- const manifest = spawnSync('git', ['-C', snapshot, 'show', `${mainSha}:release/public-export.yaml`], { encoding: 'utf8', maxBuffer: 4 << 20 });
184
+ const manifest = spawnSync('git', ['-C', snapshot, 'show', `${mainSha}:release/public-export.yaml`], { encoding: 'utf8', timeout: 60_000, maxBuffer: 4 << 20 });
80
185
  if (manifest.status !== 0) throw new Error(`origin/main public export manifest 읽기 실패: ${(manifest.stderr ?? '').trim()}`);
81
186
  mkdirSync(join(snapshot, 'release'), { recursive: true });
82
187
  writeFileSync(manifestPath, manifest.stdout ?? '');
@@ -94,7 +199,7 @@ export async function scanOriginMainExport(): Promise<{ mainSha: string; files:
94
199
  }
95
200
  }
96
201
  const checkout = spawnSync('git', ['-C', snapshot, 'checkout-index', '--stdin', '-z', '-f'], {
97
- input: `${[...needed].filter((file) => trackedSet.has(file)).join('\0')}\0`, encoding: 'utf8', maxBuffer: 16 << 20,
202
+ input: `${[...needed].filter((file) => trackedSet.has(file)).join('\0')}\0`, encoding: 'utf8', timeout: 60_000, maxBuffer: 16 << 20,
98
203
  });
99
204
  if (checkout.status !== 0) throw new Error(`origin/main export 입력 파일 준비 실패 rc=${checkout.status}: ${(checkout.stderr ?? '').trim().slice(0, 300)}`);
100
205
  let output = '';
@@ -116,12 +221,121 @@ export async function scanOriginMainExport(): Promise<{ mainSha: string; files:
116
221
  }
117
222
  if (typeof report.files !== 'number' || typeof report.leaks !== 'number') throw new Error('public-export 검사 결과가 완전하지 않다');
118
223
  if (code !== (report.leaks > 0 ? 1 : 0)) throw new Error(`public-export 종료 코드와 누수 결과 불일치: rc=${code} leaks=${report.leaks}`);
119
- return { mainSha, files: report.files, leaks: report.leaks, codeLiterals: report.codeLiterals?.hits ?? 0 };
224
+ const codeLiterals = report.codeLiterals?.hits ?? 0;
225
+ return { mainSha, files: report.files, leaks: Math.max(0, report.leaks - codeLiterals), codeLiterals };
226
+ } finally {
227
+ rmSync(temp, { recursive: true, force: true });
228
+ }
229
+ }
230
+
231
+ /** Count added release-note lines from the last published tag to the exact pinned main commit. */
232
+ export async function scanReleaseNotesAddedLines(previousVersion: string | undefined, mainSha: string | undefined): Promise<number> {
233
+ if (!previousVersion || !STABLE.test(previousVersion)) throw new Error('직전 발행 판 버전이 없어 release/next.md 변경을 비교할 수 없다');
234
+ const repoRoot = runGit(process.cwd(), ['rev-parse', '--show-toplevel']);
235
+ const pinnedMainSha = mainSha && /^[0-9a-f]{40}$/u.test(mainSha)
236
+ ? mainSha : runGit(repoRoot, ['rev-parse', '--verify', 'origin/main^{commit}']);
237
+ const previousTag = runGit(repoRoot, ['rev-parse', '--verify', `v${previousVersion}^{commit}`]);
238
+ const numstat = runGit(repoRoot, ['diff', '--numstat', `${previousTag}...${pinnedMainSha}`, '--', 'release/next.md']);
239
+ if (!numstat) return 0;
240
+ const added = Number(numstat.split('\t', 1)[0]);
241
+ if (!Number.isSafeInteger(added) || added < 0) throw new Error(`release/next.md diff 결과를 읽을 수 없다: ${numstat}`);
242
+ return added;
243
+ }
244
+
245
+ /** Count files selected by the cutoff commit's public-export manifest that its own .gitignore would hide. */
246
+ export async function scanGitIgnoredExportAtCommit(commit: string): Promise<number | null> {
247
+ if (!/^[0-9a-f]{40}$/i.test(commit)) return null;
248
+ const repoRoot = runGit(process.cwd(), ['rev-parse', '--show-toplevel']);
249
+ if (!hasLocalCommit(repoRoot, commit)) return null;
250
+ const temp = mkdtempSync(join(tmpdir(), 'release-preflight-gitignored-'));
251
+ const snapshot = join(temp, 'checkout');
252
+ try {
253
+ runGit(temp, ['clone', '--shared', '--no-checkout', '--quiet', repoRoot, snapshot]);
254
+ runGit(snapshot, ['read-tree', commit]);
255
+ runGit(snapshot, ['checkout-index', '--all', '--force']);
256
+ const tracked = runGit(snapshot, ['ls-files', '-z']).split('\0').filter(Boolean);
257
+ const config = loadExportConfig(snapshot);
258
+ return gitIgnoredExportFiles(snapshot, selectExportFiles(tracked, config)).length;
120
259
  } finally {
121
260
  rmSync(temp, { recursive: true, force: true });
122
261
  }
123
262
  }
124
263
 
264
+ /** Build the PWA from an exact candidate commit in a disposable snapshot; never build the caller's dirty tree. */
265
+ export async function scanPwaBuildAtCommit(commit: string): Promise<{ ok: boolean; detail: string; commit: string }> {
266
+ if (!/^[0-9a-f]{40}$/i.test(commit)) return { ok: false, detail: `candidate commit is not pinned: ${commit || '(missing)'}`, commit };
267
+ const repoRoot = runGit(process.cwd(), ['rev-parse', '--show-toplevel']);
268
+ if (!hasLocalCommit(repoRoot, commit)) return { ok: false, detail: `candidate commit is unavailable locally: ${commit}`, commit };
269
+ const temp = mkdtempSync(join(tmpdir(), 'release-preflight-pwa-'));
270
+ const snapshot = join(temp, 'checkout');
271
+ const run = (cmd: string, args: string[], cwd: string, timeout = 60_000) => spawnSync(cmd, args, {
272
+ cwd, encoding: 'utf8', timeout, maxBuffer: SPAWN_SYNC_MAX_BUFFER,
273
+ });
274
+ const failureDetail = (result: ReturnType<typeof run>): string =>
275
+ (result.stderr || result.stdout || (result.error instanceof Error ? result.error.message : '') || `exit ${result.status}`).trim();
276
+ try {
277
+ const clone = run('git', ['clone', '--shared', '--no-checkout', '--quiet', repoRoot, snapshot], temp);
278
+ if (clone.status !== 0) return { ok: false, detail: `candidate checkout failed: ${failureDetail(clone)}`, commit };
279
+ const tree = run('git', ['read-tree', commit], snapshot);
280
+ if (tree.status !== 0) return { ok: false, detail: `candidate tree unavailable: ${failureDetail(tree)}`, commit };
281
+ const checkout = run('git', ['checkout-index', '--all', '--force'], snapshot);
282
+ if (checkout.status !== 0) return { ok: false, detail: `candidate files unavailable: ${failureDetail(checkout)}`, commit };
283
+ const rootInstall = run('bun', ['install', '--frozen-lockfile'], snapshot, 300_000);
284
+ if (rootInstall.status !== 0) return { ok: false, detail: `candidate dependency install failed: ${failureDetail(rootInstall).slice(-500)}`, commit };
285
+ const pwaInstall = run('bun', ['install', '--frozen-lockfile'], join(snapshot, 'apps/pwa'), 300_000);
286
+ if (pwaInstall.status !== 0) return { ok: false, detail: `PWA dependency install failed: ${failureDetail(pwaInstall).slice(-500)}`, commit };
287
+ const build = run('bun', ['bin/elanous.mjs', 'nexus', 'build'], snapshot, 15 * 60_000);
288
+ const detail = (build.status === 0 ? build.stdout : failureDetail(build)).trim().slice(-800);
289
+ // The snapshot was checked out at `commit`; echo that pin so injected evidence still has to match it.
290
+ return { ok: build.status === 0 && !build.error, detail: detail || `nexus build exit ${build.status}`, commit };
291
+ } catch (error) {
292
+ return { ok: false, detail: error instanceof Error ? error.message : String(error), commit };
293
+ } finally {
294
+ rmSync(temp, { recursive: true, force: true });
295
+ }
296
+ }
297
+
298
+ /** Read pool tags with a bounded SSH timeout; local pools use the image's immutable commit label. */
299
+ export function scanGateImageTag(commit: string, poolSpec: string): { checked: boolean; pools: string[]; detail?: string } {
300
+ if (!/^[0-9a-f]{40}$/i.test(commit)) return { checked: false, pools: [], detail: 'cutoff commit is not pinned' };
301
+ try {
302
+ const members = parsePodPool(poolSpec);
303
+ if (!members.length) return { checked: false, pools: [], detail: 'gate pool is empty' };
304
+ const pools: string[] = [];
305
+ const errors: string[] = [];
306
+ for (const member of members) {
307
+ if (!member.sshHost) {
308
+ const local = spawnSync('docker', ['image', 'inspect', 'elanous-harness:local', '--format', '{{index .Config.Labels "elanous.commit"}}'], { encoding: 'utf8', timeout: 10_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
309
+ if (local.status === 0 && local.stdout.trim() === commit) pools.push(member.context);
310
+ else if (local.error || local.status === null) errors.push(`${member.context}: ${local.error?.message ?? 'docker inspection unavailable'}`);
311
+ continue;
312
+ }
313
+ const registry = member.registry ?? NODE_REGISTRY;
314
+ const portMatch = /:(\d+)$/.exec(registry);
315
+ if (!portMatch) {
316
+ errors.push(`${member.context}: registry port is missing (${registry})`);
317
+ continue;
318
+ }
319
+ // Remote registry tags use the 12-character prefix per pod-pool.ts; local elanous.commit labels are full SHAs.
320
+ const port = portMatch[1]!;
321
+ const image = 'elanous-harness';
322
+ const remote = spawnSync('ssh', ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=5', member.sshHost,
323
+ `curl -fsS --max-time 5 http://localhost:${port}/v2/${image}/tags/list 2>/dev/null`], { encoding: 'utf8', timeout: 12_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
324
+ if (remote.status !== 0) {
325
+ errors.push(`${member.context}: registry query failed${remote.error ? ` (${remote.error.message})` : ` (rc=${remote.status})`}`);
326
+ continue;
327
+ }
328
+ try {
329
+ const tags = (JSON.parse(remote.stdout) as { tags?: string[] }).tags ?? [];
330
+ if (tags.includes(commit.slice(0, 12))) pools.push(member.context);
331
+ } catch { errors.push(`${member.context}: registry tag response was not valid JSON`); }
332
+ }
333
+ return { checked: errors.length === 0, pools, ...(errors.length ? { detail: errors.join('; ') } : {}) };
334
+ } catch (error) {
335
+ return { checked: false, pools: [], detail: error instanceof Error ? error.message : String(error) };
336
+ }
337
+ }
338
+
125
339
  const POSTPUB_ID = /^POSTPUB-/i;
126
340
  // Only a title that «starts» with the marker: a pre-publish cell often mentions «발행 뒤» in its body (a slot, a plan).
127
341
  const POSTPUB_TITLE = /^[«"'(]?\s*(발행\s*(뒤|후)|post-?publish)/i;
@@ -149,6 +363,37 @@ export function cadencePublishAt(cutAt: string): string {
149
363
  return new Date(cut).toISOString();
150
364
  }
151
365
 
366
+ /** A read-only timeline; timestamps never imply that a human or automation completed a step. */
367
+ export function releaseCountdown(cutAt: string): ReleaseCountdownStep[] {
368
+ const cut = Date.parse(cutAt);
369
+ if (!Number.isFinite(cut)) throw new Error(`invalid release cut time: ${cutAt}`);
370
+ const phases = [
371
+ { id: 'notes-and-readiness', offsetMinutes: -120, action: 'release/next.md 새 줄 ≥1 · 공개 export 누수 0 · 체크리스트 게이트 모의' },
372
+ { id: 'pinned-main-preflight', offsetMinutes: -60, action: '컷 트리를 고정하고 origin/main export 및 release preflight 실행' },
373
+ { id: 'gate-window', offsetMinutes: -30, action: 'gate 모드 · node-b 자리 확보 · 디스패처 정지 · podTarget 감축 확인' },
374
+ { id: 'cut', offsetMinutes: 0, action: 'release 컷 실행' },
375
+ ] as const;
376
+ return phases.map((phase) => ({
377
+ ...phase,
378
+ at: new Date(cut + phase.offsetMinutes * 60_000).toISOString(),
379
+ observation: 'not-observed-by-countdown',
380
+ }));
381
+ }
382
+
383
+ export function formatReleaseCountdown(cutAt: string, now = new Date(), readiness?: PreflightResult['countdownReadiness']): string[] {
384
+ return [
385
+ `발행 카운트다운 · 컷 ${formatKst(cutAt)} · 이 명령은 단계 완료를 관측하거나 실행하지 않는다`,
386
+ ...releaseCountdown(cutAt).map((step) => {
387
+ const label = step.offsetMinutes === 0 ? 'T0' : `T${step.offsetMinutes}`;
388
+ const timing = now.getTime() < Date.parse(step.at) ? '예정' : '시각 도달 · 완료 미관측';
389
+ const t120 = readiness && step.id === 'notes-and-readiness'
390
+ ? ` · read-only 판정 ${readiness.status} (notes +${readiness.notes.addedLines ?? '?'} · export 누수 ${readiness.export.leaks ?? '?'} · checklist ${readiness.checklist.status})`
391
+ : '';
392
+ return `· ${label} ${formatKst(step.at)} · ${timing} · ${step.action}${t120}`;
393
+ }),
394
+ ];
395
+ }
396
+
152
397
  /**
153
398
  * A release's measured gate: the newest finished release-loop run of «that» version (`{ version }`) or, with
154
399
  * `{ below }`, the most recently measured run of any version under it. Node timings are not stored in the run state, so the gate time is the start of the gate node's context file to
@@ -209,6 +454,9 @@ export async function releasePreflight(version: string, deps: PreflightDeps = {}
209
454
  // Always the stored cut (before or after it): the preflight simulates «the cut», not the moment it is run.
210
455
  const judgeAt = schedule ? new Date(Date.parse(schedule.cutAt)) : now;
211
456
  const data = listChecklist(version);
457
+ const cutoffManifest = deps.countdown
458
+ ? (deps.manifest ?? ((v: string) => readReleaseJson<CountdownManifest>(v, 'manifest.json')))(version)
459
+ : null;
212
460
  const { autoMoved, ...gate } = cutChecklistGate(version, schedule?.landBy, judgeAt);
213
461
  const p0 = data.items.filter((item) => item.status === 'yellow' && item.priority === 'P0').map((item) => item.id);
214
462
  const byId = new Map(data.items.map((item) => [item.id, item]));
@@ -253,7 +501,8 @@ export async function releasePreflight(version: string, deps: PreflightDeps = {}
253
501
 
254
502
  // 3a. Public export leakage is measured against one pinned origin/main commit, never this possibly dirty branch.
255
503
  try {
256
- exportCheck = await (deps.exportCheck ?? scanOriginMainExport)();
504
+ const scanned = await (deps.exportCheck ?? scanOriginMainExport)();
505
+ exportCheck = scanned;
257
506
  findings.push(exportCheck.leaks
258
507
  ? { level: 'block', check: 'export', message: `origin/main ${exportCheck.mainSha.slice(0, 12)} 공개 export 누수 ${exportCheck.leaks}건 · ${exportCheck.files} 파일 검사` }
259
508
  : { level: 'ok', check: 'export', message: `origin/main ${exportCheck.mainSha.slice(0, 12)} 공개 export 누수 0건 · ${exportCheck.files} 파일 검사` });
@@ -264,15 +513,92 @@ export async function releasePreflight(version: string, deps: PreflightDeps = {}
264
513
 
265
514
  // 3. The same input check `release run --dry-run` makes.
266
515
  let runPrevious: string | undefined;
516
+ let gatePodPool: string | undefined;
267
517
  try {
268
518
  const result = await (deps.runInput ?? ((v: string) => runUnattendedRelease({ version: v, dryRun: true }, deps.releaseRunDeps ?? {})))(version);
269
519
  const input = result.input as { previousVersion?: string; gatePodPool?: string };
270
520
  runPrevious = input.previousVersion;
521
+ gatePodPool = input.gatePodPool;
271
522
  findings.push({ level: 'ok', check: 'run-input', message: `release run --dry-run 입력 OK — 직전 판 ${input.previousVersion ?? '-'} · 게이트 풀 ${input.gatePodPool ?? '-'}` });
272
523
  } catch (error) {
273
524
  findings.push({ level: 'block', check: 'run-input', message: `release run --dry-run 입력 오류 — ${error instanceof Error ? error.message : String(error)}` });
274
525
  }
275
526
 
527
+ let notesAddedLines: number | null = null;
528
+ let cutoffExportCheck: PreflightResult['cutoffExportCheck'] = null;
529
+ const manifestNotesEmpty = cutoffManifest?.escalate?.some((item) => item.kind === 'notes-empty') === true;
530
+ const fragments = cutoffManifest?.fragments;
531
+ const manifestNoteSummary = fragments
532
+ && [fragments.byPr, fragments.byNextMd, fragments.unlinked, fragments.unknown].every(Number.isFinite)
533
+ ? `PR ${fragments.byPr} · next.md ${fragments.byNextMd} · unlinked ${fragments.unlinked} · unknown ${fragments.unknown}`
534
+ : null;
535
+ if (deps.countdown) {
536
+ try {
537
+ notesAddedLines = await (deps.releaseNotesCheck ?? scanReleaseNotesAddedLines)(runPrevious, exportCheck?.mainSha);
538
+ findings.push(notesAddedLines > 0
539
+ ? { level: 'ok', check: 'release-notes', message: `직전 발행 ${runPrevious ?? '-'} 이후 고정 main의 release/next.md 추가 ${notesAddedLines}줄` }
540
+ : { level: 'block', check: 'release-notes', message: `직전 발행 ${runPrevious ?? '-'} 이후 고정 main의 release/next.md 추가 줄이 없다` });
541
+ } catch (error) {
542
+ const message = error instanceof Error ? error.message : String(error);
543
+ findings.push({ level: 'warn', check: 'release-notes', message: `release/next.md 변경을 측정하지 못했다 — ${message}` });
544
+ }
545
+
546
+ const candidateCommit = cutoffManifest?.cutoff?.sha ?? '';
547
+ // cutoff.ts already classifies all public note sources (PR fragments and next.md lines) and emits
548
+ // notes-empty only when the manifest contains no user-visible feat/fix/security line.
549
+ const notesEmpty = manifestNotesEmpty;
550
+ const notesDetail = notesEmpty
551
+ ? 'notes-empty — 컷 manifest가 공개용 feat/fix/security 노트가 없다고 판정했다'
552
+ : `컷 manifest 노트 분류: ${manifestNoteSummary ?? '확인 불가'}`;
553
+ const emptyManifestNotes = fragments !== undefined
554
+ && fragments.byPr === 0 && fragments.byNextMd === 0 && fragments.unlinked === 0 && fragments.unknown === 0;
555
+ const cutoffNotesMissing = !candidateCommit || manifestNoteSummary === null || notesEmpty || emptyManifestNotes;
556
+ findings.push({
557
+ level: cutoffNotesMissing ? 'block' : 'ok', check: 'cutoff-notes',
558
+ message: notesDetail,
559
+ ...(cutoffNotesMissing ? { suggestion: 'cutoff manifest를 만들고 고정 후보 트리의 release/next.md 에 사용자용 feat/fix 줄을 더한 뒤 다시 검사한다' } : {}),
560
+ });
561
+
562
+ let ignoredLeaks: number | null = null;
563
+ let ignoredLeaksError: string | undefined;
564
+ try { ignoredLeaks = await (deps.gitIgnoredExportCheck ?? scanGitIgnoredExportAtCommit)(candidateCommit); }
565
+ catch (error) {
566
+ ignoredLeaks = null;
567
+ ignoredLeaksError = error instanceof Error ? error.message : String(error);
568
+ }
569
+ cutoffExportCheck = { commit: candidateCommit, gitIgnoredLeaks: ignoredLeaks, ...(ignoredLeaksError ? { error: ignoredLeaksError } : {}) };
570
+ findings.push(ignoredLeaks === 0
571
+ ? { level: 'ok', check: 'gitignored-export', message: `컷 후보 ${candidateCommit.slice(0, 12) || '(SHA 없음)'} public-export export-gitignored 0건` }
572
+ : { level: 'block', check: 'gitignored-export', message: ignoredLeaks === null || ignoredLeaks === undefined
573
+ ? `컷 후보 ${candidateCommit.slice(0, 12) || '(SHA 없음)'} public-export export-gitignored 결과를 확인하지 못했다${ignoredLeaksError ? ` — ${ignoredLeaksError}` : ''}`
574
+ : `컷 후보 ${candidateCommit.slice(0, 12) || '(SHA 없음)'} public-export export-gitignored ${ignoredLeaks}건`,
575
+ suggestion: '공개 export 입력 중 .gitignore 에 걸리는 파일을 제거하거나 공개 정책을 고치고 검사를 다시 실행한다' });
576
+
577
+ let pwa: { ok: boolean; detail: string; commit?: string };
578
+ try {
579
+ pwa = await (deps.pwaBuildCheck ?? scanPwaBuildAtCommit)(candidateCommit);
580
+ const pwaOk = pwa.ok && pwa.commit === candidateCommit;
581
+ findings.push(pwaOk
582
+ ? { level: 'ok', check: 'pwa-build', message: `PWA 빌드 통과 · ${candidateCommit.slice(0, 12)}` }
583
+ : { level: 'block', check: 'pwa-build', message: `PWA 빌드 실패 또는 SHA 불일치(${pwa.commit ?? '미기록'} ≠ ${candidateCommit}) · ${pwa.detail}`,
584
+ suggestion: `고정 후보 ${candidateCommit.slice(0, 12) || '(SHA 없음)'}에서 apps/pwa 빌드를 통과시킨다` });
585
+ } catch (error) {
586
+ pwa = { ok: false, detail: error instanceof Error ? error.message : String(error) };
587
+ findings.push({ level: 'block', check: 'pwa-build', message: `PWA 빌드 증거를 확인하지 못했다 · ${pwa.detail}`,
588
+ suggestion: `고정 후보 ${candidateCommit.slice(0, 12) || '(SHA 없음)'}에서 apps/pwa 빌드를 통과시킨다` });
589
+ }
590
+
591
+ const imageCheck = gatePodPool
592
+ ? (deps.gateImageTagCheck ?? scanGateImageTag)(candidateCommit, gatePodPool)
593
+ : { checked: false, pools: [], detail: 'release run 입력에서 gatePodPool을 얻지 못했다' };
594
+ findings.push(imageCheck.checked && imageCheck.pools.length > 0
595
+ ? { level: 'ok', check: 'gate-image', message: `컷 후보 gate 이미지 태그 ${candidateCommit.slice(0, 12)} 확인 · 풀 ${imageCheck.pools.join(', ')}` }
596
+ : { level: 'block', check: 'gate-image', message: imageCheck.checked
597
+ ? `컷 후보 gate 이미지 태그 ${candidateCommit.slice(0, 12) || '(SHA 없음)'}가 어느 풀에도 없다`
598
+ : `컷 후보 gate 이미지 태그를 확인하지 못했다 · ${imageCheck.detail ?? '원인 미상'}`,
599
+ suggestion: `컷 후보 ${candidateCommit.slice(0, 12) || '(SHA 없음)'}의 gate 이미지를 하나 이상의 풀에 빌드·반입한 뒤 preflight 를 다시 실행한다` });
600
+ }
601
+
276
602
  // 4. Landing freeze (emergency switch since RELEASE-BRANCH): when on, `release run` refuses.
277
603
  try {
278
604
  const frozen = (deps.freeze ?? (() => readLandingFreeze(deps.releaseRunDeps?.freezeRoot)))();
@@ -320,10 +646,29 @@ export async function releasePreflight(version: string, deps: PreflightDeps = {}
320
646
  }
321
647
  }
322
648
 
649
+ const checklistHasResult = findings.some((f) => f.check === 'checklist' && f.level === 'ok');
650
+ const countdownReadiness: PreflightResult['countdownReadiness'] = !deps.countdown ? {
651
+ status: 'unmeasured', notes: { status: 'unmeasured', addedLines: null },
652
+ export: { status: 'unmeasured', leaks: null }, checklist: { status: 'unmeasured' },
653
+ } : (() => {
654
+ const notesFinding = findings.find((f) => f.check === 'release-notes');
655
+ const exportFinding = findings.find((f) => f.check === 'export');
656
+ const checklistBlocked = findings.some((f) => f.check === 'checklist' && f.level === 'block');
657
+ const notesStatus = notesFinding?.level === 'ok' ? 'green' : notesFinding?.level === 'block' ? 'red' : 'unmeasured';
658
+ const exportStatus = exportFinding?.level === 'ok' ? 'green' : exportFinding?.level === 'block' ? 'red' : 'unmeasured';
659
+ const checklistStatus = checklistBlocked ? 'red' : checklistHasResult ? 'green' : 'unmeasured';
660
+ const statuses = [notesStatus, exportStatus, checklistStatus];
661
+ return {
662
+ status: statuses.includes('red') ? 'red' : statuses.every((status) => status === 'green') ? 'green' : 'unmeasured',
663
+ notes: { status: notesStatus, addedLines: notesAddedLines },
664
+ export: { status: exportStatus, leaks: exportCheck?.leaks ?? null },
665
+ checklist: { status: checklistStatus },
666
+ };
667
+ })();
323
668
  const blockers = findings.filter((f) => f.level === 'block').length;
324
669
  const warnings = findings.filter((f) => f.level === 'warn').length;
325
670
  debug.log('release.preflight', 'checked', { version, blockers, warnings });
326
- return { version, next, verdict: blockers ? 'block' : warnings ? 'warn' : 'ok', blockers, warnings, findings, schedule: scheduleOut, exportCheck, previousVersion, gateMeasure, fallbackGateMeasure };
671
+ return { version, next, verdict: blockers ? 'block' : warnings ? 'warn' : 'ok', blockers, warnings, findings, schedule: scheduleOut, exportCheck, cutoffExportCheck, countdownReadiness, previousVersion, gateMeasure, fallbackGateMeasure };
327
672
  }
328
673
 
329
674
  const ICON: Record<PreflightLevel, string> = { ok: '✅', warn: '⚠️', block: '⛔' };
@@ -336,3 +681,192 @@ export function formatPreflight(result: PreflightResult): string[] {
336
681
  : result.verdict === 'warn' ? `⚠️ 판정: 통과(경고 ${result.warnings})` : '✅ 판정: 통과';
337
682
  return [`release preflight ${result.version} (다음 판 ${result.next})`, ...lines, verdict];
338
683
  }
684
+
685
+ const COUNTDOWN_ORDER: CountdownStage[] = ['T-120', 'T-60', 'T-30', 'T0'];
686
+ const COUNTDOWN_OFFSET_MINUTES: Record<CountdownStage, number> = { 'T-120': 120, 'T-60': 60, 'T-30': 30, T0: 0 };
687
+ /** Which existing finding group each stage judges — the check vocabulary of `releasePreflight`, not a new rule set. */
688
+ const COUNTDOWN_CHECKS: Record<CountdownStage, PreflightCheck[]> = {
689
+ 'T-120': ['checklist', 'postpub', 'notes', 'release-notes', 'export'],
690
+ 'T-60': ['run-input', 'freeze', 'cut-tree', 'gate-slot', 'cutoff-notes', 'gitignored-export', 'pwa-build', 'gate-image'],
691
+ 'T-30': ['schedule'],
692
+ T0: [],
693
+ };
694
+ const COUNTDOWN_LABELS: Record<CountdownStage, string> = {
695
+ 'T-120': '노트(next.md 새 줄) · 공개 export 누수 · 체크리스트 게이트 모의',
696
+ 'T-60': '컷 후보 P0 네 항목(노트 · gitignore export · PWA · gate 이미지) ⊕ node-b 게이트 자리',
697
+ 'T-30': 'gate window 준비(컷→발행 창 ≥ 직전 실측 게이트)',
698
+ T0: '컷 트리 확인(실제 컷 실행은 별도)',
699
+ };
700
+ /** 담당 자리 — who is mailed when that stage goes red. */
701
+ const COUNTDOWN_OWNER: Record<CountdownStage, string> = { 'T-120': 'TC', 'T-60': 'OP', 'T-30': 'OP', T0: 'OP' };
702
+ /** 최후 수동 취소 구간(누리호 사상): 총책임자 8초 · LD 5초. */
703
+ const MANUAL_CANCEL_SECONDS = { totalResponsibleSeconds: 8, ldSeconds: 5 };
704
+
705
+ const worstLevel = (findings: PreflightFinding[]): PreflightLevel =>
706
+ findings.some((finding) => finding.level === 'block') ? 'block' : findings.some((finding) => finding.level === 'warn') ? 'warn' : 'ok';
707
+
708
+ function readReleaseJson<T>(version: string, file: string): T | null {
709
+ try { return JSON.parse(readFileSync(join(effectiveInstanceRoot(), 'release', version, file), 'utf8')) as T; } catch { return null; }
710
+ }
711
+
712
+ /** T−120 — `next.md` got at least one new line for this release (0.2.39 found notes-empty only after the cut). */
713
+ function countdownNotes(manifest: CountdownManifest | null): PreflightFinding {
714
+ if (!manifest) return { level: 'warn', check: 'notes', message: 'next.md 새 줄 판정 없음 — 컷 manifest 없음(T−60 의 cutoff.ts 가 만든다)' };
715
+ const lines = (manifest.fragments?.byNextMd ?? 0) + (manifest.fragments?.unlinked ?? 0);
716
+ if ((manifest.escalate ?? []).some((item) => item.kind === 'notes-empty') || lines < 1) {
717
+ return {
718
+ level: 'block', check: 'notes', message: 'next.md 새 줄 0 — notes-empty(이 판 공개 노트가 비어 있다 · 컷 뒤에 드러나면 발행이 늦어진다)',
719
+ suggestion: 'release/next.md 에 이 판 feat/fix 줄을 더하고 cutoff.ts 로 manifest 를 다시 만든다',
720
+ };
721
+ }
722
+ return { level: 'ok', check: 'notes', message: `next.md 새 줄 ${lines}줄` };
723
+ }
724
+
725
+ /** T−60 — the cut tree is pinned: the manifest names one cutoff sha (cutoff.ts output, its format unchanged). */
726
+ function countdownCutTree(manifest: CountdownManifest | null): PreflightFinding {
727
+ const sha = manifest?.cutoff?.sha;
728
+ return sha
729
+ ? { level: 'ok', check: 'cut-tree', message: `컷 트리 고정 ${sha.slice(0, 12)}` }
730
+ : { level: 'warn', check: 'cut-tree', message: '컷 트리 고정 없음 — T−60 에 `bun scripts/release-loop/cutoff.ts --version <v>` 로 manifest 를 만든다' };
731
+ }
732
+
733
+ /** T−60 — the «node-b» gate slot: dispatcher stopped and the pod target lowered so the gate can take a Pod. */
734
+ function countdownGateSlot(slot: CountdownGateSlot | null): PreflightFinding[] {
735
+ if (!slot) return [{ level: 'warn', check: 'gate-slot', message: 'node-b 게이트 자리 기록 없음 — release/<v>/gate-slot.json(디스패처 정지 · podTarget 감축)' }];
736
+ const findings: PreflightFinding[] = [];
737
+ if (slot.dispatcherPaused !== true) {
738
+ findings.push({
739
+ level: 'block', check: 'gate-slot', message: 'node-b 게이트 자리 — 디스패처가 멈추지 않았다(게이트가 Pod 를 잡지 못한다)',
740
+ suggestion: '디스패처를 정지하고 release/<v>/gate-slot.json 에 dispatcherPaused: true 를 남긴다',
741
+ });
742
+ }
743
+ if (slot.podTargetFrom !== undefined && slot.podTargetTo !== undefined && slot.podTargetTo >= slot.podTargetFrom) {
744
+ findings.push({
745
+ level: 'block', check: 'gate-slot', message: `node-b 게이트 자리 — podTarget 감축 없음(${slot.podTargetFrom}→${slot.podTargetTo})`,
746
+ suggestion: 'podTarget 을 게이트 몫만큼 줄이고 gate-slot.json 에 from/to 를 기록한다',
747
+ });
748
+ }
749
+ if (!findings.length) {
750
+ const target = slot.podTargetFrom !== undefined ? ` · podTarget ${slot.podTargetFrom}→${slot.podTargetTo ?? slot.podTargetFrom}` : '';
751
+ findings.push({ level: 'ok', check: 'gate-slot', message: `node-b 게이트 자리 확보 — 디스패처 정지${target}` });
752
+ }
753
+ return findings;
754
+ }
755
+
756
+ /** T0 — model readiness only: this command never executes the cut. */
757
+ function countdownCut(manifest: CountdownManifest | null): PreflightFinding {
758
+ const sha = manifest?.cutoff?.sha;
759
+ return sha
760
+ ? { level: 'ok', check: 'cut-tree', message: `T0 컷 준비 — 고정 트리 ${sha.slice(0, 12)}(실제 컷 실행은 별도 명령)` }
761
+ : { level: 'block', check: 'cut-tree', message: 'T0 컷 불가 — 컷 트리 고정(manifest cutoff.sha)이 없다', suggestion: 'T−60 에 cutoff.ts 로 컷 트리를 고정한다' };
762
+ }
763
+
764
+ function postSeatMail(mail: { from: string; to: string; subject: string; body: string }): void {
765
+ const result = spawnSync('eln', ['msg', 'post', '--from', mail.from, '--to', mail.to, '--kind', 'report', '--body', `${mail.subject}\n${mail.body}`], {
766
+ encoding: 'utf8', timeout: 60_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER,
767
+ });
768
+ if (result.status !== 0 || result.error) process.stderr.write(`release countdown 좌석 우편 실패: ${(result.stderr || String(result.error ?? result.status)).trim()}\n`);
769
+ }
770
+
771
+ /**
772
+ * `release preflight --countdown` — the launch sequence from T−120 to T0 over the same `releasePreflight` findings.
773
+ * A red stage stops every later stage, mails the owning seat and appends one «부적합 보고» per failure to the ledger.
774
+ * Read-only apart from that ledger append and the seat mail — the cut and the checklist are never changed.
775
+ */
776
+ export async function runReleaseCountdown(version: string, deps: PreflightDeps = {}): Promise<CountdownResult> {
777
+ const preflight = await releasePreflight(version, { ...deps, countdown: true });
778
+ const now = deps.now ?? new Date();
779
+ const manifest = (deps.manifest ?? ((v: string) => readReleaseJson<CountdownManifest>(v, 'manifest.json')))(version);
780
+ const slot = (deps.gateSlot ?? ((v: string) => readReleaseJson<CountdownGateSlot>(v, 'gate-slot.json')))(version);
781
+ const cutAt = preflight.schedule?.cutAt ?? null;
782
+
783
+ const grouped = new Map<PreflightCheck, PreflightFinding[]>();
784
+ for (const finding of preflight.findings) grouped.set(finding.check, [...(grouped.get(finding.check) ?? []), finding]);
785
+ const own: Record<CountdownStage, PreflightFinding[]> = {
786
+ 'T-120': [countdownNotes(manifest)],
787
+ 'T-60': [countdownCutTree(manifest), ...countdownGateSlot(slot)],
788
+ 'T-30': [],
789
+ T0: [countdownCut(manifest)],
790
+ };
791
+
792
+ const steps: CountdownStep[] = [];
793
+ let haltedAt: CountdownStage | null = null;
794
+ for (const stage of COUNTDOWN_ORDER) {
795
+ const offsetMinutes = COUNTDOWN_OFFSET_MINUTES[stage];
796
+ const at = cutAt ? new Date(Date.parse(cutAt) - offsetMinutes * 60_000).toISOString() : null;
797
+ if (haltedAt) {
798
+ steps.push({ stage, offsetMinutes, at, label: COUNTDOWN_LABELS[stage], level: 'ok', findings: [], skipped: true });
799
+ continue;
800
+ }
801
+ const findings = [...COUNTDOWN_CHECKS[stage].flatMap((check) => grouped.get(check) ?? []), ...own[stage]];
802
+ const level = worstLevel(findings);
803
+ steps.push({ stage, offsetMinutes, at, label: COUNTDOWN_LABELS[stage], level, findings, skipped: false });
804
+ if (level === 'block') haltedAt = stage;
805
+ }
806
+
807
+ const nonconformance: NonconformanceEntry[] = steps.flatMap((step) => step.findings
808
+ .filter((finding) => finding.level === 'block')
809
+ .map((finding) => ({
810
+ version, stage: step.stage, cause: finding.message,
811
+ repair: finding.suggestion ?? `${step.stage} 담당 자리가 원인을 확인해 고친다`,
812
+ nextRelease: preflight.next, at: now.toISOString(),
813
+ })));
814
+ const nonconformanceLedger = deps.nonconformanceLog ?? join(effectiveInstanceRoot(), 'release', 'nonconformance.jsonl');
815
+ if (deps.allowSideEffects && nonconformance.length) {
816
+ mkdirSync(dirname(nonconformanceLedger), { recursive: true });
817
+ appendFileSync(nonconformanceLedger, `${nonconformance.map((entry) => JSON.stringify(entry)).join('\n')}\n`);
818
+ }
819
+
820
+ const mail = haltedAt ? countdownMail(version, haltedAt, steps, nonconformance.length) : null;
821
+ if (deps.allowSideEffects && mail) (deps.postMail ?? postSeatMail)(mail);
822
+
823
+ const cancelSeconds = MANUAL_CANCEL_SECONDS.totalResponsibleSeconds + MANUAL_CANCEL_SECONDS.ldSeconds;
824
+ const manualCancel = cutAt ? {
825
+ windowStartAt: new Date(Date.parse(cutAt) - cancelSeconds * 1000).toISOString(),
826
+ windowEndAt: cutAt, ...MANUAL_CANCEL_SECONDS,
827
+ } : null;
828
+ const verdict = haltedAt ? 'block' : steps.some((step) => step.level === 'warn') ? 'warn' : 'ok';
829
+ debug.log('release.preflight', 'countdown', { version, haltedAt, verdict, nonconformance: nonconformance.length });
830
+ return {
831
+ version, next: preflight.next, cutAt, verdict, steps, haltedAt, mail, manualCancel, nonconformance, nonconformanceLedger,
832
+ sideEffectsEnabled: deps.allowSideEffects === true, preflight,
833
+ };
834
+ }
835
+
836
+ function countdownMail(version: string, stage: CountdownStage, steps: CountdownStep[], failures: number): CountdownResult['mail'] {
837
+ const step = steps.find((entry) => entry.stage === stage)!;
838
+ const reasons = step.findings.filter((finding) => finding.level === 'block');
839
+ const repairs = reasons.filter((finding) => finding.suggestion).map((finding) => `· ${finding.message} → ${finding.suggestion}`);
840
+ return {
841
+ from: 'OP', to: COUNTDOWN_OWNER[stage], subject: `RELEASE-COUNTDOWN ${version} ${stage}`,
842
+ body: [
843
+ `RELEASE-COUNTDOWN ${version} ${stage} 빨강 — 발사 시퀀스를 자동 중지했다.`,
844
+ ...reasons.map((finding) => `· ${finding.message}`),
845
+ ...(repairs.length ? ['수리:', ...repairs] : []),
846
+ `«부적합 보고» ${failures}건을 원장에 넣었다(원인·수리·다음 판 ${nextPatch(version)} 반영).`,
847
+ ].join('\n'),
848
+ };
849
+ }
850
+
851
+ /** The one screen of the launch sequence: every stage green/red, the manual-cancel window and one «판정:» line. */
852
+ export function formatCountdown(result: CountdownResult): string[] {
853
+ const lines = [`release countdown ${result.version} (다음 판 ${result.next})${result.cutAt ? ` · 컷 ${formatKst(result.cutAt)}` : ''} · 검사 상태만 표시(단계 실행은 미관측)`];
854
+ for (const step of result.steps) {
855
+ const when = step.at ? ` · ${formatKst(step.at)}` : '';
856
+ if (step.skipped) { lines.push(`⏸ ${step.stage} — 미진행(${result.haltedAt} 빨강 — 자동 중지)${when}`); continue; }
857
+ lines.push(`${ICON[step.level]} ${step.stage} ${step.label}${when}`);
858
+ for (const finding of step.findings) {
859
+ lines.push(` ${ICON[finding.level]} ${finding.message}`);
860
+ if (finding.suggestion) lines.push(` → ${finding.suggestion}`);
861
+ }
862
+ }
863
+ if (result.manualCancel) {
864
+ const seconds = Math.round((Date.parse(result.manualCancel.windowEndAt) - Date.parse(result.manualCancel.windowStartAt)) / 1000);
865
+ lines.push(`⏱ 최후 수동 취소 구간 T0−${seconds}초 ~ T0 (${formatKst(result.manualCancel.windowEndAt)}) — 총책임자 ${result.manualCancel.totalResponsibleSeconds}초 · LD ${result.manualCancel.ldSeconds}초`);
866
+ }
867
+ if (result.mail) lines.push(`${result.sideEffectsEnabled ? '📮 담당 자리 우편' : '📮 담당 자리 우편 계획(미발송 · 기본 dry-run)'} → ${result.mail.to} · ${result.mail.subject}`);
868
+ lines.push(result.verdict === 'block'
869
+ ? `⛔ 판정: 평가 중지 — ${result.haltedAt} 빨강 · 부적합 보고 ${result.nonconformance.length}건 ${result.sideEffectsEnabled ? `원장 기록(${result.nonconformanceLedger})` : `원장 기록 계획(미기록 · 기본 dry-run: ${result.nonconformanceLedger})`}`
870
+ : result.verdict === 'warn' ? '⚠️ 판정: 통과(경고) — 경고 단계는 기록만 한다' : '✅ 판정: 통과');
871
+ return lines;
872
+ }