elanous 0.2.23 → 0.2.25

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 (688) 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/chunks/1027.a0f04663d24c2779.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +36 -0
  8. package/apps/pwa/out/_next/static/chunks/1146.80d15ea7c6e6ee95.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/1157.f08bf2b70915e176.js +148 -0
  10. package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1454-37fd16f479f5554a.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/{8532.a86e99acf9b761cb.js → 1516.83dd2c1b86b4e507.js} +2 -2
  16. package/apps/pwa/out/_next/static/chunks/{89849432.9eaa2f4c9c010965.js → 16e57f67.3ea19e893461e611.js} +3 -3
  17. package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{1505.5826671109111e75.js → 1743.563d3168e95825ef.js} +2 -2
  19. package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +27 -0
  20. package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2528.3868070ef1be69f9.js +24 -0
  25. package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2697.76ca2ac3efef6f3b.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +32 -0
  28. package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2846.d6a433b8663819a5.js +309 -0
  30. package/apps/pwa/out/_next/static/chunks/3143.257997a5f1ed79df.js +34 -0
  31. package/apps/pwa/out/_next/static/chunks/{5967-c0a80f8ffe5d4050.js → 3259-86f3be56e43eaf6a.js} +1 -1
  32. package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3309.364573ceedbdfd5f.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +287 -0
  36. package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/{5578.ac963634373b28c6.js → 4084.15ecaf4240bd9e77.js} +2 -2
  41. package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/{8438.ee7f0df3b3f6fcb4.js → 4204.a409ab6250c0f6a8.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +8 -0
  47. package/apps/pwa/out/_next/static/chunks/4565-67e4c620597965c8.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/{7905.b230b9d06cb4ad5d.js → 4593.10f5b9bd702d4a6c.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +104 -0
  51. package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +149 -0
  53. package/apps/pwa/out/_next/static/chunks/{8569.b853a8daa8f31d12.js → 5081.a240b63fad28663a.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/{2528.0af5c0e1fad88df6.js → 5408.377c8c543635b81f.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/{9502-32c5dc22e8f120f5.js → 5492-e05e26a033ba80d2.js} +3 -3
  61. package/apps/pwa/out/_next/static/chunks/5785-ea8d2c633b6688e3.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/599-a914cc0468f56143.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +23 -0
  67. package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/{5974.360f8441eb0ac1ec.js → 6196.34d6b229079e9e28.js} +1 -1
  70. package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +77 -0
  72. package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +64 -0
  74. package/apps/pwa/out/_next/static/chunks/{ce383d63.d2ede338788ed18e.js → 65ebff95.081daeff622d238e.js} +1 -1
  75. package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +2 -0
  77. package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/{83cfc5c8.349535543c1548b6.js → 6def8a8e.c81f9ca41028eccf.js} +1 -1
  79. package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/{2721.bc9a8f36bd85c07a.js → 7483.ba01c6c209b81bba.js} +1 -1
  84. package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +62 -0
  87. package/apps/pwa/out/_next/static/chunks/{6702.ebbd165d5e4a4a14.js → 7752.5cbdd0b433c54af9.js} +1 -1
  88. package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/{8925-2abfb48af477fb7f.js → 8085-1e5fce59500a2cc2.js} +1 -1
  94. package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +226 -0
  96. package/apps/pwa/out/_next/static/chunks/{b4e12462-5fab4a813a63dadd.js → 83a80514-b897b64df43ce108.js} +1 -1
  97. package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +93 -0
  98. package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +24 -0
  99. package/apps/pwa/out/_next/static/chunks/{8264.00e434fbd02e9311.js → 8578.dff22d988cc2b106.js} +3 -3
  100. package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +115 -0
  103. package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +41 -0
  105. package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +74 -0
  106. package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +94 -0
  107. package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{7518.49161e8f5814abdb.js → 9311.24c8f5ea793253dd.js} +1 -1
  110. package/apps/pwa/out/_next/static/chunks/{9976.8f28864317639fe6.js → 94.4cfed21c0b781a06.js} +3 -3
  111. package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/949.1e7d45ccb71580aa.js +90 -0
  113. package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/{0e3acd34.d42b2ef87fdf4234.js → 973cbf1b.3bbcf2b41548eca7.js} +1 -1
  115. package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/{6571-926ddf024a6ad5df.js → 9917-9e26abc5f7772735.js} +1 -1
  118. package/apps/pwa/out/_next/static/chunks/9938.b23e4d4dfa0114cd.js +79 -0
  119. package/apps/pwa/out/_next/static/chunks/{740.90b31555290236a9.js → 9974.e77b4ff4884557b3.js} +2 -2
  120. package/apps/pwa/out/_next/static/chunks/{4c5e95ce.83e9b16add6fcd8c.js → ab4d57c2.e90bc2cb31949045.js} +7 -7
  121. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-ad3bd8084048bee4.js → page-b12149c51f7d04b1.js} +1 -1
  126. package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/decisions/page-305cd294e754906e.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-a648bc464c9dc754.js → page-dea4b795741126fd.js} +1 -1
  133. package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/exec/page-45889e7070d11126.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/field/page-16a2e893f83ae4d0.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/intake/page-56888bc744e08dac.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/live/page-952c6ceec6d9de38.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live-run/page-6d0202af568f4c63.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4fb6fc34e20c07b7.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/morning/page-e9502921f4f01b1f.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/not-found-b6169ea2f09cf287.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6a14333d1b1b3967.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8c91a9a66c9ec90a.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-48fb31810693c357.js → page-4e634c5b98fd4e93.js} +1 -1
  150. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-cb154acadb025414.js → page-b9cc8a10bc6078b4.js} +1 -1
  152. package/apps/pwa/out/_next/static/chunks/app/page-3d3fc73dd187ed52.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-9c04dac0831c2bbb.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-04de9ca6d52a3893.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/page-62527677bbc2fdc4.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9a2e09c4a311764c.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/page-bfeb35d69e8b24bb.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/share/page-89cb33bced824e4a.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/today/page-6445ac4cbb8dd869.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/page-20162174b77f96cf.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-05a4b2c9b91034aa.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{53746282.1be36038d1abd949.js → e117f801.e92bf10242653497.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/e87d89b9.2e71c0ae475d28a1.js +122 -0
  175. package/apps/pwa/out/_next/static/chunks/{0673cc54.8e2ef1c23e49e190.js → f4dfbfc0.a69fb12f854882aa.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +1 -0
  182. package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +1 -0
  183. package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → dRS6lEhSRRBr1abbzp7fJ}/_buildManifest.js +1 -1
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +23 -23
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +23 -23
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +23 -23
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +22 -22
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +22 -22
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +23 -23
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +22 -22
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +23 -23
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +23 -23
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +22 -22
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +23 -23
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +23 -23
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +23 -23
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +23 -23
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +22 -22
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +22 -22
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +23 -23
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +23 -23
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +23 -23
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +22 -22
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +22 -22
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +23 -23
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +23 -23
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +22 -22
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +22 -22
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +22 -22
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +23 -23
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +23 -23
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +22 -22
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +23 -23
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +23 -23
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +23 -23
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +23 -23
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +23 -23
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +22 -22
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +23 -23
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +23 -23
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +23 -23
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +23 -23
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +23 -23
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +23 -23
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +23 -23
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +23 -23
  277. package/catalog/models/openrouter/deepseek-v4-pro-0813.yaml +39 -0
  278. package/catalog/models/openrouter/deepseek-v4.1-flash.yaml +38 -0
  279. package/catalog/models/openrouter/glm-5.3-flash.yaml +35 -0
  280. package/catalog/providers/openrouter.yaml +2 -0
  281. package/graphs/implement-loop.yaml +31 -0
  282. package/graphs/ops/harness-gc.yaml +17 -0
  283. package/graphs/ops/recipes.yaml +3 -0
  284. package/graphs/overlays/README.md +1 -1
  285. package/graphs/overlays/stop-heal-ladder.yaml +105 -0
  286. package/graphs/release/release-loop.yaml +5 -5
  287. package/graphs/steward/recipes.yaml +4 -0
  288. package/graphs/steward/steward.yaml +10 -1
  289. package/package.json +1 -1
  290. package/scripts/add-api-key.sh +1 -1
  291. package/scripts/bench-report.ts +46 -1
  292. package/scripts/ci-typecheck-changed.ts +73 -14
  293. package/scripts/fleet/bedrock-pod-secret.ts +122 -0
  294. package/scripts/fleet/node-setup-linux.sh +97 -0
  295. package/scripts/fleet/node-setup.sh +11 -0
  296. package/scripts/fleet/zai-pod-secret.ts +86 -0
  297. package/scripts/land-global-checks.ts +3 -0
  298. package/scripts/measure-log-diet-2.ts +213 -0
  299. package/scripts/measure-tsc-affected-roots.ts +289 -0
  300. package/scripts/release-loop/auto-approve-node.ts +6 -0
  301. package/scripts/release-loop/gate-closure.ts +286 -0
  302. package/scripts/release-loop/gate-executor.ts +28 -0
  303. package/scripts/release-loop/gate-node.ts +333 -37
  304. package/scripts/release-loop/gate-partial.ts +34 -6
  305. package/scripts/release-loop/ops-upgrade-node.ts +63 -42
  306. package/scripts/release-loop/publish-node.ts +6 -0
  307. package/scripts/release-loop/resume-release.ts +46 -13
  308. package/scripts/release-loop/resume.ts +110 -4
  309. package/scripts/release-loop/unattended-release.ts +22 -7
  310. package/scripts/tsc-baseline-cache.ts +172 -0
  311. package/scripts/usage-rollup.ts +72 -6
  312. package/scripts/ux-sim.ts +30 -1
  313. package/skills/youtube-master/scripts/main.ts +4 -1
  314. package/skills/youtube-master/src/obsidian.ts +9 -0
  315. package/src/agent/run-context.ts +10 -0
  316. package/src/agent-mission/driver.ts +18 -3
  317. package/src/agent-mission/resource-ladder.ts +8 -3
  318. package/src/agent-mission/review-loop.ts +8 -2
  319. package/src/agent-substrate/acp-implementer.ts +146 -0
  320. package/src/agent-substrate/pr-reviewer.ts +60 -13
  321. package/src/agent-substrate/self-review-cli.ts +170 -15
  322. package/src/autopilot/mission-critique.ts +18 -5
  323. package/src/autopilot/mission-feasibility-negotiate.ts +11 -10
  324. package/src/autopilot/mission-trace.ts +12 -1
  325. package/src/autopilot/system-lookback-run.ts +16 -10
  326. package/src/bench/bench.ts +183 -0
  327. package/src/budget/cost-tier-advisor.ts +91 -0
  328. package/src/budget/fetchers/grok.ts +15 -5
  329. package/src/budget/llm-cost.ts +37 -5
  330. package/src/budget/shadow-arms.ts +77 -0
  331. package/src/budget/types.ts +10 -2
  332. package/src/budget/unified-usage.ts +256 -10
  333. package/src/cli/bench-cli.ts +34 -0
  334. package/src/cli/doctor-cli.ts +21 -0
  335. package/src/cli/friendly-errors.ts +113 -0
  336. package/src/cli/guardian-cli.ts +26 -1
  337. package/src/cli/image-cli.ts +101 -0
  338. package/src/cli/intake-cli.ts +3 -1
  339. package/src/cli/log-event-names.ts +4 -0
  340. package/src/cli/memory-cli.ts +182 -0
  341. package/src/cli/model-watch-cli.ts +4 -1
  342. package/src/cli/pr-cli.ts +62 -5
  343. package/src/cli/pr-land-overlap-evidence.ts +160 -12
  344. package/src/cli/pr-land-pod-shadow.ts +54 -0
  345. package/src/cli/release-cli.ts +27 -16
  346. package/src/cli/release-run-if-ready.ts +11 -0
  347. package/src/cli/resource-cli.ts +48 -0
  348. package/src/cli/retro-cli.ts +12 -3
  349. package/src/cli/rfc-status.ts +52 -2
  350. package/src/cli/seat-requests-cli.ts +25 -0
  351. package/src/cli/self-update.ts +104 -0
  352. package/src/cli/tasks-cli.ts +139 -10
  353. package/src/cli/usage-cli.ts +93 -8
  354. package/src/compact/verify-probe.ts +23 -3
  355. package/src/context-card/index.ts +50 -0
  356. package/src/control/control-status.ts +30 -1
  357. package/src/core-turn/run-goal-loop.ts +5 -4
  358. package/src/dashboard/ad-run-setup-bindings.ts +279 -0
  359. package/src/dashboard/index.ts +18 -276
  360. package/src/dashboard/slash-runtime/dashboard-handlers.ts +4 -138
  361. package/src/dashboard/slash-runtime/shell-slash.ts +145 -0
  362. package/src/dashboard/stale-install-check.ts +11 -0
  363. package/src/debug/log-diet.ts +53 -10
  364. package/src/debug/log.ts +19 -1
  365. package/src/domains/morning-essential.ts +18 -3
  366. package/src/domains/release-tool.ts +43 -0
  367. package/src/error-report/report.ts +21 -4
  368. package/src/execution-loop/launch-gate.ts +158 -2
  369. package/src/fleet/host-doctor.ts +375 -0
  370. package/src/github/pr-labels.ts +1 -0
  371. package/src/goals/judge.ts +20 -6
  372. package/src/graph-runner/graph-cli.ts +42 -1
  373. package/src/graph-runner/runner.ts +176 -11
  374. package/src/graph-wizard/archetypes.ts +14 -8
  375. package/src/graph-wizard/generate.ts +77 -1
  376. package/src/grok/usage.ts +23 -2
  377. package/src/grok.ts +13 -7
  378. package/src/grounding/ref-index.ts +253 -0
  379. package/src/harness/dispatch-backpressure.ts +10 -2
  380. package/src/harness/harness-clean.ts +199 -78
  381. package/src/harness/harness-cli-command.ts +176 -52
  382. package/src/harness/harness-gc.ts +331 -0
  383. package/src/harness/harness-stop.ts +82 -10
  384. package/src/harness/harness-substrate-default.ts +142 -7
  385. package/src/harness/launch-registry.ts +6 -0
  386. package/src/harness/worktree-branch-prefix.ts +9 -0
  387. package/src/hq/fence.ts +187 -0
  388. package/src/hq/seats.ts +38 -22
  389. package/src/images/image-ledger.ts +179 -0
  390. package/src/index.ts +110 -186
  391. package/src/instance/child-scope.ts +32 -1
  392. package/src/instance/dev-project.ts +133 -0
  393. package/src/instance/resolve.ts +24 -0
  394. package/src/instance/spawn-elanous.ts +45 -0
  395. package/src/intake-plane/collect-github.ts +7 -2
  396. package/src/intake-plane/lens.ts +46 -4
  397. package/src/intake-plane/ref-prefetch.ts +110 -0
  398. package/src/intake-plane/requirement-funnel.ts +53 -1
  399. package/src/intelligence-map/catalog-feed.ts +119 -0
  400. package/src/intelligence-map/model-catalog.ts +72 -0
  401. package/src/intelligence-map/model-watch-intake.ts +40 -0
  402. package/src/intelligence-map/model-watch-mission.ts +14 -1
  403. package/src/llm/bedrock-eventstream.ts +215 -0
  404. package/src/llm/bedrock-provider.ts +114 -41
  405. package/src/llm/bedrock.ts +174 -28
  406. package/src/llm/open-weight-discipline.ts +21 -0
  407. package/src/llm/openrouter-byok-cost.ts +96 -0
  408. package/src/llm/openrouter-deepseek.ts +151 -0
  409. package/src/llm/openrouter-upstream-pins.config.ts +25 -0
  410. package/src/llm/zai-coding-plan.ts +700 -0
  411. package/src/llm.ts +91 -24
  412. package/src/loops/checker.ts +148 -9
  413. package/src/loops/orchestrator/resync-shadow.ts +34 -7
  414. package/src/maturity/feature-maturity.ts +3 -3
  415. package/src/model-tier/llm-tier-map.ts +9 -5
  416. package/src/models/prompts.ts +2 -1
  417. package/src/mss/logging/log-store.ts +58 -11
  418. package/src/nexus/api/http-server.ts +22 -0
  419. package/src/nexus/api/missions.ts +73 -2
  420. package/src/nexus/api/ops-api.ts +18 -0
  421. package/src/nexus/index.ts +7 -75
  422. package/src/nexus/tools-kind.ts +78 -0
  423. package/src/oauth/codex-account-rotation.ts +43 -3
  424. package/src/oauth/codex-account-store.ts +339 -27
  425. package/src/oauth/codex-quota-policy.ts +33 -4
  426. package/src/oauth/fallback-chain.ts +53 -15
  427. package/src/onboarding/first-run.ts +37 -18
  428. package/src/onboarding/web-first.ts +20 -2
  429. package/src/ops/machine-heartbeat.ts +185 -0
  430. package/src/ops/overview-cache.ts +175 -0
  431. package/src/ops/overview.ts +8 -0
  432. package/src/preview/terminal.ts +15 -5
  433. package/src/project/project-store.ts +20 -0
  434. package/src/prompt-cache/openai.ts +3 -0
  435. package/src/prompt-cache/types.ts +5 -0
  436. package/src/prompt-enhance/enhance.ts +23 -0
  437. package/src/release-loop/auto-start.ts +7 -5
  438. package/src/release-loop/checklist.ts +11 -1
  439. package/src/release-loop/gate-shards.ts +4 -0
  440. package/src/release-loop/light-rc.ts +84 -13
  441. package/src/release-loop/merged-pr-checklist.ts +35 -5
  442. package/src/release-loop/placement.ts +16 -1
  443. package/src/release-loop/rehearsal.ts +1 -1
  444. package/src/requirements/requirement-ledger.ts +536 -0
  445. package/src/retro/daily.ts +5 -5
  446. package/src/seat-loop/duty-ledger.ts +64 -0
  447. package/src/seat-loop/seat-loop.ts +32 -10
  448. package/src/self-dev/decompose.ts +37 -3
  449. package/src/self-dev/dev-cli.ts +44 -10
  450. package/src/self-dev/dev-pipeline.ts +39 -9
  451. package/src/self-dev/draft-residue.ts +27 -5
  452. package/src/self-dev/draft-sweep.ts +151 -0
  453. package/src/self-dev/fabric-decompose-adapter.ts +9 -2
  454. package/src/self-dev/graph-journey-route.ts +6 -0
  455. package/src/self-dev/orchestrate-cli.ts +25 -3
  456. package/src/self-dev/orchestrate.ts +41 -6
  457. package/src/self-dev/run-supervisor.ts +218 -15
  458. package/src/self-dev/run-types.ts +13 -0
  459. package/src/self-dev/supervisor-verdict-edges.ts +1 -0
  460. package/src/self-implement/budget-gate.ts +53 -13
  461. package/src/self-implement/child-context-composition.ts +139 -0
  462. package/src/self-implement/child-llm-preference.ts +13 -5
  463. package/src/self-implement/claude-acp-implement.ts +67 -0
  464. package/src/self-implement/flex-signals.ts +59 -0
  465. package/src/self-implement/frozen-merges.ts +8 -3
  466. package/src/self-implement/gate-cli.ts +29 -10
  467. package/src/self-implement/gate-remote.ts +4 -1
  468. package/src/self-implement/goal-author-decompose.ts +10 -2
  469. package/src/self-implement/goal-author.ts +36 -11
  470. package/src/self-implement/goal-new-paths.ts +42 -0
  471. package/src/self-implement/graph-overlay-condition.ts +28 -0
  472. package/src/self-implement/graph-overlay-yaml.ts +41 -7
  473. package/src/self-implement/graph-templates.ts +33 -1
  474. package/src/self-implement/graph-yaml.ts +56 -0
  475. package/src/self-implement/heal-intake.ts +5 -1
  476. package/src/self-implement/host-regate.ts +124 -30
  477. package/src/self-implement/importer-test-index.ts +3 -1
  478. package/src/self-implement/l8-merge-queue.ts +80 -2
  479. package/src/self-implement/merge-train.ts +19 -0
  480. package/src/self-implement/off-diff-evidence.ts +40 -1
  481. package/src/self-implement/orchestrator.ts +230 -32
  482. package/src/self-implement/premise-review.ts +159 -0
  483. package/src/self-implement/regate-detail.ts +79 -0
  484. package/src/self-implement/review-deferred-to-host.ts +21 -0
  485. package/src/self-implement/rework-budget-judgment.ts +23 -3
  486. package/src/self-implement/running-runs.ts +75 -5
  487. package/src/self-implement/scope-adjacency.ts +23 -0
  488. package/src/self-implement/seams.ts +89 -8
  489. package/src/self-implement/self-implement-cli.ts +9 -4
  490. package/src/self-implement/stop-autoheal.ts +69 -2
  491. package/src/session-runtime/retry-policy.ts +2 -1
  492. package/src/skills/tools/agent.ts +16 -1
  493. package/src/skills/tools/persistent-grounding.ts +11 -1
  494. package/src/skills/tools/shell.ts +33 -1
  495. package/src/steward/launch.ts +5 -4
  496. package/src/steward/rejudge-tick.ts +142 -0
  497. package/src/steward/triage-budget.ts +62 -0
  498. package/src/steward/triage.ts +83 -22
  499. package/src/task-agent/actions.ts +62 -5
  500. package/src/task-agent/board.ts +92 -3
  501. package/src/task-agent/card-evidence.ts +88 -1
  502. package/src/task-agent/completion-evidence.ts +5 -0
  503. package/src/task-agent/cover.ts +166 -3
  504. package/src/task-agent/judge.ts +16 -0
  505. package/src/task-agent/land-order-ledger.ts +95 -0
  506. package/src/task-agent/live-move-history.ts +87 -1
  507. package/src/task-agent/live-moves.ts +350 -20
  508. package/src/task-agent/parent-host.ts +27 -3
  509. package/src/task-agent/rejudge-on-head.ts +223 -24
  510. package/src/task-agent/review-prior-fix.ts +146 -0
  511. package/src/task-agent/review-result-capture.ts +3 -1
  512. package/src/task-agent/run-adopt.ts +136 -0
  513. package/src/task-agent/shadow.ts +65 -9
  514. package/src/task-agent/task-hand.ts +57 -7
  515. package/src/task-orchestrator/surfaces/pod-account-broker.ts +38 -5
  516. package/src/task-orchestrator/surfaces/pod-bedrock-child.ts +74 -0
  517. package/src/task-orchestrator/surfaces/pod-child-default.ts +50 -0
  518. package/src/task-orchestrator/surfaces/pod-child-providers.ts +14 -1
  519. package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -0
  520. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +79 -6
  521. package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +122 -0
  522. package/src/task-orchestrator/surfaces/pod-lease.ts +17 -5
  523. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
  524. package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +23 -0
  525. package/src/task-orchestrator/surfaces/pod-pool.ts +73 -4
  526. package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
  527. package/src/task-orchestrator/surfaces/self-implement-pod.ts +432 -35
  528. package/src/task-orchestrator/surfaces/self-implement.ts +19 -1
  529. package/src/typecheck-ratchet.ts +1 -0
  530. package/src/user-config.ts +69 -15
  531. package/src/ux-sim/fault-cred-expiry.ts +100 -0
  532. package/src/ux-sim/fault-pod-death.ts +74 -0
  533. package/src/ux-sim/fault-pool-cap.ts +81 -0
  534. package/src/version/packed-revision.json +1 -1
  535. package/src/webclone/qa-table.ts +22 -0
  536. package/src/workflow-runtime/triggers/registry.ts +59 -0
  537. package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
  538. package/apps/pwa/out/_next/static/chunks/0a09e062-34f747a17d8d8c2e.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +0 -77
  540. package/apps/pwa/out/_next/static/chunks/1325-dc3f3c21eb181cb4.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +0 -34
  542. package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +0 -24
  543. package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/1669-da843ed73aa301f5.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +0 -36
  553. package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +0 -122
  554. package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +0 -62
  556. package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/272-b23d2e92161d11fd.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +0 -149
  563. package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/3961-fbef064c213b2b18.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +0 -309
  567. package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/5313.1697309c532b9856.js +0 -2
  579. package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +0 -74
  587. package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +0 -64
  591. package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +0 -32
  594. package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +0 -24
  595. package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +0 -93
  599. package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +0 -148
  604. package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +0 -226
  605. package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +0 -27
  606. package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +0 -115
  609. package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +0 -8
  616. package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +0 -23
  617. package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +0 -90
  622. package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +0 -94
  623. package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +0 -41
  625. package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +0 -287
  627. package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +0 -79
  628. package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +0 -104
  633. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5341ac96203a983.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/app/board/page-55c631c032dcc47e.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/app/ceo/page-5c5d0e370f495ef6.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/app/chat/page-c325e21543cd48a8.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/app/consult/page-6168a7c43e6ef8f5.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/app/editor/page-25bb425afdb27541.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/app/exec/page-32d72fdef1afe11a.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/inside/page-2bd9ce9c020d02d4.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/intake/page-a34096245e63b9f2.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/live/page-4cc66d22ca4ec502.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/live-run/page-83b14c729fcea837.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-ccdb83ed40c8d724.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/morning/page-7ad3de8fa06fdea9.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-786111f30b6f0969.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e17cce5e2ed0fee.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/reflection/page-c2dd2b19c93f90ab.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d3c007c8a0ddd7ee.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7837ce2aacb97b04.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/showroom/page-119be9a0444d2482.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/tasks/page-fa806c018833e332.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/today/page-d431fbdea6ad93dd.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-847c3de6ca8a4416.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/workspace/page-74e69ba2c8e970d4.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-70038f41ae7cf931.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/framework-b2763b10dc6b3836.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +0 -1
  687. package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +0 -1
  688. /package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → dRS6lEhSRRBr1abbzp7fJ}/_ssgManifest.js +0 -0
