elanous 0.2.27 → 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-a9c338f5bb556f42.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-07110da72c45e68f.js → page-24453f6a302816b4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/{page-d2d76d4a7eae91bd.js → page-6c131d30bb1cf871.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-fa46d4588e8442c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-e5a9302c105725a3.js → page-dc0772b11861af51.js} +1 -1
- 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-6a16a48d1aa6e91f.js → page-256ffd365d32e100.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e0de78ae7b99f01c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-39f82acf619cca23.js → page-4c741eb760f2f77c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-7c2c8f81b185f9c0.js → page-e9ba98f5f1440a21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/{page-ae7aff07e32676bb.js → page-dafc901bb1b72b5f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-482c60c1bc540d0a.js → page-2f6f303fa114ec24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-7cb996c1aa08b6f5.js → layout-1634081c5272006c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/{page-623e08fe4477d606.js → page-ced0bcfc6542b3fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-db460cd8aecb8490.js → page-bb1f05cd51c38944.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-ff27902d0b11ef4f.js → page-646b50f63b6fe732.js} +1 -1
- 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-1917ea87ea71ab6b.js → page-f736324afa2117c1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-25a302273b054fd7.js → page-6f131b51193a36f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-670510eda8b9443f.js → not-found-435f5e2faf02bc01.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-497e0eedf20e7890.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-a8d28fdf995e16ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-4bb7556e2e97a750.js → page-8966452228753c5e.js} +1 -1
- 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-4eb96fc7241c2456.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-0d64ff212dc55d03.js → page-c71267e7681da982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-7f096248993fc2d4.js → page-a21d1e1797172692.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-e47fc72049149a38.js → page-cb249e81ed339318.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-f7d808e31e005895.js → page-781cfc5114e097da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-5c2b24b5496dad73.js → page-478157214db6a023.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-76cdb531668b5191.js → page-86141f12cf090b7f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-7256c73bb1c36751.js +1 -0
- 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-dab163b338e3b966.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-0260ffc7eda50db1.js → page-21050928a697ed7a.js} +1 -1
- 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-99c4e6c7345a9156.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-11bf9e5725a72c22.js +1 -0
- 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 +50 -17
- package/scripts/detect-test-interference.ts +31 -1
- package/scripts/release-loop/docs-node.ts +51 -22
- package/scripts/release-loop/gate-node.ts +37 -4
- package/scripts/release-loop/node-verdict.ts +3 -1
- 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 +2 -1
- package/src/agent-mission/in-agent-plugin.ts +8 -3
- package/src/agent-substrate/self-review-cli.ts +34 -10
- package/src/briefing/brief-items.ts +151 -35
- package/src/budget/llm-cost.ts +25 -2
- 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/msg-cli.ts +165 -9
- package/src/cli/plugin-cli.ts +11 -3
- package/src/cli/pr-cli.ts +80 -18
- package/src/cli/pr-land-refusal.ts +20 -0
- package/src/cli/release-cli.ts +2 -0
- package/src/cli/rfc-cli.ts +24 -0
- package/src/cli/tailscale-serve.ts +5 -8
- package/src/cli/tasks-cli.ts +393 -26
- package/src/cli/usage-cli.ts +9 -4
- package/src/dashboard/first-screen-band.ts +63 -13
- package/src/dashboard/index.ts +8 -3
- package/src/dashboard/skill-detection-runtime.ts +24 -4
- package/src/domains/morning-report.ts +3 -1
- package/src/github/pr-labels.ts +10 -4
- package/src/graph-runner/runner.ts +101 -14
- package/src/harness/harness-clean.ts +21 -3
- package/src/harness/harness-cli-command.ts +22 -6
- package/src/harness/harness-pod-dispatch.ts +11 -4
- package/src/hq/seats.ts +17 -3
- package/src/index.ts +34 -5
- package/src/intelligence-map/model-bench-scores.ts +102 -0
- 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/maturity/feature-maturity.ts +1 -1
- package/src/msg/msg-sse-server.ts +141 -0
- package/src/msg/msg-store.ts +84 -14
- package/src/patents/ledger.ts +128 -0
- package/src/plugins/install/market-fetch.ts +32 -1
- package/src/pod-lease/admission.ts +116 -41
- package/src/release-loop/checklist.ts +11 -0
- 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-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/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-implement/gate-cli.ts +6 -2
- package/src/self-implement/goal-author.ts +2 -1
- 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 +3 -10
- package/src/self-implement/rework-policy.ts +80 -2
- package/src/self-implement/speed-claims.ts +145 -6
- 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 +84 -17
- 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 +43 -12
- 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 +106 -7
- 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 +2 -1
- package/src/task-agent/shadow.ts +13 -7
- package/src/task-agent/task-hand.ts +115 -6
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +70 -12
- package/src/telegram.ts +30 -1
- package/src/user-config.ts +8 -1
- 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-a1ce306364f08da9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-30007380f2e2a3c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-b93c04b5311c97ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-718a49e86bf25f9c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-20662900d6b4b455.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-f707a509515ac50c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-fcb06e305f515527.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-6acf5ec4e6c6d1af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-8397a5c6ca569bf4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-d73c3f1e00d93986.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-65caacf54389081e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-77e04b4f8762c666.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-15c0a4095baddfba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-63e272a4cd3b6634.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-e2b63345bba1a11f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-d376e0c83edc041a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d6db55fe2c04d128.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-87b88f551f421d8b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-08019efef111ed2e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-980ce6fe36ad95eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-0ab42e0f05823013.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ad9220422d20ba33.js +0 -1
- /package/apps/pwa/out/_next/static/{Ev2jivDMTSoEc-5rIXdcy → y1ltt7oCZxVpC6zVPhZ2O}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{Ev2jivDMTSoEc-5rIXdcy → y1ltt7oCZxVpC6zVPhZ2O}/_ssgManifest.js +0 -0
package/src/cli/tasks-cli.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync } from 'node:fs';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
|
-
import {
|
|
3
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
4
|
+
import { loadRunLedger as loadRunLedgerSync, loadRunLedgerWithMetadata, runLedgerDir, resolveFederatedRunLedgerDirectories } from '../self-implement/run-ledger.js';
|
|
4
5
|
import { goalRunDbPath, loadGoalRunQuery } from '../self-implement/goal-run-store.js';
|
|
5
6
|
import { loadGoalAskRecords } from '../self-implement/goal-ask-store.js';
|
|
6
|
-
import { join, resolve } from 'node:path';
|
|
7
|
+
import { dirname, join, resolve } from 'node:path';
|
|
7
8
|
import { InvalidArgumentError, Option, type Command } from 'commander';
|
|
8
9
|
import { debug } from '../debug/log.js';
|
|
9
10
|
import { getElanousConfigDir } from '../elanous-config-dir.js';
|
|
10
11
|
import { readNexusRuntime } from '../nexus/runtime.js';
|
|
11
|
-
import { checklistDevVersion } from '../release-loop/checklist.js';
|
|
12
|
+
import { checklistDevVersion, listChecklist } from '../release-loop/checklist.js';
|
|
12
13
|
import { assignedVersions, releasedVersion, validateVersion } from '../release-loop/feature-store.js';
|
|
13
14
|
import { compareVersions } from '../directives/version-at.js';
|
|
14
15
|
import { cardsBoard, countBoardTasks, INTERNAL_PACK_PROJECT, mergeBoards, releasePackBoard, type BoardNode } from '../task-agent/board.js';
|
|
@@ -17,9 +18,11 @@ import { cardPrNumber, CLOSED_BY_HAND_EVENT, closeTaskCard, COMPLETION_KINDS, ha
|
|
|
17
18
|
import { readCardSalvageBranch, recordWiringLiveVerified, refreshCardRunBinding, type CardRunEvidenceDeps } from '../task-agent/card-evidence.js';
|
|
18
19
|
import { captureReviewResults } from '../task-agent/review-result-capture.js';
|
|
19
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';
|
|
20
23
|
import { HAND_MOVE_KINDS, recordHandMove } from '../task-agent/live-move-history.js';
|
|
21
24
|
import { advanceMission, handMission, MissionAdvanceError, openMissionIds, recordPieceRef, type AdvanceMissionResult, type MissionDecompose, type PieceEvidenceReader } from '../task-agent/mission.js';
|
|
22
|
-
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';
|
|
23
26
|
import { LogStore, STORE_SAFETY_MAX, type LogQuery, type LogStoreRow } from '../mss/logging/log-store.js';
|
|
24
27
|
import { resolveLogTargets } from './logs-cli.js';
|
|
25
28
|
|
|
@@ -36,6 +39,8 @@ export interface TasksCliDeps {
|
|
|
36
39
|
reviewResultNow?: () => Date;
|
|
37
40
|
/** 과제 카드 상태 파일(시험 주입). 기본 = `effectiveInstanceRoot()/task-agent-actions.json`. */
|
|
38
41
|
taskStatePath?: string;
|
|
42
|
+
/** 조합 선택 시험에서만 그래프 저장소 루트를 바꾼다. */
|
|
43
|
+
taskCompositionRoot?: string;
|
|
39
44
|
/** logs.db 싱크 등록(시험 주입). 기본 = `registerStandaloneLogSink`. `hand`·`show` 가 부른다. */
|
|
40
45
|
registerSink?: (surface: string) => Promise<unknown>;
|
|
41
46
|
/** `task hand --mission` 의 분해기(시험 주입). 기본 = 나열형은 결정론 · 아니면 `decomposeSelfDevGoal`. */
|
|
@@ -46,6 +51,12 @@ export interface TasksCliDeps {
|
|
|
46
51
|
releaseBoard?: (version?: string) => BoardNode[];
|
|
47
52
|
/** `tasks hand --checklist` 판 목록(시험 주입). 기본은 발행 전 체크리스트가 있는 판 전부. */
|
|
48
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;
|
|
49
60
|
/** `tasks show`·`advance` 의 런 근거 읽기(시험 주입) — 기본 = 런 원장(이 우주 ⊕ 연합) ⊕ 호스트 logs 의 Pod job-finished. */
|
|
50
61
|
cardRunEvidence?: CardRunEvidenceDeps;
|
|
51
62
|
/** `tasks live-verified` 가 적는 런 원장 디렉터리(시험 주입). 기본 = `runLedgerDir()`. */
|
|
@@ -56,15 +67,20 @@ export interface TasksCliDeps {
|
|
|
56
67
|
staleParents?: import('../task-agent/parent-host.js').StaleParentDeps;
|
|
57
68
|
/** `tasks adopt-runs` 의 백필 의존(시험 주입 · 등록부·상태 파일). */
|
|
58
69
|
adoptRuns?: import('../task-agent/run-adopt.js').RunAdoptDeps;
|
|
59
|
-
/** `tasks cover` logs 조회 seam. 기본은
|
|
60
|
-
|
|
61
|
-
coverLogs?: (query: LogQuery) => LogStoreRow[] | CoverLogRead;
|
|
70
|
+
/** `tasks cover` logs 조회 seam. 기본은 운영 우주만, --include-test 때 시험 우주도 읽는다. */
|
|
71
|
+
coverLogs?: (query: LogQuery, includeTest?: boolean) => LogStoreRow[] | CoverLogRead;
|
|
62
72
|
/** `tasks cover --by-card` 카드 원장 읽기 seam(읽기 전용). 플래그가 있을 때만 부른다. 기본 = TA 카드 원장(task-agent-actions.json). null = 못 읽음. */
|
|
63
73
|
coverCards?: () => CoverCardRow[] | null | Promise<CoverCardRow[] | null>;
|
|
64
74
|
/** Read-only authored goal provenance; null means the authoring ledger could not be read. */
|
|
65
75
|
coverGoalAsks?: () => { authorRunId: string; goalId: string; createdAt: string }[] | null;
|
|
66
76
|
/** Read-only terminal goal records; null means the goal ledger could not be read. */
|
|
67
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;
|
|
68
84
|
/** TA-REJUDGE-ON-HEAD — `tasks rejudge` 의 판단 의존(시험 주입 · 기본 = 설정 liveMoves ⊕ 실물 gh·리뷰·재게이트·land). */
|
|
69
85
|
rejudge?: Partial<import('../task-agent/rejudge-on-head.js').RejudgeDeps>;
|
|
70
86
|
/** TA-REJUDGE-ON-HEAD — `tasks regate-run` 의 재게이트(시험 주입 · 기본 `runHostRegate`). */
|
|
@@ -324,8 +340,8 @@ async function mirrorRemoteCardLedger(cardId: string, deps: TasksCliDeps): Promi
|
|
|
324
340
|
try {
|
|
325
341
|
if (deps.remoteLedgerMirror) { deps.remoteLedgerMirror(card); return; }
|
|
326
342
|
const { mirrorRemoteRunLedgers } = await import('../task-agent/parent-host.js');
|
|
327
|
-
const {
|
|
328
|
-
mirrorRemoteRunLedgers(card.parentHost, card.runId,
|
|
343
|
+
const { effectiveInstanceRoot } = await import('../instance/resolve.js');
|
|
344
|
+
mirrorRemoteRunLedgers(card.parentHost, card.runId, effectiveInstanceRoot(), deps.cardRunEvidence?.ssh);
|
|
329
345
|
} catch (error) {
|
|
330
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 */ }
|
|
331
347
|
}
|
|
@@ -456,8 +472,20 @@ const COVER_QUERY_LIMIT = STORE_SAFETY_MAX;
|
|
|
456
472
|
/** Federated read: rows from every store, whether any single store hit the bound, and how many stores could not be read. */
|
|
457
473
|
export interface CoverLogRead { rows: LogStoreRow[]; limitReached: boolean; unreadableStores: number; storesRead: number }
|
|
458
474
|
|
|
459
|
-
|
|
460
|
-
|
|
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 });
|
|
461
489
|
if (error) throw new Error(error);
|
|
462
490
|
const read: CoverLogRead = { rows: [], limitReached: false, unreadableStores: 0, storesRead: 0 };
|
|
463
491
|
for (const target of targets) {
|
|
@@ -490,6 +518,61 @@ function coverEvents<T>(rows: readonly LogStoreRow[], category: string, event: s
|
|
|
490
518
|
return events;
|
|
491
519
|
}
|
|
492
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
|
+
|
|
493
576
|
export interface CoverCardRow { id: string; createdAt: string }
|
|
494
577
|
|
|
495
578
|
/**
|
|
@@ -511,14 +594,89 @@ export function coverCardsInWindow(cards: readonly CoverCardRow[] | null, window
|
|
|
511
594
|
}).map(({ id }) => ({ id }));
|
|
512
595
|
}
|
|
513
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
|
+
|
|
514
672
|
export function readTaskAgentCover(
|
|
515
673
|
window: { sinceMs?: number; untilMs?: number } = {},
|
|
516
674
|
query: NonNullable<TasksCliDeps['coverLogs']> = queryCoverLogs,
|
|
517
675
|
taskCards?: readonly { id: string }[] | null,
|
|
518
|
-
goalReaders: Pick<TasksCliDeps, 'coverGoalAsks' | 'coverGoalRuns'> = {},
|
|
519
|
-
): { rows: CoverRow[]; sevenVerbs: SevenVerbLive; limitReached: boolean; unreadableStores: number } {
|
|
676
|
+
goalReaders: Pick<TasksCliDeps, 'coverGoalAsks' | 'coverGoalRuns' | 'coverImplementCardRuns' | 'coverImplementRunMerges' | 'taskStatePath' | 'coverIncludeTest'> = {},
|
|
677
|
+
): { rows: CoverRow[]; sevenVerbs: SevenVerbLive; landSignals: CoverLandSignals; limitReached: boolean; unreadableStores: number } {
|
|
520
678
|
let unreadableStores = 0;
|
|
521
|
-
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 => {
|
|
522
680
|
try {
|
|
523
681
|
const result = query({ ...window, limit: COVER_QUERY_LIMIT, exactCategories: [category], events: [event] });
|
|
524
682
|
if (Array.isArray(result)) return { rows: result, limitReached: result.length >= COVER_QUERY_LIMIT };
|
|
@@ -528,9 +686,13 @@ export function readTaskAgentCover(
|
|
|
528
686
|
};
|
|
529
687
|
const actionRead = read('task-agent', 'action');
|
|
530
688
|
const landRead = read('pr.land', 'step');
|
|
689
|
+
const moveRead = read('task-agent', 'live-move');
|
|
690
|
+
const orderRead = read('task-agent.land-order', 'appended');
|
|
531
691
|
const actions = actionRead?.rows ?? null;
|
|
532
692
|
const lands = landRead?.rows ?? null;
|
|
533
|
-
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;
|
|
534
696
|
const inWindow = (value: string): boolean => {
|
|
535
697
|
const time = Date.parse(value);
|
|
536
698
|
return Number.isFinite(time) && (window.sinceMs === undefined || time >= window.sinceMs) && (window.untilMs === undefined || time <= window.untilMs);
|
|
@@ -550,7 +712,9 @@ export function readTaskAgentCover(
|
|
|
550
712
|
try {
|
|
551
713
|
if (goalReaders.coverGoalRuns) goalRuns = goalReaders.coverGoalRuns();
|
|
552
714
|
else {
|
|
553
|
-
|
|
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: [] });
|
|
554
718
|
goalRunsTruncated = runs?.truncated ?? false;
|
|
555
719
|
goalRuns = runs?.records.map(({ goalId, record }) => ({
|
|
556
720
|
goalId, outcome: record.outcome, stage: record.stage, startedAt: record.startedAt ?? '', completedAt: record.completedAt,
|
|
@@ -559,11 +723,9 @@ export function readTaskAgentCover(
|
|
|
559
723
|
} catch { goalRuns = null; }
|
|
560
724
|
const authoredGoalIds = new Set(goalAsks?.map((ask) => ask.goalId) ?? []);
|
|
561
725
|
const input: TaskAgentCoverInput = {
|
|
562
|
-
taskAgentActions: actions === null ? null :
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
allLands: lands === null ? null : coverEvents(lands, 'pr.land', 'step', (data) =>
|
|
566
|
-
typeof data.step === 'string' && typeof data.ok === 'boolean' ? { step: data.step, ok: data.ok } : null),
|
|
726
|
+
taskAgentActions: actions === null ? null : coverTaskActions(actions),
|
|
727
|
+
liveLandMoves: moves,
|
|
728
|
+
allLands: lands === null ? null : coverLandSteps(lands),
|
|
567
729
|
truncated: { ...truncated, goalAsks: goalAsksTruncated, goalRuns: goalRunsTruncated },
|
|
568
730
|
// Authoring may start before this window; completion is the event being counted.
|
|
569
731
|
goalAsks,
|
|
@@ -575,7 +737,32 @@ export function readTaskAgentCover(
|
|
|
575
737
|
: goalRuns,
|
|
576
738
|
...(taskCards === undefined ? {} : { taskCards }),
|
|
577
739
|
};
|
|
578
|
-
|
|
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 };
|
|
579
766
|
}
|
|
580
767
|
|
|
581
768
|
export function registerTasksCommands(program: Command, deps: TasksCliDeps = {}): void {
|
|
@@ -590,6 +777,83 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
590
777
|
};
|
|
591
778
|
const tasks = program.command('tasks').alias('task').description('TOX 태스크 조회 및 승인 · TASK-AGENT 과제 넘기기(hand)');
|
|
592
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
|
+
|
|
593
857
|
tasks.command('cover')
|
|
594
858
|
.description('TASK-AGENT 가 대신한 착지 비율과 리뷰·재발사·green 의 아직 없는 분모를 조회(읽기 전용)')
|
|
595
859
|
.option('--since <iso>', '시작 시각 (ISO)')
|
|
@@ -597,7 +861,8 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
597
861
|
.option('--json', 'JSON 출력')
|
|
598
862
|
.option('--lines', '동사별 한 줄 요약 출력 (읽기 전용)')
|
|
599
863
|
.option('--by-card', 'review·retry·green 분모를 카드 원장(창 안 createdAt 카드 수)으로 센다 (읽기 전용)')
|
|
600
|
-
.
|
|
864
|
+
.option('--include-test', '시험 우주 logs.db 도 운영 우주와 합산(기본은 운영만)')
|
|
865
|
+
.action(async (opts: { since?: string; until?: string; json?: boolean; lines?: boolean; byCard?: boolean; includeTest?: boolean }) => {
|
|
601
866
|
const parseIso = (value: string | undefined, flag: string): number | undefined => {
|
|
602
867
|
if (value === undefined) return undefined;
|
|
603
868
|
const time = Date.parse(value);
|
|
@@ -617,18 +882,23 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
617
882
|
}
|
|
618
883
|
taskCards = coverCardsInWindow(cards, { sinceMs, untilMs });
|
|
619
884
|
}
|
|
620
|
-
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
|
+
);
|
|
621
889
|
const limitWarning = limitReached ? `조회 상한 ${COVER_QUERY_LIMIT} 도달 — 더 오래된 사건이 있을 수 있다(절단된 수를 전체로 읽지 말 것)` : null;
|
|
622
890
|
const storeWarning = unreadableStores > 0 ? `logs.db ${unreadableStores}개를 못 읽었다 — 그 우주의 사건은 수에 없다(«없다»로 읽지 말 것)` : null;
|
|
623
|
-
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 } : {}) }));
|
|
624
892
|
else {
|
|
625
893
|
if (opts.lines) {
|
|
626
894
|
for (const row of rows) out(formatTaskAgentCover(row));
|
|
627
895
|
out(formatSevenVerbLive(sevenVerbs));
|
|
896
|
+
out(formatCoverLandSignals(landSignals));
|
|
628
897
|
}
|
|
629
898
|
else {
|
|
630
899
|
out('동사\t대신\t전체\t비율\t상태\t관측 사건');
|
|
631
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));
|
|
632
902
|
}
|
|
633
903
|
if (limitWarning) out(`⚠ ${limitWarning}`);
|
|
634
904
|
if (storeWarning) out(`⚠ ${storeWarning}`);
|
|
@@ -820,6 +1090,7 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
820
1090
|
...(opts.childLlm !== undefined ? { childLlm: opts.childLlm } : {}),
|
|
821
1091
|
...(opts.childLlmEffort !== undefined ? { childLlmEffort: opts.childLlmEffort } : {}),
|
|
822
1092
|
...(deps.taskStatePath ? { statePath: deps.taskStatePath } : {}),
|
|
1093
|
+
...(deps.taskCompositionRoot ? { compositionRoot: deps.taskCompositionRoot } : {}),
|
|
823
1094
|
launcher: deps.taskLauncher ?? defaultTaskLauncher,
|
|
824
1095
|
});
|
|
825
1096
|
if (opts.json) out(JSON.stringify(result));
|
|
@@ -935,6 +1206,74 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
935
1206
|
for (const row of report.rows) out([row.pid, row.ageHours, row.seat ?? '-', row.version ?? '-', row.runId ?? '-', row.reasons.join(',')].join('\t'));
|
|
936
1207
|
});
|
|
937
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
|
+
|
|
938
1277
|
tasks.command('adopt-runs')
|
|
939
1278
|
.description('TA-OWN-ALL-RUNS 백필 — 발사 등록부의 이 우주 런 중 카드 없는 것에 `ta-<runId>` 카드를 만든다(발사 훅 이전 런 몫 · 있으면 건드리지 않는다)')
|
|
940
1279
|
.option('--json', 'JSON 출력')
|
|
@@ -1151,7 +1490,15 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
1151
1490
|
const { rejudgeCandidates, rejudgeCardOnHead } = await import('../task-agent/rejudge-on-head.js');
|
|
1152
1491
|
const statePath = deps.taskStatePath ?? taskStatePathDefault();
|
|
1153
1492
|
let ids: string[];
|
|
1154
|
-
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) {
|
|
1155
1502
|
out(error instanceof Error ? error.message : '과제 카드 읽기 실패'); process.exitCode = 1; return;
|
|
1156
1503
|
}
|
|
1157
1504
|
const { configuredTaskAgentLiveMoves } = await import('../task-agent/live-moves.js');
|
|
@@ -1206,6 +1553,26 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
|
|
|
1206
1553
|
if (!result.passed) process.exitCode = 1;
|
|
1207
1554
|
});
|
|
1208
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
|
+
|
|
1209
1576
|
tasks.command('approve <id...>')
|
|
1210
1577
|
.description('외부 태스크 승인')
|
|
1211
1578
|
.action(async (ids: string[]) => {
|
package/src/cli/usage-cli.ts
CHANGED
|
@@ -78,6 +78,8 @@ type AxisUsageRow = {
|
|
|
78
78
|
unknownCostCalls: number;
|
|
79
79
|
includedCalls: number;
|
|
80
80
|
apiEquivalentUsd: number;
|
|
81
|
+
/** 비용 순위 = usdKnown + apiEquivalentUsd — 구독 호출의 목록가 «그림자 비용»까지 더한 값. */
|
|
82
|
+
rankUsd: number;
|
|
81
83
|
};
|
|
82
84
|
|
|
83
85
|
function usageData(row: LogStoreRow, unwrapPod = false): ExtendedRunUsageInput | null {
|
|
@@ -115,6 +117,8 @@ function rollupByAxis(inputs: readonly ExtendedRunUsageInput[], axis: UsageAxis)
|
|
|
115
117
|
}
|
|
116
118
|
return [...groups].map(([value, group]) => {
|
|
117
119
|
const rows = rollupRunUsage(group);
|
|
120
|
+
const usdKnown = rows.reduce((sum, row) => sum + row.usdKnown, 0);
|
|
121
|
+
const apiEquivalentUsd = rows.reduce((sum, row) => sum + row.apiEquivalentUsd, 0);
|
|
118
122
|
const result: AxisUsageRow = {
|
|
119
123
|
[axis]: value,
|
|
120
124
|
hostIds: [...new Set(rows.flatMap((row) => row.hostIds))],
|
|
@@ -124,13 +128,14 @@ function rollupByAxis(inputs: readonly ExtendedRunUsageInput[], axis: UsageAxis)
|
|
|
124
128
|
cacheReadInputTokens: rows.reduce((sum, row) => sum + row.cacheReadInputTokens, 0),
|
|
125
129
|
cacheCreationInputTokens: rows.reduce((sum, row) => sum + row.cacheCreationInputTokens, 0),
|
|
126
130
|
reasoningOutputTokens: rows.reduce((sum, row) => sum + row.reasoningOutputTokens, 0),
|
|
127
|
-
usdKnown
|
|
131
|
+
usdKnown,
|
|
128
132
|
unknownCostCalls: rows.reduce((sum, row) => sum + row.unknownCostCalls, 0),
|
|
129
133
|
includedCalls: rows.reduce((sum, row) => sum + row.includedCalls, 0),
|
|
130
|
-
apiEquivalentUsd
|
|
134
|
+
apiEquivalentUsd,
|
|
135
|
+
rankUsd: usdKnown + apiEquivalentUsd,
|
|
131
136
|
};
|
|
132
137
|
return result;
|
|
133
|
-
}).sort((a, b) => b.calls - a.calls);
|
|
138
|
+
}).sort((a, b) => (b.rankUsd - a.rankUsd) || (b.calls - a.calls));
|
|
134
139
|
}
|
|
135
140
|
|
|
136
141
|
function isUsageAxis(value: string): value is 'run' | 'role' | UsageAxis {
|
|
@@ -139,7 +144,7 @@ function isUsageAxis(value: string): value is 'run' | 'role' | UsageAxis {
|
|
|
139
144
|
|
|
140
145
|
function formatAxisUsage(row: AxisUsageRow, axis: UsageAxis): string {
|
|
141
146
|
const value = row[axis] ?? '(none)';
|
|
142
|
-
return `${axis}=${value} hosts=${row.hostIds.join(',') || '(none)'} calls=${row.calls} input=${row.inputTokens} output=${row.outputTokens} cacheRead=${row.cacheReadInputTokens} cacheCreation=${row.cacheCreationInputTokens} reasoning=${row.reasoningOutputTokens} usdKnown=${row.usdKnown} unknownCostCalls=${row.unknownCostCalls} includedCalls=${row.includedCalls} apiEquivalentUsd=${row.apiEquivalentUsd}`;
|
|
147
|
+
return `${axis}=${value} hosts=${row.hostIds.join(',') || '(none)'} calls=${row.calls} input=${row.inputTokens} output=${row.outputTokens} cacheRead=${row.cacheReadInputTokens} cacheCreation=${row.cacheCreationInputTokens} reasoning=${row.reasoningOutputTokens} usdKnown=${row.usdKnown} unknownCostCalls=${row.unknownCostCalls} includedCalls=${row.includedCalls} apiEquivalentUsd=${row.apiEquivalentUsd} rankUsd=${row.rankUsd}`;
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
function defaultCreditPlan(): CodexCreditPlan | null {
|