elanous 0.2.17 → 0.2.19
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/J5H7BvqgSI8DUjtRRcA_S/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{726.83532d69452ee24b.js → 1470.98a6ab61ccb4131c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9247-269e4464cc4b491c.js → 2375-e061bb24cd2b5cde.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{211-9c9090c59e2c5907.js → 2977-e38442e86289f1d3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → 2ea96958.e8c7e112de50b067.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +2 -0
- package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +5 -0
- package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +1 -0
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 4323.c5dfb6f503304775.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 4847.56f5bc454fbcc827.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 6062-73e68343a9814f6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{935.4ddaa99fc1d1126c.js → 7647.2fcd30d894d5ee30.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{f6231284-45726550aba08fca.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8870-9614a213b990b10b.js +25 -0
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/ae787e4c-e5b1ef00b2dbceca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-5b29d9b06ceadade.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-b70cd005d9169e25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +1 -0
- package/apps/pwa/out/_next/static/css/{e99e3cff94ced78a.css → 599369d853c61df7.css} +1 -1
- package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +1 -0
- package/apps/pwa/out/_next/static/css/d3427996da11e480.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -0
- package/apps/pwa/out/decisions/index.txt +30 -0
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +25 -24
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +24 -23
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +28 -27
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/ops/guardian.yaml +26 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/package.json +2 -1
- package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
- package/samples/workflows/templates/chat-driven-research.yaml +35 -0
- package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
- package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
- package/samples/workflows/templates/manual-quickstart.yaml +21 -0
- package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
- package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
- package/scripts/ci-public-export-import-gate.ts +7 -2
- package/scripts/ci-typecheck-changed.ts +21 -6
- package/scripts/coord-channel-watch.sh +2 -2
- package/scripts/discord-seat-probe.ts +6 -3
- package/scripts/docs/generate-cli-anatomy.ts +162 -0
- package/scripts/goals/authoring-metrics.ts +73 -0
- package/scripts/landing-heal/must-fix-match.ts +7 -7
- package/scripts/landing-heal/run-mission.ts +3 -2
- package/scripts/lib/pch-regress-map.ts +23 -0
- package/scripts/morning-report.ts +18 -4
- package/scripts/pch-regress.ts +88 -0
- package/scripts/public-export-test-run.ts +37 -2
- package/scripts/release-loop/checklist-gate-node.ts +13 -11
- package/scripts/release-loop/cut-branch.ts +2 -1
- package/scripts/release-loop/cutoff.ts +4 -2
- package/scripts/release-loop/docs-land-node.ts +8 -1
- package/scripts/release-loop/docs-node.ts +1 -1
- package/scripts/release-loop/gate-diff.ts +18 -0
- package/scripts/release-loop/gate-node.ts +436 -59
- package/scripts/release-loop/gate-partial.ts +106 -0
- package/scripts/release-loop/gate-timing.ts +120 -0
- package/scripts/release-loop/node-verdict.ts +2 -1
- package/scripts/release-loop/npm-publish-node.ts +11 -7
- package/scripts/release-loop/ops-upgrade-node.ts +8 -1
- package/scripts/release-loop/preflight.ts +246 -0
- package/scripts/release-loop/publish-node.ts +8 -1
- package/scripts/release-loop/release-readiness.ts +11 -6
- package/scripts/release-loop/release-version.ts +48 -0
- package/scripts/release-loop/resume-release.ts +174 -0
- package/scripts/release-loop/shard-plan.ts +65 -0
- package/scripts/release-loop/unattended-release.ts +45 -7
- package/scripts/release-loop/version-node.ts +145 -40
- package/scripts/rhythm/daily.ts +25 -4
- package/scripts/skills/public-check.ts +62 -12
- package/scripts/test-deterministic.ts +51 -33
- package/scripts/test-diet/effectiveness.ts +62 -1
- package/scripts/test-diet/lib.ts +1 -1
- package/scripts/test-diet/node.ts +8 -1
- package/scripts/video/scene-table.ts +159 -0
- package/src/acp/core-turn-bridge.ts +39 -2
- package/src/agent-mission/review-loop.ts +4 -3
- package/src/agent-substrate/pr-comment-meta.ts +20 -0
- package/src/autopilot/build/llm-conflict-merge.ts +28 -8
- package/src/away/presence.ts +40 -0
- package/src/away/release-watch.ts +255 -0
- package/src/briefing/brief-items.ts +86 -17
- package/src/chat/index.ts +2 -1
- package/src/chat/short-question.ts +3 -40
- package/src/cli/autopilot-cli.ts +30 -11
- package/src/cli/away-cli.ts +68 -0
- package/src/cli/brief-cli.ts +36 -2
- package/src/cli/freeze-cli.ts +3 -2
- package/src/cli/guardian-cli.ts +49 -0
- package/src/cli/intake-cli.ts +85 -7
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/notify-cli.ts +75 -0
- package/src/cli/pod-cli.ts +5 -1
- package/src/cli/provider-cli.ts +12 -2
- package/src/cli/pty-drive-cli.ts +14 -3
- package/src/cli/release-cli.ts +128 -19
- package/src/cli/setup-cli.ts +31 -1
- package/src/cli/tasks-cli.ts +403 -4
- package/src/context-bus/condense.ts +19 -3
- package/src/context-bus/context-day.ts +77 -24
- package/src/context-bus/context-first.ts +33 -0
- package/src/context-bus/context-now-public.ts +1 -1
- package/src/context-bus/context-now-surfaces.ts +16 -6
- package/src/context-bus/context-now.ts +97 -3
- package/src/core-turn/short-question.ts +100 -0
- package/src/dashboard/dashboard-status-lines.ts +24 -4
- package/src/dashboard/index.ts +50 -5
- package/src/dashboard/input/slash-executor.ts +6 -2
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +63 -36
- package/src/dashboard/slash-runtime/decide-slash.ts +60 -0
- package/src/dashboard/slash-runtime/help-from-registry.ts +21 -8
- package/src/dashboard/slash-runtime/help-groups.ts +44 -0
- package/src/dashboard/slash-runtime/model-choices.ts +125 -0
- package/src/discord-self-message.ts +20 -2
- package/src/discord-slash-wire.ts +1 -0
- package/src/domains/guardian-tick.ts +138 -0
- package/src/domains/outbound-alert.ts +244 -40
- package/src/domains/outbound-log-probe.ts +16 -1
- package/src/domains/release-tool.ts +2 -2
- package/src/domains/schedule-registry.ts +23 -0
- package/src/domains/telegram-kind-route.ts +39 -0
- package/src/domains/watch-default-scheduler.ts +95 -0
- package/src/esc-abort-gate.ts +44 -0
- package/src/execution-loop/gates.ts +11 -2
- package/src/flow-loop/split.ts +8 -4
- package/src/git-fs/worktree.ts +23 -3
- package/src/graph-runner/runner.ts +25 -5
- package/src/harness/harness-cli-command.ts +12 -2
- package/src/harness/harness-queue.ts +388 -33
- package/src/harness/harness-salvage-cli.ts +78 -0
- package/src/harness/harness-worktree-add.ts +68 -0
- package/src/harness/helper-scan.ts +10 -3
- package/src/harness/ledger-write-guard.ts +6 -16
- package/src/hq/hq.ts +6 -1
- package/src/hq/seats.ts +369 -0
- package/src/index.ts +185 -18
- package/src/instance/test-write-guard-spy-first.fixture.ts +13 -0
- package/src/instance/test-write-guard.ts +200 -0
- package/src/intake-plane/adopt-gate.ts +49 -0
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/collect-github.ts +57 -25
- package/src/intake-plane/digest.ts +39 -15
- package/src/intake-plane/discord-seat-work.ts +8 -2
- package/src/intake-plane/harness-target.ts +4 -3
- package/src/intake-plane/intake-sources.ts +1 -0
- package/src/intake-plane/items.ts +1 -0
- package/src/intake-plane/requirement-funnel.ts +316 -0
- package/src/knowledge/know-find.ts +10 -1
- package/src/loops/budget.ts +36 -0
- package/src/loops/orchestrator/degradation.ts +57 -0
- package/src/loops/orchestrator/finish-rate.ts +106 -4
- package/src/loops/orchestrator/grid-place.ts +11 -0
- package/src/loops/orchestrator/tick.ts +450 -54
- package/src/maturity/feature-maturity.ts +8 -5
- package/src/maturity/tui-slash-maturity.ts +1 -0
- package/src/mss/logging/log-store.ts +3 -0
- package/src/nexus/api/decisions-route.ts +11 -1
- package/src/nexus/api/http-server.ts +16 -5
- package/src/nexus/api/loop-edges.ts +352 -23
- package/src/nexus/api/ops-api.ts +96 -28
- package/src/nexus/api/ops-seats-sources.ts +2 -2
- package/src/nexus/api/workflow-templates.ts +3 -0
- package/src/nexus/index.ts +7 -0
- package/src/oauth/codex.ts +38 -0
- package/src/onboarding/first-chat-gate.ts +29 -0
- package/src/release-loop/checklist.ts +106 -6
- package/src/release-loop/feature-store.ts +64 -16
- package/src/release-loop/landing-freeze.ts +6 -4
- package/src/release-loop/light-rc.ts +173 -0
- package/src/release-loop/placement.ts +64 -20
- package/src/release-loop/rubric.ts +136 -0
- package/src/seat-dispatch/seat-ask.ts +79 -16
- package/src/seat-loop/seat-loop.ts +12 -6
- package/src/self-dev/ask-launch-flow.ts +24 -3
- package/src/self-dev/ask-path-candidates.ts +68 -0
- package/src/self-dev/dev-cli.ts +36 -0
- package/src/self-dev/dev-pipeline.ts +4 -0
- package/src/self-dev/draft-triage-rules.ts +1 -1
- package/src/self-dev/orchestrate-cli.ts +22 -0
- package/src/self-dev/orchestrate.ts +1 -0
- package/src/self-dev/pr-preview.ts +179 -0
- package/src/self-dev/round-pr-supersede.ts +244 -0
- package/src/self-dev/run-supervisor.ts +14 -0
- package/src/self-dev/shard-run.ts +23 -7
- package/src/self-implement/follow-up-goals.ts +2 -2
- package/src/self-implement/gate-cli.ts +29 -8
- package/src/self-implement/gate-remote.ts +427 -0
- package/src/self-implement/goal-author-clarification.ts +88 -13
- package/src/self-implement/goal-author.ts +8 -3
- package/src/self-implement/goal-clarification-escalation.ts +2 -0
- package/src/self-implement/l8-merge-queue.ts +29 -5
- package/src/self-implement/orchestrator.ts +61 -13
- package/src/self-implement/prior-draft-findings.ts +4 -3
- package/src/self-implement/salvage-branches.ts +254 -0
- package/src/skills/skill-feedback.ts +76 -0
- package/src/task-agent/actions.ts +101 -7
- package/src/task-agent/board.ts +132 -0
- package/src/task-agent/card-evidence.ts +203 -0
- package/src/task-agent/completion-evidence.ts +126 -0
- package/src/task-agent/judge.ts +49 -5
- package/src/task-agent/live-moves.ts +176 -0
- package/src/task-agent/mission.ts +449 -0
- package/src/task-agent/shadow.ts +258 -0
- package/src/task-agent/task-hand.ts +337 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +64 -4
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +10 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +142 -13
- package/src/task-orchestrator/surfaces/pod-pool.ts +13 -5
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +88 -20
- package/src/telegram-away-command.ts +38 -0
- package/src/telegram-commands.ts +11 -0
- package/src/telegram-loops-command.ts +1 -1
- package/src/telegram-report-log-probe.ts +21 -0
- package/src/telegram-report.ts +27 -3
- package/src/telegram.ts +5 -20
- package/src/tui-client/dashboard-session.ts +1 -0
- package/src/user-config.ts +196 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-aac7332576c4d6fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +0 -5
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
- package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +0 -2
- package/apps/pwa/out/_next/static/chunks/6877-fe94e76a5841ee9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +0 -25
- package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-7865aba0f4d2ee49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3143b07a817d1da9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-17ba086ef82d339b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-7eb661ff23af7da2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-6e5c3a867557298b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9a41d65355fc25b9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-5e77cd1802567c89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-41afec0b255ed2bb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4187666132fc98c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-09328d499162bae4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-29ff3a8f84f03b6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-80ec7401bc83f511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-fc7d86467291ae38.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-de43a186e5ba5683.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-0e2d6985fb42cd2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-13e4108999f58d13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e8296ee284aa1293.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-043a9111e74f6b24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-23a4601ceb7f452e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-10d958605a77b3bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a04260b8ccc968d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-7a1d85e121065cc8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-12916918988ac97e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d8a3347a9d0c3f4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-5a64cb5208c6a8fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-1407ec83ea64c1f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-1d824510df025d18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-7f49fe645d5132f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-27d71f131acd9320.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-424659bacfb8cdd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-fc2df261f8a2c7df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-5d4fef9ccd7d406b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +0 -1
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
- package/apps/pwa/out/_next/static/yRA5ur6DcPl0YHSRheH2O/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{yRA5ur6DcPl0YHSRheH2O → J5H7BvqgSI8DUjtRRcA_S}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { mkdtempSync, rmSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
export interface PreviewOptions {
|
|
6
|
+
target: string;
|
|
7
|
+
repoRoot: string;
|
|
8
|
+
scenario?: string;
|
|
9
|
+
/** Main-based combined preview: only existing config paths may be enabled. */
|
|
10
|
+
flags?: readonly string[];
|
|
11
|
+
commentPr?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface PreviewResult {
|
|
14
|
+
target: string;
|
|
15
|
+
head: string;
|
|
16
|
+
scenario: string;
|
|
17
|
+
status: 'passed' | 'friction';
|
|
18
|
+
report: string;
|
|
19
|
+
prNumber?: number;
|
|
20
|
+
commented: boolean;
|
|
21
|
+
}
|
|
22
|
+
export function readPreviewResult(directory: string): PreviewResult | null {
|
|
23
|
+
try {
|
|
24
|
+
const value = JSON.parse(readFileSync(join(directory, 'preview-result.json'), 'utf8')) as PreviewResult;
|
|
25
|
+
return value && (value.status === 'passed' || value.status === 'friction') && typeof value.head === 'string' ? value : null;
|
|
26
|
+
} catch { return null; }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface PreviewDeps {
|
|
30
|
+
run?: (command: string, args: readonly string[], cwd: string, timeoutMs?: number) => { status: number | null; stdout: string; stderr: string };
|
|
31
|
+
makeDir?: () => string;
|
|
32
|
+
dispose?: (directory: string) => void;
|
|
33
|
+
wait?: (ms: number) => Promise<void>;
|
|
34
|
+
/** Probe a test-mode daemon without injecting a global HTTP client into the command runner. */
|
|
35
|
+
probePwa?: (url: string) => Promise<boolean>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const SCENARIOS: Record<string, string> = {
|
|
39
|
+
S2: '/help', S3: '/status', S6: '/now', S7: '/term list', S8: '/model', S9: '/harness',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function previewScenarioInput(scenario: string): string {
|
|
43
|
+
const text = Object.hasOwn(SCENARIOS, scenario) ? SCENARIOS[scenario] : undefined;
|
|
44
|
+
if (!text) throw new Error(`TUI-COMFORT §4: unsupported scenario ${scenario}; available: ${Object.keys(SCENARIOS).join(', ')}`);
|
|
45
|
+
return text;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const defaultRun: NonNullable<PreviewDeps['run']> = (command, args, cwd, timeoutMs = 120_000) => {
|
|
49
|
+
const r = spawnSync(command, [...args], { cwd, encoding: 'utf8', timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024 });
|
|
50
|
+
return { status: r.status, stdout: r.stdout ?? '', stderr: r.stderr ?? String(r.error ?? '') };
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Build and exercise a detached head, never the caller's checkout. The report is posted only after measurement. */
|
|
54
|
+
export async function runPrPreview(options: PreviewOptions, deps: PreviewDeps = {}): Promise<PreviewResult> {
|
|
55
|
+
const run = deps.run ?? defaultRun;
|
|
56
|
+
const root = resolve(options.repoRoot);
|
|
57
|
+
const target = options.target.trim();
|
|
58
|
+
if (!target || target.startsWith('-') || /[\s\x00-\x1f]/u.test(target)) throw new Error('preview: invalid PR number or branch');
|
|
59
|
+
const isPr = /^[1-9]\d*$/u.test(target);
|
|
60
|
+
if (isPr && !Number.isSafeInteger(Number(target))) throw new Error('preview: invalid PR number');
|
|
61
|
+
if (/^\d+$/u.test(target) && !isPr) throw new Error('preview: invalid PR number');
|
|
62
|
+
if (options.commentPr !== undefined && (!Number.isSafeInteger(options.commentPr) || options.commentPr <= 0)) throw new Error('preview: invalid --comment-pr');
|
|
63
|
+
if (target === 'main' && options.commentPr === undefined) throw new Error('preview: main ⊕ flags requires --comment-pr <PR number> to publish the result');
|
|
64
|
+
if (isPr && options.commentPr !== undefined && options.commentPr !== Number(target)) throw new Error('preview: --comment-pr must match the target PR');
|
|
65
|
+
const flags = options.flags ?? [];
|
|
66
|
+
if (flags.length && target !== 'main') throw new Error('preview: --flag requires main (main ⊕ feature flags)');
|
|
67
|
+
if (flags.some((flag) => !/^[a-zA-Z][\w-]*(?:\.[a-zA-Z][\w-]*)+$/u.test(flag))) throw new Error('preview: --flag expects an existing dotted config path');
|
|
68
|
+
const scenario = options.scenario ?? 'S2';
|
|
69
|
+
const input = previewScenarioInput(scenario);
|
|
70
|
+
const check = (command: string, args: readonly string[], cwd: string, timeout?: number): string => {
|
|
71
|
+
const r = run(command, args, cwd, timeout);
|
|
72
|
+
if (r.status !== 0) throw new Error(`preview: ${command} ${args.join(' ')} failed: ${(r.stdout + '\n' + r.stderr).slice(-600)}`);
|
|
73
|
+
return r.stdout.trim();
|
|
74
|
+
};
|
|
75
|
+
let prNumber = isPr ? Number(target) : options.commentPr;
|
|
76
|
+
let branchPrHead: string | undefined;
|
|
77
|
+
let ref = target;
|
|
78
|
+
if (isPr) {
|
|
79
|
+
const pr = JSON.parse(check('gh', ['pr', 'view', target, '--json', 'number,headRefOid'], root)) as { number?: number; headRefOid?: string };
|
|
80
|
+
if (pr.number !== Number(target) || !/^[0-9a-f]{40}$/iu.test(pr.headRefOid ?? '')) throw new Error('preview: PR head is unavailable');
|
|
81
|
+
ref = pr.headRefOid!;
|
|
82
|
+
} else if (target !== 'main') {
|
|
83
|
+
check('git', ['check-ref-format', '--branch', target], root);
|
|
84
|
+
if (options.commentPr === undefined) {
|
|
85
|
+
const matching = JSON.parse(check('gh', ['pr', 'list', '--head', target, '--state', 'open', '--json', 'number,headRefOid'], root)) as Array<{ number?: number; headRefOid?: string }>;
|
|
86
|
+
if (matching.length !== 1 || !Number.isSafeInteger(matching[0]?.number)) throw new Error('preview: branch has no unique open PR; use --comment-pr <PR number>');
|
|
87
|
+
prNumber = matching[0]!.number;
|
|
88
|
+
branchPrHead = matching[0]!.headRefOid;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (isPr && run('git', ['cat-file', '-e', `${ref}^{commit}`], root).status !== 0) {
|
|
92
|
+
check('git', ['fetch', '--no-tags', 'origin', `pull/${target}/head`], root, 120_000);
|
|
93
|
+
const fetched = check('git', ['rev-parse', '--verify', 'FETCH_HEAD^{commit}'], root);
|
|
94
|
+
if (fetched !== ref) throw new Error('preview: PR head changed while fetching; retry');
|
|
95
|
+
}
|
|
96
|
+
const head = check('git', ['rev-parse', '--verify', `${ref}^{commit}`], root);
|
|
97
|
+
if (!/^[0-9a-f]{40}$/iu.test(head)) throw new Error('preview: unresolved commit');
|
|
98
|
+
if (target !== 'main' && !isPr) {
|
|
99
|
+
const pr = JSON.parse(check('gh', ['pr', 'view', String(prNumber), '--json', 'headRefOid,headRefName'], root)) as { headRefOid?: string; headRefName?: string };
|
|
100
|
+
if (pr.headRefName !== target || pr.headRefOid !== head || (branchPrHead !== undefined && branchPrHead !== head)) throw new Error('preview: branch does not match the target PR head; use the PR number');
|
|
101
|
+
}
|
|
102
|
+
const directory = (deps.makeDir ?? (() => {
|
|
103
|
+
const previews = join(root, '.elanous-test', 'previews');
|
|
104
|
+
mkdirSync(previews, { recursive: true });
|
|
105
|
+
return mkdtempSync(join(previews, 'preview-'));
|
|
106
|
+
}))();
|
|
107
|
+
const worktree = join(directory, 'checkout');
|
|
108
|
+
let added = false;
|
|
109
|
+
let heldPty: string | undefined;
|
|
110
|
+
let report = '';
|
|
111
|
+
const wait = deps.wait ?? ((ms: number) => new Promise<void>((done) => setTimeout(done, ms)));
|
|
112
|
+
try {
|
|
113
|
+
check('git', ['worktree', 'add', '--detach', worktree, head], root);
|
|
114
|
+
added = true;
|
|
115
|
+
check('bun', ['install', '--frozen-lockfile'], worktree, 300_000);
|
|
116
|
+
check('bun', ['install', '--frozen-lockfile'], join(worktree, 'apps/pwa'), 300_000);
|
|
117
|
+
for (const flag of flags) {
|
|
118
|
+
const existing = check('bun', ['bin/elanous.mjs', '--test', 'config', 'get', flag], worktree);
|
|
119
|
+
if (existing !== 'true' && existing !== 'false') throw new Error(`preview: ${flag} is not a boolean feature flag`);
|
|
120
|
+
check('bun', ['bin/elanous.mjs', '--test', 'config', 'set', flag, 'true'], worktree);
|
|
121
|
+
}
|
|
122
|
+
check('bun', ['bin/elanous.mjs', '--test', 'nexus', 'build', '--cwd', join(worktree, 'apps/pwa')], worktree, 300_000);
|
|
123
|
+
const pwa = check('bun', ['bin/elanous.mjs', '--test', 'nexus', 'run', '--test', '--tool-cwd', worktree], worktree, 120_000);
|
|
124
|
+
const port = /\bnexus\s+:(314[5-9]\d)\b/u.exec(pwa)?.[1];
|
|
125
|
+
const endpoint = port ? `http://127.0.0.1:${port}` : undefined;
|
|
126
|
+
if (!endpoint) throw new Error('preview: P-1 has no local test endpoint');
|
|
127
|
+
const probeOnce = deps.probePwa ?? (async (url: string) => {
|
|
128
|
+
try {
|
|
129
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(10_000) });
|
|
130
|
+
return response.ok && (await response.text()).includes('<');
|
|
131
|
+
} catch { return false; }
|
|
132
|
+
});
|
|
133
|
+
// `nexus run` detaches and returns before the daemon listens — its exit is not readiness. Poll.
|
|
134
|
+
let probe = false;
|
|
135
|
+
for (let attempt = 0; attempt < 60 && !probe; attempt++) {
|
|
136
|
+
probe = await probeOnce(`${endpoint}/app/`);
|
|
137
|
+
if (!probe) await wait(1000);
|
|
138
|
+
}
|
|
139
|
+
if (!probe) throw new Error('preview: P-1 did not serve the built PWA');
|
|
140
|
+
const version = check('bun', ['bin/elanous.mjs', '--version'], worktree);
|
|
141
|
+
if (!version) throw new Error('preview: B-1 version probe returned no output');
|
|
142
|
+
const held = check('bun', ['bin/elanous.mjs', '--test', 'dev', '--elanous', '--hold', '--cwd', worktree, '--isolated-root', join(directory, 'tui'), '--json'], worktree, 120_000);
|
|
143
|
+
const last = held.split('\n').reverse().find((line: string) => line.trim().startsWith('{'));
|
|
144
|
+
const pty = last ? JSON.parse(last) as { held?: boolean; ptyId?: string } : {};
|
|
145
|
+
if (pty.held !== true || !/^pty_[\w-]+$/u.test(pty.ptyId ?? '')) throw new Error('preview: B-1 held TUI did not return a PTY reference');
|
|
146
|
+
heldPty = pty.ptyId;
|
|
147
|
+
const snapshotArgs = ['bin/elanous.mjs', '--test', 'pty', 'snapshot', pty.ptyId!] as const;
|
|
148
|
+
const before = check('bun', snapshotArgs, worktree);
|
|
149
|
+
check('bun', ['bin/elanous.mjs', '--test', 'pty', 'text', pty.ptyId!, input, '--enter'], worktree);
|
|
150
|
+
// A constant sleep misses slow frames under load — poll until the screen answers (≤ 20s).
|
|
151
|
+
let snapshot = '';
|
|
152
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
153
|
+
await wait(1000);
|
|
154
|
+
snapshot = check('bun', snapshotArgs, worktree);
|
|
155
|
+
if (snapshot && snapshot !== 'ok' && snapshot !== before) break;
|
|
156
|
+
}
|
|
157
|
+
if (!snapshot || snapshot === 'ok' || snapshot === before) throw new Error('preview: inner PTY did not show a response to the scenario input within 20s');
|
|
158
|
+
report = `| Check | Observation |\n|---|---|\n| head | \`${head}\` |\n| B-1 version | ${version.slice(0, 120)} |\n| P-1 PWA | \`${endpoint}/app/\` served the built PWA |\n| TUI-COMFORT §4 ${scenario} | \`${input}\` → snapshot captured |\n\n<details><summary>Inner PTY snapshot</summary>\n\n\`\`\`text\n${snapshot.slice(-3000).replaceAll('```', "'''")}\n\`\`\`\n</details>`;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
report = `| Check | Friction |\n|---|---|\n| head | \`${head}\` |\n| TUI-COMFORT §4 ${scenario} | ${String(error).replaceAll('|', '\\|').slice(0, 700)} |`;
|
|
161
|
+
} finally {
|
|
162
|
+
if (added) {
|
|
163
|
+
if (heldPty) run('bun', ['bin/elanous.mjs', '--test', 'pty', 'key', heldPty, 'ctrl+c'], worktree, 30_000);
|
|
164
|
+
run('bun', ['bin/elanous.mjs', '--test', 'nexus', 'run', '--test', '--stop'], worktree, 30_000);
|
|
165
|
+
const removed = run('git', ['worktree', 'remove', '--force', worktree], root, 30_000);
|
|
166
|
+
if (removed.status !== 0) throw new Error(`preview: failed to remove isolated worktree: ${removed.stderr}`);
|
|
167
|
+
}
|
|
168
|
+
(deps.dispose ?? ((dir) => rmSync(dir, { recursive: true, force: true })))(directory);
|
|
169
|
+
}
|
|
170
|
+
const status = report.includes('| Friction |') ? 'friction' : 'passed';
|
|
171
|
+
const body = `## PR preview — ${target} (${status})\n\n${report}`;
|
|
172
|
+
if (prNumber === undefined) throw new Error('preview: cannot comment without a PR number');
|
|
173
|
+
check('gh', ['pr', 'comment', String(prNumber), '--body', body], root);
|
|
174
|
+
const result: PreviewResult = { target, head, scenario, status, report: body, prNumber, commented: true };
|
|
175
|
+
const resultDir = join(root, '.elanous-test');
|
|
176
|
+
mkdirSync(resultDir, { recursive: true });
|
|
177
|
+
writeFileSync(join(resultDir, 'preview-result.json'), JSON.stringify(result), { mode: 0o600 });
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// DRAFT-TRIAGE (TC 10-07) — a supervisor rework round of the same run and goal opens a new PR
|
|
2
|
+
// (new task id → new goal hash → new branch). Continuing the branch would mean pinning the shard
|
|
3
|
+
// identity and the Pod child's run suffix, so this closes the previous round's draft instead:
|
|
4
|
+
// label `elanous:superseded`, comment with the newer PR link, close. Same run = one tracker per
|
|
5
|
+
// supervised invocation; same goal = `resumeKey(feature)`, the key the resume path already uses.
|
|
6
|
+
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { debug } from '../debug/log.js';
|
|
9
|
+
import { PR_LABELS } from '../github/pr-labels.js';
|
|
10
|
+
import { PROTECTED_LABELS } from './draft-triage-rules.js';
|
|
11
|
+
import { resumeKey, type SelfDevJobResult } from './orchestrate.js';
|
|
12
|
+
|
|
13
|
+
const SUPERSEDED_LABEL = PR_LABELS.find((label) => label.name.endsWith(':superseded'))!.name;
|
|
14
|
+
|
|
15
|
+
export interface RoundPrRef {
|
|
16
|
+
readonly repository: string;
|
|
17
|
+
readonly number: number;
|
|
18
|
+
readonly url: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RoundPrView {
|
|
22
|
+
readonly state: string;
|
|
23
|
+
readonly isDraft: boolean;
|
|
24
|
+
readonly labels: readonly string[];
|
|
25
|
+
readonly mergedAt?: string | null;
|
|
26
|
+
/** Comment bodies — a link already posted (even by a write that reported failure) is not posted again.
|
|
27
|
+
* An adapter that cannot read them returns a null view, so nothing is written. */
|
|
28
|
+
readonly comments: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** gh seams. A null view or a false write means «not observed / not applied» — never a reason to close. */
|
|
32
|
+
export interface RoundPrAdapters {
|
|
33
|
+
view(pr: RoundPrRef): RoundPrView | null;
|
|
34
|
+
addLabel(pr: RoundPrRef, label: string): boolean;
|
|
35
|
+
comment(pr: RoundPrRef, body: string): boolean;
|
|
36
|
+
close(pr: RoundPrRef): boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type SupersedeOutcome =
|
|
40
|
+
| { kind: 'superseded'; round: number; prevPr: number; pr: number }
|
|
41
|
+
| { kind: 'kept'; round: number; prevPr: number; pr: number; reason: string };
|
|
42
|
+
|
|
43
|
+
type RoundPrOutcome = SupersedeOutcome | { kind: 'continued'; round: number; prevPr: number; pr: number };
|
|
44
|
+
|
|
45
|
+
export function roundPrRef(prUrl: string | undefined): RoundPrRef | null {
|
|
46
|
+
if (!prUrl) return null;
|
|
47
|
+
try {
|
|
48
|
+
const url = new URL(prUrl.trim());
|
|
49
|
+
const match = /^\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/.exec(url.pathname);
|
|
50
|
+
return url.protocol === 'https:' && url.hostname === 'github.com' && match
|
|
51
|
+
// GitHub owner/repo names are case-insensitive: one PR has one identity.
|
|
52
|
+
? { repository: `${match[1]}/${match[2]}`.toLowerCase(), number: Number(match[3]), url: `https://github.com/${match[1]}/${match[2]}/pull/${match[3]}` }
|
|
53
|
+
: null;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const prId = (pr: RoundPrRef): string => `${pr.repository}#${pr.number}`;
|
|
60
|
+
|
|
61
|
+
const commentMarker = (prev: RoundPrRef, next: RoundPrRef): string => `<!-- elanous:round-pr-superseded ${prId(prev)} -> ${prId(next)} -->`;
|
|
62
|
+
|
|
63
|
+
function roundPrSupersedeComment(input: { runId: string | null; round: number; prev?: RoundPrRef; next: RoundPrRef }): string {
|
|
64
|
+
return `${input.prev ? `${commentMarker(input.prev, input.next)}\n` : ''}Supervisor round ${input.round}${input.runId ? ` of ${input.runId}` : ''}: superseded by #${input.next.number} (${input.next.url}) — same run and goal, the rework round opened a new PR. Branch preserved.`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* One tracker per supervised run. `record(results, round)` after each round: for every goal whose newest PR
|
|
69
|
+
* differs from a PR an earlier round of this run opened, the earlier one is superseded exactly once —
|
|
70
|
+
* only while it is an open draft, unmerged, without a protected label, in the same repository.
|
|
71
|
+
*/
|
|
72
|
+
export function createRoundPrTracker(input: {
|
|
73
|
+
runId: string | null;
|
|
74
|
+
adapters: RoundPrAdapters;
|
|
75
|
+
observe?: (event: string, data: Record<string, unknown>) => void;
|
|
76
|
+
}) {
|
|
77
|
+
const byGoal = new Map<string, Array<{ ref: RoundPrRef; merged: boolean; round: number }>>();
|
|
78
|
+
const handled = new Set<string>();
|
|
79
|
+
// The link comment is written once per PR even when the close after it fails and is retried next round.
|
|
80
|
+
const commented = new Set<string>();
|
|
81
|
+
const emit = (event: string, data: Record<string, unknown>): void => {
|
|
82
|
+
try { (input.observe ?? ((e, d) => debug.log('self-dev.supervisor', e, d)))(event, data); } catch { /* observation must not interrupt supervision */ }
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const supersede = (prev: RoundPrRef, next: RoundPrRef, round: number): SupersedeOutcome => {
|
|
86
|
+
const base = { round, prevPr: prev.number, pr: next.number };
|
|
87
|
+
if (prev.repository !== next.repository) return { kind: 'kept', ...base, reason: 'other-repository' };
|
|
88
|
+
if (prId(prev) === prId(next)) return { kind: 'kept', ...base, reason: 'same-pr' };
|
|
89
|
+
let view: RoundPrView | null = null;
|
|
90
|
+
try { view = input.adapters.view(prev); } catch { view = null; }
|
|
91
|
+
if (!view) return { kind: 'kept', ...base, reason: 'view-unobserved' };
|
|
92
|
+
if (view.state !== 'OPEN' || view.mergedAt) return { kind: 'kept', ...base, reason: `state:${view.mergedAt ? 'MERGED' : view.state}` };
|
|
93
|
+
if (!view.isDraft) return { kind: 'kept', ...base, reason: 'not-draft' };
|
|
94
|
+
const held = view.labels.find((label) => PROTECTED_LABELS.has(label));
|
|
95
|
+
if (held) return { kind: 'kept', ...base, reason: `label:${held}` };
|
|
96
|
+
// Label first: a closed but unlabelled PR would fall outside every later sweep (draft-sweep rule).
|
|
97
|
+
let labelled = view.labels.includes(SUPERSEDED_LABEL);
|
|
98
|
+
if (!labelled) {
|
|
99
|
+
try { labelled = input.adapters.addLabel(prev, SUPERSEDED_LABEL); } catch { labelled = false; }
|
|
100
|
+
}
|
|
101
|
+
if (!labelled) return { kind: 'kept', ...base, reason: 'label-failed' };
|
|
102
|
+
const id = prId(prev);
|
|
103
|
+
if (view.comments.some((body) => body.includes(commentMarker(prev, next)))) commented.add(id);
|
|
104
|
+
if (!commented.has(id)) {
|
|
105
|
+
let ok = false;
|
|
106
|
+
try { ok = input.adapters.comment(prev, roundPrSupersedeComment({ runId: input.runId, round, prev, next })); } catch { ok = false; }
|
|
107
|
+
if (!ok) return { kind: 'kept', ...base, reason: 'comment-failed' };
|
|
108
|
+
commented.add(id);
|
|
109
|
+
}
|
|
110
|
+
let closed = false;
|
|
111
|
+
try { closed = input.adapters.close(prev); } catch { closed = false; }
|
|
112
|
+
return closed ? { kind: 'superseded', ...base } : { kind: 'kept', ...base, reason: 'close-failed' };
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/** Unfinished supersedes, keyed by the earlier PR; `next` stays the first successor so retries link the same PR. */
|
|
116
|
+
const pending = new Map<string, { prev: { ref: RoundPrRef; merged: boolean }; next: RoundPrRef }>();
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
/** Record one round's results, then (re)try every unfinished supersede — even when this round has no PR. */
|
|
120
|
+
record(results: readonly SelfDevJobResult[], round: number): RoundPrOutcome[] {
|
|
121
|
+
const outcomes: RoundPrOutcome[] = [];
|
|
122
|
+
for (const result of results) {
|
|
123
|
+
const ref = roundPrRef(result.prUrl);
|
|
124
|
+
if (!ref) continue;
|
|
125
|
+
const key = resumeKey(result.feature);
|
|
126
|
+
const seen = byGoal.get(key) ?? [];
|
|
127
|
+
// A PR already seen in this run is never a new successor — only a new PR is.
|
|
128
|
+
const known = seen.find((entry) => prId(entry.ref) === prId(ref));
|
|
129
|
+
if (known) {
|
|
130
|
+
known.merged ||= result.merged === true;
|
|
131
|
+
// A carried (skipped) shard is not a new round's work.
|
|
132
|
+
if (result.resumeDisposition !== 'skip' && known.round < round) {
|
|
133
|
+
outcomes.push({ kind: 'continued', round, prevPr: known.ref.number, pr: ref.number });
|
|
134
|
+
emit('round-pr-continued', { runId: input.runId, round, prevPr: known.ref.number, pr: ref.number });
|
|
135
|
+
}
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
// A carried result this run has not seen before was opened by an earlier process — not this run's PR,
|
|
139
|
+
// so it is neither recorded nor allowed to supersede anything.
|
|
140
|
+
if (result.resumeDisposition === 'skip') continue;
|
|
141
|
+
// Only PRs opened in an earlier round are superseded — never a sibling of the same round.
|
|
142
|
+
for (const prev of seen) {
|
|
143
|
+
const id = prId(prev.ref);
|
|
144
|
+
if (prev.round >= round || handled.has(id) || pending.has(id)) continue;
|
|
145
|
+
if (prev.merged) {
|
|
146
|
+
handled.add(id);
|
|
147
|
+
outcomes.push({ kind: 'kept', round, prevPr: prev.ref.number, pr: ref.number, reason: 'merged' });
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
pending.set(id, { prev, next: ref });
|
|
151
|
+
}
|
|
152
|
+
seen.push({ ref, merged: result.merged === true, round });
|
|
153
|
+
byGoal.set(key, seen);
|
|
154
|
+
}
|
|
155
|
+
for (const [id, { prev: entry, next }] of [...pending]) {
|
|
156
|
+
const prev = entry.ref;
|
|
157
|
+
// A result may have reported the earlier PR merged after it was queued — re-checked right before any write.
|
|
158
|
+
const outcome: SupersedeOutcome = entry.merged
|
|
159
|
+
? { kind: 'kept', round, prevPr: prev.number, pr: next.number, reason: 'merged' }
|
|
160
|
+
: supersede(prev, next, round);
|
|
161
|
+
outcomes.push(outcome);
|
|
162
|
+
// Retry only what was not observed or not applied; a decided keep/supersede is final for this run.
|
|
163
|
+
if (outcome.kind === 'superseded' || !/^(?:view-unobserved|label-failed|comment-failed|close-failed)$/.test(outcome.reason)) {
|
|
164
|
+
handled.add(id);
|
|
165
|
+
pending.delete(id);
|
|
166
|
+
}
|
|
167
|
+
if (outcome.kind === 'superseded') {
|
|
168
|
+
emit('round-pr-superseded', { runId: input.runId, round, prevPr: prev.number, pr: next.number });
|
|
169
|
+
} else {
|
|
170
|
+
emit('round-pr-supersede-kept', { runId: input.runId, round, prevPr: prev.number, pr: next.number, reason: outcome.reason });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return outcomes;
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function ghEnv(): Record<string, string> {
|
|
179
|
+
const env: Record<string, string> = {};
|
|
180
|
+
for (const [k, v] of Object.entries(process.env)) if (v !== undefined && !/^(https?|all|no)_proxy$/i.test(k)) env[k] = v;
|
|
181
|
+
return env;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Real gh adapters: proxy variables removed, App identity when available (never the personal login by choice). */
|
|
185
|
+
export async function defaultRoundPrAdapters(deps: {
|
|
186
|
+
appToken?: () => string | null;
|
|
187
|
+
run?: (args: string[], env: Record<string, string>) => { status: number | null; stdout: string };
|
|
188
|
+
inTest?: () => boolean;
|
|
189
|
+
} = {}): Promise<RoundPrAdapters> {
|
|
190
|
+
const env = ghEnv();
|
|
191
|
+
let tokenSource: () => string | null = () => null;
|
|
192
|
+
try {
|
|
193
|
+
tokenSource = deps.appToken ?? (await import('../auth/github-app-token.js')).githubAutomationToken;
|
|
194
|
+
} catch { /* no App credential module — every write refuses */ }
|
|
195
|
+
const freshToken = (): string | null => { try { return tokenSource(); } catch { return null; } };
|
|
196
|
+
const gh = (args: string[], token?: string | null) => {
|
|
197
|
+
const callEnv = { ...env };
|
|
198
|
+
if (token) callEnv.GH_TOKEN = token;
|
|
199
|
+
return deps.run
|
|
200
|
+
? deps.run(args, callEnv)
|
|
201
|
+
: spawnSync('gh', args, { encoding: 'utf8', env: callEnv as NodeJS.ProcessEnv, timeout: 30_000 });
|
|
202
|
+
};
|
|
203
|
+
// Writes (label · comment · close) run only as the automation App — the token is fetched per write, so a
|
|
204
|
+
// token that was missing or expired earlier in the run recovers; without it the write refuses and is retried.
|
|
205
|
+
// Reads may use the machine login.
|
|
206
|
+
const write = (op: string, pr: RoundPrRef, args: string[]): boolean => {
|
|
207
|
+
const token = freshToken();
|
|
208
|
+
if (!token) {
|
|
209
|
+
try { debug.log('self-dev.supervisor', 'round-pr-write-refused', { op, pr: pr.number, reason: 'no-app-token' }); } catch { /* fail-open */ }
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
return gh(args, token).status === 0;
|
|
213
|
+
};
|
|
214
|
+
const inTest = deps.inTest ?? ((): boolean => process.env.NODE_ENV === 'test');
|
|
215
|
+
return {
|
|
216
|
+
view(pr) {
|
|
217
|
+
if (inTest()) return null;
|
|
218
|
+
const out = gh(['pr', 'view', String(pr.number), '--repo', pr.repository, '--json', 'state,isDraft,labels,mergedAt,comments'], freshToken());
|
|
219
|
+
if (out.status !== 0) return null;
|
|
220
|
+
try {
|
|
221
|
+
const parsed = JSON.parse(out.stdout) as { state?: unknown; isDraft?: unknown; labels?: unknown; mergedAt?: unknown; comments?: unknown };
|
|
222
|
+
if (typeof parsed.state !== 'string' || typeof parsed.isDraft !== 'boolean' || !Array.isArray(parsed.labels)) return null;
|
|
223
|
+
const labels = parsed.labels.map((label) => (label as { name?: unknown })?.name);
|
|
224
|
+
if (labels.some((name) => typeof name !== 'string')) return null;
|
|
225
|
+
// Unverifiable comments make the whole view unobserved — no write may follow it.
|
|
226
|
+
if (!Array.isArray(parsed.comments)) return null;
|
|
227
|
+
const comments = parsed.comments.map((comment) => (comment as { body?: unknown })?.body);
|
|
228
|
+
if (comments.some((body) => typeof body !== 'string')) return null;
|
|
229
|
+
return { state: parsed.state, isDraft: parsed.isDraft, labels: labels as string[], mergedAt: typeof parsed.mergedAt === 'string' && parsed.mergedAt ? parsed.mergedAt : null, comments: comments as string[] };
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
addLabel(pr, label) {
|
|
235
|
+
return !inTest() && write('label', pr, ['pr', 'edit', String(pr.number), '--repo', pr.repository, '--add-label', label]);
|
|
236
|
+
},
|
|
237
|
+
comment(pr, body) {
|
|
238
|
+
return !inTest() && write('comment', pr, ['pr', 'comment', String(pr.number), '--repo', pr.repository, '--body', body]);
|
|
239
|
+
},
|
|
240
|
+
close(pr) {
|
|
241
|
+
return !inTest() && write('close', pr, ['pr', 'close', String(pr.number), '--repo', pr.repository]);
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
@@ -7,6 +7,7 @@ import { recordSelfDevRunSupervisorStop, selfDevRunsDir } from './run-store.js';
|
|
|
7
7
|
import { debug } from '../debug/log.js';
|
|
8
8
|
import { sweepFrozenMerges } from '../self-implement/frozen-merges.js';
|
|
9
9
|
import { routeSupervisorVerdict, type SupervisorNext } from './supervisor-verdict-edges.js';
|
|
10
|
+
import { recordTaskAgentShadowMove, type TaskAgentShadowInput } from '../task-agent/shadow.js';
|
|
10
11
|
|
|
11
12
|
export interface SupervisorRound {
|
|
12
13
|
round: number;
|
|
@@ -461,6 +462,11 @@ export interface SuperviseRunOptions {
|
|
|
461
462
|
runStore?: { runId?: string; operatorDir?: string; dir?: string; isolatedDir?: string };
|
|
462
463
|
observe?: (event: string, data: Record<string, unknown>) => void;
|
|
463
464
|
sweepPendingMerges?: typeof sweepFrozenMerges;
|
|
465
|
+
/**
|
|
466
|
+
* TASK-AGENT-SHADOW — 멈춤이 확정되면 판단부를 그림자로 부른다(기본 `recordTaskAgentShadowMove`).
|
|
467
|
+
* `false` 면 끈다. 던져도 런의 결말은 바뀌지 않는다(fail-soft).
|
|
468
|
+
*/
|
|
469
|
+
taskAgentShadow?: ((input: TaskAgentShadowInput) => unknown) | false;
|
|
464
470
|
observeDeliverables?: () => Promise<{
|
|
465
471
|
readonly deployFindings: ReadonlyMap<string, { target: string; findings?: readonly DeployVerifyFinding[] | readonly unknown[] }>;
|
|
466
472
|
readonly unmeasured: readonly unknown[];
|
|
@@ -614,6 +620,14 @@ export async function superviseRun(opts: SuperviseRunOptions): Promise<Superviso
|
|
|
614
620
|
});
|
|
615
621
|
} catch { /* fail-open */ }
|
|
616
622
|
}
|
|
623
|
+
if (decision.stopReason && opts.taskAgentShadow !== false) {
|
|
624
|
+
// TASK-AGENT-SHADOW — 판단부를 그림자로 부른다(실행 0). 어떤 오류도 런의 결말을 바꾸지 않는다.
|
|
625
|
+
try {
|
|
626
|
+
await (opts.taskAgentShadow ?? recordTaskAgentShadowMove)({ runId, stopReason: decision.stopReason, results });
|
|
627
|
+
} catch (error) {
|
|
628
|
+
try { debug.log('task-agent', 'shadow-move-failed', { runId, stopReason: decision.stopReason, error: String(error) }); } catch { /* fail-open */ }
|
|
629
|
+
}
|
|
630
|
+
}
|
|
617
631
|
return results;
|
|
618
632
|
}
|
|
619
633
|
|
|
@@ -11,6 +11,7 @@ export interface ShardRunResult {
|
|
|
11
11
|
shards: GateTestShard[];
|
|
12
12
|
attempts: GateShardAttempt[];
|
|
13
13
|
reason?: string;
|
|
14
|
+
measurementFailures?: number;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export type ShardProcess = (cwd: string, files: readonly string[]) => {
|
|
@@ -34,24 +35,39 @@ export function runShardedGateTests(
|
|
|
34
35
|
if (!Number.isSafeInteger(count) || count < 1) throw new Error('invalid --shards count');
|
|
35
36
|
const rss = new Map<string, number>();
|
|
36
37
|
const seconds = new Map<string, number>();
|
|
38
|
+
const failedMeasurements: string[] = [];
|
|
37
39
|
for (const file of [...new Set(files)]) {
|
|
38
40
|
let sample: ReturnType<ShardProcess>;
|
|
39
41
|
try {
|
|
40
42
|
sample = run(cwd, [file]);
|
|
41
|
-
} catch
|
|
42
|
-
|
|
43
|
+
} catch {
|
|
44
|
+
failedMeasurements.push(file);
|
|
45
|
+
continue;
|
|
43
46
|
}
|
|
44
|
-
if (![0, 1].includes(sample.exitCode ?? -1) || sample.signal || !sample.junit
|
|
45
|
-
|
|
47
|
+
if (![0, 1].includes(sample.exitCode ?? -1) || sample.signal || !sample.junit
|
|
48
|
+
|| !Number.isFinite(sample.rssMb) || sample.rssMb! <= 0
|
|
49
|
+
|| !Number.isFinite(sample.seconds) || sample.seconds! < 0) {
|
|
50
|
+
failedMeasurements.push(file);
|
|
51
|
+
continue;
|
|
46
52
|
}
|
|
47
53
|
rss.set(file, sample.rssMb!);
|
|
48
54
|
seconds.set(file, sample.seconds!);
|
|
49
55
|
}
|
|
56
|
+
if (failedMeasurements.length) {
|
|
57
|
+
if (!rss.size) return { aggregate: { status: 'unmeasured', retryShardIds: [] }, shards: [], attempts: [], reason: 'no valid file measurements', measurementFailures: failedMeasurements.length };
|
|
58
|
+
const averageRss = [...rss.values()].reduce((sum, value) => sum + value, 0) / rss.size;
|
|
59
|
+
const averageSeconds = [...seconds.values()].reduce((sum, value) => sum + value, 0) / seconds.size;
|
|
60
|
+
for (const file of failedMeasurements) {
|
|
61
|
+
rss.set(file, averageRss);
|
|
62
|
+
seconds.set(file, averageSeconds);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const measurementFailures = failedMeasurements.length || undefined;
|
|
50
66
|
let shards: GateTestShard[];
|
|
51
67
|
try {
|
|
52
68
|
shards = planGateTestShards(files, rss, seconds, budgetGiB, count);
|
|
53
69
|
} catch (error) {
|
|
54
|
-
return { aggregate: { status: 'unmeasured', retryShardIds: [] }, shards: [], attempts: [], reason: String(error) };
|
|
70
|
+
return { aggregate: { status: 'unmeasured', retryShardIds: [] }, shards: [], attempts: [], reason: String(error), ...(measurementFailures ? { measurementFailures } : {}) };
|
|
55
71
|
}
|
|
56
72
|
const attempts: GateShardAttempt[] = [];
|
|
57
73
|
const baseline = baselineWorktree(cwd, baseRef, (dir) => {
|
|
@@ -76,8 +92,8 @@ export function runShardedGateTests(
|
|
|
76
92
|
}
|
|
77
93
|
return aggregateGateTestShards(shards, attempts);
|
|
78
94
|
});
|
|
79
|
-
if ('retryShardIds' in baseline) return { aggregate: baseline, shards, attempts };
|
|
80
|
-
return { aggregate: { status: 'unmeasured', retryShardIds: shards.map((shard) => shard.id) }, shards, attempts, reason: baseline.log };
|
|
95
|
+
if ('retryShardIds' in baseline) return { aggregate: baseline, shards, attempts, ...(measurementFailures ? { measurementFailures } : {}) };
|
|
96
|
+
return { aggregate: { status: 'unmeasured', retryShardIds: shards.map((shard) => shard.id) }, shards, attempts, reason: baseline.log, ...(measurementFailures ? { measurementFailures } : {}) };
|
|
81
97
|
}
|
|
82
98
|
|
|
83
99
|
export function runBunShard(cwd: string, files: readonly string[]): ReturnType<ShardProcess> {
|
|
@@ -12,7 +12,7 @@ import { addHarnessQueue, type QueueSeat } from '../harness/harness-queue.js';
|
|
|
12
12
|
import { debug } from '../debug/log.js';
|
|
13
13
|
import { withFileLockSync } from '../storage/file-lock.js';
|
|
14
14
|
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
15
|
-
import {
|
|
15
|
+
import { checklistDevVersion, listChecklist, parseOwner } from '../release-loop/checklist.js';
|
|
16
16
|
import { getUserConfig } from '../user-config.js';
|
|
17
17
|
|
|
18
18
|
export const FOLLOW_UP_DEPTH_LIMIT = 3;
|
|
@@ -193,7 +193,7 @@ function defaultAppendDraft(stateRoot: string, record: FollowUpDraftRecord): voi
|
|
|
193
193
|
|
|
194
194
|
function defaultCellOwner(cellId: string): string | undefined {
|
|
195
195
|
try {
|
|
196
|
-
return listChecklist(
|
|
196
|
+
return listChecklist(checklistDevVersion()).items.find((item) => item.id === cellId)?.owner;
|
|
197
197
|
} catch {
|
|
198
198
|
return undefined;
|
|
199
199
|
}
|