elanous 0.2.36 → 0.2.37

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 (630) 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/{uC-N0emedeNOFuaBQFv-I → 8kITumG9GAZ1LwT9POrAR}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1041-5841aae848fb6403.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1087.3177e54f672dc526.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1105.efe1e13503ac7a33.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1224.2ceca6c180d5130b.js +62 -0
  8. package/apps/pwa/out/_next/static/chunks/{4286.b70c4520306817e8.js → 1262.a89bb27a5b2f844e.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/1417-1768233ac92f8a5d.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/1612.81bbc19af06af16c.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/{3410.7f9af5ab77cbe65f.js → 1746.3563617ea64023a3.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/1766.728d42053070bb2d.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1773.d38e945481e04125.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1974-c12f9600da3d3c26.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1ed5e8af.cf424815c7c0efad.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/{9250-072df22eaadaa6b5.js → 2002-a43a3f380df873ec.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/2061.7232dc9dc058417f.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/210.90b2cbd64926cf34.js +32 -0
  19. package/apps/pwa/out/_next/static/chunks/{3727.7949ccaacaa71242.js → 2283.edd9627b7bdfb546.js} +2 -2
  20. package/apps/pwa/out/_next/static/chunks/2318-450e75d07605b8fe.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/{4615.8525eb0add35eba6.js → 2375.bf364c85dbb6f085.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/244-a810d282a20f1783.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/2479-fe7e188bd9402d14.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2514.03b60f266531a015.js +148 -0
  25. package/apps/pwa/out/_next/static/chunks/2526-a657538869888604.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2533-6f0341ff075809e9.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2676.e7994c695ef0f30d.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2734.3ffdf54254fb6f99.js +27 -0
  29. package/apps/pwa/out/_next/static/chunks/2770-23252bf3a37880b9.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2969.66b8b545139d1544.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/3128.a9210d08800ea6a8.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/{5711.957b095705c12560.js → 3199.6a1a9b0e6cd01dc6.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/3212.12179fd3eee8cef1.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/{2986.45d7652e4f13557e.js → 3578.4e9f9948a9a6f2f4.js} +2 -2
  35. package/apps/pwa/out/_next/static/chunks/3760-2909ddb9279281cd.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3825.1921b5901bf33dce.js +36 -0
  37. package/apps/pwa/out/_next/static/chunks/3994-63eb884ed0547011.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/4036.d54e55e1ebf5acdc.js +90 -0
  39. package/apps/pwa/out/_next/static/chunks/4115.5b438b99f0ae729f.js +226 -0
  40. package/apps/pwa/out/_next/static/chunks/4269.d8f9f34bb0fc6c43.js +287 -0
  41. package/apps/pwa/out/_next/static/chunks/431-4eed5bd2c5fe1719.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/{7481.39f7a4654907bf6e.js → 4313.76f232c304a15bde.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/4324.c99ba3e83eb1b691.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4337e36d.f42c3b391b3d8f20.js +122 -0
  45. package/apps/pwa/out/_next/static/chunks/{6943.c6b24124853a47b2.js → 4383.18e5c54db3a22c88.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4599-96eff3f2a9c242c1.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{3757-180308bc61021b05.js → 4725-34bed25720cc326b.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/4825-a9d9d4e9f5892309.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4833-7934c592c5569f00.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4879-68518f547a207aff.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/4896.f9b70285363b27b2.js +24 -0
  52. package/apps/pwa/out/_next/static/chunks/{771.c3cdb6c1f7f47c02.js → 4931.95cc6dbd230774ca.js} +3 -3
  53. package/apps/pwa/out/_next/static/chunks/4937-28d437999b77c9dd.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/49aa54ab.80f39398e0ed0f30.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/{552.b7d061ad91eeac51.js → 5046.7fd5cbd859299448.js} +2 -2
  56. package/apps/pwa/out/_next/static/chunks/5056.482687319b82638e.js +104 -0
  57. package/apps/pwa/out/_next/static/chunks/506.16b83d56a437a9ee.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5260-20593b0a25d0afbc.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5404.cc4e9fec26fd5925.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/576.485e83e3f3dec19a.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5832-a6be14e663f65726.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/5854-56648a9fe646b9a0.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/{9f31e478.fefcbcd3c388ebc5.js → 58edce7d.f1b0c72acf13b123.js} +1 -1
  64. package/apps/pwa/out/_next/static/chunks/5925.3b1f8b135710249b.js +93 -0
  65. package/apps/pwa/out/_next/static/chunks/{3136.30aaaa5fa03662a3.js → 5952.294fc574a8625184.js} +1 -1
  66. package/apps/pwa/out/_next/static/chunks/{da5ecc5c-b005ad8cac737d57.js → 5f6af561-f90b7a67f4f75d13.js} +1 -1
  67. package/apps/pwa/out/_next/static/chunks/6168.17749aa3905fa073.js +74 -0
  68. package/apps/pwa/out/_next/static/chunks/{3324.a09703033abcbd2f.js → 6252.240578f10952e091.js} +1 -1
  69. package/apps/pwa/out/_next/static/chunks/6416.5d50b8ec9986ca98.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/{8904.7d51af2dc033965c.js → 6680.6a693c28aae4cd19.js} +2 -2
  71. package/apps/pwa/out/_next/static/chunks/{275-02ea4c2a33980422.js → 6745-5a63492933959dd9.js} +3 -3
  72. package/apps/pwa/out/_next/static/chunks/6758.e7fbf532953c0783.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/6795.0cb8a59730d99703.js +149 -0
  74. package/apps/pwa/out/_next/static/chunks/{528.38ce20ef8aa251ff.js → 6819.be5c9c4d67f22922.js} +3 -3
  75. package/apps/pwa/out/_next/static/chunks/6877-84d47b7e85d0c41e.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6971-fb5f9dc0a2cb5d2d.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/{3a3d9966.cc4d4585a2708e7c.js → 709a918c.4fd3b4e2a32cb4e3.js} +1 -1
  78. package/apps/pwa/out/_next/static/chunks/717.522b08685979b814.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/7191.e41eb8f455c7f089.js +41 -0
  80. package/apps/pwa/out/_next/static/chunks/7233.8e744b7513119409.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7238-97c2226f80504bd8.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7285.307963afcf7e7660.js +24 -0
  83. package/apps/pwa/out/_next/static/chunks/7299-fbe4b8c47a87614a.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7314.df0d95930b0c3f50.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7406.c0589fdf2ee66629.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/{4304.e9b2512834a9e23e.js → 7424.c0fe2a595d4232b5.js} +1 -1
  87. package/apps/pwa/out/_next/static/chunks/752-90e3e7daad3d8e39.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7524-bcd3d9928c6842d9.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7549.9a4de3bf231cfc52.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/757-4fbaf50942b8c424.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/765.76ae970ceb597b5b.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/{2683.c9773a0c589b031b.js → 7867.e8725745ddd1d940.js} +4 -4
  93. package/apps/pwa/out/_next/static/chunks/{6466-92e65a48490b68a9.js → 7893-e444a1b0809cd1e4.js} +1 -1
  94. package/apps/pwa/out/_next/static/chunks/7956-33748cabb3a05562.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/{ccae1c4b-0cf414f5eff1b10d.js → 7c5ed21f-f286708e57cf88f7.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/8030-aa66b478b53dbfca.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/8051.c88c07dacbbc5f21.js +8 -0
  98. package/apps/pwa/out/_next/static/chunks/{3840-5825d56d46976a92.js → 8080-40d781e7704d3671.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/8149.80e402d4345e8dae.js +77 -0
  100. package/apps/pwa/out/_next/static/chunks/8205.50a5268f4e12ba0b.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8231.a128feaa6f8b6079.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/85.4ddb1dda2ea3ffa2.js +286 -0
  103. package/apps/pwa/out/_next/static/chunks/8537.26517a09e8247beb.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8595-e9b7081ab773a61f.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8821-30cc65a61a60951f.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8905.b3385aadb7b133e6.js +79 -0
  107. package/apps/pwa/out/_next/static/chunks/8983.246bb3a1ac38f50f.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/{873d6c12.09bd499f57171543.js → 8c58d09b.e8724a299658d86f.js} +1 -1
  109. package/apps/pwa/out/_next/static/chunks/9072.18342f5941a1f3ec.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/9123.9df04866b2d6e00f.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/{575.d929793a53f27c4b.js → 9151.a2c51e93f9156dae.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/9175-7d383df15a82e2be.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9181-bf89c07ce5b8f599.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/920-429bbc4504b45f71.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9207.625edf45d656151c.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9378.d2575c6ffd6dd756.js +94 -0
  117. package/apps/pwa/out/_next/static/chunks/957.db5b5c4d32a35240.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{190.285c832bd9626f8c.js → 958.53a8dcf4729e6997.js} +1 -1
  119. package/apps/pwa/out/_next/static/chunks/9647.8f0cd0898c592a29.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9665-26abb02665704c2c.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9703.e7d81b6faef1050a.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9882-7a9164b3c2dacbe5.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/9998.b74e033efadecd10.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c604fb980a634d88.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ec2d8a4979ca9075.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cb6add08ad3c7660.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/board/page-e2eaa321ec223fe6.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5b8a09c475b5f844.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/bots/page-91a77daedfbac191.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/ceo/page-f37fc5e974bda39b.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/chat/page-91429a0b78415a94.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/consult/page-b95798b58263b910.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/control/page-763d53e175e17aa1.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/decisions/page-d55023204055b12f.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/design-check/page-89d2140d78abc810.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/editor/page-82dc069abfe27f55.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/exec/page-25d9eee7beb552c0.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/field/page-770b4594c713ee97.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c604fb980a634d88.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/inside/page-ba8e0dcae4f83985.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/intake/page-5c112c6d7c33656e.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/layout-09788e1aa9a87db6.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/live/{page-e2de2e59111c9aad.js → page-3337c6a1328c2674.js} +1 -1
  144. package/apps/pwa/out/_next/static/chunks/app/live-run/{page-f2b2e4bf6e981450.js → page-89acd5d68c0bc560.js} +1 -1
  145. package/apps/pwa/out/_next/static/chunks/app/loops/page-5c13e2c76371d797.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/market/page-653a9f18642825c6.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-2af16e62c12f22df.js → page-fe986fe79b107546.js} +1 -1
  148. package/apps/pwa/out/_next/static/chunks/app/morning/page-1e5da297557d2354.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/not-found-91ea9f5869a40943.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/observatory/page-7824e21b999962e2.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-53d00a60cc75a922.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-88a1edbff62eaf1d.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e931248b3f9574b.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/outputs/page-443364c9e4a23b31.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/page-b9af2ac1c4737ce8.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/reflection/page-25ab9c614c14129b.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-dbcdcfac0a395a8a.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/sessions/page-bf1de6645089ec58.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c4201761a5e839c9.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/settings/page-c304f469ea5d70e4.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8e5a238cf5e50c9c.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c604fb980a634d88.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/page-32b526069ee50b3c.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/share/page-fb9e639c107f5f72.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/showroom/page-84220c776608cedc.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/tasks/page-e475618cbfd5ca2b.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/term/page-ac2c1e6169cfce57.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/today/page-fccd3020af2f8806.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/trace/page-9c0d7198ff7b5a91.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/vault/page-d54b1ec9b9c6430d.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8c4bef1791d732ca.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workflows/page-46fcc0b0bbc6fbfe.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c1c80e345fa9d6f5.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7dfda00afd172f5b.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/{053a429e.9a63436527cc751c.js → d9815f84.4918f682a45a8502.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/{ceba0382.650712b56b85b582.js → de8446d3.31b73eb239fecd7b.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/f21fd540.63ac75328926ed30.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/framework-77fdcc873519a6cd.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-app-d0d620d2ce235713.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-e0ef79c79337ba90.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-84b1bc71e3f9ce6a.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-2acb61f1363128cb.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-462ff5727ceb7824.js +1 -0
  184. package/apps/pwa/out/_next/static/css/{b5d88eba78a3d7bf.css → be9019f696217521.css} +1 -1
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +23 -23
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +23 -23
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +23 -23
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +22 -22
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +22 -22
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +23 -23
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +22 -22
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +23 -23
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +23 -23
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +22 -22
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +23 -23
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +23 -23
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +23 -23
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +23 -23
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +22 -22
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +22 -22
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +23 -23
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +23 -23
  222. package/apps/pwa/out/live-run/index.html +1 -1
  223. package/apps/pwa/out/live-run/index.txt +23 -23
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +22 -22
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +22 -22
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +23 -23
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +23 -23
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +22 -22
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +22 -22
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +22 -22
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +23 -23
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +23 -23
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +22 -22
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +23 -23
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +23 -23
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +23 -23
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +23 -23
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +23 -23
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +22 -22
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +23 -23
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +23 -23
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +23 -23
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +23 -23
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +23 -23
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +23 -23
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +23 -23
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +23 -23
  278. package/graphs/drafts/recipes.yaml +27 -7
  279. package/graphs/work/draft-close.yaml +54 -0
  280. package/graphs/work/green-close.yaml +37 -0
  281. package/graphs/work/recipes.yaml +46 -1
  282. package/graphs/work/run-research.yaml +35 -0
  283. package/package.json +1 -1
  284. package/scripts/__fixtures__/authoring-paths-10x3.json +32 -0
  285. package/scripts/boundary-proof-mapping-audit.ts +88 -0
  286. package/scripts/codex-fallback-live-probe.ts +17 -15
  287. package/scripts/fleet/node-setup.sh +32 -2
  288. package/scripts/fleet/run-light-worker.sh +9 -0
  289. package/scripts/gate-0226-failure-census.ts +507 -0
  290. package/scripts/goals/authoring-metrics.ts +258 -0
  291. package/scripts/graph-units/draft-close-node.ts +222 -0
  292. package/scripts/hook-order-sweep.ts +7 -4
  293. package/scripts/land-global-checks.ts +69 -1
  294. package/scripts/measure-nl-routing-corpus.ts +207 -2
  295. package/scripts/release-loop/docs-node.ts +140 -0
  296. package/scripts/release-loop/gate-closure.ts +48 -0
  297. package/scripts/release-loop/gate-diff.ts +2 -1
  298. package/scripts/release-loop/known-issues-node.ts +6 -4
  299. package/scripts/release-loop/notes-repair.ts +216 -0
  300. package/scripts/release-loop/prepare-node.ts +61 -5
  301. package/scripts/release-loop/resume-release.ts +114 -4
  302. package/scripts/release-loop/resume-test-traffic.ts +10 -0
  303. package/scripts/release-loop/version-node.ts +81 -12
  304. package/scripts/train/green-leg.ts +176 -0
  305. package/scripts/tui-continue-real-evidence.ts +47 -0
  306. package/scripts/ux-sim.ts +1 -1
  307. package/src/acp/client.ts +31 -5
  308. package/src/acp/core-turn-bridge.ts +6 -0
  309. package/src/acp/stderr-summary.ts +49 -0
  310. package/src/agent/monad-agent-turn.ts +1 -0
  311. package/src/agent/terminal-surface.ts +217 -0
  312. package/src/agent-substrate/pr-reviewer.ts +11 -3
  313. package/src/agent-substrate/self-review-cli.ts +343 -31
  314. package/src/autopilot/lineage/sources.ts +1 -1
  315. package/src/autopilot/pr-manager.ts +64 -13
  316. package/src/boot/daemon-runtime.ts +35 -3
  317. package/src/boot/daemon-tools/pty-shell.ts +2 -1
  318. package/src/budget/run-usage-rollup.ts +12 -7
  319. package/src/budget/unified-usage.ts +20 -0
  320. package/src/capture/tui-self-report.ts +32 -3
  321. package/src/cli/autopilot-cli.ts +24 -9
  322. package/src/cli/decisions-cli.ts +7 -4
  323. package/src/cli/doctor-cli.ts +116 -7
  324. package/src/cli/logs-cli.ts +357 -2
  325. package/src/cli/memory-cli.ts +30 -1
  326. package/src/cli/msg-cli.ts +15 -11
  327. package/src/cli/pr-granularity.ts +5 -3
  328. package/src/cli/release-checklist-read-cli.ts +109 -4
  329. package/src/cli/release-cli.ts +43 -10
  330. package/src/cli/self-update.ts +23 -5
  331. package/src/cli/self-utterances-cli.ts +13 -16
  332. package/src/cli/tasks-cli.ts +309 -11
  333. package/src/cli/usage-cli.ts +12 -10
  334. package/src/context-bus/claude-memory-import.ts +99 -0
  335. package/src/context-bus/long-term-memory.ts +125 -0
  336. package/src/core-turn/run-goal-loop.ts +5 -2
  337. package/src/core-turn/types.ts +2 -0
  338. package/src/dashboard/chat/acp-chat.ts +9 -0
  339. package/src/dashboard/index.ts +97 -13
  340. package/src/dashboard/input/agent-wake.ts +43 -0
  341. package/src/dashboard/input/chat-main-acp-dispatch.ts +21 -1
  342. package/src/dashboard/replay-preview.ts +25 -0
  343. package/src/dashboard/slash-runtime/dashboard-handlers.ts +17 -8
  344. package/src/dashboard/synchronized-frame-output.ts +74 -0
  345. package/src/display/coordinator.ts +45 -12
  346. package/src/display/resize-listener.ts +41 -20
  347. package/src/display/synchronized-output-region.ts +60 -0
  348. package/src/domains/surface-events.ts +80 -1
  349. package/src/exec-requests/planner.ts +37 -2
  350. package/src/execution-loop/launch-gate.ts +4 -3
  351. package/src/git-fs/cli-stderr.ts +45 -0
  352. package/src/git-fs/gh-cli.ts +6 -3
  353. package/src/git-fs/git-cli.ts +11 -5
  354. package/src/graph-runner/graph-cli.ts +26 -2
  355. package/src/graph-runner/graph-run-api.ts +3 -2
  356. package/src/graph-runner/runner.ts +17 -5
  357. package/src/graph-wizard/generate.ts +29 -6
  358. package/src/harness/harness-cli-command.ts +179 -33
  359. package/src/harness/review-adapter.ts +26 -6
  360. package/src/index.ts +116 -13
  361. package/src/instance/test-write-guard-real-transport.fixture.ts +52 -0
  362. package/src/llm/route-decision.ts +13 -0
  363. package/src/llm-tool-name.ts +37 -0
  364. package/src/llm-tool-schema.ts +35 -0
  365. package/src/llm.ts +123 -11
  366. package/src/loops/checker.ts +100 -0
  367. package/src/loops/consensus.ts +26 -2
  368. package/src/loops/loop-cli.ts +9 -4
  369. package/src/loops/orchestrator/anomaly-cluster.ts +243 -0
  370. package/src/loops/orchestrator/tick.ts +49 -4
  371. package/src/model-tier/llm-tier-map.ts +12 -17
  372. package/src/msg/msg-store.ts +17 -0
  373. package/src/native-tool-catalog.ts +1 -1
  374. package/src/nexus/api/graph-wizard-api.ts +27 -2
  375. package/src/nexus/api/grid.ts +24 -4
  376. package/src/nexus/api/http-server.ts +1 -1
  377. package/src/nexus/api/ops-api.ts +14 -6
  378. package/src/nexus/state/state.ts +7 -1
  379. package/src/oauth/codex-account-store.ts +34 -1
  380. package/src/ops/loops-map.ts +46 -0
  381. package/src/ops/overview-cache.ts +22 -4
  382. package/src/ops/overview.ts +41 -5
  383. package/src/policy/llm-policy.ts +3 -1
  384. package/src/prompt-enhance/enhance.ts +4 -4
  385. package/src/pty-shell/availability.ts +7 -0
  386. package/src/pty-shell/herdr-agent-report.ts +40 -31
  387. package/src/pty-shell/herdr-prune-stale.ts +17 -0
  388. package/src/pty-shell/herdr-sequence-lease.ts +60 -0
  389. package/src/pty-shell/pty-control-ipc.ts +187 -18
  390. package/src/pty-shell/registry.ts +25 -15
  391. package/src/pty-shell/tui-run-state.ts +235 -1
  392. package/src/release-loop/checklist.ts +50 -5
  393. package/src/release-loop/feature-store.ts +9 -9
  394. package/src/release-loop/landed-but-yellow.ts +93 -12
  395. package/src/repl/index.ts +8 -0
  396. package/src/seat-loop/seat-report.ts +1 -0
  397. package/src/self-dev/ask-launch-flow.ts +86 -10
  398. package/src/self-dev/decompose-proposal.ts +29 -18
  399. package/src/self-dev/dev-pipeline.ts +29 -7
  400. package/src/self-dev/draft-sweep.ts +241 -16
  401. package/src/self-dev/draft-triage-rules.ts +19 -5
  402. package/src/self-dev/review-provider-fallback.ts +45 -7
  403. package/src/self-dev/shard-plan.ts +48 -0
  404. package/src/self-dev/shard-run.ts +50 -3
  405. package/src/self-implement/doctor-visit.ts +83 -0
  406. package/src/self-implement/gate-artifacts.ts +6 -2
  407. package/src/self-implement/gate-baseline.ts +64 -1
  408. package/src/self-implement/gate-cli.ts +93 -9
  409. package/src/self-implement/gate-remote.ts +28 -4
  410. package/src/self-implement/gate-scope.ts +13 -1
  411. package/src/self-implement/goal-author.ts +333 -18
  412. package/src/self-implement/goal-supervisor-fill.ts +75 -26
  413. package/src/self-implement/importer-test-index.ts +215 -2
  414. package/src/self-implement/knowhow-pack.ts +5 -5
  415. package/src/self-implement/orchestrator.ts +318 -25
  416. package/src/self-implement/review-round-brief.ts +300 -0
  417. package/src/self-implement/rework-policy.ts +14 -0
  418. package/src/self-implement/run-ledger.ts +170 -6
  419. package/src/self-implement/run-stop.ts +1 -1
  420. package/src/self-implement/running-runs.ts +17 -3
  421. package/src/self-implement/seams.ts +12 -6
  422. package/src/self-implement/stop-autoheal.ts +1 -0
  423. package/src/self-implement/test-seams.ts +13 -1
  424. package/src/self-implement/train-reach-grouping.ts +8 -2
  425. package/src/session/chat.ts +7 -1
  426. package/src/session/id-prefix.ts +60 -0
  427. package/src/session/index.ts +4 -0
  428. package/src/session-runtime/index.ts +9 -1
  429. package/src/skills/tools/pty.ts +5 -0
  430. package/src/status/metrics.ts +12 -1
  431. package/src/steward/rejudge-tick.ts +12 -5
  432. package/src/steward/resync-tick.ts +31 -4
  433. package/src/task-agent/actions.ts +199 -4
  434. package/src/task-agent/boundary-proof-mapping.ts +12 -0
  435. package/src/task-agent/boundary-proof-producer.ts +147 -0
  436. package/src/task-agent/completion-evidence.ts +2 -1
  437. package/src/task-agent/green-close-recipe.ts +239 -0
  438. package/src/task-agent/green-sweep.ts +64 -10
  439. package/src/task-agent/live-moves.ts +48 -3
  440. package/src/task-agent/map-host-adapter.ts +134 -0
  441. package/src/task-agent/map-reduce.ts +158 -0
  442. package/src/task-agent/map-run-ledger.ts +226 -0
  443. package/src/task-agent/parent-host.ts +21 -11
  444. package/src/task-agent/rejudge-on-head.ts +72 -5
  445. package/src/task-agent/repair-cycle.ts +41 -0
  446. package/src/task-agent/repair-next-move.ts +12 -2
  447. package/src/task-agent/review-prior-fix.ts +12 -2
  448. package/src/task-agent/shadow.ts +188 -5
  449. package/src/task-agent/task-hand.ts +158 -13
  450. package/src/task-agent/task-parent-role-llm.ts +9 -0
  451. package/src/task-agent/tier-split.ts +4 -3
  452. package/src/task-agent/work-order.ts +53 -2
  453. package/src/task-orchestrator/dispatcher.ts +46 -1
  454. package/src/task-orchestrator/generator-prompt.ts +6 -0
  455. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +16 -6
  456. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +8 -6
  457. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +97 -0
  458. package/src/task-orchestrator/surfaces/pod-pool.ts +50 -4
  459. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +15 -9
  460. package/src/task-orchestrator/surfaces/self-implement-pod.ts +217 -36
  461. package/src/task-orchestrator/surfaces/self-implement.ts +4 -0
  462. package/src/testing/linux-env.ts +177 -0
  463. package/src/tui/chat/goal-interim.ts +5 -1
  464. package/src/tui/entry.ts +258 -4
  465. package/src/tui/input/at-picker.ts +55 -0
  466. package/src/tui/slash/resume.ts +35 -12
  467. package/src/tui/slash/signals.ts +114 -0
  468. package/src/tui/slash/term-next.ts +71 -0
  469. package/src/tui-client/local-chat-turn.ts +4 -3
  470. package/src/user-config.ts +19 -1
  471. package/src/ux-sim/boot-daemon-tools-pty-shell-device.ts +136 -0
  472. package/src/ux-sim/classic-tui-seat-scenarios.ts +158 -4
  473. package/src/ux-sim/fake-gh.ts +174 -0
  474. package/src/ux-sim/fake-kubectl.ts +142 -0
  475. package/src/ux-sim/fake-llm.ts +6 -1
  476. package/src/ux-sim/next-tui-seat-scenarios.ts +124 -6
  477. package/src/ux-sim/resume-picker-device.ts +173 -0
  478. package/src/ux-sim/session-id-seed.ts +60 -0
  479. package/src/ux-sim/skills-tools-shell-device.ts +156 -0
  480. package/src/ux-sim/task-agent-parent-host-device.ts +264 -0
  481. package/src/ux-sim/task-agent-rejudge-on-head-device.ts +114 -0
  482. package/src/version/packed-revision.json +1 -1
  483. package/apps/pwa/out/_next/static/chunks/109.f196abf3484bb97f.js +0 -287
  484. package/apps/pwa/out/_next/static/chunks/1201.afbfc5b1c5e5cbe4.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/1517.9275b89572098ff3.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/1574.ad11f8db8396ab45.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/1614.7a8c5c40aee0261f.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/1871-f1eb29d8dbb4065b.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/2193.0ea24c4a7aa1d004.js +0 -36
  490. package/apps/pwa/out/_next/static/chunks/223-ad865c1e80491d78.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/2265-f0d90b6dbec05f81.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/2267.eb4a62a209f09ec4.js +0 -149
  493. package/apps/pwa/out/_next/static/chunks/2341-86cd06c0cf01aa33.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/2352.9abc42422bade876.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/2397-c1653a816061842d.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/2500.cbc1257b774aae52.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/2709.8e1889034255fc5f.js +0 -24
  498. package/apps/pwa/out/_next/static/chunks/2812ca6a.3b9f4a7a08885744.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/284.ac77c0db55bfd3a8.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/2941.3c3546f96bc6efa7.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/2976.b000698a3c6512dc.js +0 -24
  502. package/apps/pwa/out/_next/static/chunks/3031-85328a92fcce4da6.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/3148-98efd4869d660a87.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/3155.2587a55196ecb394.js +0 -226
  505. package/apps/pwa/out/_next/static/chunks/3277.785db1cf09f98601.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/3320-f9c77381f85a2c0a.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/3386-91049feabff9a94e.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/339.80afb7e8839372af.js +0 -8
  509. package/apps/pwa/out/_next/static/chunks/3538.a445e50bb359d068.js +0 -32
  510. package/apps/pwa/out/_next/static/chunks/3540c645.2595a6af01a97672.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/3621.581b2054cc14bee4.js +0 -286
  512. package/apps/pwa/out/_next/static/chunks/3676.8c28d16fd84ca843.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/375-b84540853523fe52.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/379.ee262e37586fe1ae.js +0 -74
  515. package/apps/pwa/out/_next/static/chunks/3969-81054381eaec4c4c.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/4067-bf9aa60547972937.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/4116.a84a92a2492c080e.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/4176.e5729c93c29151eb.js +0 -104
  519. package/apps/pwa/out/_next/static/chunks/4227-e6c84686bc8946ca.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/4327.b9e08a71c0009348.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/4946.bd106eafd668f248.js +0 -148
  522. package/apps/pwa/out/_next/static/chunks/5085-bce4904931767ca6.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/5125.2eb9968b83767193.js +0 -93
  524. package/apps/pwa/out/_next/static/chunks/5128-75b733fd74a9a1eb.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/5489f3fe.4bb1458b09f6dead.js +0 -122
  526. package/apps/pwa/out/_next/static/chunks/5560-2a4648f6a30f16e7.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/5772-e29e14f8ff127122.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/5887.5b2f5b44ce0f7e9a.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/5899-4c112a2d908cf97f.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/599.c39c571b6733f2a2.js +0 -41
  531. package/apps/pwa/out/_next/static/chunks/6041.1b65efb796ebc2a5.js +0 -79
  532. package/apps/pwa/out/_next/static/chunks/6057-765c0a26028c86b2.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/6142.f289e0d039f453a6.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/6199-6bcd61d2a3910e04.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/6236.cdf7293e710a9b2d.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/6387-237e079dac9a3ae6.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/6448-b6d272d074276e14.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/6566.926a1d1b7c911442.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/6580-cdc0e4a634ce4708.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/6659-b242dc31b20b3f29.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/6690.c5d9b321b1f79384.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/671-e9b6ad0c771c5514.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/6862.bcbf89dd0f4496b3.js +0 -27
  544. package/apps/pwa/out/_next/static/chunks/6912-7b375ee48732c767.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/6925.562d532110879886.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/727.7deb87749b0a343a.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/7545.b1aa42cd15503319.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/7551-416d298c2dc4a9ab.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/7784.1f0ac4a16d190cf4.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/7870-309de463786ac1fc.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/7888-e29b151bb6a3bb8b.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/7891.03f910124a737985.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/7905-6ba78d9234608f79.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/8037.e254fd797aa224d1.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/8207.f527886b7c464529.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/856.50bda61439d465e6.js +0 -62
  557. package/apps/pwa/out/_next/static/chunks/9031-92e9b487edb681ee.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/9060.5410d9f6a95f6287.js +0 -90
  559. package/apps/pwa/out/_next/static/chunks/914.8abefd48d579547b.js +0 -94
  560. package/apps/pwa/out/_next/static/chunks/9187-c87a2207d4ac5d46.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/9289.3e92f278821e18e0.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/9319.7816d3cc741cd34f.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/9327-e016bb9f1674d24f.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/9365.3de48bf75193ff9a.js +0 -77
  565. package/apps/pwa/out/_next/static/chunks/9383.5b5e9aa56b5fea97.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/9387-cb929573ef436f39.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/9489.c27becacc3283dec.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/9514.131c52cc75fe6439.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/9536.28962663c7734376.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/9686-12286f5bbf15a5a0.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/9776.289d40cc5a4c342a.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/9789.60f723d18097ec30.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/989.f0c17523c5e61f65.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/9972-1df393aa8d4f7d52.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-96750aba7430959f.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4105e5bbbb043024.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5d4fa75c2acb113e.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/board/page-a79a9bdb3b2142bf.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/botlab/page-f1c2047484992a35.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/bots/page-15ffc49b2d8c70e2.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/ceo/page-67d53a3fc8734135.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/chat/page-e30c8dc696491336.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/consult/page-2136e04fa77696e2.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/control/page-fecd2814790c2e12.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/decisions/page-280aa4a98fc5bb44.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/design-check/page-c14e96b65fb4b883.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/editor/page-187dac74825ed6bf.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/exec/page-6bc7246ea8c92448.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/field/page-083747f1d4518f61.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/healthz/route-96750aba7430959f.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/inside/page-15ca95a16ec96e1f.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/intake/page-a8ac031b3810eb64.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/layout-8835f93110494cda.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/app/loops/page-5b92b7f75df20948.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/app/market/page-468eddf2b1a84ecb.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/app/morning/page-18b40867a77a5793.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/app/not-found-fe934f2979f5a4a2.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/app/observatory/page-3eec435537357170.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ff88cbfd2aa99fc8.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a5b39fb780c70ab5.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ae465dde1d1b3933.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/app/outputs/page-e7abc34a48a6f71e.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/app/page-cab9674fde9ead26.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/app/reflection/page-1da49bc8712284d0.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5e8aa657551ac912.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/app/sessions/page-fcc36f36c6dc1fe2.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-d22db22f2a7296ca.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/app/settings/page-d12dc7b1773878f0.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-99debec9ea7fbec0.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/app/setup/layout-96750aba7430959f.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/app/setup/page-2a088a78bad7e924.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/app/share/page-2e8ed57db7e69993.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4b3a16213c2ae8fe.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f098747ddf38c98b.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/app/term/page-c15958835534404b.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/app/today/page-68279e3c843c1fd7.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/app/trace/page-a34a74453367a39d.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/app/vault/page-8767a7db2155a5bd.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-bd5976683460d7f2.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/app/workflows/page-b7364835e7f2ef54.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/app/workspace/page-311af16b3ee0ffca.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fb670757274ae990.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/f9a61e4d.1fc2c1dbdd49fe46.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/framework-6d497cbf1eef04c6.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/main-app-012f5edec932f337.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/main-da5aeb809edc94a2.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/pages/_app-5ee319af6f749dfe.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/pages/_error-4bc84653a718d6c6.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/webpack-8a96cf6775c73d6e.js +0 -1
  630. /package/apps/pwa/out/_next/static/{uC-N0emedeNOFuaBQFv-I → 8kITumG9GAZ1LwT9POrAR}/_ssgManifest.js +0 -0
@@ -11,6 +11,7 @@
11
11
  // `reportTuiTurn` 을 부른다. 기록 실패는 `debug.log('pty.tui-state', …)` — 조용히 삼키지 않는다.
12
12
  // 판독(CLI 쪽): `describePtyRunState` 가 행 ⊕ 설치본 기록 ⊕ (체크아웃이면) 그 뿌리의 HEAD 로 판정한다.
13
13
 
14
+ import { spawn } from 'node:child_process';
14
15
  import { readFileSync } from 'node:fs';
15
16
  import { homedir } from 'node:os';
16
17
  import { dirname, join, resolve } from 'node:path';
@@ -307,6 +308,144 @@ export function formatTurnCell(cell: PtyTurnCell): string {
307
308
  return `${cell.state},${cell.sinceSec === null ? '?' : cell.sinceSec}s`;
308
309
  }
309
310
 
311
+ // ── H9 자기 재기동 — «재기동 필요» ∧ 유휴 ∧ 사람 입력 없음 ⇒ 스스로 재기동 ──
312
+ //
313
+ // 정본: seat-state/OPC/TUI-HOMEOSTASIS-SLICES-2026-10-16.md (TUI-H9-IDLE-SELF-RESTART).
314
+ // 판정(`decideSelfRestart`)은 순수 함수 — 실제 «새 세션 ⊕ 자리 맥락 재주입» 은 주입된 수행자(`restart`)가 한다.
315
+ // 낡음 판정은 기존 `BuildStaleness` 를 그대로 재사용한다(판정·문면을 두벌로 만들지 않는다).
316
+ // 사람 입력 대기 신호는 입력 축(대시보드)이 준다 — 이 모듈은 입력 큐를 소유하지 않는다.
317
+
318
+ /** 유휴 유예: 이만큼 조용했을 때만 재기동한다 — 턴 사이 «쉼»에서 세션을 새로 띄우지 않는다. */
319
+ export const DEFAULT_SELF_RESTART_IDLE_MS = 90_000;
320
+
321
+ export type SelfRestartReason =
322
+ | 'stale-idle-no-input'
323
+ | 'not-stale'
324
+ | 'staleness-unknown'
325
+ | 'turn-busy'
326
+ | 'turn-unknown'
327
+ | 'still-warm'
328
+ | 'human-input-pending';
329
+
330
+ export interface SelfRestartDecisionDeps {
331
+ /** 재기동 필요인가 — 미지(null)면 재기동하지 않는다. */
332
+ readonly stale: boolean | null;
333
+ readonly staleReason?: BuildStaleReason;
334
+ readonly turn: PtyTurnView;
335
+ /** 지금 턴 상태에 머문 시간(ms) — 모르면 null. */
336
+ readonly idleMs: number | null;
337
+ readonly humanInputPending: boolean;
338
+ readonly minIdleMs?: number;
339
+ }
340
+
341
+ export interface SelfRestartDecision {
342
+ readonly restart: boolean;
343
+ readonly reason: SelfRestartReason;
344
+ }
345
+
346
+ /** 판정선: 세 조건(재기동 필요 · 유휴 · 사람 입력 없음)이 모두 참일 때만 참. 거짓인 첫 조건을 이유로 낸다. */
347
+ export function decideSelfRestart(deps: SelfRestartDecisionDeps): SelfRestartDecision {
348
+ if (deps.stale === null) return { restart: false, reason: 'staleness-unknown' };
349
+ if (!deps.stale) return { restart: false, reason: 'not-stale' };
350
+ if (deps.turn === 'busy') return { restart: false, reason: 'turn-busy' };
351
+ if (deps.turn === 'unknown') return { restart: false, reason: 'turn-unknown' };
352
+ const minIdleMs = deps.minIdleMs ?? DEFAULT_SELF_RESTART_IDLE_MS;
353
+ if (deps.idleMs === null || deps.idleMs < minIdleMs) return { restart: false, reason: 'still-warm' };
354
+ if (deps.humanInputPending) return { restart: false, reason: 'human-input-pending' };
355
+ return { restart: true, reason: 'stale-idle-no-input' };
356
+ }
357
+
358
+ /** 재기동 계획 — 새 세션 ⊕ 자리 맥락 재주입 명령까지. 세션 id 를 모르면 명령은 null(판정 근거는 남긴다). */
359
+ export interface TuiSelfRestartPlan {
360
+ readonly surfaceId: string;
361
+ readonly staleReason: BuildStaleReason;
362
+ readonly idleMs: number;
363
+ readonly relaunchCommand: string | null;
364
+ }
365
+
366
+ export interface TuiSelfRestartDeps {
367
+ /** 낡음 판정 재료 — 생략하면 이 프로세스의 기동 판 ⊕ 설치본 ⊕ 체크아웃 HEAD 로 잰다. */
368
+ readonly staleness?: () => BuildStaleness;
369
+ /** 사람이 지금 입력 중인가 — 생략하면 «모름» 으로 보고 재기동하지 않는다(조용한 세션을 뺏지 않는다). */
370
+ readonly humanInputPending?: () => boolean;
371
+ readonly sessionId?: string;
372
+ /**
373
+ * 지금 이 TUI 가 «소유한» 세션 — 틱마다 다시 읽어 `sessionId` 보다 우선한다.
374
+ * 대화가 갈아 끼워지는 TUI 는 기동 시점 값을 고정할 수 없어 이 칸을 쓴다(읽기 실패 → null → 재기동 안 함).
375
+ */
376
+ readonly readSessionId?: () => string | null;
377
+ readonly minIdleMs?: number;
378
+ readonly intervalMs?: number;
379
+ /** 실제 재기동 수행자(새 세션 ⊕ 자리 맥락 재주입). 생략 시 설치본 `elanous --resume <세션>`. */
380
+ readonly restart?: (plan: TuiSelfRestartPlan) => void;
381
+ /**
382
+ * 낡은 TUI 가 좌석을 «놓는» 동작 — 새 세션을 띄우기 직전에 부른다.
383
+ * 생략해도 이 녹음기는 그 좌석 행 쓰기를 스스로 놓는다(두 TUI 가 한 좌석을 겹쳐 쓰지 않게).
384
+ */
385
+ readonly yieldSeat?: (plan: TuiSelfRestartPlan) => void;
386
+ /** 유휴 감시 등록 — 틱을 받아 해제 함수를 준다. 생략 시 unref 된 setInterval. */
387
+ readonly watch?: (tick: () => void) => () => void;
388
+ }
389
+
390
+ const stalenessFromRunningBuild = (): BuildStaleness => {
391
+ const build = runningBuild();
392
+ const installed = readCurrentInstallRecord();
393
+ let checkoutHead: string | undefined;
394
+ if (build.source === 'checkout' && build.codeRoot) checkoutHead = readCheckoutHead(build.codeRoot);
395
+ return judgeBuildStaleness({ build, installed, ...(checkoutHead ? { checkoutHead } : {}) });
396
+ };
397
+
398
+ const defaultWatch = (tick: () => void, intervalMs: number): (() => void) => {
399
+ const timer = setInterval(tick, intervalMs);
400
+ (timer as unknown as { unref?: () => void }).unref?.();
401
+ return () => { clearInterval(timer); };
402
+ };
403
+
404
+ /** 재기동 인자: 새 세션은 `--resume <세션>` 으로 그 좌석의 자리 맥락을 다시 받는다. */
405
+ export const TUI_RELAUNCH_FLAG = '--resume';
406
+
407
+ export interface TuiRelaunchChild {
408
+ on(event: 'error', listener: (error: Error) => void): unknown;
409
+ on(event: 'spawn', listener: () => void): unknown;
410
+ unref(): void;
411
+ }
412
+
413
+ /** 프로세스 띄우기 seam — 무대는 여기만 가짜로 바꿔 «생산 argv·옵션» 을 그대로 지난다. */
414
+ export type TuiRelaunchSpawn = (
415
+ command: string,
416
+ args: readonly string[],
417
+ options: { detached: boolean; stdio: 'ignore' },
418
+ ) => TuiRelaunchChild;
419
+
420
+ const defaultRelaunchSpawn: TuiRelaunchSpawn = (command, args, options) =>
421
+ spawn(command, args, options) as unknown as TuiRelaunchChild;
422
+
423
+ let relaunchSpawn: TuiRelaunchSpawn = defaultRelaunchSpawn;
424
+
425
+ /** 무대용 spawn 교체 — 인자 없이 부르면 생산 기본값(spawn)으로 되돌린다. */
426
+ export function setTuiRelaunchSpawnForTesting(next?: TuiRelaunchSpawn): void {
427
+ relaunchSpawn = next ?? defaultRelaunchSpawn;
428
+ }
429
+
430
+ /** 기본 재기동 수행자: 설치본 `elanous --resume <세션>` — 새 세션이 그 좌석의 자리 맥락으로 다시 뜬다. */
431
+ export const spawnTuiRelaunch = (sessionId: string): Promise<void> => {
432
+ return new Promise<void>((resolve, reject) => {
433
+ const child = relaunchSpawn(process.env.ELANOUS_BIN || 'elanous', [TUI_RELAUNCH_FLAG, sessionId], { detached: true, stdio: 'ignore' });
434
+ let settled = false;
435
+ child.on('error', (error: Error) => {
436
+ if (settled) return;
437
+ settled = true;
438
+ reject(error);
439
+ });
440
+ child.on('spawn', () => {
441
+ if (settled) return;
442
+ settled = true;
443
+ child.unref();
444
+ resolve();
445
+ });
446
+ });
447
+ };
448
+
310
449
  // ── 기록(TUI) ──
311
450
 
312
451
  export interface TuiRunStateRecorderDeps {
@@ -326,6 +465,11 @@ export interface TuiRunStateRecorderDeps {
326
465
  * 생략 시: 기본 신고기(herdr 미주입)일 때만 실 `process` · 주입 herdr 는 지킴이 0. `null` 이면 안 건다.
327
466
  */
328
467
  readonly signals?: HerdrSignalTarget | null;
468
+ /**
469
+ * H9 자기 재기동(낡은 판 ∧ 유휴 ∧ 사람 입력 없음 ⇒ 새 세션 ⊕ 자리 맥락 재주입).
470
+ * 생략하면 감시를 안 건다 — 이 축을 켜는 것은 호출자(생산 진입점)의 결정이다.
471
+ */
472
+ readonly selfRestart?: TuiSelfRestartDeps;
329
473
  }
330
474
 
331
475
  interface ActiveRecorder {
@@ -336,6 +480,8 @@ interface ActiveRecorder {
336
480
  readonly herdr: HerdrAgentReporterLike | null;
337
481
  /** manifest 에 실제로 적힌 마지막 턴. 실패면 안 바뀐다(재시도). */
338
482
  last?: PtyTurnState;
483
+ /** idle 로 «바뀐» 시각 — 유휴 경과의 0초. busy 로 가면 다시 모른다. */
484
+ idleSince?: number;
339
485
  /** herdr 에 마지막으로 알린 턴. manifest 성패와 따로 둔다. */
340
486
  herdrLast?: PtyTurnState;
341
487
  }
@@ -366,6 +512,88 @@ export function startTuiRunStateRecorder(surfaceId: string, deps: TuiRunStateRec
366
512
  active = recorder;
367
513
  writeTurnState(recorder, 'idle');
368
514
  if (herdr) { try { herdr.start(); } catch { /* 신고 실패가 녹음기를 깨지 않는다 */ } }
515
+ // H9 유휴 감시: 판·턴·사람 입력을 매 틱 다시 재고, 재기동 성공 뒤에만 좌석을 양도한다.
516
+ const selfRestart = deps.selfRestart;
517
+ let unwatch: () => void = () => {};
518
+ if (selfRestart) {
519
+ const minIdleMs = selfRestart.minIdleMs ?? DEFAULT_SELF_RESTART_IDLE_MS;
520
+ const restartedFor = new Set<BuildStaleReason>();
521
+ let restartInFlight = false;
522
+ const tick = (): void => {
523
+ if (active !== recorder) return;
524
+ let staleness: BuildStaleness;
525
+ try { staleness = (selfRestart.staleness ?? stalenessFromRunningBuild)(); } catch (error) {
526
+ log('self-restart-staleness-failed', { ptyId: surfaceId, error: error instanceof Error ? error.message : String(error) });
527
+ return;
528
+ }
529
+ // 사람 입력 신호가 없거나 읽을 수 없으면 모름으로 닫는다 — 명시적 false만 «사람 입력 없음»이다.
530
+ let humanInputPending = true;
531
+ if (selfRestart.humanInputPending) {
532
+ try { humanInputPending = selfRestart.humanInputPending() !== false; } catch { humanInputPending = true; }
533
+ }
534
+ // 재기동은 «지금 이 TUI 가 보여 주는 대화» 로만 잇는다 — 읽기 실패는 null(대체 세션을 지어내지 않는다).
535
+ let sessionId: string | null = selfRestart.sessionId ?? null;
536
+ if (selfRestart.readSessionId) {
537
+ try { sessionId = selfRestart.readSessionId(); } catch { sessionId = null; }
538
+ }
539
+ const idleMs = recorder.idleSince === undefined ? null : Math.max(0, now() - recorder.idleSince);
540
+ const decision = decideSelfRestart({
541
+ stale: staleness.stale,
542
+ staleReason: staleness.reason,
543
+ turn: recorder.last ?? 'unknown',
544
+ idleMs,
545
+ humanInputPending,
546
+ minIdleMs,
547
+ });
548
+ if (!decision.restart || restartedFor.has(staleness.reason) || restartInFlight) return;
549
+ if (!sessionId) {
550
+ log('self-restart-no-session', { ptyId: surfaceId, staleReason: staleness.reason });
551
+ return;
552
+ }
553
+ const plan: TuiSelfRestartPlan = {
554
+ surfaceId,
555
+ staleReason: staleness.reason,
556
+ idleMs: idleMs ?? 0,
557
+ relaunchCommand: sessionId ? buildStaleInstallRelaunchCommand(sessionId) : null,
558
+ };
559
+ log('self-restart', { ptyId: surfaceId, reason: decision.reason, staleReason: plan.staleReason, idleMs: plan.idleMs, relaunchCommand: plan.relaunchCommand });
560
+ restartInFlight = true;
561
+ const handoff = (): void => {
562
+ // 새 세션이 실제로 생성된 뒤에만 낡은 TUI 가 좌석 행 쓰기를 놓는다.
563
+ if (selfRestart.yieldSeat) {
564
+ try {
565
+ selfRestart.yieldSeat(plan);
566
+ } catch (error) {
567
+ log('self-restart-yield-failed', { ptyId: surfaceId, error: error instanceof Error ? error.message : String(error) });
568
+ }
569
+ }
570
+ if (active === recorder) active = null;
571
+ restartedFor.add(staleness.reason);
572
+ restartInFlight = false;
573
+ log('self-restart-handover', { ptyId: surfaceId, staleReason: plan.staleReason, relaunchCommand: plan.relaunchCommand });
574
+ };
575
+ const failed = (error: unknown): void => {
576
+ restartInFlight = false;
577
+ log('self-restart-failed', { ptyId: surfaceId, error: error instanceof Error ? error.message : String(error) });
578
+ };
579
+ try {
580
+ if (selfRestart.restart) {
581
+ selfRestart.restart(plan);
582
+ handoff();
583
+ } else {
584
+ void spawnTuiRelaunch(sessionId).then(handoff, failed);
585
+ }
586
+ } catch (error) {
587
+ failed(error);
588
+ }
589
+ };
590
+ try {
591
+ const watch = selfRestart.watch ?? ((fn: () => void) => defaultWatch(fn, selfRestart.intervalMs ?? Math.max(5_000, Math.round(minIdleMs / 3))));
592
+ unwatch = watch(tick);
593
+ } catch (error) {
594
+ log('self-restart-watch-failed', { ptyId: surfaceId, error: error instanceof Error ? error.message : String(error) });
595
+ }
596
+ }
369
597
  // raw 모드 전 ctrl+c(SIGINT 기본 죽음)는 'exit' 훅을 안 돌린다 — 신호에서도 반납한다.
370
598
  const signals = deps.signals !== undefined ? deps.signals : (deps.herdr === undefined ? process : null);
371
599
  let unguard: () => void = () => {};
@@ -373,6 +601,7 @@ export function startTuiRunStateRecorder(surfaceId: string, deps: TuiRunStateRec
373
601
  try { unguard = guardHerdrReleaseOnSignal(() => herdr.release(), signals); } catch { /* 지킴이 실패가 녹음기를 깨지 않는다 */ }
374
602
  }
375
603
  return () => {
604
+ try { unwatch(); } catch { /* 종료 경로를 깨지 않는다 */ }
376
605
  try { unguard(); } catch { /* 종료 경로를 깨지 않는다 */ }
377
606
  if (herdr) { try { herdr.release(); } catch { /* 종료 경로를 깨지 않는다 */ } }
378
607
  if (active === recorder) active = null;
@@ -389,7 +618,12 @@ function writeTurnState(recorder: ActiveRecorder, turn: PtyTurnState): void {
389
618
  const at = recorder.now();
390
619
  let ok = false;
391
620
  try { ok = recorder.writeTurn(recorder.surfaceId, turn, at); } catch { ok = false; }
392
- if (ok) recorder.last = turn;
621
+ if (ok) {
622
+ recorder.last = turn;
623
+ // 유휴 경과의 0초는 «idle 로 바뀐 시각» — busy 로 가면 다시 모른다.
624
+ if (turn === 'idle') recorder.idleSince = at;
625
+ else recorder.idleSince = undefined;
626
+ }
393
627
  // 실패는 관측으로 남긴다 — 바깥 제어자는 그 사이 «unknown/낡은 값»을 본다. 성공은 남기지 않는다(턴마다 두 줄은 소음).
394
628
  else recorder.log('turn-write-failed', { ptyId: recorder.surfaceId, turn, at });
395
629
  // herdr 는 manifest 쓰기 성패와 무관하게, 값이 바뀔 때 알린다(같은 값 재시도는 한 번만).
@@ -37,6 +37,8 @@ export interface ChecklistItem {
37
37
  /** 이 칸이 근거로 삼는 문서(저장소 상대 경로, 선택적으로 `#절`). 없으면 칸에 실리지 않는다. */
38
38
  refs?: string[];
39
39
  evidence?: string;
40
+ /** Public known-issue wording, independent from the internal checklist title. */
41
+ knownIssue?: string;
40
42
  disposition?: ChecklistDisposition;
41
43
  kind?: ChecklistKind;
42
44
  /** Whether the seat dispatcher should launch a coding task. Missing in old rows means code. */
@@ -67,6 +69,49 @@ export interface Checklist {
67
69
  history: ChecklistHistory[];
68
70
  }
69
71
 
72
+ export type ChecklistEvidenceState = 'observed' | 'missing' | 'unmeasurable';
73
+ export type ChecklistEvidenceStageId = 'E1' | 'E3' | 'E4' | 'E5';
74
+ export interface ChecklistEvidenceStage {
75
+ id: ChecklistEvidenceStageId;
76
+ state: ChecklistEvidenceState;
77
+ source: string | null;
78
+ measuredAt: string | null;
79
+ reason: string;
80
+ verificationCommand?: string | null;
81
+ }
82
+ export interface ChecklistEvidenceChain {
83
+ id: string;
84
+ version: string;
85
+ stages: ChecklistEvidenceStage[];
86
+ greenEligible: boolean;
87
+ reason: string | null;
88
+ }
89
+
90
+ /** Build a read-only, fail-closed evidence verdict. Claims are observed only when a source adapter verifies them. */
91
+ export function checklistEvidenceChain(input: {
92
+ item: ChecklistItem;
93
+ version: string;
94
+ stages: readonly ChecklistEvidenceStage[];
95
+ }): ChecklistEvidenceChain {
96
+ const required: ChecklistEvidenceStageId[] = ['E1', 'E3', 'E4', 'E5'];
97
+ const byId = new Map(input.stages.map((stage) => [stage.id, stage]));
98
+ const stages = required.map((id) => {
99
+ const stage = byId.get(id);
100
+ if (!stage) return { id, state: 'unmeasurable' as const, source: null, measuredAt: null, reason: '출처 어댑터 결과가 없다' };
101
+ if (stage.state === 'observed' && (!stage.source?.trim() || !stage.measuredAt || !Number.isFinite(Date.parse(stage.measuredAt))))
102
+ return { ...stage, state: 'unmeasurable' as const, reason: 'observed 판정에 유효한 출처와 측정 시각이 없다' };
103
+ return stage;
104
+ });
105
+ const unresolved = stages.filter((stage) => stage.state !== 'observed');
106
+ return {
107
+ id: input.item.id,
108
+ version: input.version,
109
+ stages,
110
+ greenEligible: unresolved.length === 0,
111
+ reason: unresolved.length ? `${unresolved.map((stage) => `${stage.id}=${stage.state}: ${stage.reason}`).join('; ')}` : null,
112
+ };
113
+ }
114
+
70
115
  export function devVersion(): string {
71
116
  return (JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf8')) as { version: string }).version;
72
117
  }
@@ -405,7 +450,7 @@ export function addItem(v: string, input: { id: string; title: string; refs?: st
405
450
  });
406
451
  }
407
452
 
408
- export function setItem(v: string, id: string, patch: { status?: ChecklistStatus; evidence?: string; refs?: string[]; owner?: string; disposition?: ChecklistDisposition; kind?: ChecklistKind; dispatchKind?: ChecklistDispatchKind; priority?: ChecklistItem['priority']; predecessors?: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean | null }, by: string): Checklist {
453
+ export function setItem(v: string, id: string, patch: { status?: ChecklistStatus; evidence?: string; knownIssue?: string | null; refs?: string[]; owner?: string; disposition?: ChecklistDisposition; kind?: ChecklistKind; dispatchKind?: ChecklistDispatchKind; priority?: ChecklistItem['priority']; predecessors?: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean | null }, by: string): Checklist {
409
454
  return setItemGuarded(v, id, patch, by);
410
455
  }
411
456
 
@@ -501,13 +546,13 @@ function setItemGuarded(v: string, id: string, patch: Parameters<typeof setItem>
501
546
  throw new CliUserError(`지금 주인: ${item.owner} — --force 로만 바꾼다`, 'release checklist claim <id> --by <자리> --force');
502
547
  }
503
548
  }
504
- const fields = (['evidence', 'owner', 'status', 'disposition', 'kind', 'dispatchKind', 'priority', 'predecessors', 'deadlineVersion', 'ceoMinutes', 'ceoDate', 'accelerator', 'refs'] as const).filter((field) => patch[field] !== undefined && JSON.stringify(patch[field]) !== JSON.stringify(item[field] ?? null));
549
+ const fields = (['evidence', 'knownIssue', 'owner', 'status', 'disposition', 'kind', 'dispatchKind', 'priority', 'predecessors', 'deadlineVersion', 'ceoMinutes', 'ceoDate', 'accelerator', 'refs'] as const).filter((field) => patch[field] !== undefined && JSON.stringify(patch[field]) !== JSON.stringify(item[field] ?? null));
505
550
  if (fields.length === 0) return false;
506
551
  const at = new Date().toISOString();
507
552
  for (const field of fields) {
508
553
  const from = item[field];
509
554
  const to = patch[field]!;
510
- if (field === 'accelerator' && to === null) delete item.accelerator;
555
+ if ((field === 'accelerator' || field === 'knownIssue') && to === null) delete (item as unknown as Record<string, unknown>)[field];
511
556
  else (item as unknown as Record<string, unknown>)[field] = to;
512
557
  item.updatedAt = at;
513
558
  item.updatedBy = by;
@@ -585,7 +630,7 @@ export interface ChecklistGate {
585
630
  undecided: string[];
586
631
  blocked: string[];
587
632
  moved: string[];
588
- knownIssues: Array<{ id: string; title: string; evidence: string }>;
633
+ knownIssues: Array<{ id: string; title: string; evidence: string; knownIssue?: string }>;
589
634
  /** Post-publication cells excluded from the pre-publication judgement. */
590
635
  postpub?: string[];
591
636
  /** Screen cells whose `짝:` line is missing or has an untracked ⏳ — a warning; it never changes `ok`. */
@@ -616,7 +661,7 @@ export function checklistGate(v: string): ChecklistGate {
616
661
  if (item.status !== 'yellow') continue;
617
662
  if (item.disposition === 'block') result.blocked.push(item.id);
618
663
  else if (item.disposition === 'move') result.moved.push(item.id);
619
- else if (item.disposition === 'known-issue') result.knownIssues.push({ id: item.id, title: item.title, evidence: item.evidence ?? '' });
664
+ else if (item.disposition === 'known-issue') result.knownIssues.push({ id: item.id, title: item.title, evidence: item.evidence ?? '', ...(item.knownIssue?.trim() ? { knownIssue: item.knownIssue.trim() } : {}) });
620
665
  else result.undecided.push(item.id);
621
666
  }
622
667
  result.ok = result.red.length === 0 && result.undecided.length === 0 && result.blocked.length === 0;
@@ -36,7 +36,7 @@ const walWait = new Int32Array(new SharedArrayBuffer(4));
36
36
  const SCHEMA_COLUMNS: Record<string, readonly string[]> = {
37
37
  imported_versions: ['json_hash', 'imported_at'],
38
38
  events: ['reason', 'generation'],
39
- assignments: ['priority', 'predecessors', 'deadline_version', 'ceo_minutes', 'ceo_date', 'accelerator', 'refs', 'dispatch_kind'],
39
+ assignments: ['priority', 'predecessors', 'deadline_version', 'ceo_minutes', 'ceo_date', 'accelerator', 'refs', 'dispatch_kind', 'known_issue'],
40
40
  release_schedules: ['freeze_from', 'freeze_until', 'publish_at'],
41
41
  };
42
42
  function schemaCurrent(db: Database): boolean {
@@ -81,7 +81,7 @@ function open(root = releaseLedgerRoot()): Database {
81
81
  if (!eventColumns.some((column) => column.name === 'generation')) db.exec('ALTER TABLE events ADD COLUMN generation INTEGER');
82
82
  db.exec('CREATE TABLE IF NOT EXISTS release_schedules (version TEXT PRIMARY KEY, cut_at TEXT NOT NULL, land_by TEXT, updated_at TEXT NOT NULL, updated_by TEXT NOT NULL)');
83
83
  const assignmentColumns = db.query('PRAGMA table_info(assignments)').all() as Array<{ name: string }>;
84
- for (const [name, sql] of [['priority', 'TEXT'], ['predecessors', 'TEXT'], ['deadline_version', 'TEXT'], ['ceo_minutes', 'INTEGER'], ['ceo_date', 'TEXT'], ['accelerator', 'INTEGER'], ['refs', 'TEXT'], ['dispatch_kind', 'TEXT']] as const) {
84
+ for (const [name, sql] of [['priority', 'TEXT'], ['predecessors', 'TEXT'], ['deadline_version', 'TEXT'], ['ceo_minutes', 'INTEGER'], ['ceo_date', 'TEXT'], ['accelerator', 'INTEGER'], ['refs', 'TEXT'], ['dispatch_kind', 'TEXT'], ['known_issue', 'TEXT']] as const) {
85
85
  if (!assignmentColumns.some((column) => column.name === name)) db.exec(`ALTER TABLE assignments ADD COLUMN ${name} ${sql}`);
86
86
  }
87
87
  const scheduleColumns = db.query('PRAGMA table_info(release_schedules)').all() as Array<{ name: string }>;
@@ -178,7 +178,7 @@ function evidenceRefs(db: Database, version: string, id: string): string[] {
178
178
  return (db.query('SELECT ref FROM evidence WHERE feature_id = ? AND version = ? ORDER BY rowid').all(id, version) as Array<{ ref: string }>).map((row) => row.ref);
179
179
  }
180
180
  function items(db: Database, version: string): ChecklistItem[] {
181
- const rows = db.query(`SELECT f.id, COALESCE(a.title_override, f.title) AS title, a.owner, a.kind, a.dispatch_kind, a.priority, a.predecessors, a.deadline_version, a.ceo_minutes, a.ceo_date, a.accelerator, a.refs, a.status, a.disposition, a.evidence, a.updated_at, a.updated_by
181
+ const rows = db.query(`SELECT f.id, COALESCE(a.title_override, f.title) AS title, a.owner, a.kind, a.dispatch_kind, a.priority, a.predecessors, a.deadline_version, a.ceo_minutes, a.ceo_date, a.accelerator, a.refs, a.status, a.disposition, a.evidence, a.known_issue, a.updated_at, a.updated_by
182
182
  FROM assignments a JOIN features f ON f.id = a.feature_id WHERE a.version = ? ORDER BY a.rowid`).all(version) as Array<Record<string, string | number | null>>;
183
183
  return rows.map((r) => {
184
184
  const evidence = [r.evidence, ...evidenceRefs(db, version, r.id as string)].filter((value) => value !== null).join('\n');
@@ -190,7 +190,7 @@ function items(db: Database, version: string): ChecklistItem[] {
190
190
  ...(r.ceo_minutes !== null ? { ceoMinutes: r.ceo_minutes as number } : {}), ...(r.ceo_date !== null ? { ceoDate: r.ceo_date as string } : {}),
191
191
  ...(r.accelerator === 1 ? { accelerator: true as const } : {}),
192
192
  ...(r.refs !== null && r.refs !== undefined ? { refs: JSON.parse(r.refs as string) as string[] } : {}),
193
- ...(r.evidence !== null || evidence ? { evidence } : {}), ...(r.disposition !== null ? { disposition: r.disposition as ChecklistItem['disposition'] } : {}),
193
+ ...(r.evidence !== null || evidence ? { evidence } : {}), ...(r.known_issue !== null ? { knownIssue: r.known_issue as string } : {}), ...(r.disposition !== null ? { disposition: r.disposition as ChecklistItem['disposition'] } : {}),
194
194
  updatedAt: r.updated_at as string, updatedBy: r.updated_by as string };
195
195
  });
196
196
  }
@@ -203,9 +203,9 @@ function putItem(db: Database, version: string, item: ChecklistItem): void {
203
203
  if (!feature) db.query('INSERT INTO features (id, title, owner, kind, created_at) VALUES (?, ?, ?, ?, ?)').run(item.id, item.title, item.owner ?? null, item.kind ?? null, item.updatedAt);
204
204
  // features.owner/kind follow the latest assignment so details() and the checklist never disagree after a set.
205
205
  else if (item.owner !== undefined || item.kind !== undefined) db.query('UPDATE features SET owner = COALESCE(?, owner), kind = COALESCE(?, kind) WHERE id = ?').run(item.owner ?? null, item.kind ?? null, item.id);
206
- db.query(`INSERT INTO assignments (feature_id, version, status, disposition, evidence, title_override, owner, kind, dispatch_kind, priority, predecessors, deadline_version, ceo_minutes, ceo_date, accelerator, refs, updated_at, updated_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
207
- ON CONFLICT(feature_id, version) DO UPDATE SET status=excluded.status, disposition=excluded.disposition, evidence=excluded.evidence, owner=excluded.owner, kind=excluded.kind, dispatch_kind=excluded.dispatch_kind, priority=excluded.priority, predecessors=excluded.predecessors, deadline_version=excluded.deadline_version, ceo_minutes=excluded.ceo_minutes, ceo_date=excluded.ceo_date, accelerator=excluded.accelerator, refs=excluded.refs, updated_at=excluded.updated_at, updated_by=excluded.updated_by`)
208
- .run(item.id, version, item.status, item.disposition ?? null, item.evidence ?? null, feature && feature.title !== item.title ? item.title : null, item.owner ?? null, item.kind ?? null, item.dispatchKind ?? null, item.priority ?? null, item.predecessors ? JSON.stringify(item.predecessors) : null, item.deadlineVersion ?? null, item.ceoMinutes ?? null, item.ceoDate ?? null, item.accelerator === true ? 1 : null, item.refs?.length ? JSON.stringify(item.refs) : null, item.updatedAt, item.updatedBy);
206
+ db.query(`INSERT INTO assignments (feature_id, version, status, disposition, evidence, title_override, owner, kind, dispatch_kind, priority, predecessors, deadline_version, ceo_minutes, ceo_date, accelerator, refs, known_issue, updated_at, updated_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
207
+ ON CONFLICT(feature_id, version) DO UPDATE SET status=excluded.status, disposition=excluded.disposition, evidence=excluded.evidence, owner=excluded.owner, kind=excluded.kind, dispatch_kind=excluded.dispatch_kind, priority=excluded.priority, predecessors=excluded.predecessors, deadline_version=excluded.deadline_version, ceo_minutes=excluded.ceo_minutes, ceo_date=excluded.ceo_date, accelerator=excluded.accelerator, refs=excluded.refs, known_issue=excluded.known_issue, updated_at=excluded.updated_at, updated_by=excluded.updated_by`)
208
+ .run(item.id, version, item.status, item.disposition ?? null, item.evidence ?? null, feature && feature.title !== item.title ? item.title : null, item.owner ?? null, item.kind ?? null, item.dispatchKind ?? null, item.priority ?? null, item.predecessors ? JSON.stringify(item.predecessors) : null, item.deadlineVersion ?? null, item.ceoMinutes ?? null, item.ceoDate ?? null, item.accelerator === true ? 1 : null, item.refs?.length ? JSON.stringify(item.refs) : null, item.knownIssue ?? null, item.updatedAt, item.updatedBy);
209
209
  }
210
210
  function jsonHash(contents: string | Buffer): string { return createHash('sha256').update(contents).digest('hex'); }
211
211
 
@@ -274,7 +274,7 @@ function importOnDb(db: Database, version: string): boolean {
274
274
  record(db, version, { at: new Date().toISOString(), by: item.updatedBy, id: item.id, field: 'reimport', from: null, to: item, released: data.released, dev: data.dev });
275
275
  continue;
276
276
  }
277
- const fields = ['title', 'status', 'evidence', 'owner', 'disposition', 'kind', 'dispatchKind', 'priority', 'predecessors', 'deadlineVersion', 'ceoMinutes', 'ceoDate', 'accelerator', 'refs'] as const;
277
+ const fields = ['title', 'status', 'evidence', 'knownIssue', 'owner', 'disposition', 'kind', 'dispatchKind', 'priority', 'predecessors', 'deadlineVersion', 'ceoMinutes', 'ceoDate', 'accelerator', 'refs'] as const;
278
278
  const baseEvidence = (db.query('SELECT evidence FROM assignments WHERE feature_id = ? AND version = ?').get(item.id, version) as { evidence: string | null }).evidence ?? undefined;
279
279
  const preserveRefs = item.evidence === baseEvidence || item.evidence === current.evidence;
280
280
  const differs = fields.some((field) => field === 'evidence'
@@ -441,7 +441,7 @@ export function set(version: string, id: string, patch: Partial<ChecklistItem>,
441
441
  return mutate(version, released, dev, (data) => {
442
442
  const item = data.items.find((i) => i.id === id);
443
443
  if (!item) throw new CliUserError(`없는 칸: ${id}`, 'list 로 칸 목록을 본다');
444
- const fields = (['evidence', 'owner', 'status', 'disposition', 'kind'] as const).filter((field) => patch[field] !== undefined && patch[field] !== item[field]);
444
+ const fields = (['evidence', 'knownIssue', 'owner', 'status', 'disposition', 'kind'] as const).filter((field) => patch[field] !== undefined && patch[field] !== item[field]);
445
445
  if (!fields.length) return false;
446
446
  const at = new Date().toISOString();
447
447
  for (const field of fields) {
@@ -1,4 +1,6 @@
1
1
  import { ownerMatches, type ChecklistItem } from './checklist.js';
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
2
4
 
3
5
  export interface MergedChecklistPr { number: number; title: string; body: string | null; mergedAt: string }
4
6
  type PrMatchBasis = 'cell-line' | 'title-strong' | 'mention';
@@ -113,35 +115,106 @@ export function landedButYellow(items: readonly ChecklistItem[], prs: readonly M
113
115
  return rows.sort((a, b) => Number(b.newer) - Number(a.newer));
114
116
  }
115
117
 
116
- export interface MergedPrRunResult { status: number | null; stdout: string; stderr: string }
118
+ export interface MergedPrRunResult { status: number | null; stdout: string; stderr: string; timedOut?: boolean }
117
119
  /** One `gh` invocation; resolves when the process exits. */
118
- export type MergedPrRunner = (command: string, args: readonly string[], cwd: string) => Promise<MergedPrRunResult>;
120
+ export type MergedPrRunner = (command: string, args: readonly string[], cwd: string, timeoutMs?: number) => Promise<MergedPrRunResult>;
119
121
 
120
122
  export const MERGED_PR_LIMIT = 400;
121
123
  /** Concurrent `gh pr list` calls once the first page is full (LANDED-YELLOW-FAST · 10-21 prod: 24 sequential calls ≈ 145s). */
122
124
  export const MERGED_PR_CONCURRENCY = 6;
123
125
  const DAY_MS = 86_400_000;
124
126
 
127
+ export interface MergedChecklistFetch {
128
+ prs: MergedChecklistPr[];
129
+ truncated: boolean;
130
+ calls: number;
131
+ /** Set when a time budget stopped the window walk before every window was fetched. */
132
+ stopReason: 'time-budget-exceeded' | undefined;
133
+ /** UTC windows not yet fetched when `stopReason` is set. */
134
+ remainingWindows: string[];
135
+ }
136
+
137
+ const FETCH_CACHE_TTL_MS = 10 * 60_000;
138
+ interface PersistedFetchCache { schema: 1; entries: Record<string, { savedAt: number; result: MergedChecklistFetch }> }
139
+
140
+ function cacheRead(path: string, key: string, nowMs: number): MergedChecklistFetch | undefined {
141
+ try {
142
+ if (!existsSync(path)) return undefined;
143
+ const cache = JSON.parse(readFileSync(path, 'utf8')) as PersistedFetchCache;
144
+ const entry = cache?.schema === 1 ? cache.entries?.[key] : undefined;
145
+ if (!entry || !Number.isFinite(entry.savedAt) || nowMs - entry.savedAt < 0 || nowMs - entry.savedAt >= FETCH_CACHE_TTL_MS) return undefined;
146
+ if (!Array.isArray(entry.result?.prs) || entry.result.stopReason !== undefined) return undefined;
147
+ return { ...entry.result, calls: 0 };
148
+ } catch { return undefined; }
149
+ }
150
+
151
+ function cacheWrite(path: string, key: string, result: MergedChecklistFetch, nowMs: number): void {
152
+ try {
153
+ mkdirSync(dirname(path), { recursive: true });
154
+ let entries: PersistedFetchCache['entries'] = {};
155
+ try {
156
+ const cache = JSON.parse(readFileSync(path, 'utf8')) as PersistedFetchCache;
157
+ if (cache?.schema === 1 && cache.entries && typeof cache.entries === 'object') entries = cache.entries;
158
+ } catch { /* replace an unreadable cache with a fresh one */ }
159
+ for (const [entryKey, entry] of Object.entries(entries)) if (!Number.isFinite(entry.savedAt) || nowMs - entry.savedAt >= FETCH_CACHE_TTL_MS) delete entries[entryKey];
160
+ entries[key] = { savedAt: nowMs, result };
161
+ const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
162
+ writeFileSync(temp, `${JSON.stringify({ schema: 1, entries } satisfies PersistedFetchCache)}\n`, { mode: 0o600 });
163
+ renameSync(temp, path);
164
+ } catch { /* cache is an optimization; a failed write must not fail the read-only command */ }
165
+ }
166
+
125
167
  /**
126
168
  * Merged PRs since `since` (UTC date). One call answers when it returns fewer than the cap. Otherwise the range
127
169
  * splits into UTC-day windows fetched with a concurrency cap; a full window is halved down to one second as before,
128
170
  * and a full one-second window probes one extra row to tell exactly 400 from truncation.
171
+ * With `timeBudgetMs`, each gh request is bounded by the remaining time; a slow initial request or window returns the
172
+ * partial result with `stopReason: 'time-budget-exceeded'` and UTC window starts in `remainingWindows`.
129
173
  * The returned set and the truncation flag match the former sequential bisection; only call shape and time differ.
174
+ * A completed result is cached across CLI processes for ten minutes; a partial (`stopReason`) result is never cached.
130
175
  */
131
176
  export async function fetchMergedChecklistPrs(since: string, now: Date, run: MergedPrRunner, cwd: string,
132
- { concurrency = MERGED_PR_CONCURRENCY }: { concurrency?: number } = {}): Promise<{ prs: MergedChecklistPr[]; truncated: boolean; calls: number }> {
177
+ { concurrency = MERGED_PR_CONCURRENCY, timeBudgetMs, cacheFile }: { concurrency?: number; timeBudgetMs?: number; cacheFile?: string } = {}): Promise<MergedChecklistFetch> {
178
+ const cacheKey = JSON.stringify([since, now.toISOString().slice(0, 10), cwd, concurrency,
179
+ process.env.GH_REPO ?? process.env.GITHUB_REPOSITORY ?? '']);
180
+ const cached = cacheFile ? cacheRead(cacheFile, cacheKey, Date.now()) : undefined;
181
+ if (cached) return cached;
182
+ const deadline = timeBudgetMs === undefined ? undefined : performance.now() + timeBudgetMs;
183
+ const over = (): boolean => deadline !== undefined && performance.now() >= deadline;
184
+ const cacheable = (result: MergedChecklistFetch): MergedChecklistFetch => {
185
+ if (result.stopReason !== undefined || !cacheFile) return result;
186
+ cacheWrite(cacheFile, cacheKey, result, Date.now());
187
+ return result;
188
+ };
133
189
  let calls = 0;
134
- const fetch = async (lower: string, upper?: string, limit = MERGED_PR_LIMIT): Promise<MergedChecklistPr[]> => {
190
+ const fetch = async (lower: string, upper?: string, limit = MERGED_PR_LIMIT): Promise<MergedChecklistPr[] | null> => {
135
191
  const search = `merged:>=${lower}${upper ? ` merged:<${upper}` : ''}`;
192
+ if (over()) return null;
136
193
  calls++;
137
- const r = await run('gh', ['pr', 'list', '--state', 'merged', '--search', search, '--limit', String(limit), '--json', 'number,title,body,mergedAt'], cwd);
194
+ const args = ['pr', 'list', '--state', 'merged', '--search', search, '--limit', String(limit), '--json', 'number,title,body,mergedAt'];
195
+ const remainingMs = deadline === undefined ? undefined : Math.max(1, Math.ceil(deadline - performance.now()));
196
+ let timer: ReturnType<typeof setTimeout> | undefined;
197
+ const request = run('gh', args, cwd, remainingMs);
198
+ let r: MergedPrRunResult | null;
199
+ try {
200
+ r = remainingMs === undefined ? await request : await Promise.race([
201
+ request,
202
+ new Promise<null>((resolve) => { timer = setTimeout(() => resolve(null), remainingMs); }),
203
+ ]);
204
+ } finally { if (timer !== undefined) clearTimeout(timer); }
205
+ if (r === null || r.timedOut) return null;
138
206
  if (r.status !== 0) throw new Error(`병합 PR 조회(gh) 실패 rc=${r.status}: ${(r.stderr || r.stdout).trim().slice(0, 400)}`);
139
207
  const page = JSON.parse(r.stdout.trim()) as MergedChecklistPr[];
140
208
  if (!Array.isArray(page)) throw new Error('병합 PR 조회(gh): 배열이 아니다');
141
209
  return page;
142
210
  };
211
+ const lower = Date.parse(`${since}T00:00:00Z`);
212
+ const upper = Math.max(lower + 1000, Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));
213
+ const windows: Array<[number, number]> = [];
214
+ for (let start = lower; start < upper; start += DAY_MS) windows.push([start, Math.min(start + DAY_MS, upper)]);
143
215
  const first = await fetch(since);
144
- if (first.length < MERGED_PR_LIMIT) return { prs: first, truncated: false, calls };
216
+ if (first === null) return cacheable({ prs: [], truncated: false, calls, stopReason: 'time-budget-exceeded', remainingWindows: windows.map(([start]) => new Date(start).toISOString()) });
217
+ if (first.length < MERGED_PR_LIMIT) return cacheable({ prs: first, truncated: false, calls, stopReason: undefined, remainingWindows: [] });
145
218
  // Bounded pool: at most `concurrency` gh processes at once; a failure stops new calls and rejects.
146
219
  let active = 0;
147
220
  let failed: unknown;
@@ -155,14 +228,20 @@ export async function fetchMergedChecklistPrs(since: string, now: Date, run: Mer
155
228
  finally { active--; if (failed !== undefined) for (const wake of waiting.splice(0)) wake(); else waiting.shift()?.(); }
156
229
  };
157
230
  let truncated = false;
231
+ const remainingWindows: string[] = [];
158
232
  const visit = async (start: number, end: number): Promise<MergedChecklistPr[]> => {
159
233
  const from = new Date(start).toISOString();
160
234
  const to = new Date(end).toISOString();
161
- const page = await limited(() => fetch(from, to));
235
+ // The budget is checked when the window actually starts (inside the pool), not when `visit` is mapped —
236
+ // every window is mapped at once, so a check here would run before the first fetch resolves.
237
+ const page = await limited(async () => (over() ? null : fetch(from, to)));
238
+ if (page === null) { remainingWindows.push(from); return []; }
162
239
  if (page.length < MERGED_PR_LIMIT) return page;
163
240
  if (end - start <= 1000) {
164
241
  // The second-precision search cannot be subdivided; probe one extra row to distinguish exactly 400 from a real truncation.
242
+ if (over()) { remainingWindows.push(from); return page.slice(0, MERGED_PR_LIMIT); }
165
243
  const probe = await limited(() => fetch(from, to, MERGED_PR_LIMIT + 1));
244
+ if (probe === null) { remainingWindows.push(from); return page.slice(0, MERGED_PR_LIMIT); }
166
245
  if (probe.length > MERGED_PR_LIMIT) truncated = true;
167
246
  return probe.slice(0, MERGED_PR_LIMIT);
168
247
  }
@@ -170,15 +249,17 @@ export async function fetchMergedChecklistPrs(since: string, now: Date, run: Mer
170
249
  const [left, right] = await Promise.all([visit(start, middle), visit(middle, end)]);
171
250
  return [...left, ...right];
172
251
  };
173
- const lower = Date.parse(`${since}T00:00:00Z`);
174
- const upper = Math.max(lower + 1000, Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));
175
- const windows: Array<[number, number]> = [];
176
- for (let start = lower; start < upper; start += DAY_MS) windows.push([start, Math.min(start + DAY_MS, upper)]);
177
252
  const settled = await Promise.allSettled(windows.map(([start, end]) => visit(start, end)));
178
253
  const rejected = settled.find((result): result is PromiseRejectedResult => result.status === 'rejected');
179
254
  if (rejected) throw failed ?? rejected.reason;
180
255
  // Chronological window order, first occurrence wins — the same de-duplication as before.
181
256
  const unique = new Map<number, MergedChecklistPr>();
182
257
  for (const result of settled) for (const pr of (result as PromiseFulfilledResult<MergedChecklistPr[]>).value) if (!unique.has(pr.number)) unique.set(pr.number, pr);
183
- return { prs: [...unique.values()], truncated, calls };
258
+ remainingWindows.sort();
259
+ if (remainingWindows.length) {
260
+ // The broad first page is usable partial evidence if the detailed window walk runs out of budget.
261
+ for (const pr of first) if (!unique.has(pr.number)) unique.set(pr.number, pr);
262
+ truncated = true;
263
+ }
264
+ return cacheable({ prs: [...unique.values()], truncated, calls, stopReason: remainingWindows.length ? 'time-budget-exceeded' : undefined, remainingWindows });
184
265
  }
package/src/repl/index.ts CHANGED
@@ -211,6 +211,14 @@ function buildReplTools(): {
211
211
  searchPlannerState: planner,
212
212
  turnIndex: undefined,
213
213
  ptyDashboardOn: false,
214
+ // Match the CLI agent path: native Agent fallback needs the parent's
215
+ // catalog and dispatcher so children can actually use inherited tools.
216
+ agentHostTools: specs,
217
+ agentDispatchTool: dispatch,
218
+ buildChildToolCatalog: (childCwd: string) => {
219
+ const cli = require('../cli/agent-cli.js') as typeof import('../cli/agent-cli.js');
220
+ return cli.buildCliAgentTools(undefined, undefined, childCwd);
221
+ },
214
222
  getToolRuntime: () => undefined,
215
223
  dispatchToolRuntime: async (n) => ({ error: `runtime unavailable in REPL: ${n}` }),
216
224
  dispatchPluginTool: async (n) => ({ ok: false as const, error: `plugin unavailable in REPL: ${n}` }),
@@ -134,6 +134,7 @@ export async function seatReport(seat: string, deps: SeatReportDeps = {}): Promi
134
134
  const [{ resolveRepositoryName }, { githubDraftSweepAdapters }] = await Promise.all([
135
135
  import('../harness/repository-name.js'), import('../harness/harness-cli-command.js'),
136
136
  ]);
137
+ // The collector folds repeated PR pages before this report formats the census.
137
138
  return collectDraftMetrics(resolveRepositoryName({}), githubDraftSweepAdapters(), clock);
138
139
  }))(now);
139
140
  draftDetail = ` · draft 재고 ${metrics.inventory} · 최장 나이 ${metrics.oldestAgeHours === null ? '해당 없음' : `${metrics.oldestAgeHours.toFixed(1)}h`} · needs-owner ${metrics.needsOwner} · 48h 전환율 ${metrics.conversion48h === null ? '표본 없음' : `${(metrics.conversion48h * 100).toFixed(1)}% (${metrics.converted48h}/${metrics.cohort48h})`}`;