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
@@ -0,0 +1,158 @@
1
+ /**
2
+ * MAPREDUCE-FANOUT backend. Inventory and card execution are ports so the CLI can
3
+ * bind the existing checklist/draft readers and tasks hand Pod launcher separately.
4
+ * A run snapshots its inventory before any card starts; one terminal row belongs
5
+ * to each item. This module never changes a checklist or closes a PR.
6
+ */
7
+ import { randomUUID } from 'node:crypto';
8
+ import { debug } from '../debug/log.js';
9
+ import {
10
+ appendMapRunRow, createMapRun, readMapRun, reduceMapRun,
11
+ type MapRecipe, type MapRowKind, type MapRunRow, type MapRunStart,
12
+ } from './map-run-ledger.js';
13
+
14
+ export type MapSource = 'landed-but-yellow' | 'drafts';
15
+ export interface MapItem { id: string }
16
+ export interface MapInventory<T extends MapItem> {
17
+ items: readonly T[];
18
+ complete: boolean;
19
+ snapshotTime: string;
20
+ snapshotRef: string;
21
+ recipeSha: string;
22
+ mainSha: string;
23
+ checklistSnapshotPath: string | null;
24
+ requestedPool: string;
25
+ selectedPool: string | null;
26
+ imageCommit: string | null;
27
+ /** A failed inventory is recorded as a suppressed HOLD row. */
28
+ reason?: string;
29
+ }
30
+ export interface MapCard {
31
+ id: string;
32
+ itemId: string;
33
+ mapRunId: string;
34
+ recipe: MapRecipe;
35
+ text: string;
36
+ }
37
+ export interface MapContext {
38
+ mapRunId: string;
39
+ recipe: MapRecipe;
40
+ source: MapSource;
41
+ dryRun: boolean;
42
+ }
43
+ export interface MapPorts<T extends MapItem> {
44
+ inventory: (recipe: MapRecipe, source: MapSource, limit: number | undefined) => Promise<MapInventory<T>>;
45
+ /** Pure card authoring. Do not persist or launch here. */
46
+ buildCard: (item: T, context: MapContext) => MapCard;
47
+ /** Production adapter must launch through tasks hand and wait for its terminal recipe result. */
48
+ launchCard: (card: MapCard, item: T, context: MapContext) => Promise<MapRunRow>;
49
+ /** Optional pure preview; default dry-run yields a HOLD without invoking launchCard. */
50
+ previewCard?: (card: MapCard, item: T, context: MapContext) => MapRunRow | Promise<MapRunRow>;
51
+ log?: (event: string, data: Record<string, unknown>) => void;
52
+ }
53
+ export interface MapRequest {
54
+ recipe: MapRecipe;
55
+ source: MapSource;
56
+ limit?: number;
57
+ concurrency?: number;
58
+ dryRun?: boolean;
59
+ root?: string;
60
+ mapRunId?: string;
61
+ }
62
+ export interface MapResult { mapRunId: string; expected: number; observed: number; dryRun: boolean }
63
+
64
+ const validPairs: Readonly<Record<MapRecipe, MapSource>> = {
65
+ 'green-close': 'landed-but-yellow', 'draft-close': 'drafts',
66
+ };
67
+ const idPattern = /^[A-Za-z0-9][A-Za-z0-9_-]{0,95}$/;
68
+
69
+ function checkPositive(value: number | undefined, label: string): void {
70
+ if (value !== undefined && (!Number.isSafeInteger(value) || value < 1)) throw new Error(`map ${label} must be a positive integer`);
71
+ }
72
+ function hold(context: MapContext, itemId: string | null, reason: string, suppressed = false): MapRunRow {
73
+ return { event: 'row', mapRunId: context.mapRunId, itemId, recipe: context.recipe,
74
+ kind: 'hold', verdict: 'hold', reason, evidence: [], ...(suppressed ? { rowsSuppressed: true, pr: null } : {}) };
75
+ }
76
+ function record(log: MapPorts<MapItem>['log'], event: string, data: Record<string, unknown>): void {
77
+ try { (log ?? ((name, fields) => debug.log('task-agent.map-reduce', name, fields)))(event, data); }
78
+ catch { /* diagnostics must not turn a completed item into a failed one */ }
79
+ }
80
+
81
+ /** Bounded workers, with no unbounded Promise.all fanout. Default is a read-only preview. */
82
+ export async function mapInventory<T extends MapItem>(request: MapRequest, ports: MapPorts<T>): Promise<MapResult> {
83
+ if (validPairs[request.recipe] !== request.source) throw new Error('map recipe/source mismatch');
84
+ checkPositive(request.limit, 'limit');
85
+ checkPositive(request.concurrency, 'concurrency');
86
+ const concurrency = request.concurrency ?? 2;
87
+ const dryRun = request.dryRun !== false;
88
+ const mapRunId = request.mapRunId ?? `map-${randomUUID()}`;
89
+ if (!idPattern.test(mapRunId)) throw new Error('map invalid run id');
90
+ const context: MapContext = { mapRunId, recipe: request.recipe, source: request.source, dryRun };
91
+ const inventory = await ports.inventory(request.recipe, request.source, request.limit);
92
+ // A limit is a selection boundary, not permission to silently truncate a partial inventory.
93
+ const items = inventory.complete ? inventory.items.slice(0, request.limit) : [];
94
+ const start: MapRunStart = { event: 'start', mapRunId, recipe: request.recipe,
95
+ recipeSha: inventory.recipeSha, snapshotTime: inventory.snapshotTime, snapshotRef: inventory.snapshotRef,
96
+ mainSha: inventory.mainSha, checklistSnapshotPath: inventory.checklistSnapshotPath,
97
+ inventoryComplete: inventory.complete, itemIds: items.map((item) => item.id),
98
+ requestedPool: inventory.requestedPool, selectedPool: inventory.selectedPool,
99
+ imageCommit: inventory.imageCommit, dryRun };
100
+ createMapRun(start, request.root);
101
+ record(ports.log, 'start', { mapRunId, recipe: request.recipe, source: request.source, count: items.length, concurrency, dryRun });
102
+ if (!inventory.complete) {
103
+ appendMapRunRow(hold(context, null, inventory.reason || 'inventory-incomplete', true), request.root);
104
+ record(ports.log, 'inventory-hold', { mapRunId, reason: inventory.reason ?? 'inventory-incomplete' });
105
+ return { mapRunId, expected: 0, observed: 1, dryRun };
106
+ }
107
+ let next = 0;
108
+ let observed = 0;
109
+ async function worker(): Promise<void> {
110
+ while (next < items.length) {
111
+ const item = items[next++]!;
112
+ let row: MapRunRow;
113
+ try {
114
+ const card = ports.buildCard(item, context);
115
+ if (!card || !idPattern.test(card.id) || card.itemId !== item.id || card.mapRunId !== mapRunId
116
+ || card.recipe !== request.recipe || !card.text.trim()) throw new Error('card identity mismatch');
117
+ row = dryRun
118
+ ? ports.previewCard ? await ports.previewCard(card, item, context) : hold(context, item.id, 'dry-run-preview')
119
+ : await ports.launchCard(card, item, context);
120
+ if (row.mapRunId !== mapRunId || row.itemId !== item.id || row.recipe !== request.recipe)
121
+ throw new Error('result identity mismatch');
122
+ row = { ...row, result: { ...row.result, cardId: card.id } };
123
+ } catch (error) {
124
+ row = hold(context, item.id, `map-item-failed: ${error instanceof Error ? error.message : String(error)}`);
125
+ }
126
+ // The ledger validates recipe-specific evidence and rejects duplicate/unknown rows.
127
+ // An invalid result is a hard error; never launder it into a successful map run.
128
+ appendMapRunRow(row, request.root);
129
+ observed++;
130
+ record(ports.log, 'item', { mapRunId, itemId: item.id, kind: row.kind, reason: row.reason });
131
+ }
132
+ }
133
+ await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
134
+ record(ports.log, 'finished', { mapRunId, expected: items.length, observed });
135
+ return { mapRunId, expected: items.length, observed, dryRun };
136
+ }
137
+
138
+ export interface MapReduceTable { mapRunId: string; markdown: string; writes: 0 }
139
+ const countKinds: readonly MapRowKind[] = ['green-candidate', 'mock-pr', 'implementation-remains', 'harvest', 'close-candidate', 'hold'];
140
+ function cell(value: unknown): string { return String(value ?? '').replaceAll('|', '\\|').replaceAll(/\r?\n/g, ' '); }
141
+
142
+ /** Read-only aggregation. Applying green/close candidates belongs to a separate guarded reducer. */
143
+ export function renderMapReduceTable(mapRunId: string, root?: string): MapReduceTable {
144
+ const { start, rows } = readMapRun(mapRunId, root);
145
+ const summary = reduceMapRun(mapRunId, root);
146
+ const lines = [
147
+ `# Map run ${cell(mapRunId)}`,
148
+ '',
149
+ `Recipe: ${start.recipe} · snapshot: ${cell(start.snapshotRef)} · dry-run: ${start.dryRun} · verdict: ${summary.verdict} (${summary.reason})`,
150
+ `Expected: ${summary.expected} · observed: ${summary.observed} · writes: 0`,
151
+ `Counts: ${countKinds.map((kind) => `${kind} ${summary.counts[kind]}`).join(' · ')}`,
152
+ '',
153
+ '| Item | Card | Result | PR | Reason |',
154
+ '| --- | --- | --- | --- | --- |',
155
+ ...rows.map((row) => `| ${cell(row.itemId ?? 'inventory')} | ${cell(row.result?.cardId)} | ${row.kind} | ${cell(row.pr)} | ${cell(row.reason)} |`),
156
+ ];
157
+ return { mapRunId, markdown: `${lines.join('\n')}\n`, writes: 0 };
158
+ }
@@ -0,0 +1,226 @@
1
+ /**
2
+ * MAPREDUCE-FANOUT: one immutable inventory snapshot, followed by one result per item.
3
+ * The reducer is read-only. A partial inventory or missing/duplicate result is a hold,
4
+ * never evidence for a checklist change or a draft close.
5
+ */
6
+ import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, writeSync } from 'node:fs';
7
+ import { dirname, isAbsolute, join } from 'node:path';
8
+ import { effectiveInstanceRoot } from '../instance/resolve.js';
9
+ import { withFileLockSync } from '../storage/file-lock.js';
10
+
11
+ export type MapRecipe = 'green-close' | 'draft-close';
12
+ export type MapRowKind = 'green-candidate' | 'mock-pr' | 'implementation-remains'
13
+ | 'harvest' | 'close-candidate' | 'hold';
14
+ export type MapEvidenceVerdict = 'pass' | 'fail' | 'hold' | 'unmeasured';
15
+
16
+ export interface MapRunStart {
17
+ event: 'start';
18
+ mapRunId: string;
19
+ recipe: MapRecipe;
20
+ recipeSha: string;
21
+ snapshotTime: string;
22
+ snapshotRef: string;
23
+ /** Exact origin/main captured with the inventory, independent of the image or recipe commit. */
24
+ mainSha: string;
25
+ /** The checklist snapshot artifact used by green-close; draft-close records null. */
26
+ checklistSnapshotPath: string | null;
27
+ inventoryComplete: boolean;
28
+ /** Stable identities from the complete inventory; item display titles are never keys. */
29
+ itemIds: string[];
30
+ requestedPool: string;
31
+ selectedPool: string | null;
32
+ imageCommit: string | null;
33
+ dryRun: boolean;
34
+ }
35
+
36
+ export interface MapRunRow {
37
+ event: 'row';
38
+ mapRunId: string;
39
+ itemId: string | null;
40
+ recipe: MapRecipe;
41
+ kind: MapRowKind;
42
+ verdict: MapRowKind;
43
+ reason: string;
44
+ evidence: string[];
45
+ e1?: { verdict: MapEvidenceVerdict; mergeSha?: string; mainAncestor?: boolean };
46
+ e2?: { verdict: MapEvidenceVerdict; command?: string; exitCode?: number };
47
+ e2Source?: 'completion-boundary' | 'prior-evidence' | 'missing';
48
+ e3?: { verdict: MapEvidenceVerdict; reason: string };
49
+ /** Only an inventory-wide failure uses this form; it suppresses all item rows and requires pr:null. */
50
+ rowsSuppressed?: boolean;
51
+ pr?: number | null;
52
+ prUrl?: string | null;
53
+ headOid?: string | null;
54
+ podJob?: string | null;
55
+ podUid?: string | null;
56
+ startedAt?: string | null;
57
+ endedAt?: string | null;
58
+ rc?: number | null;
59
+ result?: Record<string, unknown>;
60
+ }
61
+
62
+ export interface MapRunRead {
63
+ start: MapRunStart;
64
+ rows: MapRunRow[];
65
+ }
66
+
67
+ export interface MapRunReduce {
68
+ mapRunId: string;
69
+ verdict: 'complete' | 'hold';
70
+ reason: string;
71
+ expected: number;
72
+ observed: number;
73
+ counts: Record<MapRowKind, number>;
74
+ /** A dry-run has no write authority, even when every item is a candidate. */
75
+ writes: 0;
76
+ }
77
+
78
+ const ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,95}$/;
79
+ const SHA = /^[0-9a-f]{40}$/;
80
+ const KINDS = new Set<MapRowKind>(['green-candidate', 'mock-pr', 'implementation-remains', 'harvest', 'close-candidate', 'hold']);
81
+ const RECIPE_KINDS: Record<MapRecipe, ReadonlySet<MapRowKind>> = {
82
+ 'green-close': new Set(['green-candidate', 'mock-pr', 'implementation-remains', 'hold']),
83
+ 'draft-close': new Set(['harvest', 'close-candidate', 'hold']),
84
+ };
85
+
86
+ function checkId(value: string, label: string): void {
87
+ if (!ID.test(value)) throw new Error(`map-run invalid ${label}: ${value}`);
88
+ }
89
+
90
+ export function mapRunLedgerPath(mapRunId: string, root = effectiveInstanceRoot()): string {
91
+ checkId(mapRunId, 'id');
92
+ return join(root, 'map-runs', `${mapRunId}.jsonl`);
93
+ }
94
+
95
+ function validateStart(start: MapRunStart): void {
96
+ checkId(start.mapRunId, 'id');
97
+ if (start.recipe !== 'green-close' && start.recipe !== 'draft-close') throw new Error('map-run unknown recipe');
98
+ if (!SHA.test(start.recipeSha)) throw new Error('map-run recipe SHA missing');
99
+ if (!start.snapshotTime || !start.snapshotRef) throw new Error('map-run snapshot identity missing');
100
+ if (!SHA.test(start.mainSha) || !('checklistSnapshotPath' in start)
101
+ || (start.recipe === 'green-close' && (!start.checklistSnapshotPath || !isAbsolute(start.checklistSnapshotPath)))
102
+ || (start.recipe === 'draft-close' && start.checklistSnapshotPath !== null)) {
103
+ throw new Error('map-run main or checklist snapshot provenance missing');
104
+ }
105
+ if (!Array.isArray(start.itemIds) || start.itemIds.some((id) => typeof id !== 'string' || !ID.test(id))
106
+ || new Set(start.itemIds).size !== start.itemIds.length) throw new Error('map-run item identities missing or duplicate');
107
+ if (typeof start.inventoryComplete !== 'boolean' || typeof start.dryRun !== 'boolean') throw new Error('map-run completeness or dry-run missing');
108
+ if (!start.inventoryComplete && start.itemIds.length) throw new Error('map-run partial inventory cannot name items');
109
+ if (!start.requestedPool || !('selectedPool' in start) || !('imageCommit' in start)) throw new Error('map-run pool or image provenance missing');
110
+ }
111
+
112
+ function validateRow(row: MapRunRow, start: MapRunStart): void {
113
+ if (row.mapRunId !== start.mapRunId || row.recipe !== start.recipe || !KINDS.has(row.kind) || row.verdict !== row.kind
114
+ || !Array.isArray(row.evidence)
115
+ || row.evidence.some((value) => typeof value !== 'string') || typeof row.reason !== 'string') {
116
+ throw new Error('map-run row contract mismatch');
117
+ }
118
+ if (!RECIPE_KINDS[start.recipe].has(row.kind)) throw new Error('map-run kind outside recipe');
119
+ if (row.rowsSuppressed) {
120
+ if (start.inventoryComplete || row.itemId !== null || row.pr !== null || row.kind !== 'hold') {
121
+ throw new Error('map-run suppressed row must hold an incomplete inventory without a PR');
122
+ }
123
+ } else if (!start.inventoryComplete) {
124
+ throw new Error('map-run incomplete inventory suppresses item rows');
125
+ } else if (!row.itemId || !start.itemIds.includes(row.itemId)) {
126
+ throw new Error(`map-run unknown item: ${row.itemId}`);
127
+ }
128
+ if (row.kind === 'hold' && !row.reason.trim()) throw new Error('map-run hold needs a reason');
129
+ if (row.kind !== 'hold' && row.evidence.length === 0) throw new Error('map-run candidate needs evidence');
130
+ if (row.kind === 'green-candidate'
131
+ && (row.e1?.verdict !== 'pass' || row.e2?.verdict !== 'pass' || row.e3?.verdict !== 'pass'
132
+ || !SHA.test(row.e1.mergeSha ?? '') || row.e1.mainAncestor !== true
133
+ || !row.e3.reason.trim()
134
+ || typeof row.pr !== 'number' || !Number.isSafeInteger(row.pr) || row.pr <= 0
135
+ || !row.prUrl || !row.e2Source || row.e2Source === 'missing'
136
+ || !row.e2.command?.trim() || row.e2.exitCode !== 0)) {
137
+ throw new Error('map-run green candidate needs E1/E2/E3 and PR proof');
138
+ }
139
+ if ((row.kind === 'harvest' || row.kind === 'close-candidate')
140
+ && (typeof row.pr !== 'number' || !Number.isSafeInteger(row.pr) || row.pr <= 0 || !SHA.test(row.headOid ?? ''))) {
141
+ throw new Error('map-run draft decision needs exact PR head');
142
+ }
143
+ if (row.rc != null && row.rc !== 0 && row.kind !== 'hold') throw new Error('map-run failed Pod cannot yield a candidate');
144
+ for (const field of ['podJob', 'podUid', 'startedAt', 'endedAt'] as const) {
145
+ const value = row[field];
146
+ if (value != null && (typeof value !== 'string' || !value.trim())) throw new Error(`map-run invalid ${field}`);
147
+ }
148
+ if (row.result != null && (typeof row.result !== 'object' || Array.isArray(row.result))) {
149
+ throw new Error('map-run invalid result');
150
+ }
151
+ }
152
+
153
+ /** Exclusive create prevents a retry from silently replacing the original inventory. */
154
+ export function createMapRun(start: MapRunStart, root?: string): string {
155
+ validateStart(start);
156
+ const path = mapRunLedgerPath(start.mapRunId, root);
157
+ mkdirSync(dirname(path), { recursive: true });
158
+ const fd = openSync(path, 'wx', 0o600);
159
+ try { writeSync(fd, `${JSON.stringify(start)}\n`); }
160
+ finally { closeSync(fd); }
161
+ return path;
162
+ }
163
+
164
+ export function readMapRun(mapRunId: string, root?: string): MapRunRead {
165
+ const path = mapRunLedgerPath(mapRunId, root);
166
+ if (!existsSync(path)) throw new Error(`map-run missing: ${mapRunId}`);
167
+ const raw = readFileSync(path, 'utf8');
168
+ if (!raw.endsWith('\n')) throw new Error('map-run truncated JSONL tail');
169
+ const lines = raw.trimEnd().split('\n');
170
+ let start: MapRunStart;
171
+ try { start = JSON.parse(lines[0]!) as MapRunStart; }
172
+ catch { throw new Error('map-run invalid start JSON'); }
173
+ if (start.event !== 'start' || start.mapRunId !== mapRunId) throw new Error('map-run invalid start');
174
+ validateStart(start);
175
+ const rows: MapRunRow[] = [];
176
+ const seen = new Set<string>();
177
+ for (const line of lines.slice(1)) {
178
+ let row: MapRunRow;
179
+ try { row = JSON.parse(line) as MapRunRow; }
180
+ catch { throw new Error('map-run invalid row JSON'); }
181
+ if (row.event !== 'row') throw new Error('map-run unexpected event');
182
+ validateRow(row, start);
183
+ const key = row.rowsSuppressed ? '__inventory_hold__' : row.itemId!;
184
+ if (seen.has(key)) throw new Error(`map-run duplicate row: ${key}`);
185
+ seen.add(key);
186
+ rows.push(row);
187
+ }
188
+ if (rows.some((row) => row.rowsSuppressed) && rows.length !== 1) throw new Error('map-run suppressed inventory has item rows');
189
+ return { start, rows };
190
+ }
191
+
192
+ /** The lock protects the read-before-append duplicate check across concurrent Pod completions. */
193
+ export function appendMapRunRow(row: MapRunRow, root?: string): void {
194
+ const path = mapRunLedgerPath(row.mapRunId, root);
195
+ withFileLockSync(`${path}.lock`, () => {
196
+ const current = readMapRun(row.mapRunId, root);
197
+ validateRow(row, current.start);
198
+ const key = row.rowsSuppressed ? '__inventory_hold__' : row.itemId!;
199
+ if (current.rows.some((existing) => (existing.rowsSuppressed ? '__inventory_hold__' : existing.itemId) === key)) {
200
+ throw new Error(`map-run duplicate row: ${key}`);
201
+ }
202
+ if (row.rowsSuppressed && current.rows.length) throw new Error('map-run inventory hold cannot follow item rows');
203
+ if (current.rows.some((existing) => existing.rowsSuppressed)) throw new Error('map-run inventory hold suppresses item rows');
204
+ appendFileSync(path, `${JSON.stringify(row)}\n`);
205
+ });
206
+ }
207
+
208
+ /** No consumer of this result can write: incomplete evidence remains an explicit hold. */
209
+ export function reduceMapRun(mapRunId: string, root?: string): MapRunReduce {
210
+ const { start, rows } = readMapRun(mapRunId, root);
211
+ const counts: Record<MapRowKind, number> = {
212
+ 'green-candidate': 0, 'mock-pr': 0, 'implementation-remains': 0,
213
+ harvest: 0, 'close-candidate': 0, hold: 0,
214
+ };
215
+ for (const row of rows) counts[row.kind]++;
216
+ const accounted = start.inventoryComplete && rows.length === start.itemIds.length
217
+ && rows.every((row) => !row.rowsSuppressed);
218
+ const complete = accounted && start.itemIds.length > 0 && counts.hold === 0;
219
+ return {
220
+ mapRunId, verdict: complete ? 'complete' : 'hold',
221
+ reason: !start.inventoryComplete ? 'inventory-incomplete' : start.itemIds.length === 0 ? 'empty-inventory'
222
+ : !accounted ? 'results-missing'
223
+ : counts.hold ? 'item-hold' : 'all-items-accounted',
224
+ expected: start.itemIds.length, observed: rows.length, counts, writes: 0,
225
+ };
226
+ }
@@ -81,7 +81,7 @@ export function resolveParentHosts(config: ParentHostSetting | undefined, env: N
81
81
  }
