elanous 0.2.43 → 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 (544) 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/{Om5xS9jpjSXJCeTNGYEi4 → HMXsxdy5MIIcHPMHv1wD4}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{3557ac4a.c8931684475018ad.js → 08334415.f0e80896acbef722.js} +1 -1
  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/118.2a89982fe4276880.js +23 -0
  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/{3955.6922286237f2ac2b.js → 1306.967240ede9a0f757.js} +2 -2
  14. package/apps/pwa/out/_next/static/chunks/1344.3b95f3bd1b77a3fe.js +94 -0
  15. package/apps/pwa/out/_next/static/chunks/{5628.6ddb2d5ffba4ff4c.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/{2573.387a707f6f9d720c.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/{7910-6f139bcafd238b9f.js → 1850-522de4ee34957210.js} +1 -1
  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/{1406.a1dc2ab6f8449374.js → 1978.eeb60a606dc5deac.js} +2 -2
  26. package/apps/pwa/out/_next/static/chunks/{3402.44b565b314f2263c.js → 2043.e233e3b5a3e790f5.js} +1 -1
  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/{3694.e0099884345f0198.js → 2216.802962c04f40a26e.js} +1 -1
  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/239.de075467b5523469.js +287 -0
  33. package/apps/pwa/out/_next/static/chunks/{1200.6a37c202f9f17e9a.js → 249.ba7051590d603c8f.js} +3 -3
  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/{3935.16701e33f8982dc9.js → 2954.bbefc95d6b20e0d8.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/{aa57532f.510551854ff239b1.js → 2e0f720a.a0697c08e3806a60.js} +1 -1
  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/{5930.8b4df3b46c32b05a.js → 3349.b5f09ee474d9533e.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/{5866.366966af47fdde5d.js → 3503.a47a9755fb4d97b0.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/3506-5f429b3b7e1392b0.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/{7900.096f455c7c1efc53.js → 3843.bad404d008893291.js} +2 -2
  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/{8588.d1c6a2903727ce87.js → 4393.28fa5228d0eec217.js} +3 -3
  52. package/apps/pwa/out/_next/static/chunks/4404.7281c847c6e10dfc.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/{8062-7cd0bcc7f01455ba.js → 4415-b9d6a90960920a4a.js} +3 -3
  54. package/apps/pwa/out/_next/static/chunks/{8313-dfa1021bb58a46d6.js → 447-5b304de1fe098f43.js} +1 -1
  55. package/apps/pwa/out/_next/static/chunks/4484-8ebea83d431e6fae.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/{6944.309f4e3ea54d1537.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/{3072.01338851ca21a6f7.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/{8986.8e2e4efbfa655c59.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/4891.3f80ba517a3b2373.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/{8205.282ca4bb62d54a0a.js → 4998.810ca8833084caa9.js} +2 -2
  66. package/apps/pwa/out/_next/static/chunks/4f98febc.ea5841d4d616bfb2.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/{5789.2da35c258292e1e5.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/{9770.5f75bc6088b16109.js → 5439.3fec6dbce29ecb36.js} +1 -1
  72. package/apps/pwa/out/_next/static/chunks/{1313-19f737e0222c4444.js → 5612-074d9a5e00dfac6a.js} +1 -1
  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/{3969.272ee7f9fd6c62e5.js → 5790.5eb06a653ebd8571.js} +1 -1
  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/{6540.c7bef57ad8f90a87.js → 5989.7ecd362c5c44b747.js} +3 -3
  81. package/apps/pwa/out/_next/static/chunks/{0ad6802e.ae7afb63fdcb4491.js → 5a34fb2f.52f1b4e1baebb48a.js} +3 -3
  82. package/apps/pwa/out/_next/static/chunks/{0966f8a1.745fcd44f64a87ea.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/{2144.25b292396712196d.js → 6245.1182e007e9d230d9.js} +2 -2
  89. package/apps/pwa/out/_next/static/chunks/{363bfab3.9623b9f89363541f.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/{d96660fb.cbc21a2a88539dbb.js → 6549dad0.2740b25e51bf8f05.js} +7 -7
  92. package/apps/pwa/out/_next/static/chunks/{5564.9e72532188f57586.js → 6881.9e7955847640c7fe.js} +4 -4
  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/{3903.3ef4656c67572764.js → 7010.1bbd17a7f2b3e376.js} +1 -1
  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/{2312.0c8b61e22d3136d7.js → 7679.32c40cdd14f20b4a.js} +1 -1
  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/{4199.ce86b281fd0a60b7.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/8551.f762945c2cda2e95.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/8635-57a9d8d9db70f4f1.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{fb7d20d5-6892502b6ee39ca6.js → 86961958-4b0689a5048f756b.js} +1 -1
  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/87f498e5-0881de09f2e44707.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/8843.c3197f904c952b81.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/{cb0f8df8.09a4a6be97e3fc5f.js → 892fd990.456232f326cc39c2.js} +1 -1
  116. package/apps/pwa/out/_next/static/chunks/{837-81336ebaca8198a8.js → 8965-135a2c9eaa2464cb.js} +1 -1
  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/{8637.90f92d5c8db4732c.js → 9398.1f56e3f17795f43f.js} +1 -1
  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-0a815f34367b6db6.js → page-da787e8a10b8a82e.js} +1 -1
  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/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +22 -22
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +22 -22
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +22 -22
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +21 -21
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +21 -21
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +22 -22
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +21 -21
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +22 -22
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +22 -22
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +21 -21
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +22 -22
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +22 -22
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +22 -22
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +22 -22
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +21 -21
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +21 -21
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +22 -22
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +22 -22
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +22 -22
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +21 -21
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +21 -21
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +22 -22
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +22 -22
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +21 -21
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +21 -21
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +21 -21
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +22 -22
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +22 -22
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +21 -21
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +22 -22
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +22 -22
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +22 -22
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +22 -22
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +22 -22
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +21 -21
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +22 -22
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +22 -22
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +22 -22
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +22 -22
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +22 -22
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +22 -22
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +22 -22
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +22 -22
  277. package/graphs/overlays/README.md +6 -0
  278. package/graphs/release/release-loop.yaml +33 -17
  279. package/graphs/units/verbs/make-doc.yaml +1 -0
  280. package/package.json +1 -1
  281. package/scripts/bench-metering-plan.ts +229 -0
  282. package/scripts/demo/demo1104-parallel-host.ts +56 -0
  283. package/scripts/demo/demo1104-parallel.ts +72 -0
  284. package/scripts/public-leak-baseline.txt +0 -1
  285. package/scripts/release-loop/docs-node.ts +4 -2
  286. package/scripts/release-loop/gate-image-pin.ts +116 -2
  287. package/scripts/release-loop/gate-node.ts +217 -25
  288. package/scripts/release-loop/gate-progress-sim.ts +73 -0
  289. package/scripts/release-loop/notes-repair.ts +18 -1
  290. package/scripts/release-loop/preflight.ts +188 -8
  291. package/scripts/release-loop/render-warm-control-manifest.ts +22 -0
  292. package/scripts/release-loop/resume-release.ts +9 -7
  293. package/scripts/release-loop/resume.ts +13 -4
  294. package/scripts/release-loop/warm-control-forward.ts +178 -0
  295. package/scripts/retro-cycle.ts +30 -8
  296. package/scripts/rhythm/daily.ts +4 -1
  297. package/scripts/rhythm/linear-progress-sync.ts +28 -15
  298. package/scripts/ux-sim.ts +7 -1
  299. package/scripts/warm-pool-provider-config.ts +6 -1
  300. package/src/agent-mission/grok-auth-status.ts +78 -18
  301. package/src/agent-mission/mission-out.ts +2 -2
  302. package/src/agent-substrate/pr-reviewer.ts +147 -11
  303. package/src/agent-substrate/self-review-cli.ts +10 -2
  304. package/src/auto-research/research-fanout.ts +221 -0
  305. package/src/auto-research/research-turn.ts +282 -36
  306. package/src/autopilot/mission-codebase-gate.ts +7 -3
  307. package/src/cli/control-cli.ts +15 -6
  308. package/src/cli/log-event-names.ts +8 -0
  309. package/src/cli/setup-keys.ts +20 -8
  310. package/src/cli/tui-test-cli.ts +9 -5
  311. package/src/control-plane/ledger.ts +42 -4
  312. package/src/control-plane/member-tokens.ts +25 -2
  313. package/src/control-plane/ports.ts +8 -3
  314. package/src/control-plane/server.ts +26 -5
  315. package/src/core-turn/research-policy.ts +7 -1
  316. package/src/core-turn/run-core-turn.ts +186 -39
  317. package/src/core-turn/types.ts +2 -0
  318. package/src/dashboard/classic-sidetab-mission-job.ts +113 -24
  319. package/src/dashboard/classic-sidetab-mission-pair.ts +3 -2
  320. package/src/dashboard/classic-sidetab-mission-trio.ts +35 -12
  321. package/src/dashboard/classic-sidetab-mission.ts +2 -0
  322. package/src/dashboard/index.ts +9 -1
  323. package/src/dashboard/sidetab-runtime.ts +2 -0
  324. package/src/dashboard/visible-mission-diagnostics.ts +16 -0
  325. package/src/domains/retro-cycle-observation.ts +43 -0
  326. package/src/domains/retro-cycle.ts +13 -2
  327. package/src/domains/retro-proposals.ts +92 -0
  328. package/src/domains/retro-report.ts +6 -1
  329. package/src/graph-runner/graph-cli.ts +24 -1
  330. package/src/graph-runner/graph-grow-llm.ts +3 -1
  331. package/src/graph-runner/runner.ts +226 -30
  332. package/src/harness/harness-cli-command.ts +4 -2
  333. package/src/harness/harness-pod-dispatch.ts +3 -0
  334. package/src/index.ts +2 -2
  335. package/src/llm/child-first-llm-observation.ts +112 -0
  336. package/src/llm/provider-credentials.ts +60 -4
  337. package/src/llm/provider-failure-evidence.ts +44 -0
  338. package/src/llm.ts +170 -11
  339. package/src/mcp/client.ts +7 -0
  340. package/src/mcp/proxy-runtime.ts +6 -1
  341. package/src/mcp/server.ts +54 -0
  342. package/src/native-tool-catalog.ts +2 -2
  343. package/src/nexus/api/consult-requests.ts +18 -6
  344. package/src/nexus/api/discord-trigger-bot.ts +6 -6
  345. package/src/nexus/api/graphs-api.ts +4 -2
  346. package/src/nexus/api/grokbot-trigger-bot.ts +206 -0
  347. package/src/nexus/api/telegram-trigger-bot.ts +6 -6
  348. package/src/nexus/boot/register-mcp-clients.ts +114 -8
  349. package/src/nexus/index.ts +15 -0
  350. package/src/nexus/outbound/grokbot-adapter.ts +71 -0
  351. package/src/nexus/outbound/router.ts +33 -13
  352. package/src/oauth/codex-owner-mirror.ts +47 -10
  353. package/src/onboarding/first-run.ts +44 -6
  354. package/src/preview/terminal.ts +22 -7
  355. package/src/prompt-enhance/enhance.ts +10 -0
  356. package/src/pty-shell/pty-write-provenance.ts +21 -3
  357. package/src/pty-shell/registry.ts +9 -2
  358. package/src/release-loop/proposal-auto-place.ts +83 -0
  359. package/src/research/axis-fanout.ts +40 -5
  360. package/src/research/lenses/second-order.ts +312 -0
  361. package/src/self-dev/dev-cli.ts +3 -1
  362. package/src/self-dev/draft-sweep.ts +31 -10
  363. package/src/self-dev/draft-triage-rules.ts +60 -3
  364. package/src/self-dev/run-supervisor.ts +152 -2
  365. package/src/self-implement/goal-author.ts +208 -23
  366. package/src/self-implement/graph-overlay-yaml.ts +151 -0
  367. package/src/self-implement/graph-yaml.ts +68 -2
  368. package/src/self-implement/orchestrator.ts +62 -15
  369. package/src/self-implement/test-scenario-request.ts +37 -6
  370. package/src/session/chat.ts +2 -2
  371. package/src/session-runtime/index.ts +37 -5
  372. package/src/sidetab-core/model.ts +205 -10
  373. package/src/sidetab-core/visible-agent-mission-pair.ts +51 -0
  374. package/src/sidetab-core/visible-agent-mission.ts +105 -47
  375. package/src/skills/tools/omni-search.ts +4 -2
  376. package/src/task-agent/parent-host.ts +19 -1
  377. package/src/task-agent/recall-inject.ts +98 -0
  378. package/src/task-agent/task-hand.ts +26 -7
  379. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +19 -1
  380. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +15 -0
  381. package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +21 -9
  382. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +1 -1
  383. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +460 -96
  384. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +45 -3
  385. package/src/task-orchestrator/surfaces/self-implement-pod.ts +38 -8
  386. package/src/tool-hints/gate.ts +45 -14
  387. package/src/tool-hints/signals.ts +1 -1
  388. package/src/tool-hints/types.ts +2 -0
  389. package/src/tool-runtime/browser-hand-runtime.ts +11 -1
  390. package/src/tool-runtime/browser-runtime.ts +31 -18
  391. package/src/tool-runtime/elanous-linear-secretary-runtime.ts +152 -36
  392. package/src/tool-runtime/types.ts +2 -0
  393. package/src/tui/entry.ts +27 -7
  394. package/src/tui/sidetab/attach.ts +112 -0
  395. package/src/tui/sidetab/list.ts +3 -2
  396. package/src/tui-client/next-dashboard-runtime.ts +52 -0
  397. package/src/ux-sim/draft-inventory-builder.ts +2 -1
  398. package/src/ux-sim/linear-secretary.ts +4 -12
  399. package/src/ux-sim/mock-scenario-catalog.json +32 -29
  400. package/src/ux-sim/mock-scenario-catalog.ts +55 -3
  401. package/src/ux-sim/release-sim-rig.ts +62 -18
  402. package/src/ux-sim/service-key-setup.ts +63 -0
  403. package/src/version/packed-revision.json +1 -1
  404. package/apps/pwa/out/_next/static/chunks/114.2b90424d1a3ce84c.js +0 -1
  405. package/apps/pwa/out/_next/static/chunks/1151-5c2bb8f002ca2dd6.js +0 -1
  406. package/apps/pwa/out/_next/static/chunks/1261-a616d5728e02e624.js +0 -1
  407. package/apps/pwa/out/_next/static/chunks/1357.3bbef3976c37f94a.js +0 -1
  408. package/apps/pwa/out/_next/static/chunks/1362-20adddfaf662b0fa.js +0 -1
  409. package/apps/pwa/out/_next/static/chunks/1737-9491fc6ab29b9ec6.js +0 -1
  410. package/apps/pwa/out/_next/static/chunks/1803-4f91a8e7dcaf042c.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/1961.a81b12872c3e93a9.js +0 -23
  412. package/apps/pwa/out/_next/static/chunks/1970cf59.2d4d8ce5d6c81853.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/1971-8890f3962ba7ee0a.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/2073-ba274e00e4f9efb4.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/2268-53861f339e0b65a2.js +0 -1
  416. package/apps/pwa/out/_next/static/chunks/273.f156eee28f11af77.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/2873.d2140e5a82546401.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/3091.a5a4143dea33d197.js +0 -90
  419. package/apps/pwa/out/_next/static/chunks/3095.dc44586a5f83b86d.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/3113.d7472bbc27d580c7.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/3228-9650b6447b6fa288.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/360.ae9f9f4c33c395e8.js +0 -79
  423. package/apps/pwa/out/_next/static/chunks/3638.80412ea63aae8f3f.js +0 -286
  424. package/apps/pwa/out/_next/static/chunks/3744.54f87db52acfd1ab.js +0 -1
  425. package/apps/pwa/out/_next/static/chunks/4311-6dd3144002cc0c2d.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/4426-2b230fad00c88930.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/4445.7990c76b3751a918.js +0 -115
  428. package/apps/pwa/out/_next/static/chunks/4447-14c5161fa59166c7.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/4856-b0b8075bba8c53f4.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/4870-de370d65e73c0bb5.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/4885-2c989165c4b29fad.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/4894.fec2ca9e99caefa2.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/5093.8f04b7f8cb899a95.js +0 -94
  434. package/apps/pwa/out/_next/static/chunks/5122-2bb177cebf89e19a.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/5154.5f56dc3902e3d275.js +0 -36
  436. package/apps/pwa/out/_next/static/chunks/529.64ff7257180b2ac9.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/5394.5dabeb0c62293a28.js +0 -93
  438. package/apps/pwa/out/_next/static/chunks/5461.08bf2404ff38ec85.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/5474-22e08a85583209b2.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/5567-265df39ed8fe9dcb.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/5570-a94ae19811cb2046.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/5574-b47c9ef2dceff1fd.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/5730.43d7ccc09988e646.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/5824.da9527b15a3221e5.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/5963-c4d8469c52a0828b.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/6001.ec4049ceb3c43418.js +0 -27
  447. package/apps/pwa/out/_next/static/chunks/6120-471f03db1e299c0c.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/6175-41479e643aca1878.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/6291.a2de4f9787637075.js +0 -104
  450. package/apps/pwa/out/_next/static/chunks/6400-6699907c3537436a.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/6428.1f4327d6ae5fdf9a.js +0 -287
  452. package/apps/pwa/out/_next/static/chunks/644.02cb026fa44bcd0a.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/6466-6c87d60d3dd174fb.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/6773.d4264665ab31bec0.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/6822.3c789f1a8443e774.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/7261-8ff69d3b2ee671fd.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/7266.084c6bbc70035140.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/7271.5c0100e7320aa856.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/7429-bf5731358c8f2f68.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/7464.01f7a0ae1215fbe7.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/7479.0fcad5f37eb64f0c.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/7483.427a2fe2049e388d.js +0 -148
  463. package/apps/pwa/out/_next/static/chunks/7502-be74718178e13e56.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/7530.0889722b19572065.js +0 -24
  465. package/apps/pwa/out/_next/static/chunks/7645.5b2ee3ad45c55ea3.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/7788.7d1c79f1a8e18946.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/7966.f71c8958d1637548.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/8073.f6cf2f2c71aafbd0.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/8201.190b94088980f430.js +0 -74
  470. package/apps/pwa/out/_next/static/chunks/8447.7963352ef5f71b76.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/8574-868e8129525ef6d9.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/868.6e953be05bcf6dca.js +0 -41
  473. package/apps/pwa/out/_next/static/chunks/8706-e1fe0309343a2273.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/8749-06384cdfc4520add.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/8861-cc1fa1c7b920fa8e.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/8967-7dfebf710c9dec9d.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/8997-3c564dbb3b58b055.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/923.2034d10ae91e9c20.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/9312.bc1b89c5d4368033.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/943.39ae5e82f4f1893f.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/9469.0500480de55fb205.js +0 -32
  482. package/apps/pwa/out/_next/static/chunks/9810-2543267050e58b82.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/9870.8419702475254745.js +0 -8
  484. package/apps/pwa/out/_next/static/chunks/9941-376d1b74d51b01a4.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/9958.385434ab04241758.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-deea1450b17cb8dc.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/approvals/page-70985b904f8650ef.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a2e15e33e56a4353.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/board/page-fc72ce2db12d6ca9.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/botlab/page-be03e41f9436ebd9.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/bots/page-35a35999382da3f2.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/ceo/page-3ff5b1e6ca88b848.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/chat/page-a65101300a0ee5c1.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/consult/page-3aca5b6d67dba511.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/control/page-45b8ab0a71e61314.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/decisions/page-53046e282d4cda8e.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/design-check/page-17809e60e8354c17.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/editor/page-fd2442d347bf5898.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/exec/page-e9d720d6fc082e21.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/field/page-6b8b4267bab0a677.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/app/healthz/route-deea1450b17cb8dc.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/app/inside/page-8bcf6233ac7c17af.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/app/intake/page-7cc25591cfc2126f.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/app/layout-053e871f35c9beba.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/app/live/page-eff819f0c2d5e084.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/app/live-run/page-4d7e9d8db5274a87.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/app/loops/page-2b2d8f5aa5f69ad4.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/app/market/page-0b8f92c5062e68b1.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1bd9d0fdac2ced3d.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/app/morning/page-9ee28f7df07428ea.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/app/not-found-44b7a1ff468c6648.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/app/observatory/page-cbc6523711dc1b65.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-fdad47899c2695be.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-79a8b5fee4c20871.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-03e8ac9606aeaffb.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/app/outputs/page-4cd55c55ec287e2f.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/app/page-db17b4305404161e.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/app/reflection/page-30041d848350fd02.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-c540da022dbf86b0.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9399efc538da057f.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ca2d09128c585ca4.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/app/settings/page-0e2635aecd343038.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ef7f4399b6b7726e.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/app/setup/layout-deea1450b17cb8dc.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/app/setup/page-caf530535b875468.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/app/share/page-eaa20c1543a5fd8e.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c541eb1b75120f61.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/app/tasks/page-ab5e213eda9479f4.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/app/today/page-e858ba0ed9393b68.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/app/trace/page-1638ee9dd6e0582f.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/app/vault/page-4cf5a399109391f2.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b4396321238798ec.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/app/workflows/page-cb72bfd21006781a.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6135d0242afbc434.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a1dccf907eddb856.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/c7945435-b44d4dbe90ba0dcb.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/ea514caa.05a24f78d9dffe5f.js +0 -122
  538. package/apps/pwa/out/_next/static/chunks/framework-e8a911e19b1be41f.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/main-7022f418fabba021.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/main-app-e5f15df755756a1e.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/pages/_app-4525f91e36940c9b.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/pages/_error-140f0e519c085b0f.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/webpack-8f22ef63dcb865b1.js +0 -1
  544. /package/apps/pwa/out/_next/static/{Om5xS9jpjSXJCeTNGYEi4 → HMXsxdy5MIIcHPMHv1wD4}/_ssgManifest.js +0 -0
@@ -16,11 +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
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';
21
22
 
22
23
  export type PreflightLevel = 'ok' | 'warn' | 'block';
23
- export type PreflightCheck = 'checklist' | 'postpub' | 'run-input' | 'freeze' | 'schedule' | 'export' | 'notes' | 'cut-tree' | 'gate-slot' | 'release-notes';
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';
24
26
  export interface PreflightFinding { level: PreflightLevel; check: PreflightCheck; id?: string; message: string; suggestion?: string }
25
27
  export interface GateMeasure { version: string; runId: string; gateSeconds: number; toPublishSeconds?: number }
26
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]!; };
@@ -33,6 +35,7 @@ export interface PreflightResult {
33
35
  findings: PreflightFinding[];
34
36
  schedule: { cutAt: string; landBy: string | null; publishAt: string | null; publishAtSource: 'option' | 'schedule' | 'cadence' | null; exportCheckAt: string } | null;
35
37
  exportCheck: { mainSha: string; files: number; leaks: number; codeLiterals: number } | null;
38
+ cutoffExportCheck: { commit: string; gitIgnoredLeaks: number | null; error?: string } | null;
36
39
  /** Read-only T−120 evidence. It describes checks only; it never means a release stage ran. */
37
40
  countdownReadiness: {
38
41
  status: 'green' | 'red' | 'unmeasured';
@@ -128,6 +131,12 @@ export interface PreflightDeps {
128
131
  previousVersion?: string;
129
132
  /** Injectable for deterministic preflight tests. Production pins origin/main and scans a disposable checkout. */
130
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 };
131
140
  /** Countdown T−120/T−60/T0: the cut manifest `cutoff.ts` pinned (default `<instance>/release/<v>/manifest.json`). */
132
141
  manifest?: (version: string) => CountdownManifest | null;
133
142
  /** Countdown T−60: the «node-b» gate-slot record (default `<instance>/release/<v>/gate-slot.json`). */
@@ -148,6 +157,14 @@ function runGit(cwd: string, args: string[]): string {
148
157
  return (result.stdout ?? '').trim();
149
158
  }
150
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
+
151
168
  /** Read-only main snapshot: all checkout and cleanup writes are contained in a disposable temp directory. */
152
169
  export async function scanOriginMainExport(): Promise<{ mainSha: string; files: number; leaks: number; codeLiterals: number }> {
153
170
  const repoRoot = runGit(process.cwd(), ['rev-parse', '--show-toplevel']);
@@ -225,6 +242,100 @@ export async function scanReleaseNotesAddedLines(previousVersion: string | undef
225
242
  return added;
226
243
  }
227
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;
259
+ } finally {
260
+ rmSync(temp, { recursive: true, force: true });
261
+ }
262
+ }
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
+
228
339
  const POSTPUB_ID = /^POSTPUB-/i;
229
340
  // Only a title that «starts» with the marker: a pre-publish cell often mentions «발행 뒤» in its body (a slot, a plan).
230
341
  const POSTPUB_TITLE = /^[«"'(]?\s*(발행\s*(뒤|후)|post-?publish)/i;
@@ -343,6 +454,9 @@ export async function releasePreflight(version: string, deps: PreflightDeps = {}
343
454
  // Always the stored cut (before or after it): the preflight simulates «the cut», not the moment it is run.
344
455
  const judgeAt = schedule ? new Date(Date.parse(schedule.cutAt)) : now;
345
456
  const data = listChecklist(version);
457
+ const cutoffManifest = deps.countdown
458
+ ? (deps.manifest ?? ((v: string) => readReleaseJson<CountdownManifest>(v, 'manifest.json')))(version)
459
+ : null;
346
460
  const { autoMoved, ...gate } = cutChecklistGate(version, schedule?.landBy, judgeAt);
347
461
  const p0 = data.items.filter((item) => item.status === 'yellow' && item.priority === 'P0').map((item) => item.id);
348
462
  const byId = new Map(data.items.map((item) => [item.id, item]));
@@ -387,7 +501,8 @@ export async function releasePreflight(version: string, deps: PreflightDeps = {}
387
501
 
388
502
  // 3a. Public export leakage is measured against one pinned origin/main commit, never this possibly dirty branch.
389
503
  try {
390
- exportCheck = await (deps.exportCheck ?? scanOriginMainExport)();
504
+ const scanned = await (deps.exportCheck ?? scanOriginMainExport)();
505
+ exportCheck = scanned;
391
506
  findings.push(exportCheck.leaks
392
507
  ? { level: 'block', check: 'export', message: `origin/main ${exportCheck.mainSha.slice(0, 12)} 공개 export 누수 ${exportCheck.leaks}건 · ${exportCheck.files} 파일 검사` }
393
508
  : { level: 'ok', check: 'export', message: `origin/main ${exportCheck.mainSha.slice(0, 12)} 공개 export 누수 0건 · ${exportCheck.files} 파일 검사` });
@@ -398,25 +513,90 @@ export async function releasePreflight(version: string, deps: PreflightDeps = {}
398
513
 
399
514
  // 3. The same input check `release run --dry-run` makes.
400
515
  let runPrevious: string | undefined;
516
+ let gatePodPool: string | undefined;
401
517
  try {
402
518
  const result = await (deps.runInput ?? ((v: string) => runUnattendedRelease({ version: v, dryRun: true }, deps.releaseRunDeps ?? {})))(version);
403
519
  const input = result.input as { previousVersion?: string; gatePodPool?: string };
404
520
  runPrevious = input.previousVersion;
521
+ gatePodPool = input.gatePodPool;
405
522
  findings.push({ level: 'ok', check: 'run-input', message: `release run --dry-run 입력 OK — 직전 판 ${input.previousVersion ?? '-'} · 게이트 풀 ${input.gatePodPool ?? '-'}` });
406
523
  } catch (error) {
407
524
  findings.push({ level: 'block', check: 'run-input', message: `release run --dry-run 입력 오류 — ${error instanceof Error ? error.message : String(error)}` });
408
525
  }
409
526
 
410
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;
411
535
  if (deps.countdown) {
412
536
  try {
413
537
  notesAddedLines = await (deps.releaseNotesCheck ?? scanReleaseNotesAddedLines)(runPrevious, exportCheck?.mainSha);
414
538
  findings.push(notesAddedLines > 0
415
- ? { level: 'ok', check: 'release-notes', message: `직전 발행 ${runPrevious} 이후 고정 main의 release/next.md 추가 ${notesAddedLines}줄` }
539
+ ? { level: 'ok', check: 'release-notes', message: `직전 발행 ${runPrevious ?? '-'} 이후 고정 main의 release/next.md 추가 ${notesAddedLines}줄` }
416
540
  : { level: 'block', check: 'release-notes', message: `직전 발행 ${runPrevious ?? '-'} 이후 고정 main의 release/next.md 추가 줄이 없다` });
417
541
  } catch (error) {
418
- findings.push({ level: 'warn', check: 'release-notes', message: `release/next.md 변경을 측정하지 못했다 — ${error instanceof Error ? error.message : String(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 빌드를 통과시킨다` });
419
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 를 다시 실행한다` });
420
600
  }
421
601
 
422
602
  // 4. Landing freeze (emergency switch since RELEASE-BRANCH): when on, `release run` refuses.
@@ -488,7 +668,7 @@ export async function releasePreflight(version: string, deps: PreflightDeps = {}
488
668
  const blockers = findings.filter((f) => f.level === 'block').length;
489
669
  const warnings = findings.filter((f) => f.level === 'warn').length;
490
670
  debug.log('release.preflight', 'checked', { version, blockers, warnings });
491
- return { version, next, verdict: blockers ? 'block' : warnings ? 'warn' : 'ok', blockers, warnings, findings, schedule: scheduleOut, exportCheck, countdownReadiness, previousVersion, gateMeasure, fallbackGateMeasure };
671
+ return { version, next, verdict: blockers ? 'block' : warnings ? 'warn' : 'ok', blockers, warnings, findings, schedule: scheduleOut, exportCheck, cutoffExportCheck, countdownReadiness, previousVersion, gateMeasure, fallbackGateMeasure };
492
672
  }
493
673
 
494
674
  const ICON: Record<PreflightLevel, string> = { ok: '✅', warn: '⚠️', block: '⛔' };
@@ -507,13 +687,13 @@ const COUNTDOWN_OFFSET_MINUTES: Record<CountdownStage, number> = { 'T-120': 120,
507
687
  /** Which existing finding group each stage judges — the check vocabulary of `releasePreflight`, not a new rule set. */
508
688
  const COUNTDOWN_CHECKS: Record<CountdownStage, PreflightCheck[]> = {
509
689
  'T-120': ['checklist', 'postpub', 'notes', 'release-notes', 'export'],
510
- 'T-60': ['run-input', 'freeze', 'cut-tree', 'gate-slot'],
690
+ 'T-60': ['run-input', 'freeze', 'cut-tree', 'gate-slot', 'cutoff-notes', 'gitignored-export', 'pwa-build', 'gate-image'],
511
691
  'T-30': ['schedule'],
512
692
  T0: [],
513
693
  };
514
694
  const COUNTDOWN_LABELS: Record<CountdownStage, string> = {
515
695
  'T-120': '노트(next.md 새 줄) · 공개 export 누수 · 체크리스트 게이트 모의',
516
- 'T-60': 'preflight(컷 트리 고정) ⊕ node-b 게이트 자리(디스패처 정지 · podTarget 감축)',
696
+ 'T-60': '컷 후보 P0 네 항목(노트 · gitignore export · PWA · gate 이미지) ⊕ node-b 게이트 자리',
517
697
  'T-30': 'gate window 준비(컷→발행 창 ≥ 직전 실측 게이트)',
518
698
  T0: '컷 트리 확인(실제 컷 실행은 별도)',
519
699
  };
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bun
2
+ import { readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { DEFAULT_CONTROL_PORT } from '../../src/control-plane/server.js';
5
+
6
+ const args = process.argv.slice(2);
7
+ const imageIndex = args.indexOf('--image');
8
+ const namespaceIndex = args.indexOf('--namespace');
9
+ const image = imageIndex >= 0 ? args[imageIndex + 1] : undefined;
10
+ const namespace = namespaceIndex >= 0 ? args[namespaceIndex + 1] : undefined;
11
+ if (args.length !== 4 || imageIndex < 0 || namespaceIndex < 0 || imageIndex === namespaceIndex
12
+ || !image || !namespace || !/^[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?$/.test(namespace)
13
+ || !/^[a-z0-9][a-z0-9.-]*(?::[0-9]+)?\/[a-zA-Z0-9._/-]+@sha256:[a-f0-9]{64}$/.test(image)) {
14
+ process.stderr.write('usage: bun scripts/release-loop/render-warm-control-manifest.ts --image <repository@sha256:digest> --namespace <namespace>\n');
15
+ process.exitCode = 2;
16
+ } else {
17
+ const template = readFileSync(join(import.meta.dir, '../../docker/warm-control/control.yaml.tmpl'), 'utf8');
18
+ if (!template.includes('{{CONTROL_IMAGE}}') || !template.includes('{{NAMESPACE}}') || !template.includes('{{CONTROL_PORT}}'))
19
+ throw new Error('warm control manifest placeholder missing');
20
+ process.stdout.write(template.replaceAll('{{CONTROL_IMAGE}}', image)
21
+ .replaceAll('{{NAMESPACE}}', namespace).replaceAll('{{CONTROL_PORT}}', String(DEFAULT_CONTROL_PORT)));
22
+ }
@@ -17,7 +17,7 @@ import { changedFilesBetween, clearPartialPlan, collectExecutedPodShardImages, p
17
17
  import { gateAssignableTestsAtCommit, gateEnvFingerprint } from './gate-baseline-receipt.js';
18
18
  import { readGateShardManifest, readGateShardResult } from './gate-shard-receipts.js';
19
19
  import { gitClosureTree, testsReachingChanged, TRACEABLE_RELEASE_JSON, type ClosureTree } from './gate-closure.js';
20
- import { assertReleaseResumeApprovalBoundary, releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeGatePodPool, releaseResumeGateShardReroute, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
20
+ import { assertReleaseResumeApprovalBoundary, releaseFanOutForkIndex, releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeGatePodPool, releaseResumeGateShardReroute, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
21
21
  import { reserveReleaseResumeAttempt } from './resume-cap.js';
22
22
  import { cutReleaseBranch } from './cut-branch.js';
23
23
  import { releaseLoopConfig } from './unattended-release.js';
@@ -138,8 +138,9 @@ function refreshReleaseBranchTipInternal(runId: string, deps: ResumeReleaseDeps,
138
138
  const state = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
139
139
  if (state.graphId !== 'release-loop' || state.runId !== runId) throw new Error(`run identity mismatch: ${statePath}`);
140
140
  if (state.status !== 'failed') throw new Error(`release run ${runId} is ${state.status} — only a failed run is resumed`);
141
- const restartAt = from === undefined ? -1 : state.path.indexOf(from);
142
- if (from !== undefined && (restartAt < 0 || state.path.lastIndexOf(from) !== restartAt)) throw new Error(`--from ${from} is not a node on the saved path of ${runId}`);
141
+ const branchFork = from === undefined || state.path.includes(from) ? undefined : releaseFanOutForkIndex(state, from);
142
+ const restartAt = from === undefined ? -1 : state.path.indexOf(from) >= 0 ? state.path.indexOf(from) : branchFork ?? -1;
143
+ if (from !== undefined && (restartAt < 0 || (branchFork === undefined && state.path.lastIndexOf(from) !== restartAt))) throw new Error(`--from ${from} is not a node on the saved path of ${runId}`);
143
144
  const index = state.nodes.findIndex((node) => node.nodeId === 'version-release');
144
145
  const record = index >= 0 ? state.nodes[index]! : undefined;
145
146
  const output = record ? lastJsonObject(record.output) : undefined;
@@ -490,7 +491,7 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
490
491
  const path = dryRun ? undefined : writeRecheckPlan(root, recheckPlan);
491
492
  debug.log('release-loop.resume', 'interference-recheck-planned', { version: tip.version, commit: tip.to,
492
493
  remeasureFileCount: recheckPlan.files.length, fallbackReason: null, ...(path ? { path } : {}), dryRun });
493
- return { requested: true, applied: true, files: recheckPlan.files.length, priorCommit: tip.to, ...(auto ? { auto: true as const } : {}) };
494
+ return { requested: true, applied: true, files: recheckPlan.files.length, plannedFiles: recheckPlan.files, priorCommit: tip.to, ...(auto ? { auto: true as const } : {}) };
494
495
  }
495
496
  const stalled = previousGate?.stalledShards;
496
497
  const summary = previousGate?.partialSummary as { pass?: unknown; fail?: unknown; errors?: unknown; ran?: unknown; files?: unknown } | undefined;
@@ -664,7 +665,7 @@ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: Resu
664
665
  waiverCommit = recorded;
665
666
  }
666
667
  const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, true);
667
- const fromIndex = savedRun.path.indexOf(opts.from);
668
+ const fromIndex = savedRun.path.indexOf(opts.from) >= 0 ? savedRun.path.indexOf(opts.from) : releaseFanOutForkIndex(savedRun, opts.from) ?? -1;
668
669
  const gateIndex = savedRun.path.indexOf('gate');
669
670
  const gateWillRun = fromIndex >= 0 && gateIndex >= fromIndex;
670
671
  if (opts.rerouteShard !== undefined && tip.changed) throw new Error('shard reroute requires the original cut head; moved tip needs a full re-gate');
@@ -929,8 +930,9 @@ async function recoverReleaseRunLocked(opts: RecoverReleaseOptions, deps: Recove
929
930
  if (fromNode === 'version-release') {
930
931
  throw new Error(`--from version-release would cut again — recover at a later node; ${branch} was not moved`);
931
932
  }
932
- const restartAt = savedRun.path.indexOf(fromNode);
933
- if (restartAt < 0 || savedRun.path.lastIndexOf(fromNode) !== restartAt) {
933
+ const branchFork = savedRun.path.includes(fromNode) ? undefined : releaseFanOutForkIndex(savedRun, fromNode);
934
+ const restartAt = savedRun.path.indexOf(fromNode) >= 0 ? savedRun.path.indexOf(fromNode) : branchFork ?? -1;
935
+ if (restartAt < 0 || (branchFork === undefined && savedRun.path.lastIndexOf(fromNode) !== restartAt)) {
934
936
  throw new Error(`--from ${fromNode} is not a node on the saved path of ${opts.runId}; ${branch} was not moved`);
935
937
  }
936
938
  const consumers = ['export-check', 'gate'].map((node) => savedRun.path.indexOf(node)).filter((index) => index >= 0);
@@ -5,13 +5,22 @@ import { checkGateExecutorChoice, type GateExecutor } from './gate-executor.js';
5
5
 
6
6
  export interface AcceptedRegression { id: string; note: string }
7
7
 
8
+ /** Return the saved fork-node index when --from names a failed fan-out branch rather than a parent-path node. */
9
+ export function releaseFanOutForkIndex(state: GraphRunState, branchId: string): number | undefined {
10
+ const matches = Object.values(state.fanOuts ?? {}).filter((fanout) => fanout.branches[branchId]?.status === 'failed');
11
+ if (matches.length !== 1) return undefined;
12
+ const fork = matches[0]!.from;
13
+ const index = state.path.indexOf(fork);
14
+ return index >= 0 && state.path.lastIndexOf(fork) === index ? index : undefined;
15
+ }
16
+
8
17
  /** Match the graph runner's approval boundary before release resume performs tip refresh or writes an attempt. */
9
18
  export function assertReleaseResumeApprovalBoundary(state: GraphRunState, from: string): void {
10
19
  if (!Array.isArray(state.path)) return;
11
- const start = state.path.indexOf(from);
12
- if (start < 0 || state.path.lastIndexOf(from) !== start) {
13
- return;
14
- }
20
+ const pathStart = state.path.indexOf(from);
21
+ const branchStart = pathStart < 0 ? releaseFanOutForkIndex(state, from) : undefined;
22
+ const start = pathStart >= 0 ? pathStart : branchStart;
23
+ if (start === undefined || start < 0 || (pathStart >= 0 && state.path.lastIndexOf(from) !== start)) return;
15
24
  for (let index = 0; index < state.nodes.length; index++) {
16
25
  const record = state.nodes[index]!;
17
26
  if (typeof record.decidedAt !== 'string') continue;
@@ -0,0 +1,178 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createServer } from 'node:net';
3
+ import { debug } from '../../src/debug/log.js';
4
+
5
+ export interface WarmControlForwardOptions {
6
+ context: string;
7
+ namespace: string;
8
+ service: string;
9
+ remotePort: number;
10
+ kubectl?: string;
11
+ startupTimeoutMs?: number;
12
+ }
13
+
14
+ export interface WarmControlForward {
15
+ url: string;
16
+ stop(): Promise<void>;
17
+ }
18
+
19
+ /** A single-flight startup cache owned by one gate-run lifecycle. A rejected start
20
+ * is shared by that run's shards, while a later run gets a fresh instance and retries. */
21
+ export function createRunScopedWarmControlForward(start: () => Promise<WarmControlForward>): {
22
+ ensure(): Promise<WarmControlForward>;
23
+ stop(): Promise<void>;
24
+ } {
25
+ let starting: Promise<WarmControlForward> | null = null;
26
+ return {
27
+ ensure() {
28
+ starting ??= start();
29
+ return starting;
30
+ },
31
+ async stop() {
32
+ const pending = starting;
33
+ starting = null;
34
+ const forward = pending ? await pending.catch(() => null) : null;
35
+ if (forward) await forward.stop();
36
+ },
37
+ };
38
+ }
39
+
40
+ type ForwardExit = { code: number | null; signal: NodeJS.Signals | null };
41
+
42
+ function exitWithin(exited: Promise<ForwardExit>, timeoutMs: number): Promise<ForwardExit | null> {
43
+ let timer: ReturnType<typeof setTimeout> | undefined;
44
+ return Promise.race([exited, new Promise<null>(resolve => { timer = setTimeout(() => resolve(null), timeoutMs); })])
45
+ .finally(() => { if (timer) clearTimeout(timer); });
46
+ }
47
+
48
+ function ephemeralLoopbackPort(): Promise<number> {
49
+ return new Promise((resolve, reject) => {
50
+ const server = createServer();
51
+ server.once('error', reject);
52
+ server.listen(0, '127.0.0.1', () => {
53
+ const address = server.address();
54
+ if (!address || typeof address === 'string') {
55
+ server.close();
56
+ reject(new Error('warm control forward could not reserve a loopback port'));
57
+ return;
58
+ }
59
+ server.close(error => error ? reject(error) : resolve(address.port));
60
+ });
61
+ });
62
+ }
63
+
64
+ function cleanKubectlEnv(): NodeJS.ProcessEnv {
65
+ // Port-forward carries control credentials and task payloads; never route the tunnel through ambient proxies.
66
+ const env = { ...process.env };
67
+ for (const key of ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy']) delete env[key];
68
+ return env;
69
+ }
70
+
71
+ /** Start a run-owned, loopback-only tunnel. No remote interface is ever bound. */
72
+ async function startWarmControlPortForwardAt(options: WarmControlForwardOptions, localPort: number): Promise<WarmControlForward> {
73
+ const child = spawn(options.kubectl ?? 'kubectl', [
74
+ '--context', options.context, '-n', options.namespace, 'port-forward',
75
+ '--address', '127.0.0.1', `service/${options.service}`, `${localPort}:${options.remotePort}`,
76
+ ], { detached: true, stdio: ['ignore', 'pipe', 'pipe'], env: cleanKubectlEnv() });
77
+ let stdout = '';
78
+ let stderr = '';
79
+ let stopRequested = false;
80
+ let failStartup: ((error: Error) => void) | undefined;
81
+ const startedAt = Date.now();
82
+ const stopGroup = (signal: NodeJS.Signals) => {
83
+ if (child.pid) {
84
+ try { process.kill(-child.pid, signal); } catch { child.kill(signal); }
85
+ } else child.kill(signal);
86
+ };
87
+ const exited = new Promise<ForwardExit>(resolve => {
88
+ child.once('close', (code, signal) => resolve({ code, signal }));
89
+ child.once('error', () => resolve({ code: null, signal: null }));
90
+ });
91
+ child.stderr?.on('data', chunk => {
92
+ stderr = `${stderr}${String(chunk)}`.slice(-8192);
93
+ if (/address already in use/i.test(stderr)) failStartup?.(new Error('warm control local port was claimed before kubectl bound it'));
94
+ });
95
+
96
+ try {
97
+ await new Promise<void>((resolve, reject) => {
98
+ let settled = false;
99
+ const timer = setTimeout(() => finish(new Error('warm control port-forward startup timed out')), options.startupTimeoutMs ?? 15_000);
100
+ const ready = (chunk: Buffer | string) => {
101
+ if (new RegExp(`Forwarding from 127\\.0\\.0\\.1:${localPort} -> ${options.remotePort}`).test(stdout)) finish();
102
+ };
103
+ const capture = (chunk: Buffer | string) => { stdout = `${stdout}${String(chunk)}`.slice(-8192); };
104
+ const finish = (error?: Error) => {
105
+ if (settled) return;
106
+ settled = true;
107
+ failStartup = undefined;
108
+ clearTimeout(timer);
109
+ child.stdout?.off('data', ready);
110
+ if (error) reject(error); else resolve();
111
+ };
112
+ failStartup = finish;
113
+ child.stdout?.on('data', capture);
114
+ child.stdout?.on('data', ready);
115
+ void exited.then(({ code, signal }) => finish(new Error(`warm control port-forward exited before ready (code=${code}, signal=${signal})`)));
116
+ child.once('error', error => finish(error));
117
+ });
118
+ } catch (error) {
119
+ stopRequested = true;
120
+ if (child.exitCode === null && child.signalCode === null) stopGroup('SIGTERM');
121
+ let final = await exitWithin(exited, 2_000);
122
+ if (final === null) {
123
+ stopGroup('SIGKILL');
124
+ final = await exitWithin(exited, 2_000);
125
+ }
126
+ debug.log('pod.warm.control-forward', 'start-failed', { context: options.context, namespace: options.namespace,
127
+ service: options.service, localPort, elapsedMs: Date.now() - startedAt,
128
+ reason: error instanceof Error ? error.message : String(error), stderr: stderr.slice(-1000), orphaned: final === null },
129
+ { level: final === null ? 'error' : 'warn' });
130
+ if (final === null) debug.log('pod.warm.control-forward', 'orphan-detected', { context: options.context,
131
+ namespace: options.namespace, service: options.service, localHost: '127.0.0.1', localPort }, { level: 'error' });
132
+ throw error;
133
+ }
134
+ void exited.then(result => {
135
+ if (!stopRequested) debug.log('pod.warm.control-forward', 'exited-unexpectedly', {
136
+ context: options.context, namespace: options.namespace, service: options.service,
137
+ localHost: '127.0.0.1', localPort, exitCode: result.code, exitSignal: result.signal,
138
+ }, { level: 'error' });
139
+ });
140
+ debug.log('pod.warm.control-forward', 'started', { context: options.context, namespace: options.namespace,
141
+ service: options.service, localHost: '127.0.0.1', localPort, remotePort: options.remotePort });
142
+
143
+ return {
144
+ url: `http://127.0.0.1:${localPort}/`,
145
+ async stop() {
146
+ if (stopRequested) return;
147
+ stopRequested = true;
148
+ if (child.exitCode === null && child.signalCode === null) stopGroup('SIGTERM');
149
+ const result = await exitWithin(exited, 2_000);
150
+ if (result === null) stopGroup('SIGKILL');
151
+ const final = result ?? await exitWithin(exited, 2_000);
152
+ const orphaned = final === null;
153
+ debug.log('pod.warm.control-forward', orphaned ? 'orphan-detected' : 'stopped', {
154
+ context: options.context, namespace: options.namespace, service: options.service,
155
+ localHost: '127.0.0.1', localPort, remotePort: options.remotePort,
156
+ elapsedMs: Date.now() - startedAt, exitCode: final?.code ?? null, exitSignal: final?.signal ?? null,
157
+ }, { level: orphaned ? 'error' : 'info' });
158
+ if (orphaned) throw new Error('warm control port-forward process survived gate cleanup');
159
+ },
160
+ };
161
+ }
162
+
163
+ export async function startWarmControlPortForward(options: WarmControlForwardOptions): Promise<WarmControlForward> {
164
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(options.context)
165
+ || !/^[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?$/.test(options.namespace)
166
+ || !/^[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?$/.test(options.service)
167
+ || !Number.isSafeInteger(options.remotePort) || options.remotePort < 1 || options.remotePort > 65535)
168
+ throw new Error('invalid warm control forward target');
169
+ for (let attempt = 0; attempt < 2; attempt++) {
170
+ const localPort = await ephemeralLoopbackPort();
171
+ try { return await startWarmControlPortForwardAt(options, localPort); }
172
+ catch (error) {
173
+ if (attempt === 0 && error instanceof Error && /local port was claimed/i.test(error.message)) continue;
174
+ throw error;
175
+ }
176
+ }
177
+ throw new Error('warm control port-forward could not claim a loopback port');
178
+ }
@@ -21,6 +21,11 @@ import { aggregateMissionRetros } from '../src/autopilot/mission-retrospect.js';
21
21
  import { recordAutonomousActionSafe } from '../src/domains/autonomy-log.js';
22
22
  import { sendOutbound } from '../src/domains/outbound-alert.js';
23
23
  import { unknownCronFlag } from '../src/domains/cron-flag-contract.js';
24
+ import { debug } from '../src/debug/log.js';
25
+ import { registerStandaloneLogSink } from '../src/domains/standalone-log-sink.js';
26
+ import { finishRetroCycleRun } from '../src/domains/retro-cycle-observation.js';
27
+ import { readRunExits } from '../src/harness/harness-incidents.js';
28
+ import { doctorVisitIncidentRoot, readDoctorVisits } from '../src/self-implement/doctor-visit.js';
24
29
 
25
30
  // ⛔⭐ 거부는 «맨 앞»이다 — 이 파일은 모듈 최상위에서 argv 를 읽으므로 그 뒤면 늦다.
26
31
  // ⛔ 그리고 «한 줄»로 낸다 — 최상위 throw 는 스택 트레이스를 흘리고, 크론 로그에서 그것은 잡음이다.
@@ -32,6 +37,10 @@ const pIdx = process.argv.indexOf('--period');
32
37
  const period = (pIdx >= 0 ? process.argv[pIdx + 1] : 'weekly') as RetroPeriod;
33
38
  if (!['weekly', 'monthly', 'quarterly', 'annual'].includes(period)) { console.error(`잘못된 period: ${period}`); process.exit(1); }
34
39
 
40
+ // This scheduled script runs outside the daemon; attach its debug events to
41
+ // this instance's logs.db so the lifecycle observations remain queryable.
42
+ await registerStandaloneLogSink('retro');
43
+
35
44
  const REPORT_DIR = join(homedir(), '.elanous/conatus/reflections');
36
45
  const LOG = join(homedir(), '.elanous/conatus/retro_cycle.log');
37
46
  const log = (m: string): void => { try { appendFileSync(LOG, `${new Date().toISOString()} ${m}\n`); } catch { /* */ } };
@@ -114,6 +123,14 @@ const report = await runRetroCycle({
114
123
  // C3 (2026-07-18) — 미션 회고(C1 안착·C2 피드백) 집계를 주간 리포트에 합류(비파괴 opt-in).
115
124
  retro: { backtest: backtestAgg, regime: regimeAgg, surface: surfaceAgg, missions: (from, to) => aggregateMissionRetros(from, to) },
116
125
  candidates,
126
+ featureSignals: () => {
127
+ const incidents = readRunExits(doctorVisitIncidentRoot())
128
+ .map((row) => ({ runId: row.runId, symptom: row.reason, at: row.at, source: 'incident' as const }));
129
+ const visits = readDoctorVisits()
130
+ .filter((row) => !!row.timestamp)
131
+ .map((row) => ({ runId: row.runId, symptom: row.symptom, at: row.timestamp!, source: 'doctor-visit' as const }));
132
+ return [...incidents, ...visits];
133
+ },
117
134
  // LLM 정성 서사(fail-soft) — provider 없으면 정량 base 그대로.
118
135
  narrate: narrateRetro,
119
136
  writeReport: (filename, md) => {
@@ -127,15 +144,20 @@ const report = await runRetroCycle({
127
144
  notify: (t) => { log(`[HITL] ${t.replace(/\n/g, ' ')}`); if (!process.argv.includes('--collect-only')) try { sendOutbound(t, 'ops-alert'); } catch { /* fail-soft */ } },
128
145
  }, period);
129
146
 
130
- log(`retro ${period}: report=${report.reportPath} · 제안=${report.hasProposal ? report.proposalId : '없음'}`);
147
+ log(`retro ${period}: report=${report.reportPath} · 리밸런싱=${report.hasProposal ? report.proposalId : '없음'} · 반복 증상 제안=${report.featureProposals.length}`);
131
148
 
132
149
  // Autopilot P0.2 — 자율행동(회고 루프) 회상 로깅. 리밸런싱 제안은 HITL 승인 대기(자율집행 아님).
133
- recordAutonomousActionSafe({
134
- loop: 'retro',
135
- action: `${period} 회고 리포트 발행`,
136
- rationale: '기간 성과 집계(백테스트·국면·발송) → 리밸런싱 제안(HITL 승인 대기)',
137
- outcome: report.hasProposal ? `리밸런싱 제안 ${report.proposalId}(승인 대기)` : '제안 없음',
138
- refs: { reportPath: report.reportPath },
150
+ const runId = finishRetroCycleRun(report, {
151
+ recordAction: ({ runId: actionRunId, reportPath }) => {
152
+ recordAutonomousActionSafe({
153
+ loop: 'retro',
154
+ action: `${period} 회고 리포트 발행`,
155
+ rationale: '기간 성과 집계(백테스트·국면·발송) → 리밸런싱 제안(HITL 승인 대기)',
156
+ outcome: report.hasProposal ? `리밸런싱 제안 ${report.proposalId}(승인 대기)` : '제안 없음',
157
+ refs: { runId: actionRunId, reportPath },
158
+ });
159
+ },
160
+ log: (category, event, data) => debug.log(category, event, data),
139
161
  });
140
162
 
141
- console.log(JSON.stringify(report, null, 2));
163
+ console.log(JSON.stringify({ ...report, runId }, null, 2));