elanous 0.2.4 → 0.2.6
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/README.md +7 -6
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 2984-977cfa339fcd65a5.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8142-011e1f474efb8b33.js → 3190-e9282753e7ebde85.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3995-bee512a7063036ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 4240-7153881b0b377306.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +25 -0
- package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3403.4bc97b4066945a94.js → 5329.8b7e4bca684b6eec.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5791-bb82743fa6299562.js → 763-7d0e1913b5074e98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7633.0654fe6ea259c1ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → 8d0d9faf-97887ff99713b84e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 91ff6dda-3b33d42d20657429.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +1 -0
- package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-e5bea36f183b25e6.js → webpack-37b8bf78a3c6eec5.js} +1 -1
- package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
- package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +29 -27
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +29 -27
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +29 -27
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +26 -24
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +26 -24
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +26 -24
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +29 -27
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +29 -27
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +29 -27
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/icons/elanous-16.png +0 -0
- package/apps/pwa/out/icons/elanous-180.png +0 -0
- package/apps/pwa/out/icons/elanous-192.png +0 -0
- package/apps/pwa/out/icons/elanous-32.png +0 -0
- package/apps/pwa/out/icons/elanous-512.png +0 -0
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +26 -24
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +29 -27
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +29 -27
- package/apps/pwa/out/manifest.webmanifest +5 -2
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +26 -24
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +26 -24
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +29 -27
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +29 -27
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +27 -25
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +29 -27
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +26 -24
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +29 -27
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +29 -27
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +29 -27
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +29 -27
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +29 -27
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +26 -24
- package/apps/pwa/out/sw-strategies.js +44 -0
- package/apps/pwa/out/sw.js +6 -44
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +29 -27
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +29 -27
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +29 -27
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +29 -27
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +29 -27
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +29 -27
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +29 -27
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +29 -27
- package/catalog/resources.yaml +6 -6
- package/docs/goal-context/steward-directives-2026-09-28.md +35 -37
- package/graphs/landing/landing-heal.yaml +1 -1
- package/graphs/release/recipes.yaml +11 -1
- package/graphs/release/release-loop.yaml +9 -2
- package/graphs/video/README.md +1 -0
- package/graphs/video/explainer-line.yaml +58 -0
- package/package.json +3 -1
- package/plugins/card-followup/README.md +33 -0
- package/plugins/card-followup/examples/card-sample.json +7 -0
- package/plugins/card-followup/graphs/card-followup.yaml +16 -0
- package/plugins/card-followup/graphs/recipes.yaml +12 -0
- package/plugins/card-followup/graphs/run-step.ts +172 -0
- package/plugins/card-followup/plugin.json +25 -0
- package/plugins/geo-check/README.md +13 -0
- package/plugins/geo-check/examples/brand-sample.json +8 -0
- package/plugins/geo-check/graphs/geo-check.yaml +20 -0
- package/plugins/geo-check/graphs/recipes.yaml +18 -0
- package/plugins/geo-check/graphs/run-step.ts +177 -0
- package/plugins/geo-check/plugin.json +24 -0
- package/plugins/job-coach/.codex-plugin/plugin.json +18 -1
- package/plugins/job-coach/README.md +42 -0
- package/plugins/job-coach/connectors/ncs/server.ts +4 -3
- package/plugins/job-coach/graphs/gap-judge.ts +44 -0
- package/plugins/job-coach/graphs/recipes.yaml +27 -0
- package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
- package/plugins/job-coach/graphs/run-step.ts +211 -17
- package/plugins/job-coach/plugin.json +16 -3
- package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
- package/plugins/job-coach/skills/interview-to-profile/SKILL.md +18 -1
- package/scripts/backup/elanous-backup.sh +4 -1
- package/scripts/build-docs-site.ts +4 -3
- package/scripts/coord-channel-watch.sh +23 -4
- package/scripts/coord-post.sh +49 -12
- package/scripts/coord-tracks.json +5 -5
- package/scripts/daemon-port-baseline.txt +1 -2
- package/scripts/eval-bot-vs-grok.ts +79 -0
- package/scripts/eval-grok-live-baseline.ts +36 -0
- package/scripts/fixtures/front-route-corpus.sample.json +17 -0
- package/scripts/fleet/node-setup.sh +46 -7
- package/scripts/landing-heal/must-fix-match.ts +26 -0
- package/scripts/landing-heal/run-mission.ts +114 -20
- package/scripts/lib/android-test-device.ts +32 -0
- package/scripts/lib/front-route-measurement.ts +131 -0
- package/scripts/lib/tui-sim-keys.ts +42 -0
- package/scripts/measure-front-route-classifier.ts +56 -0
- package/scripts/media/ocr-leak-scan.sh +21 -0
- package/scripts/public-export.ts +2 -3
- package/scripts/public-import-pr.ts +80 -6
- package/scripts/publish-internal-dist.ts +2 -3
- package/scripts/red-sweep-diff.ts +1 -1
- package/scripts/release-loop/auto-approve-node.ts +72 -0
- package/scripts/release-loop/checklist-gate-node.ts +35 -0
- package/scripts/release-loop/gate-diff.ts +29 -0
- package/scripts/release-loop/gate-node.ts +366 -45
- package/scripts/release-loop/publish-node.ts +7 -3
- package/scripts/release-loop/release-docs.ts +20 -13
- package/scripts/release-loop/shard-plan.ts +47 -0
- package/scripts/release-loop/test-durations.ts +63 -0
- package/scripts/release-loop/upgrade-node.ts +3 -2
- package/scripts/test-audit-coverage-per-file.sh +16 -0
- package/scripts/test-audit-meaningless.ts +88 -0
- package/scripts/test-audit-overlap-and-weak.ts +73 -0
- package/scripts/test-deterministic.ts +27 -5
- package/scripts/tui-sim.ts +59 -14
- package/scripts/ux/bump-installed.sh +20 -0
- package/scripts/ux/lib/cdp.ts +72 -0
- package/scripts/ux/record-page.ts +53 -0
- package/skills/explainer-video/SKILL.md +117 -0
- package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
- package/skills/explainer-video/engine/build.mjs +431 -0
- package/skills/explainer-video/engine/reel.mjs +128 -0
- package/skills/explainer-video/engine/reel.sh +8 -0
- package/skills/explainer-video/engine/vo.ts +79 -0
- package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
- package/src/agent/nested-agent-env.ts +1 -0
- package/src/agent-mission/driver.ts +95 -16
- package/src/agent-mission/mission-cli.ts +6 -0
- package/src/agent-mission/resource-ladder.ts +73 -25
- package/src/auth/acp-token.ts +16 -0
- package/src/auth/github-app-token.ts +112 -0
- package/src/autopilot/gh-rest-fallback.ts +177 -0
- package/src/autopilot/mission-origin.ts +20 -4
- package/src/autopilot/mission-phase-track.ts +11 -1
- package/src/autopilot/pr-manager.ts +51 -4
- package/src/autopilot/track-agent.ts +3 -3
- package/src/autopilot/track-courier.ts +2 -2
- package/src/boot/acp-server.ts +2 -16
- package/src/boot/daemon-tools/web-search.ts +2 -1
- package/src/boot/ws-bridge.ts +11 -0
- package/src/chat/index.ts +37 -8
- package/src/chat/log-scroll-key.ts +12 -0
- package/src/chat/submit-trace.ts +30 -0
- package/src/cli/agent-cli.ts +7 -2
- package/src/cli/decisions-cli.ts +94 -0
- package/src/cli/doctor-cli.ts +40 -54
- package/src/cli/doctor-readiness.ts +16 -24
- package/src/cli/intake-cli.ts +1 -1
- package/src/cli/mcp-call.ts +9 -0
- package/src/cli/mcp-gateway-cli.ts +7 -3
- package/src/cli/nexus-ios-bind.ts +25 -8
- package/src/cli/phone-link.ts +117 -0
- package/src/cli/plugin-cli.ts +136 -11
- package/src/cli/pr-lineage.ts +2 -1
- package/src/cli/private-ledger-commands.ts +31 -0
- package/src/cli/pwa-start.ts +3 -0
- package/src/cli/release-cli.ts +97 -7
- package/src/cli/self-update.ts +7 -4
- package/src/cli/start-cli.ts +33 -14
- package/src/dashboard/acp-boot.ts +3 -0
- package/src/dashboard/index.ts +62 -13
- package/src/dashboard/input/no-provider-reply.ts +13 -0
- package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
- package/src/dashboard/stale-install-check.ts +77 -0
- package/src/decisions/decision-ledger.ts +404 -0
- package/src/domains/outbound-alert.ts +13 -7
- package/src/domains/telegram-kind-route.ts +21 -0
- package/src/execution-loop/dispatch-task.ts +139 -0
- package/src/execution-loop/gate-decision-summary.ts +29 -0
- package/src/execution-loop/gates.ts +197 -0
- package/src/field/field-media.ts +245 -0
- package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
- package/src/graph-kinds/plugin-nodes.ts +10 -2
- package/src/graph-runner/runner.ts +35 -5
- package/src/grounding/token.ts +12 -6
- package/src/harness/harness-cli-command.ts +68 -9
- package/src/harness/harness-pod-dispatch.ts +8 -2
- package/src/harness/harness-stop.ts +147 -18
- package/src/harness/self-send-decide.ts +26 -17
- package/src/harness/self-send-target.ts +22 -1
- package/src/harness/worktree-branch-prefix.ts +4 -3
- package/src/index.ts +50 -13
- package/src/instance/resolve.ts +6 -0
- package/src/intake-plane/collect-telegram-saved.ts +5 -0
- package/src/intake-plane/digest.ts +11 -2
- package/src/live/detail-switch.ts +8 -3
- package/src/llm/usable-llm.ts +32 -0
- package/src/loops/registry.ts +27 -5
- package/src/market/publish.ts +59 -3
- package/src/native-tool-catalog.ts +2 -2
- package/src/nexus/api/field-uploads.ts +143 -0
- package/src/nexus/api/graph-approvals.ts +174 -0
- package/src/nexus/api/harness-api.ts +219 -5
- package/src/nexus/api/http-server.ts +69 -6
- package/src/nexus/api/log-fabric.ts +10 -1
- package/src/nexus/api/mcp-oauth-hint.ts +10 -9
- package/src/nexus/api/merge-approvals.ts +2 -1
- package/src/nexus/api/meta-api.ts +14 -0
- package/src/nexus/api/plugins-credentials-api.ts +47 -0
- package/src/nexus/api/plugins-market.ts +87 -3
- package/src/nexus/api/pod-credential-api.ts +68 -14
- package/src/nexus/api/public-routes.ts +7 -0
- package/src/nexus/api/trace.ts +57 -6
- package/src/nexus/boot/register-mcp-clients.ts +99 -14
- package/src/nexus/index.ts +33 -32
- package/src/nexus/outbound/router.ts +1 -1
- package/src/notify/llm-budget-alert.ts +119 -0
- package/src/onboarding.ts +25 -2
- package/src/plugins/core/host.ts +14 -0
- package/src/plugins/install/market-fetch.ts +4 -3
- package/src/plugins/install/plugin-credentials.ts +106 -0
- package/src/plugins/install/plugin-install.ts +163 -32
- package/src/plugins/maker/plugin-maker.ts +208 -0
- package/src/prompt-library/universal-preamble.ts +8 -2
- package/src/relay/channel.ts +184 -0
- package/src/relay/mux.ts +182 -0
- package/src/relay/test-vectors.json +29 -0
- package/src/release-loop/checklist.ts +217 -0
- package/src/release-loop/release-note.ts +6 -3
- package/src/seat-address/seat-address.ts +34 -0
- package/src/self-dev/dev-cli.ts +40 -20
- package/src/self-dev/dev-pipeline.ts +77 -1
- package/src/self-implement/gate-baseline.ts +3 -1
- package/src/self-implement/goal-author.ts +18 -1
- package/src/self-implement/host-regate.ts +6 -2
- package/src/self-implement/orchestrator.ts +7 -2
- package/src/self-implement/run-ledger-compact.ts +1 -1
- package/src/self-implement/seams.ts +4 -2
- package/src/session/index.ts +23 -10
- package/src/skills/tools/dev-harness.ts +42 -0
- package/src/steward/steward-cards.ts +45 -0
- package/src/steward/triage.ts +50 -14
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +61 -3
- package/src/task-orchestrator/surfaces/pod-bun-cache.ts +18 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +36 -6
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +39 -9
- package/src/task-orchestrator/surfaces/pod-source-bundle.ts +16 -1
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +51 -13
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -28
- package/src/telegram-report.ts +12 -6
- package/src/telegram.ts +108 -1
- package/src/tui-client/acp-transport-ws-client.ts +72 -5
- package/src/tui.ts +3 -2
- package/src/user-config.ts +32 -2
- package/src/util/spawn-sync-output.ts +15 -0
- package/src/version/code-revision.ts +1 -1
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/explainer.ts +213 -0
- package/src/video-pipeline/walk-line.ts +3 -2
- package/src/web-search/free.ts +77 -0
- package/src/web-search/index.ts +2 -0
- package/src/workflow-runtime/deps-bridge.ts +4 -3
- package/src/workflow-runtime/plugin-kind-node.ts +32 -4
- package/src/workflow-runtime/types.ts +2 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1191-7b13a24117a689fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-3fcae5186c64fe6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
- package/apps/pwa/out/_next/static/chunks/319-d37df1b6e0cbeff4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4836-3c93795ab834381e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6090-1bd6077591603947.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7457-f5cb1cc8db9f7453.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-0059d82bdb4812bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-52467f244755917f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-21c3261af6ab377c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1991445b8ea86274.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-4ebfaee2369fe248.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-9fa879ebf5959096.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ca7c3cc305c3285a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9951930add3d2051.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-4472ddf1e2458e69.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4de08d4b766f134b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7a975e2b9db1dfb7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-49270714ef35bb96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-33fbdf353e2a5982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-259f122e2dd64317.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-65b82726753658e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4b8df3748dc06285.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-112f7e477bda77fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-542a5c0006da637c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-63ffb347831262ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-98e4e359d97263b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-97e24ac675ce576e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ac6391f26391d89d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f45c9d45ffc43bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-36995b9cb5d98218.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0c795b577988d63f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b4311e2f71c6735a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-eb2381074df20dba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-b01336e64eba01ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7af7f21c1f2858ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-a98ecd364586c89b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-57f0502dfbfa089b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-3467d2e1abd8a14a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-7842ca23d53ba682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-ae9ac882912b0198.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3a0d72e3f783bcef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-f3b39fe8358c2a97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-5a22af3181ac2afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-1457627bb302a22c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/css/e66b7f5efc8df3f3.css +0 -1
- /package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { Database } from 'bun:sqlite';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { debug } from '../debug/log.js';
|
|
6
|
+
import { releaseLedgerRoot } from '../instance/resolve.js';
|
|
7
|
+
|
|
8
|
+
export type ChecklistStatus = 'green' | 'yellow' | 'red' | 'done';
|
|
9
|
+
export type ChecklistDisposition = 'move' | 'known-issue' | 'block';
|
|
10
|
+
export interface ChecklistItem {
|
|
11
|
+
id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
status: ChecklistStatus;
|
|
14
|
+
owner?: string;
|
|
15
|
+
evidence?: string;
|
|
16
|
+
disposition?: ChecklistDisposition;
|
|
17
|
+
updatedAt: string;
|
|
18
|
+
updatedBy: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ChecklistHistory {
|
|
21
|
+
at: string;
|
|
22
|
+
by: string;
|
|
23
|
+
id: string;
|
|
24
|
+
field: string;
|
|
25
|
+
from: unknown;
|
|
26
|
+
to: unknown;
|
|
27
|
+
released: string;
|
|
28
|
+
dev: string;
|
|
29
|
+
}
|
|
30
|
+
export interface Checklist {
|
|
31
|
+
version: string;
|
|
32
|
+
released: string;
|
|
33
|
+
dev: string;
|
|
34
|
+
items: ChecklistItem[];
|
|
35
|
+
history: ChecklistHistory[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function devVersion(): string {
|
|
39
|
+
return (JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf8')) as { version: string }).version;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function releasedVersion(): string {
|
|
43
|
+
const dir = join(releaseLedgerRoot(), 'release');
|
|
44
|
+
if (!existsSync(dir)) return '';
|
|
45
|
+
return readdirSync(dir).filter((v) => /^\d+\.\d+\.\d+$/.test(v) && existsSync(join(dir, v, 'release.json')))
|
|
46
|
+
.filter((v) => {
|
|
47
|
+
try { const record = JSON.parse(readFileSync(join(dir, v, 'release.json'), 'utf8')) as { version?: string; publishedAt?: string }; return record.version === v && typeof record.publishedAt === 'string'; }
|
|
48
|
+
catch { return false; }
|
|
49
|
+
})
|
|
50
|
+
.sort((a, b) => {
|
|
51
|
+
const aa = a.split('.').map(Number), bb = b.split('.').map(Number);
|
|
52
|
+
return (bb[0]! - aa[0]!) || (bb[1]! - aa[1]!) || (bb[2]! - aa[2]!);
|
|
53
|
+
})[0] ?? '';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function pathFor(v: string): string {
|
|
57
|
+
if (!/^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/.test(v)) throw new Error(`체크리스트 판이 아니다: ${v}`);
|
|
58
|
+
return join(releaseLedgerRoot(), 'release', v, 'checklist.json');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function listChecklist(v: string): Checklist {
|
|
62
|
+
const path = pathFor(v);
|
|
63
|
+
if (existsSync(path)) {
|
|
64
|
+
const data = JSON.parse(readFileSync(path, 'utf8')) as Checklist;
|
|
65
|
+
refresh(data);
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
return { version: v, released: releasedVersion(), dev: devVersion(), items: [], history: [] };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// SQLite's OS-backed write lock is released when the owning process exits, even without a finally block.
|
|
72
|
+
// Hold it across the JSON read/modify/atomic-rename sequence so writers never use stale snapshots.
|
|
73
|
+
function mutate(v: string, apply: (data: Checklist) => boolean): Checklist {
|
|
74
|
+
const path = pathFor(v);
|
|
75
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
76
|
+
const lock = `${path}.mutex.sqlite`;
|
|
77
|
+
const db = new Database(lock, { create: true, strict: true });
|
|
78
|
+
try {
|
|
79
|
+
chmodSync(lock, 0o600);
|
|
80
|
+
db.exec('PRAGMA busy_timeout = 10000');
|
|
81
|
+
db.exec('BEGIN IMMEDIATE');
|
|
82
|
+
try {
|
|
83
|
+
const data = listChecklist(v);
|
|
84
|
+
if (apply(data)) save(v, data);
|
|
85
|
+
db.exec('COMMIT');
|
|
86
|
+
return data;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
db.exec('ROLLBACK');
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
} finally { db.close(); }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function save(v: string, data: Checklist): void {
|
|
95
|
+
const path = pathFor(v);
|
|
96
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
97
|
+
const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
98
|
+
try {
|
|
99
|
+
writeFileSync(temp, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600 });
|
|
100
|
+
chmodSync(temp, 0o600);
|
|
101
|
+
renameSync(temp, path);
|
|
102
|
+
} finally { if (existsSync(temp)) rmSync(temp); }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function change(data: Checklist, id: string, field: string, from: unknown, to: unknown, by: string, at: string): void {
|
|
106
|
+
data.history.push({ at, by, id, field, from: from ?? null, to: to ?? null, released: data.released, dev: data.dev });
|
|
107
|
+
debug.log('release-loop.checklist', 'change', { version: data.version, id, field, from: from ?? null, to: to ?? null, by });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function refresh(data: Checklist): void { data.released = releasedVersion(); data.dev = devVersion(); }
|
|
111
|
+
|
|
112
|
+
export function addItem(v: string, input: { id: string; title: string; owner?: string }): Checklist {
|
|
113
|
+
if (!input.id.trim()) throw new Error('칸 id 가 비었다');
|
|
114
|
+
if (!input.title.trim()) throw new Error('칸 제목이 비었다');
|
|
115
|
+
return mutate(v, (data) => {
|
|
116
|
+
if (data.items.some((item) => item.id === input.id)) throw new Error(`이미 있는 칸: ${input.id}`);
|
|
117
|
+
const by = process.env.ELANOUS_TRACK || 'cli';
|
|
118
|
+
const at = new Date().toISOString();
|
|
119
|
+
const item: ChecklistItem = { id: input.id, title: input.title, status: 'yellow', ...(input.owner !== undefined ? { owner: input.owner } : {}), updatedAt: at, updatedBy: by };
|
|
120
|
+
data.items.push(item);
|
|
121
|
+
change(data, item.id, 'add', null, item, by, at);
|
|
122
|
+
return true;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function setItem(v: string, id: string, patch: { status?: ChecklistStatus; evidence?: string; owner?: string; disposition?: ChecklistDisposition }, by: string): Checklist {
|
|
127
|
+
return mutate(v, (data) => {
|
|
128
|
+
const item = data.items.find((i) => i.id === id);
|
|
129
|
+
if (!item) throw new Error(`없는 칸: ${id}`);
|
|
130
|
+
if (patch.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(patch.status)) throw new Error(`잘못된 상태: ${patch.status}`);
|
|
131
|
+
if (patch.disposition !== undefined && !['move', 'known-issue', 'block'].includes(patch.disposition)) throw new Error(`잘못된 처분: ${patch.disposition}`);
|
|
132
|
+
const fields = (['evidence', 'owner', 'status', 'disposition'] as const).filter((field) => patch[field] !== undefined && patch[field] !== item[field]);
|
|
133
|
+
if (fields.length === 0) return false;
|
|
134
|
+
const at = new Date().toISOString();
|
|
135
|
+
for (const field of fields) {
|
|
136
|
+
const from = item[field];
|
|
137
|
+
const to = patch[field]!;
|
|
138
|
+
(item as unknown as Record<string, unknown>)[field] = to;
|
|
139
|
+
item.updatedAt = at;
|
|
140
|
+
item.updatedBy = by;
|
|
141
|
+
change(data, id, field, from, to, by, at);
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function removeItem(v: string, id: string, by: string): Checklist {
|
|
148
|
+
return mutate(v, (data) => {
|
|
149
|
+
const index = data.items.findIndex((i) => i.id === id);
|
|
150
|
+
if (index < 0) throw new Error(`없는 칸: ${id}`);
|
|
151
|
+
const at = new Date().toISOString();
|
|
152
|
+
const [item] = data.items.splice(index, 1);
|
|
153
|
+
change(data, id, 'remove', item, null, by, at);
|
|
154
|
+
return true;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function summarize(v: string): ChecklistSummary {
|
|
159
|
+
return summarizeChecklist(listChecklist(v));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface ChecklistSummary { green: number; yellow: number; red: number; done: number; blocked: string[]; byOwner: Record<string, number> }
|
|
163
|
+
|
|
164
|
+
export function summarizeChecklist(data: Checklist): ChecklistSummary {
|
|
165
|
+
const summary = { green: 0, yellow: 0, red: 0, done: 0, blocked: [] as string[], byOwner: Object.create(null) as Record<string, number> };
|
|
166
|
+
for (const item of data.items) {
|
|
167
|
+
summary[item.status]++;
|
|
168
|
+
if (item.status === 'red') summary.blocked.push(item.id);
|
|
169
|
+
const owner = item.owner ?? 'unassigned';
|
|
170
|
+
summary.byOwner[owner] = (summary.byOwner[owner] ?? 0) + 1;
|
|
171
|
+
}
|
|
172
|
+
return summary;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface ChecklistGate {
|
|
176
|
+
ok: boolean;
|
|
177
|
+
red: string[];
|
|
178
|
+
undecided: string[];
|
|
179
|
+
blocked: string[];
|
|
180
|
+
moved: string[];
|
|
181
|
+
knownIssues: Array<{ id: string; title: string; evidence: string }>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function checklistGate(v: string): ChecklistGate {
|
|
185
|
+
const result: ChecklistGate = { ok: true, red: [], undecided: [], blocked: [], moved: [], knownIssues: [] };
|
|
186
|
+
for (const item of listChecklist(v).items) {
|
|
187
|
+
if (item.status === 'red') result.red.push(item.id);
|
|
188
|
+
if (item.status !== 'yellow') continue;
|
|
189
|
+
if (item.disposition === 'block') result.blocked.push(item.id);
|
|
190
|
+
else if (item.disposition === 'move') result.moved.push(item.id);
|
|
191
|
+
else if (item.disposition === 'known-issue') result.knownIssues.push({ id: item.id, title: item.title, evidence: item.evidence ?? '' });
|
|
192
|
+
else result.undecided.push(item.id);
|
|
193
|
+
}
|
|
194
|
+
result.ok = result.red.length === 0 && result.undecided.length === 0 && result.blocked.length === 0;
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function seedFromRoadmap(v: string, markdown: string): Checklist {
|
|
199
|
+
return mutate(v, (data) => {
|
|
200
|
+
let added = false;
|
|
201
|
+
for (const line of markdown.split('\n')) {
|
|
202
|
+
const match = /^\|\s*(K\d+[a-z]?[′']?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|/.exec(line);
|
|
203
|
+
if (!match || data.items.some((i) => i.id === match[1])) continue;
|
|
204
|
+
const id = match[1]!;
|
|
205
|
+
const title = match[2]!;
|
|
206
|
+
const icon = match[3]!.trim();
|
|
207
|
+
const status: ChecklistStatus = icon.startsWith('🟢') ? 'green' : icon.startsWith('🔴') ? 'red' : icon.startsWith('✅') ? 'done' : 'yellow';
|
|
208
|
+
const at = new Date().toISOString();
|
|
209
|
+
const by = process.env.ELANOUS_TRACK || 'cli';
|
|
210
|
+
const item: ChecklistItem = { id, title, status, updatedAt: at, updatedBy: by };
|
|
211
|
+
data.items.push(item);
|
|
212
|
+
change(data, id, 'add', null, item, by, at);
|
|
213
|
+
added = true;
|
|
214
|
+
}
|
|
215
|
+
return added;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
@@ -30,9 +30,10 @@ function blankFencedCode(text: string): string {
|
|
|
30
30
|
|
|
31
31
|
export function parseReleaseNoteSection(rawPrBody: string): { fragment?: Section; problems: string[] } {
|
|
32
32
|
const prBody = blankFencedCode(rawPrBody);
|
|
33
|
-
const
|
|
33
|
+
const sections = [...prBody.matchAll(/^## 릴리스 노트[ \t]*\r?$/gm)];
|
|
34
|
+
const start = sections.at(-1);
|
|
34
35
|
if (!start) return { problems: [] };
|
|
35
|
-
const rest = prBody.slice(start.index + start[0].length).replace(/^\r?\n/, '');
|
|
36
|
+
const rest = prBody.slice(start.index! + start[0].length).replace(/^\r?\n/, '');
|
|
36
37
|
const nextHeading = /^ {0,3}#{1,6}(?:[ \t]+|$)/m.exec(rest);
|
|
37
38
|
const body = nextHeading ? rest.slice(0, nextHeading.index) : rest;
|
|
38
39
|
const fields = new Map<string, string>();
|
|
@@ -51,7 +52,9 @@ export function parseReleaseNoteSection(rawPrBody: string): { fragment?: Section
|
|
|
51
52
|
if (duplicates.has('종류') || !kind || !['fix', 'feat', 'security', 'internal'].includes(kind)) problems.push('종류');
|
|
52
53
|
if (duplicates.has('문서') || !doc || (doc.startsWith('없음') ? !/^없음\([^\s()]+(?:[^()]*)\)$/.test(doc) : !/^\S+$/.test(doc))) problems.push('문서');
|
|
53
54
|
if (duplicates.has('대상') || (target !== 'next' && target !== 'later')) problems.push('대상');
|
|
54
|
-
|
|
55
|
+
const invalid = problems.length > 0;
|
|
56
|
+
if (sections.length > 1) problems.push(`multiple release-note sections (${sections.length}); using the last`);
|
|
57
|
+
if (invalid) return { problems };
|
|
55
58
|
return {
|
|
56
59
|
fragment: {
|
|
57
60
|
line: line!, kind: kind as Section['kind'],
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import trackData from '../../scripts/coord-tracks.json';
|
|
2
|
+
|
|
3
|
+
export type SeatEntry = {
|
|
4
|
+
id: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
alias?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type SeatAddress = { seats: string[]; body: string };
|
|
10
|
+
|
|
11
|
+
const registry: readonly SeatEntry[] = trackData.tracks.filter((entry) => 'title' in entry);
|
|
12
|
+
const addressLine = /^@([A-Za-z][A-Za-z0-9_-]*(?:,[A-Za-z][A-Za-z0-9_-]*)*)(?:[ \t]+|\r?\n|(?![\s\S]))/gm;
|
|
13
|
+
|
|
14
|
+
/** Parse an address only when its @ begins a line; leave the instruction text intact. */
|
|
15
|
+
export function parseSeatAddress(text: string): SeatAddress | null {
|
|
16
|
+
const match = addressLine.exec(text);
|
|
17
|
+
addressLine.lastIndex = 0;
|
|
18
|
+
if (!match) return null;
|
|
19
|
+
|
|
20
|
+
const seats = match[1]!.split(',');
|
|
21
|
+
const body = `${text.slice(0, match.index)}${text.slice(match.index + match[0].length)}`;
|
|
22
|
+
return { seats, body };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Resolve against the seat registry, preferring titles to ids to legacy aliases. */
|
|
26
|
+
export function resolveSeat(address: string, seats: readonly SeatEntry[] = registry): SeatEntry | undefined {
|
|
27
|
+
const needle = address.replace(/^@/, '').toLocaleLowerCase();
|
|
28
|
+
if (!needle) return undefined;
|
|
29
|
+
for (const field of ['title', 'id', 'alias'] as const) {
|
|
30
|
+
const found = seats.find((seat) => seat[field]?.toLocaleLowerCase() === needle);
|
|
31
|
+
if (found) return found;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
package/src/self-dev/dev-cli.ts
CHANGED
|
@@ -45,6 +45,7 @@ import { getUserConfig, type UserConfig } from '../user-config.js';
|
|
|
45
45
|
import { resolveChildLlmPreference, type ChildLlmPreferenceInput, type ResolvedChildLlmPreference } from '../self-implement/child-llm-preference.js';
|
|
46
46
|
import { readCachedGrokQuota } from '../oauth/codex-account-store.js';
|
|
47
47
|
import { queryAbandonedDraftPrs, type AbandonedDraftPr } from '../cli/logs-abandoned-draft-prs.js';
|
|
48
|
+
import { branchLineageSlug } from '../cli/pr-lineage.js';
|
|
48
49
|
import { decideDraft, type DraftTriagePr } from './draft-triage-rules.js';
|
|
49
50
|
import { resolveLogTargets } from '../cli/logs-cli.js';
|
|
50
51
|
import { LogStore } from '../mss/logging/log-store.js';
|
|
@@ -196,12 +197,12 @@ export interface DevSelfRunExecution {
|
|
|
196
197
|
|
|
197
198
|
interface DraftTriage {
|
|
198
199
|
closed: number[];
|
|
199
|
-
kept: number | null;
|
|
200
|
+
kept: number[] | null;
|
|
200
201
|
closeFailed: number[];
|
|
201
202
|
skippedNotDraft: number[];
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
export type DraftPrViewState = { isDraft: boolean; state: string } | null;
|
|
205
|
+
export type DraftPrViewState = { isDraft: boolean; state: string; headRefName?: string } | null;
|
|
205
206
|
|
|
206
207
|
export interface DraftPrInventory {
|
|
207
208
|
readonly open: readonly (DraftTriagePr & { url: string; openedAtMs: number; runId?: string | null })[];
|
|
@@ -294,11 +295,13 @@ function defaultViewDraftPr(prUrl: string, timeoutMs?: number): DraftPrViewState
|
|
|
294
295
|
if (IN_TEST_PROCESS()) return null;
|
|
295
296
|
const target = draftPrTargetFromUrl(prUrl);
|
|
296
297
|
if (!target) return null;
|
|
297
|
-
const view = spawnSync('gh', ['pr', 'view', String(target.number), '--repo', target.repository, '--json', 'isDraft,state'], { encoding: 'utf8', ...(timeoutMs === undefined ? {} : { timeout: timeoutMs }) });
|
|
298
|
+
const view = spawnSync('gh', ['pr', 'view', String(target.number), '--repo', target.repository, '--json', 'isDraft,state,headRefName'], { encoding: 'utf8', ...(timeoutMs === undefined ? {} : { timeout: timeoutMs }) });
|
|
298
299
|
if (view.status !== 0) return null;
|
|
299
300
|
try {
|
|
300
|
-
const parsed = JSON.parse(view.stdout) as { isDraft?: unknown; state?: unknown };
|
|
301
|
-
return typeof parsed.isDraft === 'boolean' && typeof parsed.state === 'string'
|
|
301
|
+
const parsed = JSON.parse(view.stdout) as { isDraft?: unknown; state?: unknown; headRefName?: unknown };
|
|
302
|
+
return typeof parsed.isDraft === 'boolean' && typeof parsed.state === 'string'
|
|
303
|
+
? { isDraft: parsed.isDraft, state: parsed.state, ...(typeof parsed.headRefName === 'string' ? { headRefName: parsed.headRefName } : {}) }
|
|
304
|
+
: null;
|
|
302
305
|
} catch {
|
|
303
306
|
return null;
|
|
304
307
|
}
|
|
@@ -946,41 +949,58 @@ export async function executeDevSelfRun(
|
|
|
946
949
|
const view = supervise?.viewDraftPr ?? defaultViewDraftPr;
|
|
947
950
|
const closeDraft = supervise?.closeDraftPr ?? defaultCloseDraftPr;
|
|
948
951
|
const commentDraft = supervise?.commentDraftPr ?? defaultCommentDraftPr;
|
|
949
|
-
const openDrafts: string
|
|
952
|
+
const openDrafts: Array<{ url: string; lineage: string | null }> = [];
|
|
950
953
|
const skippedNotDraft: number[] = [];
|
|
951
954
|
for (const prUrl of drafts) {
|
|
952
955
|
let live: DraftPrViewState = null;
|
|
953
956
|
try { live = view(prUrl); } catch { live = null; }
|
|
954
|
-
if (live?.isDraft === true && live.state === 'OPEN')
|
|
955
|
-
|
|
957
|
+
if (live?.isDraft === true && live.state === 'OPEN') {
|
|
958
|
+
openDrafts.push({ url: prUrl, lineage: live.headRefName ? branchLineageSlug(live.headRefName) : null });
|
|
959
|
+
} else {
|
|
956
960
|
const number = prNumberFromUrl(prUrl);
|
|
957
961
|
if (number !== null) skippedNotDraft.push(number);
|
|
958
962
|
}
|
|
959
963
|
}
|
|
960
|
-
const
|
|
961
|
-
|
|
964
|
+
const keptByLineage = new Map<string, string>();
|
|
965
|
+
if (!converged) {
|
|
966
|
+
for (const draft of openDrafts) {
|
|
967
|
+
if (draft.lineage !== null) keptByLineage.set(draft.lineage, draft.url);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
const keptDrafts = converged ? [] : openDrafts.filter(({ url, lineage }) => lineage === null || keptByLineage.get(lineage) === url);
|
|
971
|
+
const kept = keptDrafts.length === 0 ? null : keptDrafts.flatMap(({ url }) => {
|
|
972
|
+
const number = prNumberFromUrl(url);
|
|
973
|
+
return number === null ? [] : [number];
|
|
974
|
+
});
|
|
962
975
|
const closed: number[] = [];
|
|
963
976
|
const closeFailed: number[] = [];
|
|
964
977
|
const landed = mergedNumbers.map((n) => `#${n}`).join(' ') || '없음';
|
|
965
|
-
for (const
|
|
966
|
-
if (
|
|
967
|
-
const number = prNumberFromUrl(
|
|
978
|
+
for (const { url, lineage } of openDrafts) {
|
|
979
|
+
if (!converged && (lineage === null || keptByLineage.get(lineage) === url)) continue;
|
|
980
|
+
const number = prNumberFromUrl(url);
|
|
981
|
+
const keptInLineage = lineage === null ? null : prNumberFromUrl(keptByLineage.get(lineage)!);
|
|
968
982
|
const comment = converged
|
|
969
983
|
? `하니스 종결 트리아지: 이 런이 수렴해 대체됨 — 착지 ${landed}`
|
|
970
|
-
: `하니스 종결 트리아지: 최신 산출 #${
|
|
984
|
+
: `하니스 종결 트리아지: 최신 산출 #${keptInLineage ?? 'unknown'} 로 대체됨`;
|
|
971
985
|
let ok = false;
|
|
972
|
-
try { ok = closeDraft(
|
|
986
|
+
try { ok = closeDraft(url, comment); } catch { ok = false; }
|
|
973
987
|
if (number === null) continue;
|
|
974
988
|
if (ok) closed.push(number);
|
|
975
989
|
else closeFailed.push(number);
|
|
976
990
|
}
|
|
977
|
-
|
|
991
|
+
for (const { url, lineage } of keptDrafts) {
|
|
992
|
+
const keptLineage = [...keptByLineage.keys()].find((other) => other !== lineage);
|
|
993
|
+
if (lineage !== null && keptLineage !== undefined) {
|
|
994
|
+
try { debug.log('self-dev.triage', 'kept-distinct-piece', { pr: prNumberFromUrl(url), lineage, keptLineage }); } catch { /* fail-open */ }
|
|
995
|
+
}
|
|
978
996
|
const rounds = Math.max(1, executedRounds);
|
|
979
|
-
const handoff = closeFailed.length
|
|
980
|
-
?
|
|
981
|
-
:
|
|
997
|
+
const handoff = closeFailed.length > 0
|
|
998
|
+
? `닫지 못한 draft 도 남아 있다: ${closeFailed.map((n) => `#${n}`).join(' ')}`
|
|
999
|
+
: lineage === null ? '브랜치 계보 미확인 — 사람 판단 대상'
|
|
1000
|
+
: keptDrafts.length === 1 ? '이 draft 가 이 골의 유일한 사람 판단 대상'
|
|
1001
|
+
: '이 draft 가 이 계보의 사람 판단 대상';
|
|
982
1002
|
const comment = `하니스 종결 트리아지: 멈춘 사유 ${supervisorStopReason ?? 'unsupervised'} · 라운드 ${rounds} · 착지한 조각 PR ${landed} · ${handoff}`;
|
|
983
|
-
try { commentDraft(
|
|
1003
|
+
try { commentDraft(url, comment); } catch { /* fail-open */ }
|
|
984
1004
|
}
|
|
985
1005
|
draftTriage = { closed, kept, closeFailed, skippedNotDraft };
|
|
986
1006
|
};
|
|
@@ -15,8 +15,12 @@
|
|
|
15
15
|
// self 는 backend 리스트 항목이 아니라 transport 축을 붕괴시키는 특수 갈래. 감독 축도 self=turn 레벨.
|
|
16
16
|
|
|
17
17
|
import { execFileSync } from 'node:child_process';
|
|
18
|
+
import { createHash } from 'node:crypto';
|
|
18
19
|
import { readFileSync } from 'node:fs';
|
|
19
|
-
import {
|
|
20
|
+
import { dispatchTask, type DispatchTaskDeps } from '../execution-loop/dispatch-task.js';
|
|
21
|
+
import { getNestDepth } from '../agent/nest-depth.js';
|
|
22
|
+
import { appendRunLedgerEntry } from '../self-implement/run-ledger.js';
|
|
23
|
+
import { appendEvidenceLocationRequirement, extractVerbatimOriginalAsk, formatGoalFileLintFinding, inspectAskDecisionSignalMarker, leadingGoalMetadata, lintGoalFile, parseGoalId, parseGoalType, tracedPathReferences, SUPERSEDED_BY_LINE, type GoalFileLintFinding, type GoalFileLintResult } from '../self-implement/goal-author.js';
|
|
20
24
|
import { createRepositoryReferencedFileReader, type ReferencedFileReader } from '../self-implement/goal-file-reader.js';
|
|
21
25
|
import { parseGoalDocumentClarifications } from '../self-implement/goal-author-clarification.js';
|
|
22
26
|
import { requiredEvidenceFromGoal } from '../self-implement/off-diff-evidence.js';
|
|
@@ -55,6 +59,8 @@ export type OrchestrateRuntime = Omit<OrchestrateSelfDevOptions, 'goals' | 'conc
|
|
|
55
59
|
export interface DevMissionOpts {
|
|
56
60
|
chain?: readonly ('codex' | 'claude' | 'elanous')[];
|
|
57
61
|
plugin?: { plugin: string; marketplace: string };
|
|
62
|
+
resources?: 'on' | 'off';
|
|
63
|
+
backendExplicit?: string;
|
|
58
64
|
headless?: boolean;
|
|
59
65
|
evidence?: EvidenceMode;
|
|
60
66
|
maxRounds?: number;
|
|
@@ -619,6 +625,8 @@ export function toAgentMissionSpec(
|
|
|
619
625
|
...(m.headless === true ? { headless: true } : {}),
|
|
620
626
|
...(m.chain ? { chain: m.chain } : {}),
|
|
621
627
|
...(m.plugin ? { plugin: m.plugin } : {}),
|
|
628
|
+
...(m.resources !== undefined ? { resources: m.resources } : {}),
|
|
629
|
+
...(m.backendExplicit !== undefined ? { backendExplicit: m.backendExplicit } : {}),
|
|
622
630
|
...(plan.base ? { base: plan.base } : {}),
|
|
623
631
|
...(plan.enhance !== undefined ? { enhance: plan.enhance } : {}),
|
|
624
632
|
...(m.maxRounds !== undefined ? { maxRounds: m.maxRounds } : {}),
|
|
@@ -644,6 +652,8 @@ export function buildAgentMissionDevSpec(o: {
|
|
|
644
652
|
headless?: boolean;
|
|
645
653
|
chain?: readonly ('codex' | 'claude' | 'elanous')[];
|
|
646
654
|
plugin?: { plugin: string; marketplace: string };
|
|
655
|
+
resources?: 'on' | 'off';
|
|
656
|
+
backendExplicit?: string;
|
|
647
657
|
/** --no-enhance 시 false(그 외 미지정 → driver 가 entry 로 구동). */
|
|
648
658
|
enhanceOff?: boolean;
|
|
649
659
|
evidence: EvidenceMode;
|
|
@@ -663,6 +673,8 @@ export function buildAgentMissionDevSpec(o: {
|
|
|
663
673
|
...(o.headless === true ? { headless: true } : {}),
|
|
664
674
|
...(o.chain ? { chain: o.chain } : {}),
|
|
665
675
|
...(o.plugin ? { plugin: o.plugin } : {}),
|
|
676
|
+
...(o.resources !== undefined ? { resources: o.resources } : {}),
|
|
677
|
+
...(o.backendExplicit !== undefined ? { backendExplicit: o.backendExplicit } : {}),
|
|
666
678
|
maxRounds: o.maxRounds,
|
|
667
679
|
commit: o.commit,
|
|
668
680
|
entry: 'elanous-apparatus',
|
|
@@ -918,6 +930,10 @@ export interface DevHarnessDispatchArgs extends Record<string, unknown> {
|
|
|
918
930
|
}
|
|
919
931
|
|
|
920
932
|
export interface DevPipelineDeps {
|
|
933
|
+
dispatchTask?: typeof dispatchTask;
|
|
934
|
+
dispatchTaskDeps?: DispatchTaskDeps;
|
|
935
|
+
/** Caller has already recorded this dispatch decision at an outer entrance. */
|
|
936
|
+
skipDispatchTask?: boolean;
|
|
921
937
|
/** Caller-managed human progress sink (for example SurfaceUx.progress). Absent callers retain byte-for-byte CLI stdout. */
|
|
922
938
|
progress?: SurfaceUx['progress'];
|
|
923
939
|
runSelfImplement?: (o: SelfImplementOptions) => Promise<SelfImplementResult>;
|
|
@@ -1777,6 +1793,65 @@ export async function runDevPipeline(spec: DevPipelineSpec, deps: DevPipelineDep
|
|
|
1777
1793
|
}
|
|
1778
1794
|
}
|
|
1779
1795
|
|
|
1796
|
+
async function observeDevTaskDispatch(spec: DevPipelineSpec, plan: ResolvedDevPlan, deps: DevPipelineDeps): Promise<void> {
|
|
1797
|
+
const skipped = plan.dispatch === 'interactive' && getNestDepth() > 0 ? 'nested-interactive'
|
|
1798
|
+
: plan.relaunch === true ? 'relaunch'
|
|
1799
|
+
: plan.dispatch === 'parallel' ? 'parallel-parent'
|
|
1800
|
+
: deps.skipDispatchTask === true ? 'skipDispatchTask'
|
|
1801
|
+
: process.env.ELANOUS_DISPATCH_RECORDED === '1' ? 'dispatch-recorded'
|
|
1802
|
+
// `bun test` preload sets this so pipeline tests do not run the real gates (usage/recall
|
|
1803
|
+
// lookups) on every launch; tests that inject a dispatcher or gates still observe.
|
|
1804
|
+
: process.env.ELANOUS_EXECUTION_LOOP_DISPATCH === 'off' && !deps.dispatchTask && !deps.dispatchTaskDeps ? 'disabled'
|
|
1805
|
+
: undefined;
|
|
1806
|
+
if (skipped) {
|
|
1807
|
+
try { debug.log('execution-loop.gate', 'dispatch-skipped', { reason: skipped, ...(spec.runId ? { runId: spec.runId } : {}) }); } catch { /* fail-soft */ }
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
try {
|
|
1811
|
+
const goalText = 'text' in plan.input ? plan.input.text : (deps.readFile ?? ((path: string) => readFileSync(path, 'utf8')))(plan.input.file);
|
|
1812
|
+
const goalId = 'file' in plan.input ? parseGoalId(goalText) : null;
|
|
1813
|
+
let recordedCardId: string | undefined;
|
|
1814
|
+
const recordCard = (cardId: string): void => {
|
|
1815
|
+
if (!spec.runId || recordedCardId) return;
|
|
1816
|
+
recordedCardId = cardId;
|
|
1817
|
+
try {
|
|
1818
|
+
appendRunLedgerEntry({ timestamp: new Date().toISOString(), runId: spec.runId,
|
|
1819
|
+
event: 'execution-loop-card', data: { cardId } });
|
|
1820
|
+
} catch (error) {
|
|
1821
|
+
try { debug.log('execution-loop.gate', 'ledger-unavailable', { reason: String(error) }); } catch { /* fail-soft */ }
|
|
1822
|
+
}
|
|
1823
|
+
};
|
|
1824
|
+
const dispatchDeps: DispatchTaskDeps = {
|
|
1825
|
+
...deps.dispatchTaskDeps,
|
|
1826
|
+
onCardCreated: (cardId) => {
|
|
1827
|
+
recordCard(cardId);
|
|
1828
|
+
deps.dispatchTaskDeps?.onCardCreated?.(cardId);
|
|
1829
|
+
},
|
|
1830
|
+
};
|
|
1831
|
+
const work = Promise.resolve().then(() => (deps.dispatchTask ?? dispatchTask)({
|
|
1832
|
+
goalId: goalId ?? `request-${createHash('sha256').update(goalText).digest('hex').slice(0, 32)}`,
|
|
1833
|
+
title: goalText.split(/\r?\n/, 1)[0]?.trim() || 'Untitled request',
|
|
1834
|
+
goalText,
|
|
1835
|
+
targetPaths: 'file' in plan.input ? tracedPathReferences(goalText).map((reference) => reference.path) : [],
|
|
1836
|
+
spec,
|
|
1837
|
+
...(spec.runId ? { runKey: spec.runId } : {}),
|
|
1838
|
+
}, dispatchDeps));
|
|
1839
|
+
// The default dispatcher bounds its gates to five seconds. Await its completed
|
|
1840
|
+
// observation so short-lived callers cannot exit before decisions reach the card.
|
|
1841
|
+
// Injected dispatchers retain an outer timeout to protect the original launch.
|
|
1842
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
1843
|
+
const result = await (deps.dispatchTask
|
|
1844
|
+
? Promise.race([
|
|
1845
|
+
work,
|
|
1846
|
+
new Promise<never>((_, reject) => { timeout = setTimeout(() => reject(new Error('dispatch timeout after 5000ms')), 5_000); }),
|
|
1847
|
+
]).finally(() => { if (timeout) clearTimeout(timeout); })
|
|
1848
|
+
: work);
|
|
1849
|
+
recordCard(result.cardId);
|
|
1850
|
+
} catch (error) {
|
|
1851
|
+
try { debug.log('execution-loop.gate', 'dispatch-unavailable', { reason: String(error) }); } catch { /* fail-soft */ }
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1780
1855
|
async function runDevPipelineDispatch(
|
|
1781
1856
|
spec: DevPipelineSpec,
|
|
1782
1857
|
deps: DevPipelineDeps,
|
|
@@ -1785,6 +1860,7 @@ async function runDevPipelineDispatch(
|
|
|
1785
1860
|
): Promise<DevPipelineRunResult> {
|
|
1786
1861
|
const plan = planDevPipeline(spec);
|
|
1787
1862
|
seen.plan = plan;
|
|
1863
|
+
await observeDevTaskDispatch(spec, plan, deps);
|
|
1788
1864
|
if (!plan.wired) {
|
|
1789
1865
|
throw new DevPipelineError(
|
|
1790
1866
|
`runDevPipeline: '${plan.dispatch}' 경로는 U4 골격에서 미배선(NotYetUnified). ` +
|
|
@@ -682,7 +682,9 @@ function runIsolatedBunTest(cwd: string, args: string[], timeout: number): Proce
|
|
|
682
682
|
return { status: null, stdout: '', stderr: message, error: error instanceof Error ? error : new Error(message) };
|
|
683
683
|
}
|
|
684
684
|
try {
|
|
685
|
-
|
|
685
|
+
// SIGKILL — a test that traps SIGTERM while blocked in a native wait never runs its handler, so spawnSync
|
|
686
|
+
// waited forever and its drive grandchildren lived on as ppid=1 orphans (09-30 O1: 19 processes, up to 11h).
|
|
687
|
+
return spawnSync('bun', ['test', ...args], { cwd, env: isolated.env, encoding: 'utf8', timeout, killSignal: 'SIGKILL', maxBuffer: 32 * 1024 * 1024 });
|
|
686
688
|
} finally {
|
|
687
689
|
isolated.cleanup();
|
|
688
690
|
}
|
|
@@ -32,6 +32,7 @@ import { quoteShellArg } from '../cli/logs-cli.js';
|
|
|
32
32
|
import type { HarnessGroundingProvenance } from './context-capsule.js';
|
|
33
33
|
import { hasGoalStepCodeName } from './goal-step-code-name.js';
|
|
34
34
|
import { targetScopedGoalText } from './goal-text-path-scope.js';
|
|
35
|
+
import { parseReleaseNoteSection } from '../release-loop/release-note.js';
|
|
35
36
|
|
|
36
37
|
// ⭐ `situation`/`complication` 은 **선택**이다 — 안 오면 `scqaNarrative` 가 종전 문면을 쓴다.
|
|
37
38
|
// ⛔ 필수로 만들지 않는다: 그러면 인핸싱이 폴백으로 떨어진 저작(LLM 실패·`disabled`)이 전부 죽는다.
|
|
@@ -334,7 +335,7 @@ function endGoalAuthorPhase(phase: GoalAuthorTimedPhase, startedAt: number, auth
|
|
|
334
335
|
|
|
335
336
|
export type GoalFileLintLevel = 'ERROR' | 'WARN';
|
|
336
337
|
|
|
337
|
-
export type GoalFileLintTag = 'canonical-structure' | 'evidence-section' | 'boundary-size' | 'launch-branch' | 'unanswered-clarification' | 'shell-damage' | 'traced-path' | 'grounding-evidence' | 'empty-result-population' | 'decision-signal-numeric-source' | 'decision-signal-numeric-coverage' | 'decision-signal-proxy-expectation' | 'out-of-target-requirement' | 'heading-form-marker' | 'blanket-invariant' | 'self-question-subject' | 'artifact-launch-declaration' | 'all-negative-signals' | 'unreadable-signals' | 'alternative-signals' | 'count-observation' | 'identifier-name-observation' | 'self-reported-observation' | 'default-invocation-observation';
|
|
338
|
+
export type GoalFileLintTag = 'canonical-structure' | 'evidence-section' | 'boundary-size' | 'launch-branch' | 'unanswered-clarification' | 'shell-damage' | 'traced-path' | 'grounding-evidence' | 'empty-result-population' | 'decision-signal-numeric-source' | 'decision-signal-numeric-coverage' | 'decision-signal-proxy-expectation' | 'out-of-target-requirement' | 'heading-form-marker' | 'blanket-invariant' | 'self-question-subject' | 'artifact-launch-declaration' | 'all-negative-signals' | 'unreadable-signals' | 'alternative-signals' | 'count-observation' | 'identifier-name-observation' | 'self-reported-observation' | 'default-invocation-observation' | 'release-note';
|
|
338
339
|
|
|
339
340
|
type GoalFileLintOrigin =
|
|
340
341
|
| { readonly kind: 'known-incident'; readonly incident: string; readonly reference: string }
|
|
@@ -369,6 +370,7 @@ export const GOAL_FILE_LINT_ORIGINS: Record<GoalFileLintTag, GoalFileLintOrigin>
|
|
|
369
370
|
'identifier-name-observation': { kind: 'known-incident', incident: "an authored observation of 'the list of test names this file registers' produced a test that greps its own source for test-name strings, which passes with every named test body emptied; 59 of 3164 goal documents carried the same wording", reference: 'PR #15791 (closed; superseded by #15802); docs/harness/observability/ISSUES.md OBS-T415' },
|
|
370
371
|
'self-reported-observation': { kind: 'known-incident', incident: "an authored observation of 'the captureScope field of the emitted JSON' stayed green across two failed implementations: a Chrome CLI flag Chrome silently ignores (capture stayed 1280x900 against a true 4651) and a CDP call that timed out leaving no file at all, both of which still emitted captureScope:'full-page'", reference: 'docs/manual/MANUAL-web-clone-to-reproducible-resource-2026-09-08.md section 6b; PR #16201; measured 2026-09-08: 190 of 2867 goal documents that carry a decision signal match this shape, three times the 63 that match identifier-name-observation' },
|
|
371
372
|
'default-invocation-observation': { kind: 'known-incident', incident: 'a scripts/*.ts checker landed green from fixture tests and, run once with no arguments from the repository root, reported covered 1 and uncovered 22 because its default catalog path never opened the map the goal named', reference: 'PR #19126; measured 2026-09-20 against the same-day controls #19123 and #19127' },
|
|
373
|
+
'release-note': { kind: 'unknown-origin', label: 'ORIGIN-UNKNOWN' },
|
|
372
374
|
} as const satisfies Record<GoalFileLintTag, GoalFileLintOrigin>;
|
|
373
375
|
|
|
374
376
|
/** Machine-countable reason for a finding that retains the canonical-structure tag. */
|
|
@@ -1452,6 +1454,21 @@ export function lintGoalFile(document: string, branch: string, deps: GoalFileLin
|
|
|
1452
1454
|
for (const finding of askSectionCountLintFindings(document)) findings.push(finding);
|
|
1453
1455
|
for (const finding of headingFormMarkerLintFindings(document)) findings.push(finding);
|
|
1454
1456
|
for (const finding of blanketInvariantLintFindings(document)) findings.push(finding);
|
|
1457
|
+
const releaseNote = parseReleaseNoteSection(document);
|
|
1458
|
+
for (const problem of releaseNote.problems) {
|
|
1459
|
+
findings.push({
|
|
1460
|
+
level: 'WARN', tag: 'release-note',
|
|
1461
|
+
message: `## 릴리스 노트 ${problem} invalid or missing; use - 한 줄: English summary, - 종류: feat|fix|security|internal (feature → feat), - 문서: path or 없음(이유), - 대상: next|later`,
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
if (releaseNote.fragment || releaseNote.problems.length) {
|
|
1465
|
+
const section = markdownSection(document, '릴리스 노트') ?? '';
|
|
1466
|
+
const kind = releaseNote.fragment?.kind ?? /^[ \t]*- 종류:[ \t]*(.*?)[ \t]*$/m.exec(section)?.[1];
|
|
1467
|
+
const line = releaseNote.fragment?.line ?? /^[ \t]*- 한 줄:[ \t]*(.*?)[ \t]*$/m.exec(section)?.[1];
|
|
1468
|
+
if (kind && ['feat', 'fix', 'security'].includes(kind) && line && /[가-힣ㄱ-ㅎㅏ-ㅣ]/u.test(line)) {
|
|
1469
|
+
findings.push({ level: 'WARN', tag: 'release-note', message: '## 릴리스 노트 한 줄: 공개 노트에 실리는 문장은 영어로(internal 은 한국어 무방)' });
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1455
1472
|
for (const violation of selfQuestionSubjectViolations) {
|
|
1456
1473
|
findings.push({
|
|
1457
1474
|
level: 'WARN',
|
|
@@ -27,9 +27,13 @@ export type HostRegateDeps = {
|
|
|
27
27
|
log?: (event: 'passed' | 'failed' | 'unmeasured' | 'base-raced', data: Record<string, unknown>) => void;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
const SPAWN_MAX_BUFFER = 256 * 1024 * 1024;
|
|
31
|
+
|
|
30
32
|
const defaultCommand: NonNullable<HostRegateDeps['command']> = (bin, args, cwd, env) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
// bun ≥1.4 applies Node's 1MB default maxBuffer and silently truncates — test output here is often larger.
|
|
34
|
+
const r = spawnSync(bin, [...args], { cwd, env: env ?? process.env, encoding: 'utf8', timeout: 600_000, maxBuffer: SPAWN_MAX_BUFFER });
|
|
35
|
+
const overflow = (r.error as NodeJS.ErrnoException | undefined)?.code === 'ENOBUFS';
|
|
36
|
+
return { status: overflow ? null : r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + (r.error ? `${r.error.message} (${overflow ? `output exceeded ${SPAWN_MAX_BUFFER} bytes` : 'command unavailable'})` : '') };
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
/** The informational pr-land gate always returns zero; enforce its measured report and the combined test result instead. */
|
|
@@ -3307,9 +3307,14 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
3307
3307
|
if (opts.childLlm && (typeof opts.childLlm.provider !== 'string' || !opts.childLlm.provider.trim() || typeof opts.childLlm.model !== 'string' || !opts.childLlm.model.trim())) {
|
|
3308
3308
|
throw new Error('childLlm.provider and childLlm.model must both be non-empty');
|
|
3309
3309
|
}
|
|
3310
|
-
const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId);
|
|
3311
3310
|
const identity = resolveRunIdentity({ explicit: opts.runId });
|
|
3312
3311
|
const { runId } = identity;
|
|
3312
|
+
const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId, runId);
|
|
3313
|
+
try {
|
|
3314
|
+
debug.log('self-implement.branch', 'planned', {
|
|
3315
|
+
runId, goalId: opts.goalId, branch, runSuffix: opts.branchName === undefined ? branch.match(/-r[a-z0-9]{6}$/)?.[0] ?? null : null,
|
|
3316
|
+
});
|
|
3317
|
+
} catch { /* observation must not block the run */ }
|
|
3313
3318
|
const attemptOrdinal = incrementRunAttemptOrdinal(runId);
|
|
3314
3319
|
const shardIdentity = parseRunShardIdentity(opts.feature);
|
|
3315
3320
|
const observeOuter = makeRunObserver(runId, opts.goalId, debug.log.bind(debug), opts.seams.writeRunLedger, shardIdentity);
|
|
@@ -4517,8 +4522,8 @@ async function runSelfImplementInner(
|
|
|
4517
4522
|
roundClassifications.push(classification);
|
|
4518
4523
|
onRoundClassification(classification);
|
|
4519
4524
|
};
|
|
4520
|
-
const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId);
|
|
4521
4525
|
const { runId, source: runIdSource } = identity ?? resolveRunIdentity({ explicit: opts.runId });
|
|
4526
|
+
const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId, runId);
|
|
4522
4527
|
try {
|
|
4523
4528
|
debug.log('run-identity', 'own', {
|
|
4524
4529
|
runId, branch, source: runIdSource, nestDepth: nestInfo().depth, ...originObservationFields(),
|
|
@@ -280,7 +280,7 @@ function recordedString(entries: readonly RunLedgerEntry[], field: string): stri
|
|
|
280
280
|
return undefined;
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
-
function summarizeTerminalLedger(runId: string, entries: readonly RunLedgerEntry[]): RunLedgerSummary {
|
|
283
|
+
export function summarizeTerminalLedger(runId: string, entries: readonly RunLedgerEntry[]): RunLedgerSummary {
|
|
284
284
|
const last = entries.at(-1)?.timestamp;
|
|
285
285
|
if (!last || !Number.isFinite(Date.parse(last)) || !hasTerminalRunStatus(entries)) throw new Error('ledger changed since plan');
|
|
286
286
|
const terminator = [...entries].reverse().find((entry) => entry.event === 'human-stop' || entry.event === 'terminal'
|