82
82
 
83
83
  /** Capacity sample is read on the host before launch. Unknown or saturated capacity is not a positive weight. */
84
- export interface ParentHostCapacity { approvalHeadroom: number; load: number }
84
+ export interface ParentHostCapacity { active: number; waiting: number; approvalHeadroom: number; load: number }
85
85
 
86
86
  export interface ParentHostPodFit {
87
87
  state: 'fits' | 'never-fits' | 'unmeasured';
@@ -127,26 +127,35 @@ export function readParentHostPodFit(target: ParentHostConfig, memoryLimit: stri
127
127
  }
128
128
 
129
129
  const CAPACITY_MARKER = '__ELANOUS_PARENT_CAPACITY__';
130
+ const CAPACITY_ERROR_MARKER = '__ELANOUS_PARENT_CAPACITY_ERROR__';
130
131
  const APPROVAL_MARKER = '__ELANOUS_PARENT_APPROVALS__';
131
132
  const LOAD_MARKER = '__ELANOUS_PARENT_LOAD__';
133
+ /** Best-effort macOS `ps` fallback; Linux/WSL uses argv boundaries below. */
134
+ export const PARENT_HOST_ACTIVE_FILTER = String.raw`/^(([^ ]+\/)?(bun|node|nodejs) )?([^ ]+\/)?elanous(\.mjs)?( --config-dir [^ ]+)? harness (say|ask)( |$)/ && / --no-auto-merge( |$)/ && !/ --substrate local( |$)/ && !/^([^ ]+\/)?[^ ]*sh -c/`;
135
+ /** Count exact Linux argv entries so Pod-local commands and goal text cannot masquerade as host parents. */
136
+ export const PARENT_HOST_PROC_COUNT_SCRIPT = "count=0; proc_root=${1:-/proc}; for file in \"$proc_root\"/[0-9]*/cmdline; do [ -r \"$file\" ] || continue; argv=(); while IFS= read -r -d '' arg; do argv+=(\"$arg\"); done < \"$file\" 2>/dev/null || true; [ ${#argv[@]} -ge 3 ] || continue; program=\"${argv[0]##*/}\"; case \"$program\" in elanous|elanous.mjs) ;; bun|node|nodejs) script_match=0; for ((i=1; i<${#argv[@]}; i++)); do if [[ \"${argv[i]}\" == */elanous.mjs ]]; then script_match=1; break; fi; [[ \"${argv[i]}\" == harness ]] && break; done; [ \"$script_match\" -eq 1 ] || continue ;; *) continue ;; esac; harness=0; for ((i=0; i+1<${#argv[@]}; i++)); do if [[ \"${argv[i]}\" == harness && ( \"${argv[i+1]}\" == say || \"${argv[i+1]}\" == ask ) ]]; then harness=1; break; fi; done; [ \"$harness\" -eq 1 ] || continue; has_no_auto_merge=0; for arg in \"${argv[@]}\"; do [[ \"$arg\" == --no-auto-merge ]] && has_no_auto_merge=1; done; [ \"$has_no_auto_merge\" -eq 1 ] || continue; local_substrate=0; for ((i=0; i+1<${#argv[@]}; i++)); do if [[ \"${argv[i]}\" == --substrate && \"${argv[i+1]}\" == local ]]; then local_substrate=1; break; fi; done; [ \"$local_substrate\" -eq 1 ] || count=$((count+1)); done; printf '__ELANOUS_PARENT_CAPACITY__%s\\n' \"$count\""
132
137
  /** Recent admission decisions are the host's measured approval backlog, not an inferred process count. */
133
138
  export function readParentHostCapacity(target: ParentHostConfig, ssh: SshRunner = defaultSshRunner): ParentHostCapacity | null {
134
139
  const executable = remotePath(target.elanous ?? DEFAULT_REMOTE_ELANOUS);
135
140
  const configDir = remotePath(target.configDir ?? DEFAULT_REMOTE_CONFIG_DIR);
136
- const script = `parents=$(ps -Ao command) || exit 1; printf '%s\\n' "$parents" | awk '/[e]lanous.*harness (say|ask)/ { n++ } END { print "${CAPACITY_MARKER}" n+0 }'; `
141
+ const unavailable = (reason: string): null => { try { debug.log('task-agent.parent-host', 'capacity-unavailable', { host: target.host, reason }); } catch { /* fail-soft */ } return null; };
142
+ const processCount = `if [ -r /proc/self/cmdline ]; then if command -v bash >/dev/null 2>&1; then bash -c ${shellQuote(PARENT_HOST_PROC_COUNT_SCRIPT)} -- /proc; else printf "${CAPACITY_ERROR_MARKER}bash-unavailable\\n"; exit 1; fi; else parents=$(ps -Ao command) || exit 1; printf '%s\\n' "$parents" | awk '${PARENT_HOST_ACTIVE_FILTER} { n++ } END { print "${CAPACITY_MARKER}" n+0 }'; fi`;
143
+ const script = `${processCount}; `
137
144
  + `${executable} --config-dir ${configDir} logs --event admit-by-usage --since 5m --json --limit 1000 || exit 1; `
138
145
  + `printf '\\n${APPROVAL_MARKER}\\n'; `
139
146
  + `cpu=$(ps -Ao %cpu=) || exit 1; printf '%s\\n' "$cpu" | awk '{ total += $1 } END { printf "${LOAD_MARKER}%.3f\\n", total+0 }'; `
140
147
  + `cores=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null) || exit 1; printf '${LOAD_MARKER}cores=%s\\n' "$cores"`;
141
148
  try {
142
149
  const result = ssh(target.host, script, 15_000);
143
- if (result.status !== 0) return null;
150
+ if (result.status !== 0) return unavailable(result.stdout.includes(`${CAPACITY_ERROR_MARKER}bash-unavailable`) ? 'bash-unavailable' : 'remote-command-failed');
144
151
  const activeMatch = new RegExp(`^${CAPACITY_MARKER}(\\d+)$`, 'm').exec(result.stdout);
145
152
  const boundary = result.stdout.indexOf(`\n${APPROVAL_MARKER}\n`);
146
153
  const logsStart = result.stdout.indexOf('\n');
147
154
  const cpuMatch = new RegExp(`^${LOAD_MARKER}([0-9]+(?:\\.[0-9]+)?)$`, 'm').exec(result.stdout);
148
155
  const coresMatch = new RegExp(`^${LOAD_MARKER}cores=(\\d+)$`, 'm').exec(result.stdout);
149
- if (!activeMatch || activeMatch.index !== 0 || logsStart < 0 || boundary < logsStart || !cpuMatch || !coresMatch) return null;
156
+ if (!activeMatch || activeMatch.index !== 0) return unavailable('active-count-marker-missing');
157
+ if (logsStart < 0 || boundary < logsStart) return unavailable('approval-boundary-missing');
158
+ if (!cpuMatch || !coresMatch) return unavailable('load-marker-missing');
150
159
  const active = Number(activeMatch[1]);
151
160
  const cores = Number(coresMatch[1]);
152
161
  const cpu = Number(cpuMatch[1]);
@@ -172,23 +181,24 @@ export function readParentHostCapacity(target: ParentHostConfig, ssh: SshRunner
172
181
  decisions.set(data.runId, data.recommended as number);
173
182
  }
174
183
  const waiting = [...decisions.values()].filter((n) => n === 0).length;
175
- return { approvalHeadroom: Math.max(0, capacity - active - waiting), load: Math.max(0, cpu / (cores * 100)) };
176
- } catch { return null; }
184
+ return { active, waiting, approvalHeadroom: Math.max(0, capacity - active - waiting), load: Math.max(0, cpu / (cores * 100)) };
185
+ } catch { return unavailable('capacity-parse-failed'); }
177
186
  }