@@ -19,6 +19,10 @@ export interface LaunchBinding {
19
19
  readonly goalPath?: string;
20
20
  /** The `self-dev-runs` directory that holds this run's `pid.json` — tells a stop which universe launched it. */
21
21
  readonly runsDir?: string;
22
+ /** `ELANOUS_POD_NAME` at launch — a Pod child's run (TA backfill skips it like the launch-time hook does). */
23
+ readonly podName?: string;
24
+ /** `ELANOUS_TASK_CARD_ID` at launch — a handed run that already owns a card (TA backfill skips it). */
25
+ readonly taskCardId?: string;
22
26
  }
23
27
 
24
28
  const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
@@ -72,6 +76,8 @@ export function readLaunchBinding(runId: string, dir = launchRegistryDir()): Lau
72
76
  ...(typeof value.argv0 === 'string' ? { argv0: value.argv0 } : {}),
73
77
  ...(typeof value.goalPath === 'string' && isAbsolute(value.goalPath) ? { goalPath: value.goalPath } : {}),
74
78
  ...(typeof value.runsDir === 'string' && isAbsolute(value.runsDir) ? { runsDir: value.runsDir } : {}),
79
+ ...(typeof value.podName === 'string' && value.podName ? { podName: value.podName } : {}),
80
+ ...(typeof value.taskCardId === 'string' && value.taskCardId ? { taskCardId: value.taskCardId } : {}),
75
81
  };
