elanous 0.2.24 → 0.2.27

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 (601) 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/{Q-7iofoxOEv1WX18S35of → Ev2jivDMTSoEc-5rIXdcy}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1027.a0f04663d24c2779.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +36 -0
  9. package/apps/pwa/out/_next/static/chunks/1146.80d15ea7c6e6ee95.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/1157.f08bf2b70915e176.js +148 -0
  11. package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1454-a9c338f5bb556f42.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/{8532.a86e99acf9b761cb.js → 1516.83dd2c1b86b4e507.js} +2 -2
  17. package/apps/pwa/out/_next/static/chunks/{89849432.9eaa2f4c9c010965.js → 16e57f67.3ea19e893461e611.js} +3 -3
  18. package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/{1505.5826671109111e75.js → 1743.563d3168e95825ef.js} +2 -2
  20. package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +27 -0
  21. package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2528.3868070ef1be69f9.js +24 -0
  26. package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2697.76ca2ac3efef6f3b.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +32 -0
  29. package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2846.d6a433b8663819a5.js +309 -0
  31. package/apps/pwa/out/_next/static/chunks/3143.257997a5f1ed79df.js +34 -0
  32. package/apps/pwa/out/_next/static/chunks/{5967-c0a80f8ffe5d4050.js → 3259-86f3be56e43eaf6a.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3309.364573ceedbdfd5f.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +287 -0
  37. package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/{5578.ac963634373b28c6.js → 4084.15ecaf4240bd9e77.js} +2 -2
  42. package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/{8438.ee7f0df3b3f6fcb4.js → 4204.a409ab6250c0f6a8.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +8 -0
  48. package/apps/pwa/out/_next/static/chunks/4565-67e4c620597965c8.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/{7905.b230b9d06cb4ad5d.js → 4593.10f5b9bd702d4a6c.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +104 -0
  52. package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +149 -0
  54. package/apps/pwa/out/_next/static/chunks/{8569.b853a8daa8f31d12.js → 5081.a240b63fad28663a.js} +1 -1
  55. package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/{2528.0af5c0e1fad88df6.js → 5408.377c8c543635b81f.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/{9502-32c5dc22e8f120f5.js → 5492-e05e26a033ba80d2.js} +3 -3
  62. package/apps/pwa/out/_next/static/chunks/5785-ea8d2c633b6688e3.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/599-a914cc0468f56143.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +23 -0
  68. package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/{5974.360f8441eb0ac1ec.js → 6196.34d6b229079e9e28.js} +1 -1
  71. package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +77 -0
  73. package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +64 -0
  75. package/apps/pwa/out/_next/static/chunks/{ce383d63.d2ede338788ed18e.js → 65ebff95.081daeff622d238e.js} +1 -1
  76. package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +2 -0
  78. package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/{83cfc5c8.349535543c1548b6.js → 6def8a8e.c81f9ca41028eccf.js} +1 -1
  80. package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/{2721.bc9a8f36bd85c07a.js → 7483.ba01c6c209b81bba.js} +1 -1
  85. package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +62 -0
  88. package/apps/pwa/out/_next/static/chunks/{6702.ebbd165d5e4a4a14.js → 7752.5cbdd0b433c54af9.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/{8925-2abfb48af477fb7f.js → 8085-1e5fce59500a2cc2.js} +1 -1
  95. package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +226 -0
  97. package/apps/pwa/out/_next/static/chunks/{b4e12462-5fab4a813a63dadd.js → 83a80514-b897b64df43ce108.js} +1 -1
  98. package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +93 -0
  99. package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +24 -0
  100. package/apps/pwa/out/_next/static/chunks/{8264.00e434fbd02e9311.js → 8578.dff22d988cc2b106.js} +3 -3
  101. package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +115 -0
  104. package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +41 -0
  106. package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +74 -0
  107. package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +94 -0
  108. package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{7518.49161e8f5814abdb.js → 9311.24c8f5ea793253dd.js} +1 -1
  111. package/apps/pwa/out/_next/static/chunks/{9976.8f28864317639fe6.js → 94.4cfed21c0b781a06.js} +3 -3
  112. package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/949.1e7d45ccb71580aa.js +90 -0
  114. package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/{0e3acd34.d42b2ef87fdf4234.js → 973cbf1b.3bbcf2b41548eca7.js} +1 -1
  116. package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{6571-926ddf024a6ad5df.js → 9917-9e26abc5f7772735.js} +1 -1
  119. package/apps/pwa/out/_next/static/chunks/9938.b23e4d4dfa0114cd.js +79 -0
  120. package/apps/pwa/out/_next/static/chunks/{740.90b31555290236a9.js → 9974.e77b4ff4884557b3.js} +2 -2
  121. package/apps/pwa/out/_next/static/chunks/{4c5e95ce.83e9b16add6fcd8c.js → ab4d57c2.e90bc2cb31949045.js} +7 -7
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-a1ce306364f08da9.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-07110da72c45e68f.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-d2d76d4a7eae91bd.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-30007380f2e2a3c1.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-e5a9302c105725a3.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-b93c04b5311c97ec.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-718a49e86bf25f9c.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-20662900d6b4b455.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-f707a509515ac50c.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-6a16a48d1aa6e91f.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-26345242e4049f55.js → page-fcb06e305f515527.js} +1 -1
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-6acf5ec4e6c6d1af.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-39f82acf619cca23.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-7c2c8f81b185f9c0.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-ae7aff07e32676bb.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-482c60c1bc540d0a.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-7cb996c1aa08b6f5.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-623e08fe4477d606.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-db460cd8aecb8490.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-ff27902d0b11ef4f.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-8397a5c6ca569bf4.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1917ea87ea71ab6b.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/page-25a302273b054fd7.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-670510eda8b9443f.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-d73c3f1e00d93986.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-65caacf54389081e.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4bb7556e2e97a750.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-77e04b4f8762c666.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-b92caddb5c715f30.js → page-15c0a4095baddfba.js} +1 -1
  153. package/apps/pwa/out/_next/static/chunks/app/page-4eb96fc7241c2456.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-63e272a4cd3b6634.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d64ff212dc55d03.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-7f096248993fc2d4.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e47fc72049149a38.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-f7d808e31e005895.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-5c2b24b5496dad73.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-76cdb531668b5191.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-e2b63345bba1a11f.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-d376e0c83edc041a.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d6db55fe2c04d128.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-87b88f551f421d8b.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-dab163b338e3b966.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-08019efef111ed2e.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-0260ffc7eda50db1.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-980ce6fe36ad95eb.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-99c4e6c7345a9156.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-0ab42e0f05823013.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ad9220422d20ba33.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/{53746282.1be36038d1abd949.js → e117f801.e92bf10242653497.js} +1 -1
  175. package/apps/pwa/out/_next/static/chunks/e87d89b9.2e71c0ae475d28a1.js +122 -0
  176. package/apps/pwa/out/_next/static/chunks/{0673cc54.8e2ef1c23e49e190.js → f4dfbfc0.a69fb12f854882aa.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +1 -0
  183. package/apps/pwa/out/approvals/index.html +1 -1
  184. package/apps/pwa/out/approvals/index.txt +22 -22
  185. package/apps/pwa/out/autopilot/index.html +1 -1
  186. package/apps/pwa/out/autopilot/index.txt +22 -22
  187. package/apps/pwa/out/board/index.html +1 -1
  188. package/apps/pwa/out/board/index.txt +22 -22
  189. package/apps/pwa/out/botlab/index.html +1 -1
  190. package/apps/pwa/out/botlab/index.txt +21 -21
  191. package/apps/pwa/out/bots/index.html +1 -1
  192. package/apps/pwa/out/bots/index.txt +21 -21
  193. package/apps/pwa/out/ceo/index.html +1 -1
  194. package/apps/pwa/out/ceo/index.txt +22 -22
  195. package/apps/pwa/out/chat/index.html +1 -1
  196. package/apps/pwa/out/chat/index.txt +21 -21
  197. package/apps/pwa/out/consult/index.html +1 -1
  198. package/apps/pwa/out/consult/index.txt +22 -22
  199. package/apps/pwa/out/control/index.html +1 -1
  200. package/apps/pwa/out/control/index.txt +22 -22
  201. package/apps/pwa/out/decisions/index.html +1 -1
  202. package/apps/pwa/out/decisions/index.txt +21 -21
  203. package/apps/pwa/out/design-check/index.html +1 -1
  204. package/apps/pwa/out/design-check/index.txt +22 -22
  205. package/apps/pwa/out/editor/index.html +1 -1
  206. package/apps/pwa/out/editor/index.txt +22 -22
  207. package/apps/pwa/out/exec/index.html +1 -1
  208. package/apps/pwa/out/exec/index.txt +22 -22
  209. package/apps/pwa/out/field/index.html +1 -1
  210. package/apps/pwa/out/field/index.txt +22 -22
  211. package/apps/pwa/out/healthz +1 -1
  212. package/apps/pwa/out/index.html +1 -1
  213. package/apps/pwa/out/index.txt +21 -21
  214. package/apps/pwa/out/inside/index.html +1 -1
  215. package/apps/pwa/out/inside/index.txt +21 -21
  216. package/apps/pwa/out/intake/index.html +1 -1
  217. package/apps/pwa/out/intake/index.txt +22 -22
  218. package/apps/pwa/out/live/index.html +1 -1
  219. package/apps/pwa/out/live/index.txt +22 -22
  220. package/apps/pwa/out/live-run/index.html +1 -1
  221. package/apps/pwa/out/live-run/index.txt +22 -22
  222. package/apps/pwa/out/loops/index.html +1 -1
  223. package/apps/pwa/out/loops/index.txt +21 -21
  224. package/apps/pwa/out/market/index.html +1 -1
  225. package/apps/pwa/out/market/index.txt +21 -21
  226. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  227. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  228. package/apps/pwa/out/morning/index.html +1 -1
  229. package/apps/pwa/out/morning/index.txt +22 -22
  230. package/apps/pwa/out/observatory/index.html +1 -1
  231. package/apps/pwa/out/observatory/index.txt +22 -22
  232. package/apps/pwa/out/ops/checklist/index.html +1 -1
  233. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  234. package/apps/pwa/out/ops/release/index.html +1 -1
  235. package/apps/pwa/out/ops/release/index.txt +21 -21
  236. package/apps/pwa/out/ops/seats/index.html +1 -1
  237. package/apps/pwa/out/ops/seats/index.txt +21 -21
  238. package/apps/pwa/out/outputs/index.html +1 -1
  239. package/apps/pwa/out/outputs/index.txt +21 -21
  240. package/apps/pwa/out/reflection/index.html +1 -1
  241. package/apps/pwa/out/reflection/index.txt +22 -22
  242. package/apps/pwa/out/scheduler/index.html +1 -1
  243. package/apps/pwa/out/scheduler/index.txt +22 -22
  244. package/apps/pwa/out/sessions/index.html +1 -1
  245. package/apps/pwa/out/sessions/index.txt +21 -21
  246. package/apps/pwa/out/settings/devices/index.html +1 -1
  247. package/apps/pwa/out/settings/devices/index.txt +22 -22
  248. package/apps/pwa/out/settings/index.html +1 -1
  249. package/apps/pwa/out/settings/index.txt +22 -22
  250. package/apps/pwa/out/setup/done/index.html +1 -1
  251. package/apps/pwa/out/setup/done/index.txt +22 -22
  252. package/apps/pwa/out/setup/index.html +1 -1
  253. package/apps/pwa/out/setup/index.txt +22 -22
  254. package/apps/pwa/out/share/index.html +1 -1
  255. package/apps/pwa/out/share/index.txt +22 -22
  256. package/apps/pwa/out/showroom/index.html +1 -1
  257. package/apps/pwa/out/showroom/index.txt +21 -21
  258. package/apps/pwa/out/tasks/index.html +1 -1
  259. package/apps/pwa/out/tasks/index.txt +22 -22
  260. package/apps/pwa/out/term/index.html +1 -1
  261. package/apps/pwa/out/term/index.txt +22 -22
  262. package/apps/pwa/out/today/index.html +1 -1
  263. package/apps/pwa/out/today/index.txt +22 -22
  264. package/apps/pwa/out/trace/index.html +1 -1
  265. package/apps/pwa/out/trace/index.txt +22 -22
  266. package/apps/pwa/out/vault/index.html +1 -1
  267. package/apps/pwa/out/vault/index.txt +22 -22
  268. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  269. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  270. package/apps/pwa/out/workflows/index.html +1 -1
  271. package/apps/pwa/out/workflows/index.txt +22 -22
  272. package/apps/pwa/out/workspace/index.html +1 -1
  273. package/apps/pwa/out/workspace/index.txt +22 -22
  274. package/apps/pwa/out/worktrees/index.html +1 -1
  275. package/apps/pwa/out/worktrees/index.txt +22 -22
  276. package/graphs/release/release-loop.yaml +5 -5
  277. package/graphs/steward/recipes.yaml +4 -0
  278. package/graphs/steward/steward.yaml +10 -1
  279. package/package.json +1 -1
  280. package/scripts/bench-report.ts +46 -1
  281. package/scripts/ci-typecheck-changed.ts +88 -7
  282. package/scripts/coord-msg-bridge.ts +209 -0
  283. package/scripts/coord-post.sh +40 -7
  284. package/scripts/coord-requests-bridge.ts +10 -5
  285. package/scripts/expose-rubric-suggest.ts +1 -1
  286. package/scripts/measure-tsc-affected-roots.ts +10 -1
  287. package/scripts/release-loop/auto-approve-node.ts +6 -0
  288. package/scripts/release-loop/export-check-node.ts +8 -5
  289. package/scripts/release-loop/gate-closure.ts +286 -0
  290. package/scripts/release-loop/gate-node.ts +73 -13
  291. package/scripts/release-loop/gate-partial.ts +34 -6
  292. package/scripts/release-loop/node-verdict.ts +7 -2
  293. package/scripts/release-loop/ops-upgrade-node.ts +63 -42
  294. package/scripts/release-loop/publish-node.ts +6 -0
  295. package/scripts/release-loop/resume-release.ts +37 -10
  296. package/scripts/release-loop/resume.ts +69 -4
  297. package/scripts/tsc-baseline-cache.ts +28 -3
  298. package/scripts/usage-rollup.ts +27 -13
  299. package/scripts/ux-sim.ts +30 -1
  300. package/skills/youtube-master/scripts/main.ts +4 -1
  301. package/skills/youtube-master/src/obsidian.ts +9 -0
  302. package/src/agent/task-notification.ts +30 -1
  303. package/src/agent-substrate/acp-implementer.ts +14 -7
  304. package/src/agent-substrate/pr-reviewer.ts +30 -10
  305. package/src/agent-substrate/review-observation.ts +9 -2
  306. package/src/agent-substrate/self-review-cli.ts +154 -16
  307. package/src/boot/daemon-tools/self-implement.ts +72 -4
  308. package/src/budget/cost-tier-advisor.ts +91 -0
  309. package/src/budget/llm-cost.ts +5 -1
  310. package/src/budget/shadow-arms.ts +77 -0
  311. package/src/budget/types.ts +5 -3
  312. package/src/budget/unified-usage.ts +172 -8
  313. package/src/cli/control-cli.ts +29 -0
  314. package/src/cli/doctor-cli.ts +106 -3
  315. package/src/cli/friendly-errors.ts +113 -0
  316. package/src/cli/intake-cli.ts +3 -1
  317. package/src/cli/memory-cli.ts +182 -0
  318. package/src/cli/model-watch-cli.ts +4 -1
  319. package/src/cli/msg-cli.ts +15 -5
  320. package/src/cli/pr-cli.ts +53 -0
  321. package/src/cli/pr-land-overlap-evidence.ts +18 -10
  322. package/src/cli/pr-land-pod-shadow.ts +54 -0
  323. package/src/cli/release-cli.ts +41 -12
  324. package/src/cli/release-run-if-ready.ts +11 -0
  325. package/src/cli/resource-cli.ts +48 -0
  326. package/src/cli/retro-cli.ts +12 -3
  327. package/src/cli/seat-cli.ts +121 -0
  328. package/src/cli/seat-requests-cli.ts +25 -0
  329. package/src/cli/self-utterances-cli.ts +140 -0
  330. package/src/cli/tasks-cli.ts +168 -14
  331. package/src/cli/usage-cli.ts +3 -1
  332. package/src/compact/verify-probe.ts +23 -3
  333. package/src/context-card/cell.ts +40 -0
  334. package/src/context-card/index.ts +50 -0
  335. package/src/control/control-status.ts +30 -1
  336. package/src/dashboard/boot-timing.ts +35 -0
  337. package/src/dashboard/index.ts +86 -24
  338. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
  339. package/src/dashboard/slash-runtime/dashboard-handlers.ts +10 -138
  340. package/src/dashboard/slash-runtime/shell-slash.ts +145 -0
  341. package/src/debug/log-diet.ts +16 -4
  342. package/src/debug/log.ts +1 -1
  343. package/src/domains/morning-essential.ts +18 -3
  344. package/src/domains/release-tool.ts +29 -8
  345. package/src/domains/surface-events.ts +16 -0
  346. package/src/elanous-config-dir.ts +2 -2
  347. package/src/execution-loop/launch-gate.ts +158 -2
  348. package/src/fleet/host-doctor.ts +446 -0
  349. package/src/goals/judge.ts +20 -6
  350. package/src/graph-runner/runner.ts +12 -6
  351. package/src/graph-wizard/archetypes.ts +14 -8
  352. package/src/graph-wizard/generate.ts +77 -1
  353. package/src/grok.ts +13 -7
  354. package/src/harness/harness-cli-command.ts +102 -51
  355. package/src/harness/harness-stop.ts +93 -19
  356. package/src/index.ts +52 -179
  357. package/src/instance/current.ts +2 -6
  358. package/src/instance/resolve.ts +5 -6
  359. package/src/intake-plane/collect-github.ts +7 -2
  360. package/src/intake-plane/lens.ts +46 -4
  361. package/src/intake-plane/requirement-funnel.ts +53 -1
  362. package/src/intelligence-map/catalog-feed.ts +119 -0
  363. package/src/intelligence-map/model-watch-intake.ts +40 -0
  364. package/src/intelligence-map/model-watch-mission.ts +14 -1
  365. package/src/llm/open-weight-discipline.ts +21 -0
  366. package/src/llm/openai-sampling-fields.ts +89 -0
  367. package/src/llm/runtime-model-family.ts +61 -0
  368. package/src/llm/zai-coding-plan.ts +27 -1
  369. package/src/llm.ts +17 -97
  370. package/src/machines/machine-ledger.ts +31 -3
  371. package/src/maturity/cli-maturity.ts +13 -3
  372. package/src/maturity/feature-maturity.ts +1 -1
  373. package/src/msg/msg-store.ts +69 -7
  374. package/src/nexus/api/http-server.ts +22 -0
  375. package/src/oauth/codex-account-rotation.ts +4 -1
  376. package/src/oauth/codex-account-store.ts +20 -7
  377. package/src/oauth/codex-quota-policy.ts +33 -4
  378. package/src/ops/overview-collect.ts +3 -2
  379. package/src/ops/overview-owner.ts +29 -0
  380. package/src/plugin-hooks/shell-hook.ts +17 -2
  381. package/src/plugin-missions/evaluator-runner.ts +18 -2
  382. package/src/plugins/install/plugin-install.ts +11 -1
  383. package/src/preview/terminal.ts +15 -5
  384. package/src/project/project-store.ts +20 -0
  385. package/src/prompt-enhance/enhance.ts +11 -0
  386. package/src/release-loop/auto-start.ts +7 -5
  387. package/src/release-loop/checklist.ts +52 -5
  388. package/src/release-loop/feature-store.ts +10 -9
  389. package/src/release-loop/gate-shards.ts +4 -0
  390. package/src/release-loop/light-rc.ts +84 -13
  391. package/src/release-loop/merged-pr-checklist.ts +35 -5
  392. package/src/release-loop/rehearsal.ts +1 -1
  393. package/src/seat-loop/duty-ledger.ts +64 -0
  394. package/src/seat-loop/seat-loop.ts +5 -1
  395. package/src/self-dev/decompose.ts +37 -3
  396. package/src/self-dev/dev-pipeline.ts +32 -7
  397. package/src/self-dev/draft-residue.ts +16 -6
  398. package/src/self-dev/draft-sweep.ts +75 -18
  399. package/src/self-dev/fabric-decompose-adapter.ts +9 -2
  400. package/src/self-dev/orchestrate.ts +6 -2
  401. package/src/self-dev/run-supervisor.ts +72 -8
  402. package/src/self-implement/gate-cli.ts +29 -10
  403. package/src/self-implement/gate-remote.ts +4 -1
  404. package/src/self-implement/goal-author.ts +15 -29
  405. package/src/self-implement/goal-command-execution.ts +26 -0
  406. package/src/self-implement/goal-new-paths.ts +42 -0
  407. package/src/self-implement/host-regate.ts +124 -30
  408. package/src/self-implement/importer-test-index.ts +3 -1
  409. package/src/self-implement/l8-merge-queue.ts +80 -2
  410. package/src/self-implement/merge-train.ts +19 -0
  411. package/src/self-implement/off-diff-evidence.ts +40 -1
  412. package/src/self-implement/orchestrator.ts +90 -20
  413. package/src/self-implement/premise-review.ts +159 -0
  414. package/src/self-implement/pwa-import-graph.ts +194 -7
  415. package/src/self-implement/regate-detail.ts +79 -0
  416. package/src/self-implement/review-deferred-to-host.ts +21 -0
  417. package/src/self-implement/rework-budget-judgment.ts +23 -3
  418. package/src/self-implement/running-runs.ts +2 -2
  419. package/src/self-implement/scope-adjacency.ts +23 -0
  420. package/src/self-implement/seams.ts +42 -7
  421. package/src/self-implement/self-implement-runtime.ts +126 -21
  422. package/src/session/index.ts +14 -4
  423. package/src/session-runtime/index.ts +55 -11
  424. package/src/skills/tools/agent.ts +16 -1
  425. package/src/skills/tools/persistent-grounding.ts +11 -1
  426. package/src/skills/tools/shell.ts +33 -1
  427. package/src/steward/rejudge-tick.ts +142 -0
  428. package/src/task-agent/board.ts +92 -3
  429. package/src/task-agent/card-evidence.ts +88 -1
  430. package/src/task-agent/cover.ts +85 -1
  431. package/src/task-agent/live-moves.ts +14 -6
  432. package/src/task-agent/parent-host.ts +27 -3
  433. package/src/task-agent/rejudge-on-head.ts +370 -29
  434. package/src/task-agent/review-result-capture.ts +67 -2
  435. package/src/task-agent/shadow.ts +148 -21
  436. package/src/task-agent/task-hand.ts +22 -3
  437. package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -0
  438. package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +122 -0
  439. package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +23 -0
  440. package/src/task-orchestrator/surfaces/pod-pool.ts +53 -0
  441. package/src/task-orchestrator/surfaces/self-implement-pod.ts +236 -28
  442. package/src/tool-runtime/tool-search-runtime.ts +25 -0
  443. package/src/tui.ts +20 -1
  444. package/src/user-config.ts +21 -63
  445. package/src/ux-sim/fault-cred-expiry.ts +100 -0
  446. package/src/ux-sim/fault-pod-death.ts +74 -0
  447. package/src/ux-sim/fault-pool-cap.ts +81 -0
  448. package/src/version/packed-revision.json +1 -1
  449. package/src/webclone/qa-table.ts +22 -0
  450. package/apps/pwa/out/_next/static/chunks/0a09e062-34f747a17d8d8c2e.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +0 -77
  452. package/apps/pwa/out/_next/static/chunks/1325-dc3f3c21eb181cb4.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +0 -34
  454. package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +0 -24
  455. package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/1669-da843ed73aa301f5.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +0 -36
  465. package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +0 -122
  466. package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +0 -62
  468. package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/272-0eed0fb0aab716b6.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +0 -149
  475. package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/3961-aaae04ed1d32622f.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +0 -309
  479. package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/5313.1697309c532b9856.js +0 -2
  491. package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +0 -74
  499. package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +0 -64
  503. package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +0 -32
  506. package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +0 -24
  507. package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +0 -93
  511. package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +0 -148
  516. package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +0 -226
  517. package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +0 -27
  518. package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +0 -115
  521. package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +0 -8
  528. package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +0 -23
  529. package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +0 -90
  534. package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +0 -94
  535. package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +0 -41
  537. package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +0 -287
  539. package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +0 -79
  540. package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +0 -104
  545. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/app/approvals/page-3b9fc39362473ae7.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f2b62c19ca68fef8.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/app/board/page-d7602d489edf5f37.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/app/botlab/page-24282bf9f90031a5.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/bots/page-8b7c5ab6b5bc8d64.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/ceo/page-4eb84d454f8c3415.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/chat/page-b2e494bdad57fcb5.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/consult/page-c0ab229673a8b0eb.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/control/page-3e2b45f3c36dbac4.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/decisions/page-c22aa511b28743ad.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/editor/page-bf88c0f558d8ef54.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/exec/page-52fec523e5b5d3ad.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/field/page-67723dfdcde92b31.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/inside/page-22725e2570f1126b.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/intake/page-1891335950d68b70.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/layout-3267af1ebdc73cf5.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/live/page-c314b75c11234998.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/live-run/page-9f9c85415f61782b.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/loops/page-c1d4276059085587.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/market/page-833aab587f26ad4c.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8526c17ae38e6a50.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/morning/page-f998637a8402115c.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/not-found-b7bb53828b17b335.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/observatory/page-1b1850de0257e46d.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6f23159fedb41f67.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1d809a87139dd024.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-da9e570234d481fd.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/page-8ce3a38e210b810a.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/reflection/page-57ffeeb6019440fd.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-abbb417bf514630b.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2c2b3f29cec1efa4.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-9369279f72fc7b51.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/settings/page-64861f14039499c3.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ddc4c93efd8ab94b.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/setup/page-901a1391b81053bc.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/share/page-68d9bbaac9841a58.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/showroom/page-b5c0c996f329371e.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/tasks/page-89881ccbcf728978.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/term/page-0e87a803b3d7165e.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/today/page-58d4e5cb30ef4871.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/trace/page-02ce4c3b5f39e4d2.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/vault/page-e620d1bb8eadcec2.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-acf868c64d77f950.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/workflows/page-54fa605fa6dba457.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/workspace/page-bc79f0833533fa84.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d5c4398eea03d8f3.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/framework-b2763b10dc6b3836.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +0 -1
  601. /package/apps/pwa/out/_next/static/{Q-7iofoxOEv1WX18S35of → Ev2jivDMTSoEc-5rIXdcy}/_ssgManifest.js +0 -0
@@ -9,7 +9,7 @@ import { prereleaseKind } from './release-version.js';
9
9
  interface HostResult { host: string; ok: boolean; before: string; after: string; error?: string; skipped?: string; hooks?: string; restart?: 'no-service' }
10
10
  /** Where the internal feed is packed from: a checkout of the release tag, never the run's working tree. */
11
11
  export interface FeedSource { checkout: string; commit: string; deps: 'linked' | 'installed'; cleanup: () => void }
12
- interface OpsUpgradeDeps { exists?: (path: string) => boolean; installSource?: () => string | null; feedSource?: (version: string) => FeedSource }
12
+ interface OpsUpgradeDeps { exists?: (path: string) => boolean; installSource?: () => string | null; installCommit?: () => string | null; feedSource?: (version: string) => FeedSource }
13
13
 
14
14
  /** Detached worktree at refs/tags/v<version> with the PWA built — the feed ships what was released (10-04 0.2.11:
15
15
  * packing process.cwd() shipped main HEAD ee689ad2 as «0.2.11» to node-b·cloud-vm instead of the cut 31129c91). */
@@ -52,16 +52,24 @@ export function tagFeedSource(run: CommandRunner, repo: string = process.cwd()):
52
52
  };
53
53
  }
54
54
 
55
- function localInstallSource(): string | null {
55
+ function installMetadata(text: string): { source: string | null; commit: string | null } {
56
56
  try {
57
- const data: unknown = JSON.parse(readFileSync(join(homedir(), '.local/share/elanous/install.json'), 'utf8'));
58
- return data && typeof data === 'object' && 'source' in data && typeof data.source === 'string' ? data.source : null;
59
- } catch { return null; }
57
+ const data: unknown = JSON.parse(text);
58
+ if (!data || typeof data !== 'object') return { source: null, commit: null };
59
+ return {
60
+ source: 'source' in data && typeof data.source === 'string' ? data.source : null,
61
+ commit: 'commit' in data && typeof data.commit === 'string' ? data.commit : null,
62
+ };
63
+ } catch { return { source: null, commit: null }; }
64
+ }
65
+
66
+ function localInstallMetadata(): { source: string | null; commit: string | null } {
67
+ try { return installMetadata(readFileSync(join(homedir(), '.local/share/elanous/install.json'), 'utf8')); }
68
+ catch { return { source: null, commit: null }; }
60
69
  }
61
70
 
62
- function installedMatches(installed: unknown, version: string, checkout: boolean): boolean {
63
- return installed === version || checkout && typeof installed === 'string'
64
- && installed.startsWith(`${version}-`) && /^[0-9a-f]{12}$/.test(installed.slice(version.length + 1));
71
+ function installedMatches(installed: unknown, version: string): boolean {
72
+ return installed === version;
65
73
  }
66
74
 
67
75
  function missingService(data: Record<string, unknown> | undefined): boolean {
@@ -116,17 +124,21 @@ export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeD
116
124
  }
117
125
  const results: HostResult[] = [];
118
126
  for (const host of hosts as string[]) {
119
- const source = host === 'local' ? (deps.installSource ?? localInstallSource)() : null;
120
- const checkout = host === 'local' && source !== null && isAbsolute(source);
121
- const path = checkout ? 'checkout' as const : 'release' as const;
127
+ // Injected install metadata replaces the machine's install.json entirely — a test that injects only the source
128
+ // must not inherit this machine's real commit (that turned update cases into kept-ahead skips).
129
+ const injected = deps.installSource !== undefined || deps.installCommit !== undefined;
130
+ let metadata: { source: string | null; commit: string | null } = host !== 'local'
131
+ ? { source: null, commit: null }
132
+ : injected
133
+ ? { source: deps.installSource?.() ?? null, commit: deps.installCommit?.() ?? null }
134
+ : localInstallMetadata();
135
+ let source = metadata.source;
136
+ let railSource = false;
137
+ let path: 'checkout' | 'release' = 'release';
122
138
  const record = (result: HostResult) => {
123
139
  results.push(result);
124
140
  debug.log('release-loop.ops-upgrade', 'host', { host, path, restart: result.restart ?? (context.input.opsRestart === true ? result.ok ? 'ok' : 'failed' : 'skipped'), ok: result.ok });
125
141
  };
126
- if (host !== 'local' && feedError) {
127
- record({ host, ok: false, before: '', after: '', skipped: '내부 피드 발행 실패', error: feedError });
128
- continue;
129
- }
130
142
  const command = (args: string[]): CommandResult => host === 'local'
131
143
  ? run('elanous', args)
132
144
  // The installer's own bin dir comes first — a --no-modify-path install (the bot VM) has elanous only there.
@@ -134,35 +146,41 @@ export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeD
134
146
  let before = '';
135
147
  let after = '';
136
148
  try {
149
+ if (host !== 'local') {
150
+ const installed = run('ssh', [host, `cat "$HOME/.local/share/elanous/install.json" 2>/dev/null || true`]);
151
+ if (installed.status === 0) metadata = installMetadata(installed.stdout);
152
+ }
153
+ source = metadata.source;
154
+ if (source !== null && isAbsolute(source)) {
155
+ const repository = host === 'local'
156
+ ? run('git', ['rev-parse', '--is-inside-work-tree'], source)
157
+ : run('ssh', [host, `git -C ${quote(source)} rev-parse --is-inside-work-tree`]);
158
+ railSource = repository.status === 0 && firstLine(repository.stdout) === 'true';
159
+ }
160
+ path = railSource ? 'checkout' : 'release';
137
161
  const previous = command(['--version']);
138
162
  if (previous.status === 0) before = firstLine(previous.stdout);
139
- if (checkout) {
140
- const git = (args: string[]) => run('git', args, source!);
141
- const dirty = git(['status', '--porcelain', '--untracked-files=all']);
142
- if (dirty.status !== 0 || dirty.stdout.trim()) {
143
- record({ host, ok: false, before, after, error: dirty.status === 0 ? '체크아웃 변경 있음' : `체크아웃 상태 확인 실패: ${reason(dirty)}` });
144
- continue;
145
- }
146
- const fetched = git(['fetch', '--tags']);
147
- if (fetched.status !== 0) {
148
- record({ host, ok: false, before, after, error: `태그 가져오기 실패: ${reason(fetched)}` });
149
- continue;
150
- }
151
- const tag = git(['rev-parse', '--verify', `refs/tags/v${version}^{commit}`]);
152
- if (tag.status !== 0 || !tag.stdout.trim()) {
153
- record({ host, ok: false, before, after, error: `태그 없음: v${version}` });
154
- continue;
155
- }
156
- const moved = git(['checkout', '--detach', `v${version}`]);
157
- if (moved.status !== 0) {
158
- record({ host, ok: false, before, after, error: `체크아웃 전환 실패: ${reason(moved)}` });
163
+ if (railSource) {
164
+ record({ host, ok: true, before, after: before, skipped: 'rail-source' });
165
+ continue;
166
+ }
167
+ const installedCommit = metadata.commit;
168
+ if (installedCommit && /^[0-9a-f]{40,64}$/.test(installedCommit)) {
169
+ const ahead = run('git', ['merge-base', '--is-ancestor', `refs/tags/v${version}`, installedCommit]);
170
+ // Only exit 1 proves non-ancestry; missing local objects must not permit a potentially destructive downgrade.
171
+ if (ahead.status !== 1) {
172
+ record({ host, ok: true, before, after: before, skipped: 'kept-ahead' });
159
173
  continue;
160
174
  }
161
175
  }
162
- const updated = command(['update', ...(checkout || host !== 'local' && feedPath !== undefined ? [] : ['--version', version]), '--json', ...(context.input.opsRestart === true ? ['--restart'] : [])]);
176
+ if (host !== 'local' && feedError) {
177
+ record({ host, ok: false, before, after: '', skipped: '내부 피드 발행 실패', error: feedError });
178
+ continue;
179
+ }
180
+ const updated = command(['update', ...(host !== 'local' && feedPath !== undefined ? [] : ['--version', version]), '--json', ...(context.input.opsRestart === true ? ['--restart'] : [])]);
163
181
  const updateResult = lastResult(updated);
164
182
  const noService = host !== 'local' && context.input.opsRestart === true && updateResult?.exitCode === 1 && missingService(updateResult);
165
- if ((!noService && (updated.status !== 0 || updateResult?.exitCode !== 0)) || !installedMatches(updateResult?.installedVersion, version, checkout)) {
183
+ if ((!noService && (updated.status !== 0 || updateResult?.exitCode !== 0)) || !installedMatches(updateResult?.installedVersion, version)) {
166
184
  const error = (updated.status === 0 && updateResult?.exitCode === 0 || noService)
167
185
  ? `설치판 불일치 (기대 ${version}, 실제 ${String(updateResult?.installedVersion)})` : reason(updated);
168
186
  record({ host, ok: false, before, after, error });
@@ -174,7 +192,7 @@ export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeD
174
192
  continue;
175
193
  }
176
194
  after = firstLine(current.stdout);
177
- if (!installedMatches(after.split(/\s+/)[0], version, checkout)) {
195
+ if (!installedMatches(after.split(/\s+/)[0], version)) {
178
196
  record({ host, ok: false, before, after, error: `올렸는데 판이 그대로 (기대 ${version}, 실제 ${after || '없음'})` });
179
197
  continue;
180
198
  }
@@ -198,14 +216,17 @@ export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeD
198
216
  feed: feedError ? 'failed' as const : feedPath === undefined ? 'skipped-no-path' as const : 'published' as const, hosts: results };
199
217
  }
200
218
 
201
- if (import.meta.main) {
219
+ function main(): void {
202
220
  let version = '';
203
221
  try {
204
222
  version = readGraphContext().input.version;
205
223
  // RELEASE-REHEARSAL-RC: a prerelease never lands docs on main or upgrades the operating hosts.
206
- process.exitCode = finishNode('ops-upgrade', version, prereleaseKind(version) !== null
207
- ? { outcome: 'ok', verdict: 'pass', summary: `ops-upgrade skipped — ${version} is a prerelease`, skipped: 'prerelease' }
208
- : runOpsUpgrade());
224
+ const result = prereleaseKind(version) !== null
225
+ ? { outcome: 'ok' as const, verdict: 'pass' as const, summary: `ops-upgrade skipped — ${version} is a prerelease`, skipped: 'prerelease' }
226
+ : runOpsUpgrade(runCommand);
227
+ process.exitCode = finishNode('ops-upgrade', version, result);
209
228
  }
210
229
  catch (error) { process.exitCode = finishNode('ops-upgrade', version, errorResult(error)); }
211
230
  }
231
+
232
+ if (import.meta.main) main();
@@ -5,6 +5,7 @@ import { join } from 'node:path';
5
5
  import { WARNING_ONLY } from './auto-approve-node.js';
6
6
  import { beginLandingMerge, landingFreezeMessage } from '../../src/release-loop/landing-freeze.js';
7
7
  import { debug } from '../../src/debug/log.js';
8
+ import { registerStandaloneLogSink } from '../../src/domains/standalone-log-sink.js';
8
9
  import { prereleaseKind } from './release-version.js';
9
10
  import { publishAtError } from './publish-at.js';
10
11
  import { errorResult, finishNode, lastResult, nodeOutput, readGraphContext, runCommand, type CommandRunner } from './node-verdict.js';
@@ -115,6 +116,11 @@ export function runPublish(run: CommandRunner = runCommand, prodFreezeRoot?: str
115
116
  }
116
117
 
117
118
  if (import.meta.main) {
119
+ // GATE-OBS2-2 — the release graph spawns this node as its own process, which inherits no daemon sink. Without this the
120
+ // `release-loop.publish / result` event (the one that says whether the irreversible publication actually happened) stayed
121
+ // in the file trail and `elanous logs` read 0 rows. Same pattern as gate-node.ts and auto-approve-node.ts (GATE-OBS2-1).
122
+ // Observability must never stop the publication node — a failed sink registration is skipped (fail-soft).
123
+ try { await registerStandaloneLogSink('release-loop'); } catch { /* fail-soft: the verdict still goes to stdout */ }
118
124
  let version = '';
119
125
  try { version = readGraphContext().input.version; process.exitCode = finishNode('publish', version, runPublish()); }
120
126
  catch (error) { process.exitCode = finishNode('publish', version, errorResult(error)); }
@@ -10,6 +10,7 @@ import { lastJsonObject, runGraph, type GraphRunState } from '../../src/graph-ru
10
10
  import { releaseRunUniverse } from './release-universe.js';
11
11
  import { isReleaseVersion } from './release-version.js';
12
12
  import { changedFilesBetween, clearPartialPlan, planPartialRegate, writePartialPlan } from './gate-partial.js';
13
+ import { gitClosureTree, testsReachingChanged, type ClosureTree } from './gate-closure.js';
13
14
  import { releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
14
15
 
15
16
  const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
@@ -29,8 +30,15 @@ export interface ResumeReleaseDeps {
29
30
  now?: () => Date;
30
31
  /** Machine release ledger (gate-failures.json of the prior gate) — default releaseLedgerRoot(). */
31
32
  ledgerRoot?: string;
33
+ /** GATE-REUSE-BY-DIFF: the tip tree for the import closure — default read from git objects of the tip commit. */
34
+ closureTree?: (commit: string) => ClosureTree;
32
35
  }
33
36
 
37
+ /** CLI entry files: a production file that only names one (help text, a goal's command line) does not run it. */
38
+ const CLI_ENTRIES = ['bin/elanous.mjs', 'src/index.ts'] as const;
39
+ /** The gate's own test-file set (gate-node sweep: `git ls-files '*.test.*'` filtered by this). */
40
+ const GATE_TEST = /\.test\.(?:tsx?|jsx?|mts|cts)$/;
41
+
34
42
  export interface TipRefresh { version: string; branch: string; from: string; to: string; changed: boolean; statePath: string; backup?: string }
35
43
 
36
44
  function gitRunner(repo: string) {
@@ -114,18 +122,25 @@ export function refreshReleaseBranchTip(runId: string, deps: ResumeReleaseDeps =
114
122
  return { version, branch, from: recorded, to, changed: true, statePath, backup };
115
123
  }
116
124
 
117
- /** GATE-PARTIAL outcome of a resume — applied (plan written) or the reason it fell back to a full gate. */
118
- export type PartialOutcome = { requested: false } | { requested: true; applied: true; files: number; priorCommit: string } | { requested: true; applied: false; reason: string };
125
+ /** GATE-PARTIAL outcome of a resume — applied (plan written) or the reason it fell back to a full gate. `auto` marks the
126
+ * default plan of `--from gate` (GATE-REUSE-BY-DIFF); `closure` is set when code changes were followed. */
127
+ export type PartialOutcome = { requested: false }
128
+ | { requested: true; applied: true; files: number; priorCommit: string; auto?: true; closure?: { reached: number; opaque: number } }
129
+ | { requested: true; applied: false; reason: string; auto?: true };
119
130
 
120
- /** Build (or clear) the partial re-gate plan for the new tip. Fail-closed: any doubt leaves a full gate. */
131
+ /** Build (or clear) the partial re-gate plan for the new tip. Fail-closed: any doubt leaves a full gate.
132
+ * GATE-REUSE-BY-DIFF: `--from gate` on a moved tip plans by default (`partial: undefined`); `partial: false` opts out. */
121
133
  function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean }, deps: ResumeReleaseDeps): PartialOutcome {
122
134
  const root = deps.root ?? effectiveInstanceRoot();
123
- // Any resume starts from no plan: one without --partial (from whatever node) never inherits an older plan.
135
+ // Any resume starts from no plan: one without a partial plan (from whatever node) never inherits an older plan.
124
136
  clearPartialPlan(root, tip.version);
125
- if (!opts.partial) return { requested: false };
137
+ const auto = opts.partial === undefined;
138
+ // The default applies only where it can help: a gate re-run on a tip that moved. An unchanged tip re-uses its own
139
+ // shard results by commit already.
140
+ if (opts.partial === false || (auto && (opts.from !== 'gate' || !tip.changed))) return { requested: false };
126
141
  const fallback = (reason: string): PartialOutcome => {
127
- debug.log('release-loop.resume', 'partial-fallback', { version: tip.version, reason });
128
- return { requested: true, applied: false, reason };
142
+ debug.log('release-loop.resume', 'partial-fallback', { version: tip.version, reason, auto });
143
+ return { requested: true, applied: false, reason, ...(auto ? { auto: true as const } : {}) };
129
144
  };
130
145
  if (opts.from !== 'gate') return fallback('--partial only applies to --from gate');
131
146
  if (!tip.changed) return fallback('release branch tip unchanged — nothing new to re-check');
@@ -151,12 +166,23 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
151
166
  // The tip is already refreshed — a failed diff must not strand the resume: run the full gate and say why.
152
167
  return fallback(`changed files unreadable: ${String(error instanceof Error ? error.message : error).slice(0, 200)}`);
153
168
  }
169
+ // The tip tree (git objects of tip.to, never the working tree): existence of planned tests ⊕ the import closure.
170
+ let tree: ClosureTree;
171
+ try { tree = (deps.closureTree ?? ((commit: string) => gitClosureTree(deps.repo ?? process.cwd(), commit)))(tip.to); }
172
+ catch (error) { return fallback(`tip tree unreadable: ${String(error instanceof Error ? error.message : error).slice(0, 200)}`); }
173
+ const tracked = new Set(tree.files);
174
+ const tests = tree.files.filter((file) => GATE_TEST.test(file));
154
175
  const planned = planPartialRegate({ version: tip.version, priorCommit: tip.from, forCommit: tip.to, changedFiles: changed,
155
- priorFailures: prior.failures as string[], priorErrors: (prior.errors ?? []) as string[] });
176
+ priorFailures: prior.failures as string[], priorErrors: (prior.errors ?? []) as string[],
177
+ existsAtTip: (file) => tracked.has(file),
178
+ closure: (changedCode) => testsReachingChanged(tree, changedCode, tests, { cliEntries: CLI_ENTRIES }) });
156
179
  if (!planned.ok) return fallback(planned.reason);
180
+ if (planned.plan.files.length >= tests.length) return fallback(`every test is re-run (${tests.length}) — full gate`);
157
181
  const path = writePartialPlan(root, planned.plan);
158
- debug.log('release-loop.resume', 'partial-planned', { version: tip.version, priorCommit: tip.from, commit: tip.to, files: planned.plan.files.length, path });
159
- return { requested: true, applied: true, files: planned.plan.files.length, priorCommit: tip.from };
182
+ debug.log('release-loop.resume', 'partial-planned', { version: tip.version, priorCommit: tip.from, commit: tip.to, files: planned.plan.files.length,
183
+ tests: tests.length, auto, path, ...(planned.closure ? { reached: planned.closure.reached, opaque: planned.closure.opaque } : {}) });
184
+ return { requested: true, applied: true, files: planned.plan.files.length, priorCommit: tip.from,
185
+ ...(auto ? { auto: true as const } : {}), ...(planned.closure ? { closure: planned.closure } : {}) };
160
186
  }
161
187
 
162
188
  /** The graph file the run started from — a snapshot run refuses any other path (the installed CLI's own copy differs). */
@@ -183,6 +209,7 @@ export async function resumeReleaseRun(opts: { runId: string; from: string; part
183
209
  if (gateExecutor) debug.log('release-loop.resume', 'gate-executor', { runId: opts.runId, from: opts.from, executor: gateExecutor.set.gateExecutor, host: gateExecutor.set.gateRemote ?? null, previous: gateExecutor.previous });
184
210
  const waiver = original && (opts.waive !== undefined || opts.reason !== undefined)
185
211
  ? releaseResumeWaiver(original, opts) : undefined;
212
+ if (waiver) debug.log('release-loop.resume', 'waiver', { runId: opts.runId, node: waiver.nodeId, reason: waiver.reason, unmeasured: waiver.unmeasured ?? null });
186
213
  const acceptedRegressions = original && opts.acceptedRegressions !== undefined && !waiver
187
214
  ? releaseResumeAcceptedRegressions(original, opts.acceptedRegressions, opts.from) : undefined;
188
215
  // A waived verdict may reuse its original commit. A moved tip still requires a full re-gate.
@@ -1,4 +1,4 @@
1
- import type { GraphRunState } from '../../src/graph-runner/runner.js';
1
+ import { lastJsonObject, STOPPED_NODE_ERROR, type GraphRunState } from '../../src/graph-runner/runner.js';
2
2
  import { publicWaiverReason } from './known-issues-node.js';
3
3
  import { checkGateExecutorChoice, type GateExecutor } from './gate-executor.js';
4
4
 
@@ -28,11 +28,66 @@ export function releaseResumeAcceptedRegressions(state: GraphRunState, additions
28
28
  return combined;
29
29
  }
30
30
 
31
+ const SWEEP_INCOMPLETE = /^(?:pod sweep incomplete|sweep incomplete|[\w-]+ shard incomplete)\b/;
32
+
33
+ /**
34
+ * RELEASE-WAIVE-UNMEASURED — why the saved gate record at `index` carries no verdict, or undefined when it does (or may).
35
+ * Fail-closed: only three shapes count, each read from what the runner and gate-node actually write:
36
+ * ① stopped — `graph runs stop` wrote {ok:false, executed:false, exit:null, error:STOPPED_NODE_ERROR} as the run's last record;
37
+ * ② no verdict — the gate process ended with exit null (signal, idle timeout, or a spawn that failed) and printed no result line at
38
+ * all (no output line is a JSON object with an `outcome`), so the run went gate → failed. A verdict followed by more
39
+ * log lines and then a kill is still a verdict: refused;
40
+ * ③ sweep incomplete — gate-node printed outcome `error` (no verdict: the baseline comparison never ran, so `introduced`
41
+ * is still the empty initial list) and the error is an incomplete sweep (stalled shards / no tests ran). The finished
42
+ * shards' counts go into the detail. Any other error (cleanup, baseline persistence, …) may follow a verdict: refused.
43
+ * An `outcome: fail` (introduced > 0), an introduced list that is not empty, or an already-waived record is never unmeasured.
44
+ */
45
+ /** Any output line that is a JSON object naming an `outcome` — a verdict was printed, whatever came after it. */
46
+ function printedVerdict(output: unknown): boolean {
47
+ if (typeof output !== 'string') return false;
48
+ return output.split('\n').some((line) => {
49
+ const text = line.trim();
50
+ if (!text.startsWith('{')) return false;
51
+ try { const value = JSON.parse(text) as unknown; return !!value && typeof value === 'object' && 'outcome' in value; } catch { return false; }
52
+ });
53
+ }
54
+
55
+ export function gateUnmeasuredDetail(state: GraphRunState, index: number): string | undefined {
56
+ const record = state.nodes[index];
57
+ if (!record || record.nodeId !== 'gate' || state.path[index] !== 'gate' || record.ok !== false || record.waiver !== undefined) return undefined;
58
+ if (state.nodes.some((saved) => saved.nodeId === 'gate' && saved !== record)) return undefined;
59
+ const last = index === state.path.length - 1 && index === state.nodes.length - 1;
60
+ if (last && record.executed === false && record.exit === null && record.error === STOPPED_NODE_ERROR && record.output === undefined) {
61
+ return `stopped: ${STOPPED_NODE_ERROR}${typeof record.seconds === 'number' ? ` after ${record.seconds}s` : ''}`;
62
+ }
63
+ const failedAfter = index === state.path.length - 2 && state.path.at(-1) === 'failed' && state.nodes.at(-1)?.nodeId === 'failed'
64
+ && state.nodes.length === state.path.length;
65
+ if (!failedAfter || record.executed !== true) return undefined;
66
+ const result = lastJsonObject(record.output);
67
+ if (result === undefined) {
68
+ if (record.exit !== null || printedVerdict(record.output)) return undefined;
69
+ return `no-verdict: gate process ended with exit null and no result line${record.error ? ` — ${record.error.split('\n')[0]!.slice(0, 120)}` : ''}`;
70
+ }
71
+ if (result.outcome !== 'error' || !Array.isArray(result.introduced) || result.introduced.length !== 0) return undefined;
72
+ const error = typeof result.error === 'string' ? result.error : '';
73
+ if (!SWEEP_INCOMPLETE.test(error)) return undefined;
74
+ // The detail keeps what WAS measured: each stalled shard's reason and the finished shards' counts (failures there are
75
+ // not yet split into introduced vs pre-existing — the baseline comparison never ran).
76
+ const stalled = Array.isArray(result.stalledShards) ? result.stalledShards as Array<{ reason?: unknown }> : [];
77
+ const reasons = new Map<string, number>();
78
+ for (const shard of stalled) reasons.set(String(shard?.reason ?? 'unknown'), (reasons.get(String(shard?.reason ?? 'unknown')) ?? 0) + 1);
79
+ const summary = result.partialSummary as { pass?: unknown; fail?: unknown; errors?: unknown; ran?: unknown } | undefined;
80
+ const measured = summary && typeof summary === 'object'
81
+ ? ` · finished shards measured pass ${String(summary.pass)} fail ${String(summary.fail)} errors ${String(summary.errors)} ran ${String(summary.ran)}` : '';
82
+ return `sweep incomplete: ${error.split('\n')[0]!.slice(0, 200)}`
83
+ + (stalled.length ? ` · stalled shards ${stalled.length} (${[...reasons].map(([reason, count]) => `${reason}×${count}`).join(', ')})` : '') + measured;
84
+ }
85
+
31
86
  /** Only the recorded failure at the restart boundary may be waived. `--from` is never a waiver. */
32
87
  export function releaseResumeWaiver(
33
88
  state: GraphRunState,
34
89
  opts: { from: string; waive?: string; reason?: string; acceptedRegressions?: AcceptedRegression[] },
35
- ): { nodeId: string; reason: string; acceptedRegressions: AcceptedRegression[] } | undefined {
90
+ ): { nodeId: string; reason: string; acceptedRegressions: AcceptedRegression[]; unmeasured?: string } | undefined {
36
91
  if ((opts.waive === undefined) !== (opts.reason === undefined)) throw new Error('--waive <node> and --reason <text> must be supplied together');
37
92
  if (opts.waive !== undefined && !opts.waive.trim()) throw new Error('--waive must not be empty');
38
93
  if (opts.waive === undefined) return undefined;
@@ -41,13 +96,23 @@ export function releaseResumeWaiver(
41
96
  publicWaiverReason(reason);
42
97
  if (opts.from !== opts.waive) throw new Error('--from must name the waived node; --from alone does not waive a failure');
43
98
  const index = state.path.indexOf(opts.waive);
44
- if (index < 0 || state.path.lastIndexOf(opts.waive) !== index || state.nodes[index]?.nodeId !== opts.waive || state.nodes[index]?.ok !== false ||
99
+ const unique = index >= 0 && state.path.lastIndexOf(opts.waive) === index;
100
+ // RELEASE-WAIVE-UNMEASURED (OP 10-12): a gate cut before any verdict is the only gate that may be waived — a measured gate
101
+ // failure or an already-waived gate stays refused (fail-closed below), and the ledger records why it was unmeasured.
102
+ const unmeasured = opts.waive === 'gate' && unique ? gateUnmeasuredDetail(state, index) : undefined;
103
+ if (unmeasured !== undefined) {
104
+ return { nodeId: opts.waive, reason, unmeasured, acceptedRegressions: releaseResumeAcceptedRegressions(state,
105
+ [...(opts.acceptedRegressions ?? []), { id: `waive:${opts.waive}`, note: reason }]) };
106
+ }
107
+ if (!unique || state.path.lastIndexOf(opts.waive) !== index || state.nodes[index]?.nodeId !== opts.waive || state.nodes[index]?.ok !== false ||
45
108
  state.nodes[index]?.executed !== true || state.path.at(-2) !== opts.waive || state.path.at(-1) !== 'failed' || state.nodes.at(-1)?.nodeId !== 'failed') {
46
109
  throw new Error(`--waive ${opts.waive} requires the failed executable node on the saved path`);
47
110
  }
48
111
  // Only user-visible, non-publication checks can become accepted regressions; build, safety and approval gates stay blocking.
112
+ // A measured gate failure (outcome fail / introduced > 0, or any error that is not an incomplete sweep) lands here too.
49
113
  if (!['mac-smoke', 'tui'].includes(opts.waive)) {
50
- throw new Error(`--waive ${opts.waive} cannot waive a safety, side-effect or approval node`);
114
+ throw new Error(`--waive ${opts.waive} cannot waive a safety, side-effect or approval node`
115
+ + (opts.waive === 'gate' ? ' (only an unmeasured gate — stopped, killed or sweep incomplete — may be waived)' : ''));
51
116
  }
52
117
  return { nodeId: opts.waive, reason, acceptedRegressions: releaseResumeAcceptedRegressions(state,
53
118
  [...(opts.acceptedRegressions ?? []), { id: `waive:${opts.waive}`, note: reason }]) };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 승격 착지의 base 쪽 tsc 진단을 «main 커밋별로 한 번만» 재고 재사용하는 캐시.
3
- * 키 = base sha ⊕ tsc 명령 ⊕ 설정 파일(base 커밋에서 git show 로 읽은 내용) 해시 ⊕ tsc 판.
3
+ * 키 = base sha ⊕ tsc 명령 ⊕ 설정 파일(base 커밋에서 git show 로 읽은 내용) 해시 ⊕ 작업 트리 설치 키(bun.lock 실물) ⊕ tsc 판.
4
4
  * 값 = rebase 전 정규화 진단(워크트리 절대 경로는 «<ROOT>» 자리표로 저장).
5
5
  * ⛔ 캐시 실패는 호출자에게 던지지 않는다 — 읽기·쓰기 모두 miss 로 떨어진다.
6
6
  */
@@ -13,7 +13,7 @@ import type { TypecheckError } from '../src/typecheck-ratchet.js';
13
13
 
14
14
  export const ROOT_PLACEHOLDER = '<ROOT>';
15
15
  export const BASELINE_CACHE_MAX_ENTRIES = 50;
16
- const CACHE_FORMAT = 1;
16
+ const CACHE_FORMAT = 2;
17
17
 
18
18
  /** 키에 들어가는 «기준 커밋의 설정 파일» — 이 중 하나라도 달라지면 같은 sha 라도 다른 진단이다. */
19
19
  export const BASELINE_CACHE_CONFIG_FILES = ['tsconfig.gate.json', 'apps/pwa/tsconfig.json', 'tsconfig.pwa-env.json', 'tsconfig.json', 'bun.lock'] as const;
@@ -24,6 +24,8 @@ export interface BaselineCacheOptions {
24
24
  env?: NodeJS.ProcessEnv;
25
25
  /** git 실행 심 — 출력 바이트를 돌려주고 실패하면 던진다. */
26
26
  git?: (args: string[], cwd: string) => Buffer;
27
+ /** 작업 트리 설치 파일 읽기 심 — 파일 바이트를 돌려주고 읽을 수 없으면 던진다. */
28
+ readInstallFile?: (path: string) => Buffer;
27
29
  /** tsc 판 조회 심. */
28
30
  tscVersion?: (cwd: string) => string;
29
31
  maxEntries?: number;
@@ -59,6 +61,27 @@ function defaultTscVersion(cwd: string): string {
59
61
  }
60
62
  }
61
63
 
64
+ /** 키에 들어가는 «작업 트리에 실제로 깔린 판» — base 쪽 bun.lock 이 같아도 설치가 다르면 진단이 다르다. */
65
+ const BASELINE_CACHE_INSTALL_FILES = ['bun.lock', 'apps/pwa/bun.lock'] as const;
66
+
67
+ /**
68
+ * 지금 트리에 실제로 깔린 판의 키 — 작업 트리 루트 `bun.lock`(⊕ 있으면 `apps/pwa/bun.lock`) 파일 해시.
69
+ * 루트 `bun.lock` 을 못 읽으면 undefined(호출자는 캐시 없이 잰다 — base 키 규율과 같다) · `apps/pwa/bun.lock` 은 못 읽으면 absent.
70
+ */
71
+ export function workingTreeInstallKey(cwd: string, options: Pick<BaselineCacheOptions, 'readInstallFile'> = {}): string | undefined {
72
+ const read = options.readInstallFile ?? ((path: string) => readFileSync(path));
73
+ const files: Record<string, string> = {};
74
+ for (const file of BASELINE_CACHE_INSTALL_FILES) {
75
+ try {
76
+ files[file] = createHash('sha256').update(read(join(cwd, file))).digest('hex');
77
+ } catch {
78
+ if (file === 'bun.lock') return undefined;
79
+ files[file] = 'absent';
80
+ }
81
+ }
82
+ return createHash('sha256').update(JSON.stringify(files)).digest('hex');
83
+ }
84
+
62
85
  /** 키를 못 만들면(base 를 sha 로 풀 수 없음 등) undefined — 호출자는 캐시 없이 잰다. */
63
86
  export function computeBaselineCacheKey(base: string, command: string, cwd: string, options: BaselineCacheOptions = {}): string | undefined {
64
87
  try {
@@ -73,8 +96,10 @@ export function computeBaselineCacheKey(base: string, command: string, cwd: stri
73
96
  files[file] = 'absent';
74
97
  }
75
98
  }
99
+ const install = workingTreeInstallKey(cwd, options);
100
+ if (install === undefined) return undefined;
76
101
  const tsc = (options.tscVersion ?? defaultTscVersion)(cwd);
77
- return createHash('sha256').update(JSON.stringify({ format: CACHE_FORMAT, sha, command, files, tsc })).digest('hex');
102
+ return createHash('sha256').update(JSON.stringify({ format: CACHE_FORMAT, sha, command, files, install, tsc })).digest('hex');
78
103
  } catch {
79
104
  return undefined;
80
105
  }
@@ -15,8 +15,11 @@ export interface UsageRow { site: string; provider: string; model: string; calls
15
15
 
16
16
  type UsageData = { site?: string; provider?: string; billingProvider?: string; model?: string; inputTokens?: number; outputTokens?: number; cacheReadInputTokens?: number; cacheCreationInputTokens?: number; cost?: { kind?: string; usd?: number }; podRollupKey?: string; partial?: boolean; partialSeq?: number; partialAt?: string };
17
17
 
18
+ /** 재방출 행 dedup 에 필요한 칸만 — 호스트 소비처(bench-report · `elanous usage`)도 같은 규칙을 쓴다. */
19
+ export type PodRollupKeyed = { podRollupKey?: unknown; partial?: unknown; partialSeq?: unknown; partialAt?: unknown };
20
+
18
21
  /** 같은 podRollupKey 두 행 중 어느 쪽이 더 완전한가 — 최종 > 부분 · 부분끼리는 seq 큰 쪽 · 같으면 at 늦은 쪽. 완전히 같으면 앞 행을 둔다. */
19
- function rollupRowBeats(next: UsageData, kept: UsageData): boolean {
22
+ function rollupRowBeats(next: PodRollupKeyed, kept: PodRollupKeyed): boolean {
20
23
  const nextFinal = next.partial !== true;
21
24
  const keptFinal = kept.partial !== true;
22
25
  if (nextFinal !== keptFinal) return nextFinal;
@@ -27,31 +30,42 @@ function rollupRowBeats(next: UsageData, kept: UsageData): boolean {
27
30
  return String(next.partialAt ?? '') > String(kept.partialAt ?? '');
28
31
  }
29
32
 
33
+ /** ⭐ METER-POD-ROLLUP-GAP-2 — 같은 Pod 사용량의 재방출(부분 ⊕ 최종 · abort ⊕ resume)을 podRollupKey 로 한 번만 남긴다.
34
+ * 들어온 순서가 아니라 «더 완전한 행»을 남기고(rollupRowBeats), 키 없는 행은 그대로 다 둔다(순서 보존). */
35
+ export function dedupePodRollupRows<T>(rows: readonly T[], dataOf: (row: T) => PodRollupKeyed = (row) => row as PodRollupKeyed): T[] {
36
+ const kept: T[] = [];
37
+ const keptIndexByKey = new Map<string, number>();
38
+ for (const row of rows) {
39
+ const d = dataOf(row);
40
+ if (typeof d.podRollupKey === 'string') {
41
+ const at = keptIndexByKey.get(d.podRollupKey);
42
+ if (at !== undefined) {
43
+ if (rollupRowBeats(d, dataOf(kept[at]!))) kept[at] = row;
44
+ continue;
45
+ }
46
+ keptIndexByKey.set(d.podRollupKey, kept.length);
47
+ }
48
+ kept.push(row);
49
+ }
50
+ return kept;
51
+ }
52
+
30
53
  export function rollup(lines: readonly string[]): { rows: UsageRow[]; truncated: boolean } {
31
54
  const by = new Map<string, UsageRow>();
32
55
  // ⭐ MF-3c0d253e — 같은 Pod 사용량의 재방출(부분 ⊕ 최종 · abort ⊕ resume)은 podRollupKey 로 한 번만 센다(이중계상 방지).
33
56
  // 들어온 순서가 아니라 «더 완전한 행»을 남긴다(최종 > 부분 · seq · at — rollupRowBeats).
34
57
  // 키가 없는 행(기존 경로 전부)은 종전과 동작·바이트 그대로다.
35
58
  let truncated = false;
36
- const entries: UsageData[] = [];
37
- const keptIndexByPodRollupKey = new Map<string, number>();
59
+ const parsed: UsageData[] = [];
38
60
  for (const line of lines) {
39
61
  if (/limitReached=true|truncated/.test(line) && !line.startsWith('{')) truncated = true;
40
62
  if (!line.startsWith('{')) continue;
41
63
  let r: { event?: string; data?: Record<string, unknown> };
42
64
  try { r = JSON.parse(line); } catch { continue; }
43
65
  if (r.event !== 'llm-usage' || !r.data) continue;
44
- const d = r.data as UsageData;
45
- if (typeof d.podRollupKey === 'string') {
46
- const keptIndex = keptIndexByPodRollupKey.get(d.podRollupKey);
47
- if (keptIndex !== undefined) {
48
- if (rollupRowBeats(d, entries[keptIndex]!)) entries[keptIndex] = d;
49
- continue;
50
- }
51
- keptIndexByPodRollupKey.set(d.podRollupKey, entries.length);
52
- }
53
- entries.push(d);
66
+ parsed.push(r.data as UsageData);
54
67
  }
68
+ const entries = dedupePodRollupRows(parsed);
55
69
  for (const d of entries) {
56
70
  const provider = d.billingProvider ?? d.provider ?? '?'; // ⭐ 과금 주체 우선(B10)
57
71
  const key = `${d.site ?? '?'}|${provider}|${d.model ?? '?'}`;
package/scripts/ux-sim.ts CHANGED
@@ -13,6 +13,9 @@
13
13
  // bun scripts/ux-sim.ts --computer-use normal --from-run <runId> --save-trajectory <name> # 이름으로 저장
14
14
  // bun scripts/ux-sim.ts --computer-use normal --trajectory <name> # 저장한 궤적을 재생
15
15
  // bun scripts/ux-sim.ts --list-trajectories # 저장한 궤적과 출처 목록
16
+ // bun scripts/ux-sim.ts --fault cred-expiry # 결함 주입: grok 구독 만료 임박 × 갱신 결과 → 디스패처·발사 판정
17
+ // bun scripts/ux-sim.ts --fault pod-death # 결함 주입: PARTIAL 줄을 남긴 Pod 가 SIGTERM 으로 지워짐 → 호스트 재방출 판정
18
+ // bun scripts/ux-sim.ts --fault pool-cap # 결함 주입: --pod-pool 에 :N 이 빠진 풀 → 승인 판정(limitedBy)
16
19
  //
17
20
  // ⛔ 이 CLI 는 «실제 위젯 render()» 를 부른다 — 흉내 렌더러가 아니다.
18
21
  import { simRenderLogFrame } from '../src/ux-sim/render-frame.js';
@@ -33,7 +36,7 @@ const argv = process.argv.slice(2);
33
36
  // ⛔ 그래서 「알려진 플래그」를 «한 곳»에 두고, 그 밖의 `--` 인자는 거부한다.
34
37
  const KNOWN_FLAGS = new Set([
35
38
  '--list', '--list-trajectories', '--computer-use', '--trajectory', '--from-run',
36
- '--save-trajectory', '--esc', '--queue', '--width', '--height', '--draft',
39
+ '--save-trajectory', '--esc', '--queue', '--width', '--height', '--draft', '--fault',
37
40
  ]);
38
41
  {
39
42
  const unknown = argv.filter((a) => a.startsWith('--') && !KNOWN_FLAGS.has(a.split('=')[0]));
@@ -46,6 +49,32 @@ const KNOWN_FLAGS = new Set([
46
49
  }
47
50
  if (argv.includes('--list')) { console.log([...PHASES, 'computer-use: normal | capture-stalls | capture-fails | target-missing'].join('\n')); process.exit(0); }
48
51
 
52
+ {
53
+ // `--fault=<이름>` 꼴도 받는다 — KNOWN_FLAGS 는 등호 꼴을 통과시키므로 여기서 놓치면 조용히 기본 국면으로 빠진다.
54
+ const eq = argv.find((a) => a.startsWith('--fault='));
55
+ if (eq) argv.splice(argv.indexOf(eq), 1, '--fault', eq.slice('--fault='.length));
56
+ }
57
+ if (argv.includes('--fault')) {
58
+ const fault = argv[argv.indexOf('--fault') + 1];
59
+ if (fault === 'cred-expiry') {
60
+ const { credExpirySweep, renderCredExpiry } = await import('../src/ux-sim/fault-cred-expiry.js');
61
+ console.log(renderCredExpiry(credExpirySweep()).join('\n'));
62
+ process.exit(0);
63
+ }
64
+ if (fault === 'pod-death') {
65
+ const { simPodDeath, renderPodDeath } = await import('../src/ux-sim/fault-pod-death.js');
66
+ console.log(renderPodDeath(simPodDeath()).join('\n'));
67
+ process.exit(0);
68
+ }
69
+ if (fault === 'pool-cap') {
70
+ const { poolCapSweep, renderPoolCap } = await import('../src/ux-sim/fault-pool-cap.js');
71
+ console.log(renderPoolCap(poolCapSweep()).join('\n'));
72
+ process.exit(0);
73
+ }
74
+ console.error(`⛔ 모르는 결함이다: ${fault ?? '(없음)'} — 아는 것: cred-expiry · pod-death · pool-cap`);
75
+ process.exit(2);
76
+ }
77
+
49
78
  if (argv.includes('--list-trajectories')) {
50
79
  const { listSavedBrowserActionTrajectories } = await import('../src/harness/browser-trajectory.js');
51
80
  const saved = listSavedBrowserActionTrajectories();
@@ -10,7 +10,7 @@ import { fetchSupadataTranscript, judgeTranscriptSufficiency } from '../src/tran
10
10
  import { downloadAudio, splitAudio } from '../src/audio.js';
11
11
  import { transcribeChunks } from '../src/stt.js';
12
12
  import { summarize, parseMetaFromSummary } from '../src/summarize.js';
13
- import { getSaveDir, saveMarkdown, prepareRunDir, persistArtifacts } from '../src/obsidian.js';
13
+ import { getSaveDir, saveMarkdown, prepareRunDir, persistArtifacts, saveSkipNotice } from '../src/obsidian.js';
14
14
  import { classifyDomain, stagingEnabled, stagingSubdir } from '../src/domain-staging.js';
15
15
  import { buildStudyNote } from '../src/study-note.js';
16
16
  import { decideRoute } from '../src/router.js';
@@ -203,6 +203,7 @@ async function main() {
203
203
  });
204
204
  console.log(` 저장 완료: ${savedPath}`);
205
205
  }
206
+ const skipNotice = savedPath === null ? saveSkipNotice(route.target, saveDir) : null;
206
207
 
207
208
  if (flags.print) {
208
209
  console.log('\n---BEGIN_YOUTUBE_MASTER_MARKDOWN---\n');
@@ -211,6 +212,8 @@ async function main() {
211
212
  }
212
213
 
213
214
  outputSignals(route);
215
+
216
+ if (skipNotice) console.log(skipNotice);
214
217
  }
215
218
 
216
219
  // ── Transcript acquisition (auto-fallback) ──
@@ -21,6 +21,15 @@ export function getStudyNoteSaveDir(): string | null {
21
21
  return join(vaultRoot, ...sub.split('/'));
22
22
  }
23
23
 
24
+ const SAVE_SKIP_NOTICE =
25
+ '저장 안 됨 · OBSIDIAN_VAULT_ROOT 가 없다 · 고칠 한 줄: export OBSIDIAN_VAULT_ROOT=<vault 경로>';
26
+
27
+ /** target 이 obsidian 인데 saveDir 이 없으면 세 칸 안내, 아니면 null. */
28
+ export function saveSkipNotice(target: string, saveDir: string | null): string | null {
29
+ if (target === 'obsidian' && saveDir == null) return SAVE_SKIP_NOTICE;
30
+ return null;
31
+ }
32
+
24
33
  /* ── Artifact persistence (Cloud STT path) ── */
25
34
 
26
35
  export async function prepareRunDir(saveDir: string, meta: VideoMeta, videoId: string): Promise<string> {