elanous 0.2.40 → 0.2.42

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 (685) hide show
  1. package/README.md +7 -7
  2. package/apps/pwa/out/404/index.html +1 -1
  3. package/apps/pwa/out/404.html +1 -1
  4. package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_buildManifest.js +1 -1
  5. package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/{4769.9fd582071301f382.js → 1063.2fb8a9dc55c731f2.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +36 -0
  8. package/apps/pwa/out/_next/static/chunks/1147-5196d19c222051b3.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{470.51a72a2348306455.js → 1262.e804feb8fd1acb8c.js} +2 -2
  10. package/apps/pwa/out/_next/static/chunks/1319.b9b815ae0f181e06.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/{7219-15bdd9a80661af8e.js → 1455-530ca16c7e71baff.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/{1281.d81877e35b9068f4.js → 1487.2f67b79444062da6.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/1574.878c18ecc97b1bd4.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +32 -0
  16. package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1945.7b54990d578c119b.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +286 -0
  22. package/apps/pwa/out/_next/static/chunks/2160.a097e7b138ae1c55.js +23 -0
  23. package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +34 -0
  26. package/apps/pwa/out/_next/static/chunks/{1350.59f40e39e02d91b1.js → 2456.b6de0e1c05f28843.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2690.0521c738b7749931.js +115 -0
  32. package/apps/pwa/out/_next/static/chunks/{5436.fdf6fa8ae3e9588d.js → 2974.595d1e29d040cb6a.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/{5383.bdfa5d886e39a826.js → 3207.a756c95e9ec8c3c8.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +93 -0
  35. package/apps/pwa/out/_next/static/chunks/{2177.df8547f9d7215c4e.js → 3347.f846266b5e5f3ede.js} +3 -3
  36. package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +41 -0
  37. package/apps/pwa/out/_next/static/chunks/{9184.45afbf44fec87574.js → 3482.a9ae9d2ad6a5bd97.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/3507.97e1977bc4d5fb0a.js +2 -0
  39. package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +24 -0
  41. package/apps/pwa/out/_next/static/chunks/{8099.1cccf31c1baa7e1c.js → 3741.9cf76acaa2ee5298.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{4864a2ca.1c1c280a7772c5e5.js → 3d0154af.5331fec737ffe8c3.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4043.6b91203436c9c86b.js +309 -0
  48. package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +77 -0
  49. package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/{3330.7443183b7e6fdbdf.js → 4276.336d76d858fcdf1b.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4320.583cbab64db6be02.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4770.769c9cd9c7034eba.js +148 -0
  55. package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +74 -0
  56. package/apps/pwa/out/_next/static/chunks/{3b355b3d.6f73c98bf6f9c749.js → 50061469.0326382241ef3b2d.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +149 -0
  58. package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +27 -0
  60. package/apps/pwa/out/_next/static/chunks/{8d173e38.18a2f7b4e1848bdb.js → 53c73f84.373b6160adfa89a4.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/{1537-6bb97b5214928ea9.js → 5431-72a1dbde59400691.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/5440-4236de26c5ea5856.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5885.0a3e52c8faccec98.js +287 -0
  70. package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/{261.0d471e8aded799f3.js → 6131.348b7d6bdcd267ce.js} +3 -3
  73. package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +90 -0
  74. package/apps/pwa/out/_next/static/chunks/{9175.2de5209a5ce6f68a.js → 6377.bc7df239edbff244.js} +1 -1
  75. package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/{2226.2cf61ec781827219.js → 6632.0eac1c554f8f2ef3.js} +2 -2
  78. package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +104 -0
  82. package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +94 -0
  83. package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/{b84cad00.594027ebe0531fe2.js → 73d2eb0e.4966ad55293c801a.js} +3 -3
  90. package/apps/pwa/out/_next/static/chunks/{6257.c0037b8949239f5e.js → 7411.9b4b8b747dc7b802.js} +2 -2
  91. package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +24 -0
  92. package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/{9189505a.980427ac048c8c60.js → 7a98bc80.fa935989e829f9c2.js} +1 -1
  98. package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/8138.6c10b2a356d52fca.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/{2566.beaffc3a5e1fcd2e.js → 8262.ddc03e06b733f184.js} +1 -1
  101. package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +8 -0
  103. package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8505.fd803c6c33b54cc3.js +79 -0
  105. package/apps/pwa/out/_next/static/chunks/{8272-025653884f9645d2.js → 8546-6f03f8ea34dd61d9.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/{6051-0ae955eedb10adb9.js → 8673-bab689a55f7e13b9.js} +3 -3
  107. package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/9211-330b6736a63d4cb3.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/{4351.0f37a6db2b34d50b.js → 9249.976f1e69a9d80754.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9415.69e4a21c60c7d2db.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{6749.0744ce6eb08d40e9.js → 9427.96aa912ddb5801e0.js} +16 -16
  119. package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{7611.85f045bf232053ac.js → 9633.ce5bddc56784da72.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/{3746-f80a78f220bf37b5.js → 9656-374232558fd528d0.js} +1 -1
  123. package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +62 -0
  124. package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/9a852a62-6192a6ea9346ced3.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/live-run/{page-83d27757e2b8060a.js → page-0e3d7b9421b79dac.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/morning/page-0f291d3c8fd55a3a.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/term/{page-139d722de1dcd6c1.js → page-bf067887b3307423.js} +1 -1
  170. package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{409c787b.e48c5bec8ec10456.js → b450776b.3db34d8d965f82d3.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/{ca3702f8.2913182e73889fa2.js → e8543907.5b040a5f22a0b0e5.js} +7 -7
  179. package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +1 -0
  184. package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +1 -0
  185. package/apps/pwa/out/_next/static/css/{b9d8151e48998417.css → 62c858ad53854f47.css} +1 -1
  186. package/apps/pwa/out/approvals/index.html +1 -1
  187. package/apps/pwa/out/approvals/index.txt +22 -22
  188. package/apps/pwa/out/autopilot/index.html +1 -1
  189. package/apps/pwa/out/autopilot/index.txt +22 -22
  190. package/apps/pwa/out/board/index.html +1 -1
  191. package/apps/pwa/out/board/index.txt +22 -22
  192. package/apps/pwa/out/botlab/index.html +1 -1
  193. package/apps/pwa/out/botlab/index.txt +21 -21
  194. package/apps/pwa/out/bots/index.html +1 -1
  195. package/apps/pwa/out/bots/index.txt +21 -21
  196. package/apps/pwa/out/ceo/index.html +1 -1
  197. package/apps/pwa/out/ceo/index.txt +22 -22
  198. package/apps/pwa/out/chat/index.html +1 -1
  199. package/apps/pwa/out/chat/index.txt +21 -21
  200. package/apps/pwa/out/consult/index.html +1 -1
  201. package/apps/pwa/out/consult/index.txt +22 -22
  202. package/apps/pwa/out/control/index.html +1 -1
  203. package/apps/pwa/out/control/index.txt +22 -22
  204. package/apps/pwa/out/decisions/index.html +1 -1
  205. package/apps/pwa/out/decisions/index.txt +21 -21
  206. package/apps/pwa/out/design-check/index.html +1 -1
  207. package/apps/pwa/out/design-check/index.txt +22 -22
  208. package/apps/pwa/out/editor/index.html +1 -1
  209. package/apps/pwa/out/editor/index.txt +23 -24
  210. package/apps/pwa/out/exec/index.html +1 -1
  211. package/apps/pwa/out/exec/index.txt +22 -22
  212. package/apps/pwa/out/field/index.html +1 -1
  213. package/apps/pwa/out/field/index.txt +22 -22
  214. package/apps/pwa/out/healthz +1 -1
  215. package/apps/pwa/out/index.html +1 -1
  216. package/apps/pwa/out/index.txt +21 -21
  217. package/apps/pwa/out/inside/index.html +1 -1
  218. package/apps/pwa/out/inside/index.txt +22 -22
  219. package/apps/pwa/out/intake/index.html +1 -1
  220. package/apps/pwa/out/intake/index.txt +22 -22
  221. package/apps/pwa/out/live/index.html +1 -1
  222. package/apps/pwa/out/live/index.txt +23 -24
  223. package/apps/pwa/out/live-run/index.html +1 -1
  224. package/apps/pwa/out/live-run/index.txt +23 -24
  225. package/apps/pwa/out/loops/index.html +1 -1
  226. package/apps/pwa/out/loops/index.txt +21 -21
  227. package/apps/pwa/out/market/index.html +1 -1
  228. package/apps/pwa/out/market/index.txt +21 -21
  229. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  230. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  231. package/apps/pwa/out/morning/index.html +1 -1
  232. package/apps/pwa/out/morning/index.txt +22 -22
  233. package/apps/pwa/out/observatory/index.html +1 -1
  234. package/apps/pwa/out/observatory/index.txt +22 -22
  235. package/apps/pwa/out/ops/checklist/index.html +1 -1
  236. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  237. package/apps/pwa/out/ops/release/index.html +1 -1
  238. package/apps/pwa/out/ops/release/index.txt +21 -21
  239. package/apps/pwa/out/ops/seats/index.html +1 -1
  240. package/apps/pwa/out/ops/seats/index.txt +21 -21
  241. package/apps/pwa/out/outputs/index.html +1 -1
  242. package/apps/pwa/out/outputs/index.txt +21 -21
  243. package/apps/pwa/out/reflection/index.html +1 -1
  244. package/apps/pwa/out/reflection/index.txt +22 -22
  245. package/apps/pwa/out/scheduler/index.html +1 -1
  246. package/apps/pwa/out/scheduler/index.txt +22 -22
  247. package/apps/pwa/out/sessions/index.html +1 -1
  248. package/apps/pwa/out/sessions/index.txt +21 -21
  249. package/apps/pwa/out/settings/devices/index.html +1 -1
  250. package/apps/pwa/out/settings/devices/index.txt +22 -22
  251. package/apps/pwa/out/settings/index.html +1 -1
  252. package/apps/pwa/out/settings/index.txt +22 -22
  253. package/apps/pwa/out/setup/done/index.html +1 -1
  254. package/apps/pwa/out/setup/done/index.txt +22 -22
  255. package/apps/pwa/out/setup/index.html +1 -1
  256. package/apps/pwa/out/setup/index.txt +22 -22
  257. package/apps/pwa/out/share/index.html +1 -1
  258. package/apps/pwa/out/share/index.txt +22 -22
  259. package/apps/pwa/out/showroom/index.html +1 -1
  260. package/apps/pwa/out/showroom/index.txt +21 -21
  261. package/apps/pwa/out/tasks/index.html +1 -1
  262. package/apps/pwa/out/tasks/index.txt +22 -22
  263. package/apps/pwa/out/term/index.html +1 -1
  264. package/apps/pwa/out/term/index.txt +22 -22
  265. package/apps/pwa/out/today/index.html +1 -1
  266. package/apps/pwa/out/today/index.txt +22 -22
  267. package/apps/pwa/out/trace/index.html +1 -1
  268. package/apps/pwa/out/trace/index.txt +22 -22
  269. package/apps/pwa/out/vault/index.html +1 -1
  270. package/apps/pwa/out/vault/index.txt +22 -22
  271. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  272. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  273. package/apps/pwa/out/workflows/index.html +1 -1
  274. package/apps/pwa/out/workflows/index.txt +22 -22
  275. package/apps/pwa/out/workspace/index.html +1 -1
  276. package/apps/pwa/out/workspace/index.txt +22 -22
  277. package/apps/pwa/out/worktrees/index.html +1 -1
  278. package/apps/pwa/out/worktrees/index.txt +22 -22
  279. package/bin/elanous.mjs +6 -1
  280. package/catalog/models/openai/gpt-6.1-sol.yaml +16 -0
  281. package/catalog/models/openai/gpt-6.1-terra.yaml +16 -0
  282. package/docs/goal-context/04-mock-scenarios.md +13 -0
  283. package/graphs/catalog/catalog.yaml +1 -1
  284. package/graphs/demo/persona-teacher/lesson-pack.yaml +16 -0
  285. package/graphs/demo/persona-teacher/recipes.yaml +17 -0
  286. package/graphs/demo/persona-teacher/weekly-lesson.yaml +36 -0
  287. package/graphs/heal/heal-loop.yaml +1 -0
  288. package/graphs/overlays/stop-heal-ladder.yaml +136 -2
  289. package/graphs/release/release-loop.yaml +3 -1
  290. package/graphs/units/gate/gate.yaml +20 -0
  291. package/graphs/units/git/git.yaml +26 -0
  292. package/graphs/units/implement/implement.yaml +19 -0
  293. package/graphs/units/landing/landing.yaml +18 -0
  294. package/graphs/units/review/review.yaml +20 -0
  295. package/graphs/units/verbs/recipes.yaml +13 -10
  296. package/graphs/units/verbs/research.yaml +20 -0
  297. package/graphs/work/research-report.yaml +21 -3
  298. package/package.json +1 -1
  299. package/scripts/disk-steward.ts +46 -0
  300. package/scripts/fleet/node-setup.sh +50 -0
  301. package/scripts/graph-units/verbs/research.ts +55 -0
  302. package/scripts/heal/run-role.ts +99 -5
  303. package/scripts/land-global-checks.ts +6 -1
  304. package/scripts/lib/decompose-ab.ts +1 -0
  305. package/scripts/lib/fixtures/tui-regress/help-insufficient-commands.txt +15 -0
  306. package/scripts/lib/fixtures/tui-regress/help-live.txt +42 -0
  307. package/scripts/lib/fixtures/tui-regress/help-missing-keys.txt +21 -0
  308. package/scripts/lib/fixtures/tui-regress/help-original.txt +51 -0
  309. package/scripts/lib/fixtures/tui-regress/help-shifted-input.txt +26 -0
  310. package/scripts/lib/tui-regress-checks.ts +33 -13
  311. package/scripts/llm-cost-ledger.ts +179 -8
  312. package/scripts/measure-nl-routing-corpus.ts +10 -2
  313. package/scripts/persona-teacher/weekly-lesson-node.ts +172 -0
  314. package/scripts/public-copy-lint.ts +163 -19
  315. package/scripts/ratchets/train-side-invariants.sh +0 -1
  316. package/scripts/release-loop/gate-autoheal.ts +117 -0
  317. package/scripts/release-loop/gate-baseline-receipt.ts +55 -10
  318. package/scripts/release-loop/gate-diff.ts +22 -3
  319. package/scripts/release-loop/gate-image-pin.ts +81 -0
  320. package/scripts/release-loop/gate-node-sim.ts +207 -0
  321. package/scripts/release-loop/gate-node.ts +489 -62
  322. package/scripts/release-loop/gate-pod-image-sticky-plan.ts +99 -0
  323. package/scripts/release-loop/gate-radius-live.ts +97 -13
  324. package/scripts/release-loop/gate-shard-dispatch.ts +43 -0
  325. package/scripts/release-loop/gate-shard-load-flaky.ts +256 -0
  326. package/scripts/release-loop/gate-train-receipts.ts +122 -0
  327. package/scripts/release-loop/gate-triage-node.ts +70 -10
  328. package/scripts/release-loop/resume-release.ts +330 -23
  329. package/scripts/release-loop/traffic-node.ts +6 -3
  330. package/scripts/release-loop/unattended-release.ts +4 -2
  331. package/scripts/shadow-authoring-probe.ts +20 -0
  332. package/scripts/ux-sim.ts +37 -3
  333. package/scripts/warm-pool-provider-config.ts +263 -0
  334. package/skills/omni-crawl/SKILL.md +5 -2
  335. package/skills/omni-crawl/scripts/main.ts +84 -23
  336. package/src/acp/core-turn-bridge.ts +99 -4
  337. package/src/agent/registry.ts +2 -0
  338. package/src/agent/task-notification.ts +25 -5
  339. package/src/agent/types.ts +6 -0
  340. package/src/agent-mission/mission-cli.ts +24 -0
  341. package/src/agent-mission/mission-out.ts +595 -0
  342. package/src/agent-mission/tool-install.ts +88 -2
  343. package/src/agent-substrate/context-strategy.ts +4 -4
  344. package/src/agent-substrate/self-review-cli.ts +267 -40
  345. package/src/auto-research/research-turn.ts +133 -51
  346. package/src/autopilot/discovery/wiki-claim-proposer.ts +22 -1
  347. package/src/autopilot/pr-manager.ts +38 -7
  348. package/src/autopilot/pty-control-loop.ts +2 -0
  349. package/src/boot/daemon-tools/self-implement.ts +46 -9
  350. package/src/budget/shadow-arms.ts +52 -0
  351. package/src/capture/tui-self-report.ts +22 -6
  352. package/src/chat/index.ts +1 -1
  353. package/src/chat/pickers/state.ts +7 -1
  354. package/src/cli/fixtures/review-loop-breaker/opwaiver-32646-1694ccc7a170.json +1 -0
  355. package/src/cli/fixtures/review-loop-breaker/opwaiver-32853-bd8f73595c83.json +1 -0
  356. package/src/cli/fixtures/review-loop-breaker/opwaiver-33097-1cc85900413c.json +1 -0
  357. package/src/cli/fixtures/review-loop-breaker/opwaiver-33106-972f31e4a3ca.json +1 -0
  358. package/src/cli/fixtures/review-loop-breaker/opwaiver-33113-3ab51f05968f.json +1 -0
  359. package/src/cli/fixtures/review-loop-breaker/pr-33097-1cc85900413c.json +1 -0
  360. package/src/cli/log-event-names.ts +5 -0
  361. package/src/cli/mcp-login.ts +8 -18
  362. package/src/cli/msg-cli.ts +57 -1
  363. package/src/cli/pr-land-heavy-remote.ts +47 -9
  364. package/src/cli/pr-land-overlap-evidence.ts +100 -1
  365. package/src/cli/pr-land-pod-shadow-job.ts +60 -0
  366. package/src/cli/release-checklist-read-cli.ts +20 -10
  367. package/src/cli/release-cli.ts +27 -1
  368. package/src/cli/research-cli.ts +4 -5
  369. package/src/cli/sim-cli.ts +7 -1
  370. package/src/cli/tasks-cli.ts +12 -4
  371. package/src/codex/models.ts +30 -15
  372. package/src/compact/auto.ts +8 -1
  373. package/src/control-plane/ledger.ts +52 -2
  374. package/src/control-plane/server.ts +16 -2
  375. package/src/core-turn/run-core-turn.ts +313 -89
  376. package/src/core-turn/run-goal-loop.ts +106 -2
  377. package/src/core-turn/types.ts +2 -0
  378. package/src/dashboard/acp-boot.ts +72 -3
  379. package/src/dashboard/classic-sidetab-mission-job.ts +58 -0
  380. package/src/dashboard/classic-sidetab-mission.ts +185 -0
  381. package/src/dashboard/index.ts +155 -15
  382. package/src/dashboard/input/agent-wake.ts +23 -1
  383. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +9 -0
  384. package/src/dashboard/mission-cli-consumer.ts +153 -0
  385. package/src/dashboard/seat-boot-preamble.ts +171 -1
  386. package/src/dashboard/side-basic-runtime.ts +54 -1
  387. package/src/dashboard/sidetab-runtime.ts +90 -1
  388. package/src/dashboard/skill-catalog-preamble.ts +101 -0
  389. package/src/dashboard/skill-detection-runtime.ts +55 -15
  390. package/src/dashboard/slash-runtime/dashboard-handlers.ts +15 -0
  391. package/src/dashboard/turn-preamble.ts +65 -16
  392. package/src/dashboard/turn-prompt-runtime.ts +4 -2
  393. package/src/disk-steward/judge.ts +73 -0
  394. package/src/disk-steward/ledger.ts +122 -0
  395. package/src/disk-steward/steward.ts +373 -0
  396. package/src/domains/guardian-tick.ts +191 -2
  397. package/src/graph-runner/graph-cli.ts +44 -7
  398. package/src/graph-runner/graph-tick.ts +10 -2
  399. package/src/graph-runner/in-process-registry.ts +37 -0
  400. package/src/graph-runner/runner.ts +34 -4
  401. package/src/grok/agent-search.ts +14 -4
  402. package/src/grok/credential.ts +28 -11
  403. package/src/grounding/ref-index.ts +19 -0
  404. package/src/grounding/sources-cli.ts +9 -1
  405. package/src/harness/harness-cli-command.ts +267 -14
  406. package/src/harness/harness-incidents.ts +312 -7
  407. package/src/harness/harness-pod-dispatch.ts +81 -15
  408. package/src/harness/harness-space.ts +15 -1
  409. package/src/harness/harness-stop.ts +155 -11
  410. package/src/harness/progress-worker-entry.ts +59 -0
  411. package/src/harness/progress-worker-runtime.ts +11 -0
  412. package/src/index.ts +52 -7
  413. package/src/instance/dev-project.ts +25 -0
  414. package/src/instance/spawn-elanous.ts +6 -4
  415. package/src/intelligence-map/model-alias.ts +6 -5
  416. package/src/intelligence-map/model-catalog.ts +59 -2
  417. package/src/llm/model-defaults.ts +4 -4
  418. package/src/llm.ts +208 -19
  419. package/src/loops/orchestrator/coordinator-shadow.ts +226 -0
  420. package/src/loops/orchestrator/tick.ts +61 -0
  421. package/src/maturity/feature-maturity.ts +1 -1
  422. package/src/mcp/mcp-oauth.ts +3 -1
  423. package/src/model-tier/llm-tier-map.ts +9 -9
  424. package/src/model-tier/turn-tier.ts +1 -1
  425. package/src/msg/msg-mirror-github.ts +80 -0
  426. package/src/msg/msg-mirror-policy.ts +61 -0
  427. package/src/msg/msg-mirror-runner.ts +219 -0
  428. package/src/msg/msg-mirror-state.ts +33 -0
  429. package/src/msg/msg-store.ts +11 -0
  430. package/src/native-tool-catalog.ts +2 -2
  431. package/src/nexus/api/harness-traversal.ts +11 -0
  432. package/src/output-truncation.ts +41 -12
  433. package/src/pr-tool/census.ts +3 -2
  434. package/src/project/project-store.ts +48 -1
  435. package/src/pty-shell/tui-run-state.ts +188 -2
  436. package/src/release-loop/checklist-target-path-hint.ts +13 -0
  437. package/src/release-loop/checklist.ts +9 -1
  438. package/src/release-loop/gate-host-reservation.ts +144 -0
  439. package/src/release-loop/release-schedule.ts +90 -1
  440. package/src/repl/index.ts +43 -2
  441. package/src/research/axis-fanout.ts +121 -16
  442. package/src/research/bloom-exemplars.ts +137 -0
  443. package/src/research/gap-followup.ts +13 -1
  444. package/src/research/plugin-discovery.ts +36 -14
  445. package/src/research-bridge/invoke.ts +22 -5
  446. package/src/research-bridge/types.ts +2 -0
  447. package/src/self-dev/ask-launch-flow.ts +39 -25
  448. package/src/self-dev/decompose.ts +119 -5
  449. package/src/self-dev/dev-cli.ts +17 -17
  450. package/src/self-dev/graph-front-nodes.ts +23 -0
  451. package/src/self-dev/launch-preflight-text.ts +5 -6
  452. package/src/self-dev/launch-preflight.ts +9 -4
  453. package/src/self-dev/orchestrate.ts +27 -2
  454. package/src/self-dev/review-provider-fallback.ts +9 -2
  455. package/src/self-dev/run-supervisor.ts +226 -6
  456. package/src/self-dev/run-types.ts +7 -0
  457. package/src/self-implement/doctor-visit.ts +88 -0
  458. package/src/self-implement/gate-artifacts.ts +13 -0
  459. package/src/self-implement/gate-cli.ts +54 -24
  460. package/src/self-implement/gate-remote.ts +23 -5
  461. package/src/self-implement/goal-author-clarification.ts +31 -12
  462. package/src/self-implement/goal-author.ts +403 -8
  463. package/src/self-implement/graph-overlay-yaml.ts +7 -0
  464. package/src/self-implement/graph-stage-units.ts +172 -0
  465. package/src/self-implement/graph-templates.ts +162 -1
  466. package/src/self-implement/graph-variant.ts +37 -4
  467. package/src/self-implement/headless-elanous-driver.ts +31 -2
  468. package/src/self-implement/heal-intake.ts +112 -4
  469. package/src/self-implement/heal-triage.ts +20 -0
  470. package/src/self-implement/host-regate.ts +31 -8
  471. package/src/self-implement/knowhow-pack.ts +38 -0
  472. package/src/self-implement/merge-train.ts +5 -0
  473. package/src/self-implement/orchestrator.ts +359 -30
  474. package/src/self-implement/regate-detail.ts +38 -1
  475. package/src/self-implement/rework-child-provider.ts +25 -0
  476. package/src/self-implement/run-chart.ts +119 -0
  477. package/src/self-implement/run-ledger.ts +176 -17
  478. package/src/self-implement/run-stop.ts +208 -5
  479. package/src/self-implement/running-runs.ts +35 -12
  480. package/src/self-implement/self-implement-cli.ts +36 -4
  481. package/src/self-implement/stop-autoheal-operational.ts +43 -0
  482. package/src/self-implement/stop-autoheal.ts +28 -6
  483. package/src/session/chat.ts +69 -58
  484. package/src/session/request-size-guard.ts +123 -0
  485. package/src/session-runtime/index.ts +98 -4
  486. package/src/session-runtime/tier-flip.ts +3 -3
  487. package/src/sidetab-core/model.ts +150 -0
  488. package/src/sidetab-core/visible-agent-mission.ts +186 -0
  489. package/src/skills/name-match.ts +20 -0
  490. package/src/skills/tools/agent.ts +30 -2
  491. package/src/skills/tools/omni-search.ts +67 -5
  492. package/src/task-agent/parent-host.ts +14 -1
  493. package/src/task-agent/pod-distributor-entry.ts +3 -0
  494. package/src/task-agent/task-hand.ts +31 -1
  495. package/src/task-orchestrator/surfaces/pod-distributor.ts +48 -0
  496. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -0
  497. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +38 -1
  498. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +29 -4
  499. package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +33 -6
  500. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +45 -9
  501. package/src/task-orchestrator/surfaces/pod-source-receive.ts +163 -13
  502. package/src/task-orchestrator/surfaces/pod-source-spec.ts +3 -3
  503. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +209 -39
  504. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +80 -3
  505. package/src/task-orchestrator/surfaces/self-implement-pod.ts +280 -60
  506. package/src/task-orchestrator/surfaces/self-implement.ts +18 -0
  507. package/src/tokens.ts +35 -2
  508. package/src/tool-runtime/agent-runtime.ts +1 -0
  509. package/src/tool-runtime/elanous-skills-list-runtime.ts +5 -1
  510. package/src/tool-runtime/skill-exec-runtime.ts +33 -13
  511. package/src/tool-runtime/types.ts +1 -0
  512. package/src/tui/entry.ts +24 -7
  513. package/src/tui/sidetab/attach.ts +47 -0
  514. package/src/tui/sidetab/list.ts +45 -0
  515. package/src/tui/sidetab/nl-tool.ts +2 -136
  516. package/src/tui/sidetab/peek.ts +52 -0
  517. package/src/tui-client/dashboard-session.ts +3 -0
  518. package/src/tui-client/local-chat-turn.ts +26 -13
  519. package/src/tui.ts +22 -0
  520. package/src/user-config.ts +11 -5
  521. package/src/ux-sim/fake-kubectl.ts +37 -3
  522. package/src/ux-sim/fault-needs-human-reason-repair.ts +107 -0
  523. package/src/ux-sim/fault-pod-child-result.ts +42 -0
  524. package/src/ux-sim/fault-pool-cap.ts +11 -6
  525. package/src/ux-sim/mock-scenario-catalog.json +41 -0
  526. package/src/ux-sim/mock-scenario-catalog.ts +156 -0
  527. package/src/ux-sim/release-sim-rig.ts +262 -0
  528. package/src/ux-sim/scenario.ts +116 -23
  529. package/src/ux-sim/seminar-rehearsal.ts +109 -0
  530. package/src/ux-sim/subagent-tools-device.ts +2 -2
  531. package/src/version/packed-revision.json +1 -1
  532. package/src/voice/voice-tts-singleton.ts +18 -3
  533. package/src/web-search/grok.ts +5 -9
  534. package/apps/pwa/out/_next/static/chunks/1060.014f1f01a17aec26.js +0 -115
  535. package/apps/pwa/out/_next/static/chunks/1415.79821ec7ae99acdb.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/1452.162bb2d9d699e82a.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/147.f92f63bbc5e85fa5.js +0 -286
  538. package/apps/pwa/out/_next/static/chunks/1661.fb425a1d8a065b72.js +0 -149
  539. package/apps/pwa/out/_next/static/chunks/1671-1c22b40b50b208f2.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/1758.0204d3f48783c49d.js +0 -90
  541. package/apps/pwa/out/_next/static/chunks/1806.027bf78b6a94d340.js +0 -104
  542. package/apps/pwa/out/_next/static/chunks/2047.8f23e210995d4548.js +0 -2
  543. package/apps/pwa/out/_next/static/chunks/2061-7f2bbc53ae96c88b.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/2066.a77257c8c350770a.js +0 -23
  545. package/apps/pwa/out/_next/static/chunks/2096-a938496bc996dcbd.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/2200-9a453b8f99687d49.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/2281-b8cede695c6c650e.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/2499.02fbfd18126c10c0.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/2522.f2766b76d22c8bdf.js +0 -34
  550. package/apps/pwa/out/_next/static/chunks/2615.3c9738153de3ef3f.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/2727.e703d99ac414916d.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/2752.7904b8df0254d0ae.js +0 -27
  553. package/apps/pwa/out/_next/static/chunks/2762-5051a862dfa40e99.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/2767-31784bfedf8e245d.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/2774-719820e3f5b2a3eb.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/2812-f016aa0dda149287.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/2829-734fe8bf2eee7347.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/311-32dcac79970985e8.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/3175-f93f7495cd28253c.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/3184.8da993f5502d2140.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/3231-d7c981db0ff8bdcc.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/3250.151309c5176d6b4b.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/3517.a5aa1797a9dbf2ee.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/3567-45f676877ed9e8a5.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/3819.8a1de79b2e8c7298.js +0 -24
  566. package/apps/pwa/out/_next/static/chunks/39.9b37b0be4c128a83.js +0 -77
  567. package/apps/pwa/out/_next/static/chunks/4023.a19045970a9ce16a.js +0 -36
  568. package/apps/pwa/out/_next/static/chunks/413-24c8284bda08241d.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/4135.e45d6ebc29e20647.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/4283.65502f5d0e6ff6c0.js +0 -287
  571. package/apps/pwa/out/_next/static/chunks/4305-4e28fa92a7b34a5b.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/4335-50efa61fd7613142.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/4720.ecbd1693fa151068.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/483.725e4a61be922788.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/4969-4b209d7307410c00.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/5068-c7f53862f83c0917.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/5315.bc96711c24dfefe2.js +0 -74
  578. package/apps/pwa/out/_next/static/chunks/5542.efbf14f16baed4dd.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/5622.94893f5aea7185d0.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/5644.8f401a25601d05fc.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/5658.35f70eb9871a54bf.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/5721.7bdc0e904c76ab51.js +0 -8
  583. package/apps/pwa/out/_next/static/chunks/5790-eef0ca20884a6bdf.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/5802.ef0d8598ca68fe84.js +0 -62
  585. package/apps/pwa/out/_next/static/chunks/5890.fb974729788dcb71.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/5924.5ef25cddd36d5dbf.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/5979-e59c099ebf8d1f35.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/6020.714cfeada2315e2e.js +0 -94
  589. package/apps/pwa/out/_next/static/chunks/6027-dfe0349d8c50f698.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/6178.bc4730c86cf74536.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/6273.d808b0dcb3c2b155.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/6292.6ef1df64fa6a4c8a.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/647-af8dd920ba46533c.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/6541-170dc5c59e135c0c.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/6624.981ef7caeb680560.js +0 -32
  596. package/apps/pwa/out/_next/static/chunks/6742-a1ad4a94ba7bc37a.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/6836-493dc55c39187949.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/6856-53ac421c06039a18.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/6908.06008c67f6380ea9.js +0 -148
  600. package/apps/pwa/out/_next/static/chunks/6915.096931864206f522.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/6925.5e181fb7dcf90193.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/6cc1d1d6.9a98dd4622f1afc6.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/7203-800e11037adc41eb.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/7279.6170f591105936bb.js +0 -93
  605. package/apps/pwa/out/_next/static/chunks/7349-373165c7fbdcc3df.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/7514-a533ba0ca1cae4f4.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/7515.1ad6efe157fa4448.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/7521.d919cc1fab80c1d9.js +0 -24
  609. package/apps/pwa/out/_next/static/chunks/7538.90e97bbdf9178cff.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/7825-39203196a12579b2.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/7869.5e1a9a12fd70d6c7.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/8086.1ab9103c41be884f.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/8179-e61fffbdb70f1152.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/8265.bdeb991b34db6a06.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/8357.1aaa713b052a78b1.js +0 -309
  616. package/apps/pwa/out/_next/static/chunks/8638-e42615f2166165b5.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/8639-92c11d069792429e.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/8691.54e22831d5e8ccf8.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/9069.657590a84afb8fac.js +0 -41
  620. package/apps/pwa/out/_next/static/chunks/9125-ae588d88a0293b93.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/9311.f9f641a46a508611.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/9371-a6fee8378ebfa3ee.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/9421-3aa9ff4fa7f63d8a.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/9992.07ce73c10bd5e6a1.js +0 -79
  625. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76394dc618d322c8.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1b948637c37b3abd.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-c63400f1bcf3e47c.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/app/board/page-95d0957267819586.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ba957ff875a026a0.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/app/bots/page-bb6fd0714879cb87.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8f9038520d2acbef.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/app/chat/page-d4b48397e05fce2c.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/app/consult/page-72d5744245dc0c27.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/app/control/page-37625dc8300fa43a.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/app/decisions/page-fbef9d160272dbc1.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/app/design-check/page-58424168df84928e.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/app/editor/page-72c12ba430ac57c0.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/app/exec/page-d53f3e9ed806d7e0.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/app/field/page-4d2fbd45feee0829.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76394dc618d322c8.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/app/inside/page-8783dc0ed7590a2c.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/app/intake/page-3706cb390606e930.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/app/layout-9b0a86598e0111fd.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/app/live/page-4edb8df90f4537ce.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/loops/page-47398b9ba1cc5e1b.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/market/page-fb86f55cfe2e681d.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e22345fc8fd47b1b.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/morning/page-fca2e59a81744d9e.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/not-found-6e96bda63100b31e.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/observatory/page-4d17ad8e0db8cea4.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-121f92766c33beaa.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-43f14212c545f92d.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ccec2023e7a974ec.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/outputs/page-b04f33f548b89a94.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/page-c8792823c0917021.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/reflection/page-8c0e32782ceef4cb.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-94bd3922eb230ce8.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/sessions/page-bca565887bafa03c.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ac1dffafe649fc6c.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/settings/page-52374edb5e23e455.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ad4ebca120d43ca7.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76394dc618d322c8.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/setup/page-718b89ce4b4ae4e4.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/share/page-204eca33eeeaddec.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/showroom/page-e58ba9bcdb9ac0f9.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/tasks/page-98f6dd50bafd7734.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/today/page-5a4bdd72bff27057.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/trace/page-6f6ab17d44f984ce.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/vault/page-aabb7bd15fb72da9.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e4322319164a4436.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/workflows/page-e9efc0c8f32e2371.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/workspace/page-ccde99f07aaf71f7.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-80464aced9614390.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/c696986b.5dc8bb1143af16d1.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/d5d3cefc-373c4eb722ed848b.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/d8de0de2-dd0f4ac8e5ab1832.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/framework-002b472a66935d31.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/main-49b88d47292c0476.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/main-app-fff2549cb10a133f.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/pages/_app-32abbbba3f3b76b7.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/pages/_error-9d9cfef60c60dd7a.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/webpack-cc7f483fa294034a.js +0 -1
  683. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +0 -1
  684. package/scripts/ratchets/next-tui-entry-budget.ts +0 -124
  685. /package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_ssgManifest.js +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  /** Warning-only public-copy shadow scan. It never edits source or exposes private candidate text. */
3
- import { existsSync, readFileSync } from 'node:fs';
4
- import { extname, normalize, relative, resolve, sep } from 'node:path';
3
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
4
+ import { extname, join, normalize, relative, resolve, sep } from 'node:path';
5
5
  import { PatentLedger } from '../src/patents/ledger.js';
6
6
  import { checkBrandText } from './brand/check.js';
7
7
 
@@ -10,7 +10,8 @@ export type PublicCopyFinding = { source: string; line: number; rule: string };
10
10
  export type PatentLineMatcher = (text: string) => number[];
11
11
  export type PublicCopyScan = {
12
12
  findings: PublicCopyFinding[];
13
- excludedSources: Array<{ source: string; reason: 'internal-goal-document' }>;
13
+ excludedSources: Array<{ source: string; reason: 'internal-goal-document' | 'internal-marketing-source' | 'asset-manifest' }>;
14
+ documents: Array<{ source: string; findings: number; companyNameMentions: number; placeholderMentions: number }>;
14
15
  brandRulesAvailable: boolean;
15
16
  patentLedgerAvailable: boolean;
16
17
  };
@@ -23,9 +24,113 @@ function normalizedLabel(label: string): string | undefined {
23
24
  return normalized;
24
25
  }
25
26
 
27
+ function publicCopyExclusion(normalized: string): PublicCopyScan['excludedSources'][number]['reason'] | undefined {
28
+ if (normalized.startsWith('docs/goals/')) return 'internal-goal-document';
29
+ if (!normalized.startsWith('docs/marketing/')) return undefined;
30
+ const relativePath = normalized.slice('docs/marketing/'.length);
31
+ const basename = relativePath.split('/').at(-1) ?? '';
32
+ // These files are published records or release-specific source notes, not
33
+ // current copy candidates. Preserve their exact product names and evidence.
34
+ if (/^(?:BLOG-02-the-agent-drives-the-agent-|BLOG-03-it-drives-the-terminal-itself-|DRAFT-0\.2\.(?:7|8)-announcement-|DRAFT-0\.2\.7-known-issues-public-wording-|DRAFT-0\.2\.10-announcement-|PRFAQ-release-0\.2\.4-|SCRIPT-ax-demo-1008-|VO-teaser-v2-)/i.test(basename)
35
+ || /^docs\/marketing\/launch\/(?:0\.2\.8-loop-final\/|0\.2\.8-rehearsal\/PRFAQ-generated\.md$|0\.2\.11-cut-)/i.test(normalized)) {
36
+ return 'internal-marketing-source';
37
+ }
38
+ if (normalized.startsWith('docs/marketing/') && /^(?:BRIEF-|DOCS-|EVENT-|GUIDE-|MANUAL-|MAP-|MATURITY-|PLAN-|RESEARCH-|RFC-|SCENARIO-|STORYBOARD-|STORYLINE-|STRATEGY-|SOURCES-)/i.test(basename)) {
39
+ return 'internal-marketing-source';
40
+ }
41
+ if (normalized.startsWith('docs/marketing/') && /^DRAFT-persona-/i.test(basename)) return 'internal-marketing-source';
42
+ if (/^docs\/marketing\/teaser-v\d+\/MANIFEST\.md$/i.test(normalized) || normalized === 'docs/marketing/decks/media/README.md') {
43
+ return 'asset-manifest';
44
+ }
45
+ return undefined;
46
+ }
47
+
48
+ function isRequestedPublicCopyPath(normalized: string): boolean {
49
+ return normalized === 'README.md' || normalized.startsWith('docs/marketing/');
50
+ }
51
+
52
+ function maskSpan(text: string, start: number, end: number): string {
53
+ return text.slice(start, end).replace(/[^\r\n]/g, ' ');
54
+ }
55
+
56
+ /** Keep line offsets while limiting brand checks to visible prose, not code, comments, metadata, or link targets. */
57
+ function visibleMarkdownProse(text: string): string {
58
+ let visible = text;
59
+ const masks: RegExp[] = [
60
+ /^---\r?\n[\s\S]*?\r?\n---\s*$/gm,
61
+ /<!--[\s\S]*?-->/g,
62
+ /^\s*(```|~~~)[^\r\n]*\r?\n[\s\S]*?^\s*\1\s*$/gm,
63
+ /`[^`\r\n]*`/g,
64
+ /\]\((?:\\.|[^)])*\)/g,
65
+ /^>\s*🧾\s*\*\*공개 전 할 일\*\*[\s\S]*?(?=^[^>\r\n]|$(?![\s\S]))/gm,
66
+ /^##\s*(?:\d+\.\s*)?(?:칸|출처 목록|sources|evidence|게시 직전 다시 잴 것|기준선|올리기 전 확인)(?:\s|$)[\s\S]*?(?=^##\s|$(?![\s\S]))/gim,
67
+ /^>\s*✏️\s*\*\*착지·실측된 것만\.[^\r\n]*$/gm,
68
+ /https?:\/\/[^\s)>]+/gi,
69
+ /\b(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,}(?:\/[^\s)>]*)?/g,
70
+ ];
71
+ for (const pattern of masks) {
72
+ const matches = [...visible.matchAll(pattern)].map((match) => ({ start: match.index!, end: match.index! + match[0].length }));
73
+ for (const match of matches.reverse()) visible = visible.slice(0, match.start) + maskSpan(visible, match.start, match.end) + visible.slice(match.end);
74
+ }
75
+ return visible;
76
+ }
77
+
78
+ const PUBLIC_COMPANY_NAMES = /\b(?:OpenAI|ChatGPT|Codex|Claude(?:\s+Code)?|Anthropic|Gemini|Google|Grok|xAI|Devin|Cognition|OpenHands|Replit(?:\s+Agent(?:\s+\d+)?)?|Factory(?:\s+AI)?|Hermes|OpenClaw|Manus)\b/gi;
79
+ const PUBLIC_PLACEHOLDER = /\b(?:another coding agent|another agent platform)\b/gi;
80
+ const PUBLIC_PLACEHOLDER_LIST = /\ba coding tool\s*[,·]\s*the coding CLI\s*[,·]\s*an AI model\s*[,·]\s*a model provider\b/gi;
81
+ const UNSUPPORTED_COMPARISON = /\b(?:better|faster|cheaper|safer|worse|superior|inferior)\s+than\b|\b(?:vs\.?|versus|compared\s+(?:with|to))\b|(?:보다\s*(?:낫|빠르|저렴|좋|강력|안전|우수))/i;
82
+
83
+ function companyNameMentions(text: string): number {
84
+ return [...text.matchAll(PUBLIC_COMPANY_NAMES)].length;
85
+ }
86
+
87
+ function placeholderMentions(text: string): number {
88
+ return [...text.matchAll(PUBLIC_PLACEHOLDER)].length + [...text.matchAll(PUBLIC_PLACEHOLDER_LIST)].length;
89
+ }
90
+
91
+ function hasSupportedComparisonEvidence(text: string): boolean {
92
+ const hasSource = /\b(?:source|citation)\b|출처/i.test(text);
93
+ const hasMeasurement = /\b(?:measur|benchmark|tested|test conditions?)\w*\b|실측|측정|측정 조건/i.test(text);
94
+ return hasSource && hasMeasurement;
95
+ }
96
+
97
+ function measuredScopeMatchesClaim(claim: string): boolean {
98
+ const quantity = String.raw`(\d+(?:\.\d+)?)`;
99
+ const unit = String.raw`(jobs?|tasks?|workflows?|items?|건|개|작업)`;
100
+ const claimScope = new RegExp(String.raw`(?:completes?|processes?|handles?|완료|처리)\s*${quantity}\s*${unit}`, 'i').exec(claim);
101
+ const measuredScope = new RegExp(String.raw`(?:measured|measurement|실측|측정).*?(?:scope|range|범위).*?(?:limited to|only|한정|범위 내)\s*${quantity}\s*${unit}`, 'i').exec(claim);
102
+ if (!claimScope || !measuredScope) return false;
103
+ const normalizeUnit = (value: string): string => {
104
+ const normalized = value.toLowerCase();
105
+ if (normalized === 'job' || normalized === 'jobs' || normalized === 'task' || normalized === 'tasks' || normalized === 'workflow' || normalized === 'workflows' || normalized === 'item' || normalized === 'items') return normalized.replace(/s$/, '');
106
+ return normalized;
107
+ };
108
+ return claimScope[1] === measuredScope[1] && normalizeUnit(claimScope[2]!) === normalizeUnit(measuredScope[2]!);
109
+ }
110
+
111
+ function oneClickFindings(text: string, source: string): PublicCopyFinding[] {
112
+ const lines = text.split(/\r?\n/);
113
+ const findings: PublicCopyFinding[] = [];
114
+ for (let index = 0; index < lines.length; index++) {
115
+ const claims = lines[index]!.split(/(?<=[.!?。!?;;])\s*/).filter((segment) => /\bone[ -]click\b|원\s*클릭/i.test(segment));
116
+ for (const claim of claims) {
117
+ const hasLimitedClaimScope = !/(?:every|all|entire|unlimited|모든|전체|무제한)/i.test(claim)
118
+ && /(?:completes?|processes?|handles?|완료|처리)/i.test(claim)
119
+ && /\b\d+(?:\.\d+)?\s+(?:jobs?|tasks?|workflows?|items?)\b|\d+(?:\.\d+)?\s*(?:건|개|작업)/i.test(claim);
120
+ const hasMeasuredScope = hasLimitedClaimScope
121
+ && measuredScopeMatchesClaim(claim)
122
+ && !/(?:no|not|without|unmeasured|측정되지 않|범위 없음|범위가 없|한정되지 않)/i.test(claim);
123
+ if (!hasMeasuredScope) findings.push({ source, line: index + 1, rule: 'ONE-CLICK-MEASURED-SCOPE' });
124
+ const hasPublicationApprovalStatus = /(?:approved for publication|publication approved|publication approval pending|publication not approved|not approved for publication|unapproved|pending|게시\s*승인(?:됨|완료)?|게시\s*미승인|게시\s*승인\s*보류|게시\s*승인\s*상태\s*:\s*(?:승인|미승인|보류)|승인되지 않)/i.test(claim);
125
+ if (!hasPublicationApprovalStatus) findings.push({ source, line: index + 1, rule: 'ONE-CLICK-APPROVAL-STATUS' });
126
+ }
127
+ }
128
+ return findings;
129
+ }
130
+
26
131
  function allowedNormalizedLabel(normalized: string | undefined): boolean {
27
132
  if (!normalized) return false;
28
- if (normalized === 'PR-BODY') return true;
133
+ if (normalized === 'PR-BODY' || normalized === 'README.md') return true;
29
134
  if (normalized.split('/').some((part) => part === '..' || part === '')) return false;
30
135
  if (!['.md', '.mdx', '.txt', '.html', '.htm'].includes(extname(normalized).toLowerCase())) return false;
31
136
  return ['docs/', 'release/public/docs/', 'blog/', 'posts/', 'content/blog/', 'content/posts/']
@@ -36,30 +141,49 @@ function allowedNormalizedLabel(normalized: string | undefined): boolean {
36
141
  export function scanPublicCopy(inputs: readonly PublicCopyInput[], matchPrivateCandidate: PatentLineMatcher, rulesPath?: string): PublicCopyScan {
37
142
  const findings: PublicCopyFinding[] = [];
38
143
  const excludedSources: PublicCopyScan['excludedSources'] = [];
144
+ const documents: PublicCopyScan['documents'] = [];
39
145
  let brandRulesAvailable = true;
40
146
  let patentLedgerAvailable = true;
41
147
  for (const input of inputs) {
42
148
  const label = normalizedLabel(input.label);
43
149
  if (!allowedNormalizedLabel(label)) throw new Error(`공개 문면 입력 경로가 허용 목록 밖입니다: ${input.label}`);
44
- // Goal specs are internal execution artifacts, not customer-facing copy.
45
- // Counting their acceptance criteria as marketing measurements created a
46
- // high false-positive rate in the 30-PR audit.
47
- if (label?.startsWith('docs/goals/')) {
48
- excludedSources.push({ source: input.label, reason: 'internal-goal-document' });
150
+ const excludedReason = label ? publicCopyExclusion(label) : undefined;
151
+ if (excludedReason) {
152
+ excludedSources.push({ source: input.label, reason: excludedReason });
49
153
  continue;
50
154
  }
155
+ const before = findings.length;
156
+ const prose = visibleMarkdownProse(input.text);
157
+ if (label && isRequestedPublicCopyPath(label)) findings.push(...oneClickFindings(prose, input.label));
51
158
  try {
52
- const brandFindings = checkBrandText(input.text, input.label, 'public-docs', rulesPath);
159
+ const brandFindings = checkBrandText(prose, input.label, 'public-docs', rulesPath)
160
+ .filter((hit) => hit.id !== 'BRAND-COMPETITOR-NAME');
53
161
  // Publication-specific promise/measurement rules apply to this explicit
54
162
  // shadow scan without changing the existing generic brand-check scopes.
55
- const publicationFindings = checkBrandText(input.text, input.label, 'release-notes', rulesPath)
163
+ const publicationFindings = checkBrandText(prose, input.label, 'release-notes', rulesPath)
56
164
  .filter((hit) => hit.id === 'BRAND-SOON' || hit.id === 'B4-NUMBER-SOURCE');
57
- for (const hit of [...brandFindings, ...publicationFindings]) {
58
- findings.push({ source: input.label, line: hit.line, rule: hit.id });
165
+ const brandLines = new Set([...brandFindings, ...publicationFindings].map((hit) => `${hit.line}:${hit.id}`));
166
+ for (const line of prose.split(/\r?\n/).map((text, index) => ({ text, line: index + 1 }))) {
167
+ if (UNSUPPORTED_COMPARISON.test(line.text) && PUBLIC_COMPANY_NAMES.test(line.text)
168
+ && !hasSupportedComparisonEvidence(line.text)) {
169
+ brandLines.add(`${line.line}:BRAND-UNSUPPORTED-COMPARISON`);
170
+ }
171
+ PUBLIC_COMPANY_NAMES.lastIndex = 0;
172
+ UNSUPPORTED_COMPARISON.lastIndex = 0;
173
+ if (PUBLIC_PLACEHOLDER.test(line.text) || PUBLIC_PLACEHOLDER_LIST.test(line.text)) {
174
+ brandLines.add(`${line.line}:PUBLIC-COPY-PLACEHOLDER`);
175
+ }
176
+ PUBLIC_PLACEHOLDER.lastIndex = 0;
177
+ PUBLIC_PLACEHOLDER_LIST.lastIndex = 0;
178
+ }
179
+ for (const key of brandLines) {
180
+ const [line, rule] = key.split(':');
181
+ findings.push({ source: input.label, line: Number(line), rule: rule! });
59
182
  }
60
183
  } catch { brandRulesAvailable = false; }
61
184
  try { for (const line of matchPrivateCandidate(input.text)) findings.push({ source: input.label, line, rule: 'PRIVATE-PATENT-CANDIDATE' }); }
62
185
  catch { patentLedgerAvailable = false; }
186
+ documents.push({ source: input.label, findings: findings.length - before, companyNameMentions: companyNameMentions(prose), placeholderMentions: placeholderMentions(prose) });
63
187
  }
64
188
  if (inputs.length === 0 || excludedSources.length === inputs.length) {
65
189
  // No scanner ran, so availability must not claim a successful measurement.
@@ -69,6 +193,7 @@ export function scanPublicCopy(inputs: readonly PublicCopyInput[], matchPrivateC
69
193
  return {
70
194
  findings: findings.sort((a, b) => a.source.localeCompare(b.source) || a.line - b.line || a.rule.localeCompare(b.rule)),
71
195
  excludedSources,
196
+ documents,
72
197
  brandRulesAvailable, patentLedgerAvailable,
73
198
  };
74
199
  }
@@ -77,6 +202,26 @@ export function lintPublicCopy(inputs: readonly PublicCopyInput[], matchPrivateC
77
202
  return scanPublicCopy(inputs, matchPrivateCandidate).findings;
78
203
  }
79
204
 
205
+ function collectPublicCopyFiles(paths: readonly string[], root: string): string[] {
206
+ const collected = new Set<string>();
207
+ const visit = (path: string): void => {
208
+ const absolute = resolve(root, path);
209
+ const stat = statSync(absolute);
210
+ if (stat.isDirectory()) {
211
+ for (const entry of readdirSync(absolute, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
212
+ if (entry.isDirectory()) visit(relative(root, join(absolute, entry.name)));
213
+ else if (entry.isFile() && ['.md', '.mdx', '.txt', '.html', '.htm'].includes(extname(entry.name).toLowerCase())) {
214
+ collected.add(relative(root, join(absolute, entry.name)));
215
+ }
216
+ }
217
+ return;
218
+ }
219
+ collected.add(relative(root, absolute));
220
+ };
221
+ for (const path of paths) visit(path);
222
+ return [...collected].sort();
223
+ }
224
+
80
225
  export function run(argv: readonly string[], root = process.cwd(), ledger = new PatentLedger()): number {
81
226
  const files: string[] = [];
82
227
  let bodyFile: string | undefined;
@@ -97,15 +242,14 @@ export function run(argv: readonly string[], root = process.cwd(), ledger = new
97
242
  } else throw new Error(`알 수 없는 옵션: ${arg}`);
98
243
  }
99
244
  if (!files.length && !bodyFile) throw new Error('usage: bun scripts/public-copy-lint.ts [--files <docs...>] [--pr-body-file <file> [--pr-body-label <repo-relative-label>]] [--json]');
100
- const inputs: PublicCopyInput[] = files.map((file) => ({
101
- label: relative(root, resolve(root, file)).split(sep).join('/'), text: readFileSync(resolve(root, file), 'utf8'),
245
+ const inputs: PublicCopyInput[] = collectPublicCopyFiles(files, root).map((file) => ({
246
+ label: file.split(sep).join('/'), text: readFileSync(resolve(root, file), 'utf8'),
102
247
  }));
103
248
  if (bodyFile) inputs.push({ label: bodyLabel, text: readFileSync(resolve(root, bodyFile), 'utf8') });
104
- const matchPrivateCandidate: PatentLineMatcher = (text) => {
105
- return ledger.unapprovedCandidateLineNumbers(text);
106
- };
249
+ const matchPrivateCandidate: PatentLineMatcher = (text) => ledger.unapprovedCandidateLineNumbers(text);
250
+ // The CLI caller uses the same scope-aware production scanner covered by focused tests.
107
251
  const scan = scanPublicCopy(inputs, matchPrivateCandidate);
108
- if (json) console.log(JSON.stringify({ mode: 'warning-only', ...scan }));
252
+ if (json) console.log(JSON.stringify({ mode: 'warning-only', documentsScanned: scan.documents.length, ...scan }));
109
253
  else {
110
254
  for (const finding of scan.findings) console.log(`${finding.source}:${finding.line}:WARN:${finding.rule}`);
111
255
  for (const excluded of scan.excludedSources) console.log(`${excluded.source}:SKIP:${excluded.reason}`);
@@ -6,7 +6,6 @@ set -u
6
6
 
7
7
  files=(
8
8
  scripts/ratchets/test-interference-patterns.test.ts
9
- scripts/ratchets/next-tui-entry-budget.test.ts
10
9
  src/domains/finance-cli.test.ts
11
10
  src/instance/state-dir-provenance-ratchet.test.ts
12
11
  src/self-implement/graph-yaml.test.ts
@@ -0,0 +1,117 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { acquireLockSync } from '../../src/storage/file-lock.js';
4
+
5
+ /** Deterministic recovery policy for release gate incidents. Side effects are supplied by callers. */
6
+ export type GateAutohealCause = 'shard-pod-death' | 'timeout' | 'host-down' | 'interference' | 'true-regression' | 'environment';
7
+ export type GateAutohealAction = 'reroute-shard' | 'retry-shard' | 'partial-resume' | 'host-switch' | 'hitl';
8
+
9
+ export interface GateAutohealInput {
10
+ cause: GateAutohealCause;
11
+ signature: string;
12
+ shardElapsedMs: number;
13
+ overallElapsedMs: number;
14
+ priorSameSignatureCount?: number;
15
+ shardKeys?: readonly string[];
16
+ }
17
+
18
+ export interface GateAutohealDecision {
19
+ action: GateAutohealAction;
20
+ cause: GateAutohealCause;
21
+ signature: string;
22
+ reason: string;
23
+ shardElapsedMs: number;
24
+ overallElapsedMs: number;
25
+ shardKey?: string;
26
+ }
27
+
28
+ export interface GateResultForAutoheal {
29
+ outcome: 'ok' | 'regression' | 'error';
30
+ introduced?: readonly string[];
31
+ error?: string;
32
+ durationMs: number;
33
+ stalledShards?: readonly { shard: number; files: readonly string[]; reason: string; detail?: string; stall?: { sinceCheckoutMs: number } }[];
34
+ interferenceErrors?: readonly string[];
35
+ loadFlaky?: readonly string[];
36
+ }
37
+
38
+ interface GateAutohealLedger {
39
+ schema: 1;
40
+ overallElapsedMs: number;
41
+ shards: Record<string, number>;
42
+ signatures: Record<string, number>;
43
+ }
44
+
45
+ const emptyLedger = (): GateAutohealLedger => ({ schema: 1, overallElapsedMs: 0, shards: {}, signatures: {} });
46
+
47
+ /** Persist the incident budget across graph-node process restarts, keyed by release version and run id. */
48
+ export function decideAndRecordGateAutoheal(input: GateAutohealInput & { version: string; runId: string; shardKey: string; storeRoot: string }): GateAutohealDecision {
49
+ const path = join(input.storeRoot, 'release', input.version, 'gate-autoheal', `${input.runId.replace(/[^a-zA-Z0-9_.-]/g, '_')}.json`);
50
+ mkdirSync(dirname(path), { recursive: true });
51
+ const unlock = acquireLockSync(`${path}.lock`, { breakDeadHolder: true });
52
+ try {
53
+ let ledger = emptyLedger();
54
+ if (existsSync(path)) {
55
+ try {
56
+ const parsed = JSON.parse(readFileSync(path, 'utf8')) as GateAutohealLedger;
57
+ if (parsed.schema !== 1 || !Number.isFinite(parsed.overallElapsedMs) || !parsed.shards || !parsed.signatures) throw new Error('invalid ledger');
58
+ ledger = parsed;
59
+ } catch {
60
+ return { action: 'hitl', cause: input.cause, signature: input.signature, reason: 'autoheal history unreadable; refusing another automatic action', shardElapsedMs: 0, overallElapsedMs: 0, shardKey: input.shardKey };
61
+ }
62
+ }
63
+ const shardKeys = [...new Set(input.shardKeys?.length ? input.shardKeys : [input.shardKey])];
64
+ const signatureKey = `${input.shardKey}:${input.signature}`;
65
+ const priorSameSignatureCount = ledger.signatures[signatureKey] ?? 0;
66
+ const elapsed = Math.max(0, input.shardElapsedMs);
67
+ const shards = { ...ledger.shards };
68
+ for (const key of shardKeys) shards[key] = (shards[key] ?? 0) + elapsed;
69
+ const shardElapsedMs = Math.max(0, ...shardKeys.map((key) => shards[key] ?? 0));
70
+ const overallElapsedMs = ledger.overallElapsedMs + Math.max(0, input.overallElapsedMs);
71
+ const decision = decideGateAutoheal({ ...input, shardElapsedMs, overallElapsedMs, priorSameSignatureCount });
72
+ ledger = { schema: 1, overallElapsedMs, shards,
73
+ signatures: { ...ledger.signatures, [signatureKey]: priorSameSignatureCount + 1 } };
74
+ const temp = `${path}.${process.pid}.tmp`;
75
+ writeFileSync(temp, `${JSON.stringify(ledger, null, 2)}\n`, { mode: 0o600 });
76
+ renameSync(temp, path);
77
+ return { ...decision, shardKey: input.shardKey };
78
+ } finally { unlock(); }
79
+ }
80
+
81
+ export const GATE_AUTOHEAL_SHARD_CAP_MS = 60 * 60 * 1000;
82
+ export const GATE_AUTOHEAL_OVERALL_CAP_MS = 90 * 60 * 1000;
83
+
84
+ /** One signature may be acted on once; seeing it again, or crossing either timebox, stops in HITL. */
85
+ export function decideGateAutoheal(input: GateAutohealInput): GateAutohealDecision {
86
+ const shardElapsedMs = Math.max(0, input.shardElapsedMs);
87
+ const overallElapsedMs = Math.max(0, input.overallElapsedMs);
88
+ let action: GateAutohealAction;
89
+ let reason: string;
90
+ if (shardElapsedMs >= GATE_AUTOHEAL_SHARD_CAP_MS) {
91
+ action = 'hitl'; reason = 'per-shard timebox exceeded';
92
+ } else if (overallElapsedMs >= GATE_AUTOHEAL_OVERALL_CAP_MS) {
93
+ action = 'hitl'; reason = 'overall timebox exceeded';
94
+ } else if ((input.priorSameSignatureCount ?? 0) >= 1) {
95
+ action = 'hitl'; reason = 'same incident signature repeated';
96
+ } else {
97
+ switch (input.cause) {
98
+ case 'shard-pod-death': action = 'reroute-shard'; reason = 'replace failed shard Pod'; break;
99
+ case 'timeout': action = 'retry-shard'; reason = 'retry the timed-out shard within its budget'; break;
100
+ case 'host-down': action = 'host-switch'; reason = 'move the gate to a healthy host'; break;
101
+ case 'interference': action = 'partial-resume'; reason = 'resume only the unmeasured or affected work'; break;
102
+ case 'true-regression': action = 'hitl'; reason = 'measured regression requires a human decision'; break;
103
+ case 'environment': action = 'hitl'; reason = 'environment cause is not safe to repair automatically'; break;
104
+ }
105
+ }
106
+ return { action, cause: input.cause, signature: input.signature, reason, shardElapsedMs, overallElapsedMs };
107
+ }
108
+
109
+ export function gateAutohealCause(result: GateResultForAutoheal): GateAutohealCause | undefined {
110
+ if (result.outcome === 'ok') return undefined;
111
+ if (result.outcome === 'regression' && (result.introduced?.length ?? 0) > 0) return 'true-regression';
112
+ if (/host.*(?:down|unreachable)|(?:down|unreachable).*host|ssh.*(?:failed|timeout)/i.test(result.error ?? '')) return 'host-down';
113
+ if (result.stalledShards?.some((shard) => /timeout/.test(shard.reason))) return 'timeout';
114
+ if ((result.interferenceErrors?.length ?? 0) > 0 || (result.loadFlaky?.length ?? 0) > 0) return 'interference';
115
+ if (result.stalledShards?.some((shard) => /pod.*(?:deleted|evicted|terminated|lost|not found)|(?:pod-dead|pod-evicted|pod-deleted)/i.test(`${shard.reason} ${shard.detail ?? ''}`))) return 'shard-pod-death';
116
+ return 'environment';
117
+ }
@@ -24,7 +24,7 @@ export function gateAssignableTestsAtCommit(repo: string, commit: string): strin
24
24
  }
25
25
 
26
26
  export interface GateBaselineReceipt {
27
- schema: 1;
27
+ schema: 2;
28
28
  valid: boolean;
29
29
  reason: string;
30
30
  version: string;
@@ -33,8 +33,35 @@ export interface GateBaselineReceipt {
33
33
  recipeSha: string | null;
34
34
  envFingerprint: string;
35
35
  imageDigest: string | null;
36
+ executionIdentity: GatePodRuntimeIdentity | null;
36
37
  files: number;
37
38
  shards: number;
39
+ attributionRecheck?: { summary: number; observed: number; identified: number; baselineResult: string; targetFiles: string[] };
40
+ }
41
+
42
+ export interface GatePodRuntimeIdentity {
43
+ bunVersion: string;
44
+ platform: string;
45
+ architecture: string;
46
+ envFingerprint: string;
47
+ }
48
+
49
+ const validRuntimeIdentity = (value: unknown): value is GatePodRuntimeIdentity => {
50
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
51
+ const row = value as Partial<GatePodRuntimeIdentity>;
52
+ return Object.keys(value).sort().join(',') === 'architecture,bunVersion,envFingerprint,platform'
53
+ && typeof row.bunVersion === 'string' && /^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/.test(row.bunVersion)
54
+ && typeof row.platform === 'string' && /^[a-z0-9_-]{1,32}$/.test(row.platform)
55
+ && typeof row.architecture === 'string' && /^[a-z0-9_-]{1,32}$/.test(row.architecture)
56
+ && typeof row.envFingerprint === 'string' && SHA256.test(row.envFingerprint);
57
+ };
58
+
59
+ /** Parse runtime facts written by the gate Pod before its tests start. */
60
+ export function parseGatePodRuntimeIdentity(text: string): GatePodRuntimeIdentity | null {
61
+ try {
62
+ const value: unknown = JSON.parse(text);
63
+ return validRuntimeIdentity(value) ? value : null;
64
+ } catch { return null; }
38
65
  }
39
66
 
40
67
  /** A direct hard rehearsal has no graph run state. Pin the graph and recipe bytes at its Git SHA. */
@@ -83,11 +110,18 @@ export function gateGraphPins(root: string, runId: string | undefined): { graphS
83
110
  }
84
111
 
85
112
  export function gateBaselineReceipt(input: { root: string; version: string; commit: string; fullSweep: boolean;
86
- graphPins: { graphSha: string; recipeSha: string } | null; envFingerprint: string }): GateBaselineReceipt {
113
+ graphPins: { graphSha: string; recipeSha: string } | null; envFingerprint: string;
114
+ attributionRecheck?: { summary: number; observed: number; identified: number; baselineResult: string; targetFiles: string[] } }): GateBaselineReceipt {
87
115
  const { root, version, commit, graphPins, envFingerprint } = input;
88
- const base = { schema: 1 as const, version, commit, graphSha: graphPins?.graphSha ?? null,
89
- recipeSha: graphPins?.recipeSha ?? null, envFingerprint, imageDigest: null as string | null, files: 0, shards: 0 };
116
+ const base = { schema: 2 as const, version, commit, graphSha: graphPins?.graphSha ?? null,
117
+ recipeSha: graphPins?.recipeSha ?? null, envFingerprint, imageDigest: null as string | null,
118
+ executionIdentity: null as GatePodRuntimeIdentity | null, files: 0, shards: 0 };
90
119
  const invalid = (reason: string): GateBaselineReceipt => ({ ...base, valid: false, reason });
120
+ if (input.attributionRecheck) {
121
+ const evidence = input.attributionRecheck;
122
+ return { ...invalid(`failure attribution unresolved after baseline recheck (${evidence.baselineResult}): summary=${evidence.summary}, observed=${evidence.observed}, identified=${evidence.identified}`),
123
+ attributionRecheck: { ...evidence, targetFiles: [...evidence.targetFiles].sort() } };
124
+ }
91
125
  if (!input.fullSweep) return invalid('gate was not a complete clean full sweep');
92
126
  if (!graphPins) return invalid('pinned graph or recipe SHA absent');
93
127
  if (!SHA256.test(envFingerprint)) return invalid('gate environment fingerprint absent');
@@ -95,12 +129,14 @@ export function gateBaselineReceipt(input: { root: string; version: string; comm
95
129
  const dir = join(root, 'release', version, 'gate-logs', 'cut');
96
130
  if (!existsSync(dir)) return invalid('cut Pod logs absent');
97
131
  const names = readdirSync(dir).filter((name) => /^pod-\d+(?:-[\w-]+)?\.json$/.test(name));
98
- const roots = new Map<number, { files: string[]; shardCount: number; sweepId: string; attemptId: string }>();
99
- const descendants: Array<{ name: string; commit?: string; imageDigest?: string; sweepId?: string; parentAttempt?: string; attemptId?: string }> = [];
132
+ const roots = new Map<number, { files: string[]; shardCount: number; sweepId: string; attemptId: string; runtimeIdentity?: GatePodRuntimeIdentity }>();
133
+ const descendants: Array<{ name: string; commit?: string; imageDigest?: string; sweepId?: string; parentAttempt?: string; attemptId?: string; runtimeIdentity?: GatePodRuntimeIdentity }> = [];
100
134
  const images = new Set<string>();
135
+ const identities = new Map<string, GatePodRuntimeIdentity>();
101
136
  for (const name of names) {
102
137
  const row = JSON.parse(readFileSync(join(dir, name), 'utf8')) as
103
- { commit?: string; files?: unknown; shardCount?: number; sweepId?: string; imageDigest?: string; parentAttempt?: string; attemptId?: string };
138
+ { commit?: string; files?: unknown; shardCount?: number; sweepId?: string; imageDigest?: string; parentAttempt?: string; attemptId?: string; runtimeIdentity?: unknown };
139
+ const runtimeIdentity = validRuntimeIdentity(row.runtimeIdentity) ? row.runtimeIdentity : undefined;
104
140
  const match = /^pod-(\d+)\.json$/.exec(name);
105
141
  if (match) {
106
142
  if (row.commit !== commit) return invalid(`root cut Pod ${name} belongs to another commit`);
@@ -108,12 +144,16 @@ export function gateBaselineReceipt(input: { root: string; version: string; comm
108
144
  return invalid(`root cut Pod ${name} has no file list`);
109
145
  if (!row.attemptId) return invalid(`root cut Pod ${name} has no attempt`);
110
146
  if (!row.sweepId) return invalid(`root cut Pod ${name} has no sweep identity`);
111
- roots.set(Number(match[1]), { files: row.files as string[], shardCount: row.shardCount ?? 0, sweepId: row.sweepId, attemptId: row.attemptId });
147
+ roots.set(Number(match[1]), { files: row.files as string[], shardCount: row.shardCount ?? 0,
148
+ sweepId: row.sweepId, attemptId: row.attemptId, runtimeIdentity });
112
149
  } else descendants.push({ name, commit: row.commit, imageDigest: row.imageDigest, sweepId: row.sweepId,
113
- parentAttempt: row.parentAttempt, attemptId: row.attemptId });
150
+ parentAttempt: row.parentAttempt, attemptId: row.attemptId, runtimeIdentity });
114
151
  if (match) {
115
152
  if (!row.imageDigest || !IMAGE_DIGEST.test(row.imageDigest)) return invalid(`actual Pod image digest absent: ${name}`);
153
+ if (!runtimeIdentity) return invalid(`actual Pod runtime identity absent: ${name}`);
154
+ if (runtimeIdentity.envFingerprint !== envFingerprint) return invalid(`actual Pod environment differs: ${name}`);
116
155
  images.add(row.imageDigest.toLowerCase());
156
+ identities.set(JSON.stringify(runtimeIdentity), runtimeIdentity);
117
157
  }
118
158
  }
119
159
  if (!roots.size) return invalid('root cut shards absent');
@@ -137,7 +177,10 @@ export function gateBaselineReceipt(input: { root: string; version: string; comm
137
177
  if (row.sweepId !== sweepId) return invalid(`cut Pod ${row.name} belongs to another sweep`);
138
178
  if (!row.imageDigest || !IMAGE_DIGEST.test(row.imageDigest)) return invalid(`actual Pod image digest absent: ${row.name}`);
139
179
  if (!row.attemptId) return invalid(`cut Pod ${row.name} has no attempt`);
180
+ if (!row.runtimeIdentity) return invalid(`actual Pod runtime identity absent: ${row.name}`);
181
+ if (row.runtimeIdentity.envFingerprint !== envFingerprint) return invalid(`actual Pod environment differs: ${row.name}`);
140
182
  images.add(row.imageDigest.toLowerCase());
183
+ identities.set(JSON.stringify(row.runtimeIdentity), row.runtimeIdentity);
141
184
  attempts.add(row.attemptId);
142
185
  }
143
186
  pending = next;
@@ -150,6 +193,8 @@ export function gateBaselineReceipt(input: { root: string; version: string; comm
150
193
  if (row.commit === commit && !row.sweepId) return invalid(`cut Pod ${row.name} has no sweep identity`);
151
194
  }
152
195
  if (images.size !== 1) return invalid('cut Pods used different image digests');
153
- return { ...base, valid: true, reason: 'complete clean full sweep', imageDigest: [...images][0]!, files: files.length, shards: count };
196
+ if (identities.size !== 1) return invalid('cut Pods used different runtime identities');
197
+ return { ...base, valid: true, reason: 'complete clean full sweep', imageDigest: [...images][0]!,
198
+ executionIdentity: identities.values().next().value!, files: files.length, shards: count };
154
199
  } catch (error) { return invalid(`cut Pod evidence unreadable: ${String(error)}`); }
155
200
  }
@@ -4,9 +4,28 @@ export interface FailureDiff {
4
4
  common: string[];
5
5
  }
6
6
 
7
+ /** One failure identity may be printed more than once when a host retries or concatenates a test run. */
8
+ export function collapseGateFailureAttempts(ids: readonly string[]): { failures: string[]; observed: number; collapsed: number } {
9
+ const failures = [...new Set(ids)].sort();
10
+ return { failures, observed: ids.length, collapsed: ids.length - failures.length };
11
+ }
12
+
13
+ export class GateFailureAttributionMismatch extends Error {
14
+ constructor(readonly label: string, readonly summary: number, readonly observed: number,
15
+ readonly identified: number, readonly collapsed: number, readonly failures: readonly string[]) {
16
+ super(`${label} incomplete (failure attribution mismatch: summary=${summary}, observed=${observed}, identified=${identified})`);
17
+ this.name = 'GateFailureAttributionMismatch';
18
+ }
19
+ }
20
+
7
21
  /** Bun's file header followed by `(fail) name` lines is the identity source. */
8
22
  export function parseFailures(output: string): string[] {
9
- const failures = new Set<string>();
23
+ return [...new Set(parseFailureAttempts(output))].sort();
24
+ }
25
+
26
+ /** Raw reporter rows, retaining repeated identities so gate attribution can distinguish retries from missing names. */
27
+ export function parseFailureAttempts(output: string): string[] {
28
+ const failures: string[] = [];
10
29
  let file: string | undefined;
11
30
  for (const raw of output.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '').split(/\r?\n/)) {
12
31
  const line = raw.trim();
@@ -14,9 +33,9 @@ export function parseFailures(output: string): string[] {
14
33
  if (header) { file = header[1]; continue; }
15
34
  // Line-start only: a title/body `(fail)` is not a reporter row. Trailing CR is still a real Bun fail row.
16
35
  const failed = /^\(fail\)[ \t]+(.+?)(?:[ \t]+\[[\d.]+(?:ms|s)\])?[ \t]*\r?$/.exec(raw);
17
- if (failed && file) failures.add(`${file} > ${failed[1]!.trim()}`);
36
+ if (failed && file) failures.push(`${file} > ${failed[1]!.trim()}`);
18
37
  }
19
- return [...failures].sort();
38
+ return failures;
20
39
  }
21
40
 
22
41
  /** GATE-INTRO-RECHECK (0.2.19) — the failures that are bun's «timed out» shape: a `(fail) name` line whose next line is
@@ -0,0 +1,81 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+
4
+ /** A gate run may execute only against one immutable image identity and its exact source tag. */
5
+ export interface GateImagePin { runId: string; digest: string; image: string; commit: string }
6
+
7
+ export type GateImagePinResolution =
8
+ | { ok: true; digest: string; eligiblePools: string[] }
9
+ | { ok: false; reason: 'saved-image-digest-invalid' | 'initial-image-digest-invalid' | 'initial-image-digest-missing' | 'pinned-image-unavailable' };
10
+
11
+ export interface ResolveGateImagePinOptions {
12
+ /** Persisted at the run boundary; takes precedence over any newly probed default. */
13
+ savedDigest?: string | null;
14
+ /** Used only to create the pin for a new run. */
15
+ initialDigest?: string | null;
16
+ /** Exact immutable digest selected by the gate image probe in each pool. */
17
+ poolImages: ReadonlyMap<string, readonly string[]>;
18
+ }
19
+
20
+ function normalizeGateImageDigest(value: unknown): string | null {
21
+ if (typeof value !== 'string' || !/^sha256:[a-f0-9]{64}$/i.test(value.trim())) return null;
22
+ return value.trim().toLowerCase();
23
+ }
24
+
25
+ /**
26
+ * Resolve the durable run pin and fail closed unless at least one pool can prove it has that exact image.
27
+ * Callers must filter placement to `eligiblePools`; a mutable tag is never a substitute.
28
+ */
29
+ export function resolveGateImagePin(options: ResolveGateImagePinOptions): GateImagePinResolution {
30
+ const hasSaved = options.savedDigest !== undefined && options.savedDigest !== null;
31
+ const rawDigest = hasSaved ? options.savedDigest : options.initialDigest;
32
+ if (!hasSaved && (rawDigest === undefined || rawDigest === null || rawDigest.trim() === '')) {
33
+ return { ok: false, reason: 'initial-image-digest-missing' };
34
+ }
35
+ const digest = normalizeGateImageDigest(rawDigest);
36
+ if (!digest) return { ok: false, reason: hasSaved ? 'saved-image-digest-invalid' : 'initial-image-digest-invalid' };
37
+ const eligiblePools = [...options.poolImages]
38
+ .filter(([, images]) => images.some((image) => normalizeGateImageDigest(image) === digest))
39
+ .map(([pool]) => pool);
40
+ if (!eligiblePools.length) return { ok: false, reason: 'pinned-image-unavailable' };
41
+ return { ok: true, digest, eligiblePools };
42
+ }
43
+
44
+ /** The pin is immutable for one graph gate run; absence on resume is a hard refusal. */
45
+ export function readGateImagePin(path: string, expectedRunId: string, expectedImage: string, expectedCommit: string): GateImagePin | null {
46
+ if (!existsSync(path)) return null;
47
+ const parsed = JSON.parse(readFileSync(path, 'utf8')) as { version?: unknown; runId?: unknown; digest?: unknown; image?: unknown; commit?: unknown };
48
+ const normalizedCommit = expectedCommit.toLowerCase();
49
+ if (parsed.version !== 2 || parsed.runId !== expectedRunId || parsed.image !== expectedImage || parsed.commit !== normalizedCommit
50
+ || !normalizeGateImageDigest(parsed.digest))
51
+ throw new Error(`invalid gate image pin: ${path}`);
52
+ return { runId: expectedRunId, digest: normalizeGateImageDigest(parsed.digest)!, image: expectedImage, commit: normalizedCommit };
53
+ }
54
+
55
+ export function writeGateImagePin(path: string, runId: string, digest: string, image: string, commit: string): void {
56
+ const normalized = normalizeGateImageDigest(digest);
57
+ if (!runId || !normalized || !image.trim() || !/^[0-9a-f]{40}$/i.test(commit)) throw new Error('invalid gate image pin identity');
58
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
59
+ const value = JSON.stringify({ version: 2, runId, digest: normalized, image, commit: commit.toLowerCase() });
60
+ if (existsSync(path)) {
61
+ const prior = JSON.parse(readFileSync(path, 'utf8')) as { runId?: unknown; digest?: unknown; image?: unknown; commit?: unknown };
62
+ if (prior.runId !== runId || normalizeGateImageDigest(prior.digest) !== normalized || prior.image !== image
63
+ || prior.commit !== commit.toLowerCase())
64
+ throw new Error(`gate image pin is immutable: ${path}`);
65
+ return;
66
+ }
67
+ try { writeFileSync(path, `${value}\n`, { mode: 0o600, flag: 'wx' }); }
68
+ catch (error) {
69
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
70
+ const prior = JSON.parse(readFileSync(path, 'utf8')) as { runId?: unknown; digest?: unknown; image?: unknown; commit?: unknown };
71
+ if (prior.runId === runId && normalizeGateImageDigest(prior.digest) === normalized && prior.image === image
72
+ && prior.commit === commit.toLowerCase()) return;
73
+ throw new Error(`gate image pin is immutable: ${path}`);
74
+ }
75
+ }
76
+
77
+ export function gateImagePinPath(root: string, version: string, runId: string): string {
78
+ if (!/^\d+\.\d+\.\d+(?:-rc\.\d+)?$/.test(version) || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(runId))
79
+ throw new Error('invalid gate image pin path identity');
80
+ return `${root.replace(/\/$/, '')}/release/${version}/gate-image-pin-${runId}.json`;
81
+ }