76
82
  } catch { return null; }
77
83
  }
@@ -1,4 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { branchGoalId } from '../cli/pr-lineage.js';
2
3
 
3
4
  /** Prefix for disposable worktree branches created by self-implement and inspected by harness clean.
4
5
  * A successfully merged run cleans up its own worktree.
@@ -31,3 +32,11 @@ export function plannedSelfImplBranch(feature: string, goalId?: string, runId?:
31
32
  const digest = createHash('sha256').update(id).digest('hex').slice(0, 8);
32
33
  return `${WORKTREE_BRANCH_PREFIX}goalid-${id}-${readable}-${digest}${runSuffix}`;
33
34
  }
35
+
36
+ /** 같은 골의 재실행이 공유하는 «안정한» 계보 키. `plannedSelfImplBranch` 가 싣는 `goalid-<id>-` 구간만 읽는다 —
37
+ * feature 문구·다이제스트·런 접미(`-r<runId>`)는 재실행마다 달라지므로 키에 넣지 않는다.
38
+ * ⛔ 골 식별자가 없는 옛 브랜치는 `null` — 읽기용 슬러그는 서로 다른 골이 같은 값을 낼 수 있어(`grounded-N-…`) 키가 될 수 없다. */
39
+ export function branchLineageKey(branch: string): string | null {
40
+ const id = branchGoalId(branch);
41
+ return id ? `goalid:${id.toLowerCase()}` : null;
42
+ }
@@ -0,0 +1,187 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readFileSync, readlinkSync, readdirSync } from 'node:fs';
3
+ import { cronMatches } from '../domains/cron-match.js';
4
+ import { resolveTimeZone } from '../time/format.js';
5
+
6
+ export interface CliProcess { pid: number; ppid: number; ageSeconds: number; cpuSeconds: number; command: string }
7
+ export interface OrphanCandidate { process: CliProcess; tree: CliProcess[]; periodMinutes: number }
8
+ export interface FenceGcDeps {
9
+ snapshot?: () => CliProcess[];
10
+ run?: (binary: string, args: string[]) => string;
11
+ sleep?: (ms: number) => Promise<void>;
12
+ kill?: (pid: number) => void;
13
+ timeZone?: string;
14
+ }
15
+
16
+ export function cliFingerprint(command: string): string {
17
+ return command.replace(/^\s*(?:\S*\/)?bun\s+(?:\S*\/)?elanous\.mjs\s+/, 'elanous ')
18
+ .replace(/^\s*(?:\S*\/)?(?:elanous\.mjs|eln)\s+/, 'elanous ')
19
+ .trim();
20
+ }
21
+
22
+ function seconds(text: string): number {
23
+ const parts = text.split(':').map(Number);
24
+ return parts.reduce((total, part) => total * 60 + part, 0);
25
+ }
26
+
27
+ /** ps gives command after the four numeric fields; never split command on whitespace. */
28
+ export function processSnapshot(): CliProcess[] {
29
+ const result = spawnSync('ps', ['-axo', 'pid=,ppid=,etime=,time=,command='], { encoding: 'utf8', timeout: 10_000, maxBuffer: 4 * 1024 * 1024 });
30
+ if (result.status !== 0) throw new Error(`ps failed: ${result.stderr}`);
31
+ return result.stdout.split('\n').flatMap(line => {
32
+ const fields = /^\s*(\d+)\s+(\d+)\s+([\d:-]+)\s+([\d:.]+)\s+(.+)$/.exec(line);
33
+ if (!fields) return [];
34
+ const elapsed = fields[3]!.split('-');
35
+ const ageSeconds = seconds(elapsed.at(-1)!) + (elapsed.length === 2 ? Number(elapsed[0]) * 86_400 : 0);
36
+ const cpuSeconds = seconds(fields[4]!);
37
+ if (!Number.isFinite(ageSeconds) || !Number.isFinite(cpuSeconds)) return [];
38
+ return [{ pid: Number(fields[1]), ppid: Number(fields[2]), ageSeconds, cpuSeconds, command: fields[5]! }];
39
+ });
40
+ }
41
+
42
+ /** Only installed elanous one-shot CLI launches, not shells, daemons, or an incidental flag inside a quoted payload. */
43
+ export function oneShotCommand(command: string): boolean {
44
+ const invocation = /^(?:(?:\S*\/)?bun\s+(?:\S*\/)?elanous\.mjs|(?:\S*\/)?(?:elanous|elanous\.mjs|eln))\s+/.exec(command);
45
+ if (!invocation) return false;
46
+ const args = command.slice(invocation[0].length).split(/\s+--\s+|\s+-c\s+/)[0] ?? '';
47
+ const payload = /\s+--\s+(.+)$/.exec(command)?.[1] ?? '';
48
+ const shellBody = /\s+--\s+(?:\S*\/)?(?:sh|bash)\s+-c\s+(.+)$/.exec(command)?.[1];
49
+ if (shellBody !== undefined) {
50
+ if (!/^['"]?(?:\S*\/)?bun\s+(?:\S*\/)?elanous\.mjs\s+/.test(shellBody)
51
+ || /[;|`&]/.test(shellBody) || /\bnexus\s+run\b/.test(shellBody)
52
+ || !/(?:^|\s)(?:--once|tick|--refresh|--notify)(?:\s|['"]|$)/.test(shellBody)) return false;
53
+ }
54
+ // Test-universe and help invocations are never collected — on the fence itself, in its payload, or in a wrapped shell body.
55
+ for (const part of [args, payload, shellBody ?? '']) {
56
+ if (/(?:^|\s|['"])--test(?:\s|=|['"]|$)/.test(part) || /(?:^|\s)(?:--help|-h)(?:\s|['"]|$)/.test(part)) return false;
57
+ }
58
+ // The daemon is never a one-shot, whatever flag rides along (`nexus run --once`), directly or as a fence payload.
59
+ if (/(?:^|\s)nexus\s+run\b/.test(args) || /(?:^|\s)nexus\s+run\b/.test(payload) || /&&|\|\||[;|`&]/.test(payload)) return false;
60
+ if (/^hq\s+fence\s+--role\s+(?:cron|seat-loop|conatus|release-run)\b/.test(args) && payload) {
61
+ return /(?:^|\s)(?:--once|tick|--refresh|--notify)(?:\s|['"]|$)/.test(payload);
62
+ }
63
+ return /(?:^|\s)hq\s+fence\s+--role\s+(?:cron|seat-loop|conatus|release-run)\b/.test(args)
64
+ || /(?:^|\s)--once(?:\s|$)/.test(args)
65
+ || /(?:^|\s)(?:tick|--refresh|--notify)(?:\s|$)/.test(args);
66
+ }
67
+
68
+ /** Unknown/mismatched schedules are not assigned a made-up period. Match the launched action (including fence payload). */
69
+ export function scheduledPeriodMinutes(command: string, schedules: readonly { command: string; cron: string }[], now: Date, timeZone = resolveTimeZone().timeZone): number | null {
70
+ const current = cliFingerprint(command);
71
+ const matches = schedules.filter(job => {
72
+ const action = job.command.trim().replace(/^cd\s+\S+\s*&&\s*/, '').replace(/\s+>>?\s+\S+(?:\s+2>&1)?\s*$/, '');
73
+ if (action.length < 16) return false;
74
+ if (current === cliFingerprint(action)) return true;
75
+ // The installed hq-fence shell wrapper execs the CLI with an explicit role and -- payload.
76
+ // Accept only a single, unambiguous quoted command; never infer a period from a substring.
77
+ const wrapper = /^(?:\S*\/)?hq-fence\s+(cron|seat-loop|conatus|release-run)\s+'([^']+)'$/.exec(action);
78
+ if (!wrapper) return false;
79
+ return current === `elanous hq fence --role ${wrapper[1]} -- ${wrapper[2]}`;
80
+ });
81
+ if (matches.length !== 1) return null;
82
+ const cron = matches[0]!.cron;
83
+ if (cron.trim().split(/\s+/).length !== 5) return null;
84
+ const start = Math.floor(now.getTime() / 60_000) * 60_000;
85
+ // A 7-day minute scan per process is costly with many hung trees; one tick asks the same (cron, zone, minute) often.
86
+ const memoKey = `${cron}\0${timeZone}\0${start}`;
87
+ if (periodMemo.has(memoKey)) return periodMemo.get(memoKey)!;
88
+ const period = cronPeriodMinutes(cron, start, timeZone);
89
+ if (periodMemo.size > 256) periodMemo.clear();
90
+ periodMemo.set(memoKey, period);
91
+ return period;
92
+ }
93
+
94
+ const periodMemo = new Map<string, number | null>();
95
+
96
+ function cronPeriodMinutes(cron: string, start: number, timeZone: string): number | null {
97
+ const fires: number[] = [];
98
+ try {
99
+ // Every fire in the next 7 days, not just the first few: a weekday-hours cron (`0 9-17 * * 1-5`) is hourly
100
+ // for three gaps and then pauses for a night or a weekend — that is no single period.
101
+ for (let i = 0; i < 7 * 24 * 60; i++) {
102
+ const at = start + i * 60_000;
103
+ if (cronMatches(cron, new Date(at), { timeZone })) fires.push(at);
104
+ }
105
+ } catch { return null; }
106
+ // An irregular schedule (any unequal gap within the window, e.g. `0 9,18 * * *`, weekday hours) has no single period
107
+ // to multiply — unknown, never collected. Fewer than three fires in 7 days (weekly) is unknown too (safe side).
108
+ if (fires.length < 3) return null;
109
+ const gap = fires[1]! - fires[0]!;
110
+ for (let i = 2; i < fires.length; i++) if (fires[i]! - fires[i - 1]! !== gap) return null;
111
+ return gap / 60_000;
112
+ }
113
+
114
+ export function orphanCandidates(first: readonly CliProcess[], second: readonly CliProcess[], schedules: readonly { command: string; cron: string }[], now: Date, timeZone?: string): OrphanCandidate[] {
115
+ const byPid = new Map(second.map(row => [row.pid, row]));
116
+ const initial = new Map(first.map(row => [row.pid, row]));
117
+ const candidates: OrphanCandidate[] = [];
118
+ // The collector itself and every ancestor of it (the guardian tick's cron/fence parents) are never signalled.
119
+ const self = new Set<number>([process.pid]);
120
+ for (let pid: number | undefined = byPid.get(process.pid)?.ppid; pid !== undefined && pid > 1 && !self.has(pid); pid = byPid.get(pid)?.ppid) self.add(pid);
121
+ for (const root of first) {
122
+ if (root.pid === process.pid || !oneShotCommand(root.command)) continue;
123
+ const current = byPid.get(root.pid);
124
+ if (!current || current.command !== root.command || current.ppid !== root.ppid || current.cpuSeconds !== root.cpuSeconds) continue;
125
+ // Identity: a reused PID restarts its age; the same process can only have grown older between snapshots.
126
+ if (current.ageSeconds < root.ageSeconds) continue;
127
+ const periodMinutes = scheduledPeriodMinutes(root.command, schedules, now, timeZone);
128
+ if (!periodMinutes || root.ageSeconds <= periodMinutes * 3 * 60) continue;
129
+ // An old ancestor with an actively executing payload is not an orphan.
130
+ if (second.some(row => row.ppid === root.pid && !initial.has(row.pid))) continue;
131
+ const tree = [current];
132
+ for (let i = 0; i < tree.length; i++) {
133
+ for (const child of second) if (child.ppid === tree[i]!.pid && !tree.some(row => row.pid === child.pid)) tree.push(child);
134
+ }
135
+ if (tree.some(row => {
136
+ const before = initial.get(row.pid);
137
+ return !before || before.command !== row.command || before.cpuSeconds !== row.cpuSeconds || before.ppid !== row.ppid
138
+ || Math.abs((row.ageSeconds - before.ageSeconds) - (current.ageSeconds - root.ageSeconds)) > 2;
139
+ })) continue;
140
+ if (tree.some(row => self.has(row.pid))) continue;
141
+ candidates.push({ process: current, tree, periodMinutes });
142
+ }
143
+ return candidates.filter(candidate => !candidates.some(parent => parent !== candidate && parent.tree.some(row => row.pid === candidate.process.pid)));
144
+ }
145
+
146
+ export function gcTools(deps: FenceGcDeps = {}) {
147
+ return {
148
+ snapshot: deps.snapshot ?? processSnapshot,
149
+ sleep: deps.sleep ?? ((ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms))),
150
+ run: deps.run ?? ((binary: string, args: string[]) => {
151
+ // Linux has no macOS sample. Over one second, sample each thread's wait channel and state (readable by the
152
+ // process owner); the kernel stack is root-only on most hosts, so it is added only when readable.
153
+ if (binary === 'sample' && process.platform === 'linux') {
154
+ const pid = args[0];
155
+ const samples: string[] = [];
156
+ const deadline = Date.now() + 1_000;
157
+ do {
158
+ for (const tid of readdirSync(`/proc/${pid}/task`)) {
159
+ const base = `/proc/${pid}/task/${tid}`;
160
+ const state = /^State:\s*(.+)$/m.exec(readFileSync(`${base}/status`, 'utf8'))?.[1] ?? '?';
161
+ let stack = '';
162
+ // Root-only on most kernels: carried when readable, otherwise the evidence says so (not a collection gate).
163
+ try { stack = `\n${readFileSync(`${base}/stack`, 'utf8')}`; } catch (error) { stack = ` kernel-stack=unreadable(${(error as NodeJS.ErrnoException).code ?? 'error'})`; }
164
+ samples.push(`tid=${tid} state=${state} wchan=${readFileSync(`${base}/wchan`, 'utf8') || '0'}${stack}`);
165
+ }
166
+ if (Date.now() < deadline) Bun.sleepSync(Math.min(100, deadline - Date.now()));
167
+ } while (Date.now() < deadline);
168
+ if (samples.length === 0) throw new Error(`sample: no threads for PID ${pid}`);
169
+ return samples.join('\n');
170
+ }
171
+ // macOS `sample` by absolute path: a PATH shim (e.g. pyenv's `sample`) must not stand in for the profiler.
172
+ const resolved = binary === 'sample' && process.platform === 'darwin' ? '/usr/bin/sample' : binary;
173
+ // sample symbolicates a large Bun binary after its 1s window — allow time and room, or it would never succeed.
174
+ const result = spawnSync(resolved, args, { encoding: 'utf8', timeout: binary === 'sample' ? 30_000 : 10_000, maxBuffer: 8 * 1024 * 1024 });
175
+ if (result.status !== 0) throw new Error(`${binary}: ${result.error ?? result.stderr ?? 'failed'}`);
176
+ return result.stdout;
177
+ }),
178
+ kill: deps.kill ?? ((pid: number) => process.kill(pid, 'SIGTERM')),
179
+ };
180
+ }
181
+
182
+ /** The target's own Bun binary — read from the target PID, so a PID that died (or was reused) fails closed. */
183
+ export function bunVersion(pid: number, run: (binary: string, args: string[]) => string): string {
184
+ const bin = process.platform === 'linux' ? readlinkSync(`/proc/${pid}/exe`) : run('ps', ['-o', 'comm=', '-p', String(pid)]).trim();
185
+ if (!/(?:^|\/)bun(?:$|\s)/.test(bin)) throw new Error(`PID ${pid} is not a Bun process`);
186
+ return run(bin, ['--version']).trim();
187
+ }
package/src/hq/seats.ts CHANGED
@@ -47,6 +47,12 @@ export function resolveHqLaunchSeat(role: string, home: string, options: { root?
47
47
  if (head.status !== 1) throw new Error(`git symbolic-ref --quiet HEAD: ${head.stderr.trim() || `exit ${head.status}`}`);
48
48
  return path;
49
49
  }
50
+
51
+ /** Installed-CLI argv: absolute runtime + absolute CLI entry, so a launch never resolves `bin/elanous.mjs` against its cwd. */
52
+ export function installedCliArgv(args: readonly string[] = []): string[] {
53
+ return [process.execPath, resolve(import.meta.dir, '../../bin/elanous.mjs'), ...args];
54
+ }
55
+
50
56
  function readLedger(root: string): HouseLedger {
51
57
  const ledger = JSON.parse(readFileSync(ledgerPath(root), 'utf8')) as HouseLedger;
52
58
  if (!ledger.home?.path || !ledger.sandboxes || typeof ledger.sandboxes !== 'object') throw new Error('invalid house.json; refusing to guess houses');
@@ -157,7 +163,7 @@ export function proveMergedPr(repo: string, branch: string, tip: string, gh: (ar
157
163
  : { status: parsed.length >= MERGED_PR_QUERY_LIMIT ? 'unknown' : 'not-merged' };
158
164
  }
159
165
 
160
- // Every tip checked by done is tied to its own branch, including a different attached branch.
166
+ // Attached tips use their branch; a detached HEAD is checked separately from the registered work tip.
161
167
  function landedTip(repo: string, branch: string, tip: string, gh: (args: string[]) => GhCliResult): 'ancestor' | 'pr-merged' | 'tree-equal' | null {
162
168
  if (mergedIntoMain(repo, tip)) return 'ancestor';
163
169
  if (proveMergedPr(repo, branch, tip, gh, 'main').status === 'pr-merged') return 'pr-merged';
@@ -170,8 +176,8 @@ function judgeLandedTip(repo: string, branch: string, tip: string, gh: (args: st
170
176
  return rule !== null;
171
177
  }
172
178
 
173
- // Squash changes the commit identity. Prove the original commit changed at least one path and
174
- // that every one of those paths has the same content and mode in the fetched main tree.
179
+ // Squash changes commit identity. Check the changed paths together against a fetched main
180
+ // snapshot, not merely the latest tree: a later main commit may have edited those paths again.
175
181
  function treeEqualToMain(repo: string, tip: string): boolean {
176
182
  const main = 'refs/remotes/origin/main';
177
183
  const base = git(repo, 'merge-base', tip, main);
@@ -179,9 +185,15 @@ function treeEqualToMain(repo: string, tip: string): boolean {
179
185
  if (changed.status !== 0) throw new Error(`cannot verify changed files: ${changed.stderr.trim() || `exit ${changed.status}`}`);
180
186
  const files = changed.stdout.split('\0').filter(Boolean);
181
187
  if (files.length === 0) return false;
182
- const equal = runGitCommand(repo, ['diff', '--quiet', tip, main, '--', ...files], { encoding: 'utf8' });
183
- if (equal.status !== 0 && equal.status !== 1) throw new Error(`cannot verify tree equality: ${equal.stderr.trim() || `exit ${equal.status}`}`);
184
- return equal.status === 0;
188
+ const equalAt = (commit: string): boolean => {
189
+ const equal = runGitCommand(repo, ['diff', '--quiet', tip, commit, '--', ...files], { encoding: 'utf8' });
190
+ if (equal.status !== 0 && equal.status !== 1) throw new Error(`cannot verify tree equality: ${equal.stderr.trim() || `exit ${equal.status}`}`);
191
+ return equal.status === 0;
192
+ };
193
+ if (equalAt(main)) return true;
194
+ // Only first-parent main snapshots can establish a previous landing; a side branch alone cannot.
195
+ const history = git(repo, 'log', '--first-parent', '--format=%H', `${base}..${main}`, '--', ...files);
196
+ return history.split('\n').filter(Boolean).some(equalAt);
185
197
  }
186
198
  // Git lock files are created exclusively; ours carry our pid. A lock left by a killed `work done` is never
187
199
  // removed automatically (between our check and a delete, a live git could have re-taken it): the error names it.
@@ -398,49 +410,53 @@ export function hqWork(action: 'new' | 'done', value: string, nameValue: string,
398
410
  // Resume: an interrupted done left the work detached at its recorded merged tip, with the branch either
399
411
  // still at that tip (stopped before the delete) or already deleted (stopped before the removal).
400
412
  const branchPresent = branchTip.status === 0;
401
- const resuming = attached.status !== 0 && entry.reclaiming !== undefined
402
- && git(path, 'rev-parse', 'HEAD') === (entry.checkoutTip ?? entry.reclaiming)
413
+ const head = git(path, 'rev-parse', 'HEAD');
414
+ const resuming = attached.status === 1 && entry.reclaiming !== undefined
415
+ && head === (entry.checkoutTip ?? entry.reclaiming)
403
416
  && (!branchPresent || branchTip.stdout.trim() === entry.reclaiming);
404
- if (!resuming && (attached.status !== 0 || !branchPresent)) throw new Error(`work branch mismatch: ${path}`);
417
+ if (!resuming && (attached.status !== 0 && attached.status !== 1 || !branchPresent)) throw new Error(`work branch mismatch: ${path}`);
405
418
  const ownTip = resuming ? entry.reclaiming! : branchTip.stdout.trim();
406
- const checkoutBranch = resuming ? entry.checkoutBranch ?? branch : attached.stdout.trim();
419
+ const detached = !resuming && attached.status === 1;
420
+ const detachedCheckout = detached || (resuming && entry.checkoutBranch === branch && entry.checkoutTip !== undefined && entry.checkoutTip !== ownTip);
421
+ const checkoutBranch = resuming ? entry.checkoutBranch ?? branch : detached ? branch : attached.stdout.trim();
407
422
  const checkoutRef = `refs/heads/${checkoutBranch}`;
408
423
  const differentBranch = checkoutBranch !== branch;
409
- const tip = differentBranch
410
- ? (resuming ? entry.checkoutTip! : git(repo, 'rev-parse', '--verify', checkoutRef))
411
- : ownTip;
412
- if (!resuming && differentBranch && git(path, 'rev-parse', 'HEAD') !== tip) throw new Error(`work branch moved before the merge check: ${checkoutBranch}`);
424
+ const tip = detached ? head : resuming && entry.checkoutTip ? entry.checkoutTip : differentBranch
425
+ ? git(repo, 'rev-parse', '--verify', checkoutRef) : ownTip;
426
+ if (!resuming && !detached && differentBranch && head !== tip) throw new Error(`work branch moved before the merge check: ${checkoutBranch}`);
413
427
  // The ancestor check is against origin/main, not bare HEAD (which may still point to master).
414
428
  if (!judgeLandedTip(repo, checkoutBranch, tip, options.gh ?? runGhCliWithResult)) throw new Error(`work is not merged into origin/main: ${checkoutBranch}`);
415
- if (differentBranch && !judgeLandedTip(repo, branch, ownTip, options.gh ?? runGhCliWithResult)) throw new Error(`work is not merged into origin/main: ${branch}`);
429
+ if ((differentBranch || detachedCheckout) && tip !== ownTip && !judgeLandedTip(repo, branch, ownTip, options.gh ?? runGhCliWithResult)) throw new Error(`work is not merged into origin/main: ${branch}`);
416
430
  if (differentBranch && !resuming) {
417
431
  const current = runGitCommand(repo, ['rev-parse', '--verify', checkoutRef], { encoding: 'utf8' });
418
432
  if (current.status !== 0 || current.stdout.trim() !== tip || git(path, 'rev-parse', 'HEAD') !== tip) throw new Error(`work branch moved before removal: ${checkoutBranch}`);
419
433
  }
420
434
  // Removal order keeps every interruption resumable: record the proven checkout tip and original branch tip
421
435
  // → detach → hold the work's git locks → compare-and-delete the original branch → remove the worktree
422
- // → drop the registration last. A different attached branch is proven but its ref is never deleted.
436
+ // → drop the registration last. A different attached branch is proven but its ref is never deleted;
437
+ // a pre-existing detached HEAD is checked independently and never reattached on a failed removal.
423
438
  // Holding this worktree's HEAD.lock and index.lock makes git refuse every commit and `add` in it until the
424
439
  // removal ends (`worktree remove` itself does not need them), so no commit can land between the last
425
440
  // check and the removal. The locks live in the worktree's admin dir, which the removal deletes.
426
441
  updateLedgerLocked(root, ledger => { ledger.sandboxes[path] = { ...entry, reclaiming: ownTip,
427
- ...(differentBranch ? { checkoutTip: tip, checkoutBranch } : {}) }; });
442
+ ...(differentBranch || (detachedCheckout && tip !== ownTip) ? { checkoutTip: tip, checkoutBranch } : {}) }; });
428
443
  const admin = git(path, 'rev-parse', '--absolute-git-dir');
429
444
  const held: string[] = [];
430
445
  const release = (): void => { for (const lock of held.splice(0)) rmSync(lock, { force: true }); };
431
446
  const restore = (): string => {
432
447
  release();
433
448
  const head = runGitCommand(path, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8' });
434
- runGitCommand(repo, ['update-ref', ref, differentBranch ? ownTip : head.status === 0 ? head.stdout.trim() : tip, ''], { encoding: 'utf8' });
435
- const reattached = runGitCommand(path, ['symbolic-ref', 'HEAD', checkoutRef], { encoding: 'utf8' }).status === 0;
436
- // If HEAD could not be re-attached (e.g. a stale HEAD.lock), keep `reclaiming` so the next done resumes.
449
+ runGitCommand(repo, ['update-ref', ref, differentBranch || detachedCheckout ? ownTip : head.status === 0 ? head.stdout.trim() : tip, ''], { encoding: 'utf8' });
450
+ const reattached = detachedCheckout ? head.status === 0 && head.stdout.trim() === tip
451
+ : runGitCommand(path, ['symbolic-ref', 'HEAD', checkoutRef], { encoding: 'utf8' }).status === 0;
452
+ // If HEAD could not be restored (e.g. a stale HEAD.lock), keep `reclaiming` so the next done resumes.
437
453
  const restored = reattached ? { kind: entry.kind, path: entry.path, owner: entry.owner } : { ...entry, reclaiming: ownTip,
438
- ...(differentBranch ? { checkoutTip: tip, checkoutBranch } : {}) };
454
+ ...(differentBranch || (detachedCheckout && tip !== ownTip) ? { checkoutTip: tip, checkoutBranch } : {}) };
439
455
  try { updateLedgerLocked(root, ledger => { ledger.sandboxes[path] = restored; }); return reattached ? 'registration restored' : 'left resumable'; }
440
456
  catch (error) { return `REGISTRATION NOT RESTORED (fix ${path} in house.json by hand): ${error instanceof Error ? error.message : String(error)}`; }
441
457
  };
442
458
  const fail = (message: string): never => { throw new Error(`${message}; nothing removed: ${path} · ${restore()}`); };
443
- if (!resuming) git(path, 'checkout', '--quiet', '--detach');
459
+ if (!resuming && !detached) git(path, 'checkout', '--quiet', '--detach');
444
460
  for (const name of ['HEAD.lock', 'index.lock']) {
445
461
  const lock = join(admin, name);
446
462
  try { takeLock(lock); held.push(lock); }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * IMAGE-LEDGER-TAGS (RFC-adaptive-graph-ops §9.11) — first slice: the image ledger.
3
+ *
4
+ * The registry tag itself carries only the 12-char commit (docker/harness/build.sh:276), so the
5
+ * identity of an image (why it exists, how long to keep it) lives in a small append-only ledger
6
+ * next to the config that pins it (`<config dir>/image-ledger.jsonl`). Writers append events;
7
+ * readers fold them per tag. A registry tag with no ledger row is `transient` (RFC §9.11).
8
+ *
9
+ * Writers in this slice: the baseline pin (`config set harness.imageCommit <commit>` — the rail
10
+ * pins through that command). Launch · gate · release writers and `image tag` are later slices.
11
+ *
12
+ * Unreadable is never "none": a ledger line that fails to parse is counted, and a registry that
13
+ * cannot be read is reported as `null` (not as an empty tag list).
14
+ */
15
+ import { spawnSync } from 'node:child_process';
16
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
17
+ import { hostname } from 'node:os';
18
+ import { dirname, join } from 'node:path';
19
+ import { getElanousConfigDir } from '../elanous-config-dir.js';
20
+
21
+ export const IMAGE_ROLES = ['baseline', 'release', 'gate', 'demo', 'candidate', 'transient'] as const;
22
+ export type ImageRole = typeof IMAGE_ROLES[number];
23
+ export const IMAGE_KEEPS = ['pin', 'long', 'short', 'gc'] as const;
24
+ export type ImageKeep = typeof IMAGE_KEEPS[number];
25
+ export const IMAGE_ORIGIN_REASONS = ['rail', 'launch', 'gate', 'release', 'manual', 'config-set'] as const;
26
+ export type ImageOriginReason = typeof IMAGE_ORIGIN_REASONS[number];
27
+
28
+ export interface ImageLedgerEvent {
29
+ readonly v: 1;
30
+ readonly ts: string;
31
+ /** Registry tag = first 12 hex chars of the commit (build.sh:276). */
32
+ readonly tag: string;
33
+ readonly commit: string;
34
+ readonly role: ImageRole;
35
+ readonly keep: ImageKeep;
36
+ readonly origin: { readonly host: string; readonly reason: ImageOriginReason };
37
+ }
38
+
39
+ export interface ImageLedgerRead {
40
+ readonly path: string;
41
+ readonly exists: boolean;
42
+ readonly events: readonly ImageLedgerEvent[];
43
+ /** Lines that were present but did not parse as a ledger event. */
44
+ readonly unreadableLines: number;
45
+ }
46
+
47
+ export interface ImageLedgerRow {
48
+ readonly tag: string;
49
+ readonly commit: string;
50
+ readonly role: ImageRole;
51
+ readonly keep: ImageKeep;
52
+ /** Origin of the first event for this tag · null for a registry-only (unledgered) tag — its origin is unknown. */
53
+ readonly origin: { readonly host: string; readonly reason: ImageOriginReason } | null;
54
+ readonly firstTs: string;
55
+ readonly lastTs: string;
56
+ /** For `baseline`: true only for the most recently pinned baseline. */
57
+ readonly current: boolean;
58
+ /** true when the tag is on the registry · false when not · null when the registry was not read. */
59
+ readonly inRegistry: boolean | null;
60
+ /** true when the row comes only from the registry (no ledger event) — always role `transient`. */
61
+ readonly unledgered: boolean;
62
+ }
63
+
64
+ const HEX = /^[0-9a-f]{12,40}$/u;
65
+
66
+ export function imageLedgerPath(configDir: string = getElanousConfigDir()): string {
67
+ return join(configDir, 'image-ledger.jsonl');
68
+ }
69
+
70
+ export function imageTag(commit: string): string {
71
+ return commit.trim().toLowerCase().slice(0, 12);
72
+ }
73
+
74
+ function isEvent(value: unknown): value is ImageLedgerEvent {
75
+ if (!value || typeof value !== 'object') return false;
76
+ const e = value as Record<string, unknown>;
77
+ const origin = e.origin as Record<string, unknown> | undefined;
78
+ return e.v === 1 && typeof e.ts === 'string' && typeof e.tag === 'string' && /^[0-9a-f]{12}$/u.test(e.tag)
79
+ && typeof e.commit === 'string' && HEX.test(e.commit)
80
+ && (IMAGE_ROLES as readonly unknown[]).includes(e.role) && (IMAGE_KEEPS as readonly unknown[]).includes(e.keep)
81
+ && !!origin && typeof origin.host === 'string' && (IMAGE_ORIGIN_REASONS as readonly unknown[]).includes(origin.reason);
82
+ }
83
+
84
+ export function readImageLedger(path: string = imageLedgerPath()): ImageLedgerRead {
85
+ if (!existsSync(path)) return { path, exists: false, events: [], unreadableLines: 0 };
86
+ const events: ImageLedgerEvent[] = [];
87
+ let unreadableLines = 0;
88
+ for (const line of readFileSync(path, 'utf8').split('\n')) {
89
+ if (line.trim() === '') continue;
90
+ try {
91
+ const parsed: unknown = JSON.parse(line);
92
+ if (isEvent(parsed)) events.push(parsed); else unreadableLines += 1;
93
+ } catch { unreadableLines += 1; }
94
+ }
95
+ return { path, exists: true, events, unreadableLines };
96
+ }
97
+
98
+ export function appendImageLedgerEvent(event: ImageLedgerEvent, path: string = imageLedgerPath()): void {
99
+ if (!isEvent(event)) throw new Error(`invalid image ledger event: ${JSON.stringify(event)}`);
100
+ mkdirSync(dirname(path), { recursive: true });
101
+ appendFileSync(path, `${JSON.stringify(event)}\n`, { mode: 0o600 });
102
+ }
103
+
104
+ /** The baseline pin event — what `config set harness.imageCommit <commit>` records. */
105
+ export function baselinePinEvent(commit: string, deps: { now?: () => Date; host?: string } = {}): ImageLedgerEvent {
106
+ const c = commit.trim().toLowerCase();
107
+ if (!HEX.test(c)) throw new Error(`not a commit (hex 12~40): ${commit}`);
108
+ return {
109
+ v: 1,
110
+ ts: (deps.now?.() ?? new Date()).toISOString(),
111
+ tag: imageTag(c),
112
+ commit: c,
113
+ role: 'baseline',
114
+ keep: 'pin',
115
+ origin: { host: deps.host ?? hostname(), reason: 'config-set' },
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Fold events per tag (the last event decides role and keep), then add registry tags that have no
121
+ * ledger event as `transient`. `registryTags === null` means the registry was not read.
122
+ * Non-commit registry tags (`latest`) are moving aliases and are not rows.
123
+ */
124
+ export function foldImageLedger(events: readonly ImageLedgerEvent[], registryTags: readonly string[] | null): ImageLedgerRow[] {
125
+ const byTag = new Map<string, { first: ImageLedgerEvent; last: ImageLedgerEvent }>();
126
+ // Events for one tag may carry the 12-char tag or the full 40-char commit; the row keeps the longest one seen.
127
+ const fullCommit = new Map<string, string>();
128
+ for (const e of events) {
129
+ const seen = byTag.get(e.tag);
130
+ if (!seen) byTag.set(e.tag, { first: e, last: e });
131
+ else if (e.ts >= seen.last.ts) seen.last = e;
132
+ if (e.commit.length > (fullCommit.get(e.tag)?.length ?? 0)) fullCommit.set(e.tag, e.commit);
133
+ }
134
+ let currentBaseline: string | null = null;
135
+ let currentTs = '';
136
+ for (const e of events) if (e.role === 'baseline' && e.ts >= currentTs) { currentBaseline = e.tag; currentTs = e.ts; }
137
+ const registry = registryTags === null ? null : new Set(registryTags.filter((t) => /^[0-9a-f]{12}$/u.test(t)));
138
+ const rows: ImageLedgerRow[] = [...byTag.values()].map(({ first, last }) => ({
139
+ tag: last.tag,
140
+ commit: fullCommit.get(last.tag) ?? last.commit,
141
+ role: last.role,
142
+ keep: last.keep,
143
+ origin: first.origin,
144
+ firstTs: first.ts,
145
+ lastTs: last.ts,
146
+ current: last.role === 'baseline' && last.tag === currentBaseline,
147
+ inRegistry: registry === null ? null : registry.has(last.tag),
148
+ unledgered: false,
149
+ }));
150
+ if (registry) for (const tag of [...registry].sort()) {
151
+ if (byTag.has(tag)) continue;
152
+ rows.push({ tag, commit: tag, role: 'transient', keep: 'short', origin: null, firstTs: '', lastTs: '', current: false, inRegistry: true, unledgered: true });
153
+ }
154
+ return rows.sort((a, b) => (b.lastTs || '').localeCompare(a.lastTs || '') || a.tag.localeCompare(b.tag));
155
+ }
156
+
157
+ export type RegistryRead = { readonly ok: true; readonly tags: readonly string[] } | { readonly ok: false; readonly reason: string };
158
+
159
+ /**
160
+ * Read `elanous-harness` tags from the registry the way the rail does (`/v2/elanous-harness/tags/list`
161
+ * on the registry node's localhost port). `sshHost` runs it on that node; otherwise on this machine.
162
+ */
163
+ export function readRegistryTags(opts: { port: string; sshHost?: string; run?: (cmd: string, args: readonly string[]) => { status: number | null; stdout: string; stderr?: string } }): RegistryRead {
164
+ const url = `http://localhost:${opts.port}/v2/elanous-harness/tags/list`;
165
+ const run = opts.run ?? ((cmd, args) => {
166
+ const r = spawnSync(cmd, [...args], { encoding: 'utf8', timeout: 25_000 });
167
+ return { status: r.status, stdout: r.stdout ?? '', stderr: r.stderr ?? (r.error ? String(r.error) : '') };
168
+ });
169
+ const r = opts.sshHost
170
+ ? run('ssh', ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=10', opts.sshHost, `curl -fsS --max-time 15 ${url}`])
171
+ : run('curl', ['-fsS', '--max-time', '15', url]);
172
+ if (r.status !== 0) return { ok: false, reason: `registry read failed (exit ${r.status ?? 'timeout'})${r.stderr ? `: ${r.stderr.trim().slice(0, 200)}` : ''}` };
173
+ try {
174
+ const tags = (JSON.parse(r.stdout) as { tags?: unknown }).tags;
175
+ if (tags === null) return { ok: true, tags: [] };
176
+ if (!Array.isArray(tags) || !tags.every((t) => typeof t === 'string')) return { ok: false, reason: 'registry answered without a tags list' };
177
+ return { ok: true, tags };
178
+ } catch { return { ok: false, reason: 'registry answered non-JSON' }; }
179
+ }