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
@@ -0,0 +1,156 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ export interface MockScenarioDevice {
5
+ id: string;
6
+ name: string;
7
+ implementation: string[];
8
+ tests: string[];
9
+ target: string;
10
+ inputs: string[];
11
+ observations: string[];
12
+ }
13
+
14
+ export interface MockScenarioEntry {
15
+ id: string;
16
+ name: string;
17
+ kind: string;
18
+ device: string;
19
+ implementation: string[];
20
+ tests: string[];
21
+ trigger: string;
22
+ observe: string[];
23
+ expected: string;
24
+ }
25
+
26
+ export interface MockScenarioCatalog {
27
+ version: 1;
28
+ runEvidence: {
29
+ scope: string;
30
+ runId: string;
31
+ event: { category: string; name: string };
32
+ persistence: string;
33
+ artifact: string;
34
+ };
35
+ devices: MockScenarioDevice[];
36
+ scenarios: MockScenarioEntry[];
37
+ discovery: { trackedRoot: string; candidateRoots: string[]; limits: string };
38
+ }
39
+
40
+ function isRecord(value: unknown): value is Record<string, unknown> {
41
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
42
+ }
43
+
44
+ function requireString(value: unknown, path: string): string {
45
+ if (typeof value !== 'string' || value.trim() === '') throw new Error(`${path} must be a non-empty string`);
46
+ return value;
47
+ }
48
+
49
+ function requireStringArray(value: unknown, path: string, allowEmpty = false): string[] {
50
+ if (!Array.isArray(value) || (!allowEmpty && value.length === 0) || value.some((item) => typeof item !== 'string' || item.trim() === '')) {
51
+ throw new Error(`${path} must be ${allowEmpty ? 'an' : 'a non-empty'} array of non-empty strings`);
52
+ }
53
+ return value as string[];
54
+ }
55
+
56
+ function uniqueIds(rows: unknown[], path: string): Set<string> {
57
+ const ids = new Set<string>();
58
+ rows.forEach((row, index) => {
59
+ if (!isRecord(row)) throw new Error(`${path}[${index}] must be an object`);
60
+ const id = requireString(row.id, `${path}[${index}].id`);
61
+ if (ids.has(id)) throw new Error(`${path} contains duplicate id: ${id}`);
62
+ ids.add(id);
63
+ });
64
+ return ids;
65
+ }
66
+
67
+ /** Validate the checked-in index before CLI output or scenario metadata is consumed. */
68
+ export function parseMockScenarioCatalog(value: unknown): MockScenarioCatalog {
69
+ if (!isRecord(value) || value.version !== 1) throw new Error('mock scenario catalog must declare version: 1');
70
+ if (!Array.isArray(value.devices) || !Array.isArray(value.scenarios)) throw new Error('mock scenario catalog requires devices and scenarios arrays');
71
+ const deviceIds = uniqueIds(value.devices, 'devices');
72
+ uniqueIds(value.scenarios, 'scenarios');
73
+
74
+ const devices = value.devices.map((row, index): MockScenarioDevice => {
75
+ if (!isRecord(row)) throw new Error(`devices[${index}] must be an object`);
76
+ return {
77
+ id: requireString(row.id, `devices[${index}].id`),
78
+ name: requireString(row.name, `devices[${index}].name`),
79
+ implementation: requireStringArray(row.implementation, `devices[${index}].implementation`),
80
+ tests: requireStringArray(row.tests, `devices[${index}].tests`),
81
+ target: requireString(row.target, `devices[${index}].target`),
82
+ inputs: requireStringArray(row.inputs, `devices[${index}].inputs`),
83
+ observations: requireStringArray(row.observations, `devices[${index}].observations`),
84
+ };
85
+ });
86
+ const scenarios = value.scenarios.map((row, index): MockScenarioEntry => {
87
+ if (!isRecord(row)) throw new Error(`scenarios[${index}] must be an object`);
88
+ const device = requireString(row.device, `scenarios[${index}].device`);
89
+ if (!deviceIds.has(device)) throw new Error(`scenarios[${index}] references unknown device: ${device}`);
90
+ return {
91
+ id: requireString(row.id, `scenarios[${index}].id`),
92
+ name: requireString(row.name, `scenarios[${index}].name`),
93
+ kind: requireString(row.kind, `scenarios[${index}].kind`),
94
+ device,
95
+ implementation: requireStringArray(row.implementation, `scenarios[${index}].implementation`),
96
+ tests: requireStringArray(row.tests, `scenarios[${index}].tests`),
97
+ trigger: requireString(row.trigger, `scenarios[${index}].trigger`),
98
+ observe: requireStringArray(row.observe, `scenarios[${index}].observe`),
99
+ expected: requireString(row.expected, `scenarios[${index}].expected`),
100
+ };
101
+ });
102
+ if (!isRecord(value.runEvidence) || !isRecord(value.runEvidence.event)) throw new Error('runEvidence and runEvidence.event are required');
103
+ if (!isRecord(value.discovery)) throw new Error('discovery is required');
104
+ const runEvidence = value.runEvidence;
105
+ const event = runEvidence.event;
106
+ const discovery = value.discovery;
107
+ return {
108
+ version: 1,
109
+ devices,
110
+ scenarios,
111
+ runEvidence: {
112
+ scope: requireString(runEvidence.scope, 'runEvidence.scope'),
113
+ runId: requireString(runEvidence.runId, 'runEvidence.runId'),
114
+ event: { category: requireString(event.category, 'runEvidence.event.category'), name: requireString(event.name, 'runEvidence.event.name') },
115
+ persistence: requireString(runEvidence.persistence, 'runEvidence.persistence'),
116
+ artifact: requireString(runEvidence.artifact, 'runEvidence.artifact'),
117
+ },
118
+ discovery: {
119
+ trackedRoot: requireString(discovery.trackedRoot, 'discovery.trackedRoot'),
120
+ candidateRoots: requireStringArray(discovery.candidateRoots, 'discovery.candidateRoots'),
121
+ limits: requireString(discovery.limits, 'discovery.limits'),
122
+ },
123
+ };
124
+ }
125
+
126
+ export function loadMockScenarioCatalog(): MockScenarioCatalog {
127
+ const path = fileURLToPath(new URL('./mock-scenario-catalog.json', import.meta.url));
128
+ return parseMockScenarioCatalog(JSON.parse(readFileSync(path, 'utf8')) as unknown);
129
+ }
130
+
131
+ export function renderMockScenarioList(catalog: MockScenarioCatalog): string[] {
132
+ return [
133
+ 'Mock scenario catalog · reusable devices and scenario references',
134
+ ...catalog.scenarios.map((scenario) => `${scenario.id}\t${scenario.kind}\t${scenario.name}`),
135
+ `장치 ${catalog.devices.length} · 시나리오 ${catalog.scenarios.length} · 자세히: bun scripts/ux-sim.ts show <MS-id>`,
136
+ ];
137
+ }
138
+
139
+ export function renderMockScenarioDetails(catalog: MockScenarioCatalog, id: string): string[] {
140
+ const scenario = catalog.scenarios.find((entry) => entry.id === id);
141
+ if (!scenario) throw new Error(`unknown mock scenario id: ${id}`);
142
+ const device = catalog.devices.find((entry) => entry.id === scenario.device)!;
143
+ return [
144
+ `${scenario.id} · ${scenario.name}`,
145
+ `유형: ${scenario.kind}`,
146
+ `장치: ${device.name} (${device.id})`,
147
+ `대상: ${device.target}`,
148
+ `입력: ${device.inputs.join(' · ')}`,
149
+ `주입: ${scenario.trigger}`,
150
+ `관측: ${scenario.observe.join(' · ')}`,
151
+ `기대: ${scenario.expected}`,
152
+ `구현: ${scenario.implementation.join(' · ')}`,
153
+ `시험: ${scenario.tests.join(' · ')}`,
154
+ `ScenarioV0 실행 증거: ${catalog.runEvidence.scope} · runId → ${catalog.runEvidence.event.category}/${catalog.runEvidence.event.name} → test logs.db · ${catalog.runEvidence.artifact}`,
155
+ ];
156
+ }
@@ -0,0 +1,262 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ import { getTestStateRoot } from '../nexus/paths.js';
5
+ import { listGraphRuns, runGraph, type GraphRunState } from '../graph-runner/runner.js';
6
+ import { resolveScenarioFilePath } from './scenario.js';
7
+
8
+ const REPOSITORY_ROOT = join(import.meta.dir, '../..');
9
+ const RELEASE_GRAPH = join(REPOSITORY_ROOT, 'graphs/release/release-loop.yaml');
10
+ const CUT_TIP = 'a'.repeat(40);
11
+
12
+ export interface ReleaseSimRigCase {
13
+ id: string;
14
+ mode?: 'baseline' | 'cut-tip-moved-after-resume';
15
+ fault?: { node: string; outcome: 'fail' | 'error' | 'timeout'; evidence?: Record<string, unknown> };
16
+ expect: { status: 'done' | 'failed' | 'resume-refused'; mustVisit: string[]; mustNotVisit: string[] };
17
+ }
18
+
19
+ export interface ReleaseSimRigScenario {
20
+ version: 1;
21
+ kind: 'release-graph';
22
+ id: string;
23
+ isolation: 'test';
24
+ input: { version: string; previousVersion: string };
25
+ cases: ReleaseSimRigCase[];
26
+ }
27
+
28
+ export interface ReleaseSimRigCaseResult {
29
+ id: string;
30
+ pass: boolean;
31
+ status: string;
32
+ path: string[];
33
+ dispatchedNodes: string[];
34
+ unexpectedEdges: Array<{ from: string; to: string; outcome: string; expectedTo?: string }>;
35
+ checks: { status: boolean; mustVisit: boolean; mustNotVisit: boolean; ledgerEvents: boolean };
36
+ injectedEvidence: Record<string, unknown>;
37
+ ledgerEvents: Array<Record<string, unknown>>;
38
+ statePath: string;
39
+ refusal?: string;
40
+ }
41
+
42
+ function unexpectedEdges(path: string[], state: GraphRunState, edges: Map<string, Record<string, unknown>>): ReleaseSimRigCaseResult['unexpectedEdges'] {
43
+ const nodes = new Map(state.nodes.map((node) => [node.nodeId, node]));
44
+ const issues: ReleaseSimRigCaseResult['unexpectedEdges'] = [];
45
+ for (let index = 0; index + 1 < path.length; index++) {
46
+ const from = path[index]!;
47
+ const to = path[index + 1]!;
48
+ const edge = edges.get(from);
49
+ const output = nodes.get(from)?.output;
50
+ let parsed: Record<string, unknown> = {};
51
+ if (typeof output === 'string') {
52
+ try { parsed = record(JSON.parse(output.trim().split('\n').at(-1) ?? ''), `${from} output`); } catch { /* use runner fallback below */ }
53
+ } else if (output && typeof output === 'object' && !Array.isArray(output)) parsed = output as Record<string, unknown>;
54
+ const named = typeof parsed.outcome === 'string' ? parsed.outcome : nodes.get(from)?.ok ? 'ok' : 'fail';
55
+ const map = edge?.map && typeof edge.map === 'object' ? edge.map as Record<string, unknown> : {};
56
+ const expectedTo = typeof map[named] === 'string' ? map[named] as string : typeof map[named === 'ok' ? 'ok' : 'fail'] === 'string'
57
+ ? map[named === 'ok' ? 'ok' : 'fail'] as string : undefined;
58
+ if (expectedTo !== to) issues.push({ from, to, outcome: named, ...(expectedTo ? { expectedTo } : {}) });
59
+ }
60
+ return issues;
61
+ }
62
+
63
+ function recordObservedEdges(path: string[], events: Array<Record<string, unknown>>): void {
64
+ for (let index = 0; index + 1 < path.length; index++) {
65
+ events.push({ event: 'edge-observed', from: path[index], to: path[index + 1], source: 'release-run-path' });
66
+ }
67
+ }
68
+
69
+ export interface ReleaseSimRigResult {
70
+ id: string;
71
+ pass: boolean;
72
+ universe: string;
73
+ cases: ReleaseSimRigCaseResult[];
74
+ table: Array<{ scenario: string; path: string; status: string; unexpectedEdges: string }>;
75
+ artifactPath: string;
76
+ }
77
+
78
+ function record(value: unknown, where: string): Record<string, unknown> {
79
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`${where} must be an object`);
80
+ return value as Record<string, unknown>;
81
+ }
82
+
83
+ function exactKeys(value: Record<string, unknown>, allowed: readonly string[], where: string): void {
84
+ const extra = Object.keys(value).filter((key) => !allowed.includes(key));
85
+ if (extra.length) throw new Error(`${where} has unsupported key(s): ${extra.join(', ')}`);
86
+ }
87
+
88
+ function stringList(value: unknown, where: string): string[] {
89
+ if (!Array.isArray(value) || value.some((item) => typeof item !== 'string' || item.trim() === '')) {
90
+ throw new Error(`${where} must be an array of non-empty strings`);
91
+ }
92
+ return value as string[];
93
+ }
94
+
95
+ function releaseGraphShape(): { nodeIds: Set<string>; edges: Map<string, Record<string, unknown>> } {
96
+ const graph = record(parseYaml(readFileSync(RELEASE_GRAPH, 'utf8')), 'release graph');
97
+ if (!Array.isArray(graph.nodes) || !Array.isArray(graph.edges)) throw new Error('release graph nodes or edges missing');
98
+ const nodeIds = new Set(graph.nodes.map((value) => record(value, 'release graph node').node_id)
99
+ .filter((id): id is string => typeof id === 'string'));
100
+ const edges = new Map<string, Record<string, unknown>>();
101
+ for (const raw of graph.edges) {
102
+ const edge = record(raw, 'release edge');
103
+ if (typeof edge.from !== 'string' || edge.on !== 'outcome' || !edge.map || typeof edge.map !== 'object') {
104
+ throw new Error('release simulation requires outcome edges with explicit maps');
105
+ }
106
+ if (edges.has(edge.from)) throw new Error(`release graph has multiple outcome edges from ${edge.from}`);
107
+ edges.set(edge.from, edge);
108
+ }
109
+ return { nodeIds, edges };
110
+ }
111
+
112
+ export function parseReleaseSimRigScenario(source: string): ReleaseSimRigScenario {
113
+ const root = record(parseYaml(source), 'release simulation scenario');
114
+ exactKeys(root, ['version', 'kind', 'id', 'isolation', 'input', 'cases'], 'release simulation scenario');
115
+ if (root.version !== 1 || root.kind !== 'release-graph' || root.isolation !== 'test') {
116
+ throw new Error('release simulation scenario requires version: 1, kind: release-graph, isolation: test');
117
+ }
118
+ if (typeof root.id !== 'string' || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(root.id)) throw new Error('scenario.id must be lowercase letters, digits, and hyphens');
119
+ const input = record(root.input, 'scenario.input');
120
+ exactKeys(input, ['version', 'previousVersion'], 'scenario.input');
121
+ if (typeof input.version !== 'string' || !/^\d+\.\d+\.\d+$/.test(input.version)
122
+ || typeof input.previousVersion !== 'string' || !/^\d+\.\d+\.\d+$/.test(input.previousVersion)) {
123
+ throw new Error('scenario.input requires release version and previousVersion');
124
+ }
125
+ const rawCases = root.cases;
126
+ if (!Array.isArray(rawCases) || rawCases.length === 0 || rawCases.length > 32) throw new Error('scenario.cases must contain 1–32 cases');
127
+ const { nodeIds } = releaseGraphShape();
128
+ const ids = new Set<string>();
129
+ const cases = rawCases.map((raw, index): ReleaseSimRigCase => {
130
+ const row = record(raw, `scenario.cases[${index}]`);
131
+ exactKeys(row, ['id', 'mode', 'fault', 'expect'], `scenario.cases[${index}]`);
132
+ if (typeof row.id !== 'string' || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(row.id) || ids.has(row.id)) throw new Error(`scenario.cases[${index}].id must be unique lowercase text`);
133
+ ids.add(row.id);
134
+ let mode: ReleaseSimRigCase['mode'];
135
+ if (row.mode !== undefined) {
136
+ if (row.mode !== 'baseline' && row.mode !== 'cut-tip-moved-after-resume') throw new Error(`scenario.cases[${index}].mode is unregistered`);
137
+ mode = row.mode;
138
+ }
139
+ let fault: ReleaseSimRigCase['fault'];
140
+ if (row.fault !== undefined) {
141
+ const parsed = record(row.fault, `scenario.cases[${index}].fault`);
142
+ exactKeys(parsed, ['node', 'outcome', 'evidence'], `scenario.cases[${index}].fault`);
143
+ if (typeof parsed.node !== 'string' || !nodeIds.has(parsed.node)) throw new Error(`scenario.cases[${index}].fault.node is not in the release graph`);
144
+ if (parsed.outcome !== 'fail' && parsed.outcome !== 'error' && parsed.outcome !== 'timeout') throw new Error(`scenario.cases[${index}].fault.outcome is unregistered`);
145
+ fault = { node: parsed.node, outcome: parsed.outcome, ...(parsed.evidence === undefined ? {} : { evidence: record(parsed.evidence, `scenario.cases[${index}].fault.evidence`) }) };
146
+ }
147
+ if ((mode === undefined) === (fault === undefined)) throw new Error(`scenario.cases[${index}] requires exactly one of mode or fault`);
148
+ const expected = record(row.expect, `scenario.cases[${index}].expect`);
149
+ exactKeys(expected, ['status', 'mustVisit', 'mustNotVisit'], `scenario.cases[${index}].expect`);
150
+ if (expected.status !== 'done' && expected.status !== 'failed' && expected.status !== 'resume-refused') throw new Error(`scenario.cases[${index}].expect.status is invalid`);
151
+ const mustVisit = stringList(expected.mustVisit, `scenario.cases[${index}].expect.mustVisit`);
152
+ const mustNotVisit = stringList(expected.mustNotVisit, `scenario.cases[${index}].expect.mustNotVisit`);
153
+ for (const id of [...mustVisit, ...mustNotVisit]) if (!nodeIds.has(id)) throw new Error(`scenario.cases[${index}] names unknown release node ${id}`);
154
+ if (mustVisit.some((id) => mustNotVisit.includes(id))) throw new Error(`scenario.cases[${index}] visits and excludes the same node`);
155
+ return { id: row.id, ...(mode ? { mode } : {}), ...(fault ? { fault } : {}), expect: { status: expected.status, mustVisit, mustNotVisit } };
156
+ });
157
+ return { version: 1, kind: 'release-graph', id: root.id, isolation: 'test',
158
+ input: { version: input.version, previousVersion: input.previousVersion }, cases };
159
+ }
160
+
161
+ function parseContext(env: NodeJS.ProcessEnv): { nodeId: string } {
162
+ const location = env.ELANOUS_GRAPH_CONTEXT;
163
+ if (!location) throw new Error('ELANOUS_GRAPH_CONTEXT missing in release simulation');
164
+ const context = record(JSON.parse(readFileSync(location, 'utf8')), 'release graph context');
165
+ if (typeof context.nodeId !== 'string') throw new Error('release graph context nodeId missing');
166
+ return { nodeId: context.nodeId };
167
+ }
168
+
169
+ function resultForNode(nodeId: string, fault: ReleaseSimRigCase['fault'], version: string): { exitCode: number; stdout: string; stderr: string; evidence: Record<string, unknown> } {
170
+ const triggeredFault = fault?.node === nodeId ? fault : undefined;
171
+ const outcome = triggeredFault ? triggeredFault.outcome === 'timeout' ? 'error' : triggeredFault.outcome : 'ok';
172
+ const evidence = triggeredFault?.evidence ?? {};
173
+ const commit = nodeId === 'version-release' ? { commit: CUT_TIP, tag: `v${version}` } : {};
174
+ const payload = { outcome, verdict: outcome === 'ok' ? 'pass' : 'fail', summary: triggeredFault ? `simulated ${triggeredFault.outcome}` : `simulated ${nodeId} success`, ...commit,
175
+ simulation: { injected: triggeredFault !== undefined, node: nodeId, evidence } };
176
+ return { exitCode: !triggeredFault ? 0 : triggeredFault.outcome === 'fail' ? 1 : triggeredFault.outcome === 'timeout' ? 124 : 2,
177
+ stdout: `${JSON.stringify(payload)}\n`, stderr: triggeredFault ? `SIMULATED_${triggeredFault.outcome.toUpperCase()}` : '', evidence };
178
+ }
179
+
180
+ async function runCase(scenario: ReleaseSimRigScenario, item: ReleaseSimRigCase, root: string,
181
+ edges: Map<string, Record<string, unknown>>): Promise<ReleaseSimRigCaseResult> {
182
+ const runId = `release-sim-${scenario.id}-${item.id}`;
183
+ const ledgerEvents: Array<Record<string, unknown>> = [];
184
+ const dispatchedNodes: string[] = [];
185
+ const runBash = async (command: string, options: { env?: NodeJS.ProcessEnv }) => {
186
+ const { nodeId } = parseContext(options.env ?? {});
187
+ if (!command.trim()) throw new Error(`empty command at simulated node ${nodeId}`);
188
+ dispatchedNodes.push(nodeId);
189
+ const result = resultForNode(nodeId, item.fault, scenario.input.version);
190
+ return { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr };
191
+ };
192
+ const capture = (event: string, data: Record<string, unknown>) => ledgerEvents.push({ event, ...data });
193
+
194
+ if (item.mode === 'cut-tip-moved-after-resume') {
195
+ let beforeNode = 0;
196
+ const simulatedStop = new Error('simulated runner stop after cut');
197
+ try {
198
+ await runGraph(RELEASE_GRAPH, { runId, input: scenario.input, pinChildUniverse: true, quiet: true,
199
+ deps: { root, runBash, log: capture, beforeNode: () => { if (++beforeNode === 2) throw simulatedStop; } } });
200
+ } catch (error) {
201
+ if (error !== simulatedStop) throw error;
202
+ }
203
+ let refusal = '';
204
+ try {
205
+ await runGraph(RELEASE_GRAPH, { resumeRunId: runId, pinChildUniverse: true, quiet: true,
206
+ releaseCommandSource: { root: REPOSITORY_ROOT, cutTip: 'b'.repeat(40), git: () => { throw new Error('git must not be consulted after the saved tip differs'); } },
207
+ deps: { root, runBash, log: capture } });
208
+ } catch (error) { refusal = error instanceof Error ? error.message : String(error); }
209
+ const interrupted = listGraphRuns(root).runs.find((run) => run.runId === runId);
210
+ const stateFile = interrupted?.statePath ?? '';
211
+ const path = interrupted?.path ?? [];
212
+ recordObservedEdges(path, ledgerEvents);
213
+ const edgeIssues = interrupted ? unexpectedEdges(path, interrupted, edges) : [];
214
+ const statusOk = item.expect.status === 'resume-refused' && refusal.includes('release command source differs from the saved cut tip');
215
+ const visitOk = item.expect.mustVisit.every((id) => path.includes(id));
216
+ const notVisitOk = item.expect.mustNotVisit.every((id) => !path.includes(id));
217
+ const ledgerOk = ledgerEvents.some((row) => row.event === 'node-end')
218
+ && ledgerEvents.filter((row) => row.event === 'edge-observed').length === Math.max(0, path.length - 1);
219
+ return { id: item.id, pass: statusOk && visitOk && notVisitOk && ledgerOk && edgeIssues.length === 0, status: statusOk ? 'resume-refused' : interrupted?.status ?? 'missing',
220
+ path, dispatchedNodes, unexpectedEdges: edgeIssues, checks: { status: statusOk, mustVisit: visitOk, mustNotVisit: notVisitOk, ledgerEvents: ledgerOk }, injectedEvidence: {}, ledgerEvents,
221
+ statePath: stateFile, ...(refusal ? { refusal } : {}) };
222
+ }
223
+
224
+ // Initial runs have no releaseCommandSource (the runner only accepts it on resume); all node shell work is intercepted here.
225
+ const state = await runGraph(RELEASE_GRAPH, { runId, input: scenario.input, pinChildUniverse: true, quiet: true,
226
+ deps: { root, runBash, log: capture } });
227
+ const path = state.path;
228
+ recordObservedEdges(path, ledgerEvents);
229
+ const edgeIssues = unexpectedEdges(path, state, edges);
230
+ const statusOk = state.status === item.expect.status;
231
+ const visitOk = item.expect.mustVisit.every((id) => path.includes(id));
232
+ const notVisitOk = item.expect.mustNotVisit.every((id) => !path.includes(id));
233
+ const ledgerOk = ledgerEvents.some((row) => row.event === 'node-start') && ledgerEvents.some((row) => row.event === 'node-end')
234
+ && ledgerEvents.filter((row) => row.event === 'edge-observed').length === Math.max(0, path.length - 1);
235
+ const injectedEvidence = item.fault ? item.fault.evidence ?? {} : {};
236
+ return { id: item.id, pass: statusOk && visitOk && notVisitOk && ledgerOk && edgeIssues.length === 0, status: state.status, path, dispatchedNodes, unexpectedEdges: edgeIssues,
237
+ checks: { status: statusOk, mustVisit: visitOk, mustNotVisit: notVisitOk, ledgerEvents: ledgerOk }, injectedEvidence,
238
+ ledgerEvents, statePath: state.statePath };
239
+ }
240
+
241
+ export async function runReleaseSimRigScenario(source: string): Promise<ReleaseSimRigResult> {
242
+ const scenario = parseReleaseSimRigScenario(source);
243
+ const root = getTestStateRoot();
244
+ if (!root) throw new Error('release simulation rig requires --test isolated mode');
245
+ const { edges } = releaseGraphShape();
246
+ const cases = [];
247
+ for (const item of scenario.cases) cases.push(await runCase(scenario, item, root, edges));
248
+ const result: ReleaseSimRigResult = { id: scenario.id, pass: cases.every((item) => item.pass), universe: root, cases,
249
+ table: cases.map((item) => ({ scenario: item.id, path: item.path.join(' → '), status: item.status,
250
+ unexpectedEdges: item.unexpectedEdges.length ? JSON.stringify(item.unexpectedEdges) : 'none' })),
251
+ artifactPath: join(root, 'ux-sim', 'evidence', `${scenario.id}.json`) };
252
+ mkdirSync(join(root, 'ux-sim', 'evidence'), { recursive: true });
253
+ writeFileSync(result.artifactPath, `${JSON.stringify(result, null, 2)}\n`, { mode: 0o600 });
254
+ return result;
255
+ }
256
+
257
+ export async function runReleaseSimRigIfFile(filePath: string): Promise<ReleaseSimRigResult | null> {
258
+ const source = readFileSync(resolveScenarioFilePath(filePath), 'utf8');
259
+ if (record(parseYaml(source), 'scenario').kind !== 'release-graph') return null;
260
+ if (!getTestStateRoot()) throw new Error('release simulation rig requires --test isolated mode');
261
+ return runReleaseSimRigScenario(source);
262
+ }
@@ -1,10 +1,10 @@
1
- import { closeSync, constants, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, realpathSync, renameSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
1
+ import { closeSync, constants, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
3
2
  import { extname, join, relative, resolve, sep } from 'node:path';
4
3
  import { fileURLToPath } from 'node:url';
5
4
  import { createHash, randomUUID } from 'node:crypto';
6
5
  import { parse as parseYaml } from 'yaml';
7
6
  import { debug } from '../debug/log.js';
7
+ import type { LogSink } from '../mss/logging/sink.js';
8
8
  import { streamCodexResponsesEvents } from '../llm.js';
9
9
  import { LogStore, StoreSink, type LogStoreRow } from '../mss/logging/log-store.js';
10
10
  import { getTestStateRoot } from '../nexus/paths.js';
@@ -24,8 +24,10 @@ export interface ScenarioV0 {
24
24
 
25
25
  export interface ScenarioResult {
26
26
  id: string;
27
+ runId: string;
27
28
  seed: number;
28
29
  evidenceArtifact: string;
30
+ runEvidence: { category: string; event: string; persisted: boolean };
29
31
  pass: boolean;
30
32
  trigger: { id: string; type: string; status: number | null; expectedCount: number; fetchCount: number; error: string | null };
31
33
  screen: { pass: boolean; containsMissing: string[]; excludesFound: string[]; snapshot: string };
@@ -35,6 +37,7 @@ export interface ScenarioResult {
35
37
 
36
38
  let previousScenarioTurn: Promise<void> = Promise.resolve();
37
39
  const REPOSITORY_ROOT = fileURLToPath(new URL('../..', import.meta.url));
40
+ const SENSITIVE_EXPECTATION_KEY = /(?:token|secret|password|prompt|authorization|credential|api.?key|request.?body)/i;
38
41
 
39
42
  function record(value: unknown, where: string): Record<string, unknown> {
40
43
  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`${where} must be an object`);
@@ -46,6 +49,21 @@ function exactKeys(value: Record<string, unknown>, allowed: readonly string[], w
46
49
  if (extra.length) throw new Error(`${where} has unsupported key(s): ${extra.join(', ')}`);
47
50
  }
48
51
 
52
+ function validateExpectationData(value: Record<string, unknown>, where = 'scenario.expect.logs[0].data'): void {
53
+ for (const [key, entry] of Object.entries(value)) {
54
+ if (SENSITIVE_EXPECTATION_KEY.test(key)) throw new Error(`${where} cannot include sensitive field: ${key}`);
55
+ if (entry && typeof entry === 'object' && !Array.isArray(entry)) {
56
+ validateExpectationData(entry as Record<string, unknown>, `${where}.${key}`);
57
+ } else if (Array.isArray(entry)) {
58
+ entry.forEach((item, index) => {
59
+ if (item && typeof item === 'object' && !Array.isArray(item)) {
60
+ validateExpectationData(item as Record<string, unknown>, `${where}.${key}[${index}]`);
61
+ }
62
+ });
63
+ }
64
+ }
65
+ }
66
+
49
67
  function requiredString(value: unknown, where: string): string {
50
68
  if (typeof value !== 'string' || value.trim() === '') throw new Error(`${where} must be a non-empty string`);
51
69
  return value;
@@ -88,10 +106,19 @@ export function parseScenarioV0(source: string): ScenarioV0 {
88
106
  exactKeys(expectedLog, ['category', 'event', 'level', 'count', 'data'], 'scenario.expect.logs[0]');
89
107
  const category = requiredString(expectedLog.category, 'scenario.expect.logs[0].category');
90
108
  const event = requiredString(expectedLog.event, 'scenario.expect.logs[0].event');
109
+ if (category === 'ux.sim.scenario' && event === 'completed') {
110
+ throw new Error('scenario.expect.logs cannot use reserved event ux.sim.scenario/completed');
111
+ }
91
112
  const level = requiredString(expectedLog.level, 'scenario.expect.logs[0].level');
92
113
  if (expectedLog.count !== undefined && (!Number.isInteger(expectedLog.count) || (expectedLog.count as number) < 0)) throw new Error('scenario.expect.logs[0].count must be a non-negative integer');
93
114
  if (expectedLog.count === 0 && trigger.type !== 'codex.responses.fetch-network-error') throw new Error('zero expected log events are allowed only for codex.responses.fetch-network-error');
94
- if (expectedLog.data !== undefined) record(expectedLog.data, 'scenario.expect.logs[0].data');
115
+ if (expectedLog.data !== undefined) {
116
+ const data = record(expectedLog.data, 'scenario.expect.logs[0].data');
117
+ validateExpectationData(data);
118
+ if (category === 'llm.codex' && event === 'request-rejected-shape' && 'status' in data && typeof data.status !== 'number') {
119
+ throw new Error('scenario.expect.logs[0].data.status must be numeric');
120
+ }
121
+ }
95
122
 
96
123
  return {
97
124
  version: 0,
@@ -191,9 +218,53 @@ export async function runScenarioV0(scenario: ScenarioV0): Promise<ScenarioResul
191
218
  }
192
219
 
193
220
  async function runScenarioV0Exclusive(scenario: ScenarioV0): Promise<ScenarioResult> {
194
- const root = mkdtempSync(join(tmpdir(), `ux-sim-${scenario.id}-`));
195
- const store = new LogStore(join(root, 'logs.db'), { instance: `ux-sim:${scenario.id}` });
196
- const sink = new StoreSink(store, `ux-sim:${scenario.id}`, { installExitHandlers: false });
221
+ const stateRoot = getTestStateRoot()!;
222
+ assertNoSymlinkComponents(stateRoot);
223
+ mkdirSync(stateRoot, { recursive: true });
224
+ if (!lstatSync(stateRoot).isDirectory()) throw new Error('scenario test-state root must be a directory');
225
+ const logsDir = join(stateRoot, 'logs');
226
+ assertNoSymlinkComponents(logsDir);
227
+ mkdirSync(logsDir, { recursive: true });
228
+ assertNoSymlinkComponents(logsDir);
229
+ if (!lstatSync(logsDir).isDirectory()) throw new Error('scenario logs path must be a directory');
230
+ const logsPath = join(logsDir, 'logs.db');
231
+ assertNoSymlinkComponents(logsPath);
232
+ try {
233
+ const logsStat = lstatSync(logsPath);
234
+ if (!logsStat.isFile()) throw new Error('scenario logs path must be a regular file');
235
+ if (logsStat.nlink > 1) throw new Error('scenario logs path must not be a hardlink');
236
+ } catch (error) {
237
+ if (error instanceof Error && error.message === 'scenario logs path must be a regular file') throw error;
238
+ if (error instanceof Error && error.message === 'scenario logs path must not be a hardlink') throw error;
239
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
240
+ }
241
+ const runId = randomUUID();
242
+ const instance = `ux-sim:${scenario.id}:${runId}`;
243
+ const store = new LogStore(logsPath, { instance });
244
+ const storeSink = new StoreSink(store, instance, { installExitHandlers: false });
245
+ const expectedLog = scenario.expect.logs[0];
246
+ const sink: LogSink = {
247
+ name: storeSink.name,
248
+ emit(rec) {
249
+ if (rec.category === 'ux.sim.scenario' && rec.event === 'completed') {
250
+ const data = rec.data && typeof rec.data === 'object' ? rec.data as Record<string, unknown> : {};
251
+ storeSink.emit({
252
+ ts: rec.ts,
253
+ category: rec.category,
254
+ event: rec.event,
255
+ data: { runId, scenarioId: scenario.id, deviceVerdict: data.deviceVerdict },
256
+ });
257
+ } else if (rec.category === expectedLog?.category && rec.event === expectedLog.event) {
258
+ storeSink.emit({
259
+ ts: rec.ts,
260
+ category: rec.category,
261
+ event: rec.event,
262
+ level: rec.level,
263
+ ...(expectedLog.data ? { data: projectSubset(rec.data, expectedLog.data) } : {}),
264
+ });
265
+ }
266
+ },
267
+ };
197
268
  const off = debug.registerSink(sink);
198
269
  const originalFetch = globalThis.fetch;
199
270
  const trigger = scenario.triggers[0];
@@ -222,9 +293,9 @@ async function runScenarioV0Exclusive(scenario: ScenarioV0): Promise<ScenarioRes
222
293
  const containsMissing = scenario.expect.screen.snapshot.contains.filter((fragment) => !screenText.includes(fragment));
223
294
  const excludesFound = (scenario.expect.screen.snapshot.excludes ?? []).filter((fragment) => screenText.includes(fragment));
224
295
 
225
- sink.flush();
296
+ storeSink.flush();
226
297
  const expected = scenario.expect.logs[0];
227
- const rows = store.query({ exactCategories: [expected.category], events: [expected.event], limit: 1000 });
298
+ const rows = store.query({ exactCategories: [expected.category], events: [expected.event], instances: [instance], limit: 1000 });
228
299
  const expectedCount = expected.count ?? 1;
229
300
  const levelMismatch = rows.some((row) => row.level !== expected.level);
230
301
  const dataMismatch = expected.data ? rows.flatMap((row) => matchesSubset(rowData(row), expected.data!)) : [];
@@ -240,10 +311,25 @@ async function runScenarioV0Exclusive(scenario: ScenarioV0): Promise<ScenarioRes
240
311
  if (levelMismatch) failureReasons.push(`log-level-mismatch:expected-${expected.level}-observed-${rows[0]?.level ?? 'none'}`);
241
312
  if (dataMismatch.length) failureReasons.push('log-data-mismatch');
242
313
 
314
+ const devicePass = failureReasons.length === 0;
315
+ const runEvidence = { category: 'ux.sim.scenario', event: 'completed' } as const;
316
+ debug.log(runEvidence.category, runEvidence.event, {
317
+ runId,
318
+ scenarioId: scenario.id,
319
+ deviceVerdict: devicePass ? 'PASS' : 'FAIL',
320
+ });
321
+ storeSink.flush();
322
+ const persistedRunEvents = store.queryByDataKeys({ exactCategories: [runEvidence.category], runIds: [runId] })
323
+ .filter((row) => row.event === runEvidence.event);
324
+ const runEventPersisted = persistedRunEvents.length === 1;
325
+ if (!runEventPersisted) failureReasons.push('run-evidence-event-not-persisted');
326
+
243
327
  const result: ScenarioResult = {
244
328
  id: scenario.id,
329
+ runId,
245
330
  seed: scenario.seed,
246
331
  evidenceArtifact: '',
332
+ runEvidence: { ...runEvidence, persisted: runEventPersisted },
247
333
  pass: failureReasons.length === 0,
248
334
  trigger: { id: trigger.id, type: trigger.type, status: trigger.status ?? null, expectedCount: expectedTriggerCount, fetchCount, error: errorMessage },
249
335
  screen: { pass: screenPass, containsMissing, excludesFound, snapshot: screenText },
@@ -271,37 +357,44 @@ async function runScenarioV0Exclusive(scenario: ScenarioV0): Promise<ScenarioRes
271
357
  mkdirSync(evidenceDir, { recursive: true });
272
358
  assertNoSymlinkComponents(evidenceDir);
273
359
  if (!lstatSync(evidenceDir).isDirectory()) throw new Error('scenario evidence directory must stay inside test state');
274
- const evidenceArtifact = join(evidenceDir, `${scenario.id}-seed-${scenario.seed}.json`);
360
+ const evidenceArtifact = join(evidenceDir, `${scenario.id}-seed-${scenario.seed}-run-${runId}.json`);
275
361
  result.evidenceArtifact = evidenceArtifact;
276
362
  const artifact = {
277
363
  version: 0,
278
364
  id: scenario.id,
365
+ runId,
279
366
  seed: scenario.seed,
280
367
  pass: result.pass,
281
368
  trigger: { type: result.trigger.type, ...(result.trigger.status !== null ? { status: result.trigger.status } : {}), expectedCount: result.trigger.expectedCount, fetchCount: result.trigger.fetchCount, errorObserved: result.trigger.error !== null },
282
369
  screen: { pass: result.screen.pass, snapshotSha256: createHash('sha256').update(result.screen.snapshot).digest('hex'), containsMissingCount: result.screen.containsMissing.length, excludesFoundCount: result.screen.excludesFound.length },
283
370
  logs: { pass: result.logs.pass, expectedCount: result.logs.expectedCount, observedCount: result.logs.observedCount, levelMismatch: result.logs.levelMismatch, dataMismatchCount: result.logs.dataMismatch.length },
371
+ runEvidence: { runId: result.runId, persisted: result.runEvidence.persisted },
284
372
  failureReasonCount: result.failureReasons.length,
285
373
  };
286
- const tempArtifact = join(evidenceDir, `.${scenario.id}-${randomUUID()}.tmp`);
287
- const fd = openSync(tempArtifact, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600);
288
- try {
289
- writeFileSync(fd, `${JSON.stringify(artifact, null, 2)}\n`);
290
- closeSync(fd);
291
- assertNoSymlinkComponents(evidenceDir);
292
- renameSync(tempArtifact, evidenceArtifact);
293
- } catch (error) {
294
- try { closeSync(fd); } catch { /* already closed */ }
295
- try { unlinkSync(tempArtifact); } catch { /* absent after successful rename */ }
296
- throw error;
297
- }
374
+ writeScenarioEvidenceArtifact(evidenceDir, evidenceArtifact, artifact);
298
375
  return result;
299
376
  } finally {
300
377
  globalThis.fetch = originalFetch;
301
378
  off();
302
- sink.close();
379
+ storeSink.close();
303
380
  store.close();
304
- rmSync(root, { recursive: true, force: true });
381
+ }
382
+ }
383
+
384
+ /** Write one already-validated evidence record atomically without following a target link. */
385
+ export function writeScenarioEvidenceArtifact(evidenceDir: string, evidenceArtifact: string, artifact: unknown): void {
386
+ assertNoSymlinkComponents(evidenceDir);
387
+ const tempArtifact = join(evidenceDir, `.${randomUUID()}.tmp`);
388
+ const fd = openSync(tempArtifact, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600);
389
+ try {
390
+ writeFileSync(fd, `${JSON.stringify(artifact, null, 2)}\n`);
391
+ closeSync(fd);
392
+ assertNoSymlinkComponents(evidenceDir);
393
+ renameSync(tempArtifact, evidenceArtifact);
394
+ } catch (error) {
395
+ try { closeSync(fd); } catch { /* already closed */ }
396
+ try { unlinkSync(tempArtifact); } catch { /* absent after successful rename */ }
397
+ throw error;
305
398
  }
306
399
  }
307
400