178
187
 
179
- /** Weighted rendezvous: stable per run, no shared counter or dependence on dispatch order. */
180
- export function orderParentHosts(targets: readonly ParentHostConfig[], runId: string, capacities: Readonly<Record<string, ParentHostCapacity | null>>): ParentHostConfig[] {
188
+ /** Weighted rendezvous rank with evidence fields for launch-time observation. */
189
+ export function rankParentHosts(targets: readonly ParentHostConfig[], runId: string, capacities: Readonly<Record<string, ParentHostCapacity | null>>): Array<{ target: ParentHostConfig; weight: number; score: number }> {
181
190
  return targets.map((target, index) => {
182
191
  const sample = capacities[target.host];
183
192
  const weight = sample && Number.isSafeInteger(sample.approvalHeadroom) && sample.approvalHeadroom > 0 && Number.isFinite(sample.load) && sample.load >= 0
184
193
  ? sample.approvalHeadroom / (1 + sample.load) : 0;
185
194
  const hash = createHash('sha256').update(`${runId}\0${target.host}`).digest();
186
195
  const fraction = (hash.readUIntBE(0, 6) + 1) / (2 ** 48 + 1);
187
- return { target, index, score: weight > 0 ? -Math.log(fraction) / weight : Infinity };
188
- }).sort((a, b) => a.score - b.score || a.index - b.index).map(({ target }) => target);
196
+ return { target, index, weight, score: weight > 0 ? -Math.log(fraction) / weight : Infinity };
197
+ }).sort((a, b) => a.score - b.score || a.index - b.index).map(({ target, weight, score }) => ({ target, weight, score }));
189
198
  }
