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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import { cpSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, existsSync, statSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
5
5
|
import ts from 'typescript';
|
|
6
6
|
import { pickRange } from './lib.js';
|
|
7
7
|
|
|
@@ -119,6 +119,67 @@ export function fixedCountAssertions(testPath: string, root: string): Array<{ li
|
|
|
119
119
|
return assertions.sort((a, b) => a.line - b.line);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// Read module specifiers from syntax, not from comments or strings; re-exports count for non-test importers.
|
|
123
|
+
function directImports(file: string, includeReexports = false): string[] {
|
|
124
|
+
const source = ts.createSourceFile(file, readFileSync(file, 'utf8'), ts.ScriptTarget.Latest, true);
|
|
125
|
+
const imports: string[] = [];
|
|
126
|
+
const visit = (node: ts.Node): void => {
|
|
127
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) imports.push(node.moduleSpecifier.text);
|
|
128
|
+
if (includeReexports && ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier))
|
|
129
|
+
imports.push(node.moduleSpecifier.text);
|
|
130
|
+
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword
|
|
131
|
+
&& node.arguments.length === 1 && ts.isStringLiteral(node.arguments[0]!)) imports.push(node.arguments[0]!.text);
|
|
132
|
+
ts.forEachChild(node, visit);
|
|
133
|
+
};
|
|
134
|
+
visit(source);
|
|
135
|
+
return imports;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Directly imported repository modules with no direct, tracked, non-test importer. */
|
|
139
|
+
export function unreachableImports(testPath: string, root: string, deps: {
|
|
140
|
+
importers?: (modulePath: string, root: string) => string[];
|
|
141
|
+
} = {}): string[] {
|
|
142
|
+
const repo = realpathSync(resolve(root));
|
|
143
|
+
const inside = (path: string) => path !== repo && !relative(repo, path).startsWith(`..${sep}`)
|
|
144
|
+
&& relative(repo, path) !== '..' && !isAbsolute(relative(repo, path));
|
|
145
|
+
const validFile = (path: string) => inside(path) && existsSync(path) && realpathSync(path) === path && statSync(path).isFile();
|
|
146
|
+
const isTest = (path: string) => path.endsWith('.test.ts') || path === 'test' || path.startsWith(`test${sep}`);
|
|
147
|
+
const testFile = resolve(repo, testPath);
|
|
148
|
+
if (!validFile(testFile)) throw new Error('test path outside repository or symlinked');
|
|
149
|
+
const resolveImport = (from: string, specifier: string): string | undefined => {
|
|
150
|
+
if (!/^\.{1,2}\//.test(specifier)) return undefined;
|
|
151
|
+
const base = resolve(dirname(from), specifier);
|
|
152
|
+
const candidates = base.endsWith('.js')
|
|
153
|
+
? [base.replace(/\.js$/, '.ts'), base, `${base}.ts`, join(base, 'index.ts')]
|
|
154
|
+
: [base, `${base}.ts`, join(base, 'index.ts')];
|
|
155
|
+
return candidates.find(validFile);
|
|
156
|
+
};
|
|
157
|
+
const importers = deps.importers ?? ((modulePath: string, rootPath: string): string[] => {
|
|
158
|
+
const stem = basename(modulePath).replace(/\.[^.]+$/, '');
|
|
159
|
+
const needles = stem === 'index' ? [stem, basename(dirname(modulePath))] : [stem];
|
|
160
|
+
const matches = new Set<string>();
|
|
161
|
+
for (const needle of needles) {
|
|
162
|
+
const found = spawnSync('git', ['-C', rootPath, 'grep', '-l', '-F', '--', needle], { encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
|
|
163
|
+
if (found.error || (found.status !== 0 && found.status !== 1)) throw new Error(`importer lookup failed: ${found.error?.message ?? found.stderr}`);
|
|
164
|
+
for (const file of found.stdout.split('\n').filter(Boolean)) matches.add(file);
|
|
165
|
+
}
|
|
166
|
+
return [...matches];
|
|
167
|
+
});
|
|
168
|
+
const sources = new Set(directImports(testFile).map((specifier) => resolveImport(testFile, specifier))
|
|
169
|
+
.filter((path): path is string => path !== undefined && !isTest(relative(repo, path))));
|
|
170
|
+
const unreachable: string[] = [];
|
|
171
|
+
for (const source of sources) {
|
|
172
|
+
const modulePath = relative(repo, source);
|
|
173
|
+
const used = importers(modulePath, repo).some((candidate) => {
|
|
174
|
+
const path = resolve(repo, candidate);
|
|
175
|
+
if (!validFile(path) || isTest(relative(repo, path)) || path === source) return false;
|
|
176
|
+
return directImports(path, true).some((specifier) => resolveImport(path, specifier) === source);
|
|
177
|
+
});
|
|
178
|
+
if (!used) unreachable.push(modulePath.split(sep).join('/'));
|
|
179
|
+
}
|
|
180
|
+
return unreachable.sort();
|
|
181
|
+
}
|
|
182
|
+
|
|
122
183
|
export function mutationProbe(file: string, deps: { root: string; run?: (root: string, file: string) => number | null; baselineStable?: boolean }): Mutation {
|
|
123
184
|
if (deps.baselineStable === false) return 'n/a';
|
|
124
185
|
const root = realpathSync(resolve(deps.root));
|
package/scripts/test-diet/lib.ts
CHANGED
|
@@ -7,7 +7,7 @@ export const DEFAULT_COST_SECS = 10;
|
|
|
7
7
|
export const SLOW_SECS = 60;
|
|
8
8
|
export const HEAVY_MB = 2048;
|
|
9
9
|
|
|
10
|
-
export interface Effectiveness { flake: 'stable' | 'flaky' | 'failing'; mutation: 'caught' | 'survived' | 'n/a'; fixedCounts?: number }
|
|
10
|
+
export interface Effectiveness { flake: 'stable' | 'flaky' | 'failing'; mutation: 'caught' | 'survived' | 'n/a'; fixedCounts?: number; unreachable?: number }
|
|
11
11
|
export interface Measurement { file: string; secs: number; rssMb: number | null; rc: number | null; pass: number | null; fail: number | null; reason?: string; effectiveness?: Effectiveness }
|
|
12
12
|
export type Verdict = 'keep' | 'failing' | 'review';
|
|
13
13
|
export type Proposal = 'delete' | 'shrink' | 'rewrite-cheap' | 'move-out-of-gate' | 'investigate';
|
|
@@ -10,7 +10,7 @@ import { effectiveInstanceRoot } from '../../src/instance/resolve.js';
|
|
|
10
10
|
import { CardStore } from '../../src/task-cards/card-store.js';
|
|
11
11
|
import { GATE_NIGHTLY_AUDITS } from '../release-loop/gate-node.js';
|
|
12
12
|
import { appendLedger, costTable, judge, lastLedgerLine, ledgerPath, nightlyAuditLedgerPath, pickRange, propose, td1Dispositions, writeCardDraft, type LedgerLine, type Measurement, type Td1Disposition } from './lib.js';
|
|
13
|
-
import { fixedCountAssertions, flakeVerdict, sweepSlice, type Mutation, type TestRun } from './effectiveness.js';
|
|
13
|
+
import { fixedCountAssertions, flakeVerdict, sweepSlice, unreachableImports, type Mutation, type TestRun } from './effectiveness.js';
|
|
14
14
|
|
|
15
15
|
type Context = { graphId?: string; input: Record<string, unknown>; outputs: Record<string, Record<string, unknown> | null> };
|
|
16
16
|
function context(): Context {
|
|
@@ -123,6 +123,13 @@ function measure(ctx: Context, audit = false, effectiveness = false): number {
|
|
|
123
123
|
file: measurement.file, reason: error instanceof Error ? error.message : String(error),
|
|
124
124
|
});
|
|
125
125
|
}
|
|
126
|
+
try {
|
|
127
|
+
measurement.effectiveness.unreachable = unreachableImports(measurement.file, process.cwd()).length;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
debug.log('test-diet.effectiveness', 'unreachable-unreadable', {
|
|
130
|
+
file: measurement.file, reason: error instanceof Error ? error.message : String(error),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
126
133
|
debug.log('test-diet.effectiveness', 'judged', { file: measurement.file, ...measurement.effectiveness, secs: effect.secs });
|
|
127
134
|
}
|
|
128
135
|
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// ── 장면표(scenes.json) 한 벌에서 SRT·VO 대본을 뽑고, 공개 전 검사를 한다 ──────
|
|
3
|
+
//
|
|
4
|
+
// 계기(DEMO-VIDEO-1008 · 2026-10-07): 스토리보드 SRT 초안이 «한 줄 22자 · 두 줄»을
|
|
5
|
+
// 스스로 어겼다(한 블록 세 줄 · 24자 줄). 자막·VO·숫자 카드가 표 «셋»에 따로 있으면
|
|
6
|
+
// 한쪽만 고쳐지고 갈린다 ⇒ 장면표가 SSOT 이고 SRT·VO 는 «뽑아낸 것»이다.
|
|
7
|
+
//
|
|
8
|
+
// 사용:
|
|
9
|
+
// bun scripts/video/scene-table.ts check <scenes.json> 검사(exit 0 통과 · 1 걸림 · 3 입력 불량)
|
|
10
|
+
// bun scripts/video/scene-table.ts srt <scenes.json> 한국어 SRT 를 stdout 으로(검사 통과 때만)
|
|
11
|
+
// bun scripts/video/scene-table.ts vo <scenes.json> 영어 VO 대본(장면 번호 · 시각)을 stdout 으로
|
|
12
|
+
//
|
|
13
|
+
// ⛔ 숫자 자리표시 `{{KEY}}` 는 `measured.KEY.value` 로만 채운다 — 값이 없으면 «걸림»이다(지어낸 수치 금지).
|
|
14
|
+
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
|
|
17
|
+
export type Cue = { start: number; end: number; lines: string[] };
|
|
18
|
+
export type Scene = {
|
|
19
|
+
n: number; start: number; end: number; source: string[];
|
|
20
|
+
vo?: string | null; ko?: Cue[]; numberCard?: string; requestText?: string;
|
|
21
|
+
};
|
|
22
|
+
export type SceneTable = {
|
|
23
|
+
durationSec: number;
|
|
24
|
+
vo?: { maxWpm?: number };
|
|
25
|
+
subtitles?: { maxCharsPerLine?: number; maxLines?: number };
|
|
26
|
+
measured?: Record<string, { value?: number | string | null }>;
|
|
27
|
+
scenes: Scene[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// 공개 수위: 특허 보류 기전 · 경쟁사 이름 · 미래 약속 · 내부 표식 · 기기 경로.
|
|
31
|
+
// 단어 경계가 없는 한국어는 부분 일치로 잡는다.
|
|
32
|
+
export const BANNED: Array<{ re: RegExp; why: string }> = [
|
|
33
|
+
{ re: /시냅스|synap/i, why: '특허 보류 기전' },
|
|
34
|
+
{ re: /수면|\bsleep/i, why: '특허 보류 기전' },
|
|
35
|
+
{ re: /그라운딩\s*방아쇠|grounding\s+trigger/i, why: '특허 보류 기전' },
|
|
36
|
+
{ re: /선제\s*PTY|pre-?emptive\s+PTY/i, why: '특허 보류 기전' },
|
|
37
|
+
{ re: /면역|immun|암\s*억제|\bcancer\b/i, why: '특허 보류 기전' },
|
|
38
|
+
{ re: /openclaw|hermes|codex|cursor|devin|copilot|claude\s*code/i, why: '경쟁사 이름' },
|
|
39
|
+
{ re: /곧\s|\bsoon\b|\bcoming\b|available now|out now/i, why: '미래 약속' },
|
|
40
|
+
// 표식 문자는 코드 포인트로 적는다 — 이 파일 자체가 공개본 유출 검사에 걸리지 않게.
|
|
41
|
+
{ re: /[\u{1F451}\u{1F162}\u{1F163}\u{1F155}\u{1F15E}]|RFC-|HAND[O]FF-/u, why: '내부 표식' },
|
|
42
|
+
{ re: /\/Users\/|\.ts\.net|tail[0-9a-f]{6}/, why: '기기 경로·이름' },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const PLACEHOLDER = /\{\{([A-Z0-9_]+)\}\}/g;
|
|
46
|
+
|
|
47
|
+
export function fill(text: string, t: SceneTable, problems: string[], where: string): string {
|
|
48
|
+
return text.replace(PLACEHOLDER, (_m, key: string) => {
|
|
49
|
+
const v = t.measured?.[key]?.value;
|
|
50
|
+
if (v === undefined || v === null || v === '') {
|
|
51
|
+
problems.push(`${where}: {{${key}}} 의 실측값이 없다(measured.${key}.value)`);
|
|
52
|
+
return `{{${key}}}`;
|
|
53
|
+
}
|
|
54
|
+
return String(v);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const words = (s: string) => s.trim().split(/\s+/).filter(Boolean).length;
|
|
59
|
+
|
|
60
|
+
export function checkTable(t: SceneTable): string[] {
|
|
61
|
+
const p: string[] = [];
|
|
62
|
+
const maxChars = t.subtitles?.maxCharsPerLine ?? 22;
|
|
63
|
+
const maxLines = t.subtitles?.maxLines ?? 2;
|
|
64
|
+
const maxWpm = t.vo?.maxWpm ?? 150;
|
|
65
|
+
const scenes = [...t.scenes].sort((a, b) => a.start - b.start);
|
|
66
|
+
|
|
67
|
+
let cursor = 0;
|
|
68
|
+
let lastCueEnd = -1;
|
|
69
|
+
for (const s of scenes) {
|
|
70
|
+
const at = `장면 ${s.n}`;
|
|
71
|
+
if (s.start !== cursor) p.push(`${at}: 시작 ${s.start}s — 앞 장면 끝 ${cursor}s 와 이어지지 않는다`);
|
|
72
|
+
if (!(s.end > s.start)) p.push(`${at}: 끝(${s.end}s)이 시작(${s.start}s) 뒤가 아니다`);
|
|
73
|
+
cursor = s.end;
|
|
74
|
+
|
|
75
|
+
const pub: Array<[string, string]> = [];
|
|
76
|
+
if (s.vo) {
|
|
77
|
+
const wpm = (words(s.vo) / (s.end - s.start)) * 60;
|
|
78
|
+
if (wpm > maxWpm) p.push(`${at}: VO ${wpm.toFixed(0)} wpm > ${maxWpm}`);
|
|
79
|
+
pub.push([`${at} VO`, s.vo]);
|
|
80
|
+
}
|
|
81
|
+
if (s.numberCard) pub.push([`${at} 숫자 카드`, fill(s.numberCard, t, p, `${at} 숫자 카드`)]);
|
|
82
|
+
if (s.requestText) pub.push([`${at} 요청 문구`, s.requestText]);
|
|
83
|
+
|
|
84
|
+
for (const [i, c] of (s.ko ?? []).entries()) {
|
|
85
|
+
const cat = `${at} 자막 ${i + 1}`;
|
|
86
|
+
if (!(c.end > c.start)) p.push(`${cat}: 끝이 시작 뒤가 아니다`);
|
|
87
|
+
if (c.start < s.start || c.end > s.end) p.push(`${cat}: ${c.start}–${c.end}s 가 장면 ${s.start}–${s.end}s 밖으로 나간다`);
|
|
88
|
+
if (c.start < lastCueEnd) p.push(`${cat}: 앞 자막(끝 ${lastCueEnd}s)과 겹친다`);
|
|
89
|
+
lastCueEnd = c.end;
|
|
90
|
+
if (c.lines.length === 0 || c.lines.length > maxLines) p.push(`${cat}: ${c.lines.length}줄(1~${maxLines})`);
|
|
91
|
+
for (const raw of c.lines) {
|
|
92
|
+
const line = fill(raw, t, p, cat);
|
|
93
|
+
const len = [...line].length;
|
|
94
|
+
if (len > maxChars) p.push(`${cat}: «${line}» ${len}자 > ${maxChars}`);
|
|
95
|
+
pub.push([cat, line]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const [where, text] of pub) {
|
|
99
|
+
for (const b of BANNED) if (b.re.test(text)) p.push(`${where}: ${b.why} — «${text}»`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (cursor !== t.durationSec) p.push(`마지막 장면 끝 ${cursor}s ≠ durationSec ${t.durationSec}s`);
|
|
103
|
+
return p;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const ts = (sec: number) => {
|
|
107
|
+
const ms = Math.round(sec * 1000);
|
|
108
|
+
const h = Math.floor(ms / 3_600_000), m = Math.floor((ms % 3_600_000) / 60_000);
|
|
109
|
+
const s = Math.floor((ms % 60_000) / 1000), r = ms % 1000;
|
|
110
|
+
const z = (n: number, w = 2) => String(n).padStart(w, '0');
|
|
111
|
+
return `${z(h)}:${z(m)}:${z(s)},${z(r, 3)}`;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export function toSrt(t: SceneTable): string {
|
|
115
|
+
const scratch: string[] = [];
|
|
116
|
+
const cues = t.scenes.flatMap((s) => s.ko ?? []).sort((a, b) => a.start - b.start);
|
|
117
|
+
return cues
|
|
118
|
+
.map((c, i) => `${i + 1}\n${ts(c.start)} --> ${ts(c.end)}\n${c.lines.map((l) => fill(l, t, scratch, '')).join('\n')}\n`)
|
|
119
|
+
.join('\n');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function toVoScript(t: SceneTable): string {
|
|
123
|
+
return t.scenes
|
|
124
|
+
.filter((s) => s.vo)
|
|
125
|
+
.map((s) => `[${s.n}] ${ts(s.start).slice(3, 8)}–${ts(s.end).slice(3, 8)} ${s.vo}`)
|
|
126
|
+
.join('\n') + '\n';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function readTable(file: string): SceneTable {
|
|
130
|
+
try {
|
|
131
|
+
const t = JSON.parse(readFileSync(file, 'utf8')) as SceneTable;
|
|
132
|
+
if (!Array.isArray(t.scenes) || typeof t.durationSec !== 'number') throw new Error('scenes[] · durationSec 가 필요하다');
|
|
133
|
+
return t;
|
|
134
|
+
} catch (e) {
|
|
135
|
+
console.error(`⛔ 장면표를 못 읽었다: ${(e as Error).message}`);
|
|
136
|
+
process.exit(3);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (import.meta.main) {
|
|
141
|
+
const [cmd, file] = process.argv.slice(2);
|
|
142
|
+
if (!cmd || !file || !['check', 'srt', 'vo'].includes(cmd)) {
|
|
143
|
+
console.error('사용: bun scripts/video/scene-table.ts <check|srt|vo> <scenes.json>');
|
|
144
|
+
process.exit(3);
|
|
145
|
+
}
|
|
146
|
+
const t = readTable(file);
|
|
147
|
+
const problems = checkTable(t);
|
|
148
|
+
if (cmd === 'check') {
|
|
149
|
+
for (const x of problems) console.log(`⛔ ${x}`);
|
|
150
|
+
console.log(problems.length ? `걸림 ${problems.length}` : `✅ 통과 · 장면 ${t.scenes.length} · 자막 ${t.scenes.flatMap((s) => s.ko ?? []).length}`);
|
|
151
|
+
process.exit(problems.length ? 1 : 0);
|
|
152
|
+
}
|
|
153
|
+
if (problems.length) {
|
|
154
|
+
for (const x of problems) console.error(`⛔ ${x}`);
|
|
155
|
+
console.error(`걸림 ${problems.length} — 검사를 통과한 장면표에서만 ${cmd} 를 뽑는다`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
process.stdout.write(cmd === 'srt' ? toSrt(t) : toVoScript(t));
|
|
159
|
+
}
|
|
@@ -49,9 +49,24 @@ import type { AcpServerOptions, AcpTurnContext } from './server.js';
|
|
|
49
49
|
import { acpToolDenialResult } from './tool-approval.js';
|
|
50
50
|
import { readOriginSessionMeta } from './origin-session-meta.js';
|
|
51
51
|
import { isFactQuestion } from './fact-question.js';
|
|
52
|
+
import { classifyTuiChatIntent, type TuiChatRoute } from '../core-turn/short-question.js';
|
|
53
|
+
import { HARNESS_ROLE_ENV } from '../harness/harness-space.js';
|
|
52
54
|
|
|
53
55
|
const FACT_QUESTION_INSTRUCTION = '사실·최신 정보 질문이다. WebSearch 를 1~2회 쓰고 검색 결과(제목·스니펫·URL)로 답하라. 출처 링크를 붙여라. 셸·브라우저·파일 도구는 검색 결과로 부족할 때만. 도구 예산에 닿으면 지금까지 근거로 답하고 끝에 «더 찾아볼까요?»를 붙여라.';
|
|
54
56
|
|
|
57
|
+
// An allowlist, not a denylist: a new repository writer cannot slip into a quick turn.
|
|
58
|
+
const QUICK_TOOLS: Record<Exclude<TuiChatRoute, '코딩·구현'>, readonly string[]> = {
|
|
59
|
+
'조회': ['decisions_pending', 'ops_seats', 'ops_status', 'release_status', 'logs_query', 'context_now', 'memory_recall', 'Read'],
|
|
60
|
+
'설정 변경': ['DashboardConfigGet', 'DashboardConfigSet', 'LlmListAvailableModels', 'prompt_getRuntimeConfig', 'prompt_setRuntimeConfig', 'view_getConfig', 'view_applyRuntimeConfig', 'view_saveConfig'],
|
|
61
|
+
'조사': ['WebSearch', 'OmniSearch', 'Grep', 'Glob', 'Read', 'fact_check'],
|
|
62
|
+
'상시 일': ['schedule_manage', 'schedule_list', 'schedule_create', 'schedule_update', 'session_manage', 'ops_status'],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export function toolsForTuiRoute(tools: readonly LLMToolSpec[], route: Exclude<TuiChatRoute, '코딩·구현'>): LLMToolSpec[] {
|
|
66
|
+
const preferred = QUICK_TOOLS[route];
|
|
67
|
+
return preferred.flatMap((name) => tools.filter((tool) => tool.name === name));
|
|
68
|
+
}
|
|
69
|
+
|
|
55
70
|
export interface CoreTurnBridgeDeps {
|
|
56
71
|
/** Build the seed message list for this ACP prompt. Implementations
|
|
57
72
|
* typically concatenate per-session history with
|
|
@@ -110,6 +125,8 @@ export interface CoreTurnBridgeDeps {
|
|
|
110
125
|
* to 1ms so they don't spend real wall clock waiting for the
|
|
111
126
|
* flag to propagate. */
|
|
112
127
|
abortPollMs?: number;
|
|
128
|
+
/** Only the in-process dashboard session opts into TUI routing; daemon ACP stays unchanged. */
|
|
129
|
+
tuiRouting?: boolean;
|
|
113
130
|
}
|
|
114
131
|
|
|
115
132
|
/** Default abort-poll cadence. 50ms = 20 Hz: responsive without
|
|
@@ -192,12 +209,32 @@ export function bridgeCoreTurnToAcp(
|
|
|
192
209
|
promptMeta: turnCtx.promptMeta,
|
|
193
210
|
}),
|
|
194
211
|
);
|
|
195
|
-
const
|
|
212
|
+
const availableTools = await Promise.resolve(
|
|
196
213
|
deps.getTools({
|
|
197
214
|
sessionId: turnCtx.sessionId,
|
|
198
215
|
userText: turnCtx.userText,
|
|
199
216
|
}),
|
|
200
217
|
);
|
|
218
|
+
// Only a goal-driven harness child (explicit executor role — dev-harness · self-implement surfaces) keeps every
|
|
219
|
+
// turn on the goal loop. A human TUI inside a harness space (one-word `dev --elanous`, `--hold`) is routed (UX 10-07).
|
|
220
|
+
const harness = process.env[HARNESS_ROLE_ENV]?.trim() === 'executor';
|
|
221
|
+
const route = deps.tuiRouting && !harness
|
|
222
|
+
? classifyTuiChatIntent(turnCtx.userText, {
|
|
223
|
+
hasAttachments: turnCtx.promptBlocks?.some((block) => block.type !== 'text') ?? false,
|
|
224
|
+
})
|
|
225
|
+
: { route: '코딩·구현' as const, reason: harness ? 'harness' : 'legacy', goalLoop: true };
|
|
226
|
+
const tools = !route.goalLoop && route.route !== '코딩·구현'
|
|
227
|
+
? toolsForTuiRoute(availableTools, route.route)
|
|
228
|
+
: availableTools;
|
|
229
|
+
if (deps.tuiRouting) {
|
|
230
|
+
debug.log('chat.route', 'decided', { route: route.route, reason: route.reason, goalLoop: route.goalLoop });
|
|
231
|
+
// Only a quick route is announced: a coding turn's transcript stays exactly as before (preservation).
|
|
232
|
+
// Awaited so the route line precedes the answer; a failed write is observed, not fatal to the turn.
|
|
233
|
+
if (!route.goalLoop) {
|
|
234
|
+
try { await turnCtx.push(`라우팅: ${route.route}\n`); }
|
|
235
|
+
catch (error) { debug.log('chat.route', 'push-failed', { route: route.route, error: String(error) }); }
|
|
236
|
+
}
|
|
237
|
+
}
|
|
201
238
|
const modelOverride = deps.resolveModel?.({ sessionId: turnCtx.sessionId, userText: turnCtx.userText });
|
|
202
239
|
const factQuestion = isFactQuestion(turnCtx.userText);
|
|
203
240
|
const explicitMaxToolTurns = deps.resolveMaxToolTurns?.({ sessionId: turnCtx.sessionId });
|
|
@@ -233,7 +270,7 @@ export function bridgeCoreTurnToAcp(
|
|
|
233
270
|
const goalLoopCfg = getUserConfig().llm.goalLoop;
|
|
234
271
|
const turnModel = modelOverride ?? getUserConfig().llm.model;
|
|
235
272
|
const turnFamily = getModelFamily(turnModel);
|
|
236
|
-
const armGoalLoop = goalLoopCfg?.enabled === true;
|
|
273
|
+
const armGoalLoop = goalLoopCfg?.enabled === true && route.goalLoop;
|
|
237
274
|
if (armGoalLoop) {
|
|
238
275
|
// 관측 — 어느 family/모델에서 across-turn goal-loop 가 아밍됐나(전-family 개방 검증).
|
|
239
276
|
debug.log('goal.loop', 'arm', { family: turnFamily, model: turnModel });
|
|
@@ -22,7 +22,7 @@ import { budgetReviewerContext } from '../agent-substrate/pr-reviewer.js';
|
|
|
22
22
|
import { DEFAULT_REVIEW_BACKEND } from '../agent-substrate/acp-reviewer.js';
|
|
23
23
|
import { loadReviewerContext } from '../agent-substrate/self-review-cli.js';
|
|
24
24
|
import { createRepositoryReferencedFileReader, type ReferencedFileReader } from '../self-implement/goal-file-reader.js';
|
|
25
|
-
import { parse as parsePrCommentMeta } from '../agent-substrate/pr-comment-meta.js';
|
|
25
|
+
import { expandRecords, parse as parsePrCommentMeta } from '../agent-substrate/pr-comment-meta.js';
|
|
26
26
|
|
|
27
27
|
export type ReviewVerdict = 'ok' | 'reinforce' | 'reject' | 'ambiguous';
|
|
28
28
|
export type ReviewClassificationSource = 'llm' | 'llm-call-failure' | 'empty-review' | 'injected';
|
|
@@ -310,9 +310,10 @@ function analyzeAppliedReviewItems(comments: readonly NonNullable<LatestReviewSo
|
|
|
310
310
|
const items: string[] = [];
|
|
311
311
|
const seen = new Set<string>();
|
|
312
312
|
let headlineComments = 0;
|
|
313
|
-
const newestFirst =
|
|
313
|
+
const newestFirst = comments.flatMap((comment) => expandRecords(comment.body ?? '').map((body) => ({ ...comment, body })))
|
|
314
|
+
.sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? ''));
|
|
314
315
|
for (const comment of newestFirst) {
|
|
315
|
-
const lines =
|
|
316
|
+
const lines = comment.body.split('\n');
|
|
316
317
|
const headlineIndex = parsePrCommentMeta(lines[0] ?? '') ? 1 : 0;
|
|
317
318
|
const headline = lines[headlineIndex]?.trim() ?? '';
|
|
318
319
|
if (!headline.startsWith(REVIEW_REINFORCEMENT_HEADLINE) && !headline.startsWith(ACP_REWORK_HEADLINE)) continue;
|
|
@@ -97,3 +97,23 @@ export function parse(body: string): PrCommentMeta | null {
|
|
|
97
97
|
}
|
|
98
98
|
return parsed;
|
|
99
99
|
}
|
|
100
|
+
|
|
101
|
+
/** 상태 댓글의 details 에 쌓인 라운드 기록을 개별 댓글 본문으로 펼친다. */
|
|
102
|
+
export function expandRecords(body: string): string[] {
|
|
103
|
+
if (!/^<!-- elanous:run-status -->(?:\r?\n|$)/.test(body)) return [body];
|
|
104
|
+
|
|
105
|
+
const records: string[] = [];
|
|
106
|
+
for (const details of body.matchAll(/<details(?:\s[^>]*)?>([\s\S]*?)<\/details>/g)) {
|
|
107
|
+
const content = details[1];
|
|
108
|
+
const headers: Array<{ index: number; round?: number }> = [];
|
|
109
|
+
for (const header of content.matchAll(/^<!-- elanous-pr-comment v1 [^\r\n]* -->$/gm)) {
|
|
110
|
+
const meta = parse(header[0]);
|
|
111
|
+
if (meta) headers.push({ index: header.index, round: meta.round });
|
|
112
|
+
}
|
|
113
|
+
for (let i = 0; i < headers.length; i++) {
|
|
114
|
+
if (headers[i].round === undefined) continue;
|
|
115
|
+
records.push(content.slice(headers[i].index, headers[i + 1]?.index ?? content.length).replace(/(?:\r?\n)+$/, ''));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return records.length ? records : [body];
|
|
119
|
+
}
|
|
@@ -67,8 +67,10 @@ export interface LlmMergeOutcome {
|
|
|
67
67
|
testDeclarationUnmeasured?: string[];
|
|
68
68
|
/** 해결기 응답이 LLM 공급자 오류 문구였다 — 파일 내용이 아니다(🅢 2026-09-27 #21086: 세 파일이 통째로 그 문구가 됐다). */
|
|
69
69
|
providerFailure?: string[];
|
|
70
|
-
/**
|
|
71
|
-
reason?: 'conflict-input-too-large' | 'conflict-resolver-interrupted';
|
|
70
|
+
/** 충돌 미해결의 원인. 기존 두 원인에 마커 잔존·파일 읽기 실패를 추가한다. */
|
|
71
|
+
reason?: 'conflict-input-too-large' | 'conflict-resolver-interrupted' | 'conflict-markers-remain' | 'conflict-file-unreadable';
|
|
72
|
+
/** 해결이 멎은 충돌 파일(worktree 상대 경로). */
|
|
73
|
+
failedFile?: string;
|
|
72
74
|
inputChars?: number;
|
|
73
75
|
/** 병합 결과가 양쪽 판 중 작은 쪽의 절반 아래로 줄었다 — 통째로 날린 것으로 보고 해결 실패로 친다. */
|
|
74
76
|
sizeCollapse?: Array<{ file: string; ours: number; theirs: number; merged: number }>;
|
|
@@ -107,6 +109,20 @@ function addSizeChange(size: LlmMergeSizeChange, file: string, before: string, a
|
|
|
107
109
|
|
|
108
110
|
export function formatLlmMergeOutcome(outcome: LlmMergeOutcome): string {
|
|
109
111
|
const files = outcome.resolvedFiles?.length ? ` (LLM 종합 ${outcome.resolvedFiles.length}파일: ${outcome.resolvedFiles.join(', ')})` : '';
|
|
112
|
+
if (outcome.status === 'conflict-unresolved') {
|
|
113
|
+
// ⛔ reason·failedFile 은 «선택» 칸이다 — 공급자 오류·시험 선언 유실·크기 붕괴 출구는 계약상 reason 이 없다.
|
|
114
|
+
// 있는 칸만 싣고, 그 출구들은 자기 실제 결과(어느 판정이 멈췄나)를 싣는다 — 「사유 undefined」 금지.
|
|
115
|
+
const parts = [
|
|
116
|
+
...(outcome.reason ? [`사유 ${outcome.reason}`] : []),
|
|
117
|
+
...(outcome.failedFile ? [`파일 ${outcome.failedFile}`] : []),
|
|
118
|
+
...(outcome.providerFailure?.length ? [`공급자 오류 문구 ${outcome.providerFailure.join(', ')}`] : []),
|
|
119
|
+
...(outcome.testDeclarationLoss?.length
|
|
120
|
+
? [`시험 선언 유실 ${outcome.testDeclarationLoss.map((l) => `${l.file} ${l.ours}/${l.theirs}→${l.merged}`).join(', ')}`] : []),
|
|
121
|
+
...(outcome.sizeCollapse?.length
|
|
122
|
+
? [`크기 붕괴 ${outcome.sizeCollapse.map((c) => `${c.file} ${c.ours}/${c.theirs}→${c.merged}줄`).join(', ')}`] : []),
|
|
123
|
+
];
|
|
124
|
+
return `${outcome.status}${files}${parts.length ? `; ${parts.join(' · ')}` : ''}`;
|
|
125
|
+
}
|
|
110
126
|
if (outcome.status !== 'llm-resolved') return `${outcome.status}${files}`;
|
|
111
127
|
const size = outcome.sizeChange;
|
|
112
128
|
if (size === undefined) return `${outcome.status}${files}; 규모 변화: 못 쟀다`;
|
|
@@ -211,6 +227,10 @@ export async function mergeMainWithLlmResolve(
|
|
|
211
227
|
...outcome,
|
|
212
228
|
...(testDeclarationUnmeasured.length ? { testDeclarationUnmeasured } : {}),
|
|
213
229
|
});
|
|
230
|
+
const unresolved = (outcome: LlmMergeOutcome & { status: 'conflict-unresolved'; failedFile: string }): LlmMergeOutcome => {
|
|
231
|
+
debug.log('self-dev.merge', 'conflict-unresolved', { reason: outcome.reason, failedFile: outcome.failedFile, resolvedCount: resolvedFiles.length });
|
|
232
|
+
return measuredOutcome(outcome);
|
|
233
|
+
};
|
|
214
234
|
for (const f of files) {
|
|
215
235
|
const abs = join(worktreePath, f);
|
|
216
236
|
let conflicted: string;
|
|
@@ -219,7 +239,7 @@ export async function mergeMainWithLlmResolve(
|
|
|
219
239
|
conflicted = git.readFile(abs);
|
|
220
240
|
} catch {
|
|
221
241
|
git.abort(worktreePath);
|
|
222
|
-
return
|
|
242
|
+
return unresolved({ status: 'conflict-unresolved', reason: 'conflict-file-unreadable', failedFile: f, resolvedFiles });
|
|
223
243
|
}
|
|
224
244
|
try {
|
|
225
245
|
// release/next.md: concurrent landings each append a line — keep both without asking the LLM (REL7c).
|
|
@@ -227,7 +247,7 @@ export async function mergeMainWithLlmResolve(
|
|
|
227
247
|
merged = deterministic ?? await resolve(f, conflicted);
|
|
228
248
|
} catch (error) {
|
|
229
249
|
git.abort(worktreePath); // LLM 예외 → base 유지
|
|
230
|
-
return
|
|
250
|
+
return unresolved({ status: 'conflict-unresolved', resolvedFiles, failedFile: f,
|
|
231
251
|
...(error instanceof MergeConflictInputTooLarge
|
|
232
252
|
? { reason: 'conflict-input-too-large' as const, inputChars: error.inputChars }
|
|
233
253
|
: { reason: 'conflict-resolver-interrupted' as const }),
|
|
@@ -235,11 +255,11 @@ export async function mergeMainWithLlmResolve(
|
|
|
235
255
|
}
|
|
236
256
|
if (PROVIDER_FAILURE_TEXT.test(merged)) {
|
|
237
257
|
git.abort(worktreePath); // 해결기가 공급자 오류 문구를 «내용»으로 돌려줬다 → base 유지
|
|
238
|
-
return
|
|
258
|
+
return unresolved({ status: 'conflict-unresolved', resolvedFiles, failedFile: f, providerFailure: [f] });
|
|
239
259
|
}
|
|
240
260
|
if (hasConflictMarkers(merged)) {
|
|
241
261
|
git.abort(worktreePath); // LLM 이 종합 못 함(마커 잔존) → base 유지
|
|
242
|
-
return
|
|
262
|
+
return unresolved({ status: 'conflict-unresolved', reason: 'conflict-markers-remain', failedFile: f, resolvedFiles });
|
|
243
263
|
}
|
|
244
264
|
try {
|
|
245
265
|
const ours = countTestDeclarations(git.readIndexStage(worktreePath, 2, f));
|
|
@@ -248,7 +268,7 @@ export async function mergeMainWithLlmResolve(
|
|
|
248
268
|
if (mergedDeclarations < Math.min(ours, theirs)) {
|
|
249
269
|
const testDeclarationLoss = [{ file: f, ours, theirs, merged: mergedDeclarations }];
|
|
250
270
|
git.abort(worktreePath);
|
|
251
|
-
return
|
|
271
|
+
return unresolved({ status: 'conflict-unresolved', resolvedFiles, failedFile: f, testDeclarationLoss });
|
|
252
272
|
}
|
|
253
273
|
} catch {
|
|
254
274
|
testDeclarationUnmeasured.push(f);
|
|
@@ -260,7 +280,7 @@ export async function mergeMainWithLlmResolve(
|
|
|
260
280
|
const smaller = Math.min(ours, theirs);
|
|
261
281
|
if (smaller >= SIZE_COLLAPSE_MIN_LINES && mergedLines * 2 < smaller) {
|
|
262
282
|
git.abort(worktreePath); // 양쪽 판 모두보다 절반 넘게 작다 → 통째로 날렸다 → base 유지
|
|
263
|
-
return
|
|
283
|
+
return unresolved({ status: 'conflict-unresolved', resolvedFiles, failedFile: f, sizeCollapse: [{ file: f, ours, theirs, merged: mergedLines }] });
|
|
264
284
|
}
|
|
265
285
|
} catch {
|
|
266
286
|
/* 한쪽 판이 없는 충돌(삭제·추가) — 크기 비교 대상이 아니다 */
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
4
|
+
|
|
5
|
+
/** AWAY-MODE-1 — 대표가 자리를 비웠나. 한 파일(`<instance>/presence.json`)이 정본이고 CLI·텔레그램 /away 가 같이 쓴다. */
|
|
6
|
+
export interface Presence {
|
|
7
|
+
away: boolean;
|
|
8
|
+
/** 마지막으로 바꾼 시각(ISO). 한 번도 안 바꿨으면 없음. */
|
|
9
|
+
since?: string;
|
|
10
|
+
/** 누가 바꿨나 — cli · telegram · pwa. */
|
|
11
|
+
by?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function presencePath(root: string = effectiveInstanceRoot()): string {
|
|
15
|
+
return join(root, 'presence.json');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** 못 읽으면 «자리에 있음»으로 읽는다 — 외출 알림은 켠 사람만 받는다. */
|
|
19
|
+
export function readPresence(path: string = presencePath()): Presence {
|
|
20
|
+
try {
|
|
21
|
+
const value: unknown = JSON.parse(readFileSync(path, 'utf8'));
|
|
22
|
+
if (!value || typeof value !== 'object') return { away: false };
|
|
23
|
+
const rec = value as Partial<Presence>;
|
|
24
|
+
return {
|
|
25
|
+
away: rec.away === true,
|
|
26
|
+
...(typeof rec.since === 'string' ? { since: rec.since } : {}),
|
|
27
|
+
...(typeof rec.by === 'string' ? { by: rec.by } : {}),
|
|
28
|
+
};
|
|
29
|
+
} catch { return { away: false }; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function writePresence(away: boolean, by: string, opts: { path?: string; now?: Date } = {}): Presence {
|
|
33
|
+
const path = opts.path ?? presencePath();
|
|
34
|
+
const presence: Presence = { away, since: (opts.now ?? new Date()).toISOString(), by };
|
|
35
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
36
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
37
|
+
writeFileSync(tmp, `${JSON.stringify(presence)}\n`);
|
|
38
|
+
renameSync(tmp, path);
|
|
39
|
+
return presence;
|
|
40
|
+
}
|