elanous 0.2.25 → 0.2.28

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 (337) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/chunks/{1454-37fd16f479f5554a.js → 1454-29d1d773d5996a49.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{599-a914cc0468f56143.js → 599-56f2c0a5cc4a7295.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-b12149c51f7d04b1.js → page-fa46d4588e8442c2.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/app/decisions/{page-305cd294e754906e.js → page-256ffd365d32e100.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-dea4b795741126fd.js → page-e0de78ae7b99f01c.js} +1 -1
  16. package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/exec/{page-45889e7070d11126.js → page-4c741eb760f2f77c.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/field/{page-16a2e893f83ae4d0.js → page-e9ba98f5f1440a21.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/app/intake/{page-56888bc744e08dac.js → page-2f6f303fa114ec24.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/app/live/{page-952c6ceec6d9de38.js → page-ced0bcfc6542b3fc.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/live-run/{page-6d0202af568f4c63.js → page-bb1f05cd51c38944.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-4fb6fc34e20c07b7.js → page-f736324afa2117c1.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/app/morning/{page-e9502921f4f01b1f.js → page-6f131b51193a36f9.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/{not-found-b6169ea2f09cf287.js → not-found-435f5e2faf02bc01.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/app/observatory/{page-6a14333d1b1b3967.js → page-497e0eedf20e7890.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-8c91a9a66c9ec90a.js → page-a8d28fdf995e16ea.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/app/{page-3d3fc73dd187ed52.js → page-66a67eb19ebd976b.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-9c04dac0831c2bbb.js → page-c71267e7681da982.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-04de9ca6d52a3893.js → page-cb249e81ed339318.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/app/settings/{page-62527677bbc2fdc4.js → page-781cfc5114e097da.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-9a2e09c4a311764c.js → page-478157214db6a023.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/setup/{page-bfeb35d69e8b24bb.js → page-86141f12cf090b7f.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/share/{page-89cb33bced824e4a.js → page-7256c73bb1c36751.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/today/{page-6445ac4cbb8dd869.js → page-979d318a534e92da.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-20162174b77f96cf.js → page-601fb77340e63c9a.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-05a4b2c9b91034aa.js → page-11bf9e5725a72c22.js} +1 -1
  53. package/apps/pwa/out/approvals/index.html +1 -1
  54. package/apps/pwa/out/approvals/index.txt +13 -13
  55. package/apps/pwa/out/autopilot/index.html +1 -1
  56. package/apps/pwa/out/autopilot/index.txt +13 -13
  57. package/apps/pwa/out/board/index.html +1 -1
  58. package/apps/pwa/out/board/index.txt +13 -13
  59. package/apps/pwa/out/botlab/index.html +1 -1
  60. package/apps/pwa/out/botlab/index.txt +13 -13
  61. package/apps/pwa/out/bots/index.html +1 -1
  62. package/apps/pwa/out/bots/index.txt +13 -13
  63. package/apps/pwa/out/ceo/index.html +1 -1
  64. package/apps/pwa/out/ceo/index.txt +13 -13
  65. package/apps/pwa/out/chat/index.html +1 -1
  66. package/apps/pwa/out/chat/index.txt +13 -13
  67. package/apps/pwa/out/consult/index.html +1 -1
  68. package/apps/pwa/out/consult/index.txt +13 -13
  69. package/apps/pwa/out/control/index.html +1 -1
  70. package/apps/pwa/out/control/index.txt +13 -13
  71. package/apps/pwa/out/decisions/index.html +1 -1
  72. package/apps/pwa/out/decisions/index.txt +13 -13
  73. package/apps/pwa/out/design-check/index.html +1 -1
  74. package/apps/pwa/out/design-check/index.txt +13 -13
  75. package/apps/pwa/out/editor/index.html +1 -1
  76. package/apps/pwa/out/editor/index.txt +13 -13
  77. package/apps/pwa/out/exec/index.html +1 -1
  78. package/apps/pwa/out/exec/index.txt +13 -13
  79. package/apps/pwa/out/field/index.html +1 -1
  80. package/apps/pwa/out/field/index.txt +13 -13
  81. package/apps/pwa/out/healthz +1 -1
  82. package/apps/pwa/out/index.html +1 -1
  83. package/apps/pwa/out/index.txt +13 -13
  84. package/apps/pwa/out/inside/index.html +1 -1
  85. package/apps/pwa/out/inside/index.txt +13 -13
  86. package/apps/pwa/out/intake/index.html +1 -1
  87. package/apps/pwa/out/intake/index.txt +13 -13
  88. package/apps/pwa/out/live/index.html +1 -1
  89. package/apps/pwa/out/live/index.txt +13 -13
  90. package/apps/pwa/out/live-run/index.html +1 -1
  91. package/apps/pwa/out/live-run/index.txt +13 -13
  92. package/apps/pwa/out/loops/index.html +1 -1
  93. package/apps/pwa/out/loops/index.txt +13 -13
  94. package/apps/pwa/out/market/index.html +1 -1
  95. package/apps/pwa/out/market/index.txt +13 -13
  96. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  97. package/apps/pwa/out/missions/placeholder/index.txt +13 -13
  98. package/apps/pwa/out/morning/index.html +1 -1
  99. package/apps/pwa/out/morning/index.txt +13 -13
  100. package/apps/pwa/out/observatory/index.html +1 -1
  101. package/apps/pwa/out/observatory/index.txt +13 -13
  102. package/apps/pwa/out/ops/checklist/index.html +1 -1
  103. package/apps/pwa/out/ops/checklist/index.txt +13 -13
  104. package/apps/pwa/out/ops/release/index.html +1 -1
  105. package/apps/pwa/out/ops/release/index.txt +13 -13
  106. package/apps/pwa/out/ops/seats/index.html +1 -1
  107. package/apps/pwa/out/ops/seats/index.txt +13 -13
  108. package/apps/pwa/out/outputs/index.html +1 -1
  109. package/apps/pwa/out/outputs/index.txt +13 -13
  110. package/apps/pwa/out/reflection/index.html +1 -1
  111. package/apps/pwa/out/reflection/index.txt +14 -14
  112. package/apps/pwa/out/scheduler/index.html +1 -1
  113. package/apps/pwa/out/scheduler/index.txt +13 -13
  114. package/apps/pwa/out/sessions/index.html +1 -1
  115. package/apps/pwa/out/sessions/index.txt +13 -13
  116. package/apps/pwa/out/settings/devices/index.html +1 -1
  117. package/apps/pwa/out/settings/devices/index.txt +13 -13
  118. package/apps/pwa/out/settings/index.html +1 -1
  119. package/apps/pwa/out/settings/index.txt +13 -13
  120. package/apps/pwa/out/setup/done/index.html +1 -1
  121. package/apps/pwa/out/setup/done/index.txt +13 -13
  122. package/apps/pwa/out/setup/index.html +1 -1
  123. package/apps/pwa/out/setup/index.txt +13 -13
  124. package/apps/pwa/out/share/index.html +1 -1
  125. package/apps/pwa/out/share/index.txt +13 -13
  126. package/apps/pwa/out/showroom/index.html +1 -1
  127. package/apps/pwa/out/showroom/index.txt +13 -13
  128. package/apps/pwa/out/tasks/index.html +1 -1
  129. package/apps/pwa/out/tasks/index.txt +13 -13
  130. package/apps/pwa/out/term/index.html +1 -1
  131. package/apps/pwa/out/term/index.txt +13 -13
  132. package/apps/pwa/out/today/index.html +1 -1
  133. package/apps/pwa/out/today/index.txt +13 -13
  134. package/apps/pwa/out/trace/index.html +1 -1
  135. package/apps/pwa/out/trace/index.txt +13 -13
  136. package/apps/pwa/out/vault/index.html +1 -1
  137. package/apps/pwa/out/vault/index.txt +13 -13
  138. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  139. package/apps/pwa/out/workflows/chat-ui/index.txt +13 -13
  140. package/apps/pwa/out/workflows/index.html +1 -1
  141. package/apps/pwa/out/workflows/index.txt +13 -13
  142. package/apps/pwa/out/workspace/index.html +1 -1
  143. package/apps/pwa/out/workspace/index.txt +13 -13
  144. package/apps/pwa/out/worktrees/index.html +1 -1
  145. package/apps/pwa/out/worktrees/index.txt +13 -13
  146. package/graphs/implement-loop.yaml +38 -1
  147. package/graphs/steward/recipes.yaml +6 -0
  148. package/graphs/steward/release-day.yaml +23 -0
  149. package/graphs/steward/steward.yaml +9 -1
  150. package/graphs/work/code-pr.yaml +23 -0
  151. package/package.json +1 -1
  152. package/scripts/browser-observe-cycle.ts +76 -2
  153. package/scripts/ci-typecheck-changed.ts +119 -7
  154. package/scripts/coord-msg-bridge.ts +209 -0
  155. package/scripts/coord-post.sh +40 -7
  156. package/scripts/coord-requests-bridge.ts +10 -5
  157. package/scripts/detect-test-interference.ts +31 -1
  158. package/scripts/expose-rubric-suggest.ts +1 -1
  159. package/scripts/measure-tsc-affected-roots.ts +10 -1
  160. package/scripts/release-loop/docs-node.ts +51 -22
  161. package/scripts/release-loop/export-check-node.ts +8 -5
  162. package/scripts/release-loop/gate-node.ts +37 -4
  163. package/scripts/release-loop/node-verdict.ts +10 -3
  164. package/scripts/release-loop/publish-node.ts +3 -0
  165. package/scripts/release-loop/tui-node-checks.ts +5 -2
  166. package/scripts/skills/public-check.ts +5 -2
  167. package/scripts/test-deterministic.ts +520 -37
  168. package/scripts/ux-sim.ts +7 -1
  169. package/scripts/verify-kpack6-live.ts +230 -0
  170. package/src/acp/codex-plugins.ts +17 -0
  171. package/src/agent/task-notification.ts +32 -2
  172. package/src/agent-mission/in-agent-plugin.ts +8 -3
  173. package/src/agent-substrate/review-observation.ts +9 -2
  174. package/src/agent-substrate/self-review-cli.ts +62 -16
  175. package/src/boot/daemon-tools/self-implement.ts +72 -4
  176. package/src/briefing/brief-items.ts +151 -35
  177. package/src/budget/llm-cost.ts +30 -3
  178. package/src/budget/usage-outcomes.ts +32 -0
  179. package/src/claims/claims-ledger.ts +23 -0
  180. package/src/claims/claims-render.ts +22 -2
  181. package/src/cli/brief-cli.ts +131 -10
  182. package/src/cli/claims-cli.ts +24 -4
  183. package/src/cli/control-cli.ts +29 -0
  184. package/src/cli/doctor-cli.ts +90 -8
  185. package/src/cli/msg-cli.ts +177 -11
  186. package/src/cli/plugin-cli.ts +11 -3
  187. package/src/cli/pr-cli.ts +133 -18
  188. package/src/cli/pr-land-overlap-evidence.ts +18 -10
  189. package/src/cli/pr-land-refusal.ts +20 -0
  190. package/src/cli/release-cli.ts +34 -5
  191. package/src/cli/rfc-cli.ts +24 -0
  192. package/src/cli/seat-cli.ts +121 -0
  193. package/src/cli/self-utterances-cli.ts +140 -0
  194. package/src/cli/tailscale-serve.ts +5 -8
  195. package/src/cli/tasks-cli.ts +528 -31
  196. package/src/cli/usage-cli.ts +9 -4
  197. package/src/context-card/cell.ts +40 -0
  198. package/src/dashboard/boot-timing.ts +35 -0
  199. package/src/dashboard/first-screen-band.ts +63 -13
  200. package/src/dashboard/index.ts +94 -27
  201. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
  202. package/src/dashboard/skill-detection-runtime.ts +24 -4
  203. package/src/dashboard/slash-runtime/dashboard-handlers.ts +6 -0
  204. package/src/debug/log.ts +1 -1
  205. package/src/domains/morning-report.ts +3 -1
  206. package/src/domains/release-tool.ts +29 -12
  207. package/src/domains/surface-events.ts +16 -0
  208. package/src/elanous-config-dir.ts +2 -2
  209. package/src/fleet/host-doctor.ts +73 -2
  210. package/src/github/pr-labels.ts +10 -4
  211. package/src/graph-runner/runner.ts +104 -15
  212. package/src/harness/harness-clean.ts +21 -3
  213. package/src/harness/harness-cli-command.ts +68 -10
  214. package/src/harness/harness-pod-dispatch.ts +11 -4
  215. package/src/harness/harness-stop.ts +11 -7
  216. package/src/hq/seats.ts +17 -3
  217. package/src/index.ts +69 -9
  218. package/src/instance/current.ts +2 -6
  219. package/src/instance/resolve.ts +5 -6
  220. package/src/intelligence-map/model-bench-scores.ts +102 -0
  221. package/src/llm/openai-sampling-fields.ts +89 -0
  222. package/src/llm/runtime-model-family.ts +61 -0
  223. package/src/llm/zai-coding-plan.ts +27 -1
  224. package/src/llm.ts +13 -89
  225. package/src/loops/orchestrator/inbox.ts +73 -0
  226. package/src/loops/orchestrator/resync-shadow.ts +100 -12
  227. package/src/loops/orchestrator/tick.ts +73 -2
  228. package/src/machines/machine-ledger.ts +31 -3
  229. package/src/maturity/cli-maturity.ts +13 -3
  230. package/src/maturity/feature-maturity.ts +1 -1
  231. package/src/msg/msg-sse-server.ts +141 -0
  232. package/src/msg/msg-store.ts +149 -17
  233. package/src/ops/overview-collect.ts +3 -2
  234. package/src/ops/overview-owner.ts +29 -0
  235. package/src/patents/ledger.ts +128 -0
  236. package/src/plugin-hooks/shell-hook.ts +17 -2
  237. package/src/plugin-missions/evaluator-runner.ts +18 -2
  238. package/src/plugins/install/market-fetch.ts +32 -1
  239. package/src/plugins/install/plugin-install.ts +11 -1
  240. package/src/pod-lease/admission.ts +116 -41
  241. package/src/prompt-enhance/enhance.ts +11 -0
  242. package/src/release-loop/checklist.ts +52 -4
  243. package/src/release-loop/feature-store.ts +10 -9
  244. package/src/release-loop/merged-pr-checklist.ts +24 -5
  245. package/src/release-loop/rubric.ts +70 -0
  246. package/src/rfc-registry/registry.ts +88 -0
  247. package/src/seat-dispatch/seat-request-ledger.ts +4 -0
  248. package/src/seat-loop/knowhow-lint.ts +233 -0
  249. package/src/seat-loop/seat-loop.ts +5 -1
  250. package/src/seat-loop/seat-pack.ts +16 -4
  251. package/src/seat-loop/traffic-signal.ts +7 -0
  252. package/src/self-dev/ask-launch-flow.ts +48 -8
  253. package/src/self-dev/ask-path-candidates.ts +69 -14
  254. package/src/self-dev/decompose-proposal.ts +43 -1
  255. package/src/self-dev/draft-sweep.ts +23 -14
  256. package/src/self-dev/graph-journey-nodes.ts +2 -0
  257. package/src/self-dev/launch-preflight-text.ts +5 -1
  258. package/src/self-dev/launch-preflight.ts +143 -10
  259. package/src/self-dev/orchestrate.ts +6 -2
  260. package/src/self-dev/run-supervisor.ts +35 -1
  261. package/src/self-implement/gate-cli.ts +6 -2
  262. package/src/self-implement/goal-author.ts +5 -28
  263. package/src/self-implement/goal-command-execution.ts +26 -0
  264. package/src/self-implement/goal-new-paths.ts +4 -5
  265. package/src/self-implement/graph-authority.ts +17 -3
  266. package/src/self-implement/graph-yaml.ts +15 -7
  267. package/src/self-implement/headless-elanous-driver.ts +16 -1
  268. package/src/self-implement/host-regate.ts +44 -9
  269. package/src/self-implement/orchestrator.ts +5 -11
  270. package/src/self-implement/pwa-import-graph.ts +194 -7
  271. package/src/self-implement/rework-policy.ts +80 -2
  272. package/src/self-implement/self-implement-runtime.ts +126 -21
  273. package/src/self-implement/speed-claims.ts +145 -6
  274. package/src/session/index.ts +14 -4
  275. package/src/session-runtime/index.ts +55 -11
  276. package/src/skills/tools/persistent-grounding.ts +59 -4
  277. package/src/skills/tools/shell.ts +79 -1
  278. package/src/steward/rejudge-tick.ts +12 -1
  279. package/src/steward/release-day.ts +158 -0
  280. package/src/steward/resync-tick.ts +123 -0
  281. package/src/task-agent/actions.ts +1 -0
  282. package/src/task-agent/adopt-pr.ts +140 -0
  283. package/src/task-agent/board.ts +11 -1
  284. package/src/task-agent/card-evidence.ts +37 -5
  285. package/src/task-agent/cover.ts +105 -19
  286. package/src/task-agent/failure-notify.ts +61 -0
  287. package/src/task-agent/green-sweep.ts +170 -0
  288. package/src/task-agent/land-worktree.ts +52 -5
  289. package/src/task-agent/live-move-history.ts +1 -0
  290. package/src/task-agent/live-moves.ts +56 -17
  291. package/src/task-agent/parent-host.ts +1 -1
  292. package/src/task-agent/postland-card.ts +139 -0
  293. package/src/task-agent/rejudge-on-head.ts +321 -25
  294. package/src/task-agent/remote-rejudge-bind.ts +62 -0
  295. package/src/task-agent/repair-branch-cas.ts +182 -0
  296. package/src/task-agent/repair-cycle.ts +275 -0
  297. package/src/task-agent/repair-next-move.ts +31 -0
  298. package/src/task-agent/repair-review.ts +24 -0
  299. package/src/task-agent/repair-worker.ts +87 -0
  300. package/src/task-agent/review-result-capture.ts +66 -2
  301. package/src/task-agent/shadow.ts +150 -29
  302. package/src/task-agent/task-hand.ts +133 -8
  303. package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
  304. package/src/task-orchestrator/surfaces/self-implement-pod.ts +71 -12
  305. package/src/telegram.ts +30 -1
  306. package/src/tool-runtime/tool-search-runtime.ts +25 -0
  307. package/src/tui.ts +20 -1
  308. package/src/user-config.ts +11 -60
  309. package/src/ux-sim/fault-sqlite-busy.ts +50 -0
  310. package/src/version/packed-revision.json +1 -1
  311. package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +0 -1
  312. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +0 -1
  313. package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +0 -1
  314. package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +0 -1
  315. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +0 -1
  316. package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +0 -1
  317. package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +0 -1
  318. package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +0 -1
  324. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4e634c5b98fd4e93.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/app/outputs/page-b9cc8a10bc6078b4.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +0 -1
  336. /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_buildManifest.js +0 -0
  337. /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_ssgManifest.js +0 -0
@@ -30,10 +30,11 @@
30
30
  // group. If group termination fails it falls back to the direct child and
31
31
  // reports the failure.
32
32
 
33
- import { mkdtempSync, rmSync } from 'node:fs';
33
+ import { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
34
34
  import { spawnSync } from 'node:child_process';
35
35
  import { tmpdir } from 'node:os';
36
- import { join, resolve } from 'node:path';
36
+ import { isAbsolute, join, relative, resolve, sep } from 'node:path';
37
+ import * as ts from 'typescript';
37
38
  // The predicate lives in its own side-effect-free module so it can be tested:
38
39
  // importing THIS file used to spawn the suite at load time. The runner is now
39
40
  // behind `import.meta.main` / `runDeterministicTests()`, but the filter stays
@@ -79,9 +80,12 @@ const EXECUTION_ORIGIN_ENV_KEYS = [
79
80
 
80
81
  export type ShutdownSignal = 'SIGINT' | 'SIGTERM';
81
82
 
83
+ export type NamePatternEvidence = { nameMissDiagnostic: boolean; passSummary: boolean; runSummary: boolean };
84
+
82
85
  export type DirectChild = {
83
86
  readonly pid?: number;
84
87
  readonly exited: Promise<number>;
88
+ readonly stderrEvidence?: Promise<NamePatternEvidence>;
85
89
  kill(signal?: number | NodeJS.Signals): boolean | void;
86
90
  };
87
91
 
@@ -93,6 +97,8 @@ export type SpawnDirectChild = (opts: {
93
97
  stdout: 'inherit';
94
98
  stderr: 'inherit';
95
99
  detached: true;
100
+ captureNoMatch?: boolean;
101
+ namePatterns?: string[];
96
102
  }) => DirectChild;
97
103
 
98
104
  export type KillProcessGroup = (pid: number, signal: NodeJS.Signals) => void;
@@ -113,6 +119,8 @@ export type DeterministicRunnerDeps = {
113
119
  argv?: string[];
114
120
  /** 시험이 파생을 갈아 끼울 자리 — 실물 rg 를 안 부르고 목록을 준다. */
115
121
  deriveCdpPatterns?: () => string[];
122
+ deriveMockModuleFiles?: () => string[];
123
+ deriveTestFiles?: () => string[];
116
124
  cwd?: string;
117
125
  tmpdir?: () => string;
118
126
  mkdtempSync?: (prefix: string) => string;
@@ -317,15 +325,68 @@ export function prepareIsolatedTestEnv(sourceEnv: NodeJS.ProcessEnv, testRoot: s
317
325
  return envLiteral(env);
318
326
  }
319
327
 
320
- const defaultSpawn: SpawnDirectChild = (opts) => Bun.spawn({
321
- cmd: opts.cmd,
322
- cwd: opts.cwd,
323
- env: opts.env,
324
- stdin: 'inherit',
325
- stdout: 'inherit',
326
- stderr: 'inherit',
327
- detached: true,
328
- });
328
+ /** Read every stderr line, retaining only Bun's verdict signals. A long test
329
+ * log must not become a second in-memory copy of the full output. */
330
+ export async function scanNamePatternStderr(
331
+ stream: AsyncIterable<Uint8Array>, patterns: string[], forward: (chunk: Uint8Array) => void = () => {},
332
+ ): Promise<NamePatternEvidence> {
333
+ const evidence = { nameMissDiagnostic: false, passSummary: false, runSummary: false };
334
+ const decoder = new TextDecoder();
335
+ let pending = '';
336
+ let overflow = false;
337
+ const inspect = (line: string) => {
338
+ if (/^\s*[1-9]\d* pass$/.test(line)) evidence.passSummary = true;
339
+ if (/^Ran [1-9]\d* tests? across \d+ files?\. \[\d+(?:\.\d+)?ms\]$/.test(line)) evidence.runSummary = true;
340
+ for (const pattern of patterns) {
341
+ const prefix = `error: regex ${JSON.stringify(pattern)} matched 0 tests. Searched `;
342
+ if (line.startsWith(prefix)
343
+ && /^\d+ files? \(skipping \d+ tests?\) \[\d+(?:\.\d+)?ms\]$/.test(line.slice(prefix.length))) {
344
+ evidence.nameMissDiagnostic = true;
345
+ }
346
+ }
347
+ };
348
+ const feed = (text: string) => {
349
+ let start = 0;
350
+ while (start < text.length) {
351
+ const newline = text.indexOf('\n', start);
352
+ const part = text.slice(start, newline < 0 ? undefined : newline);
353
+ if (!overflow) {
354
+ if (pending.length + part.length > 4096) { pending = ''; overflow = true; }
355
+ else pending += part;
356
+ }
357
+ if (newline < 0) break;
358
+ if (!overflow) inspect(pending.endsWith('\r') ? pending.slice(0, -1) : pending);
359
+ pending = '';
360
+ overflow = false;
361
+ start = newline + 1;
362
+ }
363
+ };
364
+ for await (const chunk of stream) {
365
+ forward(chunk);
366
+ feed(decoder.decode(chunk, { stream: true }));
367
+ }
368
+ feed(decoder.decode());
369
+ if (!overflow) inspect(pending.endsWith('\r') ? pending.slice(0, -1) : pending);
370
+ return evidence;
371
+ }
372
+
373
+ const defaultSpawn: SpawnDirectChild = (opts) => {
374
+ const child = Bun.spawn({
375
+ cmd: opts.cmd,
376
+ cwd: opts.cwd,
377
+ env: opts.env,
378
+ stdin: 'inherit',
379
+ stdout: 'inherit',
380
+ stderr: opts.captureNoMatch ? 'pipe' : 'inherit',
381
+ detached: true,
382
+ });
383
+ if (!opts.captureNoMatch) return child;
384
+ const stderrEvidence = scanNamePatternStderr(
385
+ child.stderr as ReadableStream<Uint8Array>, opts.namePatterns ?? [],
386
+ (chunk) => process.stderr.write(chunk),
387
+ );
388
+ return { pid: child.pid, exited: child.exited, kill: (signal) => child.kill(signal), stderrEvidence };
389
+ };
329
390
 
330
391
  export function installShutdownSignalGate(
331
392
  target: SignalTarget = process,
@@ -403,6 +464,357 @@ export function deriveCdpTestPatterns(deps: { spawnSync?: typeof Bun.spawnSync;
403
464
  .sort();
404
465
  }
405
466
 
467
+ // Keep both scans aligned with Bun's default test discovery (four name forms,
468
+ // eight JavaScript/TypeScript extensions). A file absent from either list can
469
+ // be silently skipped when a positional filter matches multiple test files.
470
+ const BUN_TEST_EXTENSIONS = ['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'mts', 'cts'];
471
+ const BUN_TEST_GLOBS = BUN_TEST_EXTENSIONS.flatMap((ext) => [
472
+ `*.test.${ext}`, `*_test.${ext}`, `*.spec.${ext}`, `*_spec.${ext}`,
473
+ ]);
474
+ const BUN_TEST_FILE_PATTERN = /(?:\.|_)(?:test|spec)\.(?:js|jsx|ts|tsx|mjs|cjs|mts|cts)$/;
475
+ const BUN_EXPLICIT_TEST_FILE_PATTERN = /\.(?:js|jsx|ts|tsx|mjs|cjs|mts|cts)$/;
476
+
477
+ const MOCK_SCAN_OPTIONS: ts.CompilerOptions = {
478
+ allowJs: true, noResolve: true, noLib: true, jsx: ts.JsxEmit.Preserve,
479
+ };
480
+
481
+ function bunTestScriptKind(file: string): ts.ScriptKind {
482
+ if (file.endsWith('.jsx')) return ts.ScriptKind.JSX;
483
+ if (file.endsWith('.tsx')) return ts.ScriptKind.TSX;
484
+ if (file.endsWith('.js') || file.endsWith('.mjs') || file.endsWith('.cjs')) return ts.ScriptKind.JS;
485
+ return ts.ScriptKind.TS; // .ts, .mts, .cts
486
+ }
487
+
488
+ function sourceHasModuleMockCall(tree: ts.SourceFile): boolean {
489
+ let found = false;
490
+ const unwrap = (expression: ts.Expression): ts.Expression => {
491
+ while (ts.isParenthesizedExpression(expression)) expression = expression.expression;
492
+ return expression;
493
+ };
494
+ const visit = (node: ts.Node): void => {
495
+ if (found) return;
496
+ if (ts.isCallExpression(node)) {
497
+ const callee = unwrap(node.expression);
498
+ if (ts.isPropertyAccessExpression(callee)) {
499
+ const receiver = unwrap(callee.expression);
500
+ if (ts.isIdentifier(receiver) && receiver.text === 'mock' && callee.name.text === 'module') {
501
+ found = true;
502
+ return;
503
+ }
504
+ }
505
+ }
506
+ ts.forEachChild(node, visit);
507
+ };
508
+ visit(tree);
509
+ return found;
510
+ }
511
+
512
+ /** Use public syntactic diagnostics; TypeScript's parseDiagnostics field is
513
+ * internal and absent from SourceFile's public type. */
514
+ function scanMockCallBatch(files: string[]): boolean[] {
515
+ const roots = new Set(files.map((file) => resolve(file)));
516
+ const host = ts.createCompilerHost(MOCK_SCAN_OPTIONS);
517
+ host.getSourceFile = (file, languageVersion) => {
518
+ if (!roots.has(resolve(file))) return undefined;
519
+ try {
520
+ return ts.createSourceFile(file, readFileSync(file, 'utf8'), languageVersion, true, bunTestScriptKind(file));
521
+ } catch { return undefined; }
522
+ };
523
+ const program = ts.createProgram(files, MOCK_SCAN_OPTIONS, host);
524
+ return files.map((file) => {
525
+ const tree = program.getSourceFile(file);
526
+ return !tree || program.getSyntacticDiagnostics(tree).length > 0 || sourceHasModuleMockCall(tree);
527
+ });
528
+ }
529
+
530
+ function hasModuleMockCall(source: string, file: string): boolean {
531
+ const host = ts.createCompilerHost(MOCK_SCAN_OPTIONS);
532
+ host.getSourceFile = (name, languageVersion) => resolve(name) === resolve(file)
533
+ ? ts.createSourceFile(name, source, languageVersion, true, bunTestScriptKind(file)) : undefined;
534
+ const program = ts.createProgram([file], MOCK_SCAN_OPTIONS, host);
535
+ const tree = program.getSourceFile(file);
536
+ return !tree || program.getSyntacticDiagnostics(tree).length > 0 || sourceHasModuleMockCall(tree);
537
+ }
538
+
539
+ /** Parse every Bun-discovered test: text search misses valid calls with a
540
+ * comment between `mock` and `.module`. An extra solo file costs time, while a
541
+ * missed call can contaminate every later file in the Bun process. */
542
+ export function deriveMockModuleTestFiles(deps: { spawnSync?: typeof Bun.spawnSync; cwd?: string } = {}): string[] {
543
+ const cwd = deps.cwd ?? process.cwd();
544
+ const files = deriveTestFiles({ spawnSync: deps.spawnSync, cwd });
545
+ const mocks: string[] = [];
546
+ // A bounded program keeps the public syntax check below 600 MB on the
547
+ // current repository, rather than retaining all 5,836 ASTs at once.
548
+ for (let index = 0; index < files.length; index += 100) {
549
+ const batch = files.slice(index, index + 100);
550
+ const matched = scanMockCallBatch(batch.map((file) => resolve(cwd, file)));
551
+ for (let offset = 0; offset < batch.length; offset++) if (matched[offset]) mocks.push(batch[offset]!);
552
+ }
553
+ return mocks.sort();
554
+ }
555
+
556
+ export function deriveTestFiles(deps: { spawnSync?: typeof Bun.spawnSync; cwd?: string } = {}): string[] {
557
+ const run = deps.spawnSync ?? Bun.spawnSync;
558
+ const result = run({
559
+ cmd: ['rg', '--files', ...BUN_TEST_GLOBS.flatMap((glob) => ['--glob', glob])],
560
+ cwd: deps.cwd ?? process.cwd(),
561
+ stdout: 'pipe',
562
+ stderr: 'pipe',
563
+ });
564
+ // rg --files exits 1 for a valid directory with no matching test files.
565
+ if (result.exitCode !== 0 && result.exitCode !== 1) {
566
+ throw new Error(`test file scan failed (exit ${result.exitCode}): ${decodeSpawnOutput(result.stderr).trim()}`);
567
+ }
568
+ return decodeSpawnOutput(result.stdout).split('\n').map((line) => line.trim()).filter((line) => BUN_TEST_FILE_PATTERN.test(line));
569
+ }
570
+
571
+ // Bun accepts both `--flag=value` and `--flag value`. The value of a test
572
+ // option is never a file selector, even when it happens to match a file name.
573
+ // Audited against `bun --help` and `bun test --help` in Bun 1.4.2. Bun accepts
574
+ // these global value options after `test` too. Missing one makes its value look
575
+ // like a path selector, which disables the default CDP exclusion.
576
+ const BUN_TEST_VALUE_OPTIONS = new Set([
577
+ '--elide-lines', '-F', '--filter', '--shell', '--watch-kill-signal',
578
+ '--preload', '-r', '--require', '--import', '--inspect', '--inspect-wait', '--inspect-brk',
579
+ '--cpu-prof-name', '--cpu-prof-dir', '--cpu-prof-interval',
580
+ '--heap-prof-name', '--heap-prof-dir', '--heap-prof-interval',
581
+ '--install', '--eval', '-e', '--print', '-p', '--port', '--conditions',
582
+ '--fetch-preconnect', '--max-http-header-size', '--dns-result-order',
583
+ '--redirect-warnings', '--disable-warning', '--title', '--unhandled-rejections',
584
+ '--console-depth', '--user-agent', '--cron-title', '--cron-period', '--main-fields',
585
+ '--extension-order', '--tsconfig-override', '--define', '-d', '--drop', '--feature',
586
+ '--loader', '-l', '--jsx-factory', '--jsx-fragment', '--jsx-import-source',
587
+ '--jsx-runtime', '--env-file', '--cwd', '--config', '-c',
588
+ // `bun test --help` values:
589
+ '--timeout', '--rerun-each', '--retry', '--seed', '--coverage-reporter',
590
+ '--coverage-dir', '--test-name-pattern', '-t', '--reporter',
591
+ '--reporter-outfile', '--max-concurrency', '--path-ignore-patterns',
592
+ '--parallel-delay', '--shard',
593
+ // `bun test --timings file.test.ts` reads file.test.ts as timing JSON and
594
+ // errors if it contains test code; it does not treat the file as a selector.
595
+ '--timings',
596
+ ]);
597
+ export function partitionBunTestArguments(argv: string[]): { selectors: string[]; soloArgs: string[] } {
598
+ const selectors: string[] = [];
599
+ const soloArgs: string[] = [];
600
+ for (let index = 0; index < argv.length; index++) {
601
+ const arg = argv[index]!;
602
+ if (arg === '--') {
603
+ soloArgs.push(arg);
604
+ selectors.push(...argv.slice(index + 1));
605
+ break;
606
+ }
607
+ if (!arg.startsWith('-')) {
608
+ selectors.push(arg);
609
+ continue;
610
+ }
611
+ soloArgs.push(arg);
612
+ if (arg.includes('=')) continue;
613
+ const next = argv[index + 1];
614
+ // Bun 1.4.2 reads `--bail 2`, `--parallel 2`, and `--changed HEAD` as
615
+ // positional filters. Their optional values require `--flag=value`.
616
+ if (next !== undefined && BUN_TEST_VALUE_OPTIONS.has(arg)) {
617
+ soloArgs.push(next);
618
+ index++;
619
+ }
620
+ }
621
+ return { selectors, soloArgs };
622
+ }
623
+
624
+ function bunTestOptionTokens(argv: string[]): string[] {
625
+ const options: string[] = [];
626
+ for (let index = 0; index < argv.length; index++) {
627
+ const arg = argv[index]!;
628
+ if (arg === '--') break;
629
+ if (!arg.startsWith('-')) continue;
630
+ options.push(arg);
631
+ if (!arg.includes('=') && BUN_TEST_VALUE_OPTIONS.has(arg) && argv[index + 1] !== undefined) index++;
632
+ }
633
+ return options;
634
+ }
635
+
636
+ function bunTestOptionValues(argv: string[], name: string): string[] {
637
+ const values: string[] = [];
638
+ for (let index = 0; index < argv.length; index++) {
639
+ const arg = argv[index]!;
640
+ if (arg === '--') break;
641
+ if (arg === name && argv[index + 1] !== undefined) values.push(argv[index + 1]!);
642
+ else if (arg.startsWith(`${name}=`)) values.push(arg.slice(name.length + 1));
643
+ if (!arg.includes('=') && BUN_TEST_VALUE_OPTIONS.has(arg) && argv[index + 1] !== undefined) index++;
644
+ }
645
+ return values;
646
+ }
647
+
648
+ /** Bun applies the final --cwd against the process launch directory, not a
649
+ * previous --cwd. Use the same root for discovery as the child test runner. */
650
+ export function effectiveTestCwd(argv: string[], launchCwd: string): string {
651
+ let testCwd = launchCwd;
652
+ for (let index = 0; index < argv.length; index++) {
653
+ const arg = argv[index]!;
654
+ if (arg === '--') break;
655
+ if (arg.startsWith('--cwd=')) {
656
+ testCwd = resolve(launchCwd, arg.slice('--cwd='.length));
657
+ } else if (arg === '--cwd' && argv[index + 1] !== undefined) {
658
+ testCwd = resolve(launchCwd, argv[++index]!);
659
+ } else if (BUN_TEST_VALUE_OPTIONS.has(arg)) {
660
+ index++;
661
+ }
662
+ }
663
+ return testCwd;
664
+ }
665
+
666
+ /** rg intentionally honors ignore rules, but Bun can execute an explicit
667
+ * ignored or outside-repository test file. Inspect those paths directly. */
668
+ export function classifyExplicitTestFiles(argv: string[], cwd: string): { files: string[]; mockFiles: string[] } {
669
+ const files: string[] = [];
670
+ const mockFiles: string[] = [];
671
+ for (const selector of partitionBunTestArguments(argv).selectors) {
672
+ const absolute = resolve(cwd, selector);
673
+ // Bun treats an unsuffixed `test/custom.ts` as a name filter, while
674
+ // `./test/custom.ts` and absolute paths explicitly select that file.
675
+ const explicitFilePath = BUN_TEST_FILE_PATTERN.test(absolute)
676
+ || selector.startsWith('./') || selector.startsWith('../') || isAbsolute(selector);
677
+ let candidates: string[] = [];
678
+ let isDirectory = false;
679
+ try {
680
+ const stats = statSync(absolute);
681
+ isDirectory = stats.isDirectory();
682
+ if (stats.isFile() && explicitFilePath && BUN_EXPLICIT_TEST_FILE_PATTERN.test(absolute)) candidates = [absolute];
683
+ } catch (error) {
684
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') continue;
685
+ // A file Bun may open but our classifier cannot read must run alone.
686
+ candidates = explicitFilePath && BUN_EXPLICIT_TEST_FILE_PATTERN.test(absolute) ? [absolute] : [];
687
+ }
688
+ // A failed directory scan must stop planning; it cannot certify that a
689
+ // selected directory contains no ordinary tests.
690
+ if (isDirectory) candidates = deriveTestFiles({ cwd: absolute }).map((file) => resolve(absolute, file));
691
+ for (const candidate of candidates) {
692
+ const rel = relative(cwd, candidate);
693
+ // Bun discovers a custom.ts passed by absolute path, but treats bare
694
+ // `test/custom.ts` as a name filter rather than an explicit file path.
695
+ const file = rel.startsWith('..') || isAbsolute(rel) || !BUN_TEST_FILE_PATTERN.test(candidate)
696
+ ? candidate : rel;
697
+ files.push(file);
698
+ try {
699
+ if (hasModuleMockCall(readFileSync(candidate, 'utf8'), candidate)) mockFiles.push(file);
700
+ } catch {
701
+ mockFiles.push(file);
702
+ }
703
+ }
704
+ }
705
+ return { files: [...new Set(files)], mockFiles: [...new Set(mockFiles)] };
706
+ }
707
+
708
+ function replaceBulkSelectors(argv: string[], replacement: (selector: string) => string[]): string[] {
709
+ const result: string[] = [];
710
+ for (let index = 0; index < argv.length; index++) {
711
+ const arg = argv[index]!;
712
+ if (arg === '--') {
713
+ result.push(arg, ...argv.slice(index + 1).flatMap(replacement));
714
+ break;
715
+ }
716
+ if (arg.startsWith('-')) {
717
+ result.push(arg);
718
+ if (BUN_TEST_VALUE_OPTIONS.has(arg) && argv[index + 1] !== undefined) result.push(argv[++index]!);
719
+ } else {
720
+ result.push(...replacement(arg));
721
+ }
722
+ }
723
+ return result;
724
+ }
725
+
726
+ /** Return one bulk command and one command per selected global module mock. */
727
+ export function planDeterministicTestCommands(
728
+ argv: string[], cdpPatterns: string[], mockFiles: string[], allTestFiles: string[] = [], testCwd = process.cwd(),
729
+ ): string[][] {
730
+ const { selectors: pathArgs, soloArgs } = partitionBunTestArguments(argv);
731
+ const explicitPaths = pathArgs.length > 0;
732
+ const directory = (selector: string): string | undefined => {
733
+ const path = resolve(testCwd, selector);
734
+ try { return statSync(path).isDirectory() ? path : undefined; } catch { return undefined; }
735
+ };
736
+ const insideDirectory = (file: string, path: string): boolean => {
737
+ const absolute = resolve(testCwd, file);
738
+ return absolute.startsWith(path + sep);
739
+ };
740
+ const discoveredPaths = new Set(allTestFiles.map((file) => resolve(testCwd, file)));
741
+ const isExact = (selector: string): boolean => discoveredPaths.has(resolve(testCwd, selector));
742
+ const matchesSelector = (file: string, selector: string): boolean => {
743
+ const dir = directory(selector);
744
+ if (dir) return insideDirectory(file, dir);
745
+ return isExact(selector)
746
+ ? resolve(testCwd, file) === resolve(testCwd, selector)
747
+ : file.includes(selector.replace(/^\.\//, ''));
748
+ };
749
+ const callerIgnoreGlobs = bunTestOptionValues(argv, '--path-ignore-patterns').map((pattern) => new Bun.Glob(pattern));
750
+ const selectedMocks = mockFiles.filter((file) => !cdpPatterns.includes(file)
751
+ && !callerIgnoreGlobs.some((glob) => glob.match(file) || glob.match(resolve(testCwd, file)))
752
+ && (!explicitPaths || pathArgs.some((selector) => matchesSelector(file, selector))));
753
+ if (selectedMocks.length === 0) {
754
+ return [['bun', 'test', ...cdpPatterns.flatMap((pattern) => ['--path-ignore-patterns', pattern]), ...argv]];
755
+ }
756
+ const globalBail = !explicitPaths && bunTestOptionTokens(argv).some((arg) =>
757
+ arg === '--bail' || arg === '--bail=1');
758
+ if ((explicitPaths && pathArgs.every((selector) =>
759
+ allTestFiles.some((file) => matchesSelector(file, selector))))
760
+ || (globalBail && allTestFiles.length > 0)) {
761
+ // Bun runs selected files in path order, regardless of selector order.
762
+ // Keep the ordinary files in contiguous bulk runs around solo mock files;
763
+ // this also preserves which file a global --bail stops on.
764
+ const files = allTestFiles.filter((file) => (explicitPaths
765
+ ? pathArgs.some((selector) => matchesSelector(file, selector))
766
+ : !cdpPatterns.includes(file))
767
+ && !callerIgnoreGlobs.some((glob) => glob.match(file) || glob.match(resolve(testCwd, file))))
768
+ .sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
769
+ const commands: string[][] = [];
770
+ let ordinary: string[] = [];
771
+ const flushOrdinary = () => {
772
+ if (ordinary.length > 0) commands.push(['bun', 'test', ...soloArgs, ...ordinary]);
773
+ ordinary = [];
774
+ };
775
+ for (const file of files) {
776
+ if (selectedMocks.includes(file)) {
777
+ flushOrdinary();
778
+ commands.push(['bun', 'test', ...soloArgs, file]);
779
+ } else ordinary.push(file);
780
+ }
781
+ flushOrdinary();
782
+ return commands;
783
+ }
784
+ const exactMockSelector = (selector: string): boolean => isExact(selector)
785
+ && allTestFiles.some((file) => resolve(testCwd, file) === resolve(testCwd, selector))
786
+ && selectedMocks.some((file) => resolve(testCwd, file) === resolve(testCwd, selector));
787
+ // Remove selected exact mock arguments from bulk so each runs in its own
788
+ // process. Caller ignore globs are applied above before this selection.
789
+ const replacement = (selector: string): string[] => {
790
+ const dir = directory(selector);
791
+ if (dir) {
792
+ const matched = allTestFiles.filter((file) => insideDirectory(file, dir));
793
+ return matched.length === 0 ? [selector] : matched.filter((file) => !selectedMocks.includes(file));
794
+ }
795
+ if (exactMockSelector(selector)) return [];
796
+ const matched = allTestFiles.filter((file) => matchesSelector(file, selector));
797
+ // A partial path selector can select only mock files. In that case Bun's
798
+ // bulk child has no tests after exclusion, so leave the selector to solo.
799
+ // Unknown selectors stay in bulk to preserve Bun's no-tests failure.
800
+ return matched.length > 0 && matched.every((file) => selectedMocks.includes(file)) ? [] : [selector];
801
+ };
802
+ const bulkSelectors = pathArgs.flatMap(replacement);
803
+ const skipBulk = explicitPaths && bulkSelectors.length === 0;
804
+ const bulkArgv = replaceBulkSelectors(argv, replacement);
805
+ const bulk = skipBulk ? [] : [[
806
+ 'bun', 'test',
807
+ ...[...new Set([...cdpPatterns, ...selectedMocks])].flatMap((pattern) => ['--path-ignore-patterns', pattern]),
808
+ // With only a name pattern, ordinary tests may match zero names while a
809
+ // solo mock child still matches. There is no path selector to validate.
810
+ ...(!explicitPaths && selectedMocks.length > 0 && !bunTestOptionTokens(argv).some((arg) =>
811
+ arg === '--test-name-pattern' || arg.startsWith('--test-name-pattern=') || arg === '-t' || arg.startsWith('-t='))
812
+ ? ['--pass-with-no-tests'] : []),
813
+ ...bulkArgv,
814
+ ]];
815
+ return [...bulk, ...selectedMocks.map((file) => ['bun', 'test', ...soloArgs, file])];
816
+ }
817
+
406
818
  export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}): Promise<number> {
407
819
  const makeTemp = deps.mkdtempSync ?? mkdtempSync;
408
820
  const tempBase = deps.tmpdir ?? tmpdir;
@@ -454,12 +866,64 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
454
866
  }
455
867
  };
456
868
  // ⛔ 사람이 «경로»를 직접 준 창에서는 아무것도 빼지 않는다 — 그 창은 「이것만 돌려라」다.
457
- const explicitPaths = argv.some((arg) => !arg.startsWith('-'));
458
- const cdpPatterns = explicitPaths ? [] : (deps.deriveCdpPatterns ?? deriveCdpTestPatterns)();
459
- const ignoreArgs = cdpPatterns.flatMap((pattern) => ['--path-ignore-patterns', pattern]);
869
+ const explicitPaths = partitionBunTestArguments(argv).selectors.length > 0;
870
+ const fullBail = !explicitPaths && bunTestOptionTokens(argv).some((arg) =>
871
+ arg === '--bail' || arg === '--bail=1');
872
+ const testCwd = effectiveTestCwd(argv, cwd);
873
+ const cdpPatterns = explicitPaths ? [] : (deps.deriveCdpPatterns ?? (() => deriveCdpTestPatterns({ cwd: testCwd })))();
874
+ const explicitFiles = classifyExplicitTestFiles(argv, testCwd);
875
+ const mockFiles = [...new Set([...(deps.deriveMockModuleFiles ?? (() => deriveMockModuleTestFiles({ cwd: testCwd })))(), ...explicitFiles.mockFiles])];
876
+ const allTestFiles = (explicitPaths || fullBail) && mockFiles.length > 0
877
+ ? [...new Set([...(deps.deriveTestFiles ?? (() => deriveTestFiles({ cwd: testCwd })))(), ...explicitFiles.files])]
878
+ : [];
879
+ let commands = planDeterministicTestCommands(argv, cdpPatterns, mockFiles, allTestFiles, testCwd);
880
+ const optionTokens = bunTestOptionTokens(argv);
881
+ // Bun writes these outputs to one caller-selected file. Separate children
882
+ // would overwrite each other, so preserve the caller's single-process run.
883
+ const sharedOutput = optionTokens.some((arg) => arg === '--reporter-outfile' || arg.startsWith('--reporter-outfile='))
884
+ || bunTestOptionValues(argv, '--coverage-reporter').some((value) => value.split(',').includes('lcov'))
885
+ || (optionTokens.includes('--update-timings') && bunTestOptionValues(argv, '--timings').length > 0);
886
+ const sharedOutputFallback = commands.length > 1 && sharedOutput;
887
+ if (sharedOutputFallback) {
888
+ report('[test-deterministic] 공유 파일 출력 옵션: mock.module 분리를 끄고 단일 Bun 시험 프로세스로 실행한다.');
889
+ commands = [['bun', 'test', ...cdpPatterns.flatMap((pattern) => ['--path-ignore-patterns', pattern]), ...argv]];
890
+ }
891
+ const namePatterns = [
892
+ ...bunTestOptionValues(argv, '--test-name-pattern'),
893
+ ...bunTestOptionValues(argv, '-t'),
894
+ ];
895
+ const captureNoMatch = commands.length > 1 && namePatterns.length > 0;
896
+ if (captureNoMatch && explicitPaths && !optionTokens.includes('--pass-with-no-tests')) {
897
+ // Internal --pass-with-no-tests may only neutralize a valid file whose
898
+ // test name missed. It must not turn a nonexistent file filter into pass.
899
+ const unmatched = partitionBunTestArguments(argv).selectors.find((selector) => {
900
+ const path = resolve(testCwd, selector);
901
+ let isDirectory = false;
902
+ try { isDirectory = statSync(path).isDirectory(); } catch { /* a Bun substring filter may still match */ }
903
+ return !allTestFiles.some((file) => {
904
+ const absolute = resolve(testCwd, file);
905
+ return isDirectory ? absolute.startsWith(path + sep)
906
+ : absolute === path || file.includes(selector.replace(/^\.\//, ''));
907
+ });
908
+ });
909
+ if (unmatched !== undefined) {
910
+ report(`[test-deterministic] test selector matched no files: ${unmatched}`);
911
+ return 1;
912
+ }
913
+ }
914
+ const hasShard = optionTokens.some((arg) => arg === '--shard' || arg.startsWith('--shard='));
915
+ const bailArgs = optionTokens.filter((arg) => arg === '--bail' || arg.startsWith('--bail='));
916
+ const stopOnFirstFailure = bailArgs.length > 0 && bailArgs.every((arg) => arg === '--bail' || arg === '--bail=1');
917
+ if (commands.length > 1 && (hasShard || (bailArgs.length > 0 && !stopOnFirstFailure))) {
918
+ report('[test-deterministic] --shard 또는 --bail=1 이외의 실패 한도는 여러 시험 프로세스에서 전역 의미를 보존할 수 없다.');
919
+ return 1;
920
+ }
460
921
  if (cdpPatterns.length > 0) {
461
922
  report(`[test-deterministic] CDP 시험 ${cdpPatterns.length}개 파일을 뺐다 — bun run test:cdp 로 돈다 (파생: requireCdpBase 직접 import ⊕ 포트 9333 직접 사용).`);
462
923
  }
924
+ if (!sharedOutputFallback && commands.some((cmd) => mockFiles.includes(cmd.at(-1) ?? ''))) {
925
+ report('[test-deterministic] mock.module 시험 파일을 각자 단독 프로세스로 실행한다.');
926
+ }
463
927
 
464
928
  let testRoot: string | undefined;
465
929
  let child: DirectChild | undefined;
@@ -480,30 +944,47 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
480
944
  : installShutdownSignalGate(deps.signalTarget ?? process, deps.onAbsorbSignal);
481
945
  const signalPromise = deps.waitForSignal ? deps.waitForSignal() : gate!.received;
482
946
 
483
- child = spawn({
484
- cmd: ['bun', 'test', ...ignoreArgs, ...argv],
485
- cwd: deps.cwd ?? process.cwd(),
486
- env,
487
- stdin: 'inherit',
488
- stdout: 'inherit',
489
- stderr: 'inherit',
490
- detached: true,
491
- });
947
+ let exitCode = 0;
948
+ let sawNameMatch = false;
949
+ let sawNameMiss = false;
950
+ for (const cmd of commands) {
951
+ // With a name filter, Bun gives an unmatched child exit 0 only with this
952
+ // flag. A real failed test still exits nonzero; aggregate matches below.
953
+ const childCmd = captureNoMatch && !cmd.includes('--pass-with-no-tests')
954
+ ? [cmd[0]!, cmd[1]!, '--pass-with-no-tests', ...cmd.slice(2)] : cmd;
955
+ child = spawn({
956
+ cmd: childCmd,
957
+ cwd,
958
+ env,
959
+ stdin: 'inherit',
960
+ stdout: 'inherit',
961
+ stderr: 'inherit',
962
+ detached: true,
963
+ captureNoMatch,
964
+ namePatterns,
965
+ });
492
966
 
493
- const childExit = child.exited.then(
494
- (code) => ({ kind: 'exit' as const, code }),
495
- (error) => ({ kind: 'error' as const, error }),
496
- );
497
- const outcome = await Promise.race([
498
- childExit,
499
- signalPromise.then((signal) => ({ kind: 'signal' as const, signal })),
500
- ]);
501
-
502
- if (outcome.kind === 'error') {
503
- throw outcome.error;
504
- }
967
+ const childExit = child.exited.then(
968
+ (code) => ({ kind: 'exit' as const, code }),
969
+ (error) => ({ kind: 'error' as const, error }),
970
+ );
971
+ const outcome = await Promise.race([
972
+ childExit,
973
+ signalPromise.then((signal) => ({ kind: 'signal' as const, signal })),
974
+ ]);
975
+
976
+ if (outcome.kind === 'error') throw outcome.error;
977
+ if (outcome.kind === 'exit') {
978
+ const evidence = await child.stderrEvidence;
979
+ const passedTests = evidence?.passSummary && evidence.runSummary;
980
+ if (captureNoMatch && evidence?.nameMissDiagnostic && !passedTests && outcome.code === 0) sawNameMiss = true;
981
+ else if (outcome.code === 0) sawNameMatch = true;
982
+ else exitCode = outcome.code ?? 1;
983
+ child = undefined;
984
+ if (exitCode !== 0 && stopOnFirstFailure) break;
985
+ continue;
986
+ }
505
987
 
506
- if (outcome.kind === 'signal') {
507
988
  await terminateDirectChild(child, outcome.signal, {
508
989
  graceMs: deps.childGraceMs,
509
990
  report,
@@ -525,10 +1006,12 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
525
1006
  }
526
1007
  return 1;
527
1008
  }
1009
+ if (sawNameMiss && !sawNameMatch && exitCode === 0
1010
+ && !optionTokens.includes('--pass-with-no-tests')) exitCode = 1;
528
1011
 
529
1012
  cleanupRoot();
530
1013
  checkConfig();
531
- return configChanged ? 1 : (outcome.code ?? 1);
1014
+ return configChanged ? 1 : exitCode;
532
1015
  } catch (error) {
533
1016
  if (child) {
534
1017
  try {
package/scripts/ux-sim.ts CHANGED
@@ -16,6 +16,7 @@
16
16
  // bun scripts/ux-sim.ts --fault cred-expiry # 결함 주입: grok 구독 만료 임박 × 갱신 결과 → 디스패처·발사 판정
17
17
  // bun scripts/ux-sim.ts --fault pod-death # 결함 주입: PARTIAL 줄을 남긴 Pod 가 SIGTERM 으로 지워짐 → 호스트 재방출 판정
18
18
  // bun scripts/ux-sim.ts --fault pool-cap # 결함 주입: --pod-pool 에 :N 이 빠진 풀 → 승인 판정(limitedBy)
19
+ // bun scripts/ux-sim.ts --fault sqlite-busy # 결함 주입: 실제 SQLite 잠금 → 로그 배치 버퍼·복구
19
20
  //
20
21
  // ⛔ 이 CLI 는 «실제 위젯 render()» 를 부른다 — 흉내 렌더러가 아니다.
21
22
  import { simRenderLogFrame } from '../src/ux-sim/render-frame.js';
@@ -71,7 +72,12 @@ if (argv.includes('--fault')) {
71
72
  console.log(renderPoolCap(poolCapSweep()).join('\n'));
72
73
  process.exit(0);
73
74
  }
74
- console.error(`⛔ 모르는 결함이다: ${fault ?? '(없음)'} — 아는 것: cred-expiry · pod-death · pool-cap`);
75
+ if (fault === 'sqlite-busy') {
76
+ const { simSqliteBusy, renderSqliteBusy } = await import('../src/ux-sim/fault-sqlite-busy.js');
77
+ console.log(renderSqliteBusy(simSqliteBusy()).join('\n'));
78
+ process.exit(0);
79
+ }
80
+ console.error(`⛔ 모르는 결함이다: ${fault ?? '(없음)'} — 아는 것: cred-expiry · pod-death · pool-cap · sqlite-busy`);
75
81
  process.exit(2);
76
82
  }
77
83