elanous 0.2.35 → 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 (827) 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/{T1mBQqmkwKDLGtyisDCOd → 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/{9952.5daf5f94063a2b3e.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/1746.3563617ea64023a3.js +115 -0
  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/{1dc5a567.9b092bfae3d7099b.js → 1ed5e8af.cf424815c7c0efad.js} +1 -1
  16. package/apps/pwa/out/_next/static/chunks/{4705-194e64e47ca0fbdb.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/{13.02a07436dec15f89.js → 210.90b2cbd64926cf34.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/{9512.5a76207a730e0491.js → 2283.edd9627b7bdfb546.js} +2 -2
  20. package/apps/pwa/out/_next/static/chunks/{6866-79aa5b4e47cac6aa.js → 2318-450e75d07605b8fe.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/{938.66ad1ef0e5abae1c.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/3199.6a1a9b0e6cd01dc6.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/3212.12179fd3eee8cef1.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/{9214.63a510d39178e997.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/{2018.a741297bce759490.js → 4313.76f232c304a15bde.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/{1165.bad13fc9151a7288.js → 4324.c99ba3e83eb1b691.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/{3f7c4ee5.3dde54f01a816613.js → 4337e36d.f42c3b391b3d8f20.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/{7385.50a308a477bdfc21.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/{8557-e7898769499cd56f.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/{5483-dd5d7697f6bdb106.js → 4833-7934c592c5569f00.js} +1 -1
  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/{8444.4c26b379ca2b9596.js → 4931.95cc6dbd230774ca.js} +17 -17
  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/{3408.21d1570602f4b6c0.js → 5046.7fd5cbd859299448.js} +1 -1
  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/58edce7d.f1b0c72acf13b123.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5925.3b1f8b135710249b.js +93 -0
  65. package/apps/pwa/out/_next/static/chunks/5952.294fc574a8625184.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/{b6705a4f-b9a9047de9eecfd0.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/6252.240578f10952e091.js +34 -0
  69. package/apps/pwa/out/_next/static/chunks/6416.5d50b8ec9986ca98.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/{5101.e32282718f99ed6f.js → 6680.6a693c28aae4cd19.js} +1 -1
  71. package/apps/pwa/out/_next/static/chunks/{4718-17d2af636796a330.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/{6236.e18cced2d5ba6afb.js → 6819.be5c9c4d67f22922.js} +16 -16
  75. package/apps/pwa/out/_next/static/chunks/6877-84d47b7e85d0c41e.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/{2833-0fe47af99ed13c29.js → 6971-fb5f9dc0a2cb5d2d.js} +1 -1
  77. package/apps/pwa/out/_next/static/chunks/{da5176c6.6773b6c5994e1d99.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/7424.c0fe2a595d4232b5.js +23 -0
  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/{7644.17e37ba6f1d4226e.js → 7867.e8725745ddd1d940.js} +4 -4
  93. package/apps/pwa/out/_next/static/chunks/7893-e444a1b0809cd1e4.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7956-33748cabb3a05562.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/{0368f25d-ad84af92fefee68f.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/{359-2250789a2d3499e7.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/{b2f6ba4d.fb56fdbbf798df33.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/{1530.0e612369b6fd7ea0.js → 9151.a2c51e93f9156dae.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/{4441-875b97f5821ed58a.js → 9175-7d383df15a82e2be.js} +1 -1
  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/958.53a8dcf4729e6997.js +64 -0
  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-3337c6a1328c2674.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/live-run/page-89acd5d68c0bc560.js +1 -0
  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-fe986fe79b107546.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/morning/{page-47c5d4db587266bd.js → page-1e5da297557d2354.js} +1 -1
  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-1f5a60f70cb4dd0c.js → page-88a1edbff62eaf1d.js} +1 -1
  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-bac7dee94e5eae4a.js → page-443364c9e4a23b31.js} +1 -1
  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-dc0a532f5b9196ec.js → page-ac2c1e6169cfce57.js} +1 -1
  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/{d06f9160.a3d9062f95de8ad7.js → d9815f84.4918f682a45a8502.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/{cbe38a15.5a032598e39970c6.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/docs/goal-context/05-authoring-lessons.jsonl +47 -0
  279. package/docs/goal-context/README.md +3 -0
  280. package/graphs/default-loop.yaml +6 -1
  281. package/graphs/drafts/recipes.yaml +27 -7
  282. package/graphs/implement-loop.yaml +24 -0
  283. package/graphs/release/recipes.yaml +6 -0
  284. package/graphs/release/release-loop.yaml +17 -4
  285. package/graphs/wizard/recipes.yaml +44 -0
  286. package/graphs/wizard/wizard-loop.yaml +44 -0
  287. package/graphs/work/draft-close.yaml +54 -0
  288. package/graphs/work/green-close.yaml +37 -0
  289. package/graphs/work/recipes.yaml +46 -1
  290. package/graphs/work/research-report.yaml +2 -0
  291. package/graphs/work/run-research.yaml +35 -0
  292. package/package.json +1 -1
  293. package/scripts/__fixtures__/authoring-paths-10x3.json +32 -0
  294. package/scripts/boundary-proof-mapping-audit.ts +88 -0
  295. package/scripts/ci-typecheck-changed.ts +139 -5
  296. package/scripts/codex-fallback-live-probe.ts +17 -15
  297. package/scripts/coord-role-pack.ts +1 -1
  298. package/scripts/first10-gate.ts +416 -0
  299. package/scripts/fleet/node-setup.sh +32 -2
  300. package/scripts/fleet/run-light-worker.sh +9 -0
  301. package/scripts/fleet/wsl-node-setup.sh +432 -0
  302. package/scripts/gate-0226-failure-census.ts +507 -0
  303. package/scripts/goals/authoring-metrics.ts +258 -0
  304. package/scripts/graph-units/draft-close-node.ts +222 -0
  305. package/scripts/hook-order-sweep.ts +7 -4
  306. package/scripts/land-global-checks.ts +69 -1
  307. package/scripts/measure-nl-routing-corpus.ts +207 -2
  308. package/scripts/release-loop/docs-node.ts +344 -8
  309. package/scripts/release-loop/gate-closure.ts +48 -0
  310. package/scripts/release-loop/gate-diff.ts +2 -1
  311. package/scripts/release-loop/gate-node.ts +249 -53
  312. package/scripts/release-loop/known-issues-node.ts +6 -4
  313. package/scripts/release-loop/node-verdict.ts +2 -0
  314. package/scripts/release-loop/notes-repair.ts +216 -0
  315. package/scripts/release-loop/prepare-node.ts +61 -5
  316. package/scripts/release-loop/resume-cap.ts +14 -34
  317. package/scripts/release-loop/resume-release.ts +151 -15
  318. package/scripts/release-loop/resume-test-traffic.ts +10 -0
  319. package/scripts/release-loop/traffic-node.ts +59 -0
  320. package/scripts/release-loop/traffic-transition.ts +159 -0
  321. package/scripts/release-loop/version-node.ts +81 -12
  322. package/scripts/rhythm/daily.ts +137 -3
  323. package/scripts/train/green-leg.ts +176 -0
  324. package/scripts/tui-continue-real-evidence.ts +47 -0
  325. package/scripts/ux-sim.ts +53 -0
  326. package/skills/README.md +1 -0
  327. package/src/acp/client.ts +31 -5
  328. package/src/acp/core-turn-bridge.ts +54 -6
  329. package/src/acp/stderr-summary.ts +49 -0
  330. package/src/agent/delegate-hint.ts +16 -0
  331. package/src/agent/monad-agent-turn.ts +1 -0
  332. package/src/agent/runner.ts +82 -2
  333. package/src/agent/task-notification.ts +56 -10
  334. package/src/agent/terminal-surface.ts +217 -0
  335. package/src/agent/types.ts +4 -0
  336. package/src/agent-substrate/pr-reviewer.ts +64 -4
  337. package/src/agent-substrate/review-intent.ts +29 -2
  338. package/src/agent-substrate/review-observation.ts +3 -0
  339. package/src/agent-substrate/self-review-cli.ts +490 -26
  340. package/src/autopilot/lineage/sources.ts +1 -1
  341. package/src/autopilot/mission-codebase-gate.ts +141 -12
  342. package/src/autopilot/pr-manager.ts +89 -18
  343. package/src/boot/daemon-runtime.ts +35 -3
  344. package/src/boot/daemon-tools/pty-shell.ts +2 -1
  345. package/src/boot/daemon-tools/self-implement.ts +1 -0
  346. package/src/budget/run-usage-rollup.ts +12 -7
  347. package/src/budget/unified-usage.ts +20 -0
  348. package/src/budget/usage-outcomes.ts +8 -4
  349. package/src/capture/tui-self-report.ts +132 -1
  350. package/src/chat/goal-continue-verdict.ts +41 -0
  351. package/src/chat/index.ts +30 -4
  352. package/src/chat/internal-turn-origin.ts +1 -0
  353. package/src/chat/paste-fold.ts +86 -0
  354. package/src/chat/tool-render/block.ts +32 -0
  355. package/src/chat/tool-render/dispatch.ts +2 -20
  356. package/src/chat/tool-render/result-text.ts +98 -0
  357. package/src/chat/turn-typeahead.ts +70 -7
  358. package/src/cli/autopilot-cli.ts +24 -9
  359. package/src/cli/config-audit.ts +119 -0
  360. package/src/cli/decisions-cli.ts +7 -4
  361. package/src/cli/doctor-cli.ts +137 -6
  362. package/src/cli/guardian-cli.ts +10 -0
  363. package/src/cli/herdr-cli.ts +34 -0
  364. package/src/cli/light-entry-route.ts +6 -3
  365. package/src/cli/log-event-names.ts +2 -0
  366. package/src/cli/logs-cli.ts +357 -2
  367. package/src/cli/make-cli.ts +247 -11
  368. package/src/cli/memory-cli.ts +30 -1
  369. package/src/cli/msg-cli.ts +132 -14
  370. package/src/cli/pr-cli.ts +29 -16
  371. package/src/cli/pr-granularity.ts +5 -3
  372. package/src/cli/pr-land-base-read.ts +11 -2
  373. package/src/cli/pr-land-heavy-remote.ts +6 -3
  374. package/src/cli/pr-land-overlap-evidence.ts +23 -4
  375. package/src/cli/pr-land-recovery.ts +117 -1
  376. package/src/cli/pty-takeover-cli.ts +8 -7
  377. package/src/cli/release-checklist-read-cli.ts +109 -4
  378. package/src/cli/release-cli.ts +64 -15
  379. package/src/cli/release-single-flight.ts +1 -1
  380. package/src/cli/self-update.ts +23 -5
  381. package/src/cli/self-utterances-cli.ts +13 -16
  382. package/src/cli/tasks-card-show.ts +162 -0
  383. package/src/cli/tasks-cli.ts +868 -352
  384. package/src/cli/tasks-read-cli.ts +197 -0
  385. package/src/cli/usage-cli.ts +12 -10
  386. package/src/context-bus/claude-memory-import.ts +99 -0
  387. package/src/context-bus/long-term-memory.ts +125 -0
  388. package/src/core-turn/run-core-turn.ts +4 -2
  389. package/src/core-turn/run-goal-loop.ts +76 -16
  390. package/src/core-turn/types.ts +8 -1
  391. package/src/dashboard/chat/acp-chat.ts +9 -0
  392. package/src/dashboard/context/keys.ts +8 -31
  393. package/src/dashboard/index.ts +299 -62
  394. package/src/dashboard/input/agent-wake.ts +50 -1
  395. package/src/dashboard/input/chat-main-acp-dispatch.ts +21 -1
  396. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +146 -3
  397. package/src/dashboard/input/chat-main-slash-command.ts +4 -2
  398. package/src/dashboard/input/goal-continue.ts +210 -0
  399. package/src/dashboard/input/goal-interim.ts +2 -0
  400. package/src/dashboard/input/internal-turn.ts +2 -8
  401. package/src/dashboard/input/seat-autowake.ts +9 -7
  402. package/src/dashboard/input/turn-autoheal.ts +52 -0
  403. package/src/dashboard/replay-preview.ts +25 -0
  404. package/src/dashboard/seat-boot-preamble.ts +132 -5
  405. package/src/dashboard/slash-runtime/chat-persona.ts +65 -8
  406. package/src/dashboard/slash-runtime/dashboard-handlers.ts +20 -10
  407. package/src/dashboard/slash-runtime/model-choices.ts +15 -0
  408. package/src/dashboard/stale-install-check.ts +3 -99
  409. package/src/dashboard/synchronized-frame-output.ts +74 -0
  410. package/src/debug/batch-queue.ts +119 -0
  411. package/src/debug/tui-log-tag.ts +67 -0
  412. package/src/display/agent-surface.ts +7 -0
  413. package/src/display/coordinator.ts +45 -12
  414. package/src/display/resize-listener.ts +41 -20
  415. package/src/display/synchronized-output-region.ts +60 -0
  416. package/src/domains/standalone-log-sink.ts +15 -2
  417. package/src/domains/surface-events.ts +80 -1
  418. package/src/esc-abort-gate.ts +69 -0
  419. package/src/exec-requests/planner.ts +37 -2
  420. package/src/execution-loop/launch-gate.ts +4 -3
  421. package/src/git-fs/cli-stderr.ts +45 -0
  422. package/src/git-fs/gh-cli.ts +6 -3
  423. package/src/git-fs/git-cli.ts +11 -5
  424. package/src/git-fs/worktree.ts +95 -1
  425. package/src/graph-runner/graph-cli.ts +44 -2
  426. package/src/graph-runner/graph-run-api.ts +3 -2
  427. package/src/graph-runner/runner.ts +37 -16
  428. package/src/graph-wizard/generate.ts +131 -4
  429. package/src/graph-wizard/steps.ts +15 -0
  430. package/src/grounding/proactive-ground.ts +86 -0
  431. package/src/harness/control-inbox.ts +49 -1
  432. package/src/harness/harness-cli-command.ts +230 -40
  433. package/src/harness/harness-incidents.ts +52 -1
  434. package/src/harness/pod-control-send.ts +33 -5
  435. package/src/harness/review-adapter.ts +26 -6
  436. package/src/harness/self-send-remote.ts +69 -0
  437. package/src/harness/self-send-routing-guard.ts +6 -0
  438. package/src/harness/self-send-target.ts +4 -3
  439. package/src/index.ts +443 -25
  440. package/src/input-core/context-key-runtime.ts +21 -0
  441. package/src/instance/test-write-guard-real-transport.fixture.ts +52 -0
  442. package/src/light-cli.ts +6 -1
  443. package/src/llm/route-decision.ts +13 -0
  444. package/src/llm-tool-name.ts +37 -0
  445. package/src/llm-tool-schema.ts +35 -0
  446. package/src/llm-wellformed.ts +70 -0
  447. package/src/llm.ts +158 -15
  448. package/src/loops/checker.ts +100 -0
  449. package/src/loops/consensus.ts +26 -2
  450. package/src/loops/loop-cli.ts +9 -4
  451. package/src/loops/msg-wake.ts +35 -39
  452. package/src/loops/orchestrator/anomaly-cluster.ts +243 -0
  453. package/src/loops/orchestrator/inbox.ts +59 -11
  454. package/src/loops/orchestrator/knowhow-pack.ts +364 -0
  455. package/src/loops/orchestrator/tick.ts +54 -5
  456. package/src/maturity/feature-maturity.ts +1 -1
  457. package/src/model-tier/llm-tier-map.ts +12 -17
  458. package/src/models/prompts.ts +17 -2
  459. package/src/msg/msg-post-office.ts +116 -0
  460. package/src/msg/msg-store.ts +17 -0
  461. package/src/mss/logging/log-store.ts +79 -22
  462. package/src/native-tool-catalog.ts +1 -1
  463. package/src/nexus/api/decisions-route.ts +38 -18
  464. package/src/nexus/api/graph-wizard-api.ts +46 -3
  465. package/src/nexus/api/grid.ts +24 -4
  466. package/src/nexus/api/http-server.ts +1 -1
  467. package/src/nexus/api/ops-api.ts +14 -6
  468. package/src/nexus/boot/register-mcp-clients.ts +263 -38
  469. package/src/nexus/onboarding/first-boot-wizard.ts +56 -8
  470. package/src/nexus/state/state.ts +7 -1
  471. package/src/oauth/codex-account-store.ts +34 -1
  472. package/src/onboarding/first-run.ts +37 -6
  473. package/src/onboarding/web-first.ts +43 -11
  474. package/src/ops/loops-map.ts +46 -0
  475. package/src/ops/overview-cache.ts +22 -4
  476. package/src/ops/overview-collect.ts +12 -6
  477. package/src/ops/overview.ts +44 -8
  478. package/src/persona/loader.ts +11 -0
  479. package/src/persona/types.ts +8 -0
  480. package/src/policy/llm-policy.ts +3 -1
  481. package/src/prompt-enhance/enhance.ts +4 -4
  482. package/src/prompt-library/session-guidance.ts +14 -0
  483. package/src/provider-summary.ts +8 -0
  484. package/src/pty-shell/availability.ts +7 -0
  485. package/src/pty-shell/herdr-agent-report.ts +155 -37
  486. package/src/pty-shell/herdr-prompt.ts +266 -0
  487. package/src/pty-shell/herdr-prune-stale.ts +17 -0
  488. package/src/pty-shell/herdr-sequence-lease.ts +60 -0
  489. package/src/pty-shell/pty-control-ipc.ts +327 -15
  490. package/src/pty-shell/pty-special-keys.ts +42 -2
  491. package/src/pty-shell/registry.ts +25 -15
  492. package/src/pty-shell/stale-install-check.ts +99 -0
  493. package/src/pty-shell/tui-run-state.ts +249 -3
  494. package/src/release-loop/auto-start.ts +20 -2
  495. package/src/release-loop/checklist.ts +126 -9
  496. package/src/release-loop/feature-store.ts +9 -9
  497. package/src/release-loop/landed-but-yellow.ts +93 -12
  498. package/src/release-loop/rehearsal.ts +36 -20
  499. package/src/repl/index.ts +27 -1
  500. package/src/seat-loop/knowhow-lint.ts +1 -1
  501. package/src/seat-loop/seat-report.ts +1 -0
  502. package/src/self-dev/ask-launch-flow.ts +86 -10
  503. package/src/self-dev/decompose-proposal.ts +183 -20
  504. package/src/self-dev/decompose.ts +61 -0
  505. package/src/self-dev/dev-cli.ts +37 -3
  506. package/src/self-dev/dev-pipeline.ts +37 -9
  507. package/src/self-dev/draft-sweep.ts +241 -16
  508. package/src/self-dev/draft-triage-rules.ts +50 -5
  509. package/src/self-dev/glm-credential-guard.ts +77 -0
  510. package/src/self-dev/graph-journey-nodes.ts +1 -0
  511. package/src/self-dev/graph-journey-route.ts +12 -2
  512. package/src/self-dev/orchestrate-cli.ts +68 -3
  513. package/src/self-dev/orchestrate.ts +116 -17
  514. package/src/self-dev/review-provider-fallback.ts +45 -7
  515. package/src/self-dev/run-supervisor.ts +55 -2
  516. package/src/self-dev/run-types.ts +1 -1
  517. package/src/self-dev/shard-plan.ts +48 -0
  518. package/src/self-dev/shard-run.ts +50 -3
  519. package/src/self-health/autoheal.ts +153 -0
  520. package/src/self-health/signatures.ts +6 -0
  521. package/src/self-implement/device-first.ts +116 -0
  522. package/src/self-implement/doctor-visit.ts +322 -1
  523. package/src/self-implement/evidence-command-budget.ts +80 -0
  524. package/src/self-implement/gate-artifacts.ts +6 -2
  525. package/src/self-implement/gate-baseline.ts +64 -1
  526. package/src/self-implement/gate-cli.ts +93 -9
  527. package/src/self-implement/gate-remote.ts +28 -4
  528. package/src/self-implement/gate-scope.ts +13 -1
  529. package/src/self-implement/goal-author-cli.ts +1 -0
  530. package/src/self-implement/goal-author-decompose.ts +265 -4
  531. package/src/self-implement/goal-author.ts +366 -26
  532. package/src/self-implement/goal-elaborate.ts +176 -22
  533. package/src/self-implement/goal-refine.ts +3 -0
  534. package/src/self-implement/goal-supervisor-fill.ts +75 -26
  535. package/src/self-implement/graph-variant.ts +40 -6
  536. package/src/self-implement/headless-elanous-driver.ts +19 -3
  537. package/src/self-implement/importer-test-index.ts +215 -2
  538. package/src/self-implement/knowhow-pack.ts +5 -5
  539. package/src/self-implement/off-diff-evidence.ts +58 -6
  540. package/src/self-implement/orchestrator.ts +536 -42
  541. package/src/self-implement/pod-warm-pool-profile.ts +1 -0
  542. package/src/self-implement/pwa-import-graph.ts +1 -1
  543. package/src/self-implement/review-round-brief.ts +300 -0
  544. package/src/self-implement/rework-policy.ts +44 -0
  545. package/src/self-implement/run-ledger.ts +278 -6
  546. package/src/self-implement/run-stop.ts +1 -1
  547. package/src/self-implement/run-ttl.ts +38 -24
  548. package/src/self-implement/running-runs.ts +17 -3
  549. package/src/self-implement/scope-adjacency.ts +3 -1
  550. package/src/self-implement/seams.ts +72 -10
  551. package/src/self-implement/stop-autoheal.ts +1 -0
  552. package/src/self-implement/test-seams.ts +13 -1
  553. package/src/self-implement/train-reach-grouping.ts +8 -2
  554. package/src/self-implement/wiring-check.ts +358 -0
  555. package/src/session/chat.ts +7 -1
  556. package/src/session/id-prefix.ts +60 -0
  557. package/src/session/index.ts +4 -0
  558. package/src/session-runtime/index.ts +9 -1
  559. package/src/shell-env-bootstrap.ts +11 -4
  560. package/src/skills/runner.ts +4 -2
  561. package/src/skills/tools/agent.ts +290 -48
  562. package/src/skills/tools/pty.ts +5 -0
  563. package/src/skills/tools/schedule-wakeup.ts +23 -4
  564. package/src/skills/tools/shell.ts +61 -6
  565. package/src/skills/tools/terminal-modal-inject.ts +1 -2
  566. package/src/skills/tools/terminal-modal.ts +10 -8
  567. package/src/skills/url-router.ts +41 -1
  568. package/src/smart-wizard/run.ts +66 -0
  569. package/src/status/bar.ts +34 -6
  570. package/src/status/metrics.ts +12 -1
  571. package/src/status/status-provider-info.ts +37 -6
  572. package/src/steward/rejudge-tick.ts +22 -7
  573. package/src/steward/resync-tick.ts +31 -4
  574. package/src/steward/ta-repair-event-collect.ts +17 -6
  575. package/src/task-agent/actions.ts +202 -5
  576. package/src/task-agent/boundary-proof-mapping.ts +12 -0
  577. package/src/task-agent/boundary-proof-producer.ts +147 -0
  578. package/src/task-agent/completion-evidence.ts +2 -1
  579. package/src/task-agent/green-close-recipe.ts +239 -0
  580. package/src/task-agent/green-sweep.ts +64 -10
  581. package/src/task-agent/land-queue.ts +21 -8
  582. package/src/task-agent/live-moves.ts +70 -9
  583. package/src/task-agent/map-host-adapter.ts +134 -0
  584. package/src/task-agent/map-reduce.ts +158 -0
  585. package/src/task-agent/map-run-ledger.ts +226 -0
  586. package/src/task-agent/mission.ts +30 -0
  587. package/src/task-agent/parent-host.ts +21 -11
  588. package/src/task-agent/rejudge-on-head.ts +87 -5
  589. package/src/task-agent/repair-cycle.ts +179 -32
  590. package/src/task-agent/repair-next-move.ts +42 -4
  591. package/src/task-agent/repair-outcomes.ts +29 -0
  592. package/src/task-agent/repair-worker.ts +14 -4
  593. package/src/task-agent/review-prior-fix.ts +12 -2
  594. package/src/task-agent/shadow.ts +229 -8
  595. package/src/task-agent/task-card-prefix.ts +2 -0
  596. package/src/task-agent/task-hand.ts +630 -30
  597. package/src/task-agent/task-parent-role-llm.ts +45 -0
  598. package/src/task-agent/tier-split.ts +80 -10
  599. package/src/task-agent/work-order.ts +53 -2
  600. package/src/task-orchestrator/dispatcher.ts +46 -1
  601. package/src/task-orchestrator/generator-prompt.ts +6 -0
  602. package/src/task-orchestrator/surfaces/pod-account-broker.ts +9 -0
  603. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +16 -6
  604. package/src/task-orchestrator/surfaces/pod-child-reuse.ts +146 -0
  605. package/src/task-orchestrator/surfaces/pod-command-job.ts +86 -5
  606. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +215 -3
  607. package/src/task-orchestrator/surfaces/pod-launch-history.ts +133 -0
  608. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +8 -6
  609. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +97 -0
  610. package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +11 -0
  611. package/src/task-orchestrator/surfaces/pod-pool.ts +126 -8
  612. package/src/task-orchestrator/surfaces/pod-provenance.ts +8 -2
  613. package/src/task-orchestrator/surfaces/pod-repair-commit.ts +18 -3
  614. package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +16 -0
  615. package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +224 -0
  616. package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +201 -0
  617. package/src/task-orchestrator/surfaces/pod-si-warm-provider.ts +444 -0
  618. package/src/task-orchestrator/surfaces/pod-si-warm-runner.ts +387 -0
  619. package/src/task-orchestrator/surfaces/pod-si-warm-transport.ts +157 -0
  620. package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +4 -0
  621. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +491 -36
  622. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +3 -0
  623. package/src/task-orchestrator/surfaces/self-implement-pod.ts +612 -54
  624. package/src/task-orchestrator/surfaces/self-implement.ts +46 -2
  625. package/src/testing/linux-env.ts +177 -0
  626. package/src/tool-runtime/index.ts +15 -2
  627. package/src/tool-runtime/mcp-proxy-bootstrap.ts +29 -0
  628. package/src/tty-foreground.ts +155 -0
  629. package/src/tui/chat/goal-interim.ts +177 -0
  630. package/src/tui/chat/log-view.ts +205 -18
  631. package/src/tui/chat/self-health-status.ts +232 -0
  632. package/src/tui/entry.ts +623 -80
  633. package/src/tui/input/at-picker.ts +55 -0
  634. package/src/tui/input/composer.ts +8 -1
  635. package/src/tui/slash/compact-command.ts +7 -0
  636. package/src/tui/slash/compact.ts +1 -7
  637. package/src/tui/slash/resume.ts +98 -0
  638. package/src/tui/slash/signals.ts +114 -0
  639. package/src/tui/slash/term-next.ts +71 -0
  640. package/src/tui-client/local-chat-turn.ts +49 -5
  641. package/src/tui-client/next-dashboard-runtime.ts +8 -0
  642. package/src/tui-client/next-loop-command.ts +70 -0
  643. package/src/tui-client/next-wakeup.ts +191 -0
  644. package/src/tui.ts +37 -0
  645. package/src/ui/modal-adapter.ts +2 -2
  646. package/src/user-config.ts +19 -1
  647. package/src/ux-sim/boot-daemon-tools-pty-shell-device.ts +136 -0
  648. package/src/ux-sim/classic-tui-seat-scenarios.ts +670 -0
  649. package/src/ux-sim/cli-config-audit-device.ts +121 -0
  650. package/src/ux-sim/dashboard-event-loop-stall-monitor-device.ts +238 -0
  651. package/src/ux-sim/event-monkey.ts +3 -2
  652. package/src/ux-sim/fake-bin.ts +45 -0
  653. package/src/ux-sim/fake-gh.ts +174 -0
  654. package/src/ux-sim/fake-herdr.ts +238 -0
  655. package/src/ux-sim/fake-kubectl.ts +142 -0
  656. package/src/ux-sim/fake-llm.ts +116 -6
  657. package/src/ux-sim/fault-glm-no-credential.ts +116 -0
  658. package/src/ux-sim/fault-nextmove-dispatch.ts +102 -1
  659. package/src/ux-sim/fault-scope-escape-lift.ts +33 -6
  660. package/src/ux-sim/fault-tui-400-autoheal.ts +297 -0
  661. package/src/ux-sim/goal-device.ts +619 -0
  662. package/src/ux-sim/in-process-cli.ts +22 -5
  663. package/src/ux-sim/mock-authoring-first-run-child.ts +35 -0
  664. package/src/ux-sim/mock-authoring-first-run.ts +97 -0
  665. package/src/ux-sim/mock-seat-pty-frame-child.ts +29 -0
  666. package/src/ux-sim/mock-seat-pty-frame.ts +297 -0
  667. package/src/ux-sim/mock-seat-pty-sequence-child.ts +21 -0
  668. package/src/ux-sim/mock-seat-pty-sequence.ts +206 -0
  669. package/src/ux-sim/next-tui-seat-scenarios.ts +749 -0
  670. package/src/ux-sim/onboarding-first-run-device.ts +269 -0
  671. package/src/ux-sim/pty-key-modifiers-device.ts +85 -0
  672. package/src/ux-sim/resume-picker-device.ts +173 -0
  673. package/src/ux-sim/session-id-seed.ts +60 -0
  674. package/src/ux-sim/skills-tools-shell-device.ts +156 -0
  675. package/src/ux-sim/subagent-tools-device.ts +81 -0
  676. package/src/ux-sim/task-agent-parent-host-device.ts +264 -0
  677. package/src/ux-sim/task-agent-rejudge-on-head-device.ts +114 -0
  678. package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +250 -0
  679. package/src/ux-sim/tty-foreground-steal-child.ts +21 -0
  680. package/src/version/packed-revision.json +1 -1
  681. package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +0 -41
  682. package/apps/pwa/out/_next/static/chunks/1674.2a65fd5668e1cc5f.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +0 -24
  684. package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +0 -74
  685. package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/1993-c0007b7ad0c0ef41.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/1c735de4.f993a30bbd3c57c4.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +0 -148
  694. package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/2625.5d870aa999f33db1.js +0 -34
  696. package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +0 -77
  697. package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +0 -62
  699. package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +0 -90
  701. package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/3589.5444a06bcf6e775c.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +0 -36
  705. package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/3677-4215f826f6aa68e5.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +0 -79
  708. package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/4346.8601952e73bf5803.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +0 -286
  712. package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +0 -24
  715. package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/5154.a118f3957ed14e36.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +0 -23
  724. package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +0 -94
  725. package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/56d372f2.681ab4ec4c998fae.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +0 -287
  729. package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +0 -8
  736. package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +0 -27
  739. package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +0 -93
  741. package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/6b89c8bd.24df687a820500b3.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +0 -1
  744. package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +0 -149
  747. package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +0 -104
  748. package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +0 -1
  752. package/apps/pwa/out/_next/static/chunks/8219.baa715d1deee80e2.js +0 -1
  753. package/apps/pwa/out/_next/static/chunks/8256.e3ac8421a71f82f4.js +0 -1
  754. package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +0 -1
  755. package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +0 -1
  756. package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +0 -1
  757. package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +0 -226
  758. package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +0 -1
  759. package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +0 -64
  760. package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +0 -1
  761. package/apps/pwa/out/_next/static/chunks/8637.47f5b35afcae5e6b.js +0 -1
  762. package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +0 -1
  763. package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +0 -1
  764. package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +0 -1
  765. package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +0 -1
  766. package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +0 -115
  767. package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +0 -1
  768. package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +0 -1
  769. package/apps/pwa/out/_next/static/chunks/967-2aa39b2d04b4c690.js +0 -1
  770. package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +0 -1
  771. package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +0 -1
  772. package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +0 -1
  773. package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +0 -1
  774. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +0 -1
  775. package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +0 -1
  776. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +0 -1
  777. package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +0 -1
  778. package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +0 -1
  779. package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +0 -1
  780. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +0 -1
  781. package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +0 -1
  782. package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +0 -1
  783. package/apps/pwa/out/_next/static/chunks/app/control/page-406b47745b53f269.js +0 -1
  784. package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +0 -1
  785. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +0 -1
  786. package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +0 -1
  787. package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +0 -1
  788. package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +0 -1
  789. package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +0 -1
  790. package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +0 -1
  791. package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +0 -1
  792. package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +0 -1
  793. package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +0 -1
  794. package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +0 -1
  795. package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +0 -1
  796. package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +0 -1
  797. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +0 -1
  798. package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +0 -1
  799. package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +0 -1
  800. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +0 -1
  801. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +0 -1
  802. package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +0 -1
  803. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +0 -1
  804. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +0 -1
  805. package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +0 -1
  806. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +0 -1
  807. package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +0 -1
  808. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +0 -1
  809. package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +0 -1
  810. package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +0 -1
  811. package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +0 -1
  812. package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +0 -1
  813. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +0 -1
  814. package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +0 -1
  815. package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +0 -1
  816. package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +0 -1
  817. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +0 -1
  818. package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +0 -1
  819. package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +0 -1
  820. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +0 -1
  821. package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +0 -1
  822. package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +0 -1
  823. package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +0 -1
  824. package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +0 -1
  825. package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +0 -1
  826. package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +0 -1
  827. /package/apps/pwa/out/_next/static/{T1mBQqmkwKDLGtyisDCOd → 8kITumG9GAZ1LwT9POrAR}/_ssgManifest.js +0 -0
@@ -40,6 +40,7 @@ import { writeGoalDocumentAtomic } from './goal-document-write.js';
40
40
  import { hostname, loadavg } from 'node:os';
41
41
  import { resolveHostId } from '../platform/host-id.js';
42
42
  import { resolveInstanceName } from '../instance-identity.js';
43
+ import { ghAutomationEnv } from '../autopilot/pr-manager.js';
43
44
  import { citedReviewSymbols, MAX_CITED_REVIEW_SYMBOL_CHARS, measureReviewFindingKeyOverlap, normalizeReviewFindingKey, REVIEW_FINDING_KEY_VERSION, reviewFindingKey, shortNormalizedReviewFindingHash, type CitedReviewSymbol } from '../agent-substrate/review-finding-key.js';
44
45
  export { citedReviewSymbols, MAX_CITED_REVIEW_SYMBOL_CHARS, normalizeReviewFindingKey, REVIEW_FINDING_KEY_VERSION, reviewFindingKey, shortNormalizedReviewFindingHash, type CitedReviewSymbol } from '../agent-substrate/review-finding-key.js';
45
46
 
@@ -47,6 +48,46 @@ function displayBranchName(ref: string): string {
47
48
  return ref.trim().replace(/^refs\/heads\//, '').replace(/^origin\//, '');
48
49
  }
49
50
 
51
+ export const HARVEST_DEFERRED_MARKER = '수확보류: harvest-deferred';
52
+ const execFileAsync = promisify(execFileCallback);
53
+
54
+ export function markHarvestDeferredBody(input: {
55
+ runStatus: string | undefined;
56
+ prNumber: number | undefined;
57
+ body: string | undefined;
58
+ }): string | undefined {
59
+ if (input.runStatus !== 'failed' || input.prNumber === undefined || !Number.isSafeInteger(input.prNumber) || input.prNumber < 1 || input.body === undefined) return undefined;
60
+ if (harvestDeferredMarker(input.body)) return input.body;
61
+ const separator = input.body.endsWith('\n') ? '\n' : '\n\n';
62
+ return `${input.body}${separator}${HARVEST_DEFERRED_MARKER}`;
63
+ }
64
+
65
+ export async function readHarvestPrBody(opts: { number: number; cwd: string }): Promise<{ body: string; state: string; isDraft: boolean } | undefined> {
66
+ const { stdout } = await execFileAsync('gh', ['pr', 'view', String(opts.number), '--json', 'body,state,isDraft'], {
67
+ cwd: opts.cwd,
68
+ env: ghAutomationEnv(process.env),
69
+ encoding: 'utf8',
70
+ timeout: 120_000,
71
+ maxBuffer: 2 * 1024 * 1024,
72
+ });
73
+ const parsed: unknown = JSON.parse(stdout);
74
+ if (!parsed || typeof parsed !== 'object') return undefined;
75
+ const record = parsed as { body?: unknown; state?: unknown; isDraft?: unknown };
76
+ return typeof record.body === 'string' && typeof record.state === 'string' && typeof record.isDraft === 'boolean'
77
+ ? { body: record.body, state: record.state, isDraft: record.isDraft }
78
+ : undefined;
79
+ }
80
+
81
+ export async function updateHarvestPrBody(opts: { number: number; body: string; cwd: string }): Promise<void> {
82
+ await execFileAsync('gh', ['pr', 'edit', String(opts.number), '--body', opts.body], {
83
+ cwd: opts.cwd,
84
+ env: ghAutomationEnv(process.env),
85
+ encoding: 'utf8',
86
+ timeout: 120_000,
87
+ maxBuffer: 2 * 1024 * 1024,
88
+ });
89
+ }
90
+
50
91
  export function formatAutoMergeSuccessMessage(input: {
51
92
  prNumber: number;
52
93
  mergedBase?: string | undefined;
@@ -68,6 +109,7 @@ import { getUserConfig } from '../user-config.js';
68
109
  import { appendRunLedgerEntry, loadRunLedger, parseRunShardIdentity, queryRunChain, type RunChainShardSibling, type RunLedgerEntry, type RunLedgerWriter, type RunOriginData, type RunShardIdentity } from './run-ledger.js';
69
110
  import { classifyMergeHoldStop, forgetRunStop, isRunStopClass, readRunStopRecord, recordRunStop, runStopRecorded } from './run-stop.js';
70
111
  import { autohealFromStop, parseAutohealMode, type AutohealActions, type AutohealResult } from './stop-autoheal.js';
112
+ import { recordParentNeedsOwnerVisit } from './doctor-visit.js';
71
113
  import { emitSessionEvent, type SessionEventInput } from '../context-bus/session-events.js';
72
114
  import { decideLineageSupersede, lineageSupersedeCloseComment, type LineageSupersedeOpenDraft } from './lineage-supersede.js';
73
115
  import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
@@ -79,6 +121,7 @@ import { classifyGateEnvDeficit, DEFAULT_GATE_ENV_RETRY, GATE_ENV_DEFICIT_HARVES
79
121
  import { recordFailureEvent } from './heal-intake.js';
80
122
  import { classifyReworkCapExhaustion, type ReworkCapHealDecision } from './heal-triage.js';
81
123
  import { releasePathHold, releasePathHoldComment, RELEASE_PATH_LABEL } from '../self-dev/release-path-guard.js';
124
+ import { harvestDeferredMarker } from '../self-dev/draft-triage-rules.js';
82
125
  import { emitDecision } from '../live/detail-switch.js';
83
126
  import { reworkDecision } from './decision-events.js';
84
127
  import type { LogSink } from '../mss/logging/sink.js';
@@ -91,7 +134,7 @@ import { classifyError } from '../session-runtime/retry-policy.js';
91
134
  import { decomposeSelfDevGoal, inferHotPaths, type SelfDevDecomposeOptions } from '../self-dev/decompose.js';
92
135
  import { boundaryGlobs, boundaryViolations } from './goal-boundary.js';
93
136
  import { askAtExecution, type ExecutionClarificationResult } from './execution-clarification.js';
94
- import { decideScopeExpansion, renderScopeExpansionSection, SCOPE_EXPANSION_CAP, type ScopeExpansion } from './scope-adjacency.js';
137
+ import { decideScopeExpansion, renderScopeExpansionSection, SCOPE_EXPANSION_CAP, type ScopeExpansion, type ScopeStopReason } from './scope-adjacency.js';
95
138
  import { targetScopedGoalText } from './goal-text-path-scope.js';
96
139
  import { selectKnowhowPack } from './knowhow-pack.js';
97
140
  import { declaredNewFilePaths } from './goal-new-paths.js';
@@ -175,7 +218,7 @@ function onePrChecklistLine(body: string): string {
175
218
  }
176
219
  import { renderRoundSummaryCard, type RoundSummaryEntry } from './round-summary-card.js';
177
220
  import { computeFlexSignals } from './flex-signals.js';
178
- import { resolveAdaptiveMaxReworkDecision, resolveEscalateTier, resolveEscalateTarget, buildReworkFeature, failIndicator, isRepeatedUnverifiedOnlyGateFailure, unverifiedRepeatKey, appendReworkHistory, truncateReworkHistoryByItem, applyReworkBudgetDecision, parseContractConflictRelaxation, parseReworkBudgetDecision, resolveReworkBudgetCarry, stripReworkBudgetHeaders, countConsecutiveMustFixIds, mergeReworkNotes, resolveReworkKind, type ReworkPlanRevision, type ReworkNotePart } from './rework-policy.js';
221
+ import { resolveAdaptiveMaxReworkDecision, resolveEscalateTier, resolveEscalateTarget, buildReworkFeature, failIndicator, isRepeatedUnverifiedOnlyGateFailure, unverifiedRepeatKey, appendReworkHistory, truncateReworkHistoryByItem, applyReworkBudgetDecision, parseContractConflictRelaxation, parseReworkBudgetDecision, resolveReworkBudgetCarry, stripReworkBudgetHeaders, countConsecutiveMustFixIds, mergeReworkNotes, resolveReworkKind, decideReviewRound, type ReviewRoundDecision, type ReworkPlanRevision, type ReworkNotePart } from './rework-policy.js';
179
222
  import type { SupervisionReworkSource } from './supervision-vocabulary.js';
180
223
  import { classifyReworkBudgetShadow } from './classify-shadow.js';
181
224
  import { createReworkBudgetJudgment, REWORK_BUDGET_JUDGMENT, reworkBudgetEvidenceForObservation, reworkBudgetWorkflow } from './rework-budget-judgment.js';
@@ -183,7 +226,8 @@ import { formatGateFailedId, type PremiseReviewCard, type PremiseReviewInput } f
183
226
  import { runWorkflowToCompletion } from '../workflow-runtime/executor.js';
184
227
  import { mapReworkVerdict, supervisionObservationFields } from './supervision-vocabulary.js';
185
228
  import { resolveRunOutcome } from './run-outcome.js';
186
- import { authorLimitationCountFromGoal, harvestEvidenceLines, harvestedEvidenceObservation, MAX_HARVESTED_EVIDENCE_CHARS, parseOffDiffEvidence, requiredEvidenceFromGoal, runRequiredEvidenceChecks, coverRequiredEvidence, settleUncoveredEvidence, formatUncoveredEvidenceFeedback, type OffDiffEvidenceParse } from './off-diff-evidence.js';
229
+ import { authorLimitationCountFromGoal, harvestEvidenceLines, harvestedEvidenceObservation, MAX_HARVESTED_EVIDENCE_CHARS, parseOffDiffEvidence, requiredEvidenceFromGoal, createRequiredEvidenceBudget, coverRequiredEvidence, settleUncoveredEvidence, formatUncoveredEvidenceFeedback, type OffDiffEvidenceParse } from './off-diff-evidence.js';
230
+ import { resolveEvidenceHeadCommit } from './evidence-command-budget.js';
187
231
  import { pressDecisionSignals, type DecisionSignalPressResult } from './decision-signal-press.js';
188
232
  import { inspectDecisionObservations, inspectDecisionSignalKinds, inspectDecisionSignalObservations } from '../../scripts/ask-marker-check.js';
189
233
 
@@ -257,7 +301,9 @@ import type { ClassifyShadowCallLLM } from './classify-shadow.js';
257
301
  import { hasModuleLoadFailure, type GateTestFailure } from './gate-baseline.js';
258
302
  import { commitWorktree, defaultBranchRef, preservationHasChanges } from './seams.js';
259
303
  import { detectUncommittedWork } from './uncommitted-work.js';
260
- import { spawnSync } from 'node:child_process';
304
+ import { execFile as execFileCallback, spawnSync } from 'node:child_process';
305
+ import { SPAWN_SYNC_MAX_BUFFER } from '../util/spawn-sync-output.js';
306
+ import { promisify } from 'node:util';
261
307
  import { blockedDraftDisposition, blockedDraftTargetFor, decideDraftOnStop, nextMoveFor, parseDraftOnStopMode, repairSalvageLinkComment, salvageBranchForRun, syntheticRepairBasePr, type DraftOnStopMode } from './blocked-draft-policy.js';
262
308
  import { formatLlmMergeOutcome } from '../autopilot/build/llm-conflict-merge.js';
263
309
  import { FOUND_CITED_PATH_REFUTATION_QUOTE, MISSING_CITED_PATH_REFUTATION_QUOTE, MUST_FIX_REFUTATION_ACKNOWLEDGEMENT_WITH_REASON, REFUTATION_QUOTE_GRAMMAR, observeMustFixCitedPaths, parseMustFixRefutationAcknowledgement, parseMustFixRefutations, renderMustFixCitedPathFacts, snapshotMustFixFindings, stableMustFixId, type ReflectEvidenceFacts, type ReflectGateFacts, type MustFixCitedPathFact, type MustFixFinding, type MustFixRecurrenceHistory, type MustFixRefutation, type MustFixRefutationKind } from './reflect-mustfix.js';
@@ -281,6 +327,7 @@ import { appendGateWorktreeFreshnessEvidence, gateEvidenceNote } from '../harnes
281
327
  import { diffHeaderPaths } from '../agent-substrate/self-review-cli.js';
282
328
  import { format as formatPrComment, type PrCommentRole } from '../agent-substrate/pr-comment-meta.js';
283
329
  import { changedFiles, commitTitles, type OpenPrOutcome, type SelfImplementCompletionDisposition } from './seams.js';
330
+ import { findUnwiredExports, gitBaseFileReader } from './wiring-check.js';
284
331
  import {
285
332
  classifyAbandonedRun,
286
333
  isAbandonedClassificationOutcome,
@@ -291,6 +338,7 @@ import {
291
338
  export type { QuotaAccountAvailabilityEvidence };
292
339
  import { decideReworkSalvage, type ReworkSalvageEvidence } from './rework-salvage.js';
293
340
  import { type SupervisorGoalFillEvidence, fillUnverifiableGoalSlots, type SupervisorGoalFillResult } from './goal-supervisor-fill.js';
341
+ import { repairNextMove } from '../task-agent/repair-next-move.js';
294
342
  import { escalateGoalDocumentClarifications, recordSupervisorPlanRevision, type GoalClarificationEscalationResult, type SupervisorPlanRevisionRelaxation, type SupervisorPlanRevisionResult } from './goal-clarification-escalation.js';
295
343
  import { parseGoalDocumentClarifications, selfAnswerGoalDocumentClarifications, type GoalClarificationSelfResolver } from './goal-author-clarification.js';
296
344
  import {
@@ -1008,6 +1056,7 @@ export type ImplementTerminalStatus = {
1008
1056
  export interface SelfImplementSeams {
1009
1057
  /** Run-observer ledger sink. Tests inject an in-memory sink to avoid touching user state. */
1010
1058
  writeRunLedger?: RunLedgerWriter;
1059
+ checkUnwiredExports?: typeof findUnwiredExports;
1011
1060
  /** STOP-AUTOHEAL — heal action seams (live mode only runs actions given here; missing = needs-owner). */
1012
1061
  stopAutohealActions?: AutohealActions;
1013
1062
  /** STOP-AUTOHEAL — one-attempt guard reader. Default: real run ledger, or none when `writeRunLedger` is injected. */
@@ -1159,6 +1208,8 @@ export interface SelfImplementSeams {
1159
1208
  /** Test seam for blocked environment branch persistence; return whether the branch was pushed. */
1160
1209
  /** Test seam — the worktree commit to pin before main sync (default `git rev-parse HEAD`). */
1161
1210
  headCommit?: (cwd: string) => string | undefined;
1211
+ /** Test seam for required evidence command execution; production uses its bounded shell runner. */
1212
+ evidenceCommandRunner?: (command: string, cwd: string) => Pick<ReturnType<typeof spawnSync>, 'status' | 'stdout' | 'stderr' | 'error'>;
1162
1213
  /** `commit` pins the pushed state; without it the worktree HEAD is pushed. */
1163
1214
  preserveBlockedBranch?: (ctx: { cwd: string; branch: string; title: string; commit?: string }) => boolean | Promise<boolean>;
1164
1215
  /** DRAFT-NOT-ARCHIVE — `tools.selfImplement.draftOnStop` 읽기 seam. 생략하면 user-config(실패·부재 = 'always' · 종전). */
@@ -1173,11 +1224,17 @@ export interface SelfImplementSeams {
1173
1224
  openPr: (opts: { title: string; body: string; head: string; base?: string; draft?: boolean; labels?: string[]; cwd: string; runId?: string }) => Promise<OpenPrOutcome>;
1174
1225
  /** PR 생성 뒤 런의 라운드 대화를 게시한다. 실패는 PR 개설을 되돌리지 않는다. */
1175
1226
  postPrComment?: (opts: { number: number; body: string; cwd: string }) => Promise<void>;
1227
+ /** Read-only body/state confirmation for the failed-run draft preservation marker. */
1228
+ readPrBodyForHarvest?: (opts: { number: number; cwd: string }) => Promise<{ body: string; state: string; isDraft: boolean } | undefined>;
1229
+ /** Update only the confirmed existing PR body; never creates a PR. */
1230
+ updatePrBodyForHarvest?: (opts: { number: number; body: string; cwd: string }) => Promise<void>;
1176
1231
  /** Read and edit the single marked status comment; missing seams fall back to individual comments. */
1177
1232
  findPrComment?: (opts: { number: number; marker: string; cwd: string; comments?: unknown }) => Promise<{ id: number; body: string } | undefined>;
1178
1233
  editPrComment?: (opts: { id: number; body: string; cwd: string }) => Promise<void>;
1179
1234
  addPrLabel?: (opts: { number: number; label: string; cwd: string }) => Promise<void>;
1180
1235
  readPrFiles?: (opts: { number: number; cwd: string; base?: string }) => Promise<string[]>;
1236
+ /** Read the authoritative GitHub head branch and creation timestamp before run attribution. */
1237
+ readPrAttribution?: (opts: { number: number; cwd: string }) => Promise<{ headRefName: string; createdAt: string }>;
1181
1238
  /** PR의 base/head SHA와 관측된 PR base를 한 응답에서 고정한다. 미주입·실패·빈 SHA면 검사 대상을 증명할 수 없어 fail-closed로 PR을 열어 둔다. */
1182
1239
  readPrCommitShas?: (opts: { number: number; cwd: string }) => Promise<{ baseCommit: string; headCommit: string; baseRefName?: string }>;
1183
1240
  /** 고정한 base/head SHA 쌍의 unified diff. 가변 PR 조회를 다시 사용하면 ABA head 변경에 안전하지 않으므로 두 SHA는 필수 입력이다. */
@@ -2333,6 +2390,8 @@ export interface SelfImplementResult {
2333
2390
  scopeExpansion?: ScopeExpansion;
2334
2391
  /** Bounded names measured by the execution-scope guard on a held edit. */
2335
2392
  outOfScopeEdits?: string[];
2393
+ /** SCOPE-ESCAPE-AS-REFINE: why the held edit stops (deletion · protected path · cap) — the parent stops only on these. */
2394
+ scopeStopReasons?: ScopeStopReason[];
2336
2395
  nameCapReached?: boolean;
2337
2396
  outsideCount?: number;
2338
2397
  gate?: SelfImplementGateResult;
@@ -3507,6 +3566,17 @@ async function autohealRecordedStop(stopEntry: RunLedgerEntry, s: SelfImplementS
3507
3566
  ...(loadLedger ? { loadLedger } : {}),
3508
3567
  ...(s?.writeRunLedger ? { writeLedger: s.writeRunLedger } : {}),
3509
3568
  });
3569
+ if (result.action === 'needs-owner') {
3570
+ try {
3571
+ const stopKey = JSON.stringify([stopEntry.timestamp, record.site ?? '', record.class, record.cause, record.evidenceRef]);
3572
+ const visit = recordParentNeedsOwnerVisit({ runId: stopEntry.runId, stopKey, evidence: result.reason ?? record.cause });
3573
+ if (visit.status === 'missing-ledger') {
3574
+ debug.log('run-doctor', 'visit-failed', { runId: stopEntry.runId, from: 'parent', symptom: 'needs-owner', error: 'run-ledger-missing' });
3575
+ }
3576
+ } catch (error) {
3577
+ try { debug.log('run-doctor', 'visit-failed', { runId: stopEntry.runId, from: 'parent', symptom: 'needs-owner', error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
3578
+ }
3579
+ }
3510
3580
  s?.onStopAutoheal?.(result);
3511
3581
  } catch (error) {
3512
3582
  try { debug.log('self-implement.autoheal', 'dispatch-failed', { runId: stopEntry.runId, error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
@@ -3760,6 +3830,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
3760
3830
  reworkCapProceed = false;
3761
3831
  const progressDelivery = { delivered: 0, unwired: 0, callbackFailed: 0 };
3762
3832
  let capturedPrNumber: number | undefined;
3833
+ let prAttributionMismatch = false;
3763
3834
  let openPrInvoked = false;
3764
3835
  const defaultProviderName = opts.seams.currentProviderName?.() ?? currentProviderName();
3765
3836
  const runProviderName = opts.childLlm?.provider ?? defaultProviderName;
@@ -3778,8 +3849,6 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
3778
3849
  openPrInvoked = true;
3779
3850
  const pr = await opts.seams.openPr(args);
3780
3851
  if (pr.rebind) debug.log('self-implement.same-cell', 'rebind-result', pr.rebind);
3781
- const number = recordedPrNumber(pr.number);
3782
- if (number !== undefined) capturedPrNumber = number;
3783
3852
  return pr;
3784
3853
  },
3785
3854
  onProgress: (event) => {
@@ -4213,6 +4282,9 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
4213
4282
  hasCurrentCredentialFailure,
4214
4283
  (commit: string | undefined) => { finishedImplementationAtMainSync = true; preSyncCommit = commit; },
4215
4284
  preserveMergeHarvest,
4285
+ startedAtMs,
4286
+ (number) => { capturedPrNumber = number; },
4287
+ () => { prAttributionMismatch = true; },
4216
4288
  );
4217
4289
  terminalNode = result.node;
4218
4290
  await mergeChildProviderErrors();
@@ -4257,6 +4329,22 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
4257
4329
  // ⛔ 이 스코프엔 `progress` 헬퍼가 «없다»(3695행 · 다른 스코프) — seam 을 직접 쓴다.
4258
4330
  progress: (stage, message) => { try { opts.seams.onProgress?.({ stage, message }); } catch { /* best-effort */ } },
4259
4331
  });
4332
+ if (addressedResult.abandonedClassification?.classification === 'report-deficit') {
4333
+ const classification = addressedResult.abandonedClassification;
4334
+ const basis = classification.classificationBasis;
4335
+ const kind = basis === 'terminal-state-not-reviewed' || basis === 'review-result-observation-unmeasured'
4336
+ ? 'unmeasured' as const : 'unverified' as const;
4337
+ const previousEvidence = JSON.stringify({ classification, stage: addressedResult.stage,
4338
+ review: addressedResult.review ?? null, gate: addressedResult.gate ?? null });
4339
+ const deficit = { evidenceId: `report:${runId}:${basis}`, kind, previousEvidence, round: roundCount };
4340
+ observeOuter('supervisor-report-deficit', { ...deficit, supervisorResult: 'unverifiable', classification });
4341
+ if (deficit.kind !== 'unverified') {
4342
+ const move = repairNextMove({ supervisorDeficit: deficit, supervisorResult: 'unverifiable' });
4343
+ if (move.kind === 'grounding') observeOuter('repair-next-move-input', {
4344
+ ...move.supervisorDeficit, supervisorResult: 'unverifiable', action: move.action, classification,
4345
+ });
4346
+ }
4347
+ }
4260
4348
  observeRunOutcome(observeOuter, {
4261
4349
  node: addressedResult.node,
4262
4350
  stage: addressedResult.stage,
@@ -4294,7 +4382,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
4294
4382
  ...terminalDetail(addressedResult.sessionId),
4295
4383
  },
4296
4384
  });
4297
- observeRunRollup(observeOuter, addressedResult, roundClassifications, roundCount, capturedPrNumber, openPrInvoked);
4385
+ observeRunRollup(observeOuter, prAttributionMismatch ? { ...addressedResult, prNumber: undefined } : addressedResult, roundClassifications, roundCount, capturedPrNumber, openPrInvoked);
4298
4386
  observeProgressDelivery();
4299
4387
  return terminalResult;
4300
4388
  } catch (e) {
@@ -4369,7 +4457,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
4369
4457
  ...terminalDetail(addressedResult.sessionId ?? terminalSessionId),
4370
4458
  },
4371
4459
  });
4372
- observeRunRollup(observeOuter, addressedResult, roundClassifications, roundCount, capturedPrNumber, openPrInvoked);
4460
+ observeRunRollup(observeOuter, prAttributionMismatch ? { ...addressedResult, prNumber: undefined } : addressedResult, roundClassifications, roundCount, capturedPrNumber, openPrInvoked);
4373
4461
  observeProgressDelivery();
4374
4462
  return terminalResult;
4375
4463
  }
@@ -4394,6 +4482,50 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
4394
4482
  } finally {
4395
4483
  if (loopRegistered) registerRunLoop('ended');
4396
4484
  const terminalStage = terminalResult?.stage ?? 'crashed';
4485
+ // A blocked draft may be opened on the first failed attempt, before the
4486
+ // normal outcome observer has written its terminal status. Persist the
4487
+ // actual terminal result before exposing harvest-deferred on that PR.
4488
+ const terminalOutcome = terminalResult ? mapStageToRunStatus(terminalStage) : undefined;
4489
+ if (terminalOutcome?.runStatus === 'failed'
4490
+ && loadRunLedger(runId)?.filter((entry) => entry.event === 'run-status').at(-1)?.data.runStatus !== 'failed') {
4491
+ observeOuter('run-status', {
4492
+ stage: terminalStage, node: terminalResult?.node ?? null, ...terminalOutcome,
4493
+ ...(terminalResult?.reviewReflectRejectedCount !== undefined
4494
+ ? { reviewReflectRejectedCount: terminalResult.reviewReflectRejectedCount }
4495
+ : {}),
4496
+ }, { level: 'warn' });
4497
+ }
4498
+ try {
4499
+ const runStatus = terminalOutcome?.runStatus;
4500
+ const prNumber = recordedPrNumber(terminalResult?.prNumber) ?? capturedPrNumber;
4501
+ // openPr is required on every seam (including production); it cannot
4502
+ // distinguish test PRs from real ones. The production adapter explicitly
4503
+ // supplies both body operations. A test replacing openPr without both
4504
+ // body operations must fail closed instead of touching a real PR.
4505
+ const readPrBody = opts.seams.readPrBodyForHarvest;
4506
+ const updatePrBody = opts.seams.updatePrBodyForHarvest;
4507
+ // The terminal observer must have persisted this run's failure before a
4508
+ // draft can advertise harvest deferral. Even an injected writer must
4509
+ // leave a matching record in the canonical ledger before mutation.
4510
+ const lastPersistedStatus = loadRunLedger(runId)?.filter((entry) => entry.event === 'run-status').at(-1)?.data.runStatus;
4511
+ const persistedFailure = runStatus === 'failed' && lastPersistedStatus === 'failed';
4512
+ if (persistedFailure && prNumber !== undefined && readPrBody && updatePrBody) {
4513
+ const existing = await readPrBody({ number: prNumber, cwd: worktreePath ?? process.cwd() });
4514
+ if (existing?.state === 'OPEN' && existing.isDraft === true) {
4515
+ const markedBody = markHarvestDeferredBody({ runStatus, prNumber, body: existing.body });
4516
+ if (markedBody !== undefined && markedBody !== existing.body) {
4517
+ await updatePrBody({ number: prNumber, body: markedBody, cwd: worktreePath ?? process.cwd() });
4518
+ observeOuter('harvest-deferred', { runStatus, prNumber, marker: HARVEST_DEFERRED_MARKER });
4519
+ }
4520
+ } else {
4521
+ observeOuter('harvest-deferred-skipped', { runStatus, prNumber, reason: 'existing-draft-not-confirmed' }, { level: 'warn' });
4522
+ }
4523
+ } else {
4524
+ observeOuter('harvest-deferred-skipped', { runStatus: runStatus ?? null, prNumber: prNumber ?? null, reason: 'status-pr-body-seam-unavailable' }, { level: 'warn' });
4525
+ }
4526
+ } catch (error) {
4527
+ logRunAwareFailSoft(runId, 'harvest-deferred-failed', { error: safeErrorDescription(error) });
4528
+ }
4397
4529
  try {
4398
4530
  const mappedStatus = mapStageToRunStatus(terminalStage)?.runStatus;
4399
4531
  const reworkCapStop = terminalResult?.stage === 'pr-opened' && (
@@ -4741,7 +4873,26 @@ export function observeRunOutcome(
4741
4873
  type RollupPrNumber = number | 'none' | 'unknown';
4742
4874
 
4743
4875
  function recordedPrNumber(value: unknown): number | undefined {
4744
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
4876
+ return typeof value === 'number' && Number.isSafeInteger(value) && value > 0 ? value : undefined;
4877
+ }
4878
+
4879
+ /** GitHub's second-precision createdAt cannot distinguish events within that second. */
4880
+ export function prCreatedInRunWindow(createdAt: string, startedAtMs: number): boolean {
4881
+ const createdAtMs = Date.parse(createdAt);
4882
+ if (!Number.isFinite(createdAtMs) || !Number.isFinite(startedAtMs)) return false;
4883
+ const secondPrecision = !/\.\d+(?:Z|[+-]\d\d:\d\d)$/.test(createdAt);
4884
+ return createdAtMs >= (secondPrecision ? Math.floor(startedAtMs / 1_000) * 1_000 : startedAtMs);
4885
+ }
4886
+
4887
+ function prAttributedToRun(
4888
+ attribution: { headRefName: string; createdAt: string }, branch: string, startedAtMs: number,
4889
+ boundCardReuse: { headRefName: string } | undefined,
4890
+ ): boolean {
4891
+ if (attribution.headRefName !== (boundCardReuse?.headRefName ?? branch)) return false;
4892
+ // A verified same-card PR predates this run. The production seam exposes the
4893
+ // exception only after the manager verifies card, PR identity, and leased push.
4894
+ return prCreatedInRunWindow(attribution.createdAt, startedAtMs)
4895
+ || (!!boundCardReuse && Number.isFinite(Date.parse(attribution.createdAt)));
4745
4896
  }
4746
4897
 
4747
4898
  function rollupPrNumber(
@@ -4839,6 +4990,9 @@ async function runSelfImplementInner(
4839
4990
  hasCurrentCredentialFailure: () => boolean = hasCredentialFailure,
4840
4991
  onMainSyncStarted: (commit: string | undefined) => void = () => {},
4841
4992
  preserveMergeHarvest: (reason: string, inputChars?: number) => Promise<boolean> = async () => false,
4993
+ startedAtMs = Date.now(),
4994
+ onPrAttributed: (number: number) => void = () => {},
4995
+ onPrAttributionMismatch: () => void = () => {},
4842
4996
  ): Promise<Omit<SelfImplementResult, 'runId'>> {
4843
4997
  const s = opts.seams;
4844
4998
  const pipelineStartedAtMs = Date.now();
@@ -4869,7 +5023,7 @@ async function runSelfImplementInner(
4869
5023
  const { graphVersionHash } = await import('./graph-yaml.js');
4870
5024
  const { applyGraphOverlays } = await import('./graph-overlay-yaml.js');
4871
5025
  const { createGraphVariant } = await import('./graph-variant.js');
4872
- const { runLedgerPath } = await import('./run-ledger.js');
5026
+ const { saveRunGraphVariant, loadRunGraphVariant } = await import('./run-ledger.js');
4873
5027
  const { stringify } = await import('yaml');
4874
5028
  const baseSpec = GRAPH_SPECS[graphTemplate.graphId];
4875
5029
  const variantRunId = identity?.runId ?? opts.runId;
@@ -4881,16 +5035,16 @@ async function runSelfImplementInner(
4881
5035
  : undefined;
4882
5036
  if (launch && !launch.ok) throw new Error(`graph variant rejected: ${JSON.stringify(launch.rejections)}`);
4883
5037
  const sourceSpec = launch?.ok ? launch.template : baseSpec;
4884
- const variant = createGraphVariant({ template: sourceSpec, goal: `g_${variantRunId.replace(/[^a-z0-9_-]/gi, '_')}`, plan: opts.graphVariantPlan });
5038
+ // ⭐ 최초 변형 선택 — 골 종류(graphGoalType)·대상 경로 수(declaredPremiseScopePaths) 두 입력만으로
5039
+ // 결정론적으로 id·선택 이유를 유도해 검증 결과에 싣는다(종류를 못 읽었으면 지어내지 않는다).
5040
+ const variant = createGraphVariant({ template: sourceSpec, goal: `g_${variantRunId.replace(/[^a-z0-9_-]/gi, '_')}`, plan: opts.graphVariantPlan,
5041
+ ...(graphGoalType === undefined ? {} : { goalAnalysis: { goalType: graphGoalType, targetPathCount: declaredPremiseScopePaths(opts.feature).length } }) });
4885
5042
  if (!variant.ok) throw new Error(`graph variant rejected: ${JSON.stringify(variant.rejections)}`);
4886
5043
  variantSpec = variant.template;
4887
5044
  graphTemplate = { ...specToTemplate(variantSpec), version: graphVersionHash(variantSpec) };
4888
- const { runLedgerDir } = await import('./run-ledger.js');
4889
- const graphFile = `${runLedgerPath(variantRunId)}.graph.yaml`;
4890
- mkdirSync(runLedgerDir(), { recursive: true });
4891
- const { writeFileSync } = await import('node:fs');
4892
- writeFileSync(graphFile, stringify({
5045
+ const graphFile = saveRunGraphVariant(variantRunId, stringify({
4893
5046
  graph_id: variantSpec.graphId, version: variantSpec.version,
5047
+ ...(variant.selection === undefined ? {} : { variant_id: variant.selection.variantId, reason: variant.selection.reason }),
4894
5048
  entry_node: variantSpec.entryNode, terminal_nodes: variantSpec.terminalNodes,
4895
5049
  ...(variantSpec.journeyStages ? { journey_stages: variantSpec.journeyStages } : {}),
4896
5050
  ...(variantSpec.runContract ? { run_contract: variantSpec.runContract } : {}),
@@ -4900,6 +5054,9 @@ async function runSelfImplementInner(
4900
5054
  ...(n.progress ? { progress: n.progress } : {}), ...(n.phases ? { phases: n.phases } : {}),
4901
5055
  ...(n.terminalStages ? { terminal_stages: n.terminalStages } : {}),
4902
5056
  ...(n.fanOut ? { fan_out: n.fanOut } : {}), ...(n.contract ? { contract: n.contract } : {}),
5057
+ ...(n.call ? { call: n.call } : {}),
5058
+ ...(n.callEntry ? { call_entry: n.callEntry } : {}),
5059
+ ...(n.budget ? { budget: { inner_steps: n.budget.innerSteps } } : {}),
4903
5060
  ...(n.skipWhen ? { skip_when: n.skipWhen } : {}),
4904
5061
  })),
4905
5062
  edges: variantSpec.edges.map(e => ({
@@ -4908,9 +5065,17 @@ async function runSelfImplementInner(
4908
5065
  ...(e.observed === undefined ? {} : { observed: e.observed }),
4909
5066
  ...(e.when ? { when: e.when } : {}),
4910
5067
  })),
4911
- }), { flag: 'wx' });
5068
+ }));
5069
+ // ⭐ 저장·diff — 방금 원장 옆에 쓴 선택(id·이유)을 다시 읽어 «같은지» 확인한다(다르면 이 런의 그래프 신원이 거짓).
5070
+ if (variant.selection !== undefined) {
5071
+ const persisted = loadRunGraphVariant(variantRunId);
5072
+ if (persisted === null || persisted.variantId !== variant.selection.variantId || persisted.reason !== variant.selection.reason) {
5073
+ throw new Error(`graph variant rejected: persisted selection mismatch: ${JSON.stringify(persisted)}`);
5074
+ }
5075
+ }
4912
5076
  observe('graph-variant-applied', { graphId: variant.template.graphId, overlayId: variant.overlay.overlayId,
4913
- graphFile, patches: variant.patches });
5077
+ graphFile, patches: variant.patches,
5078
+ ...(variant.selection === undefined ? {} : { variantId: variant.selection.variantId, variantReason: variant.selection.reason }) });
4914
5079
  }
4915
5080
  /** 단계 이름 → 그 템플릿의 노드 이름. */
4916
5081
  const node = (stage: PipelineNodeId): PipelineNodeId => nodeNameForStage(stage, graphTemplate) as PipelineNodeId;
@@ -5017,14 +5182,22 @@ async function runSelfImplementInner(
5017
5182
  : opts.goalFile
5018
5183
  ? 'authored-goal-file'
5019
5184
  : 'no-goal-file';
5020
- let activeProvider: Pick<ActiveProviderInfo, 'provider' | 'model' | 'auth'> | {
5021
- provider: 'unknown'; model: 'unknown'; auth: 'unknown';
5022
- };
5185
+ let activeProvider: { provider: string; model: string; auth: string };
5023
5186
  try {
5024
5187
  activeProvider = (s.inspectActiveProvider ?? inspectActiveProvider)();
5025
5188
  } catch {
5026
5189
  activeProvider = { provider: 'unknown', model: 'unknown', auth: 'unknown' };
5027
5190
  }
5191
+ const configuredProvider = activeProvider;
5192
+ if (opts.childLlm) {
5193
+ // The configured parent model can be sol while this run's implementation child is
5194
+ // explicitly luna. Attribute the run to the model actually passed to implement.
5195
+ activeProvider = {
5196
+ provider: opts.childLlm.provider,
5197
+ model: opts.childLlm.model,
5198
+ auth: configuredProvider.provider?.replace(/^auto:/, '') === opts.childLlm.provider ? configuredProvider.auth : 'unknown',
5199
+ };
5200
+ }
5028
5201
  // ⭐ 관측 이벤트와 «같은 값»을 원장으로도 보낸다 — 로그에만 있으면 원장 질의가 못 센다(`F12`).
5029
5202
  try { onActiveProvider(activeProvider); } catch { /* 보조 기록이 런을 죽이지 않는다 */ }
5030
5203
  const boundedFeature = boundReadableText(opts.feature, RUN_START_FEATURE_MAX_CHARS);
@@ -5038,6 +5211,7 @@ async function runSelfImplementInner(
5038
5211
  nestDepth: nestInfo().depth, goalSource, goalFile: opts.goalFile === undefined ? null : resolve(launchCwd, opts.goalFile),
5039
5212
  targetRoot: gitRootOf(launchCwd) ?? launchCwd, ...originObservationFields(),
5040
5213
  provider: activeProvider.provider ?? null, model: activeProvider.model ?? null, auth: activeProvider.auth ?? null,
5214
+ ...(opts.childLlm ? { configuredProvider: configuredProvider.provider ?? null, configuredModel: configuredProvider.model ?? null, modelSelectionSource: 'child' } : {}),
5041
5215
  ...(opts.correlationId !== undefined ? { correlationId: opts.correlationId } : {}),
5042
5216
  });
5043
5217
  try {
@@ -5091,6 +5265,14 @@ async function runSelfImplementInner(
5091
5265
  ...goalContext,
5092
5266
  }), T.worktree, 'worktree');
5093
5267
  onWorktreeCreated(wt.path);
5268
+ // Capture the child worktree's HEAD at creation. `wt.invokedHead` names the
5269
+ // caller checkout and may differ after base selection or PR-source checkout.
5270
+ const terminalLaunchHeadRead = runGitCommand(wt.path, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8', timeout: 10_000 });
5271
+ const terminalLaunchHeadCandidate = terminalLaunchHeadRead.status === 0 ? terminalLaunchHeadRead.stdout.trim() : '';
5272
+ const terminalLaunchHead = /^[0-9a-f]{40,64}$/i.test(terminalLaunchHeadCandidate) ? terminalLaunchHeadCandidate : undefined;
5273
+ const terminalLaunchWork = detectUncommittedWork(wt.path);
5274
+ const terminalLaunchDirtyPaths = terminalLaunchWork.status === 'unavailable' ? undefined
5275
+ : new Set([...terminalLaunchWork.trackedChanges, ...terminalLaunchWork.untrackedFiles]);
5094
5276
  // ⭐ 제1원칙 — **어디서 갈랐나**를 관측에 남긴다. 이것이 없어서 2026-07-29 에 하니스 런 둘이
5095
5277
  // `main` 위에서 돌았고(호출자가 브랜치 워크트리에서 발사했는데도) 산출 diff 를 눈으로 봐야 알았다.
5096
5278
  // ⚠️ `branch` 는 **새로 만든 브랜치 이름**이라 그 질문에 답하지 않는다.
@@ -5104,6 +5286,8 @@ async function runSelfImplementInner(
5104
5286
  observe('worktree', {
5105
5287
  path: wt.path,
5106
5288
  branch: wt.branch,
5289
+ terminalLaunchHead: terminalLaunchHead ?? null,
5290
+ terminalLaunchDirtyPaths: terminalLaunchDirtyPaths ? [...terminalLaunchDirtyPaths] : null,
5107
5291
  // ⭐ 우리가 **무엇을 요청했는지는 이미 안다** — 시임 반환에서 되읽지 않는다.
5108
5292
  // 커스텀 시임이 관측 필드를 생략하면 명시/생략이 둘 다 null 이 되어 구분이 사라진다(리뷰 must-fix).
5109
5293
  requestedBase,
@@ -5635,6 +5819,9 @@ async function runSelfImplementInner(
5635
5819
  };
5636
5820
  }
5637
5821
  let round = 0;
5822
+ // A timeout is evidence about this command, not an invitation to spend the
5823
+ // next rework round on the identical oversized check.
5824
+ const runBudgetedEvidenceChecks = createRequiredEvidenceBudget({ run: s.evidenceCommandRunner });
5638
5825
  // A goal-only retry spends its one allowance for this run even if a later
5639
5826
  // gate/review round removes the target diff and reaches the same disposition.
5640
5827
  let goalOnlyReworkUsed = false;
@@ -6121,7 +6308,7 @@ async function runSelfImplementInner(
6121
6308
  return preserveReviewBlockedBranch(`review-blocked 작업 커밋 실패: ${commitError}`, false, `; 미커밋 작업은 worktree 에 남음: ${wt.path}`);
6122
6309
  }
6123
6310
  }
6124
- let pr: { url: string; number: number };
6311
+ let pr: OpenPrOutcome;
6125
6312
  try {
6126
6313
  pr = await withStepTimeout(s.openPr({
6127
6314
  title: prTitle(opts.feature, opts.goalFile),
@@ -6172,6 +6359,48 @@ async function runSelfImplementInner(
6172
6359
  : `중단 산출물의 draft PR 생성 실패(worktree·branch 보존 · 판정 본문 보존 실패: ${artifactError ?? 'unknown error'})`);
6173
6360
  return undefined;
6174
6361
  }
6362
+ let draftAttribution: { headRefName: string; createdAt: string } | undefined;
6363
+ let draftAttributionReadError: string | undefined;
6364
+ try {
6365
+ if (s.readPrAttribution) {
6366
+ draftAttribution = await s.readPrAttribution({ number: pr.number, cwd: wt.path });
6367
+ } else {
6368
+ const viewed = spawnSync('gh', ['pr', 'view', String(pr.number), '--json', 'headRefName,createdAt'], { cwd: wt.path, encoding: 'utf8', timeout: 30_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
6369
+ if (viewed.status !== 0) throw new Error('gh pr view failed');
6370
+ const parsed = JSON.parse(viewed.stdout) as { headRefName?: unknown; createdAt?: unknown };
6371
+ if (typeof parsed.headRefName !== 'string' || typeof parsed.createdAt !== 'string') throw new Error('invalid PR attribution metadata');
6372
+ draftAttribution = { headRefName: parsed.headRefName, createdAt: parsed.createdAt };
6373
+ }
6374
+ } catch (error) { draftAttributionReadError = safeErrorDescription(error); }
6375
+ const expectedPrHead = pr.boundCardReuse?.headRefName ?? wt.branch;
6376
+ const draftAttributed = !draftAttributionReadError && !!draftAttribution
6377
+ && prAttributedToRun(draftAttribution, wt.branch, startedAtMs, pr.boundCardReuse);
6378
+ if (draftAttributionReadError || !draftAttribution) {
6379
+ onPrAttributionMismatch();
6380
+ observe('pr-attribution-unverified', {
6381
+ expectedBranch: expectedPrHead,
6382
+ number: pr.number,
6383
+ url: pr.url,
6384
+ reason: draftAttributionReadError ?? 'metadata-absent',
6385
+ }, { level: 'warn' });
6386
+ } else if (draftAttributed) {
6387
+ onPrAttributed(pr.number);
6388
+ observe('pr-opened', { url: pr.url, number: pr.number, branch: expectedPrHead, draft: true });
6389
+ } else {
6390
+ onPrAttributionMismatch();
6391
+ observe('pr-attribution-mismatch', {
6392
+ expectedBranch: expectedPrHead,
6393
+ actualBranch: draftAttribution?.headRefName ?? null,
6394
+ createdAt: draftAttribution?.createdAt ?? null,
6395
+ number: pr.number,
6396
+ url: pr.url,
6397
+ }, { level: 'warn' });
6398
+ }
6399
+ if (!draftAttributed) {
6400
+ blockedDraftPrFailure = `draft PR 귀속 확인 실패; worktree·branch 보존: ${wt.branch} (${wt.path})`;
6401
+ progress('aborted', blockedDraftPrFailure);
6402
+ return undefined;
6403
+ }
6175
6404
  observe('rework-blocked-draft-pr', {
6176
6405
  ...observation,
6177
6406
  url: pr.url,
@@ -6389,6 +6618,19 @@ async function runSelfImplementInner(
6389
6618
  return 'parked';
6390
6619
  }
6391
6620
  };
6621
+ let pendingReviewTriage: {
6622
+ round: number;
6623
+ verdict: 'fail';
6624
+ rawMustFix: string[];
6625
+ reflectedMustFix: string[];
6626
+ reflectionApplied: boolean;
6627
+ effectiveMax: number;
6628
+ } | undefined;
6629
+ const recordPendingReviewTriage = (decision: ReviewRoundDecision, nextNode: 'open-pr' | 'rework' | 'review-blocked', effectiveMax: number): void => {
6630
+ if (!pendingReviewTriage) return;
6631
+ observe('review-round-triage', { ...pendingReviewTriage, decision, nextNode, effectiveMax });
6632
+ pendingReviewTriage = undefined;
6633
+ };
6392
6634
  for (;;) {
6393
6635
  if (round > 0) {
6394
6636
  const stoppedBeforeRework = honorParentSoftStop('rework');
@@ -6441,7 +6683,7 @@ async function runSelfImplementInner(
6441
6683
  const predictionAccuracyBefore = supervisorDecisionHistory.length
6442
6684
  ? judgePredictionAccuracy(supervisorDecisionHistory)
6443
6685
  : undefined;
6444
- diagnosis = await withStepTimeout(s.diagnose({ runId, note: reworkJudgeNote, kind: reworkKind, round, cwd: wt.path, goal: opts.feature, history: priorHistory, supervisorDecisionHistory, ...(predictionAccuracyBefore ? { judgePredictionAccuracy: predictionAccuracyBefore } : {}), effectiveMax, priorRuns, reviewFindingTelemetry: reviewFindingTelemetrySnapshot, ...(diagnosisRefutations ? { refutations: diagnosisRefutations.refutations, refutationRound: diagnosisRefutations.round } : {}), purpose: 'budget' }), T.review, 'review');
6686
+ diagnosis = await withStepTimeout(s.diagnose({ runId, note: reworkJudgeNote, kind: reworkKind, round, cwd: wt.path, goal: roundGoal, history: priorHistory, supervisorDecisionHistory, ...(predictionAccuracyBefore ? { judgePredictionAccuracy: predictionAccuracyBefore } : {}), effectiveMax, priorRuns, reviewFindingTelemetry: reviewFindingTelemetrySnapshot, ...(diagnosisRefutations ? { refutations: diagnosisRefutations.refutations, refutationRound: diagnosisRefutations.round } : {}), purpose: 'budget' }), T.review, 'review');
6445
6687
  const diagnosedDecision = parseReworkBudgetDecision(diagnosis);
6446
6688
  if (diagnosedDecision?.verdict === 'CONTRACT-CONFLICT') lastSupervisorVerdict = diagnosedDecision.verdict;
6447
6689
  let parsedDecision = diagnosedDecision;
@@ -6546,6 +6788,17 @@ async function runSelfImplementInner(
6546
6788
  });
6547
6789
  }
6548
6790
  effectiveMax = budgetDecision.effectiveMax;
6791
+ if (pendingReviewTriage && budgetDecision.stop) {
6792
+ recordPendingReviewTriage('follow-up', budgetDecision.exit === 'proceed' ? 'open-pr' : 'review-blocked', effectiveMax);
6793
+ } else if (pendingReviewTriage && round <= effectiveMax) {
6794
+ const extendedDecision = decideReviewRound({
6795
+ verdict: pendingReviewTriage.verdict,
6796
+ mustFix: pendingReviewTriage.reflectedMustFix,
6797
+ round: pendingReviewTriage.round,
6798
+ effectiveMax,
6799
+ });
6800
+ if (extendedDecision === 'rework') recordPendingReviewTriage('rework', 'rework', effectiveMax);
6801
+ }
6549
6802
  if (parsedDecision) {
6550
6803
  if (diagnosisRefutations) {
6551
6804
  const adjudications = parseSupervisorRefutationAdjudications(diagnosis, diagnosisRefutations.refutations);
@@ -6754,6 +7007,7 @@ async function runSelfImplementInner(
6754
7007
  : 'UNCONVERGEABLE' as const;
6755
7008
  const supervisorOutcome = { supervisorVerdict, ...(goalCauseObserved ? { goalCauseObserved: true as const } : {}) };
6756
7009
  if (isReviewRework(reworkKind)) {
7010
+ recordPendingReviewTriage('follow-up', 'review-blocked', effectiveMax);
6757
7011
  if (reflectRejectedAudit.length && review) review.shouldFix = [...review.shouldFix, ...reflectRejectedAudit];
6758
7012
  const pr = await preserveBlockedArtifacts({ stage: 'review-blocked', verdict: parsedDecision.verdict, reason: parsedDecision.reason, gate, decomposition: terminalDecomposition, salvageStatusExpected: false });
6759
7013
  await draftFollowUpAfterBlocked({ runId, prNumber: pr?.number, stage: 'review-blocked', unresolvedMustFix: review?.mustFix, decompositionPieces: terminalDecomposition?.pieces?.map((p) => p.feature), feature: opts.feature, cellId: opts.goalId, goalFile: opts.goalFile }, s.followUp);
@@ -6785,6 +7039,7 @@ async function runSelfImplementInner(
6785
7039
  const terminal = resolveRunOutcome({ termination: 'supervisor-abandoned', verdict: 'UNCONVERGEABLE', applied: true });
6786
7040
  const reason = `rework judged unconvergeable after ${round - 1} rework round(s)`;
6787
7041
  if (isReviewRework(reworkKind)) {
7042
+ recordPendingReviewTriage('follow-up', 'review-blocked', effectiveMax);
6788
7043
  if (reflectRejectedAudit.length && review) review.shouldFix = [...review.shouldFix, ...reflectRejectedAudit];
6789
7044
  const pr = await preserveBlockedArtifacts({ stage: 'review-blocked', verdict: lastReworkVerdict, reason, gate, salvageStatusExpected: false });
6790
7045
  await draftFollowUpAfterBlocked({ runId, prNumber: pr?.number, stage: 'review-blocked', unresolvedMustFix: review?.mustFix, feature: opts.feature, cellId: opts.goalId, goalFile: opts.goalFile }, s.followUp);
@@ -6828,6 +7083,7 @@ async function runSelfImplementInner(
6828
7083
  );
6829
7084
  podDecision(acceptance.decision.action === 'accept');
6830
7085
  if (acceptance.decision.action === 'accept') {
7086
+ recordPendingReviewTriage('follow-up', 'open-pr', effectiveMax);
6831
7087
  if (podReviewBlockedDraft) {
6832
7088
  observe('merge-decision', {
6833
7089
  autoMerge: autoMergeEnabled(opts), reviewed: review?.reviewed === true, verdict: review?.verdict ?? 'none',
@@ -6866,6 +7122,7 @@ async function runSelfImplementInner(
6866
7122
  };
6867
7123
  }
6868
7124
  } else podDecision(false);
7125
+ recordPendingReviewTriage('follow-up', 'review-blocked', effectiveMax);
6869
7126
  const pr = await preserveBlockedArtifacts({ stage: 'review-blocked', reason, gate, salvageStatusExpected: true });
6870
7127
  const salvage = blockedDraftNoChanges || blockedDraftPrFailure ? 'parked' : await salvageHardCap(canSalvage, pr);
6871
7128
  const disposition = { ...terminal, ...(salvage ? { salvage } : {}) };
@@ -6885,7 +7142,7 @@ async function runSelfImplementInner(
6885
7142
  if (escalationEligible && s.diagnose) {
6886
7143
  try {
6887
7144
  escalationJudgement = await withStepTimeout(s.diagnose({
6888
- runId, note: reworkNote, kind: reworkKind, round, cwd: wt.path, goal: opts.feature,
7145
+ runId, note: reworkNote, kind: reworkKind, round, cwd: wt.path, goal: roundGoal,
6889
7146
  history: reworkHistory.slice(0, -1), supervisorDecisionHistory, effectiveMax,
6890
7147
  purpose: 'escalation-triage',
6891
7148
  }), T.review, 'review');
@@ -6906,7 +7163,9 @@ async function runSelfImplementInner(
6906
7163
  const firstRound = roundSummaryEntries.at(-1);
6907
7164
  const refined = await (await import('./goal-refine.js')).runFirstRoundRefine({
6908
7165
  goal: opts.feature, round, visit: goalRefineVisits, callLLM: s.elaborateCallLLM, observe,
6909
- evidence: { implSummary: firstRound?.tried, findings: reworkNote, changedFiles: firstRound?.filesTouched },
7166
+ // SCOPE-ESCAPE-AS-REFINE — accepted out-of-scope edits are the «new scope» the first implementation found.
7167
+ evidence: { implSummary: firstRound?.tried, findings: reworkNote, changedFiles: firstRound?.filesTouched,
7168
+ ...(scopeExpansion?.files.length ? { newScope: scopeExpansion.files.map((file) => file.path) } : {}) },
6910
7169
  });
6911
7170
  roundGoal = refined.goal;
6912
7171
  // PR «골 변경» 절 — 유지(kept · no-provider 포함)여도 «시도 · v1 유지 · 사유» 한 줄을 싣는다(리뷰 r2).
@@ -7136,6 +7395,45 @@ async function runSelfImplementInner(
7136
7395
  }
7137
7396
  }
7138
7397
  }
7398
+ // The child can report changed=false after committing its edits. Compare
7399
+ // against the worktree HEAD captured at creation, not the caller's HEAD or
7400
+ // origin/main: a PR source already had a diff before the child ran.
7401
+ if (!impl.ok && impl.completionDisposition === 'completed-without-changes'
7402
+ && impl.terminalStatus?.reached === true && impl.terminalStatus.timedOut === false
7403
+ && !hasCurrentProviderError() && !hasCurrentCredentialFailure()) {
7404
+ const committed = terminalLaunchHead
7405
+ ? runGitCommand(wt.path, ['diff', '--name-only', '-z', terminalLaunchHead, 'HEAD'], { encoding: 'utf8', timeout: 10_000 })
7406
+ : undefined;
7407
+ const pending = detectUncommittedWork(wt.path);
7408
+ const observedFiles = committed?.status === 0 && pending.status !== 'unavailable'
7409
+ ? [...new Set([
7410
+ ...committed.stdout.split('\0').filter(Boolean),
7411
+ ...pending.trackedChanges, ...pending.untrackedFiles,
7412
+ ])]
7413
+ : [];
7414
+ if (!terminalLaunchHead || terminalLaunchDirtyPaths === undefined || committed?.status !== 0 || pending.status === 'unavailable') {
7415
+ observe('terminal-change-evidence', { round, status: 'unmeasured',
7416
+ reason: !terminalLaunchHead ? 'launch-head-unavailable' : terminalLaunchDirtyPaths === undefined
7417
+ ? 'launch-status-unavailable' : committed?.status !== 0 ? 'git-diff-unavailable' : 'git-status-unavailable' }, { level: 'warn' });
7418
+ }
7419
+ // A PR-source worktree may be dirty before this child starts. A path
7420
+ // already dirty at launch cannot prove this child's edits from names
7421
+ // alone, even if the child later commits it. Leave that path unmeasured.
7422
+ const implementationFiles = terminalLaunchDirtyPaths === undefined ? [] : observedFiles.filter(path =>
7423
+ !terminalLaunchDirtyPaths.has(path) && !harnessSeededPaths.includes(path) && !isHarnessTempArtifact(path));
7424
+ const preexistingDirtyPaths = terminalLaunchDirtyPaths === undefined ? [] : observedFiles.filter(path =>
7425
+ terminalLaunchDirtyPaths.has(path) && !harnessSeededPaths.includes(path) && !isHarnessTempArtifact(path));
7426
+ if (preexistingDirtyPaths.length > 0) observe('terminal-change-evidence', {
7427
+ round, status: 'unmeasured', reason: 'preexisting-dirty-path', paths: preexistingDirtyPaths,
7428
+ }, { level: 'warn' });
7429
+ if (implementationFiles.length > 0) {
7430
+ observe('terminal-change-reconciled', {
7431
+ round, source: 'post-launch-git-diff', launchHead: terminalLaunchHead, childDisposition: impl.completionDisposition,
7432
+ paths: implementationFiles, pathCount: implementationFiles.length,
7433
+ });
7434
+ impl = { ...impl, ok: true, completionDisposition: undefined };
7435
+ }
7436
+ }
7139
7437
  // A finished child can leave edits without a commit. Measure them before any
7140
7438
  // diff/no-changes classification, and commit only paths declared in the goal.
7141
7439
  finishedChildCommitMissing = false;
@@ -7184,7 +7482,7 @@ async function runSelfImplementInner(
7184
7482
  env.GIT_COMMITTER_NAME = childName;
7185
7483
  env.GIT_COMMITTER_EMAIL = childEmail;
7186
7484
  const git = (args: string[]) => spawnSync('git', ['--no-optional-locks', ...args], {
7187
- cwd, env, encoding: 'utf8', timeout: 20_000,
7485
+ cwd, env, encoding: 'utf8', timeout: 20_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER,
7188
7486
  });
7189
7487
  const staged = git(['add', '-A', '--', ...paths]);
7190
7488
  if (staged.status !== 0) return { ok: false, out: staged.stderr || staged.stdout || staged.error?.message || 'git add failed' };
@@ -7241,7 +7539,13 @@ async function runSelfImplementInner(
7241
7539
  // E3 — refine 뒤 라운드는 «현재 골»(v2)로 증거를 센다(REQUIRED EVIDENCE 절은 고정 칸이라 v1 과 같다).
7242
7540
  const requiredEvidence = requiredEvidenceFromGoal(roundGoal);
7243
7541
  const limitationCount = authorLimitationCountFromGoal(roundGoal);
7244
- const evidenceChecks = runRequiredEvidenceChecks(roundGoal, wt.path, runId);
7542
+ const evidenceHead = resolveEvidenceHeadCommit(s.headCommit?.(wt.path), () =>
7543
+ runGitCommand(wt.path, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8', timeout: 10_000 }).stdout.trim());
7544
+ if (!evidenceHead) {
7545
+ try { debug.log('self-implement.evidence', 'command-budget-head-unresolved', { runId, round, cwd: wt.path }); }
7546
+ catch { /* observation is fail-soft */ }
7547
+ }
7548
+ const evidenceChecks = runBudgetedEvidenceChecks(roundGoal, wt.path, runId, evidenceHead);
7245
7549
  const tagCoverage = coverRequiredEvidence(requiredEvidence, offDiffEvidence.items);
7246
7550
  // EVIDENCE-WIRING-EXEMPT — a requirement whose own `|| bun test …` check passed is covered by what the harness measured.
7247
7551
  const settledByCheck = settleUncoveredEvidence(tagCoverage.uncovered, { checks: evidenceChecks, required: requiredEvidence });
@@ -7387,7 +7691,7 @@ async function runSelfImplementInner(
7387
7691
  files: decision.expansion.files });
7388
7692
  if ('parked' in result) progress('parked', `critical 질문 ${result.questionId} 미응답 — 이 런만 대기`);
7389
7693
  return { ok: false, stage: 'parked' in result ? 'parked' : 'soft-stopped', node: 'implement', ...resolveRunOutcome({ termination: 'abandoned' }), sessionId, worktreePath: wt.path, branch: wt.branch,
7390
- outOfScopeEdits, nameCapReached: scope.nameCapReached, outsideCount: scope.outsideCount,
7694
+ outOfScopeEdits, ...(!decision.accept ? { scopeStopReasons: decision.reasons } : {}), nameCapReached: scope.nameCapReached, outsideCount: scope.outsideCount,
7391
7695
  detail: 'parked' in result ? `critical-unanswered: ${result.questionId}` : 'Out-of-scope edits held in the worktree; gate and publication skipped.' };
7392
7696
  }
7393
7697
  }
@@ -7684,6 +7988,43 @@ async function runSelfImplementInner(
7684
7988
  if (untracked.status === 0) untrackedFiles = untracked.stdout.split(/\r?\n/).filter(Boolean);
7685
7989
  } catch { /* change discovery must not stop review */ }
7686
7990
  }
7991
+ // Gate has passed. Measure newly exported runtime values before review, without
7992
+ // turning a static-analysis guess into a rework verdict during the first rollout.
7993
+ const wiringFiles = [...new Set([...(runFacts.changedFiles ?? []), ...(gateRouteFiles ?? []), ...untrackedFiles])];
7994
+ let goalWiring: 'unwired' | undefined;
7995
+ if (wiringFiles.some((file) => /^(?:src|scripts|apps\/pwa\/src)\/.*\.tsx?$/.test(file))) {
7996
+ const wiringBase = wt.resolvedBase;
7997
+ const wiring = wiringBase && /^[0-9a-f]{40}$/i.test(wiringBase)
7998
+ ? (s.checkUnwiredExports ?? findUnwiredExports)({
7999
+ cwd: wt.path,
8000
+ changedFiles: wiringFiles,
8001
+ // Timeouts and git errors throw → recorded as `failure`, never as "absent at base".
8002
+ readBaseFile: gitBaseFileReader(
8003
+ (args) => runGitCommand(wt.path, args, { encoding: 'utf8', timeout: 10_000 }),
8004
+ wiringBase,
8005
+ ),
8006
+ })
8007
+ : { checked: 0, unwired: [], failure: 'resolved-base-unavailable' };
8008
+ if (!wiring.failure && opts.goalFile && wiring.unwired.length > 0) {
8009
+ try {
8010
+ const declared = declaredPathsFromGoalDocumentText(readFileSync(opts.goalFile, 'utf8'));
8011
+ if (declared.readable && wiring.unwired.some(({ file }) =>
8012
+ declared.paths.some((target) => normalizeDeclaredScopePath(target) === normalizeDeclaredScopePath(file)))) {
8013
+ goalWiring = 'unwired';
8014
+ }
8015
+ } catch { /* unreadable goal text cannot establish a wiring deficit */ }
8016
+ }
8017
+ const wiringObservation = {
8018
+ round,
8019
+ checked: wiring.checked,
8020
+ unwired: wiring.unwired.length,
8021
+ examples: wiring.unwired.slice(0, 10),
8022
+ ...(wiring.failure ? { failure: wiring.failure } : {}),
8023
+ mode: 'warn',
8024
+ };
8025
+ debug.log('self-implement.wiring-check', wiring.failure ? 'unmeasured' : 'measured', wiringObservation);
8026
+ observe('wiring-check', wiringObservation, wiring.failure || wiring.unwired.length ? { level: 'warn' } : {});
8027
+ }
7687
8028
  if ([...(runFacts.changedFiles ?? []), ...(gateRouteFiles ?? []), ...untrackedFiles].some((file) => /\.(?:html|css)$/i.test(file))) {
7688
8029
  try {
7689
8030
  designGate = (s.runDesignGate ?? runDesignGate)({ projectDir: wt.path, base: wt.resolvedBase });
@@ -7798,15 +8139,74 @@ async function runSelfImplementInner(
7798
8139
  : `리뷰 미실행${review.failureReason !== undefined ? ` (${review.failureReason})` : ''}`);
7799
8140
  if (opts.goalFile) {
7800
8141
  let goalSlotFill: SupervisorGoalFillResult = 'unverifiable';
8142
+ let goalSlotDeficit: import('./goal-supervisor-fill.js').SupervisorGoalDeficit | undefined;
8143
+ let goalSlotAction: 'remeasure' | 'connect-goal' | undefined;
7801
8144
  try {
7802
8145
  goalSlotFill = await fillUnverifiableGoalSlots({
7803
8146
  path: opts.goalFile, round, gate, review,
8147
+ ...(goalWiring ? { wiring: goalWiring } : {}),
7804
8148
  independentlyCheck: s.independentlyCheckGoalSlots ?? (async () => false),
8149
+ observeDeficit: (deficit) => { goalSlotDeficit = deficit; },
7805
8150
  });
7806
8151
  } catch {
7807
8152
  goalSlotFill = 'rejected';
7808
8153
  }
7809
- observe('goal-slot-fill', { round, result: goalSlotFill, gatePassed: gate.passed, reviewVerdict: review.verdict });
8154
+ if (goalSlotFill === 'not-applicable') {
8155
+ observe('goal-slot-remeasure-result', { round, result: goalSlotFill, gatePassed: gate.passed,
8156
+ reason: 'test-step-scope-skipped', scopeReason: gate.scopeReason, testStepExecuted: gate.testStepExecuted });
8157
+ }
8158
+ if (goalSlotDeficit) {
8159
+ observe('supervisor-report-deficit', { runId, ...goalSlotDeficit, supervisorResult: goalSlotFill });
8160
+ if (goalSlotDeficit.kind !== 'unverified') {
8161
+ const move = repairNextMove({ supervisorDeficit: goalSlotDeficit,
8162
+ supervisorResult: goalSlotFill === 'rejected' ? 'rejected' : 'unverifiable' });
8163
+ if (move.kind === 'grounding') {
8164
+ goalSlotAction = move.action;
8165
+ observe('repair-next-move-input', { runId, ...move.supervisorDeficit,
8166
+ supervisorResult: goalSlotFill, action: move.action });
8167
+ }
8168
+ }
8169
+ }
8170
+ if (goalSlotFill === 'unverifiable' && gate.passed && goalSlotAction === 'remeasure' && goalSlotDeficit && runsGate) {
8171
+ observe('goal-slot-remeasure', { round, evidenceId: goalSlotDeficit.evidenceId, action: 'remeasure' });
8172
+ const measuredGate = await stepTimeout(s.gate(wt.path, { runId, round }), T.gate, 'gate');
8173
+ gate = measuredGate;
8174
+ let remeasureDeficit: import('./goal-supervisor-fill.js').SupervisorGoalDeficit | undefined;
8175
+ if (gate.passed) {
8176
+ goalSlotFill = await fillUnverifiableGoalSlots({ path: opts.goalFile, round, gate, review,
8177
+ independentlyCheck: s.independentlyCheckGoalSlots ?? (async () => false),
8178
+ observeDeficit: (deficit) => { remeasureDeficit = deficit; } });
8179
+ }
8180
+ observe('goal-slot-remeasure-result', { round, evidenceId: goalSlotDeficit.evidenceId, result: goalSlotFill,
8181
+ gatePassed: measuredGate.passed,
8182
+ ...(remeasureDeficit ? { deficitKind: remeasureDeficit.kind,
8183
+ deficitEvidenceId: remeasureDeficit.evidenceId, gateLog: remeasureDeficit.gateLog,
8184
+ reason: remeasureDeficit.reason, inputSummary: remeasureDeficit.inputSummary, gateLogRef: remeasureDeficit.gateLogRef } : {}) });
8185
+ if (!gate.passed || goalSlotFill === 'unverifiable' || goalSlotFill === 'rejected') {
8186
+ finalizeSupervisorDeliveries('goal-slot-remeasure-gate-failed');
8187
+ const reason = `goal-slot remeasurement ${!gate.passed ? 'failed' : goalSlotFill === 'rejected' ? 'rejected' : 'unmeasured'} · evidence ${goalSlotDeficit.evidenceId}`;
8188
+ const pr = await preserveBlockedArtifacts({ stage: 'gate-failed', reason, gate, salvageStatusExpected: false });
8189
+ return { ok: false, stage: 'gate-failed', node: 'rework', ...resolveRunOutcome({ termination: 'abandoned' }),
8190
+ sessionId, worktreePath: wt.path, branch: wt.branch, gate,
8191
+ quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(),
8192
+ ...(pr ? { prUrl: pr.url, prNumber: pr.number } : {}), detail: reason };
8193
+ }
8194
+ }
8195
+ observe('goal-slot-fill', { round, result: goalSlotFill, gatePassed: gate.passed, reviewVerdict: review.verdict,
8196
+ ...(goalSlotFill === 'unverifiable' && goalSlotDeficit ? { reason: goalSlotDeficit.reason, inputSummary: goalSlotDeficit.inputSummary, gateLogRef: goalSlotDeficit.gateLogRef } : {}),
8197
+ ...(goalSlotFill === 'not-applicable' ? { reason: 'test-step-scope-skipped', scopeReason: gate.scopeReason,
8198
+ testStepExecuted: gate.testStepExecuted } : {}) });
8199
+ if (goalSlotAction === 'connect-goal' && goalSlotDeficit) {
8200
+ finalizeSupervisorDeliveries('goal-slot-connect-goal');
8201
+ const reason = `goal-slot wiring deficit · evidence ${goalSlotDeficit.evidenceId}`;
8202
+ const pr = await preserveBlockedArtifacts({ stage: 'review-blocked', reason, gate, salvageStatusExpected: false });
8203
+ await draftFollowUpAfterBlocked({ runId, prNumber: pr?.number, stage: 'review-blocked',
8204
+ unresolvedMustFix: [reason], feature: opts.feature, cellId: opts.goalId, goalFile: opts.goalFile }, s.followUp);
8205
+ return { ok: false, stage: 'review-blocked', node: 'rework', ...resolveRunOutcome({ termination: 'abandoned' }),
8206
+ sessionId, worktreePath: wt.path, branch: wt.branch, gate, review,
8207
+ quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(),
8208
+ ...(pr ? { prUrl: pr.url, prNumber: pr.number } : {}), detail: reason };
8209
+ }
7810
8210
  }
7811
8211
  const evidenceFacts: ReflectEvidenceFacts = evidenceCoverage
7812
8212
  ? { requiredEvidence: evidenceCoverage.required, coveredEvidence: evidenceCoverage.covered, missingEvidence: evidenceCoverage.uncovered }
@@ -7868,6 +8268,9 @@ async function runSelfImplementInner(
7868
8268
  });
7869
8269
  } catch { /* fail-safe: REFUTE never changes a finding without supervisor judgment */ }
7870
8270
  }
8271
+ const rawReviewMustFix = [...review.mustFix];
8272
+ let reflectionApplied = false;
8273
+ let reviewTriageRecorded = false;
7871
8274
  if (review.verdict === 'fail' && review.mustFix.length > 0) {
7872
8275
  let effectiveMustFix = review.mustFix;
7873
8276
  if (s.reflectMustFix) {
@@ -7876,6 +8279,7 @@ async function runSelfImplementInner(
7876
8279
  try {
7877
8280
  const recurrenceHistory = recurrenceHistoryFor(review.mustFix);
7878
8281
  const reflected = await withStepTimeout(s.reflectMustFix({ mustFix: review.mustFix, goal: roundGoal, cwd: wt.path, evidenceFacts, gateFacts: gate.reflectGateFacts, citedPathFacts, ...(recurrenceHistory.length ? { recurrenceHistory } : {}), runId, round }), T.review, 'review');
8282
+ reflectionApplied = true;
7879
8283
  const rejectedItems = new Set(reflected.rejected.filter((r) => r.reason && r.reason.trim().length > 0).map((r) => r.item));
7880
8284
  const rejected = reflected.rejected.filter((r) => rejectedItems.has(r.item));
7881
8285
  effectiveMustFix = review.mustFix.filter((m) => !rejectedItems.has(m));
@@ -7912,6 +8316,17 @@ async function runSelfImplementInner(
7912
8316
  }
7913
8317
  }
7914
8318
  if (effectiveMustFix.length === 0) {
8319
+ observe('review-round-triage', {
8320
+ round,
8321
+ verdict: 'fail',
8322
+ rawMustFix: rawReviewMustFix,
8323
+ reflectedMustFix: effectiveMustFix,
8324
+ reflectionApplied,
8325
+ decision: 'pass',
8326
+ nextNode: 'open-pr',
8327
+ effectiveMax,
8328
+ });
8329
+ reviewTriageRecorded = true;
7915
8330
  finalizeSupervisorDeliveries('review-reflect-converged');
7916
8331
  completionStatus = 'review-reflect-converged';
7917
8332
  review.mustFix = [];
@@ -7921,6 +8336,21 @@ async function runSelfImplementInner(
7921
8336
  progress('reviewed', '리뷰: fail → warn (반사가 must-fix 를 «전부» 기각 — 사유는 위 줄과 PR 코멘트에)');
7922
8337
  break;
7923
8338
  }
8339
+ const reviewDecision = decideReviewRound({ verdict: review.verdict, mustFix: effectiveMustFix, round, effectiveMax });
8340
+ const reviewTriage = {
8341
+ round,
8342
+ verdict: review.verdict,
8343
+ rawMustFix: rawReviewMustFix,
8344
+ reflectedMustFix: [...effectiveMustFix],
8345
+ reflectionApplied,
8346
+ effectiveMax,
8347
+ };
8348
+ if (reviewDecision === 'follow-up') {
8349
+ pendingReviewTriage = { ...reviewTriage, verdict: 'fail' };
8350
+ } else {
8351
+ observe('review-round-triage', { ...reviewTriage, decision: reviewDecision, nextNode: 'rework' });
8352
+ reviewTriageRecorded = true;
8353
+ }
7924
8354
  appliedLastRound = [...effectiveMustFix];
7925
8355
  roundSummaryEntries.push({ round, kind: 'review', tried: impl.summary.replace(/\s+/g, ' ').trim().slice(0, 200),
7926
8356
  failedIds: effectiveMustFix.map(stableMustFixId), filesTouched: runFacts.changedFiles,
@@ -7973,6 +8403,18 @@ async function runSelfImplementInner(
7973
8403
  round++;
7974
8404
  continue;
7975
8405
  }
8406
+ if (!reviewTriageRecorded && !pendingReviewTriage) {
8407
+ observe('review-round-triage', {
8408
+ round,
8409
+ verdict: review.verdict,
8410
+ rawMustFix: rawReviewMustFix,
8411
+ reflectedMustFix: rawReviewMustFix,
8412
+ reflectionApplied: false,
8413
+ decision: 'pass',
8414
+ nextNode: 'open-pr',
8415
+ effectiveMax,
8416
+ });
8417
+ }
7976
8418
  }
7977
8419
  // EVIDENCE-WIRING-EXEMPT — a commandless [preservation] is covered once the changed-file gate passed.
7978
8420
  if (gate.passed && evidenceCoverage?.uncoveredCount) {
@@ -8044,10 +8486,13 @@ async function runSelfImplementInner(
8044
8486
  // (모집단 0인데 distinguishes=1) 틀린 값으로 막으면 새 병목이 된다.
8045
8487
  // ⛔ `ran === false`(skipped)는 «다른 것»이다 — 자식이 테스트를 안 건드린 경우이고
8046
8488
  // 그건 `skippedReason` 이 이미 가른다. 그래서 여기 조건에 넣지 않는다.
8047
- const verifyInconclusive = gate.verifyByBreaking?.ran === true && (gate.verifyByBreaking.distinguishes ?? 0) === 0;
8048
- const verifyNothingAtBase = verifyInconclusive && (gate.verifyByBreaking?.missingAtBase ?? 0) > 0;
8489
+ // A new test absent from the fixed base is a structural RED witness when the
8490
+ // head gate passed. Keep that distinct from an executed test that proved nothing.
8491
+ const verifyStructuralRed = gate.verifyByBreaking?.ran === true && (gate.verifyByBreaking.missingAtBase ?? 0) > 0;
8492
+ const verifyInconclusive = gate.verifyByBreaking?.ran === true
8493
+ && (gate.verifyByBreaking.distinguishes ?? 0) === 0 && !verifyStructuralRed;
8049
8494
  let mergeReason = canAuto && reviewDeferredToHost ? REVIEW_DEFERRED_TO_HOST_MERGE_REASON
8050
- : canAuto ? (verifyNothingAtBase ? 'review-clean-verify-nothing-at-base' : verifyInconclusive ? 'review-clean-verify-inconclusive' : 'review-clean-armed')
8495
+ : canAuto ? (verifyStructuralRed ? 'review-clean-verify-structural-red' : verifyInconclusive ? 'review-clean-verify-inconclusive' : 'review-clean-armed')
8051
8496
  : gateTimeoutUnmeasured ? GATE_TIMEOUT_UNMEASURED_MERGE_REASON
8052
8497
  : !autoMerge ? 'no-auto-flag'
8053
8498
  : requiredEvidenceMissing ? 'required-evidence-uncovered'
@@ -8316,7 +8761,7 @@ async function runSelfImplementInner(
8316
8761
  s.persistPrBodyArtifact,
8317
8762
  'self-implement-pr',
8318
8763
  );
8319
- let pr: { url: string; number: number };
8764
+ let pr: OpenPrOutcome;
8320
8765
  try {
8321
8766
  pr = await withStepTimeout(s.openPr({
8322
8767
  title: prTitle(opts.feature, opts.goalFile),
@@ -8347,15 +8792,64 @@ async function runSelfImplementInner(
8347
8792
  const current = runGitCommand(wt.path, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8', timeout: 10_000 });
8348
8793
  if (current.status !== 0 || current.stdout.trim() !== reviewedHeadCommit) delete reviewedHeadResult.reviewedHeadCommit;
8349
8794
  }
8350
- observe('pr-opened', {
8351
- url: pr.url,
8352
- number: pr.number,
8353
- branch: wt.branch,
8354
- headCommit: runGitCommand(wt.path, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8', timeout: 10_000 }).stdout.trim(),
8355
- autoMerge: canAuto,
8356
- autoReview: !!labels,
8357
- ...(preparedPrBody.truncated ? { prBodyTruncated: true, prBodyOriginalChars: preparedPrBody.originalChars } : {}),
8358
- });
8795
+ let prAttribution: { headRefName: string; createdAt: string } | undefined;
8796
+ let prAttributionReadError: string | undefined;
8797
+ try {
8798
+ if (s.readPrAttribution) {
8799
+ prAttribution = await s.readPrAttribution({ number: pr.number, cwd: wt.path });
8800
+ } else {
8801
+ const viewed = spawnSync('gh', ['pr', 'view', String(pr.number), '--json', 'headRefName,createdAt'], {
8802
+ cwd: wt.path, encoding: 'utf8', timeout: 30_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER,
8803
+ });
8804
+ if (viewed.status !== 0) throw new Error(viewed.stderr || viewed.stdout || 'gh pr view failed');
8805
+ const parsed = JSON.parse(viewed.stdout) as { headRefName?: unknown; createdAt?: unknown };
8806
+ if (typeof parsed.headRefName !== 'string' || typeof parsed.createdAt !== 'string') throw new Error('invalid PR attribution metadata');
8807
+ prAttribution = { headRefName: parsed.headRefName, createdAt: parsed.createdAt };
8808
+ }
8809
+ } catch (error) {
8810
+ prAttributionReadError = safeErrorDescription(error);
8811
+ }
8812
+ const expectedPrHead = pr.boundCardReuse?.headRefName ?? wt.branch;
8813
+ const prAttributed = !prAttributionReadError && !!prAttribution
8814
+ && prAttributedToRun(prAttribution, wt.branch, startedAtMs, pr.boundCardReuse);
8815
+ if (prAttributionReadError || !prAttribution) {
8816
+ onPrAttributionMismatch();
8817
+ observe('pr-attribution-unverified', {
8818
+ expectedBranch: expectedPrHead,
8819
+ number: pr.number,
8820
+ url: pr.url,
8821
+ reason: prAttributionReadError ?? 'metadata-absent',
8822
+ }, { level: 'warn' });
8823
+ } else if (!prAttributed) {
8824
+ onPrAttributionMismatch();
8825
+ observe('pr-attribution-mismatch', {
8826
+ expectedBranch: expectedPrHead,
8827
+ actualBranch: prAttribution.headRefName,
8828
+ createdAt: prAttribution.createdAt,
8829
+ number: pr.number,
8830
+ url: pr.url,
8831
+ }, { level: 'warn' });
8832
+ } else {
8833
+ const attributedNumber = recordedPrNumber(pr.number);
8834
+ if (attributedNumber !== undefined) onPrAttributed(attributedNumber);
8835
+ observe('pr-opened', {
8836
+ url: pr.url,
8837
+ number: pr.number,
8838
+ branch: expectedPrHead,
8839
+ headCommit: runGitCommand(wt.path, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8', timeout: 10_000 }).stdout.trim(),
8840
+ autoMerge: canAuto,
8841
+ autoReview: !!labels,
8842
+ ...(preparedPrBody.truncated ? { prBodyTruncated: true, prBodyOriginalChars: preparedPrBody.originalChars } : {}),
8843
+ });
8844
+ }
8845
+ if (!prAttributed) {
8846
+ progress('aborted', `PR 귀속 확인 실패 — 외부 PR 후속 처리 중단 (${wt.branch}; worktree: ${wt.path})`);
8847
+ return {
8848
+ ok: false, stage: 'aborted', node: 'open-pr', ...resolveRunOutcome({ termination: 'abandoned' }),
8849
+ ...decisionSignalResult, ...scopeExpansionResult, sessionId, worktreePath: wt.path, branch: wt.branch,
8850
+ gate, ...(review ? { review } : {}), detail: 'PR attribution could not be verified; worktree and branch preserved',
8851
+ };
8852
+ }
8359
8853
  await flushPrComments(pr);
8360
8854
 
8361
8855
  // Inspect the opened PR itself: the goal text and the pre-sync worktree can differ from its final file list.