190
199
 
191
- /** `$HOME/…` 은 원격 셸이 풀도록 따옴표 밖에 둔다 — 나머지는 shellQuote. */
200
+
201
+ /** Keep `$HOME/...` outside quotes so the remote shell expands it; quote every other path. */
192
202
  function remotePath(path: string): string {
193
203
  return path.startsWith('$HOME/') ? `"$HOME"/${shellQuote(path.slice('$HOME/'.length))}` : shellQuote(path);
194
204
  }
@@ -102,6 +102,8 @@ export interface RejudgeDeps {
102
102
  notifyRepair?: (seat: string, body: string) => void;
103
103
  /** Shared new-launch budget for this `tasks rejudge --all` call. */
104
104
  repairBudget?: import('./repair-cycle.js').RepairTickBudget;
105
+ /** Verify a later PR head contains the harvested owner commit (read-only). */
106
+ ownerHarvestAncestor?: (cwd: string, owner: string, head: string) => boolean;
105
107
  /** One review request across all candidates in one --all tick; standalone card rejudge is uncapped. */
106
108
  reviewBudget?: { max: number; requested: number };
107
109
  /** One land or queue attempt across all candidates in one --all tick. */
@@ -346,10 +348,11 @@ const catchUpFail = (reason: string, extra: Partial<CatchUpResult> = {}): CatchU
346
348
  /**
347
349
  * origin/main 을 PR 머리에 3-way merge(rerere 켬) 하고 일반 push 한다.
348
350
  * ⛔ rebase · force-push · reset · 남의 가지 파일째 checkout — 충돌이면 merge --abort.
351
+ * 이전 시도의 worktree 경로가 남았으면 새 경로로 한 번 더 시도한다. 남은 트리를 강제 제거하지 않는다.
349
352
  */
350
353
  export async function defaultCatchUpMain(request: CatchUpRequest, git: CatchUpGit = defaultCatchUpGit): Promise<CatchUpResult> {
351
354
  const repo = request.cwd;
352
- const work = request.worktreePath ?? join(tmpdir(), `ta-catch-up-${request.pr}-${safe(request.head.slice(0, 12))}-${process.pid}`);
355
+ let work = request.worktreePath ?? join(tmpdir(), `ta-catch-up-${request.pr}-${safe(request.head.slice(0, 12))}-${process.pid}`);
353
356
  const gitRepo = (args: string[]) => git(repo, args);
354
357
  const gitWork = (args: string[]) => git(work, args);
355
358
  const fromBase = request.fromBase;
@@ -367,7 +370,12 @@ export async function defaultCatchUpMain(request: CatchUpRequest, git: CatchUpGi
367
370
  return catchUpFail(`fetched PR head ${(fetchedHead ?? '?').slice(0, 12)} ≠ ${request.head.slice(0, 12)}`, { fromBase, toBase });
368
371
  }
369
372
 
370
- const added = gitRepo(['worktree', 'add', '--detach', work, request.head]);
373
+ let added = gitRepo(['worktree', 'add', '--detach', work, request.head]);
374
+ if (added.status !== 0 && /already exists|already registered|already used by worktree/i.test(added.stderr || added.stdout)) {
375
+ // 남은 경로가 살아 있는 다른 시도의 것일 수 있으므로 건드리지 않는다.
376
+ work = `${work}-retry-${randomUUID()}`;
377
+ added = gitRepo(['worktree', 'add', '--detach', work, request.head]);
378
+ }
371
379
  if (added.status !== 0) return catchUpFail(`worktree add failed: ${(added.stderr || added.stdout).trim().slice(-200)}`, { fromBase, toBase });
372
380
  const cleanup = () => { gitRepo(['worktree', 'remove', '--force', work]); };
373
381
 
@@ -495,7 +503,8 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
495
503
  if ('reason' in found) return target('pr-unreadable', found.reason, pr, fromHead);
496
504
  const { cwd, view } = found;
497
505
  const toHead = view.head;
498
- const eligible = !!reviews.length || adopted || (!!card.parentHost && (deps.remoteRunTerminal ?? remoteBoundRunTerminal)(card));
506
+ const harvest = pendingOwnerHarvest(card, pr);
507
+ const eligible = !!reviews.length || adopted || !!harvestReviewPending(card, pr) || (!!card.parentHost && (deps.remoteRunTerminal ?? remoteBoundRunTerminal)(card));
499
508
  const trustedTrain = view.state === 'CLOSED' && card.status === 'launched'
500
509
  ? (() => { try { return (deps.trainLanding ?? verifiedTrainLanding)(pr, cwd); } catch { return null; } })() : null;
501
510
  // An unreviewed or unproven remote card retains the old guard. Only verified train
@@ -543,6 +552,25 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
543
552
  : target('pr-not-open', `PR #${pr} is ${view.state}`, pr, fromHead, toHead);
544
553
  }
545
554
  if (view.state !== 'OPEN') return target('pr-not-open', `PR #${pr} is ${view.state}`, pr, fromHead, toHead);
555
+ if (harvest) {
556
+ const ancestor = deps.ownerHarvestAncestor ?? ((repo: string, owner: string, head: string) => {
557
+ const result = spawnSync('git', ['merge-base', '--is-ancestor', owner, head], { cwd: repo, encoding: 'utf8', timeout: 10_000 });
558
+ return result.status === 0;
559
+ });
560
+ let reached = sameHead(toHead, harvest.head);
561
+ if (!reached) { try { reached = ancestor(cwd, harvest.head, toHead); } catch { reached = false; } }
562
+ if (!reached) {
563
+ emit({ card: card.id, pr, fromHead, toHead, step: 'review', decision: 'awaiting-harvest',
564
+ reason: `PR #${pr} head ${toHead.slice(0, 12)} awaits owner-worktree-commit ${harvest.head.slice(0, 12)} · next: wait for harvest` }, false);
565
+ return outcomes;
566
+ }
567
+ updateTaskCard(statePath, card.id, current => current && cardPrNumber(current) === pr
568
+ && typeof current.pr === 'object' && current.pr.url === url && pendingOwnerHarvest(current, pr)?.head === harvest.head
569
+ && !current.history.some(entry => entry.event === 'review-harvest-reached' && entry.pr === pr
570
+ && entry.runId === current.runId && entry.head && sameHead(entry.head, toHead))
571
+ ? { ...current, history: [...current.history, { at: now().toISOString(), event: 'review-harvest-reached',
572
+ pr, head: toHead, ...(current.runId ? { runId: current.runId } : {}), detail: `PR #${pr} head reached owner-worktree-commit · next: review` }] } : undefined);
573
+ }
546
574
  // An explicitly rejudged PR can be reopened. Clear the old marker before normal head judgement.
547
575
  if (boundPrTerminal(card)) {
548
576
  updateTaskCard(statePath, card.id, (current) => current && cardPrNumber(current) === pr && typeof current.pr === 'object' && current.pr.url === url
@@ -552,6 +580,26 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
552
580
  const step = (s: RejudgeStep, decision: string, reason: string, record: boolean, extra: Partial<RejudgeOutcome> = {}) =>
553
581
  emit({ card: card!.id, pr, fromHead, toHead, step: s, decision, reason, ...extra }, record);
554
582
  const requestReview = async (candidate: TaskCard, reason: string, forceNewReview = false): Promise<void> => {
583
+ // The head used for this decision may have moved while the card was being updated.
584
+ // Never request a review of a new head until it is verified against the owner commit.
585
+ if (harvest) {
586
+ let fresh: PrHeadView | null;
587
+ try { fresh = await (live.prHead ?? defaultPrHead)(pr, cwd); } catch { fresh = null; }
588
+ if (!fresh || fresh.state !== 'OPEN' || !fresh.url || !sameUrl(fresh.url, url)) {
589
+ step('review', 'awaiting-harvest', `PR #${pr} fresh head unavailable · next: wait for harvest`, false);
590
+ return;
591
+ }
592
+ let reached = sameHead(fresh.head, harvest.head);
593
+ if (!reached) {
594
+ try { reached = (deps.ownerHarvestAncestor ?? ((repo: string, owner: string, head: string) =>
595
+ spawnSync('git', ['merge-base', '--is-ancestor', owner, head], { cwd: repo, encoding: 'utf8', timeout: 10_000 }).status === 0))(cwd, harvest.head, fresh.head); }
596
+ catch { reached = false; }
597
+ }
598
+ if (!reached) {
599
+ step('review', 'awaiting-harvest', `PR #${pr} head ${fresh.head.slice(0, 12)} awaits owner-worktree-commit ${harvest.head.slice(0, 12)} · next: wait for harvest`, false);
600
+ return;
601
+ }
602
+ }
555
603
  const budget = deps.reviewBudget;
556
604
  if (budget && budget.requested >= budget.max) {
557
605
  step('review', 'tick-cap', `review request cap ${budget.max} reached — next tick`, false);
@@ -560,9 +608,12 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
560
608
  // Reserve before awaiting the worker so another candidate cannot launch a second request.
561
609
  if (budget) budget.requested++;
562
610
  const before = { reviewRequests: candidate.reviewRequests, landAttempts: candidate.landAttempts };
563
- const result = await executeLiveReview(candidate, pr, { runId: candidate.runId ?? null, stopReason: reason, cwd, forceNewReview }, { ...live, ...(deps.log ? { log: deps.log } : {}) });
611
+ const result = await executeLiveReview(candidate, pr, { runId: candidate.runId ?? null, stopReason: reason, cwd, forceNewReview,
612
+ ...(harvest ? { ownerHarvestHead: harvest.head, ...(deps.ownerHarvestAncestor ? { ownerHarvestAncestor: deps.ownerHarvestAncestor } : {}) } : {}) },
613
+ { ...live, ...(deps.log ? { log: deps.log } : {}) });
564
614
  recordMove(statePath, candidate.id, candidate.runId, 'review', result, pr, before, now, deps.log);
565
- step('review', result.executed ? 'review-requested' : 'review-request-failed', result.detail, true);
615
+ step('review', harvest && !result.executed && result.detail.includes('awaits owner-worktree-commit') ? 'awaiting-harvest'
616
+ : result.executed ? 'review-requested' : 'review-request-failed', result.detail, true);
566
617
  };
567
618
 
568
619
  // Adoption is a first judgement for the current head, even when the implementation run
@@ -938,6 +989,21 @@ function recordMove(statePath: string, cardId: string, runId: string | undefined
938
989
  } catch { /* recordLiveMoveHistory 는 스스로 관측한다 */ }
939
990
  }
940
991
 
992
+ function harvestReviewPending(card: TaskCard, pr: number): boolean {
993
+ if (card.status !== 'launched' || typeof card.pr !== 'object' || card.pr.number !== pr || !card.pr.url || !card.runId) return false;
994
+ const latest = card.history.filter(entry => (entry.event === 'review-skipped-harvest' || entry.event === 'review-harvest-reached')
995
+ && entry.pr === pr && typeof entry.head === 'string' && /^[a-f0-9]{40}$/i.test(entry.head)
996
+ && entry.runId === card.runId).at(-1);
997
+ return !!latest && !(card.reviewRequests ?? []).some(entry => entry.pr === pr && sameHead(entry.head, latest.head!));
998
+ }
999
+
1000
+ function pendingOwnerHarvest(card: TaskCard, pr: number): { head: string } | undefined {
1001
+ if (!harvestReviewPending(card, pr)) return undefined;
1002
+ const latest = card.history.filter(entry => entry.event === 'review-skipped-harvest' && entry.pr === pr
1003
+ && entry.runId === card.runId && typeof entry.head === 'string' && /^[a-f0-9]{40}$/i.test(entry.head)).at(-1);
1004
+ return latest?.effect === 'wait' ? { head: latest.head! } : undefined;
1005
+ }
1006
+
941
1007
  /** 재판정 대상 카드 — 기존 리뷰 또는 HQ 가 종결 원장을 검증할 원격 부모 카드. */
942
1008
  export function rejudgeCandidates(tasks: Readonly<Record<string, TaskCard>>, opts: {
943
1009
  now?: Date; maxAgeHours?: number; onAgeSkip?: (card: TaskCard, ageHours: number | null) => void;
@@ -949,6 +1015,7 @@ export function rejudgeCandidates(tasks: Readonly<Record<string, TaskCard>>, opt
949
1015
  return Object.values(tasks).filter((card) => {
950
1016
  const pr = cardPrNumber(card);
951
1017
  const eligible = pr !== undefined && !boundPrTerminal(card) && ((card.reviewRequests ?? []).some((entry) => entry.pr === pr)
1018
+ || (pr !== undefined && harvestReviewPending(card, pr))
952
1019
  || (card.status === 'launched' && card.taLand === true && ((!!card.parentHost && !!card.runId && !!card.runChildId)
953
1020
  || (card.adoptedPr?.pr === pr && typeof card.pr === 'object' && !!card.pr.url && sameUrl(card.adoptedPr.url, card.pr.url)))));
954
1021
  if (!eligible) return false;