elanous 0.2.25 → 0.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/{1454-37fd16f479f5554a.js → 1454-29d1d773d5996a49.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{599-a914cc0468f56143.js → 599-56f2c0a5cc4a7295.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-b12149c51f7d04b1.js → page-fa46d4588e8442c2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-305cd294e754906e.js → page-256ffd365d32e100.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-dea4b795741126fd.js → page-e0de78ae7b99f01c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-45889e7070d11126.js → page-4c741eb760f2f77c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-16a2e893f83ae4d0.js → page-e9ba98f5f1440a21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-56888bc744e08dac.js → page-2f6f303fa114ec24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-952c6ceec6d9de38.js → page-ced0bcfc6542b3fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-6d0202af568f4c63.js → page-bb1f05cd51c38944.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-4fb6fc34e20c07b7.js → page-f736324afa2117c1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-e9502921f4f01b1f.js → page-6f131b51193a36f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-b6169ea2f09cf287.js → not-found-435f5e2faf02bc01.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-6a14333d1b1b3967.js → page-497e0eedf20e7890.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-8c91a9a66c9ec90a.js → page-a8d28fdf995e16ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{page-3d3fc73dd187ed52.js → page-66a67eb19ebd976b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-9c04dac0831c2bbb.js → page-c71267e7681da982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-04de9ca6d52a3893.js → page-cb249e81ed339318.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-62527677bbc2fdc4.js → page-781cfc5114e097da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-9a2e09c4a311764c.js → page-478157214db6a023.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-bfeb35d69e8b24bb.js → page-86141f12cf090b7f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-89cb33bced824e4a.js → page-7256c73bb1c36751.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-6445ac4cbb8dd869.js → page-979d318a534e92da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-20162174b77f96cf.js → page-601fb77340e63c9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-05a4b2c9b91034aa.js → page-11bf9e5725a72c22.js} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +13 -13
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +13 -13
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +13 -13
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +13 -13
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +13 -13
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +13 -13
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +13 -13
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +13 -13
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +13 -13
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +13 -13
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +13 -13
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +13 -13
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +13 -13
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +13 -13
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +13 -13
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +13 -13
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +13 -13
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +13 -13
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +13 -13
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +13 -13
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +13 -13
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +13 -13
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +13 -13
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +13 -13
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +13 -13
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +13 -13
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +13 -13
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +13 -13
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +14 -14
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +13 -13
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +13 -13
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +13 -13
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +13 -13
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +13 -13
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +13 -13
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +13 -13
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +13 -13
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +13 -13
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +13 -13
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +13 -13
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +13 -13
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +13 -13
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +13 -13
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +13 -13
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +13 -13
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +13 -13
- package/graphs/implement-loop.yaml +38 -1
- package/graphs/steward/recipes.yaml +6 -0
- package/graphs/steward/release-day.yaml +23 -0
- package/graphs/steward/steward.yaml +9 -1
- package/graphs/work/code-pr.yaml +23 -0
- package/package.json +1 -1
- package/scripts/browser-observe-cycle.ts +76 -2
- package/scripts/ci-typecheck-changed.ts +119 -7
- package/scripts/coord-msg-bridge.ts +209 -0
- package/scripts/coord-post.sh +40 -7
- package/scripts/coord-requests-bridge.ts +10 -5
- package/scripts/detect-test-interference.ts +31 -1
- package/scripts/expose-rubric-suggest.ts +1 -1
- package/scripts/measure-tsc-affected-roots.ts +10 -1
- package/scripts/release-loop/docs-node.ts +51 -22
- package/scripts/release-loop/export-check-node.ts +8 -5
- package/scripts/release-loop/gate-node.ts +37 -4
- package/scripts/release-loop/node-verdict.ts +10 -3
- package/scripts/release-loop/publish-node.ts +3 -0
- package/scripts/release-loop/tui-node-checks.ts +5 -2
- package/scripts/skills/public-check.ts +5 -2
- package/scripts/test-deterministic.ts +520 -37
- package/scripts/ux-sim.ts +7 -1
- package/scripts/verify-kpack6-live.ts +230 -0
- package/src/acp/codex-plugins.ts +17 -0
- package/src/agent/task-notification.ts +32 -2
- package/src/agent-mission/in-agent-plugin.ts +8 -3
- package/src/agent-substrate/review-observation.ts +9 -2
- package/src/agent-substrate/self-review-cli.ts +62 -16
- package/src/boot/daemon-tools/self-implement.ts +72 -4
- package/src/briefing/brief-items.ts +151 -35
- package/src/budget/llm-cost.ts +30 -3
- package/src/budget/usage-outcomes.ts +32 -0
- package/src/claims/claims-ledger.ts +23 -0
- package/src/claims/claims-render.ts +22 -2
- package/src/cli/brief-cli.ts +131 -10
- package/src/cli/claims-cli.ts +24 -4
- package/src/cli/control-cli.ts +29 -0
- package/src/cli/doctor-cli.ts +90 -8
- package/src/cli/msg-cli.ts +177 -11
- package/src/cli/plugin-cli.ts +11 -3
- package/src/cli/pr-cli.ts +133 -18
- package/src/cli/pr-land-overlap-evidence.ts +18 -10
- package/src/cli/pr-land-refusal.ts +20 -0
- package/src/cli/release-cli.ts +34 -5
- package/src/cli/rfc-cli.ts +24 -0
- package/src/cli/seat-cli.ts +121 -0
- package/src/cli/self-utterances-cli.ts +140 -0
- package/src/cli/tailscale-serve.ts +5 -8
- package/src/cli/tasks-cli.ts +528 -31
- package/src/cli/usage-cli.ts +9 -4
- package/src/context-card/cell.ts +40 -0
- package/src/dashboard/boot-timing.ts +35 -0
- package/src/dashboard/first-screen-band.ts +63 -13
- package/src/dashboard/index.ts +94 -27
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
- package/src/dashboard/skill-detection-runtime.ts +24 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +6 -0
- package/src/debug/log.ts +1 -1
- package/src/domains/morning-report.ts +3 -1
- package/src/domains/release-tool.ts +29 -12
- package/src/domains/surface-events.ts +16 -0
- package/src/elanous-config-dir.ts +2 -2
- package/src/fleet/host-doctor.ts +73 -2
- package/src/github/pr-labels.ts +10 -4
- package/src/graph-runner/runner.ts +104 -15
- package/src/harness/harness-clean.ts +21 -3
- package/src/harness/harness-cli-command.ts +68 -10
- package/src/harness/harness-pod-dispatch.ts +11 -4
- package/src/harness/harness-stop.ts +11 -7
- package/src/hq/seats.ts +17 -3
- package/src/index.ts +69 -9
- package/src/instance/current.ts +2 -6
- package/src/instance/resolve.ts +5 -6
- package/src/intelligence-map/model-bench-scores.ts +102 -0
- package/src/llm/openai-sampling-fields.ts +89 -0
- package/src/llm/runtime-model-family.ts +61 -0
- package/src/llm/zai-coding-plan.ts +27 -1
- package/src/llm.ts +13 -89
- package/src/loops/orchestrator/inbox.ts +73 -0
- package/src/loops/orchestrator/resync-shadow.ts +100 -12
- package/src/loops/orchestrator/tick.ts +73 -2
- package/src/machines/machine-ledger.ts +31 -3
- package/src/maturity/cli-maturity.ts +13 -3
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/msg/msg-sse-server.ts +141 -0
- package/src/msg/msg-store.ts +149 -17
- package/src/ops/overview-collect.ts +3 -2
- package/src/ops/overview-owner.ts +29 -0
- package/src/patents/ledger.ts +128 -0
- package/src/plugin-hooks/shell-hook.ts +17 -2
- package/src/plugin-missions/evaluator-runner.ts +18 -2
- package/src/plugins/install/market-fetch.ts +32 -1
- package/src/plugins/install/plugin-install.ts +11 -1
- package/src/pod-lease/admission.ts +116 -41
- package/src/prompt-enhance/enhance.ts +11 -0
- package/src/release-loop/checklist.ts +52 -4
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/merged-pr-checklist.ts +24 -5
- package/src/release-loop/rubric.ts +70 -0
- package/src/rfc-registry/registry.ts +88 -0
- package/src/seat-dispatch/seat-request-ledger.ts +4 -0
- package/src/seat-loop/knowhow-lint.ts +233 -0
- package/src/seat-loop/seat-loop.ts +5 -1
- package/src/seat-loop/seat-pack.ts +16 -4
- package/src/seat-loop/traffic-signal.ts +7 -0
- package/src/self-dev/ask-launch-flow.ts +48 -8
- package/src/self-dev/ask-path-candidates.ts +69 -14
- package/src/self-dev/decompose-proposal.ts +43 -1
- package/src/self-dev/draft-sweep.ts +23 -14
- package/src/self-dev/graph-journey-nodes.ts +2 -0
- package/src/self-dev/launch-preflight-text.ts +5 -1
- package/src/self-dev/launch-preflight.ts +143 -10
- package/src/self-dev/orchestrate.ts +6 -2
- package/src/self-dev/run-supervisor.ts +35 -1
- package/src/self-implement/gate-cli.ts +6 -2
- package/src/self-implement/goal-author.ts +5 -28
- package/src/self-implement/goal-command-execution.ts +26 -0
- package/src/self-implement/goal-new-paths.ts +4 -5
- package/src/self-implement/graph-authority.ts +17 -3
- package/src/self-implement/graph-yaml.ts +15 -7
- package/src/self-implement/headless-elanous-driver.ts +16 -1
- package/src/self-implement/host-regate.ts +44 -9
- package/src/self-implement/orchestrator.ts +5 -11
- package/src/self-implement/pwa-import-graph.ts +194 -7
- package/src/self-implement/rework-policy.ts +80 -2
- package/src/self-implement/self-implement-runtime.ts +126 -21
- package/src/self-implement/speed-claims.ts +145 -6
- package/src/session/index.ts +14 -4
- package/src/session-runtime/index.ts +55 -11
- package/src/skills/tools/persistent-grounding.ts +59 -4
- package/src/skills/tools/shell.ts +79 -1
- package/src/steward/rejudge-tick.ts +12 -1
- package/src/steward/release-day.ts +158 -0
- package/src/steward/resync-tick.ts +123 -0
- package/src/task-agent/actions.ts +1 -0
- package/src/task-agent/adopt-pr.ts +140 -0
- package/src/task-agent/board.ts +11 -1
- package/src/task-agent/card-evidence.ts +37 -5
- package/src/task-agent/cover.ts +105 -19
- package/src/task-agent/failure-notify.ts +61 -0
- package/src/task-agent/green-sweep.ts +170 -0
- package/src/task-agent/land-worktree.ts +52 -5
- package/src/task-agent/live-move-history.ts +1 -0
- package/src/task-agent/live-moves.ts +56 -17
- package/src/task-agent/parent-host.ts +1 -1
- package/src/task-agent/postland-card.ts +139 -0
- package/src/task-agent/rejudge-on-head.ts +321 -25
- package/src/task-agent/remote-rejudge-bind.ts +62 -0
- package/src/task-agent/repair-branch-cas.ts +182 -0
- package/src/task-agent/repair-cycle.ts +275 -0
- package/src/task-agent/repair-next-move.ts +31 -0
- package/src/task-agent/repair-review.ts +24 -0
- package/src/task-agent/repair-worker.ts +87 -0
- package/src/task-agent/review-result-capture.ts +66 -2
- package/src/task-agent/shadow.ts +150 -29
- package/src/task-agent/task-hand.ts +133 -8
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +71 -12
- package/src/telegram.ts +30 -1
- package/src/tool-runtime/tool-search-runtime.ts +25 -0
- package/src/tui.ts +20 -1
- package/src/user-config.ts +11 -60
- package/src/ux-sim/fault-sqlite-busy.ts +50 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4e634c5b98fd4e93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b9cc8a10bc6078b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +0 -1
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_ssgManifest.js +0 -0
package/src/cli/tasks-cli.ts
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync } from 'node:fs';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
4
|
+
import { loadRunLedger as loadRunLedgerSync, loadRunLedgerWithMetadata, runLedgerDir, resolveFederatedRunLedgerDirectories } from '../self-implement/run-ledger.js';
|
|
5
|
+
import { goalRunDbPath, loadGoalRunQuery } from '../self-implement/goal-run-store.js';
|
|
6
|
+
import { loadGoalAskRecords } from '../self-implement/goal-ask-store.js';
|
|
7
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
8
|
import { InvalidArgumentError, Option, type Command } from 'commander';
|
|
6
9
|
import { debug } from '../debug/log.js';
|
|
7
10
|
import { getElanousConfigDir } from '../elanous-config-dir.js';
|
|
8
11
|
import { readNexusRuntime } from '../nexus/runtime.js';
|
|
9
|
-
import { checklistDevVersion } from '../release-loop/checklist.js';
|
|
12
|
+
import { checklistDevVersion, listChecklist } from '../release-loop/checklist.js';
|
|
13
|
+
import { assignedVersions, releasedVersion, validateVersion } from '../release-loop/feature-store.js';
|
|
14
|
+
import { compareVersions } from '../directives/version-at.js';
|
|
10
15
|
import { cardsBoard, countBoardTasks, INTERNAL_PACK_PROJECT, mergeBoards, releasePackBoard, type BoardNode } from '../task-agent/board.js';
|
|
11
16
|
import type { Task } from '../task-orchestrator/types.js';
|
|
12
|
-
import { cardPrNumber, CLOSED_BY_HAND_EVENT, closeTaskCard, COMPLETION_KINDS, handTask, updateTaskCard, LIVE_MOVE_HISTORY_EVENT, LIVE_MOVE_RESULT_EVENT, nextMoveFor, resolveHandText, readTaskAgentState, readTaskCard, taskAgentStatePath as taskStatePathDefault, shellQuote,
|
|
17
|
+
import { cardPrNumber, CLOSED_BY_HAND_EVENT, closeTaskCard, COMPLETION_KINDS, handTask, updateTaskCard, LIVE_MOVE_HISTORY_EVENT, LIVE_MOVE_RESULT_EVENT, nextMoveFor, resolveHandText, readTaskAgentState, readTaskCard, taskAgentStatePath as taskStatePathDefault, shellQuote, TASK_CARD_PREFIX, TASK_SEATS, type CompletionKind, type TaskCard, type TaskLaunchContext, type TaskLaunchReceipt, type TaskLauncher, type TaskSeat } from '../task-agent/task-hand.js';
|
|
13
18
|
import { readCardSalvageBranch, recordWiringLiveVerified, refreshCardRunBinding, type CardRunEvidenceDeps } from '../task-agent/card-evidence.js';
|
|
14
19
|
import { captureReviewResults } from '../task-agent/review-result-capture.js';
|
|
20
|
+
import { replayRemoteCardMoves } from '../task-agent/shadow.js';
|
|
21
|
+
import { remoteLedgerMirrorDirs } from '../task-agent/parent-host.js';
|
|
22
|
+
import { ambiguousGreenSweepPrs, decideGreenSweep, greenSweepCandidates, observeGreenSweep, type GreenSweepPr } from '../task-agent/green-sweep.js';
|
|
15
23
|
import { HAND_MOVE_KINDS, recordHandMove } from '../task-agent/live-move-history.js';
|
|
16
24
|
import { advanceMission, handMission, MissionAdvanceError, openMissionIds, recordPieceRef, type AdvanceMissionResult, type MissionDecompose, type PieceEvidenceReader } from '../task-agent/mission.js';
|
|
17
|
-
import { formatSevenVerbLive, formatTaskAgentCover, measureSevenVerbLive, measureTaskAgentCover, type CoverRow, type SevenVerbLive, type TaskAgentCoverInput } from '../task-agent/cover.js';
|
|
25
|
+
import { formatSevenVerbLive, formatTaskAgentCover, measureSevenVerbLive, measureTaskAgentCover, type CoverRow, type ImplementCardRunBinding, type ImplementRunMergeEvent, type SevenVerbLive, type TaskAgentCoverInput } from '../task-agent/cover.js';
|
|
18
26
|
import { LogStore, STORE_SAFETY_MAX, type LogQuery, type LogStoreRow } from '../mss/logging/log-store.js';
|
|
19
27
|
import { resolveLogTargets } from './logs-cli.js';
|
|
20
28
|
|
|
@@ -31,6 +39,8 @@ export interface TasksCliDeps {
|
|
|
31
39
|
reviewResultNow?: () => Date;
|
|
32
40
|
/** 과제 카드 상태 파일(시험 주입). 기본 = `effectiveInstanceRoot()/task-agent-actions.json`. */
|
|
33
41
|
taskStatePath?: string;
|
|
42
|
+
/** 조합 선택 시험에서만 그래프 저장소 루트를 바꾼다. */
|
|
43
|
+
taskCompositionRoot?: string;
|
|
34
44
|
/** logs.db 싱크 등록(시험 주입). 기본 = `registerStandaloneLogSink`. `hand`·`show` 가 부른다. */
|
|
35
45
|
registerSink?: (surface: string) => Promise<unknown>;
|
|
36
46
|
/** `task hand --mission` 의 분해기(시험 주입). 기본 = 나열형은 결정론 · 아니면 `decomposeSelfDevGoal`. */
|
|
@@ -38,7 +48,15 @@ export interface TasksCliDeps {
|
|
|
38
48
|
/** `task advance` 의 착지 근거 읽기(시험 주입). 기본 = `predecessorState`(gh · 런 원장). */
|
|
39
49
|
pieceEvidence?: PieceEvidenceReader;
|
|
40
50
|
/** `task board` 의 내부 팩 어댑터(시험 주입). 기본 = 현재 판(`checklistDevVersion()`)의 `releasePackBoard` — 읽기만. */
|
|
41
|
-
releaseBoard?: () => BoardNode[];
|
|
51
|
+
releaseBoard?: (version?: string) => BoardNode[];
|
|
52
|
+
/** `tasks hand --checklist` 판 목록(시험 주입). 기본은 발행 전 체크리스트가 있는 판 전부. */
|
|
53
|
+
releaseVersions?: () => string[];
|
|
54
|
+
/** Isolated checklist read for PR adoption. */
|
|
55
|
+
adoptChecklist?: typeof listChecklist;
|
|
56
|
+
/** `tasks adopt-pr` live PR read seam; default is gh in this repository. */
|
|
57
|
+
adoptPrView?: (number: number, cwd: string) => import('../task-agent/adopt-pr.js').AdoptPrView;
|
|
58
|
+
/** Isolated ledger proof for a launched card being adopted. */
|
|
59
|
+
adoptRunTerminal?: typeof import('../task-agent/adopt-pr.js').adoptedRunTerminal;
|
|
42
60
|
/** `tasks show`·`advance` 의 런 근거 읽기(시험 주입) — 기본 = 런 원장(이 우주 ⊕ 연합) ⊕ 호스트 logs 의 Pod job-finished. */
|
|
43
61
|
cardRunEvidence?: CardRunEvidenceDeps;
|
|
44
62
|
/** `tasks live-verified` 가 적는 런 원장 디렉터리(시험 주입). 기본 = `runLedgerDir()`. */
|
|
@@ -49,15 +67,26 @@ export interface TasksCliDeps {
|
|
|
49
67
|
staleParents?: import('../task-agent/parent-host.js').StaleParentDeps;
|
|
50
68
|
/** `tasks adopt-runs` 의 백필 의존(시험 주입 · 등록부·상태 파일). */
|
|
51
69
|
adoptRuns?: import('../task-agent/run-adopt.js').RunAdoptDeps;
|
|
52
|
-
/** `tasks cover` logs 조회 seam. 기본은
|
|
53
|
-
|
|
54
|
-
coverLogs?: (query: LogQuery) => LogStoreRow[] | CoverLogRead;
|
|
70
|
+
/** `tasks cover` logs 조회 seam. 기본은 운영 우주만, --include-test 때 시험 우주도 읽는다. */
|
|
71
|
+
coverLogs?: (query: LogQuery, includeTest?: boolean) => LogStoreRow[] | CoverLogRead;
|
|
55
72
|
/** `tasks cover --by-card` 카드 원장 읽기 seam(읽기 전용). 플래그가 있을 때만 부른다. 기본 = TA 카드 원장(task-agent-actions.json). null = 못 읽음. */
|
|
56
73
|
coverCards?: () => CoverCardRow[] | null | Promise<CoverCardRow[] | null>;
|
|
74
|
+
/** Read-only authored goal provenance; null means the authoring ledger could not be read. */
|
|
75
|
+
coverGoalAsks?: () => { authorRunId: string; goalId: string; createdAt: string }[] | null;
|
|
76
|
+
/** Read-only terminal goal records; null means the goal ledger could not be read. */
|
|
77
|
+
coverGoalRuns?: () => { goalId: string; outcome: string; stage?: string; startedAt: string; completedAt?: string }[] | null;
|
|
78
|
+
/** 구현 출처 시험 seam. 생략하면 task-agent-actions.json을 읽고, null이면 카드 원장을 못 읽은 것이다. */
|
|
79
|
+
coverImplementCardRuns?: () => readonly ImplementCardRunBinding[] | null;
|
|
80
|
+
/** 구현 출처 시험 seam. 생략하면 로컬·연합·원격 거울 run-ledger를 읽고, null이면 못 읽은 것이다. */
|
|
81
|
+
coverImplementRunMerges?: () => ReadonlyMap<string, ImplementRunMergeEvent> | null;
|
|
82
|
+
/** Read test-universe ledgers only when --include-test was requested. */
|
|
83
|
+
coverIncludeTest?: boolean;
|
|
57
84
|
/** TA-REJUDGE-ON-HEAD — `tasks rejudge` 의 판단 의존(시험 주입 · 기본 = 설정 liveMoves ⊕ 실물 gh·리뷰·재게이트·land). */
|
|
58
85
|
rejudge?: Partial<import('../task-agent/rejudge-on-head.js').RejudgeDeps>;
|
|
59
86
|
/** TA-REJUDGE-ON-HEAD — `tasks regate-run` 의 재게이트(시험 주입 · 기본 `runHostRegate`). */
|
|
60
87
|
hostRegate?: (input: import('../self-implement/host-regate.js').HostRegateInput) => Promise<import('../self-implement/host-regate.js').HostRegateResult>;
|
|
88
|
+
/** Isolated regate-run tests can disable the asynchronous completion callback explicitly. */
|
|
89
|
+
regateCompletion?: false;
|
|
61
90
|
}
|
|
62
91
|
|
|
63
92
|
/** `<조각id>=<값>` 반복 인자. */
|
|
@@ -152,8 +181,8 @@ async function tryRemoteParent(args: string[], cwd: string | undefined, context:
|
|
|
152
181
|
try { debug.log('task-agent', 'parent-host-fallback', { host: target.host, runId: context.runId, gaps, facts }); } catch { /* fail-soft */ }
|
|
153
182
|
notice(`[parent-host] ${target.host} 로 못 띄움 — ${multi ? '다음 호스트 또는 로컬 발사로 간다' : '로컬 발사로 간다'}: ${gaps.join(' · ')}`);
|
|
154
183
|
};
|
|
155
|
-
if (cwd
|
|
156
|
-
failed(targets[0]!, [
|
|
184
|
+
if (cwd) {
|
|
185
|
+
failed(targets[0]!, ['project-target-is-local']);
|
|
157
186
|
return null;
|
|
158
187
|
}
|
|
159
188
|
let launchHoldActive = false;
|
|
@@ -311,8 +340,8 @@ async function mirrorRemoteCardLedger(cardId: string, deps: TasksCliDeps): Promi
|
|
|
311
340
|
try {
|
|
312
341
|
if (deps.remoteLedgerMirror) { deps.remoteLedgerMirror(card); return; }
|
|
313
342
|
const { mirrorRemoteRunLedgers } = await import('../task-agent/parent-host.js');
|
|
314
|
-
const {
|
|
315
|
-
mirrorRemoteRunLedgers(card.parentHost, card.runId,
|
|
343
|
+
const { effectiveInstanceRoot } = await import('../instance/resolve.js');
|
|
344
|
+
mirrorRemoteRunLedgers(card.parentHost, card.runId, effectiveInstanceRoot(), deps.cardRunEvidence?.ssh);
|
|
316
345
|
} catch (error) {
|
|
317
346
|
try { debug.log('task-agent', 'remote-ledger-mirror-failed', { card: cardId, host: card.parentHost.host, runId: card.runId, error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
|
|
318
347
|
}
|
|
@@ -443,8 +472,20 @@ const COVER_QUERY_LIMIT = STORE_SAFETY_MAX;
|
|
|
443
472
|
/** Federated read: rows from every store, whether any single store hit the bound, and how many stores could not be read. */
|
|
444
473
|
export interface CoverLogRead { rows: LogStoreRow[]; limitReached: boolean; unreadableStores: number; storesRead: number }
|
|
445
474
|
|
|
446
|
-
|
|
447
|
-
|
|
475
|
+
/** Land attempts are observed separately from completed land actions; an appended order or skipped move is not a merge. */
|
|
476
|
+
export interface CoverLandSignals {
|
|
477
|
+
liveMoveEvents: number | null;
|
|
478
|
+
executedLiveMoves: number | null;
|
|
479
|
+
landOrderEvents: number | null;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export function formatCoverLandSignals(signals: CoverLandSignals): string {
|
|
483
|
+
const count = (value: number | null) => value === null ? '-' : String(value);
|
|
484
|
+
return `착지 시도 관측: live-move ${count(signals.liveMoveEvents)} · 집행 ${count(signals.executedLiveMoves)} · land-order ${count(signals.landOrderEvents)} (병합 수와 별개)`;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function queryCoverLogs(query: LogQuery, includeTest = false): CoverLogRead {
|
|
488
|
+
const { targets, error } = resolveLogTargets({ all: true, includeTest });
|
|
448
489
|
if (error) throw new Error(error);
|
|
449
490
|
const read: CoverLogRead = { rows: [], limitReached: false, unreadableStores: 0, storesRead: 0 };
|
|
450
491
|
for (const target of targets) {
|
|
@@ -477,6 +518,61 @@ function coverEvents<T>(rows: readonly LogStoreRow[], category: string, event: s
|
|
|
477
518
|
return events;
|
|
478
519
|
}
|
|
479
520
|
|
|
521
|
+
/** A malformed land record makes the numerator unknown; dropping it would assert a false zero. */
|
|
522
|
+
function coverLandMoves(rows: readonly LogStoreRow[]): { card: string; executed: boolean; ok: boolean }[] | null {
|
|
523
|
+
const moves: { card: string; executed: boolean; ok: boolean }[] = [];
|
|
524
|
+
for (const row of rows) {
|
|
525
|
+
if (row.category !== 'task-agent' || row.event !== 'live-move') continue;
|
|
526
|
+
if (typeof row.data !== 'string') return null;
|
|
527
|
+
let data: unknown;
|
|
528
|
+
try { data = JSON.parse(row.data); } catch { return null; }
|
|
529
|
+
if (data === null || typeof data !== 'object' || Array.isArray(data)) return null;
|
|
530
|
+
const move = data as Record<string, unknown>;
|
|
531
|
+
if (typeof move.kind !== 'string') return null;
|
|
532
|
+
if (move.kind !== 'land') continue;
|
|
533
|
+
if (typeof move.card !== 'string' || !move.card || typeof move.executed !== 'boolean'
|
|
534
|
+
|| typeof move.ok !== 'boolean') return null;
|
|
535
|
+
moves.push({ card: move.card, executed: move.executed, ok: move.ok });
|
|
536
|
+
}
|
|
537
|
+
return moves;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/** An incomplete action source cannot provide a certain land numerator. */
|
|
541
|
+
function coverTaskActions(rows: readonly LogStoreRow[]): NonNullable<TaskAgentCoverInput['taskAgentActions']> | null {
|
|
542
|
+
const actions: NonNullable<TaskAgentCoverInput['taskAgentActions']>[number][] = [];
|
|
543
|
+
for (const row of rows) {
|
|
544
|
+
if (row.category !== 'task-agent' || row.event !== 'action') continue;
|
|
545
|
+
if (typeof row.data !== 'string') return null;
|
|
546
|
+
let data: unknown;
|
|
547
|
+
try { data = JSON.parse(row.data); } catch { return null; }
|
|
548
|
+
if (data === null || typeof data !== 'object' || Array.isArray(data)) return null;
|
|
549
|
+
const action = data as Record<string, unknown>;
|
|
550
|
+
if (typeof action.kind !== 'string' || typeof action.result !== 'string') return null;
|
|
551
|
+
if (action.kind === 'land' && action.result === 'done'
|
|
552
|
+
&& (typeof action.taskId !== 'string' || !action.taskId)) return null;
|
|
553
|
+
actions.push({ kind: action.kind as NonNullable<TaskAgentCoverInput['taskAgentActions']>[number]['kind'],
|
|
554
|
+
result: action.result,
|
|
555
|
+
...(typeof action.taskId === 'string' ? { taskId: action.taskId } : {}),
|
|
556
|
+
...(typeof action.reason === 'string' ? { reason: action.reason } : {}) });
|
|
557
|
+
}
|
|
558
|
+
return actions;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function coverLandSteps(rows: readonly LogStoreRow[]): NonNullable<TaskAgentCoverInput['allLands']> | null {
|
|
562
|
+
const steps: NonNullable<TaskAgentCoverInput['allLands']>[number][] = [];
|
|
563
|
+
for (const row of rows) {
|
|
564
|
+
if (row.category !== 'pr.land' || row.event !== 'step') continue;
|
|
565
|
+
if (typeof row.data !== 'string') return null;
|
|
566
|
+
let data: unknown;
|
|
567
|
+
try { data = JSON.parse(row.data); } catch { return null; }
|
|
568
|
+
if (data === null || typeof data !== 'object' || Array.isArray(data)) return null;
|
|
569
|
+
const step = data as Record<string, unknown>;
|
|
570
|
+
if (typeof step.step !== 'string' || typeof step.ok !== 'boolean') return null;
|
|
571
|
+
steps.push({ step: step.step, ok: step.ok });
|
|
572
|
+
}
|
|
573
|
+
return steps;
|
|
574
|
+
}
|
|
575
|
+
|
|
480
576
|
export interface CoverCardRow { id: string; createdAt: string }
|
|
481
577
|
|
|
482
578
|
/**
|
|
@@ -498,13 +594,89 @@ export function coverCardsInWindow(cards: readonly CoverCardRow[] | null, window
|
|
|
498
594
|
}).map(({ id }) => ({ id }));
|
|
499
595
|
}
|
|
500
596
|
|
|
597
|
+
/** Read the card ledger itself. A missing file or malformed row makes the aggregate unknown. */
|
|
598
|
+
function readImplementCardBindings(statePath: string): ImplementCardRunBinding[] {
|
|
599
|
+
if (!existsSync(statePath)) throw new Error('TASK-AGENT card ledger is missing');
|
|
600
|
+
const state = readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(statePath);
|
|
601
|
+
if (state.tasks !== undefined && (!state.tasks || typeof state.tasks !== 'object' || Array.isArray(state.tasks))) {
|
|
602
|
+
throw new Error('TASK-AGENT card ledger is malformed');
|
|
603
|
+
}
|
|
604
|
+
const cards = Object.entries(state.tasks ?? {});
|
|
605
|
+
if (cards.length >= COVER_QUERY_LIMIT) throw new Error('TASK-AGENT card ledger reached query bound');
|
|
606
|
+
return cards.map(([key, card]) => {
|
|
607
|
+
if (!card || typeof card !== 'object' || Array.isArray(card) || typeof card.id !== 'string' || !card.id
|
|
608
|
+
|| card.id !== key || (card.runId != null && typeof card.runId !== 'string')
|
|
609
|
+
|| (card.runChildId != null && typeof card.runChildId !== 'string')) {
|
|
610
|
+
throw new Error('TASK-AGENT card binding is malformed');
|
|
611
|
+
}
|
|
612
|
+
return { cardId: card.id, runId: card.runId, runChildId: card.runChildId };
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function implementLedgerDirectories(statePath: string, includeTest: boolean): string[] {
|
|
617
|
+
const root = dirname(statePath);
|
|
618
|
+
return [...new Set([
|
|
619
|
+
runLedgerDir(root),
|
|
620
|
+
runLedgerDir(),
|
|
621
|
+
...resolveFederatedRunLedgerDirectories({ includeTest }),
|
|
622
|
+
...remoteLedgerMirrorDirs(root),
|
|
623
|
+
])];
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** Read only runs bound to cards. Merge time, rather than card launch time, owns the cover window. */
|
|
627
|
+
function readImplementRunMerges(
|
|
628
|
+
bindings: readonly ImplementCardRunBinding[],
|
|
629
|
+
directories: readonly string[],
|
|
630
|
+
window: { sinceMs?: number; untilMs?: number },
|
|
631
|
+
): ReadonlyMap<string, ImplementRunMergeEvent> {
|
|
632
|
+
const runIds = new Set(bindings.flatMap(({ runId, runChildId }) => [runId, runChildId])
|
|
633
|
+
.filter((id): id is string => typeof id === 'string' && id.length > 0));
|
|
634
|
+
if (runIds.size >= COVER_QUERY_LIMIT) throw new Error('bound run ledger query reached bound');
|
|
635
|
+
const merges = new Map<string, ImplementRunMergeEvent>();
|
|
636
|
+
let eventsRead = 0;
|
|
637
|
+
for (const runId of runIds) {
|
|
638
|
+
let found = false;
|
|
639
|
+
let merged = false;
|
|
640
|
+
let failure: unknown;
|
|
641
|
+
for (const directory of directories) {
|
|
642
|
+
try {
|
|
643
|
+
const loaded = loadRunLedgerWithMetadata(runId, directory);
|
|
644
|
+
if (loaded === null) continue;
|
|
645
|
+
found = true;
|
|
646
|
+
if (loaded.skippedTrailingBytes) throw new Error(`run ledger is incomplete: ${runId}`);
|
|
647
|
+
eventsRead += loaded.entries.length;
|
|
648
|
+
if (eventsRead >= COVER_QUERY_LIMIT) throw new Error('run ledger query reached bound');
|
|
649
|
+
for (const entry of loaded.entries) {
|
|
650
|
+
if (entry.event !== 'merged') continue;
|
|
651
|
+
if (typeof entry.data.merged !== 'boolean') throw new Error(`merged event is malformed: ${runId}`);
|
|
652
|
+
if (!entry.data.merged) continue;
|
|
653
|
+
if (window.sinceMs !== undefined || window.untilMs !== undefined) {
|
|
654
|
+
const at = Date.parse(entry.timestamp ?? '');
|
|
655
|
+
if (!Number.isFinite(at)) throw new Error(`merged event timestamp is missing: ${runId}`);
|
|
656
|
+
if ((window.sinceMs !== undefined && at < window.sinceMs)
|
|
657
|
+
|| (window.untilMs !== undefined && at > window.untilMs)) continue;
|
|
658
|
+
}
|
|
659
|
+
merged = true;
|
|
660
|
+
}
|
|
661
|
+
} catch (error) { failure ??= error; }
|
|
662
|
+
}
|
|
663
|
+
// A local parent may contain only launch events while a federated or mirrored copy has the merge.
|
|
664
|
+
// Read every physical copy. Any unreadable copy leaves the aggregate unknown.
|
|
665
|
+
if (failure !== undefined) throw failure;
|
|
666
|
+
if (!found) throw new Error(`bound run ledger is missing: ${runId}`);
|
|
667
|
+
merges.set(runId, { merged });
|
|
668
|
+
}
|
|
669
|
+
return merges;
|
|
670
|
+
}
|
|
671
|
+
|
|
501
672
|
export function readTaskAgentCover(
|
|
502
673
|
window: { sinceMs?: number; untilMs?: number } = {},
|
|
503
674
|
query: NonNullable<TasksCliDeps['coverLogs']> = queryCoverLogs,
|
|
504
675
|
taskCards?: readonly { id: string }[] | null,
|
|
505
|
-
|
|
676
|
+
goalReaders: Pick<TasksCliDeps, 'coverGoalAsks' | 'coverGoalRuns' | 'coverImplementCardRuns' | 'coverImplementRunMerges' | 'taskStatePath' | 'coverIncludeTest'> = {},
|
|
677
|
+
): { rows: CoverRow[]; sevenVerbs: SevenVerbLive; landSignals: CoverLandSignals; limitReached: boolean; unreadableStores: number } {
|
|
506
678
|
let unreadableStores = 0;
|
|
507
|
-
const read = (category: 'task-agent' | 'pr.land', event: 'action' | 'step'): { rows: LogStoreRow[]; limitReached: boolean } | null => {
|
|
679
|
+
const read = (category: 'task-agent' | 'pr.land' | 'task-agent.land-order', event: 'action' | 'step' | 'live-move' | 'appended'): { rows: LogStoreRow[]; limitReached: boolean } | null => {
|
|
508
680
|
try {
|
|
509
681
|
const result = query({ ...window, limit: COVER_QUERY_LIMIT, exactCategories: [category], events: [event] });
|
|
510
682
|
if (Array.isArray(result)) return { rows: result, limitReached: result.length >= COVER_QUERY_LIMIT };
|
|
@@ -514,19 +686,83 @@ export function readTaskAgentCover(
|
|
|
514
686
|
};
|
|
515
687
|
const actionRead = read('task-agent', 'action');
|
|
516
688
|
const landRead = read('pr.land', 'step');
|
|
689
|
+
const moveRead = read('task-agent', 'live-move');
|
|
690
|
+
const orderRead = read('task-agent.land-order', 'appended');
|
|
517
691
|
const actions = actionRead?.rows ?? null;
|
|
518
692
|
const lands = landRead?.rows ?? null;
|
|
519
|
-
const truncated = { taskAgentActions: actionRead?.limitReached === true, allLands: landRead?.limitReached === true
|
|
693
|
+
const truncated = { taskAgentActions: actionRead?.limitReached === true, allLands: landRead?.limitReached === true,
|
|
694
|
+
liveLandMoves: moveRead?.limitReached === true };
|
|
695
|
+
const moves = moveRead && !moveRead.limitReached ? coverLandMoves(moveRead.rows) : null;
|
|
696
|
+
const inWindow = (value: string): boolean => {
|
|
697
|
+
const time = Date.parse(value);
|
|
698
|
+
return Number.isFinite(time) && (window.sinceMs === undefined || time >= window.sinceMs) && (window.untilMs === undefined || time <= window.untilMs);
|
|
699
|
+
};
|
|
700
|
+
let goalAsks: ReturnType<NonNullable<TasksCliDeps['coverGoalAsks']>> = null;
|
|
701
|
+
let goalRuns: ReturnType<NonNullable<TasksCliDeps['coverGoalRuns']>> = null;
|
|
702
|
+
let goalAsksTruncated = false;
|
|
703
|
+
let goalRunsTruncated = false;
|
|
704
|
+
try {
|
|
705
|
+
if (goalReaders.coverGoalAsks) goalAsks = goalReaders.coverGoalAsks();
|
|
706
|
+
else {
|
|
707
|
+
const asks = loadGoalAskRecords({ limit: COVER_QUERY_LIMIT }, goalRunDbPath());
|
|
708
|
+
goalAsksTruncated = asks.length >= COVER_QUERY_LIMIT;
|
|
709
|
+
goalAsks = asks;
|
|
710
|
+
}
|
|
711
|
+
} catch { goalAsks = null; }
|
|
712
|
+
try {
|
|
713
|
+
if (goalReaders.coverGoalRuns) goalRuns = goalReaders.coverGoalRuns();
|
|
714
|
+
else {
|
|
715
|
+
// Cover consumes only goal identity and terminal outcome. Child-brain enrichment would query every
|
|
716
|
+
// runId against every registered test logs.db, even though this measurement never reads childBrain.
|
|
717
|
+
const runs = loadGoalRunQuery({ limit: COVER_QUERY_LIMIT }, goalRunDbPath(), { targets: [] });
|
|
718
|
+
goalRunsTruncated = runs?.truncated ?? false;
|
|
719
|
+
goalRuns = runs?.records.map(({ goalId, record }) => ({
|
|
720
|
+
goalId, outcome: record.outcome, stage: record.stage, startedAt: record.startedAt ?? '', completedAt: record.completedAt,
|
|
721
|
+
})) ?? null;
|
|
722
|
+
}
|
|
723
|
+
} catch { goalRuns = null; }
|
|
724
|
+
const authoredGoalIds = new Set(goalAsks?.map((ask) => ask.goalId) ?? []);
|
|
520
725
|
const input: TaskAgentCoverInput = {
|
|
521
|
-
taskAgentActions: actions === null ? null :
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
726
|
+
taskAgentActions: actions === null ? null : coverTaskActions(actions),
|
|
727
|
+
liveLandMoves: moves,
|
|
728
|
+
allLands: lands === null ? null : coverLandSteps(lands),
|
|
729
|
+
truncated: { ...truncated, goalAsks: goalAsksTruncated, goalRuns: goalRunsTruncated },
|
|
730
|
+
// Authoring may start before this window; completion is the event being counted.
|
|
731
|
+
goalAsks,
|
|
732
|
+
goalRuns: goalRuns && (window.sinceMs !== undefined || window.untilMs !== undefined)
|
|
733
|
+
? goalRuns.some((run) => authoredGoalIds.has(run.goalId)
|
|
734
|
+
&& run.stage === 'merged' && (run.outcome === 'completed' || run.outcome === 'merged')
|
|
735
|
+
&& !Number.isFinite(Date.parse(run.completedAt ?? '')))
|
|
736
|
+
? null : goalRuns.filter((run) => inWindow(run.completedAt ?? ''))
|
|
737
|
+
: goalRuns,
|
|
527
738
|
...(taskCards === undefined ? {} : { taskCards }),
|
|
528
739
|
};
|
|
529
|
-
|
|
740
|
+
// A supplied reader overrides only its own source. The ordinary CLI always reads both physical ledgers.
|
|
741
|
+
try {
|
|
742
|
+
input.implementCardRuns = goalReaders.coverImplementCardRuns
|
|
743
|
+
? goalReaders.coverImplementCardRuns()
|
|
744
|
+
: readImplementCardBindings(goalReaders.taskStatePath ?? taskStatePathDefault());
|
|
745
|
+
} catch { input.implementCardRuns = null; }
|
|
746
|
+
try {
|
|
747
|
+
input.implementRunMerges = goalReaders.coverImplementRunMerges
|
|
748
|
+
? goalReaders.coverImplementRunMerges()
|
|
749
|
+
: input.implementCardRuns === null
|
|
750
|
+
? null
|
|
751
|
+
: readImplementRunMerges(
|
|
752
|
+
input.implementCardRuns,
|
|
753
|
+
implementLedgerDirectories(goalReaders.taskStatePath ?? taskStatePathDefault(), goalReaders.coverIncludeTest === true),
|
|
754
|
+
window,
|
|
755
|
+
);
|
|
756
|
+
} catch { input.implementRunMerges = null; }
|
|
757
|
+
const orders = orderRead && !orderRead.limitReached ? coverEvents(orderRead.rows, 'task-agent.land-order', 'appended', (data) =>
|
|
758
|
+
Number.isSafeInteger(data.pr) ? { pr: data.pr as number } : null) : null;
|
|
759
|
+
const landSignals: CoverLandSignals = {
|
|
760
|
+
liveMoveEvents: moves?.length ?? null,
|
|
761
|
+
executedLiveMoves: moves?.filter((move) => move.executed).length ?? null,
|
|
762
|
+
landOrderEvents: orders?.length ?? null,
|
|
763
|
+
};
|
|
764
|
+
return { rows: measureTaskAgentCover(input), sevenVerbs: measureSevenVerbLive(input), landSignals,
|
|
765
|
+
limitReached: truncated.taskAgentActions || truncated.allLands || moveRead?.limitReached === true || orderRead?.limitReached === true || goalAsksTruncated || goalRunsTruncated, unreadableStores };
|
|
530
766
|
}
|
|
531
767
|
|
|
532
768
|
export function registerTasksCommands(program: Command, deps: TasksCliDeps = {}): void {
|
|
@@ -541,6 +777,83 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
541
777
|
};
|
|
542
778
|
const tasks = program.command('tasks').alias('task').description('TOX 태스크 조회 및 승인 · TASK-AGENT 과제 넘기기(hand)');
|
|
543
779
|
|
|
780
|
+
tasks.command('green-sweep')
|
|
781
|
+
.description('병합된 과제 PR 과 칸 근거를 대조해 green/기록만/건너뜀을 미리 본다 (항상 dry-run)')
|
|
782
|
+
.requiredOption('--version <version>', '조회할 체크리스트 판')
|
|
783
|
+
.option('--cell <id>', '한 칸만 조회')
|
|
784
|
+
.option('--traffic <path>', '실물 green 판정에는 traffic.json 필수 — 빠지거나 못 읽으면 hold 불명으로 보수 판정')
|
|
785
|
+
.option('--json', 'JSON 출력')
|
|
786
|
+
.action((opts: { version: string; cell?: string; traffic?: string; json?: boolean }) => {
|
|
787
|
+
try {
|
|
788
|
+
validateVersion(opts.version);
|
|
789
|
+
const snapshot = listChecklist(opts.version);
|
|
790
|
+
const cards = Object.values(readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(deps.taskStatePath ?? taskStatePathDefault()).tasks ?? {});
|
|
791
|
+
let holds: Set<string> | null = null;
|
|
792
|
+
if (opts.traffic) {
|
|
793
|
+
try {
|
|
794
|
+
const traffic: unknown = JSON.parse(readFileSync(opts.traffic, 'utf8'));
|
|
795
|
+
if (traffic && typeof traffic === 'object' && !Array.isArray(traffic)
|
|
796
|
+
&& Array.isArray((traffic as { hold?: unknown }).hold)
|
|
797
|
+
&& (traffic as { hold: unknown[] }).hold.every((id) => typeof id === 'string'))
|
|
798
|
+
holds = new Set((traffic as { hold: string[] }).hold);
|
|
799
|
+
} catch { /* unknown is not an empty hold set */ }
|
|
800
|
+
}
|
|
801
|
+
const prCache = new Map<number, GreenSweepPr | null>();
|
|
802
|
+
const ambiguousPrs = ambiguousGreenSweepPrs(snapshot.items, opts.version, cards);
|
|
803
|
+
const origin = spawnSync('git', ['remote', 'get-url', 'origin'],
|
|
804
|
+
{ cwd: process.cwd(), encoding: 'utf8', timeout: 20_000 });
|
|
805
|
+
const originRepo = origin.status === 0
|
|
806
|
+
? /github\.com[:/]([^/\s]+\/[^/\s]+?)(?:\.git)?$/i.exec(origin.stdout.trim())?.[1]?.toLowerCase() : undefined;
|
|
807
|
+
const localMain = spawnSync('git', ['rev-parse', 'origin/main'],
|
|
808
|
+
{ cwd: process.cwd(), encoding: 'utf8', timeout: 20_000 });
|
|
809
|
+
const remoteMain = spawnSync('git', ['ls-remote', 'origin', 'refs/heads/main'],
|
|
810
|
+
{ cwd: process.cwd(), encoding: 'utf8', timeout: 20_000 });
|
|
811
|
+
const localMainSha = localMain.status === 0 ? localMain.stdout.trim() : '';
|
|
812
|
+
const remoteMainSha = remoteMain.status === 0 ? remoteMain.stdout.trim().split(/\s+/)[0] : '';
|
|
813
|
+
const mainFresh = /^[a-f0-9]{40}$/i.test(localMainSha) && localMainSha === remoteMainSha;
|
|
814
|
+
if (opts.cell && !snapshot.items.some((item) => item.id === opts.cell)) throw new Error(`없는 칸: ${opts.cell}`);
|
|
815
|
+
const decisions = snapshot.items.filter((item) => !opts.cell || item.id === opts.cell).flatMap((item) => {
|
|
816
|
+
const candidates = greenSweepCandidates(item, opts.version, cards);
|
|
817
|
+
return candidates.map((candidate) => {
|
|
818
|
+
let pr = prCache.get(candidate.pr);
|
|
819
|
+
if (pr === undefined) {
|
|
820
|
+
const view = spawnSync('gh', ['pr', 'view', String(candidate.pr), '--json', 'number,state,mergeCommit,url'],
|
|
821
|
+
{ cwd: process.cwd(), encoding: 'utf8', timeout: 20_000 });
|
|
822
|
+
try {
|
|
823
|
+
if (view.status !== 0) throw new Error('gh failed');
|
|
824
|
+
const parsed = JSON.parse(view.stdout) as { number?: number; state?: string; mergeCommit?: { oid?: string }; url?: string };
|
|
825
|
+
pr = parsed.number === candidate.pr && typeof parsed.state === 'string'
|
|
826
|
+
? { number: candidate.pr, state: parsed.state, mergeSha: parsed.mergeCommit?.oid, url: parsed.url } : null;
|
|
827
|
+
} catch { pr = null; }
|
|
828
|
+
prCache.set(candidate.pr, pr);
|
|
829
|
+
}
|
|
830
|
+
const sha = pr?.mergeSha;
|
|
831
|
+
const prRepo = /^https:\/\/github\.com\/([^/]+\/[^/]+)\/pull\/\d+(?:\/|$)/i.exec(pr?.url ?? '')?.[1]?.toLowerCase();
|
|
832
|
+
const repositoryMatches = originRepo && prRepo ? originRepo === prRepo : null;
|
|
833
|
+
const ancestor = !mainFresh || !sha || !/^[a-f0-9]{40}$/i.test(sha) ? null : (() => {
|
|
834
|
+
const check = spawnSync('git', ['merge-base', '--is-ancestor', sha, 'origin/main'],
|
|
835
|
+
{ cwd: process.cwd(), encoding: 'utf8', timeout: 20_000 });
|
|
836
|
+
return check.status === 0 ? true : check.status === 1 ? false : null;
|
|
837
|
+
})();
|
|
838
|
+
const decision = decideGreenSweep({ item, candidate, candidateCount: candidates.length,
|
|
839
|
+
pr: pr ?? null, ancestor, hold: holds === null ? null : holds.has(item.id), repositoryMatches,
|
|
840
|
+
ambiguousAcrossCells: ambiguousPrs.has(candidate.pr) });
|
|
841
|
+
observeGreenSweep(decision);
|
|
842
|
+
return decision;
|
|
843
|
+
});
|
|
844
|
+
});
|
|
845
|
+
const counts = { candidate: decisions.length, green: decisions.filter((d) => d.verdict === 'green').length,
|
|
846
|
+
recordOnly: decisions.filter((d) => d.verdict === 'record-only').length,
|
|
847
|
+
skipped: decisions.filter((d) => d.verdict === 'skip').length };
|
|
848
|
+
if (opts.json) out(JSON.stringify({ dryRun: true, version: opts.version, counts, decisions }));
|
|
849
|
+
else {
|
|
850
|
+
out(`dry-run · ${opts.version} · 후보 ${counts.candidate} · green ${counts.green} · 기록만 ${counts.recordOnly} · 건너뜀 ${counts.skipped}`);
|
|
851
|
+
for (const d of decisions) out(`${d.verdict}\t${d.cell}\t#${d.pr}\t${d.mergeSha ?? '-'}\t${d.reason}\t${d.evidence}\tbefore=${JSON.stringify(d.existingEvidence)}\tafter=${JSON.stringify(d.plannedEvidence)}`);
|
|
852
|
+
if (holds === null) out('⚠ hold 원장을 읽지 못해 green 없이 기록만 판정');
|
|
853
|
+
}
|
|
854
|
+
} catch (error) { out(`green-sweep 조회 실패: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
|
|
855
|
+
});
|
|
856
|
+
|
|
544
857
|
tasks.command('cover')
|
|
545
858
|
.description('TASK-AGENT 가 대신한 착지 비율과 리뷰·재발사·green 의 아직 없는 분모를 조회(읽기 전용)')
|
|
546
859
|
.option('--since <iso>', '시작 시각 (ISO)')
|
|
@@ -548,7 +861,8 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
548
861
|
.option('--json', 'JSON 출력')
|
|
549
862
|
.option('--lines', '동사별 한 줄 요약 출력 (읽기 전용)')
|
|
550
863
|
.option('--by-card', 'review·retry·green 분모를 카드 원장(창 안 createdAt 카드 수)으로 센다 (읽기 전용)')
|
|
551
|
-
.
|
|
864
|
+
.option('--include-test', '시험 우주 logs.db 도 운영 우주와 합산(기본은 운영만)')
|
|
865
|
+
.action(async (opts: { since?: string; until?: string; json?: boolean; lines?: boolean; byCard?: boolean; includeTest?: boolean }) => {
|
|
552
866
|
const parseIso = (value: string | undefined, flag: string): number | undefined => {
|
|
553
867
|
if (value === undefined) return undefined;
|
|
554
868
|
const time = Date.parse(value);
|
|
@@ -568,18 +882,23 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
568
882
|
}
|
|
569
883
|
taskCards = coverCardsInWindow(cards, { sinceMs, untilMs });
|
|
570
884
|
}
|
|
571
|
-
const
|
|
885
|
+
const scopedCoverLogs = (query: LogQuery) => (deps.coverLogs ?? queryCoverLogs)(query, opts.includeTest === true);
|
|
886
|
+
const { rows, sevenVerbs, landSignals, limitReached, unreadableStores } = readTaskAgentCover(
|
|
887
|
+
{ sinceMs, untilMs }, scopedCoverLogs, taskCards, { ...deps, coverIncludeTest: opts.includeTest === true },
|
|
888
|
+
);
|
|
572
889
|
const limitWarning = limitReached ? `조회 상한 ${COVER_QUERY_LIMIT} 도달 — 더 오래된 사건이 있을 수 있다(절단된 수를 전체로 읽지 말 것)` : null;
|
|
573
890
|
const storeWarning = unreadableStores > 0 ? `logs.db ${unreadableStores}개를 못 읽었다 — 그 우주의 사건은 수에 없다(«없다»로 읽지 말 것)` : null;
|
|
574
|
-
if (opts.json) out(JSON.stringify({ rows, sevenVerbs, limitReached, unreadableStores, ...(limitWarning ? { warning: limitWarning } : {}), ...(storeWarning ? { storeWarning } : {}) }));
|
|
891
|
+
if (opts.json) out(JSON.stringify({ rows, sevenVerbs, landSignals, limitReached, unreadableStores, ...(limitWarning ? { warning: limitWarning } : {}), ...(storeWarning ? { storeWarning } : {}) }));
|
|
575
892
|
else {
|
|
576
893
|
if (opts.lines) {
|
|
577
894
|
for (const row of rows) out(formatTaskAgentCover(row));
|
|
578
895
|
out(formatSevenVerbLive(sevenVerbs));
|
|
896
|
+
out(formatCoverLandSignals(landSignals));
|
|
579
897
|
}
|
|
580
898
|
else {
|
|
581
899
|
out('동사\t대신\t전체\t비율\t상태\t관측 사건');
|
|
582
900
|
for (const row of rows) out(`${row.verb}\t${row.byTaskAgent ?? '-'}\t${row.total ?? '-'}\t${row.ratio ?? '-'}\t${row.state}\t${row.observedActions ?? '-'}`);
|
|
901
|
+
out(formatCoverLandSignals(landSignals));
|
|
583
902
|
}
|
|
584
903
|
if (limitWarning) out(`⚠ ${limitWarning}`);
|
|
585
904
|
if (storeWarning) out(`⚠ ${storeWarning}`);
|
|
@@ -624,6 +943,7 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
624
943
|
.description('TASK-AGENT 에 과제 한 줄을 넘긴다 — 카드를 적고 첫 수(harness say --substrate pod --merge-by-host)를 고른다 · 기본 shadow(발사 0) · `--mission` 이면 조각 카드 ⊕ 선후로 쪼개 선행 없는 조각만 넘긴다')
|
|
625
944
|
.addOption(new Option('--seat <seat>', `과제 자리 (${TASK_SEATS.join('|')})`).choices([...TASK_SEATS]))
|
|
626
945
|
.option('--checklist <cellId>', '릴리스 체크리스트 칸 id')
|
|
946
|
+
.option('--version <version>', '체크리스트 판 고정(생략하면 발행 전 판을 가까운 순서로 탐색)')
|
|
627
947
|
.addOption(new Option('--completion <kind>', `종결 종류 (${COMPLETION_KINDS.join('|')} · 기본 code-pr = PR 병합)`).choices([...COMPLETION_KINDS]))
|
|
628
948
|
.option('--mission <text>', '미션 문면 — 조각(`- …`·`① …`·`1. …` ⊕ `after: 1`)으로 나열돼 있으면 결정론, 아니면 분해기로 쪼갠다')
|
|
629
949
|
.option('--project <id>', '프로젝트 id — --target 과 함께')
|
|
@@ -635,7 +955,72 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
635
955
|
.option('--child-llm <provider/model>', 'TASKS-HAND-CHILD-LLM — 구현 자식 LLM(예: grok/grok-4.7) → 발사 인자 --child-llm-provider <provider> --child-llm-model <model> · «첫» `/` 에서만 가른다(모델 id 의 `/` 는 보존) · openrouter 는 모델 id 가 openrouter/ 접두를 포함하므로 전체를 모델로(openrouter/z-ai/glm-5.3 → --child-llm-model openrouter/z-ai/glm-5.3) · 카드 childLlm · 안 주면 명령·카드 무변경')
|
|
636
956
|
.option('--child-llm-effort <level>', '구현 자식 추론 노력 → 발사 인자 --child-llm-effort(검증은 하니스 · --child-llm 과 함께)')
|
|
637
957
|
.option('--json', 'JSON 출력')
|
|
638
|
-
.action(async (text: string | undefined, opts: { seat?: TaskSeat; checklist?: string; completion?: CompletionKind; mission?: string; project?: string; target?: string; goal?: string; milestone?: string; live?: boolean; taLand?: boolean; childLlm?: string; childLlmEffort?: string; json?: boolean }) => {
|
|
958
|
+
.action(async (text: string | undefined, opts: { seat?: TaskSeat; checklist?: string; version?: string; completion?: CompletionKind; mission?: string; project?: string; target?: string; goal?: string; milestone?: string; live?: boolean; taLand?: boolean; childLlm?: string; childLlmEffort?: string; json?: boolean }) => {
|
|
959
|
+
const suppliedText = text !== undefined;
|
|
960
|
+
if (opts.version !== undefined && opts.checklist === undefined) {
|
|
961
|
+
out('--version 은 --checklist 와 함께 준다');
|
|
962
|
+
process.exitCode = 1;
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
if (opts.checklist !== undefined) {
|
|
966
|
+
const findTitle = (nodes: BoardNode[]): string | undefined => {
|
|
967
|
+
for (const node of nodes) {
|
|
968
|
+
if (node.level === 'task' && node.id === opts.checklist) return node.title;
|
|
969
|
+
const nested = node.children ? findTitle(node.children) : undefined;
|
|
970
|
+
if (nested !== undefined) return nested;
|
|
971
|
+
}
|
|
972
|
+
return undefined;
|
|
973
|
+
};
|
|
974
|
+
if (opts.version !== undefined) {
|
|
975
|
+
try { validateVersion(opts.version); }
|
|
976
|
+
catch (error) {
|
|
977
|
+
out(error instanceof Error ? error.message : String(error));
|
|
978
|
+
process.exitCode = 1;
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
const versions = opts.version !== undefined ? [opts.version]
|
|
983
|
+
: [checklistDevVersion(), ...(deps.releaseVersions?.() ?? (deps.releaseBoard ? [] : (() => {
|
|
984
|
+
const released = releasedVersion();
|
|
985
|
+
return assignedVersions().filter((version) => !released || compareVersions(version, released) > 0);
|
|
986
|
+
})()))];
|
|
987
|
+
const current = checklistDevVersion();
|
|
988
|
+
const orderedVersions = [...new Set(versions)].sort((a, b) => {
|
|
989
|
+
if (a === current) return -1;
|
|
990
|
+
if (b === current) return 1;
|
|
991
|
+
const aFuture = compareVersions(a, current) > 0;
|
|
992
|
+
const bFuture = compareVersions(b, current) > 0;
|
|
993
|
+
if (aFuture !== bFuture) return aFuture ? -1 : 1;
|
|
994
|
+
return aFuture ? compareVersions(a, b) : compareVersions(b, a);
|
|
995
|
+
});
|
|
996
|
+
const matches: Array<{ version: string; title: string }> = [];
|
|
997
|
+
for (const version of orderedVersions) {
|
|
998
|
+
try {
|
|
999
|
+
const title = findTitle((deps.releaseBoard ?? releasePackBoard)(version));
|
|
1000
|
+
if (title !== undefined) matches.push({ version, title });
|
|
1001
|
+
} catch (error) {
|
|
1002
|
+
if (opts.version !== undefined) {
|
|
1003
|
+
out(`체크리스트 판 읽기 실패: ${version}`);
|
|
1004
|
+
process.exitCode = 1;
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
console.error(`⚠ 체크리스트 판 ${version} 읽기 실패 — 다른 판 계속 조회: ${error instanceof Error ? error.message : String(error)}`);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
const title = matches[0]?.title;
|
|
1011
|
+
if (title === undefined) {
|
|
1012
|
+
out(`체크리스트 칸 없음: ${opts.checklist}`);
|
|
1013
|
+
process.exitCode = 1;
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
if (matches.length > 1) console.error(`⚠ 체크리스트 칸 ${opts.checklist} 중복: ${matches.map((match) => match.version).join(', ')} — 가장 가까운 판 ${matches[0]!.version} 선택 (--version 으로 고정 가능)`);
|
|
1017
|
+
if (opts.mission === undefined && text === undefined) text = title;
|
|
1018
|
+
}
|
|
1019
|
+
if (opts.checklist !== undefined && opts.mission === undefined && suppliedText && !text!.split(/\r?\n/, 1)[0]!.startsWith('대상 경로:')) {
|
|
1020
|
+
out('칸 문면을 쓰려면 문면 인자를 빼라');
|
|
1021
|
+
process.exitCode = 1;
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
639
1024
|
await registerTaskAgentSink(deps.registerSink);
|
|
640
1025
|
if ((text === undefined) === (opts.mission === undefined)) {
|
|
641
1026
|
out('과제 한 줄 «또는» --mission <문면> 중 하나만 준다');
|
|
@@ -705,6 +1090,7 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
705
1090
|
...(opts.childLlm !== undefined ? { childLlm: opts.childLlm } : {}),
|
|
706
1091
|
...(opts.childLlmEffort !== undefined ? { childLlmEffort: opts.childLlmEffort } : {}),
|
|
707
1092
|
...(deps.taskStatePath ? { statePath: deps.taskStatePath } : {}),
|
|
1093
|
+
...(deps.taskCompositionRoot ? { compositionRoot: deps.taskCompositionRoot } : {}),
|
|
708
1094
|
launcher: deps.taskLauncher ?? defaultTaskLauncher,
|
|
709
1095
|
});
|
|
710
1096
|
if (opts.json) out(JSON.stringify(result));
|
|
@@ -712,7 +1098,7 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
712
1098
|
out(`${result.card.id}\t${result.mode}\t${result.launched ? 'launched' : 'not launched'}`);
|
|
713
1099
|
out(`elanous ${result.move.command!.map(shellQuote).join(' ')}`);
|
|
714
1100
|
if (result.cwd) out(`(cwd: ${result.cwd})`);
|
|
715
|
-
if (result.card.taLand) out('(ta-land — 발사 env ELANOUS_TA_LAND=1 · 로컬 부모 · taskAgent.liveMoves 에 propose-land 가 있으면 병합은 TASK-AGENT 몫, 없으면 종전대로 호스트 병합)');
|
|
1101
|
+
if (result.card.taLand) out('(ta-land — 발사 env ELANOUS_TA_LAND=1 · 원격 또는 로컬 부모 · taskAgent.liveMoves 에 propose-land 가 있으면 병합은 TASK-AGENT 몫, 없으면 종전대로 호스트 병합)');
|
|
716
1102
|
if (!result.launched) out('(shadow — 띄우지 않았다 · 띄우려면 --live)');
|
|
717
1103
|
}
|
|
718
1104
|
} catch (error) {
|
|
@@ -820,6 +1206,74 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
820
1206
|
for (const row of report.rows) out([row.pid, row.ageHours, row.seat ?? '-', row.version ?? '-', row.runId ?? '-', row.reasons.join(',')].join('\t'));
|
|
821
1207
|
});
|
|
822
1208
|
|
|
1209
|
+
tasks.command('adopt-pr <number>')
|
|
1210
|
+
.description('기존 OPEN/main PR을 TA 카드에 묶고 다음 tasks rejudge 대상에 넣는다(구현 발사 0)')
|
|
1211
|
+
.option('--card <id>', '기존 launched 카드(미바인딩 또는 같은 PR이 등록부에서 바인딩됨)')
|
|
1212
|
+
.option('--checklist <id>', '새 카드에 붙일 체크리스트 칸')
|
|
1213
|
+
.option('--version <version>', '체크리스트 판(기존 카드에도 고정 가능)')
|
|
1214
|
+
.option('--unlinked-terminal', '기존 카드의 모든 구현 자식이 종결되고 PR 사건이 전혀 없을 때만 별도로 열린 PR을 명시적으로 입양')
|
|
1215
|
+
.option('--json', 'JSON 출력')
|
|
1216
|
+
.action(async (value: string, opts: { card?: string; checklist?: string; version?: string; unlinkedTerminal?: boolean; json?: boolean }) => {
|
|
1217
|
+
await registerTaskAgentSink(deps.registerSink);
|
|
1218
|
+
try {
|
|
1219
|
+
if (!/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(Number(value))) throw new Error(`PR 번호가 아니다: ${value}`);
|
|
1220
|
+
if ((opts.card === undefined) === (opts.checklist === undefined))
|
|
1221
|
+
throw new Error('--card 또는 --checklist [--version] 중 하나만 준다');
|
|
1222
|
+
if (opts.unlinkedTerminal && !opts.card) throw new Error('--unlinked-terminal은 --card와 함께 준다');
|
|
1223
|
+
if (opts.version) validateVersion(opts.version);
|
|
1224
|
+
let cardVersion = opts.version;
|
|
1225
|
+
let observedChecklist: { id: string; version: string; status: string; cardVersion?: string } | undefined;
|
|
1226
|
+
if (opts.card) {
|
|
1227
|
+
const current = readTaskCard(opts.card, deps.taskStatePath ?? taskStatePathDefault());
|
|
1228
|
+
if (!current?.checklistId) throw new Error(`카드 ${opts.card}의 칸이 없다`);
|
|
1229
|
+
const versions = opts.version ? [opts.version] : [...new Set([checklistDevVersion(), ...(deps.releaseVersions?.() ?? assignedVersions())])];
|
|
1230
|
+
const matches = versions.filter((version) => {
|
|
1231
|
+
try { return (deps.adoptChecklist ?? listChecklist)(version).items.some((item) => item.id === current.checklistId); }
|
|
1232
|
+
catch (error) { if (opts.version) throw error; return false; }
|
|
1233
|
+
});
|
|
1234
|
+
if (matches.length !== 1) throw new Error(matches.length ? `칸 ${current.checklistId}가 여러 판에 있다 — --version 지정: ${matches.join(', ')}` : `체크리스트 칸 없음: ${current.checklistId}`);
|
|
1235
|
+
cardVersion = matches[0]!;
|
|
1236
|
+
const item = (deps.adoptChecklist ?? listChecklist)(cardVersion).items.find((candidate) => candidate.id === current.checklistId)!;
|
|
1237
|
+
if (item.status === 'green' || item.status === 'done') throw new Error(`이미 완료된 칸은 새 PR 입양 대상이 아니다: ${cardVersion}/${item.id}`);
|
|
1238
|
+
observedChecklist = { id: item.id, version: cardVersion, status: item.status, cardVersion: current.checklistVersion };
|
|
1239
|
+
}
|
|
1240
|
+
let checklist: { version: string; id: string; title: string; status: string } | undefined;
|
|
1241
|
+
if (opts.checklist) {
|
|
1242
|
+
const versions = opts.version ? [opts.version] : [...new Set([checklistDevVersion(), ...(deps.releaseVersions?.() ?? assignedVersions())])];
|
|
1243
|
+
const matches = versions.flatMap((version) => {
|
|
1244
|
+
try { return (deps.adoptChecklist ?? listChecklist)(version).items.filter((item) => item.id === opts.checklist).map((item) => ({ version, id: item.id, title: item.title, status: item.status })); }
|
|
1245
|
+
catch (error) { if (opts.version) throw error; return []; }
|
|
1246
|
+
});
|
|
1247
|
+
if (matches.length !== 1) throw new Error(matches.length ? `칸 ${opts.checklist}가 여러 판에 있다 — --version 지정: ${matches.map((item) => item.version).join(', ')}` : `체크리스트 칸 없음: ${opts.checklist}`);
|
|
1248
|
+
checklist = matches[0];
|
|
1249
|
+
}
|
|
1250
|
+
const number = Number(value);
|
|
1251
|
+
const origin = execFileSync('git', ['remote', 'get-url', 'origin'], { cwd: process.cwd(), encoding: 'utf8' }).trim().replace(/\.git\/?$/, '');
|
|
1252
|
+
const repo = origin.match(/^(?:git@github\.com:|https:\/\/github\.com\/|ssh:\/\/git@github\.com\/)([^/\s]+\/[^/\s]+)\/?$/)?.[1];
|
|
1253
|
+
if (!repo) throw new Error(`현재 worktree origin이 GitHub 저장소가 아니다: ${origin}`);
|
|
1254
|
+
const view = (deps.adoptPrView ?? ((pr: number, cwd: string) => JSON.parse(execFileSync('gh', ['pr', 'view', String(pr), '--repo', repo, '--json', 'number,url,state,baseRefName,headRefOid'], { cwd, encoding: 'utf8' }))))(number, process.cwd());
|
|
1255
|
+
if (view.number !== number) throw new Error(`조회 PR 번호가 다르다: 요청 #${number} · 응답 #${view.number}`);
|
|
1256
|
+
const { adoptExistingPr, adoptedRunTerminal } = await import('../task-agent/adopt-pr.js');
|
|
1257
|
+
const targetCard = opts.card ? readTaskCard(opts.card, deps.taskStatePath ?? taskStatePathDefault()) : undefined;
|
|
1258
|
+
const checkRun = deps.adoptRunTerminal ?? adoptedRunTerminal;
|
|
1259
|
+
const linkedEvidence = targetCard ? checkRun(targetCard, view) : undefined;
|
|
1260
|
+
const terminalRunEvidence = linkedEvidence || (opts.unlinkedTerminal && targetCard ? checkRun(targetCard, view, undefined, true) : false);
|
|
1261
|
+
const unlinkedTerminalUsed = opts.unlinkedTerminal === true && linkedEvidence !== true;
|
|
1262
|
+
const chosen = observedChecklist ?? checklist;
|
|
1263
|
+
const result = adoptExistingPr({ pr: view, repo, statePath: deps.taskStatePath ?? taskStatePathDefault(),
|
|
1264
|
+
...(opts.card ? { cardId: opts.card, version: cardVersion, terminalRunEvidence, unlinkedTerminal: unlinkedTerminalUsed, observedChecklist,
|
|
1265
|
+
observedRun: { runId: targetCard?.runId, runChildId: targetCard?.runChildId, parentHost: targetCard?.parentHost?.host, launchId: targetCard?.launchId } } : {}),
|
|
1266
|
+
...(chosen ? { checklistAtWrite: () => (deps.adoptChecklist ?? listChecklist)(chosen.version).items.find((item) => item.id === chosen.id) } : {}),
|
|
1267
|
+
...(checklist ? { checklist } : {}) });
|
|
1268
|
+
try { debug.log('task-agent', 'pr-adopted', { card: result.card.id, pr: number, head: view.headRefOid, created: result.created, changed: result.changed, launched: false }); } catch { /* fail-soft */ }
|
|
1269
|
+
if (opts.json) out(JSON.stringify({ card: result.card.id, pr: number, head: view.headRefOid, created: result.created, changed: result.changed, next: `tasks rejudge ${result.card.id}` }));
|
|
1270
|
+
else out(`${result.card.id}\tPR #${number}\t${result.changed ? 'adopted' : 'already-adopted'}\t구현 발사 0 · 다음: tasks rejudge ${result.card.id}`);
|
|
1271
|
+
} catch (error) {
|
|
1272
|
+
out(error instanceof Error ? error.message : String(error));
|
|
1273
|
+
process.exitCode = 1;
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
|
|
823
1277
|
tasks.command('adopt-runs')
|
|
824
1278
|
.description('TA-OWN-ALL-RUNS 백필 — 발사 등록부의 이 우주 런 중 카드 없는 것에 `ta-<runId>` 카드를 만든다(발사 훅 이전 런 몫 · 있으면 건드리지 않는다)')
|
|
825
1279
|
.option('--json', 'JSON 출력')
|
|
@@ -961,6 +1415,9 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
961
1415
|
for (const cardId of bindable) {
|
|
962
1416
|
// HARNESS-PARENT-ON-MSB1: 원격 부모 런은 원장이 그 호스트에 있다 — HQ 거울로 먼저 당긴다(읽기 전용).
|
|
963
1417
|
await mirrorRemoteCardLedger(cardId, deps);
|
|
1418
|
+
try { await replayRemoteCardMoves(cardId, deps.taskStatePath ?? taskStatePathDefault(), deps.cardRunEvidence ?? {}); } catch (error) {
|
|
1419
|
+
try { debug.log('task-agent', 'card-binding-replay-failed', { card: cardId, reason: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
|
|
1420
|
+
}
|
|
964
1421
|
try { await refreshCardRunBinding(cardId, deps.taskStatePath ?? taskStatePathDefault(), deps.cardRunEvidence ?? {}); } catch (error) {
|
|
965
1422
|
// 근거 없음으로 보인다 — 원장 부재와 갈리게 실패는 남긴다.
|
|
966
1423
|
try { debug.log('task-agent', 'card-bind-failed', { card: cardId, via: 'show', error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
|
|
@@ -1033,13 +1490,25 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
1033
1490
|
const { rejudgeCandidates, rejudgeCardOnHead } = await import('../task-agent/rejudge-on-head.js');
|
|
1034
1491
|
const statePath = deps.taskStatePath ?? taskStatePathDefault();
|
|
1035
1492
|
let ids: string[];
|
|
1036
|
-
try {
|
|
1493
|
+
try {
|
|
1494
|
+
if (!id) {
|
|
1495
|
+
const { bindMirroredRemoteRejudgeCards } = await import('../task-agent/remote-rejudge-bind.js');
|
|
1496
|
+
const cards = readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(statePath).tasks ?? {};
|
|
1497
|
+
const binding = await bindMirroredRemoteRejudgeCards(cards, statePath);
|
|
1498
|
+
try { debug.log('task-agent', 'remote-rejudge-bind', binding); } catch { /* fail-soft */ }
|
|
1499
|
+
}
|
|
1500
|
+
ids = id ? [id] : rejudgeCandidates(readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(statePath).tasks ?? {});
|
|
1501
|
+
} catch (error) {
|
|
1037
1502
|
out(error instanceof Error ? error.message : '과제 카드 읽기 실패'); process.exitCode = 1; return;
|
|
1038
1503
|
}
|
|
1039
1504
|
const { configuredTaskAgentLiveMoves } = await import('../task-agent/live-moves.js');
|
|
1040
1505
|
const liveMoves = deps.rejudge?.liveMoves ?? await configuredTaskAgentLiveMoves();
|
|
1041
1506
|
const all = [];
|
|
1042
1507
|
for (const cardId of ids) {
|
|
1508
|
+
await mirrorRemoteCardLedger(cardId, deps);
|
|
1509
|
+
try { await replayRemoteCardMoves(cardId, statePath, deps.cardRunEvidence ?? {}); } catch (error) {
|
|
1510
|
+
try { debug.log('task-agent', 'card-binding-replay-failed', { card: cardId, reason: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
|
|
1511
|
+
}
|
|
1043
1512
|
let outcomes: Awaited<ReturnType<typeof rejudgeCardOnHead>>;
|
|
1044
1513
|
try { outcomes = await rejudgeCardOnHead(cardId, { ...deps.rejudge, liveMoves, live: { ...deps.rejudge?.live, statePath } }); } catch (error) {
|
|
1045
1514
|
// 카드 하나의 예외가 --all 의 나머지 카드 판단을 멈추지 않게 — 사유를 남기고 다음 카드로.
|
|
@@ -1073,9 +1542,37 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
1073
1542
|
try { debug.log('task-agent', 'regate-run', { card: opts.card, ...record, failures: result.failures.slice(0, 3) }); } catch { /* fail-soft */ }
|
|
1074
1543
|
mkdir(dir(opts.out), { recursive: true });
|
|
1075
1544
|
write(opts.out, JSON.stringify(record));
|
|
1545
|
+
if (deps.regateCompletion !== false) {
|
|
1546
|
+
try {
|
|
1547
|
+
const { rejudgeCompletedTaskRegate } = await import('../task-agent/rejudge-on-head.js');
|
|
1548
|
+
await rejudgeCompletedTaskRegate(opts.card, pr, opts.head, opts.out, deps.taskStatePath ?? taskStatePathDefault(), undefined, deps.rejudge);
|
|
1549
|
+
} catch (error) {
|
|
1550
|
+
try { debug.log('task-agent', 'regate-completion-rejudge-failed', { card: opts.card, pr, head: opts.head, reason: String(error).slice(-300) }, { level: 'warn' }); } catch { /* fail-soft */ }
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1076
1553
|
if (!result.passed) process.exitCode = 1;
|
|
1077
1554
|
});
|
|
1078
1555
|
|
|
1556
|
+
tasks.command('repair-run', { hidden: true })
|
|
1557
|
+
.description('TA-REPAIR-SAME-BRANCH 내부 — source PR 머리에서 commit-only Pod 수리를 실행하고 결과를 파일로 남긴다')
|
|
1558
|
+
.requiredOption('--card <id>').requiredOption('--pr <n>').requiredOption('--head <sha>')
|
|
1559
|
+
.requiredOption('--repo <root>').requiredOption('--out <path>').requiredOption('--goal-file <path>')
|
|
1560
|
+
.requiredOption('--run-id <id>').requiredOption('--model-provider <provider>').requiredOption('--model-id <id>')
|
|
1561
|
+
.option('--seat <seat>')
|
|
1562
|
+
.action(async (opts: { card: string; pr: string; head: string; repo: string; out: string; goalFile: string;
|
|
1563
|
+
runId: string; modelProvider: string; modelId: string; seat?: string }) => {
|
|
1564
|
+
const { readFileSync } = await import('node:fs');
|
|
1565
|
+
const { getElanousConfigDirOverride } = await import('../elanous-config-dir.js');
|
|
1566
|
+
const { effectiveInstanceRoot } = await import('../instance/resolve.js');
|
|
1567
|
+
const { runRepairWorker } = await import('../task-agent/repair-worker.js');
|
|
1568
|
+
const result = await runRepairWorker({ card: opts.card, pr: Number(opts.pr), head: opts.head,
|
|
1569
|
+
repo: opts.repo, resultPath: opts.out, entry: ELANOUS_CLI_ENTRY,
|
|
1570
|
+
configDir: getElanousConfigDirOverride() ?? effectiveInstanceRoot(), runId: opts.runId,
|
|
1571
|
+
model: { provider: opts.modelProvider, model: opts.modelId }, seat: opts.seat,
|
|
1572
|
+
goal: readFileSync(opts.goalFile, 'utf8') });
|
|
1573
|
+
if (!result.ok) process.exitCode = 1;
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1079
1576
|
tasks.command('approve <id...>')
|
|
1080
1577
|
.description('외부 태스크 승인')
|
|
1081
1578
|
.action(async (ids: string[]) => {
|