elanous 0.2.32 → 0.2.35

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 (753) 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/{IVKr3AlRUVLKsw2fh7eXF → T1mBQqmkwKDLGtyisDCOd}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{997ce8a5-2ad501927003b41b.js → 0368f25d-ad84af92fefee68f.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1165.bad13fc9151a7288.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/13.02a07436dec15f89.js +32 -0
  7. package/apps/pwa/out/_next/static/chunks/{5525.86debf231e181813.js → 1530.0e612369b6fd7ea0.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +41 -0
  9. package/apps/pwa/out/_next/static/chunks/{7391.ebae168025c91b6a.js → 1674.2a65fd5668e1cc5f.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +24 -0
  11. package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +74 -0
  12. package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/{7947-d0759430f8042240.js → 1993-c0007b7ad0c0ef41.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/{60fb96c9.360b01d8d4927b7b.js → 1c735de4.f993a30bbd3c57c4.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/1dc5a567.9b092bfae3d7099b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{5307.60f2209e809bd627.js → 2018.a741297bce759490.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +148 -0
  23. package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/{3902.e1e71aaac850a045.js → 2625.5d870aa999f33db1.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +77 -0
  26. package/apps/pwa/out/_next/static/chunks/2833-0fe47af99ed13c29.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +62 -0
  29. package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +90 -0
  31. package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/{4174.92ed04922709ac9e.js → 3408.21d1570602f4b6c0.js} +2 -2
  33. package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3589.5444a06bcf6e775c.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/359-2250789a2d3499e7.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +36 -0
  37. package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{9398-6aed28f0a96d0d6f.js → 3677-4215f826f6aa68e5.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +79 -0
  40. package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3f7c4ee5.3dde54f01a816613.js +122 -0
  42. package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/{7371.80c05c653b08e907.js → 4346.8601952e73bf5803.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/4441-875b97f5821ed58a.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +286 -0
  46. package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +24 -0
  49. package/apps/pwa/out/_next/static/chunks/{2940-619bfd8ef337c611.js → 4705-194e64e47ca0fbdb.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/{1335-6731f392c35216eb.js → 4718-17d2af636796a330.js} +3 -3
  51. package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/{7502.ccd5b8065545edd0.js → 5101.e32282718f99ed6f.js} +2 -2
  57. package/apps/pwa/out/_next/static/chunks/{2001.6c7e0fd02190886c.js → 5154.a118f3957ed14e36.js} +1 -1
  58. package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +23 -0
  61. package/apps/pwa/out/_next/static/chunks/{5173-233188df2f42c626.js → 5483-dd5d7697f6bdb106.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +94 -0
  63. package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/{3267c0e6.d34854e7b3984a6e.js → 56d372f2.681ab4ec4c998fae.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +287 -0
  67. package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/{6433.fc573af22bbc9ab7.js → 6236.e18cced2d5ba6afb.js} +17 -17
  74. package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +8 -0
  75. package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +27 -0
  78. package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +93 -0
  80. package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6866-79aa5b4e47cac6aa.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/{6544cb48.a87fe20ddf137be2.js → 6b89c8bd.24df687a820500b3.js} +1 -1
  83. package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +149 -0
  87. package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +104 -0
  88. package/apps/pwa/out/_next/static/chunks/{2871.0bca6137343b3f76.js → 7385.50a308a477bdfc21.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/{2985.39ccdecd36848b12.js → 7644.17e37ba6f1d4226e.js} +4 -4
  90. package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/{8018.a32663f04a6fc5f9.js → 8219.baa715d1deee80e2.js} +1 -1
  95. package/apps/pwa/out/_next/static/chunks/{4111.2bfedbb233ad206b.js → 8256.e3ac8421a71f82f4.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/{2213.2ccbada92c33ea0f.js → 8444.4c26b379ca2b9596.js} +17 -17
  100. package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +226 -0
  101. package/apps/pwa/out/_next/static/chunks/8557-e7898769499cd56f.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +64 -0
  104. package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/{6092.a7f99e68b188ca66.js → 8637.47f5b35afcae5e6b.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{3278.2e5d15b53a4091c8.js → 9214.63a510d39178e997.js} +2 -2
  111. package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +115 -0
  112. package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/{9221.7d22eaf353a2d4e7.js → 938.66ad1ef0e5abae1c.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/{6157.0c9af021de4fb31b.js → 9512.5a76207a730e0491.js} +2 -2
  115. package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{69-0a2c8209c6d4a158.js → 967-2aa39b2d04b4c690.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/{7862.30a8d6482bb238eb.js → 9952.5daf5f94063a2b3e.js} +1 -1
  120. package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/{page-a27b1e5a0d1b308f.js → page-406b47745b53f269.js} +1 -1
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/{page-32866485913d6a04.js → page-47c5d4db587266bd.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1f5a60f70cb4dd0c.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-bac7dee94e5eae4a.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/{page-04d00ae9b0890349.js → page-dc0a532f5b9196ec.js} +1 -1
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{7a1887c4.f36b88fba31da8fd.js → b2f6ba4d.fb56fdbbf798df33.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/b6705a4f-b9a9047de9eecfd0.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/{b313b242.cb1265eea908db61.js → cbe38a15.5a032598e39970c6.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/{409f1e57.d0649fc47691ebfe.js → d06f9160.a3d9062f95de8ad7.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/{0bb0ff36.b8d418980951e5ba.js → da5176c6.6773b6c5994e1d99.js} +7 -7
  178. package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +1 -0
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +22 -22
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +22 -22
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +22 -22
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +21 -21
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +21 -21
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +22 -22
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +21 -21
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +22 -22
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +22 -22
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +21 -21
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +22 -22
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +22 -22
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +22 -22
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +22 -22
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +21 -21
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +21 -21
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +22 -22
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +22 -22
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +22 -22
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +21 -21
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +21 -21
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +22 -22
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +22 -22
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +21 -21
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +21 -21
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +21 -21
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +22 -22
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +22 -22
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +21 -21
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +22 -22
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +22 -22
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +22 -22
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +22 -22
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +22 -22
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +21 -21
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +22 -22
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +22 -22
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +22 -22
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +22 -22
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +22 -22
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +22 -22
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +22 -22
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +22 -22
  277. package/bin/elanous.mjs +10 -2
  278. package/catalog/providers/opengateway.yaml +25 -0
  279. package/docs/goal-context/01-goal-authoring-checks.md +8 -0
  280. package/docs/goal-context/03-verification-menu.md +24 -0
  281. package/docs/goal-context/04-mock-scenarios.md +100 -0
  282. package/docs/goal-context/README.md +5 -0
  283. package/graphs/catalog/catalog.yaml +1 -1
  284. package/graphs/default-loop.yaml +76 -5
  285. package/graphs/document-loop.yaml +71 -4
  286. package/graphs/implement-loop.yaml +59 -4
  287. package/graphs/journey/SCENARIOS.md +33 -0
  288. package/graphs/journey/recipes.yaml +1 -0
  289. package/graphs/journey/ta-journey.yaml +94 -0
  290. package/graphs/journey/units/author/author.yaml +20 -0
  291. package/graphs/journey/units/author/recipes.yaml +2 -0
  292. package/graphs/journey/units/author-dialogue/author-dialogue.yaml +42 -0
  293. package/graphs/journey/units/author-dialogue/observe.ts +96 -0
  294. package/graphs/journey/units/author-dialogue/recipes.yaml +10 -0
  295. package/graphs/journey/units/green/green.yaml +20 -0
  296. package/graphs/journey/units/green/recipes.yaml +2 -0
  297. package/graphs/journey/units/pr/pr.yaml +20 -0
  298. package/graphs/journey/units/pr/recipes.yaml +2 -0
  299. package/graphs/journey/units/remember/recipes.yaml +2 -0
  300. package/graphs/journey/units/remember/remember.yaml +20 -0
  301. package/graphs/operate-loop.yaml +71 -4
  302. package/graphs/overlays/stop-heal-ladder.yaml +1 -1
  303. package/graphs/release-rehearsal/recipes.yaml +1 -1
  304. package/graphs/release-rehearsal/release-rehearsal.yaml +2 -2
  305. package/graphs/research-loop.yaml +159 -4
  306. package/graphs/test-diet/test-diet.yaml +3 -0
  307. package/graphs/units/dispatch-pod/dispatch-pod.yaml +50 -0
  308. package/graphs/units/dispatch-pod/recipes.yaml +14 -0
  309. package/graphs/units/entry/entry.yaml +50 -0
  310. package/graphs/units/entry/recipes.yaml +14 -0
  311. package/graphs/units/harvest/harvest.yaml +25 -0
  312. package/graphs/units/harvest/recipes.yaml +4 -0
  313. package/graphs/units/land/land.yaml +45 -0
  314. package/graphs/units/land/recipes.yaml +3 -0
  315. package/graphs/units/recipes.yaml +7 -0
  316. package/graphs/units/recover.yaml +39 -0
  317. package/graphs/units/reflect/recipes.yaml +3 -0
  318. package/graphs/units/reflect/reflect.yaml +15 -0
  319. package/graphs/units/task-agent-recover-review.yaml +17 -0
  320. package/graphs/units/task-agent-recover-wait.yaml +17 -0
  321. package/graphs/units/verbs/make-doc.yaml +34 -0
  322. package/graphs/units/verbs/recipes.yaml +33 -0
  323. package/graphs/units/verbs/verify.yaml +55 -0
  324. package/graphs/work/recipes.yaml +2 -0
  325. package/graphs/work/research-report.yaml +28 -0
  326. package/package.json +1 -1
  327. package/scripts/ci-typecheck-changed.ts +69 -16
  328. package/scripts/coord-role-pack.ts +19 -6
  329. package/scripts/docs/generate-graph-anatomy.ts +19 -2
  330. package/scripts/graph-harvest-unit.ts +22 -0
  331. package/scripts/graph-units/journey-stub.ts +6 -0
  332. package/scripts/graph-units/land-enqueue.ts +78 -0
  333. package/scripts/graph-units/recover-plan.ts +15 -0
  334. package/scripts/graph-units/reflect-node.ts +88 -0
  335. package/scripts/graph-units/ta-journey-observe.ts +109 -0
  336. package/scripts/graph-units/verbs/headless-goal-loop.ts +152 -0
  337. package/scripts/graph-units/verbs/verify-stage.ts +75 -0
  338. package/scripts/graph-units/verbs-envelope.ts +139 -0
  339. package/scripts/hq/standby-snapshot.ts +30 -0
  340. package/scripts/public-export.ts +43 -1
  341. package/scripts/ratchets/train-side-invariants.sh +45 -0
  342. package/scripts/release-loop/auto-approve-node.ts +30 -16
  343. package/scripts/release-loop/docs-land-node.ts +21 -5
  344. package/scripts/release-loop/gate-baseline-receipt.ts +155 -0
  345. package/scripts/release-loop/gate-closure.ts +5 -1
  346. package/scripts/release-loop/gate-diff.ts +1 -1
  347. package/scripts/release-loop/gate-node.ts +675 -49
  348. package/scripts/release-loop/gate-partial.ts +68 -14
  349. package/scripts/release-loop/gate-radius-live.ts +177 -0
  350. package/scripts/release-loop/gate-radius-shadow.ts +138 -0
  351. package/scripts/release-loop/gate-traffic-mode.ts +128 -0
  352. package/scripts/release-loop/gate-triage-node.ts +5 -2
  353. package/scripts/release-loop/known-issues-0.2.33-approved.json +194 -0
  354. package/scripts/release-loop/known-issues-node.ts +77 -8
  355. package/scripts/release-loop/node-verdict.ts +12 -1
  356. package/scripts/release-loop/npm-publish-node.ts +66 -12
  357. package/scripts/release-loop/preflight.ts +93 -7
  358. package/scripts/release-loop/probe-pod-host-dirs-live.ts +5 -0
  359. package/scripts/release-loop/publish-node.ts +60 -9
  360. package/scripts/release-loop/pwa-remote.ts +166 -0
  361. package/scripts/release-loop/pwa-scenarios-node.ts +19 -3
  362. package/scripts/release-loop/resume-cap.ts +56 -0
  363. package/scripts/release-loop/resume-release.ts +155 -19
  364. package/scripts/release-loop/resume.ts +24 -1
  365. package/scripts/release-loop/tui-sim-node.ts +8 -2
  366. package/scripts/release-loop/unattended-release.ts +10 -2
  367. package/scripts/test-diet/lib.ts +6 -1
  368. package/scripts/test-diet/node.ts +194 -13
  369. package/scripts/ux-sim.ts +39 -0
  370. package/src/acp/core-turn-bridge.ts +20 -5
  371. package/src/acp/reasoning-thought-buffer.ts +45 -0
  372. package/src/agent/self-ambient-cache.ts +8 -0
  373. package/src/agent-substrate/acp-reviewer.ts +90 -15
  374. package/src/agent-substrate/pr-reviewer.ts +17 -0
  375. package/src/agent-substrate/review-finding-key.ts +53 -0
  376. package/src/agent-substrate/self-review-cli.ts +188 -18
  377. package/src/ask-user-question/pending-questions.ts +1 -1
  378. package/src/autopilot/pr-manager.ts +393 -65
  379. package/src/budget/run-usage-rollup.ts +1 -0
  380. package/src/chat/index.ts +0 -2
  381. package/src/chat/pickers/modal-runtime.ts +10 -27
  382. package/src/chat/tool-render/dispatch.ts +62 -10
  383. package/src/cli/agent-cli.ts +13 -2
  384. package/src/cli/control-cli.ts +24 -7
  385. package/src/cli/doctor-cli.ts +4 -1
  386. package/src/cli/guardian-cli.ts +10 -0
  387. package/src/cli/light-entry-route.ts +75 -0
  388. package/src/cli/llm-detect-cli.ts +23 -1
  389. package/src/cli/pod-cli.ts +77 -3
  390. package/src/cli/pr-cli.ts +81 -14
  391. package/src/cli/pr-land-base-read.ts +2 -0
  392. package/src/cli/pr-land-overlap-evidence.ts +154 -16
  393. package/src/cli/pr-land-pod-shadow-job.ts +3 -0
  394. package/src/cli/pr-land-recovery.ts +64 -0
  395. package/src/cli/pty-takeover-cli.ts +6 -5
  396. package/src/cli/release-checklist-read-cli.ts +79 -0
  397. package/src/cli/release-cli.ts +189 -132
  398. package/src/cli/release-run-if-ready.ts +29 -28
  399. package/src/cli/release-single-flight.ts +178 -0
  400. package/src/cli/remote-resolve.ts +1 -1
  401. package/src/cli/root-tui-entry.ts +48 -0
  402. package/src/cli/seat-cli.ts +37 -0
  403. package/src/cli/setup-cli.ts +40 -1
  404. package/src/cli/sim-cli.ts +22 -0
  405. package/src/cli/tasks-cli.ts +667 -14
  406. package/src/cli/usage-cli.ts +13 -11
  407. package/src/clipboard/history.ts +179 -13
  408. package/src/config.ts +8 -1
  409. package/src/control/control-status.ts +8 -3
  410. package/src/control-plane/ledger.ts +5 -3
  411. package/src/control-plane/server.ts +5 -3
  412. package/src/core-turn/short-question.ts +5 -1
  413. package/src/dashboard/chat/acp-chat.ts +8 -0
  414. package/src/dashboard/index.ts +114 -77
  415. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +3 -0
  416. package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
  417. package/src/dashboard/input/seat-autowake.ts +56 -3
  418. package/src/dashboard/render/theme-resolver.ts +33 -4
  419. package/src/dashboard/seat-boot-preamble.ts +1 -1
  420. package/src/dashboard/slash-runtime/ask-clarification.ts +48 -0
  421. package/src/dashboard/slash-runtime/dashboard-delegated-slashes.ts +68 -0
  422. package/src/dashboard/slash-runtime/dashboard-handlers.ts +62 -322
  423. package/src/dashboard/slash-runtime/dashboard-log-slash.ts +208 -0
  424. package/src/dashboard/turn-preamble.ts +47 -1
  425. package/src/dashboard/turn-stream-formatter.ts +34 -1
  426. package/src/dashboard/turn-stream-runtime.ts +2 -0
  427. package/src/dashboard/turn-typeahead-prompt-zone.ts +67 -0
  428. package/src/domains/guardian-tick.ts +54 -2
  429. package/src/domains/outbound-alert.ts +3 -2
  430. package/src/domains/schedule-registry.ts +16 -2
  431. package/src/extractors.ts +3 -1
  432. package/src/git-fs/idle-refresh.ts +76 -0
  433. package/src/git-fs/index.ts +18 -7
  434. package/src/git-fs/retry.ts +4 -0
  435. package/src/git-fs/runner.ts +2 -0
  436. package/src/git-fs/worktree.ts +42 -4
  437. package/src/graph-kinds/registry.ts +2 -2
  438. package/src/graph-runner/runner.ts +267 -54
  439. package/src/grok/credential.ts +15 -3
  440. package/src/harness/harness-cli-command.ts +151 -38
  441. package/src/harness/harness-incidents.ts +37 -0
  442. package/src/harness/harness-pod-dispatch.ts +38 -1
  443. package/src/harness/harness-salvage-cli.ts +14 -2
  444. package/src/harness/launch-utterance-ledger.ts +208 -0
  445. package/src/impl-discipline/detector.ts +16 -4
  446. package/src/impl-discipline/system-prompt.ts +11 -0
  447. package/src/impl-discipline/types.ts +2 -2
  448. package/src/index.ts +683 -146
  449. package/src/intake-plane/already-built-hint.ts +91 -0
  450. package/src/intake-plane/requirement-funnel.ts +29 -1
  451. package/src/knowhow/pack.ts +199 -0
  452. package/src/light-cli.ts +111 -0
  453. package/src/llm/opengateway-models.ts +11 -0
  454. package/src/llm/provider-credentials.ts +14 -0
  455. package/src/llm/provider-detect.ts +8 -1
  456. package/src/llm/runtime-model-family.ts +2 -1
  457. package/src/llm.ts +268 -65
  458. package/src/loops/budget.ts +95 -0
  459. package/src/loops/msg-wake.ts +8 -2
  460. package/src/loops/orchestrator/traffic.ts +132 -1
  461. package/src/model-tier/llm-tier-map.ts +15 -1
  462. package/src/mss/logging/log-store.ts +4 -5
  463. package/src/native-tool-catalog.ts +15 -1
  464. package/src/nexus/api/graphs-api.ts +22 -3
  465. package/src/nexus/api/http-server.ts +1 -1
  466. package/src/nexus/api/loop-resources.ts +13 -1
  467. package/src/nexus/api/ops-api.ts +12 -0
  468. package/src/oauth/claude-pty-maturity.ts +66 -0
  469. package/src/oauth/codex-access-mirror.ts +55 -0
  470. package/src/oauth/codex.ts +7 -0
  471. package/src/oauth/fallback-chain.ts +1 -1
  472. package/src/onboarding/first-run.ts +5 -3
  473. package/src/onboarding/non-interactive.ts +64 -13
  474. package/src/onboarding.ts +10 -2
  475. package/src/ops/overview-collect.ts +54 -4
  476. package/src/ops/overview.ts +31 -7
  477. package/src/pr-tool/census.ts +282 -30
  478. package/src/pr-tool/inflight-registry.ts +160 -7
  479. package/src/prompt-library/gpt6-family-addendum.ts +14 -1
  480. package/src/pty-shell/herdr-agent-report.ts +442 -0
  481. package/src/pty-shell/pty-control-ipc.ts +5 -5
  482. package/src/pty-shell/pty-manifest.ts +7 -0
  483. package/src/pty-shell/pty-write-outcome.ts +24 -6
  484. package/src/pty-shell/tui-run-state.ts +117 -3
  485. package/src/release-loop/checklist.ts +103 -5
  486. package/src/release-loop/feature-store.ts +19 -2
  487. package/src/release-loop/landed-but-yellow.ts +129 -5
  488. package/src/release-loop/light-rc.ts +1 -0
  489. package/src/release-loop/placement.ts +10 -3
  490. package/src/release-loop/rehearsal.ts +205 -25
  491. package/src/release-loop/rubric.ts +16 -2
  492. package/src/repl/index.ts +174 -17
  493. package/src/repl/ops-msg-bridge.ts +32 -0
  494. package/src/self-dev/ask-launch-flow.ts +44 -5
  495. package/src/self-dev/cli-help-text.ts +17 -0
  496. package/src/self-dev/dev-cli.ts +11 -9
  497. package/src/self-dev/draft-residue.ts +37 -1
  498. package/src/self-dev/draft-sweep.ts +188 -13
  499. package/src/self-dev/draft-triage-rules.ts +107 -0
  500. package/src/self-dev/fleet-reduce.ts +3 -2
  501. package/src/self-dev/graph-front-nodes.ts +112 -0
  502. package/src/self-dev/graph-journey-nodes.ts +11 -6
  503. package/src/self-dev/graph-journey-route.ts +60 -6
  504. package/src/self-dev/harness-run-cli.ts +3 -15
  505. package/src/self-dev/orchestrate-cli-text.ts +10 -0
  506. package/src/self-dev/orchestrate.ts +79 -3
  507. package/src/self-dev/review-provider-fallback.ts +35 -1
  508. package/src/self-dev/run-supervisor.ts +107 -12
  509. package/src/self-dev/run-types.ts +5 -1
  510. package/src/self-dev/self-orchestrate-runtime.ts +1 -11
  511. package/src/self-dev/shard-aggregate.ts +42 -5
  512. package/src/self-dev/shard-run.ts +8 -1
  513. package/src/self-implement/blocked-draft-policy.ts +20 -0
  514. package/src/self-implement/classify-shadow.ts +3 -7
  515. package/src/self-implement/doctor-visit.ts +18 -1
  516. package/src/self-implement/gate-baseline.ts +95 -12
  517. package/src/self-implement/gate-cli.ts +1 -0
  518. package/src/self-implement/gate-remote.ts +40 -25
  519. package/src/self-implement/goal-author-decompose.ts +275 -47
  520. package/src/self-implement/goal-author-identity.ts +66 -0
  521. package/src/self-implement/goal-author-preservation-obligation.ts +35 -0
  522. package/src/self-implement/goal-author.ts +254 -120
  523. package/src/self-implement/goal-elaborate.ts +295 -0
  524. package/src/self-implement/goal-refine.ts +270 -0
  525. package/src/self-implement/graph-overlay-yaml.ts +28 -2
  526. package/src/self-implement/graph-unit-flatten.ts +103 -0
  527. package/src/self-implement/graph-unit-resolver.ts +107 -0
  528. package/src/self-implement/graph-yaml.ts +151 -1
  529. package/src/self-implement/headless-elanous-driver.ts +1 -0
  530. package/src/self-implement/knowhow-pack.ts +103 -0
  531. package/src/self-implement/orchestrator.ts +160 -45
  532. package/src/self-implement/pod-warm-pool-profile.ts +45 -0
  533. package/src/self-implement/review-mustfix-class.ts +203 -0
  534. package/src/self-implement/rework-budget-judgment.ts +5 -8
  535. package/src/self-implement/run-stop.ts +17 -2
  536. package/src/self-implement/running-runs.ts +29 -1
  537. package/src/self-implement/scope-adjacency.ts +113 -0
  538. package/src/self-implement/seams.ts +138 -4
  539. package/src/self-implement/stop-autoheal.ts +112 -2
  540. package/src/session/chat.ts +27 -0
  541. package/src/session-runtime/index.ts +1 -1
  542. package/src/session-runtime/retry-policy.ts +2 -1
  543. package/src/setup/llm-env-detect.ts +5 -0
  544. package/src/skills/tools/agent.ts +140 -14
  545. package/src/skills/tools/msg-ask.ts +100 -0
  546. package/src/skills/tools/ops-ledger.ts +2 -1
  547. package/src/skills/tools/schedule-wakeup.ts +66 -17
  548. package/src/steward/hold-hygiene.ts +24 -6
  549. package/src/steward/release-day.ts +7 -3
  550. package/src/steward/triage-budget.ts +194 -0
  551. package/src/steward/triage.ts +57 -7
  552. package/src/task-agent/actions.ts +69 -1
  553. package/src/task-agent/green-sweep.ts +4 -0
  554. package/src/task-agent/harvest-unit.ts +141 -0
  555. package/src/task-agent/live-moves.ts +257 -15
  556. package/src/task-agent/parent-host.ts +216 -6
  557. package/src/task-agent/postland-card.ts +80 -1
  558. package/src/task-agent/rejudge-on-head.ts +112 -26
  559. package/src/task-agent/repair-branch-cas.ts +4 -1
  560. package/src/task-agent/repair-cycle.ts +16 -3
  561. package/src/task-agent/run-identity.ts +4 -3
  562. package/src/task-agent/shadow.ts +19 -3
  563. package/src/task-agent/task-hand.ts +96 -36
  564. package/src/task-agent/tier-split.ts +127 -0
  565. package/src/task-agent/work-order-build.ts +24 -0
  566. package/src/task-agent/work-order.ts +3 -2
  567. package/src/task-orchestrator/generator-prompt.ts +60 -11
  568. package/src/task-orchestrator/surfaces/pod-account-broker.ts +72 -3
  569. package/src/task-orchestrator/surfaces/pod-child-providers.ts +1 -1
  570. package/src/task-orchestrator/surfaces/pod-command-job.ts +47 -8
  571. package/src/task-orchestrator/surfaces/pod-image-source.ts +89 -1
  572. package/src/task-orchestrator/surfaces/pod-lease.ts +17 -8
  573. package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +86 -8
  574. package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +8 -2
  575. package/src/task-orchestrator/surfaces/pod-pool.ts +73 -17
  576. package/src/task-orchestrator/surfaces/pod-provenance.ts +76 -0
  577. package/src/task-orchestrator/surfaces/pod-source-receive.ts +72 -4
  578. package/src/task-orchestrator/surfaces/pod-warm-provenance.ts +81 -0
  579. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +32 -5
  580. package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +3 -0
  581. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +421 -0
  582. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +2 -1
  583. package/src/task-orchestrator/surfaces/self-implement-pod.ts +588 -71
  584. package/src/task-orchestrator/surfaces/self-implement.ts +106 -2
  585. package/src/tool-runtime/ops-ledger-runtimes.ts +10 -2
  586. package/src/tool-runtime/truncation-store.ts +4 -0
  587. package/src/tui/README.md +2 -0
  588. package/src/tui/chat/background-job-status.ts +54 -0
  589. package/src/tui/chat/log-view.ts +80 -0
  590. package/src/tui/entry.ts +406 -0
  591. package/src/tui/input/composer.ts +144 -0
  592. package/src/tui/seat-mail-boot.ts +59 -0
  593. package/src/tui/slash/compact.ts +100 -0
  594. package/src/tui/slash/new-session.ts +35 -0
  595. package/src/tui-client/dashboard-session.ts +10 -1
  596. package/src/tui-client/local-chat-turn.ts +239 -0
  597. package/src/tui-client/local-harness-command.ts +63 -0
  598. package/src/tui-client/local-provider-command.ts +184 -0
  599. package/src/user-config/coercion.ts +36 -0
  600. package/src/user-config.ts +22 -43
  601. package/src/ux-sim/event-monkey.ts +378 -0
  602. package/src/ux-sim/fake-bin.ts +42 -7
  603. package/src/ux-sim/fault-nextmove-dispatch.ts +266 -0
  604. package/src/ux-sim/fault-pod-codex-503.ts +211 -0
  605. package/src/ux-sim/fault-scope-escape-lift.ts +189 -0
  606. package/src/ux-sim/scenario.ts +72 -13
  607. package/src/version/packed-revision.json +1 -1
  608. package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +0 -27
  611. package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +0 -287
  612. package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +0 -62
  613. package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/1654.32de4b324981a188.js +0 -23
  617. package/apps/pwa/out/_next/static/chunks/1834-98b64172bc1efe77.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +0 -149
  619. package/apps/pwa/out/_next/static/chunks/1913.7cc6fb6055b1ed3f.js +0 -41
  620. package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/2112-e4efc21a6505d5bc.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/2534.e85ae21ad0dac829.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/2898.fd781bc7cf01a7b7.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +0 -115
  629. package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +0 -64
  630. package/apps/pwa/out/_next/static/chunks/3541-ce3a5424ed5ad7f1.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/3583.a05e88cc8d594628.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +0 -36
  636. package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +0 -286
  640. package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +0 -77
  641. package/apps/pwa/out/_next/static/chunks/4745.67d6f99c245af3f7.js +0 -226
  642. package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +0 -79
  644. package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +0 -148
  647. package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/5458-16cd7a2b51b74c8c.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/5543.a438b8ab5a13ea47.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +0 -74
  653. package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +0 -94
  655. package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +0 -90
  661. package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +0 -32
  662. package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +0 -104
  663. package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +0 -24
  668. package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +0 -93
  669. package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +0 -8
  674. package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +0 -24
  685. package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/9138.b6e80031afb1d973.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/9279-d28c5b7754d010ae.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/9989.3694300e5f000b14.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a9717a67c2eecbaf.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c0f1ca371f710b06.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/app/chat/page-46430d45a9e04eac.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8ea49a4c6dd2c6e7.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/app/exec/page-0e5a41fac1c75396.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/app/field/page-31aaf1ed37c6df9a.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/app/intake/page-c69247e7134d329d.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/app/layout-66dc5cd3a0999abe.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/app/loops/page-d256f5d5d9552823.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/app/market/page-ce8f1907b8a6101c.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/app/not-found-7607cca9b84950c6.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eeab86176b03dc3b.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5b93dbae3432b996.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f47e768b5b242b5a.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-95306f892aa2d0ee.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/app/sessions/page-940fb6c217f6b470.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-8c66a3b24a745630.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/app/settings/page-7b675dfd35c36fc9.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8bd0d3cf7665bcfc.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/app/setup/page-c219f2ed171131be.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/app/share/page-40f8fa21bdae955b.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/app/showroom/page-468f8313e4f1fbd9.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/app/today/page-4214b855d9233ea8.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/app/trace/page-936752c7aa58762e.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/app/vault/page-ec052ad359df1797.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/app/workflows/page-fad650e4793adb65.js +0 -1
  741. package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +0 -122
  744. package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/ebc7d829.264a0bcb6c750b50.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +0 -1
  752. package/src/cli/g6a-registration-probe.ts +0 -16
  753. /package/apps/pwa/out/_next/static/{IVKr3AlRUVLKsw2fh7eXF → T1mBQqmkwKDLGtyisDCOd}/_ssgManifest.js +0 -0
@@ -5,12 +5,17 @@ import { join } from 'node:path';
5
5
  import { effectiveInstanceRoot } from '../instance/resolve.js';
6
6
  import { withFileLockSync } from '../storage/file-lock.js';
7
7
  import { resolveHostId } from '../platform/host-id.js';
8
+ import { spawnSync } from 'node:child_process';
8
9
 
9
10
  export interface PrToolCommandResult { ok: boolean; out: string; err?: string }
10
11
  export type PrToolCommand = (command: string, args: readonly string[], opts?: { cwd?: string }) => PrToolCommandResult;
12
+ export interface PrToolCall { command: string; args: readonly string[]; opts?: { cwd?: string } }
13
+ /** PR-CENSUS-PARALLEL: run every call with at most `concurrency` in flight; results stay in input order. */
14
+ export type PrToolParallelCommand = (calls: readonly PrToolCall[], concurrency: number) => PrToolCommandResult[];
11
15
  export type Seat = 'OP' | 'TC' | 'MK' | 'UX';
12
16
  export interface OpenPr {
13
17
  number: number;
18
+ headRefName?: string;
14
19
  title: string;
15
20
  draft: boolean;
16
21
  updatedAt: string;
@@ -20,6 +25,10 @@ export interface OpenPr {
20
25
  seat: Seat | null;
21
26
  lastCommitAt?: string;
22
27
  cellId: string | null;
28
+ cardId?: string | null;
29
+ cardMarkerPresent?: boolean;
30
+ /** «조각 i/N — 부모 <칸>» 표지가 있으면 그 조각 번호(없으면 null) — sibling-slice 판정용. */
31
+ slice?: { slice: number; total: number } | null;
23
32
  hunks?: readonly DiffHunk[];
24
33
  }
25
34
  export type Inventory = { status: 'checked'; prs: readonly OpenPr[] } | { status: 'unknown'; reason: string };
@@ -31,7 +40,9 @@ export type GuardCode = 'inventory-unknown' | 'candidate-unknown' | 'goal-only'
31
40
  export interface GuardReason { code: GuardCode; detail: string; prs?: readonly number[]; files?: readonly string[] }
32
41
  /** PR-GUARD-HUNK-WARN (대표 10-17): 줄 겹침은 기본 경고 — 착지 쪽 스마트 머지가 푼다. 'refuse' = 옛 거부 동작. */
33
42
  export type HunkOverlapMode = 'refuse' | 'warn';
34
- export interface GuardVerdict { status: 'allow' | 'refuse' | 'unknown'; reasons: readonly GuardReason[]; warnings: readonly GuardReason[] }
43
+ export interface GuardVerdict { status: 'allow' | 'refuse' | 'unknown'; reasons: readonly GuardReason[]; warnings: readonly GuardReason[];
44
+ sameCellConflicts?: readonly { pr: number; cardId: string | null; slice?: { slice: number; total: number } | null }[];
45
+ sameCellReuse?: { number: number; branch: string; headSha: string; cellId: string; cardId: string; legacyBound?: true } }
35
46
 
36
47
  const SEATS = ['OP', 'TC', 'MK', 'UX'] as const;
37
48
  const DEFAULT_SEAT_CAP = 30;
@@ -41,6 +52,126 @@ const MAX_FILES = 3000; // GitHub does not expose more than 3000 PR files.
41
52
  const UNMEASURED_WINDOW_MS = 60 * 60 * 1000;
42
53
  const UNMEASURED_ALLOW_COUNT = 3;
43
54
 
55
+ export const DEFAULT_DIFF_CONCURRENCY = 6;
56
+ const DIFF_CACHE_MAX = 2000;
57
+ const DIFF_CACHE_MAX_BYTES = 64 * 1024 * 1024;
58
+ const DIFF_CACHE_ENTRY_MAX_BYTES = 4 * 1024 * 1024;
59
+ const PARALLEL_CALL_TIMEOUT_MS = 120_000;
60
+ /** Whole pool ceiling, independent of the call count — a stuck pool fails (inventory unknown) instead of hanging the guard. */
61
+ const PARALLEL_POOL_TIMEOUT_MS = 10 * 60 * 1000;
62
+ const PARALLEL_MAX_BUFFER = 256 * 1024 * 1024;
63
+
64
+ /** `gh pr diff` patches keyed by PR number ⊕ head sha. A head sha fixes the diff, so a retry or a second guard
65
+ * call in the same process never refetches it. Bounded by entry count and total bytes (the oldest entry leaves
66
+ * first); a single patch above the per-entry limit is not cached at all. */
67
+ export class CensusDiffCache {
68
+ private readonly entries = new Map<string, { patch: string; bytes: number }>();
69
+ private totalBytes = 0;
70
+ constructor(private readonly max = DIFF_CACHE_MAX, private readonly maxBytes = DIFF_CACHE_MAX_BYTES,
71
+ private readonly entryMaxBytes = DIFF_CACHE_ENTRY_MAX_BYTES) {}
72
+ private key(number: number, headSha: string): string { return `${number}@${headSha}`; }
73
+ private drop(key: string): void {
74
+ const entry = this.entries.get(key);
75
+ if (!entry) return;
76
+ this.totalBytes -= entry.bytes;
77
+ this.entries.delete(key);
78
+ }
79
+ get(number: number, headSha: string): string | undefined { return this.entries.get(this.key(number, headSha))?.patch; }
80
+ set(number: number, headSha: string, patch: string): void {
81
+ const key = this.key(number, headSha);
82
+ this.drop(key);
83
+ const bytes = Buffer.byteLength(patch, 'utf8');
84
+ if (bytes > this.entryMaxBytes || bytes > this.maxBytes) return;
85
+ while (this.entries.size > 0 && (this.entries.size >= this.max || this.totalBytes + bytes > this.maxBytes)) {
86
+ const oldest = this.entries.keys().next().value;
87
+ if (oldest === undefined) break;
88
+ this.drop(oldest);
89
+ }
90
+ this.entries.set(key, { patch, bytes });
91
+ this.totalBytes += bytes;
92
+ }
93
+ get size(): number { return this.entries.size; }
94
+ get bytes(): number { return this.totalBytes; }
95
+ clear(): void { this.entries.clear(); this.totalBytes = 0; }
96
+ }
97
+ /** Process-lifetime cache for production callers (PR creation guard · `pr open-guard`). Tests pass their own. */
98
+ export const sharedCensusDiffCache = new CensusDiffCache();
99
+ export function resetCensusDiffCache(): void { sharedCensusDiffCache.clear(); }
100
+
101
+ // Runs inside a child `bun`/`node` so the synchronous inventory API can still fan out. stdin: { calls, concurrency,
102
+ // timeoutMs } · stdout: one result per call, input order. Each call runs in its own process group (detached) so a
103
+ // per-call timeout, or this pool ending on SIGTERM/SIGINT/SIGHUP from the outer ceiling, kills the whole group —
104
+ // no `gh` (or its children) outlives the pool.
105
+ const PARALLEL_SCRIPT = `
106
+ const { spawn } = require('node:child_process');
107
+ const live = new Set();
108
+ const killGroup = pid => { try { process.kill(-pid, 'SIGKILL'); } catch { try { process.kill(pid, 'SIGKILL'); } catch {} } };
109
+ const killAll = () => { for (const pid of live) killGroup(pid); live.clear(); };
110
+ for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP']) process.on(signal, () => { killAll(); process.exit(124); });
111
+ process.on('exit', killAll);
112
+ let input = '';
113
+ process.stdin.setEncoding('utf8');
114
+ process.stdin.on('data', chunk => { input += chunk; });
115
+ process.stdin.on('end', () => {
116
+ const { calls, concurrency, timeoutMs } = JSON.parse(input);
117
+ const results = new Array(calls.length);
118
+ let next = 0;
119
+ const one = index => new Promise(resolve => {
120
+ const call = calls[index];
121
+ const out = []; const err = [];
122
+ let settled = false; let timedOut = false; let timer;
123
+ let child;
124
+ const finish = result => {
125
+ if (settled) return; settled = true; clearTimeout(timer);
126
+ if (child && child.pid) { killGroup(child.pid); live.delete(child.pid); }
127
+ results[index] = result; resolve();
128
+ };
129
+ try { child = spawn(call.command, call.args, { cwd: call.cwd || undefined, stdio: ['ignore', 'pipe', 'pipe'], detached: true }); }
130
+ catch (error) { results[index] = { ok: false, out: '', err: String(error && error.message || error) }; resolve(); return; }
131
+ if (child.pid) live.add(child.pid);
132
+ timer = setTimeout(() => { timedOut = true; if (child.pid) killGroup(child.pid); }, timeoutMs);
133
+ child.stdout.on('data', chunk => out.push(chunk));
134
+ child.stderr.on('data', chunk => err.push(chunk));
135
+ child.on('error', error => finish({ ok: false, out: '', err: String(error && error.message || error) }));
136
+ child.on('close', (code, signal) => finish({ ok: code === 0 && !signal && !timedOut, out: Buffer.concat(out).toString('utf8').trim(),
137
+ err: [Buffer.concat(err).toString('utf8').trim(), timedOut ? 'timed out after ' + timeoutMs + 'ms' : signal ? 'terminated by ' + signal : ''].filter(Boolean).join('\\n') }));
138
+ });
139
+ const worker = async () => { while (next < calls.length) { const index = next++; await one(index); } };
140
+ Promise.all(Array.from({ length: Math.max(1, Math.min(concurrency, calls.length)) }, worker))
141
+ .then(() => process.stdout.write(JSON.stringify(results)));
142
+ });
143
+ `;
144
+
145
+ /** Real-spawn {@link PrToolParallelCommand}: one child runtime fans out the calls with a bounded pool. Any failure of
146
+ * the pool itself (including the fixed pool ceiling) returns every call as failed, so the caller's conservative
147
+ * unknown rule applies. `timeoutMs` = per call · `poolTimeoutMs` = whole pool (tests inject short values). */
148
+ export function spawnParallelCommands(calls: readonly PrToolCall[], options: { concurrency?: number; env?: NodeJS.ProcessEnv; timeoutMs?: number; poolTimeoutMs?: number } = {}): PrToolCommandResult[] {
149
+ if (calls.length === 0) return [];
150
+ const concurrency = Math.max(1, Math.floor(options.concurrency ?? DEFAULT_DIFF_CONCURRENCY));
151
+ const timeoutMs = options.timeoutMs ?? PARALLEL_CALL_TIMEOUT_MS;
152
+ const failAll = (err: string) => calls.map(() => ({ ok: false, out: '', err }));
153
+ try {
154
+ const r = spawnSync(process.execPath, ['-e', PARALLEL_SCRIPT], {
155
+ input: JSON.stringify({ calls: calls.map(call => ({ command: call.command, args: [...call.args], cwd: call.opts?.cwd })), concurrency, timeoutMs }),
156
+ encoding: 'utf-8', maxBuffer: PARALLEL_MAX_BUFFER, timeout: options.poolTimeoutMs ?? PARALLEL_POOL_TIMEOUT_MS, killSignal: 'SIGTERM',
157
+ ...(options.env ? { env: options.env } : {}),
158
+ });
159
+ if ((r.error as NodeJS.ErrnoException | undefined)?.code === 'ENOBUFS') return failAll('parallel output exceeded 256MiB (ENOBUFS)');
160
+ if ((r.error as NodeJS.ErrnoException | undefined)?.code === 'ETIMEDOUT' || r.signal) return failAll(`parallel runner timed out or was killed (${r.signal ?? 'ETIMEDOUT'})`);
161
+ if (r.status !== 0) return failAll(`parallel runner rc=${r.status}: ${(r.stderr ?? '').trim().slice(0, 300)}`);
162
+ const parsed: unknown = JSON.parse(r.stdout ?? '');
163
+ if (!Array.isArray(parsed) || parsed.length !== calls.length) return failAll('parallel runner result shape mismatch');
164
+ return parsed.map(row => {
165
+ const value = row as { ok?: unknown; out?: unknown; err?: unknown } | null;
166
+ return value && typeof value.ok === 'boolean' && typeof value.out === 'string'
167
+ ? { ok: value.ok, out: value.out, err: typeof value.err === 'string' ? value.err : '' }
168
+ : { ok: false, out: '', err: 'parallel runner row missing' };
169
+ });
170
+ } catch (error) {
171
+ return failAll(`parallel runner: ${error instanceof Error ? error.message : String(error)}`);
172
+ }
173
+ }
174
+
44
175
  function unknown(reason: string): Inventory { return { status: 'unknown', reason }; }
45
176
  function seatValue(value: unknown): Seat | null { return SEATS.find(seat => seat === value) ?? null; }
46
177
  function seatFromLabels(labels: readonly string[]): Seat | null {
@@ -49,6 +180,22 @@ function seatFromLabels(labels: readonly string[]): Seat | null {
49
180
  export function cellIdFromBody(body: string): string | null {
50
181
  return /^\s*(?:[-*]\s*)?칸\s*:\s*([A-Z][A-Z0-9-]{2,})\s*$/m.exec(body)?.[1] ?? null;
51
182
  }
183
+ export function cardIdFromBody(body: string): string | null {
184
+ const ids = [...body.matchAll(/^\s*(?:[-*]\s*)?카드\s*:\s*([^\r\n]*)$/gm)].map(match => match[1]!.trim());
185
+ return ids.length > 0 && ids.every(id => /^[A-Za-z0-9][A-Za-z0-9._:-]{2,}$/.test(id) && id === ids[0]) ? ids[0]! : null;
186
+ }
187
+ export function hasCardMarker(body: string): boolean {
188
+ return /^\s*(?:[-*]\s*)?카드\s*:/m.test(body);
189
+ }
190
+ /** 골/PR 본문의 «조각 i/N — 부모 <칸>» 표지 → {slice,total}. 표지 없으면 null, 1≤i≤N 아니면 null. */
191
+ export function sliceMarkerFromBody(body: string): { slice: number; total: number } | null {
192
+ const m = /^\s*(?:[-*]\s*)?(?:«\s*)?조각\s*(\d+)\s*\/\s*(\d+)\s*(?:»)?\s*—\s*부모\s+\S[^\r\n]*$/m.exec(body);
193
+ if (!m) return null;
194
+ const slice = Number(m[1]);
195
+ const total = Number(m[2]);
196
+ return Number.isSafeInteger(slice) && Number.isSafeInteger(total) && slice >= 1 && total >= 1 && slice <= total
197
+ ? { slice, total } : null;
198
+ }
52
199
 
53
200
  export function parseDiffHunks(patch: string): DiffHunk[] {
54
201
  const hunks: DiffHunk[] = [];
@@ -81,8 +228,8 @@ export function parseDiffHunks(patch: string): DiffHunk[] {
81
228
  }
82
229
 
83
230
  /** `gh api --paginate --slurp` must return every REST page, including the last page. */
84
- function pages(run: PrToolCommand, endpoint: string, cwd?: string): readonly unknown[] {
85
- const result = run('gh', ['api', '--paginate', '--slurp', endpoint], cwd ? { cwd } : undefined);
231
+ function pagesArgs(endpoint: string): string[] { return ['api', '--paginate', '--slurp', endpoint]; }
232
+ function parsePages(result: PrToolCommandResult, endpoint: string): readonly unknown[] {
86
233
  if (!result.ok) throw new Error(`gh api ${endpoint}: ${result.err || result.out || '조회 실패'}`);
87
234
  let parsed: unknown;
88
235
  try { parsed = JSON.parse(result.out); } catch { throw new Error(`gh api ${endpoint}: JSON 오류`); }
@@ -91,6 +238,9 @@ function pages(run: PrToolCommand, endpoint: string, cwd?: string): readonly unk
91
238
  }
92
239
  return parsed.flat();
93
240
  }
241
+ function pages(run: PrToolCommand, endpoint: string, cwd?: string): readonly unknown[] {
242
+ return parsePages(run('gh', pagesArgs(endpoint), cwd ? { cwd } : undefined), endpoint);
243
+ }
94
244
 
95
245
  function object(value: unknown): Record<string, unknown> {
96
246
  if (value === null || typeof value !== 'object' || Array.isArray(value)) throw new Error('PR 응답 행 형식 오류');
@@ -105,29 +255,59 @@ function date(value: unknown, name: string): string {
105
255
  if (!Number.isFinite(Date.parse(raw))) throw new Error(`${name} 날짜 오류`);
106
256
  return raw;
107
257
  }
108
- function pathsForPr(run: PrToolCommand, number: number, cwd?: string): string[] {
109
- const rows = pages(run, `repos/{owner}/{repo}/pulls/${number}/files?per_page=100`, cwd);
258
+ function filesEndpoint(number: number): string { return `repos/{owner}/{repo}/pulls/${number}/files?per_page=100`; }
259
+ function pathsFromFilesResult(result: PrToolCommandResult, number: number): string[] {
260
+ const rows = parsePages(result, filesEndpoint(number));
110
261
  if (rows.length >= MAX_FILES) throw new Error(`PR #${number}: 파일 ${MAX_FILES}개 GitHub 상한에 닿음`);
111
262
  return rows.map(row => text(object(row).filename, `PR #${number} 파일명`));
112
263
  }
113
264
 
114
- /** Fast file projection; the REST page census below remains the authority for which PRs exist. */
115
- function projectedFiles(run: PrToolCommand, count: number, cwd?: string): Map<number, string[]> {
265
+ interface FileProjection {
266
+ files: Map<number, string[]>;
267
+ /** Set when the one-shot list produced nothing usable — every PR then takes the REST file fallback. */
268
+ failure?: string;
269
+ /** Rows the projection could not vouch for (malformed · file list shorter than changedFiles, e.g. >100 files). */
270
+ skipped: number;
271
+ }
272
+
273
+ /** Fast file projection; the REST page census below remains the authority for which PRs exist. A failed or partial
274
+ * projection is never silent: the reason and skipped row count go to the `diff-scan` observation. */
275
+ function projectedFiles(run: PrToolCommand, count: number, cwd?: string): FileProjection {
116
276
  const result = run('gh', ['pr', 'list', '--state', 'open', '--limit', String(Math.max(count, 1)), '--json', 'number,changedFiles,files'], cwd ? { cwd } : undefined);
117
- if (!result.ok) return new Map();
277
+ if (!result.ok) return { files: new Map(), skipped: 0, failure: `gh pr list 실패: ${(result.err || result.out || 'rc≠0').trim().slice(0, 200)}` };
118
278
  let rows: unknown;
119
- try { rows = JSON.parse(result.out); } catch { return new Map(); }
120
- if (!Array.isArray(rows)) return new Map();
279
+ try { rows = JSON.parse(result.out); } catch { return { files: new Map(), skipped: 0, failure: `gh pr list JSON 오류 (${result.out.length}자)` }; }
280
+ if (!Array.isArray(rows)) return { files: new Map(), skipped: 0, failure: 'gh pr list 응답이 배열 아님' };
121
281
  const projection = new Map<number, string[]>();
282
+ let skipped = 0;
122
283
  for (const value of rows) {
123
284
  try {
124
285
  const row = object(value);
125
- if (!Number.isSafeInteger(row.number) || (row.number as number) <= 0 || !Number.isSafeInteger(row.changedFiles) || (row.changedFiles as number) < 0 || !Array.isArray(row.files)) continue;
286
+ if (!Number.isSafeInteger(row.number) || (row.number as number) <= 0 || !Number.isSafeInteger(row.changedFiles) || (row.changedFiles as number) < 0 || !Array.isArray(row.files)) { skipped += 1; continue; }
126
287
  const paths = row.files.map(file => text(object(file).path, 'PR 파일명'));
127
288
  if (paths.length === row.changedFiles && paths.length < MAX_FILES && !projection.has(row.number as number)) projection.set(row.number as number, paths);
128
- } catch { /* malformed projection: REST file endpoint below decides this PR */ }
289
+ else skipped += 1;
290
+ } catch { skipped += 1; /* malformed projection: REST file endpoint below decides this PR */ }
291
+ }
292
+ if (rows.length > 0 && projection.size === 0) return { files: projection, skipped, failure: `gh pr list ${rows.length}행 모두 사용 불가` };
293
+ return { files: projection, skipped };
294
+ }
295
+
296
+ /** Run `calls` through the bounded pool when given, else one by one with the injected runner, stopping at the first
297
+ * failure (the historical order and call count). The result may be shorter than `calls` only in that sequential case. */
298
+ function runCalls(run: PrToolCommand, calls: readonly PrToolCall[], runParallel: PrToolParallelCommand | undefined, concurrency: number, what: string): PrToolCommandResult[] {
299
+ if (runParallel) {
300
+ const results = runParallel(calls, concurrency);
301
+ if (results.length !== calls.length) throw new Error(`${what} 결과 ${results.length}/${calls.length}건 · 판정 불가`);
302
+ return results;
129
303
  }
130
- return projection;
304
+ const results: PrToolCommandResult[] = [];
305
+ for (const call of calls) {
306
+ const result = run(call.command, call.args, call.opts);
307
+ results.push(result);
308
+ if (!result.ok) break;
309
+ }
310
+ return results;
131
311
  }
132
312
 
133
313
  export function readOpenPrs(run: PrToolCommand, options: {
@@ -135,10 +315,28 @@ export function readOpenPrs(run: PrToolCommand, options: {
135
315
  seatOfPr?: (pr: { number: number; headRefName: string; body: string; labels: readonly string[] }) => Seat | null;
136
316
  includeDraftCommitDates?: boolean;
137
317
  candidateFiles?: readonly string[];
318
+ /** Bounded parallel REST-file fallback and diff fetch. Absent ⇒ the injected `run`, one by one (historical order and call count). */
319
+ runParallel?: PrToolParallelCommand;
320
+ /** Operations default {@link DEFAULT_DIFF_CONCURRENCY} (6) — production callers do not pass it; for test injection. */
321
+ diffConcurrency?: number;
322
+ /** Absent ⇒ no cache. Production callers pass {@link sharedCensusDiffCache}. */
323
+ diffCache?: CensusDiffCache;
138
324
  } = {}): Inventory {
325
+ const started = Date.now();
326
+ const callOpts = options.cwd ? { opts: { cwd: options.cwd } } : {};
327
+ const concurrency = options.runParallel ? Math.max(1, Math.floor(options.diffConcurrency ?? DEFAULT_DIFF_CONCURRENCY)) : 1;
328
+ const scan = { openPrs: 0, projected: 0, restFallback: 0, projectionFailed: false, projectionReason: undefined as string | undefined,
329
+ projectionSkipped: 0, fileOverlap: 0, patchFetched: 0, cacheHits: 0 };
330
+ let status: 'checked' | 'inventory-unknown' | 'files-failed' | 'diff-failed' | 'commit-date-failed' = 'inventory-unknown';
331
+ const logDiffScan = () => {
332
+ debug.log('pr-tool.census', 'diff-scan', { ...scan, concurrency, elapsedMs: Date.now() - started, status });
333
+ };
139
334
  try {
140
335
  const rows = pages(run, 'repos/{owner}/{repo}/pulls?state=open&per_page=100', options.cwd);
141
- const projected = projectedFiles(run, rows.length, options.cwd);
336
+ const projection = projectedFiles(run, rows.length, options.cwd);
337
+ scan.projectionFailed = projection.failure !== undefined;
338
+ scan.projectionReason = projection.failure;
339
+ scan.projectionSkipped = projection.skipped;
142
340
  const prs: OpenPr[] = [];
143
341
  const seen = new Set<number>();
144
342
  for (const value of rows) {
@@ -155,26 +353,69 @@ export function readOpenPrs(run: PrToolCommand, options: {
155
353
  const headSha = text(head.sha, `PR #${number} head sha`);
156
354
  const body = typeof row.body === 'string' ? row.body : '';
157
355
  const seat = seatValue(options.seatOfPr?.({ number: number as number, headRefName, body, labels: labelNames })) ?? seatFromLabels(labelNames);
158
- const pr: OpenPr = {
159
- number: number as number, title: text(row.title, `PR #${number} title`), draft: row.draft,
356
+ prs.push({
357
+ number: number as number, headRefName, title: text(row.title, `PR #${number} title`), draft: row.draft,
160
358
  updatedAt: date(row.updated_at, `PR #${number} updated_at`), headSha, labels: labelNames,
161
- files: projected.get(number as number) ?? pathsForPr(run, number as number, options.cwd), seat,
162
- cellId: cellIdFromBody(body),
163
- };
164
- if (options.candidateFiles?.some(file => pr.files.includes(file))) {
165
- const patch = run('gh', ['pr', 'diff', String(number), '--patch'], options.cwd ? { cwd: options.cwd } : undefined);
166
- if (!patch.ok) throw new Error(`PR #${number}: 수정 줄 조회 실패: ${patch.err || patch.out}`);
359
+ files: projection.files.get(number as number) ?? [], seat,
360
+ cellId: cellIdFromBody(body), cardId: cardIdFromBody(body), cardMarkerPresent: hasCardMarker(body),
361
+ slice: sliceMarkerFromBody(body),
362
+ });
363
+ }
364
+ scan.openPrs = prs.length;
365
+ // Files: PRs the one-shot projection did not cover take the REST file endpoint — in the pool when given.
366
+ const restPrs = prs.filter(pr => !projection.files.has(pr.number));
367
+ scan.projected = prs.length - restPrs.length;
368
+ scan.restFallback = restPrs.length;
369
+ if (restPrs.length) {
370
+ status = 'files-failed';
371
+ const results = runCalls(run, restPrs.map(pr => ({ command: 'gh', args: pagesArgs(filesEndpoint(pr.number)), ...callOpts })), options.runParallel, concurrency, 'PR 파일 조회');
372
+ // The first failure in input order decides the reason; one failure keeps the whole inventory unknown.
373
+ // Invariant: a result is missing only after an earlier failure (sequential stop), which throws first. Still,
374
+ // a missing result is a failure (unknown), never an empty file list.
375
+ restPrs.forEach((pr, index) => {
376
+ const result = results[index];
377
+ if (!result) throw new Error(`PR #${pr.number}: 파일 조회 결과 없음 · 판정 불가`);
378
+ pr.files = pathsFromFilesResult(result, pr.number);
379
+ });
380
+ }
381
+ // Diffs: only PRs sharing a candidate file; a cached (number, headSha) patch is not refetched.
382
+ const pendingDiffs: OpenPr[] = [];
383
+ for (const pr of prs) {
384
+ if (!options.candidateFiles?.some(file => pr.files.includes(file))) continue;
385
+ scan.fileOverlap += 1;
386
+ const cached = options.diffCache?.get(pr.number, pr.headSha);
387
+ if (cached !== undefined) { pr.hunks = parseDiffHunks(cached); scan.cacheHits += 1; }
388
+ else pendingDiffs.push(pr);
389
+ }
390
+ if (pendingDiffs.length) {
391
+ status = 'diff-failed';
392
+ const results = runCalls(run, pendingDiffs.map(pr => ({ command: 'gh', args: ['pr', 'diff', String(pr.number), '--patch'], ...callOpts })), options.runParallel, concurrency, '수정 줄 조회');
393
+ scan.patchFetched = results.length;
394
+ pendingDiffs.forEach((pr, index) => {
395
+ const patch = results[index];
396
+ if (!patch) throw new Error(`PR #${pr.number}: 수정 줄 조회 결과 없음 · 판정 불가`);
397
+ if (!patch.ok) throw new Error(`PR #${pr.number}: 수정 줄 조회 실패: ${patch.err || patch.out}`);
398
+ });
399
+ pendingDiffs.forEach((pr, index) => {
400
+ const patch = results[index]!;
167
401
  pr.hunks = parseDiffHunks(patch.out);
402
+ options.diffCache?.set(pr.number, pr.headSha, patch.out);
403
+ });
404
+ }
405
+ if (options.includeDraftCommitDates) {
406
+ status = 'commit-date-failed';
407
+ for (const pr of prs) {
408
+ if (!pr.draft) continue;
409
+ const commit = run('gh', ['api', `repos/{owner}/{repo}/commits/${pr.headSha}`, '--jq', '.commit.committer.date'], options.cwd ? { cwd: options.cwd } : undefined);
410
+ if (!commit.ok) throw new Error(`PR #${pr.number}: 마지막 커밋 날짜 조회 실패: ${commit.err || commit.out}`);
411
+ pr.lastCommitAt = date(commit.out.trim(), `PR #${pr.number} 마지막 커밋`);
168
412
  }
169
- if (pr.draft && options.includeDraftCommitDates) {
170
- const commit = run('gh', ['api', `repos/{owner}/{repo}/commits/${headSha}`, '--jq', '.commit.committer.date'], options.cwd ? { cwd: options.cwd } : undefined);
171
- if (!commit.ok) throw new Error(`PR #${number}: 마지막 커밋 날짜 조회 실패: ${commit.err || commit.out}`);
172
- pr.lastCommitAt = date(commit.out.trim(), `PR #${number} 마지막 커밋`);
173
- }
174
- prs.push(pr);
175
413
  }
414
+ status = 'checked';
415
+ logDiffScan();
176
416
  return { status: 'checked', prs };
177
417
  } catch (error) {
418
+ logDiffScan();
178
419
  return unknown(error instanceof Error ? error.message : String(error));
179
420
  }
180
421
  }
@@ -211,7 +452,7 @@ export function censusOpenPrs(inventory: Inventory, now = Date.now()): Census {
211
452
  return result;
212
453
  }
213
454
 
214
- export function guardNewPr(input: { files: readonly string[] | null; seat?: Seat | null; seatCap?: number; cellId?: string | null; hunks?: readonly DiffHunk[]; hunkOverlap?: HunkOverlapMode }, inventory: Inventory): GuardVerdict {
455
+ export function guardNewPr(input: { files: readonly string[] | null; seat?: Seat | null; seatCap?: number; cellId?: string | null; cardId?: string | null; slice?: { slice: number; total: number } | null; boundPrNumber?: number; hunks?: readonly DiffHunk[]; hunkOverlap?: HunkOverlapMode }, inventory: Inventory): GuardVerdict {
215
456
  if (!input.files || input.files.length === 0 || input.files.some(file => !file || file.startsWith('/') || file.includes('..'))) {
216
457
  const reasons: GuardReason[] = [{ code: 'candidate-unknown', detail: '새 PR 변경 파일을 확인할 수 없음' }];
217
458
  debug.log('pr.tool', 'guard-refuse', { status: 'unknown', reasons });
@@ -260,7 +501,18 @@ export function guardNewPr(input: { files: readonly string[] | null; seat?: Seat
260
501
  const status = reasons.some(reason => reason.code === 'candidate-unknown') ? 'unknown' : reasons.length ? 'refuse' : 'allow';
261
502
  if (status !== 'allow') debug.log('pr.tool', 'guard-refuse', { status, reasons, warnings });
262
503
  else if (warnings.length) debug.log('pr.tool', 'guard-warn', { warnings });
263
- return { status, reasons, warnings };
504
+ const legacyBound = sameCell.length === 1 && sameCell[0]?.cardMarkerPresent === false
505
+ && input.boundPrNumber === sameCell[0].number;
506
+ const reusable = status === 'refuse' && reasons.length === 1 && reasons[0]?.code === 'same-cell'
507
+ && input.cardId && sameCell.length === 1 && (sameCell[0]?.cardId === input.cardId || legacyBound)
508
+ && (input.slice?.slice ?? null) === (sameCell[0]?.slice?.slice ?? null)
509
+ && (input.slice?.total ?? null) === (sameCell[0]?.slice?.total ?? null)
510
+ && sameCell[0].headRefName && /^[0-9a-f]{40}$/i.test(sameCell[0].headSha)
511
+ ? { number: sameCell[0].number, branch: sameCell[0].headRefName, headSha: sameCell[0].headSha,
512
+ cellId: input.cellId!, cardId: input.cardId, ...(legacyBound ? { legacyBound: true as const } : {}) } : undefined;
513
+ return { status, reasons, warnings,
514
+ ...(sameCell.length ? { sameCellConflicts: sameCell.map(pr => ({ pr: pr.number, cardId: pr.cardId ?? null, slice: pr.slice ?? null })) } : {}),
515
+ ...(reusable ? { sameCellReuse: reusable } : {}) };
264
516
  }
265
517
 
266
518
  /** Persist the unknown-read budget across harness processes sharing one instance state root. */
@@ -5,17 +5,27 @@
5
5
  * 만지는 줄들의 의도를 읽는다. 착지하면 지운다. 정본은 PR 본문이고 이것은 «색인»이다 — 못 읽으면 빈 목록
6
6
  * (머지는 종전 그대로 간다), 못 쓰면 발사·착지를 막지 않는다(호출부가 fail-soft).
7
7
  *
8
- * 폭증 막기: 런당 1줄(같은 칸 재발사는 덮어씀) · 의도 ≤300자 · 파일 ≤20 · 48h 지난 줄은 읽을 때 거르고 쓸 때 걷는다.
8
+ * 폭증 막기: 런당 1줄(같은 칸 재발사는 덮어씀) · 의도 ≤300자 · 파일 ≤20.
9
+ *
10
+ * 걷기: 닫힌 PR(`removeInflightForClosedPrs`)·끝난 런(`removeInflightForFinishedRuns`)은 그때 바로 걷고,
11
+ * 남은 줄은 `gcInflight` 가 6h 시한(`INFLIGHT_GC_TTL_MS`)으로 걷는다 — 6h 넘게 살아 있는 런은 없다.
12
+ * 48h 지연 필터(`INFLIGHT_TTL_MS`)는 GC 가 한 번도 안 돈 등록부의 백스톱이다(읽기는 쓰지 않는다).
13
+ *
14
+ * 쓰기는 임시 파일 → rename 원자 쓰기 — 반쯤 쓴 등록부를 동시에 읽는 쪽이 보지 않는다.
9
15
  */
10
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
11
- import { dirname, join } from 'node:path';
16
+ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
17
+ import { randomUUID } from 'node:crypto';
18
+ import { basename, dirname, join } from 'node:path';
12
19
  import { debug } from '../debug/log.js';
13
20
  import { effectiveInstanceRoot } from '../instance/resolve.js';
14
21
  import { withFileLockSync } from '../storage/file-lock.js';
15
22
 
16
23
  export const INFLIGHT_INTENT_MAX_CHARS = 300;
17
24
  export const INFLIGHT_FILES_MAX = 20;
25
+ /** 지연 필터 시한 — GC 가 안 돈 등록부가 머지를 영원히 오염시키지 못하게 하는 백스톱. */
18
26
  export const INFLIGHT_TTL_MS = 48 * 60 * 60 * 1000;
27
+ /** GC 시한 — 이만큼 지난 줄은 지금 살아 있는 런이 아니다. */
28
+ export const INFLIGHT_GC_TTL_MS = 6 * 60 * 60 * 1000;
19
29
  export const INFLIGHT_REGISTRY_FILE = 'inflight-registry.json';
20
30
 
21
31
  export interface InflightEntry {
@@ -33,6 +43,53 @@ export interface InflightOptions {
33
43
  now?: () => Date;
34
44
  }
35
45
 
46
+ /** 줄을 연 과제 카드의 최소 모양 — PR 상태(`state`)는 호출부가 gh 로 채워 준다. */
47
+ export interface InflightOwnerCard {
48
+ id: string;
49
+ runId?: string;
50
+ pr?: number | { number: number; state?: string };
51
+ }
52
+
53
+ export interface InflightGcInput extends InflightOptions {
54
+ /** 머지 없이 닫힌 PR 번호. */
55
+ closedPrs?: readonly number[];
56
+ /** 끝난 런 id — 정상 종료·중단 모두. */
57
+ finishedRuns?: readonly string[];
58
+ /** 과제 카드들 — `card.pr` 이 닫힌 PR 이면 그 카드의 줄을 걷는다. */
59
+ cards?: Iterable<InflightOwnerCard>;
60
+ }
61
+
62
+ export type InflightGcReason = 'expired' | 'finished-run' | 'closed-pr';
63
+
64
+ export interface InflightGcResult {
65
+ /** 걷고 남은 줄 수. */
66
+ kept: number;
67
+ removed: Array<{ entry: InflightEntry; reason: InflightGcReason }>;
68
+ }
69
+
70
+ const CLOSED_PR_STATES = new Set(['CLOSED', 'closed', 'MERGED', 'merged']);
71
+
72
+ function prNumberOf(card: InflightOwnerCard): number | undefined {
73
+ return typeof card.pr === 'number' ? card.pr : card.pr?.number;
74
+ }
75
+
76
+ function isClosedOwner(card: InflightOwnerCard, closedPrs: ReadonlySet<number>): boolean {
77
+ const pr = card.pr;
78
+ if (pr === undefined) return false;
79
+ if (typeof pr === 'object' && pr.state !== undefined && CLOSED_PR_STATES.has(pr.state)) return true;
80
+ const number = prNumberOf(card);
81
+ return number !== undefined && closedPrs.has(number);
82
+ }
83
+
84
+ /** 닫힌 카드들의 카드 id·runId — 줄을 찾는 키. */
85
+ function closedOwnerKeys(cards: readonly InflightOwnerCard[], closedPrs: ReadonlySet<number>): { cardIds: Set<string>; runIds: Set<string> } {
86
+ const owners = cards.filter((card) => isClosedOwner(card, closedPrs));
87
+ return {
88
+ cardIds: new Set(owners.map((card) => card.id)),
89
+ runIds: new Set(owners.flatMap((card) => (card.runId ? [card.runId] : []))),
90
+ };
91
+ }
92
+
36
93
  /** 기본 자리 — 과제 카드 상태(`task-agent-actions.json`)와 같은 우주 뿌리. */
37
94
  export function inflightRegistryPath(): string {
38
95
  return join(effectiveInstanceRoot(), INFLIGHT_REGISTRY_FILE);
@@ -52,9 +109,30 @@ function isEntry(value: unknown): value is InflightEntry {
52
109
  && Array.isArray(entry.files) && typeof entry.registeredAt === 'string';
53
110
  }
54
111
 
55
- function isExpired(entry: InflightEntry, now: Date): boolean {
112
+ function olderThan(entry: InflightEntry, now: Date, ttlMs: number): boolean {
56
113
  const at = Date.parse(entry.registeredAt);
57
- return !Number.isFinite(at) || now.getTime() - at > INFLIGHT_TTL_MS;
114
+ return !Number.isFinite(at) || now.getTime() - at > ttlMs;
115
+ }
116
+
117
+ function isExpired(entry: InflightEntry, now: Date): boolean {
118
+ return olderThan(entry, now, INFLIGHT_TTL_MS);
119
+ }
120
+
121
+ /**
122
+ * 임시 파일 → rename. 임시 파일은 같은 디렉터리(=같은 파일시스템)에 만들어 rename 이 원자다 —
123
+ * 읽는 쪽은 «옛 등록부» 아니면 «완성된 새 등록부»만 본다(반쯤 쓴 JSON 을 파싱하지 않는다).
124
+ */
125
+ function writeEntriesAtomic(path: string, entries: InflightEntry[]): void {
126
+ const dir = dirname(path);
127
+ mkdirSync(dir, { recursive: true });
128
+ const tmp = join(dir, `.${basename(path)}.${process.pid}.${randomUUID()}.tmp`);
129
+ try {
130
+ writeFileSync(tmp, `${JSON.stringify({ entries })}\n`, 'utf8');
131
+ renameSync(tmp, path);
132
+ } catch (error) {
133
+ try { unlinkSync(tmp); } catch { /* rename 이 이미 끝났으면 지울 임시 파일이 없다 */ }
134
+ throw error;
135
+ }
58
136
  }
59
137
 
60
138
  /** 잠금 안에서 읽고 고쳐 쓴다 — 48h 지난 줄은 쓸 때 걷는다(`expire` 관측). */
@@ -67,11 +145,24 @@ function mutate(opts: InflightOptions, update: (entries: InflightEntry[]) => Inf
67
145
  if (live.length < all.length) {
68
146
  try { debug.log('pr-tool.inflight', 'expire', { phase: 'write', runIds: all.filter((entry) => isExpired(entry, now)).map((entry) => entry.runId) }); } catch { /* fail-soft */ }
69
147
  }
70
- mkdirSync(dirname(path), { recursive: true });
71
- writeFileSync(path, JSON.stringify({ entries: update(live) }));
148
+ writeEntriesAtomic(path, update(live));
72
149
  });
73
150
  }
74
151
 
152
+ /** 잠금 한 번·원자 쓰기 한 번으로 걷는다 — 지울 줄이 없으면 디스크를 건드리지 않는다. */
153
+ function sweep(opts: InflightOptions, reason: string, drop: (entry: InflightEntry) => boolean): InflightEntry[] {
154
+ const path = opts.path ?? inflightRegistryPath();
155
+ let removed: InflightEntry[] = [];
156
+ withFileLockSync(`${path}.lock`, () => {
157
+ const all = readEntries(path);
158
+ removed = all.filter(drop);
159
+ if (removed.length === 0) return;
160
+ writeEntriesAtomic(path, all.filter((entry) => !drop(entry)));
161
+ });
162
+ try { debug.log('pr-tool.inflight', 'remove', { reason, removed: removed.map((entry) => entry.runId) }); } catch { /* fail-soft */ }
163
+ return removed;
164
+ }
165
+
75
166
  /** 한 줄 등록 — 같은 runId 또는 같은 칸의 옛 줄을 덮어쓴다. 의도·파일은 상한에서 자른다. */
76
167
  export function registerInflight(input: { runId: string; cell: string; intent: string; files: readonly string[]; cardId?: string }, opts: InflightOptions = {}): InflightEntry {
77
168
  const now = (opts.now ?? (() => new Date()))();
@@ -141,3 +232,65 @@ export function removeInflightForPr(
141
232
  }
142
233
  return removed;
143
234
  }
235
+
236
+ /**
237
+ * 닫힌 PR(머지 포함)의 줄을 지운다 — `closedPrs` 번호와 `card.pr` 의 닫힘 상태 양쪽을 본다.
238
+ * 열린 PR 카드는 건드리지 않고, 닫힌 카드가 없으면 디스크도 건드리지 않는다.
239
+ */
240
+ export function removeInflightForClosedPrs(
241
+ closedPrs: readonly number[],
242
+ cards: Iterable<InflightOwnerCard>,
243
+ opts: InflightOptions = {},
244
+ ): number {
245
+ const closed = new Set(closedPrs);
246
+ const { cardIds, runIds } = closedOwnerKeys([...cards], closed);
247
+ if (cardIds.size === 0 && runIds.size === 0) {
248
+ try { debug.log('pr-tool.inflight', 'remove', { reason: 'pr-closed', removed: [], cards: 0, closedPrs: [...closed] }); } catch { /* fail-soft */ }
249
+ return 0;
250
+ }
251
+ return sweep(opts, `pr-closed:${[...closed].sort((a, b) => a - b).join(',')}`, (entry) => (
252
+ (entry.cardId !== undefined && cardIds.has(entry.cardId)) || runIds.has(entry.runId)
253
+ )).length;
254
+ }
255
+
256
+ /** 끝난 런(정상 종료·중단 모두)의 줄을 지운다 — 카드가 없어도 runId 로 걷는다. */
257
+ export function removeInflightForFinishedRuns(runIds: readonly string[], opts: InflightOptions = {}): number {
258
+ const finished = new Set(runIds);
259
+ if (finished.size === 0) return 0;
260
+ return sweep(opts, 'run-finished', (entry) => finished.has(entry.runId)).length;
261
+ }
262
+
263
+ /**
264
+ * 등록부 청소 한 번 — 닫힌 PR 줄 · 끝난 런 줄 · 6h 넘은 줄을 한 잠금·원자 쓰기 한 번으로 걷는다.
265
+ * 지울 줄이 없으면 디스크를 건드리지 않고, 무엇이 왜 걷혔는지(`reason`)를 돌려준다.
266
+ * 우선순위: `expired` → `finished-run` → `closed-pr`(한 줄이 여러 규칙에 걸리면 시한이 먼저다).
267
+ */
268
+ export function gcInflight(input: InflightGcInput = {}): InflightGcResult {
269
+ const path = input.path ?? inflightRegistryPath();
270
+ const now = (input.now ?? (() => new Date()))();
271
+ const finished = new Set(input.finishedRuns ?? []);
272
+ const { cardIds, runIds } = closedOwnerKeys([...(input.cards ?? [])], new Set(input.closedPrs ?? []));
273
+ const classify = (entry: InflightEntry): InflightGcReason | null => {
274
+ if (olderThan(entry, now, INFLIGHT_GC_TTL_MS)) return 'expired';
275
+ if (finished.has(entry.runId)) return 'finished-run';
276
+ if (runIds.has(entry.runId)) return 'closed-pr';
277
+ if (entry.cardId !== undefined && cardIds.has(entry.cardId)) return 'closed-pr';
278
+ return null;
279
+ };
280
+ let kept = 0;
281
+ let removed: InflightGcResult['removed'] = [];
282
+ withFileLockSync(`${path}.lock`, () => {
283
+ const all = readEntries(path);
284
+ removed = all.flatMap((entry) => {
285
+ const reason = classify(entry);
286
+ return reason === null ? [] : [{ entry, reason }];
287
+ });
288
+ kept = all.length - removed.length;
289
+ if (removed.length === 0) return;
290
+ writeEntriesAtomic(path, all.filter((entry) => classify(entry) === null));
291
+ });
292
+ try {
293
+ debug.log('pr-tool.inflight', 'gc', { kept, removed: removed.map(({ entry, reason }) => ({ runId: entry.runId, reason })) });
294
+ } catch { /* fail-soft */ }
295
+ return { kept, removed };
296
+ }