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.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/{1454-37fd16f479f5554a.js → 1454-29d1d773d5996a49.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{599-a914cc0468f56143.js → 599-56f2c0a5cc4a7295.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-b12149c51f7d04b1.js → page-fa46d4588e8442c2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-305cd294e754906e.js → page-256ffd365d32e100.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-dea4b795741126fd.js → page-e0de78ae7b99f01c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-45889e7070d11126.js → page-4c741eb760f2f77c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-16a2e893f83ae4d0.js → page-e9ba98f5f1440a21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-56888bc744e08dac.js → page-2f6f303fa114ec24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-952c6ceec6d9de38.js → page-ced0bcfc6542b3fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-6d0202af568f4c63.js → page-bb1f05cd51c38944.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-4fb6fc34e20c07b7.js → page-f736324afa2117c1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-e9502921f4f01b1f.js → page-6f131b51193a36f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-b6169ea2f09cf287.js → not-found-435f5e2faf02bc01.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-6a14333d1b1b3967.js → page-497e0eedf20e7890.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-8c91a9a66c9ec90a.js → page-a8d28fdf995e16ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{page-3d3fc73dd187ed52.js → page-66a67eb19ebd976b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-9c04dac0831c2bbb.js → page-c71267e7681da982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-04de9ca6d52a3893.js → page-cb249e81ed339318.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-62527677bbc2fdc4.js → page-781cfc5114e097da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-9a2e09c4a311764c.js → page-478157214db6a023.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-bfeb35d69e8b24bb.js → page-86141f12cf090b7f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-89cb33bced824e4a.js → page-7256c73bb1c36751.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-6445ac4cbb8dd869.js → page-979d318a534e92da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-20162174b77f96cf.js → page-601fb77340e63c9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-05a4b2c9b91034aa.js → page-11bf9e5725a72c22.js} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +13 -13
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +13 -13
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +13 -13
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +13 -13
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +13 -13
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +13 -13
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +13 -13
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +13 -13
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +13 -13
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +13 -13
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +13 -13
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +13 -13
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +13 -13
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +13 -13
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +13 -13
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +13 -13
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +13 -13
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +13 -13
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +13 -13
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +13 -13
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +13 -13
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +13 -13
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +13 -13
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +13 -13
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +13 -13
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +13 -13
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +13 -13
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +13 -13
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +14 -14
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +13 -13
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +13 -13
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +13 -13
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +13 -13
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +13 -13
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +13 -13
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +13 -13
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +13 -13
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +13 -13
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +13 -13
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +13 -13
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +13 -13
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +13 -13
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +13 -13
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +13 -13
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +13 -13
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +13 -13
- package/graphs/implement-loop.yaml +38 -1
- package/graphs/steward/recipes.yaml +6 -0
- package/graphs/steward/release-day.yaml +23 -0
- package/graphs/steward/steward.yaml +9 -1
- package/graphs/work/code-pr.yaml +23 -0
- package/package.json +1 -1
- package/scripts/browser-observe-cycle.ts +76 -2
- package/scripts/ci-typecheck-changed.ts +119 -7
- package/scripts/coord-msg-bridge.ts +209 -0
- package/scripts/coord-post.sh +40 -7
- package/scripts/coord-requests-bridge.ts +10 -5
- package/scripts/detect-test-interference.ts +31 -1
- package/scripts/expose-rubric-suggest.ts +1 -1
- package/scripts/measure-tsc-affected-roots.ts +10 -1
- package/scripts/release-loop/docs-node.ts +51 -22
- package/scripts/release-loop/export-check-node.ts +8 -5
- package/scripts/release-loop/gate-node.ts +37 -4
- package/scripts/release-loop/node-verdict.ts +10 -3
- package/scripts/release-loop/publish-node.ts +3 -0
- package/scripts/release-loop/tui-node-checks.ts +5 -2
- package/scripts/skills/public-check.ts +5 -2
- package/scripts/test-deterministic.ts +520 -37
- package/scripts/ux-sim.ts +7 -1
- package/scripts/verify-kpack6-live.ts +230 -0
- package/src/acp/codex-plugins.ts +17 -0
- package/src/agent/task-notification.ts +32 -2
- package/src/agent-mission/in-agent-plugin.ts +8 -3
- package/src/agent-substrate/review-observation.ts +9 -2
- package/src/agent-substrate/self-review-cli.ts +62 -16
- package/src/boot/daemon-tools/self-implement.ts +72 -4
- package/src/briefing/brief-items.ts +151 -35
- package/src/budget/llm-cost.ts +30 -3
- package/src/budget/usage-outcomes.ts +32 -0
- package/src/claims/claims-ledger.ts +23 -0
- package/src/claims/claims-render.ts +22 -2
- package/src/cli/brief-cli.ts +131 -10
- package/src/cli/claims-cli.ts +24 -4
- package/src/cli/control-cli.ts +29 -0
- package/src/cli/doctor-cli.ts +90 -8
- package/src/cli/msg-cli.ts +177 -11
- package/src/cli/plugin-cli.ts +11 -3
- package/src/cli/pr-cli.ts +133 -18
- package/src/cli/pr-land-overlap-evidence.ts +18 -10
- package/src/cli/pr-land-refusal.ts +20 -0
- package/src/cli/release-cli.ts +34 -5
- package/src/cli/rfc-cli.ts +24 -0
- package/src/cli/seat-cli.ts +121 -0
- package/src/cli/self-utterances-cli.ts +140 -0
- package/src/cli/tailscale-serve.ts +5 -8
- package/src/cli/tasks-cli.ts +528 -31
- package/src/cli/usage-cli.ts +9 -4
- package/src/context-card/cell.ts +40 -0
- package/src/dashboard/boot-timing.ts +35 -0
- package/src/dashboard/first-screen-band.ts +63 -13
- package/src/dashboard/index.ts +94 -27
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
- package/src/dashboard/skill-detection-runtime.ts +24 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +6 -0
- package/src/debug/log.ts +1 -1
- package/src/domains/morning-report.ts +3 -1
- package/src/domains/release-tool.ts +29 -12
- package/src/domains/surface-events.ts +16 -0
- package/src/elanous-config-dir.ts +2 -2
- package/src/fleet/host-doctor.ts +73 -2
- package/src/github/pr-labels.ts +10 -4
- package/src/graph-runner/runner.ts +104 -15
- package/src/harness/harness-clean.ts +21 -3
- package/src/harness/harness-cli-command.ts +68 -10
- package/src/harness/harness-pod-dispatch.ts +11 -4
- package/src/harness/harness-stop.ts +11 -7
- package/src/hq/seats.ts +17 -3
- package/src/index.ts +69 -9
- package/src/instance/current.ts +2 -6
- package/src/instance/resolve.ts +5 -6
- package/src/intelligence-map/model-bench-scores.ts +102 -0
- package/src/llm/openai-sampling-fields.ts +89 -0
- package/src/llm/runtime-model-family.ts +61 -0
- package/src/llm/zai-coding-plan.ts +27 -1
- package/src/llm.ts +13 -89
- package/src/loops/orchestrator/inbox.ts +73 -0
- package/src/loops/orchestrator/resync-shadow.ts +100 -12
- package/src/loops/orchestrator/tick.ts +73 -2
- package/src/machines/machine-ledger.ts +31 -3
- package/src/maturity/cli-maturity.ts +13 -3
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/msg/msg-sse-server.ts +141 -0
- package/src/msg/msg-store.ts +149 -17
- package/src/ops/overview-collect.ts +3 -2
- package/src/ops/overview-owner.ts +29 -0
- package/src/patents/ledger.ts +128 -0
- package/src/plugin-hooks/shell-hook.ts +17 -2
- package/src/plugin-missions/evaluator-runner.ts +18 -2
- package/src/plugins/install/market-fetch.ts +32 -1
- package/src/plugins/install/plugin-install.ts +11 -1
- package/src/pod-lease/admission.ts +116 -41
- package/src/prompt-enhance/enhance.ts +11 -0
- package/src/release-loop/checklist.ts +52 -4
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/merged-pr-checklist.ts +24 -5
- package/src/release-loop/rubric.ts +70 -0
- package/src/rfc-registry/registry.ts +88 -0
- package/src/seat-dispatch/seat-request-ledger.ts +4 -0
- package/src/seat-loop/knowhow-lint.ts +233 -0
- package/src/seat-loop/seat-loop.ts +5 -1
- package/src/seat-loop/seat-pack.ts +16 -4
- package/src/seat-loop/traffic-signal.ts +7 -0
- package/src/self-dev/ask-launch-flow.ts +48 -8
- package/src/self-dev/ask-path-candidates.ts +69 -14
- package/src/self-dev/decompose-proposal.ts +43 -1
- package/src/self-dev/draft-sweep.ts +23 -14
- package/src/self-dev/graph-journey-nodes.ts +2 -0
- package/src/self-dev/launch-preflight-text.ts +5 -1
- package/src/self-dev/launch-preflight.ts +143 -10
- package/src/self-dev/orchestrate.ts +6 -2
- package/src/self-dev/run-supervisor.ts +35 -1
- package/src/self-implement/gate-cli.ts +6 -2
- package/src/self-implement/goal-author.ts +5 -28
- package/src/self-implement/goal-command-execution.ts +26 -0
- package/src/self-implement/goal-new-paths.ts +4 -5
- package/src/self-implement/graph-authority.ts +17 -3
- package/src/self-implement/graph-yaml.ts +15 -7
- package/src/self-implement/headless-elanous-driver.ts +16 -1
- package/src/self-implement/host-regate.ts +44 -9
- package/src/self-implement/orchestrator.ts +5 -11
- package/src/self-implement/pwa-import-graph.ts +194 -7
- package/src/self-implement/rework-policy.ts +80 -2
- package/src/self-implement/self-implement-runtime.ts +126 -21
- package/src/self-implement/speed-claims.ts +145 -6
- package/src/session/index.ts +14 -4
- package/src/session-runtime/index.ts +55 -11
- package/src/skills/tools/persistent-grounding.ts +59 -4
- package/src/skills/tools/shell.ts +79 -1
- package/src/steward/rejudge-tick.ts +12 -1
- package/src/steward/release-day.ts +158 -0
- package/src/steward/resync-tick.ts +123 -0
- package/src/task-agent/actions.ts +1 -0
- package/src/task-agent/adopt-pr.ts +140 -0
- package/src/task-agent/board.ts +11 -1
- package/src/task-agent/card-evidence.ts +37 -5
- package/src/task-agent/cover.ts +105 -19
- package/src/task-agent/failure-notify.ts +61 -0
- package/src/task-agent/green-sweep.ts +170 -0
- package/src/task-agent/land-worktree.ts +52 -5
- package/src/task-agent/live-move-history.ts +1 -0
- package/src/task-agent/live-moves.ts +56 -17
- package/src/task-agent/parent-host.ts +1 -1
- package/src/task-agent/postland-card.ts +139 -0
- package/src/task-agent/rejudge-on-head.ts +321 -25
- package/src/task-agent/remote-rejudge-bind.ts +62 -0
- package/src/task-agent/repair-branch-cas.ts +182 -0
- package/src/task-agent/repair-cycle.ts +275 -0
- package/src/task-agent/repair-next-move.ts +31 -0
- package/src/task-agent/repair-review.ts +24 -0
- package/src/task-agent/repair-worker.ts +87 -0
- package/src/task-agent/review-result-capture.ts +66 -2
- package/src/task-agent/shadow.ts +150 -29
- package/src/task-agent/task-hand.ts +133 -8
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +71 -12
- package/src/telegram.ts +30 -1
- package/src/tool-runtime/tool-search-runtime.ts +25 -0
- package/src/tui.ts +20 -1
- package/src/user-config.ts +11 -60
- package/src/ux-sim/fault-sqlite-busy.ts +50 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4e634c5b98fd4e93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b9cc8a10bc6078b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +0 -1
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_buildManifest.js +0 -0
- /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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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.
|
|
458
|
-
const
|
|
459
|
-
|
|
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
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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 :
|
|
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
|
-
|
|
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
|
|