elanous 0.2.23 → 0.2.24
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/{272-b23d2e92161d11fd.js → 272-0eed0fb0aab716b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3961-fbef064c213b2b18.js → 3961-aaae04ed1d32622f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-d5341ac96203a983.js → page-3b9fc39362473ae7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f2b62c19ca68fef8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/{page-55c631c032dcc47e.js → page-d7602d489edf5f37.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-ad3bd8084048bee4.js → page-24282bf9f90031a5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-8b7c5ab6b5bc8d64.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/{page-5c5d0e370f495ef6.js → page-4eb84d454f8c3415.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-c325e21543cd48a8.js → page-b2e494bdad57fcb5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-6168a7c43e6ef8f5.js → page-c0ab229673a8b0eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-3e2b45f3c36dbac4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c22aa511b28743ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-26345242e4049f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-25bb425afdb27541.js → page-bf88c0f558d8ef54.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-32d72fdef1afe11a.js → page-52fec523e5b5d3ad.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-67723dfdcde92b31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/{page-2bd9ce9c020d02d4.js → page-22725e2570f1126b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-a34096245e63b9f2.js → page-1891335950d68b70.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-3267af1ebdc73cf5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-4cc66d22ca4ec502.js → page-c314b75c11234998.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-83b14c729fcea837.js → page-9f9c85415f61782b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c1d4276059085587.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-833aab587f26ad4c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-ccdb83ed40c8d724.js → page-8526c17ae38e6a50.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-7ad3de8fa06fdea9.js → page-f998637a8402115c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-b7bb53828b17b335.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1b1850de0257e46d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-786111f30b6f0969.js → page-6f23159fedb41f67.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-48fb31810693c357.js → page-1d809a87139dd024.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-4e17cce5e2ed0fee.js → page-da9e570234d481fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-cb154acadb025414.js → page-b92caddb5c715f30.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-8ce3a38e210b810a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-c2dd2b19c93f90ab.js → page-57ffeeb6019440fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-abbb417bf514630b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-d3c007c8a0ddd7ee.js → page-2c2b3f29cec1efa4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-7837ce2aacb97b04.js → page-9369279f72fc7b51.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-64861f14039499c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ddc4c93efd8ab94b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-901a1391b81053bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-68d9bbaac9841a58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-119be9a0444d2482.js → page-b5c0c996f329371e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-fa806c018833e332.js → page-89881ccbcf728978.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-0e87a803b3d7165e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-d431fbdea6ad93dd.js → page-58d4e5cb30ef4871.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-02ce4c3b5f39e4d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-e620d1bb8eadcec2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-847c3de6ca8a4416.js → page-acf868c64d77f950.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-54fa605fa6dba457.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-74e69ba2c8e970d4.js → page-bc79f0833533fa84.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-70038f41ae7cf931.js → page-d5c4398eea03d8f3.js} +1 -1
- package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +14 -14
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +14 -14
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/catalog/models/openrouter/deepseek-v4-pro-0813.yaml +39 -0
- package/catalog/models/openrouter/deepseek-v4.1-flash.yaml +38 -0
- package/catalog/models/openrouter/glm-5.3-flash.yaml +35 -0
- package/catalog/providers/openrouter.yaml +2 -0
- package/graphs/implement-loop.yaml +31 -0
- package/graphs/ops/harness-gc.yaml +17 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/overlays/README.md +1 -1
- package/graphs/overlays/stop-heal-ladder.yaml +105 -0
- package/package.json +1 -1
- package/scripts/add-api-key.sh +1 -1
- package/scripts/ci-typecheck-changed.ts +71 -14
- package/scripts/fleet/bedrock-pod-secret.ts +122 -0
- package/scripts/fleet/node-setup-linux.sh +97 -0
- package/scripts/fleet/node-setup.sh +11 -0
- package/scripts/fleet/zai-pod-secret.ts +86 -0
- package/scripts/land-global-checks.ts +3 -0
- package/scripts/measure-log-diet-2.ts +213 -0
- package/scripts/measure-tsc-affected-roots.ts +289 -0
- package/scripts/release-loop/gate-executor.ts +28 -0
- package/scripts/release-loop/gate-node.ts +262 -26
- package/scripts/release-loop/resume-release.ts +9 -3
- package/scripts/release-loop/resume.ts +41 -0
- package/scripts/release-loop/unattended-release.ts +22 -7
- package/scripts/tsc-baseline-cache.ts +147 -0
- package/scripts/usage-rollup.ts +58 -6
- package/src/agent/run-context.ts +10 -0
- package/src/agent-mission/driver.ts +18 -3
- package/src/agent-mission/resource-ladder.ts +8 -3
- package/src/agent-mission/review-loop.ts +8 -2
- package/src/agent-substrate/acp-implementer.ts +139 -0
- package/src/agent-substrate/pr-reviewer.ts +31 -4
- package/src/agent-substrate/self-review-cli.ts +48 -9
- package/src/autopilot/mission-critique.ts +18 -5
- package/src/autopilot/mission-feasibility-negotiate.ts +11 -10
- package/src/autopilot/mission-trace.ts +12 -1
- package/src/autopilot/system-lookback-run.ts +16 -10
- package/src/bench/bench.ts +183 -0
- package/src/budget/fetchers/grok.ts +15 -5
- package/src/budget/llm-cost.ts +37 -5
- package/src/budget/types.ts +8 -2
- package/src/budget/unified-usage.ts +88 -6
- package/src/cli/bench-cli.ts +34 -0
- package/src/cli/guardian-cli.ts +26 -1
- package/src/cli/image-cli.ts +101 -0
- package/src/cli/log-event-names.ts +4 -0
- package/src/cli/pr-cli.ts +62 -5
- package/src/cli/pr-land-overlap-evidence.ts +160 -12
- package/src/cli/release-cli.ts +18 -9
- package/src/cli/rfc-status.ts +52 -2
- package/src/cli/self-update.ts +104 -0
- package/src/cli/tasks-cli.ts +109 -4
- package/src/cli/usage-cli.ts +91 -8
- package/src/core-turn/run-goal-loop.ts +5 -4
- package/src/dashboard/ad-run-setup-bindings.ts +279 -0
- package/src/dashboard/index.ts +18 -276
- package/src/dashboard/stale-install-check.ts +11 -0
- package/src/debug/log-diet.ts +38 -7
- package/src/debug/log.ts +19 -1
- package/src/domains/release-tool.ts +39 -0
- package/src/error-report/report.ts +21 -4
- package/src/github/pr-labels.ts +1 -0
- package/src/graph-runner/graph-cli.ts +42 -1
- package/src/graph-runner/runner.ts +167 -6
- package/src/grok/usage.ts +23 -2
- package/src/grounding/ref-index.ts +253 -0
- package/src/harness/dispatch-backpressure.ts +10 -2
- package/src/harness/harness-clean.ts +199 -78
- package/src/harness/harness-cli-command.ts +122 -7
- package/src/harness/harness-gc.ts +331 -0
- package/src/harness/harness-stop.ts +4 -2
- package/src/harness/harness-substrate-default.ts +142 -7
- package/src/harness/launch-registry.ts +6 -0
- package/src/harness/worktree-branch-prefix.ts +9 -0
- package/src/hq/fence.ts +187 -0
- package/src/hq/seats.ts +38 -22
- package/src/images/image-ledger.ts +179 -0
- package/src/index.ts +93 -11
- package/src/instance/child-scope.ts +32 -1
- package/src/instance/dev-project.ts +133 -0
- package/src/instance/resolve.ts +24 -0
- package/src/instance/spawn-elanous.ts +45 -0
- package/src/intake-plane/ref-prefetch.ts +110 -0
- package/src/intelligence-map/model-catalog.ts +72 -0
- package/src/llm/bedrock-eventstream.ts +215 -0
- package/src/llm/bedrock-provider.ts +114 -41
- package/src/llm/bedrock.ts +174 -28
- package/src/llm/openrouter-byok-cost.ts +96 -0
- package/src/llm/openrouter-deepseek.ts +151 -0
- package/src/llm/openrouter-upstream-pins.config.ts +25 -0
- package/src/llm/zai-coding-plan.ts +700 -0
- package/src/llm.ts +87 -16
- package/src/loops/checker.ts +148 -9
- package/src/loops/orchestrator/resync-shadow.ts +34 -7
- package/src/maturity/feature-maturity.ts +2 -2
- package/src/model-tier/llm-tier-map.ts +9 -5
- package/src/models/prompts.ts +2 -1
- package/src/mss/logging/log-store.ts +58 -11
- package/src/nexus/api/missions.ts +73 -2
- package/src/nexus/api/ops-api.ts +18 -0
- package/src/nexus/index.ts +7 -75
- package/src/nexus/tools-kind.ts +78 -0
- package/src/oauth/codex-account-rotation.ts +39 -2
- package/src/oauth/codex-account-store.ts +323 -24
- package/src/oauth/fallback-chain.ts +53 -15
- package/src/onboarding/first-run.ts +37 -18
- package/src/onboarding/web-first.ts +20 -2
- package/src/ops/machine-heartbeat.ts +185 -0
- package/src/ops/overview-cache.ts +175 -0
- package/src/ops/overview.ts +8 -0
- package/src/prompt-cache/openai.ts +3 -0
- package/src/prompt-cache/types.ts +5 -0
- package/src/prompt-enhance/enhance.ts +23 -0
- package/src/release-loop/placement.ts +16 -1
- package/src/requirements/requirement-ledger.ts +536 -0
- package/src/retro/daily.ts +5 -5
- package/src/seat-loop/seat-loop.ts +32 -10
- package/src/self-dev/dev-cli.ts +44 -10
- package/src/self-dev/dev-pipeline.ts +7 -2
- package/src/self-dev/draft-residue.ts +14 -2
- package/src/self-dev/draft-sweep.ts +103 -0
- package/src/self-dev/graph-journey-route.ts +6 -0
- package/src/self-dev/orchestrate-cli.ts +25 -3
- package/src/self-dev/orchestrate.ts +41 -6
- package/src/self-dev/run-supervisor.ts +188 -15
- package/src/self-dev/run-types.ts +13 -0
- package/src/self-dev/supervisor-verdict-edges.ts +1 -0
- package/src/self-implement/budget-gate.ts +53 -13
- package/src/self-implement/child-context-composition.ts +139 -0
- package/src/self-implement/child-llm-preference.ts +13 -5
- package/src/self-implement/claude-acp-implement.ts +67 -0
- package/src/self-implement/flex-signals.ts +59 -0
- package/src/self-implement/frozen-merges.ts +8 -3
- package/src/self-implement/goal-author-decompose.ts +10 -2
- package/src/self-implement/goal-author.ts +24 -9
- package/src/self-implement/graph-overlay-condition.ts +28 -0
- package/src/self-implement/graph-overlay-yaml.ts +41 -7
- package/src/self-implement/graph-templates.ts +33 -1
- package/src/self-implement/graph-yaml.ts +56 -0
- package/src/self-implement/heal-intake.ts +5 -1
- package/src/self-implement/orchestrator.ts +144 -15
- package/src/self-implement/running-runs.ts +75 -5
- package/src/self-implement/seams.ts +47 -1
- package/src/self-implement/self-implement-cli.ts +9 -4
- package/src/self-implement/stop-autoheal.ts +69 -2
- package/src/session-runtime/retry-policy.ts +2 -1
- package/src/steward/launch.ts +5 -4
- package/src/steward/triage-budget.ts +62 -0
- package/src/steward/triage.ts +83 -22
- package/src/task-agent/actions.ts +62 -5
- package/src/task-agent/completion-evidence.ts +5 -0
- package/src/task-agent/cover.ts +101 -3
- package/src/task-agent/judge.ts +16 -0
- package/src/task-agent/land-order-ledger.ts +95 -0
- package/src/task-agent/live-move-history.ts +87 -1
- package/src/task-agent/live-moves.ts +350 -20
- package/src/task-agent/rejudge-on-head.ts +75 -20
- package/src/task-agent/review-prior-fix.ts +146 -0
- package/src/task-agent/run-adopt.ts +136 -0
- package/src/task-agent/shadow.ts +53 -9
- package/src/task-agent/task-hand.ts +54 -7
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +38 -5
- package/src/task-orchestrator/surfaces/pod-bedrock-child.ts +74 -0
- package/src/task-orchestrator/surfaces/pod-child-default.ts +50 -0
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +14 -1
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +79 -6
- package/src/task-orchestrator/surfaces/pod-lease.ts +17 -5
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
- package/src/task-orchestrator/surfaces/pod-pool.ts +20 -4
- package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +213 -23
- package/src/task-orchestrator/surfaces/self-implement.ts +19 -1
- package/src/typecheck-ratchet.ts +1 -0
- package/src/user-config.ts +51 -11
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/triggers/registry.ts +59 -0
- package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-a648bc464c9dc754.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +0 -1
- package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +0 -1
- /package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → Q-7iofoxOEv1WX18S35of}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → Q-7iofoxOEv1WX18S35of}/_ssgManifest.js +0 -0
|
@@ -24,12 +24,16 @@ export interface GraphNodeSpec {
|
|
|
24
24
|
readonly kind: GraphNodeKind | (string & {});
|
|
25
25
|
readonly recipe: string;
|
|
26
26
|
readonly maxVisits: number;
|
|
27
|
+
/** L0 journey-stage declaration (optional for other graph templates). */
|
|
28
|
+
readonly journeyStage?: string;
|
|
27
29
|
readonly progress?: readonly string[];
|
|
28
30
|
readonly phases?: readonly string[];
|
|
29
31
|
readonly terminalStages?: readonly string[];
|
|
30
32
|
/** ⭐ 「아크마다 한 번」처럼 «한 노드가 여럿 돈다」를 값으로. 없으면 한 번이다. */
|
|
31
33
|
readonly fanOut?: string;
|
|
32
34
|
readonly contract?: GraphNodeContract;
|
|
35
|
+
readonly call?: string;
|
|
36
|
+
readonly budget?: { readonly innerSteps: number };
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
export interface GraphEdgeSpec {
|
|
@@ -50,11 +54,13 @@ export interface GraphTemplateSpec {
|
|
|
50
54
|
readonly version: number;
|
|
51
55
|
readonly entryNode: string;
|
|
52
56
|
readonly terminalNodes: readonly string[];
|
|
57
|
+
readonly journeyStages?: readonly string[];
|
|
53
58
|
/** 문서 전용 변경이면 gate 노드를 건너뛸 수 있다는 템플릿 정책. */
|
|
54
59
|
readonly docsOnlyGateSkip?: boolean;
|
|
55
60
|
readonly state?: readonly string[];
|
|
56
61
|
/** ⭐ 런 계약(선택) — 이 그래프가 «기본으로» 요구하는 실행 칸 등. 발사 인자가 덮는다(graph-run-contract.ts). */
|
|
57
62
|
readonly runContract?: GraphRunContractSpec;
|
|
63
|
+
readonly unit?: { readonly inputs: readonly string[]; readonly outputs: readonly string[]; readonly exits: readonly string[] };
|
|
58
64
|
readonly nodes: readonly GraphNodeSpec[];
|
|
59
65
|
readonly edges: readonly GraphEdgeSpec[];
|
|
60
66
|
}
|
|
@@ -102,6 +108,12 @@ export function parseGraphTemplateYaml(source: string, label = '<inline>'): Grap
|
|
|
102
108
|
const terminalNodes = asArray(doc.terminal_nodes).filter((v): v is string => typeof v === 'string');
|
|
103
109
|
if (terminalNodes.length === 0) errors.push({ path: `${label}/terminal_nodes`, message: 'terminal_nodes 가 비었다 — 끝날 수 없는 그래프다' });
|
|
104
110
|
|
|
111
|
+
const journeyStages = doc.journey_stages;
|
|
112
|
+
if (journeyStages !== undefined && (!Array.isArray(journeyStages) || journeyStages.length === 0
|
|
113
|
+
|| !journeyStages.every((stage) => typeof stage === 'string' && stage.trim())
|
|
114
|
+
|| new Set(journeyStages).size !== journeyStages.length)) {
|
|
115
|
+
errors.push({ path: `${label}/journey_stages`, message: 'journey_stages 는 중복 없는 비어 있지 않은 문자열 목록이어야 한다' });
|
|
116
|
+
}
|
|
105
117
|
const nodes: GraphNodeSpec[] = [];
|
|
106
118
|
for (const [index, entry] of asArray(doc.nodes).entries()) {
|
|
107
119
|
const at = `${label}/nodes/${index}`;
|
|
@@ -126,6 +138,13 @@ export function parseGraphTemplateYaml(source: string, label = '<inline>'): Grap
|
|
|
126
138
|
errors.push({ path: `${at}/max_visits`, message: 'max_visits 는 1 이상의 정수여야 한다' });
|
|
127
139
|
continue;
|
|
128
140
|
}
|
|
141
|
+
if (n.journey_stage !== undefined && (typeof n.journey_stage !== 'string' || !n.journey_stage.trim()
|
|
142
|
+
|| (Array.isArray(journeyStages) && !journeyStages.includes(n.journey_stage)))) {
|
|
143
|
+
errors.push({ path: `${at}/journey_stage`, message: typeof n.journey_stage === 'string' && n.journey_stage.trim()
|
|
144
|
+
? `journey_stage '${n.journey_stage}' 가 journey_stages 목록에 없다`
|
|
145
|
+
: 'journey_stage 는 비어 있지 않은 문자열이어야 한다' });
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
129
148
|
const contractRaw = n.contract as Record<string, unknown> | undefined;
|
|
130
149
|
const contract: GraphNodeContract | undefined = contractRaw && typeof contractRaw === 'object'
|
|
131
150
|
? {
|
|
@@ -134,17 +153,38 @@ export function parseGraphTemplateYaml(source: string, label = '<inline>'): Grap
|
|
|
134
153
|
outputs: asArray(contractRaw.outputs).filter((v): v is string => typeof v === 'string'),
|
|
135
154
|
}
|
|
136
155
|
: undefined;
|
|
156
|
+
// A subgraph node becomes a unit call only when it names `call:`; without it the node keeps the
|
|
157
|
+
// pre-unit meaning (catalog role / recipe node), so existing declarations stay valid.
|
|
158
|
+
if (kind === 'subgraph' && (n.call !== undefined || n.budget !== undefined)) {
|
|
159
|
+
if (typeof n.call !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]*@(?:\^?[1-9]\d*)$/.test(n.call))
|
|
160
|
+
errors.push({ path: `${at}/call`, message: 'call 은 id@정수판 또는 id@^주판이어야 한다' });
|
|
161
|
+
const budget = n.budget as Record<string, unknown> | undefined;
|
|
162
|
+
if (!budget || typeof budget !== 'object' || Array.isArray(budget) ||
|
|
163
|
+
!Number.isSafeInteger(budget.inner_steps) || (budget.inner_steps as number) < 1 || Object.keys(budget).some(key => key !== 'inner_steps'))
|
|
164
|
+
errors.push({ path: `${at}/budget/inner_steps`, message: 'inner_steps 는 1 이상의 안전한 정수여야 한다' });
|
|
165
|
+
if (!contractRaw || !Array.isArray(contractRaw.inputs) || !contractRaw.inputs.every(v => typeof v === 'string') ||
|
|
166
|
+
!Array.isArray(contractRaw.outputs) || !contractRaw.outputs.every(v => typeof v === 'string'))
|
|
167
|
+
errors.push({ path: `${at}/contract`, message: 'subgraph 에는 inputs·outputs 계약이 필요하다' });
|
|
168
|
+
if (recipe !== 'none') errors.push({ path: `${at}/recipe`, message: 'subgraph 는 recipe 를 실행하지 않는다' });
|
|
169
|
+
} else if (kind !== 'subgraph' && (n.call !== undefined || n.budget !== undefined)) {
|
|
170
|
+
errors.push({ path: at, message: 'call·budget 은 subgraph 전용이다' });
|
|
171
|
+
}
|
|
137
172
|
// ⛔ 계약이 없으면 「이 노드가 무엇을 할 수 있나」를 답할 수 없다 — 경고로 남긴다(거절은 오버레이 쪽).
|
|
138
173
|
if (contract === undefined) warnings.push({ path: `${at}/contract`, message: '계약(inputs·tools·outputs)이 없다 — 권한을 코드에서만 알 수 있다' });
|
|
139
174
|
else if (contract.tools === '') warnings.push({ path: `${at}/contract/tools`, message: 'tools 가 비었다' });
|
|
140
175
|
|
|
141
176
|
nodes.push({
|
|
142
177
|
nodeId, kind, recipe, maxVisits,
|
|
178
|
+
...(typeof n.journey_stage === 'string' ? { journeyStage: n.journey_stage } : {}),
|
|
143
179
|
...(Array.isArray(n.progress) ? { progress: n.progress.filter((v): v is string => typeof v === 'string') } : {}),
|
|
144
180
|
...(Array.isArray(n.phases) ? { phases: n.phases.filter((v): v is string => typeof v === 'string') } : {}),
|
|
145
181
|
...(Array.isArray(n.terminal_stages) ? { terminalStages: n.terminal_stages.filter((v): v is string => typeof v === 'string') } : {}),
|
|
146
182
|
...(typeof n.fan_out === 'string' ? { fanOut: n.fan_out } : {}),
|
|
147
183
|
...(contract ? { contract } : {}),
|
|
184
|
+
...(kind === 'subgraph' && typeof n.call === 'string' ? { call: n.call } : {}),
|
|
185
|
+
...(kind === 'subgraph' && n.budget && typeof n.budget === 'object' && !Array.isArray(n.budget) &&
|
|
186
|
+
typeof (n.budget as Record<string, unknown>).inner_steps === 'number'
|
|
187
|
+
? { budget: { innerSteps: (n.budget as { inner_steps: number }).inner_steps } } : {}),
|
|
148
188
|
});
|
|
149
189
|
}
|
|
150
190
|
if (nodes.length === 0) errors.push({ path: `${label}/nodes`, message: '노드가 하나도 없다' });
|
|
@@ -198,6 +238,20 @@ export function parseGraphTemplateYaml(source: string, label = '<inline>'): Grap
|
|
|
198
238
|
if (entryNode !== undefined && !known.has(entryNode)) errors.push({ path: `${label}/entry_node`, message: `'${entryNode}' 는 선언된 노드가 아니다` });
|
|
199
239
|
for (const t of terminalNodes) if (!known.has(t)) errors.push({ path: `${label}/terminal_nodes`, message: `'${t}' 는 선언된 노드가 아니다` });
|
|
200
240
|
|
|
241
|
+
let unit: GraphTemplateSpec['unit'];
|
|
242
|
+
if (doc.unit !== undefined) {
|
|
243
|
+
const rawUnit = doc.unit as Record<string, unknown>;
|
|
244
|
+
if (!rawUnit || typeof rawUnit !== 'object' || Array.isArray(rawUnit) ||
|
|
245
|
+
Object.keys(rawUnit).some(key => !['inputs', 'outputs', 'exits'].includes(key)) ||
|
|
246
|
+
!['inputs', 'outputs', 'exits'].every(key => Array.isArray(rawUnit[key]) &&
|
|
247
|
+
(rawUnit[key] as unknown[]).every(v => typeof v === 'string' && v.length > 0) &&
|
|
248
|
+
new Set(rawUnit[key] as string[]).size === (rawUnit[key] as string[]).length) ||
|
|
249
|
+
!(rawUnit.exits as string[]).length ||
|
|
250
|
+
(rawUnit.exits as string[]).some(exit => !terminalNodes.includes(exit)) ||
|
|
251
|
+
terminalNodes.some(exit => !(rawUnit.exits as string[]).includes(exit))) {
|
|
252
|
+
errors.push({ path: `${label}/unit`, message: 'unit 의 inputs·outputs·exits 및 terminal_nodes 가 일치해야 한다' });
|
|
253
|
+
} else unit = { inputs: rawUnit.inputs as string[], outputs: rawUnit.outputs as string[], exits: rawUnit.exits as string[] };
|
|
254
|
+
}
|
|
201
255
|
const runContract = parseGraphRunContract(doc.run_contract);
|
|
202
256
|
if (runContract.error) errors.push({ path: `${label}/run_contract`, message: runContract.error });
|
|
203
257
|
let loop: GraphTemplateSpec['loop'];
|
|
@@ -225,6 +279,8 @@ export function parseGraphTemplateYaml(source: string, label = '<inline>'): Grap
|
|
|
225
279
|
return {
|
|
226
280
|
template: {
|
|
227
281
|
graphId: graphId!, version: version!, entryNode: entryNode!, terminalNodes, nodes, edges,
|
|
282
|
+
...(unit ? { unit } : {}),
|
|
283
|
+
...(Array.isArray(journeyStages) ? { journeyStages: journeyStages as string[] } : {}),
|
|
228
284
|
...(loop ? { loop } : {}),
|
|
229
285
|
...(typeof doc.docs_only_gate_skip === 'boolean' ? { docsOnlyGateSkip: doc.docs_only_gate_skip } : {}),
|
|
230
286
|
...(Array.isArray(doc.state) ? { state: doc.state.filter((v): v is string => typeof v === 'string') } : {}),
|
|
@@ -170,7 +170,11 @@ function withInboxLock<T>(file: string, work: () => T): T {
|
|
|
170
170
|
finally { rmdirSync(lock); }
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
/** One durable JSON line per failure; repeat source+ref within an hour is folded.
|
|
173
|
+
/** One durable JSON line per failure; repeat source+ref within an hour is folded.
|
|
174
|
+
* POD-HEAL-HOST — the loop start itself is refused inside a Pod (`startHealLoop` → `skipped-in-pod`):
|
|
175
|
+
* a loop started there dies with the Pod. The line stays in the inbox, and the host re-records the
|
|
176
|
+
* recovered Pod ledger failure into the HOST universe (`pod-ledger-collect.ts` → `intakeCollectedFailure`),
|
|
177
|
+
* so the host heal loop triages it (harvest + narrowed relaunch). */
|
|
174
178
|
export function recordFailureEvent(event: FailureEvent, root = effectiveInstanceRoot(), startLoop: StartHealLoop = startHealLoop): { folded: boolean } {
|
|
175
179
|
if (!['release-run', 'harness-run', 'loop-tick', 'cron'].includes(event.source) ||
|
|
176
180
|
!event.kind || !event.ref || !event.summary || !Number.isFinite(Date.parse(event.at))) {
|
|
@@ -31,7 +31,8 @@ export {
|
|
|
31
31
|
type PrEvidenceInput,
|
|
32
32
|
} from './pr-evidence-artifact.js';
|
|
33
33
|
import { judgePredictionAccuracy } from './judge-prediction-accuracy.js';
|
|
34
|
-
import { classifyPodReviewBudgetResidue, measureReviewFindingRecurrence, type ReviewFindingRecurrence } from './review-finding-recurrence.js';
|
|
34
|
+
import { classifyPodReviewBudgetResidue, measureReviewFindingRecurrence, type ReviewFindingRecurrence, type RecurrenceFinding } from './review-finding-recurrence.js';
|
|
35
|
+
import type { ReviewRework } from '../self-dev/run-types.js';
|
|
35
36
|
import { createHash } from 'node:crypto';
|
|
36
37
|
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync } from 'node:fs';
|
|
37
38
|
import { writeGoalDocumentAtomic } from './goal-document-write.js';
|
|
@@ -80,7 +81,7 @@ import { releasePathHold, releasePathHoldComment, RELEASE_PATH_LABEL } from '../
|
|
|
80
81
|
import { emitDecision } from '../live/detail-switch.js';
|
|
81
82
|
import { reworkDecision } from './decision-events.js';
|
|
82
83
|
import type { LogSink } from '../mss/logging/sink.js';
|
|
83
|
-
import type
|
|
84
|
+
import { LogStore, logsDbPath, type LogQuery, type LogStoreRow } from '../mss/logging/log-store.js';
|
|
84
85
|
import { refreshCodexQuotaSignals } from '../budget/codex-quota-refresh.js';
|
|
85
86
|
import { inspectActiveProvider, type ActiveProviderInfo } from '../provider-summary.js';
|
|
86
87
|
import { recordHitlEvent } from '../self-dev/hitl-log.js';
|
|
@@ -151,6 +152,7 @@ export { slugifyFeature } from '../harness/worktree-branch-prefix.js';
|
|
|
151
152
|
import { AUTO_REVIEW_LABEL, resolveAutoReviewLabels } from './context-capsule.js';
|
|
152
153
|
import { GOAL_TYPES, extractVerbatimOriginalAsk, leadingGoalMetadata, parseAskFile, parseGoalType, type GoalType } from './goal-author.js';
|
|
153
154
|
import { renderRoundSummaryCard, type RoundSummaryEntry } from './round-summary-card.js';
|
|
155
|
+
import { computeFlexSignals } from './flex-signals.js';
|
|
154
156
|
import { resolveAdaptiveMaxReworkDecision, resolveEscalateTier, resolveEscalateTarget, buildReworkFeature, failIndicator, isRepeatedUnverifiedOnlyGateFailure, unverifiedRepeatKey, appendReworkHistory, truncateReworkHistoryByItem, applyReworkBudgetDecision, parseContractConflictRelaxation, parseReworkBudgetDecision, resolveReworkBudgetCarry, stripReworkBudgetHeaders, countConsecutiveMustFixIds, mergeReworkNotes, resolveReworkKind, type ReworkPlanRevision, type ReworkNotePart } from './rework-policy.js';
|
|
155
157
|
import type { SupervisionReworkSource } from './supervision-vocabulary.js';
|
|
156
158
|
import { classifyReworkBudgetShadow } from './classify-shadow.js';
|
|
@@ -246,11 +248,12 @@ import { persistReviewArtifact, type ReviewArtifactWriter } from '../agent-subst
|
|
|
246
248
|
import { createArtifactStore } from '../artifact/index.js';
|
|
247
249
|
import { mapStageToRunStatus, type SelfImplementStage } from './run-status-mapping.js';
|
|
248
250
|
import type { LifecycleScreenComparison } from './lifecycle-screen-scoreboard.js';
|
|
251
|
+
import { buildRuntimeOverlayState } from './graph-overlay-condition.js';
|
|
249
252
|
import { activeTemplate, decideTemplate, graphAuthorityFields, nodeNameForStage, overlayDecisionObservation, resolveGraphAuthorityForUserConfig, routeGate, visitBudgetOf } from './graph-authority.js';
|
|
250
253
|
import type { GraphTemplate } from './graph-templates.js';
|
|
251
254
|
import { defaultOverlaysDir, loadGraphOverlays, type GraphOverlaySpec } from './graph-overlay-yaml.js';
|
|
252
|
-
import { graphIdentityOf, pipelineNodeEntryPayload } from './graph-templates.js';
|
|
253
|
-
import { AMBIGUOUS_STAGES, checkPipelineTraversal, nodesForStage, pipelineGraphIdentity, type PipelineNodeId, type PipelineTraversalCheck } from './pipeline-shape.js';
|
|
255
|
+
import { graphIdentityOf, measureL0Coverage, pipelineNodeEntryPayload } from './graph-templates.js';
|
|
256
|
+
import { AMBIGUOUS_STAGES, checkPipelineTraversal, nodesForStage, pipelineGraphIdentity, TERMINAL_STAGES_BY_NODE, type PipelineNodeId, type PipelineTraversalCheck } from './pipeline-shape.js';
|
|
254
257
|
import { appendGateWorktreeFreshnessEvidence, gateEvidenceNote } from '../harness/review-adapter.js';
|
|
255
258
|
import { diffHeaderPaths } from '../agent-substrate/self-review-cli.js';
|
|
256
259
|
import { format as formatPrComment, type PrCommentRole } from '../agent-substrate/pr-comment-meta.js';
|
|
@@ -786,7 +789,9 @@ function changedFileIsInsideDeclaredTarget(changedFile: string, declaredPath: st
|
|
|
786
789
|
const target = normalizeDeclaredScopePath(declaredPath);
|
|
787
790
|
if (!file || !target) return false;
|
|
788
791
|
return file === target || file.startsWith(`${target}/`)
|
|
789
|
-
|| (target.endsWith('.ts') && !target.endsWith('.test.ts') && file === `${target.slice(0, -3)}.test.ts`)
|
|
792
|
+
|| (target.endsWith('.ts') && !target.endsWith('.test.ts') && file === `${target.slice(0, -3)}.test.ts`)
|
|
793
|
+
// SCOPE-LOCKFILE-PAIR — 선언한 package.json 을 고치면 같은 폴더의 bun.lock 이 따라 바뀐다(10-11 TSGO-TRIAL 이 bun.lock 하나로 보류).
|
|
794
|
+
|| ((target === 'package.json' || target.endsWith('/package.json')) && file === `${target.slice(0, -'package.json'.length)}bun.lock`);
|
|
790
795
|
}
|
|
791
796
|
|
|
792
797
|
function parseDeclaredTargetPathsFromAsk(askText: string): { readable: boolean; paths: readonly string[] } {
|
|
@@ -803,10 +808,20 @@ function parseDeclaredTargetPathsFromAsk(askText: string): { readable: boolean;
|
|
|
803
808
|
return { readable: true, paths: [...new Set(paths.filter(Boolean))] };
|
|
804
809
|
}
|
|
805
810
|
|
|
811
|
+
// SCOPE-NEW-FILES-LINE — 칸 문면은 새로 만들 파일을 «신설:» 줄로 따로 적는다(OP D칸 규칙). 그 줄도 선언 범위다.
|
|
812
|
+
// 10-11 FLEX-SIGNALS run-95799887: 칸이 적은 신설 파일 둘을 «범위 밖»으로 세어 구현을 보류했다.
|
|
813
|
+
const DECLARED_NEW_FILES_LINE = /^\s*(?:신설|new\s*files?)\s*[::]\s*(.*)$/i;
|
|
814
|
+
|
|
806
815
|
function declaredPathsFromGoalDocumentText(document: string): { readable: boolean; paths: readonly string[] } {
|
|
807
816
|
const originalAsk = extractVerbatimOriginalAsk(document)?.ask ?? null;
|
|
808
817
|
if (originalAsk === null) return { readable: false, paths: [] };
|
|
809
|
-
|
|
818
|
+
const declared = parseDeclaredTargetPathsFromAsk(originalAsk);
|
|
819
|
+
if (!declared.readable) return declared;
|
|
820
|
+
const newFiles = originalAsk.split(/\r?\n/).flatMap((line) => {
|
|
821
|
+
const match = DECLARED_NEW_FILES_LINE.exec(line);
|
|
822
|
+
return match ? parseDeclaredTargetPathsFromAsk(`대상 경로: ${match[1]}`).paths : [];
|
|
823
|
+
});
|
|
824
|
+
return newFiles.length > 0 ? { readable: true, paths: [...new Set([...declared.paths, ...newFiles])] } : declared;
|
|
810
825
|
}
|
|
811
826
|
|
|
812
827
|
/** Only paths explicitly designated as targets in the ask or piece text protect an intentional revert. */
|
|
@@ -2280,6 +2295,7 @@ export interface SelfImplementResult {
|
|
|
2280
2295
|
branch?: string;
|
|
2281
2296
|
gate?: SelfImplementGateResult;
|
|
2282
2297
|
review?: SelfImplementReview;
|
|
2298
|
+
rework?: ReviewRework;
|
|
2283
2299
|
/** Worktree commit observed while this review ran; not inferred from the later PR head. */
|
|
2284
2300
|
reviewedHeadCommit?: string;
|
|
2285
2301
|
completionDisposition?: SelfImplementCompletionDisposition;
|
|
@@ -3569,6 +3585,29 @@ async function autohealRecordedStop(stopEntry: RunLedgerEntry, s: SelfImplementS
|
|
|
3569
3585
|
}
|
|
3570
3586
|
}
|
|
3571
3587
|
|
|
3588
|
+
export function measureReviewRework(counts: readonly number[], findings: readonly (readonly RecurrenceFinding[])[]): ReviewRework {
|
|
3589
|
+
const deltas = counts.slice(1).map((count, index) => Math.sign(count - counts[index]!));
|
|
3590
|
+
const directions = deltas.filter((delta) => delta !== 0);
|
|
3591
|
+
const seen = new Set<string>();
|
|
3592
|
+
let resurfaced = false;
|
|
3593
|
+
for (let index = 0; index < findings.length; index++) {
|
|
3594
|
+
const ids = new Set(findings[index]!.map((finding) => finding.id));
|
|
3595
|
+
if (index > 0 && [...ids].some((id) => seen.has(id) && !findings[index - 1]!.some((finding) => finding.id === id))) {
|
|
3596
|
+
const prior = findings.slice(0, index - 1).reverse().find((round) => round.some((finding) => ids.has(finding.id)));
|
|
3597
|
+
if (prior && measureReviewFindingRecurrence(findings[index], prior)?.normalizedRepeatedReviewFindingCount) resurfaced = true;
|
|
3598
|
+
}
|
|
3599
|
+
for (const id of ids) seen.add(id);
|
|
3600
|
+
}
|
|
3601
|
+
const trend: ReviewRework['trend'] = resurfaced || directions.some((delta) => delta !== directions[0])
|
|
3602
|
+
? 'oscillating'
|
|
3603
|
+
: counts.length < 2 ? 'unmeasured'
|
|
3604
|
+
: deltas.every((delta) => delta < 0) ? 'improving'
|
|
3605
|
+
: deltas.every((delta) => delta === 0) ? 'stable'
|
|
3606
|
+
: counts.at(-1)! > counts[0]! && deltas.every((delta) => delta >= 0) ? 'worsening'
|
|
3607
|
+
: 'stable';
|
|
3608
|
+
return { trend, signature: [...new Set((findings.at(-1) ?? []).map((finding) => finding.id))].sort().join(','), rounds: counts.length };
|
|
3609
|
+
}
|
|
3610
|
+
|
|
3572
3611
|
export async function runSelfImplement(opts: SelfImplementOptions): Promise<SelfImplementResult> {
|
|
3573
3612
|
if (opts.childLlm && (typeof opts.childLlm.provider !== 'string' || !opts.childLlm.provider.trim() || typeof opts.childLlm.model !== 'string' || !opts.childLlm.model.trim())) {
|
|
3574
3613
|
throw new Error('childLlm.provider and childLlm.model must both be non-empty');
|
|
@@ -3633,6 +3672,13 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
3633
3672
|
let roundCount = 0;
|
|
3634
3673
|
let lastTier: EscalateTier | undefined;
|
|
3635
3674
|
let lastReviewFindings: string[] | undefined;
|
|
3675
|
+
let measuredRework: ReviewRework | undefined;
|
|
3676
|
+
const withMeasuredRework = (result: SelfImplementResult): SelfImplementResult => {
|
|
3677
|
+
if (!measuredRework) return result;
|
|
3678
|
+
const classification = result.abandonedClassification?.classification;
|
|
3679
|
+
// An empty must-fix set stays an empty signature: a bare classification prefix would make two clean reviews «match».
|
|
3680
|
+
return { ...result, rework: { ...measuredRework, signature: `${classification && measuredRework.signature ? `${classification}:` : ''}${measuredRework.signature}` } };
|
|
3681
|
+
};
|
|
3636
3682
|
let reviewFindingRoundCount = 0;
|
|
3637
3683
|
const verbatimReviewFindingOccurrences = new Map<string, number>();
|
|
3638
3684
|
const normalizedReviewFindingFirstSeenRounds = new Map<string, number>();
|
|
@@ -3982,6 +4028,62 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
3982
4028
|
const finalizeTraversal = (): void => {
|
|
3983
4029
|
if (traversalFinalized) return;
|
|
3984
4030
|
traversalFinalized = true;
|
|
4031
|
+
if (terminalResult?.ok && runGraphTemplate?.graphId === 'self-implement') {
|
|
4032
|
+
const entered = new Set<string>(traversedNodes);
|
|
4033
|
+
let journeyRowsRead = false;
|
|
4034
|
+
const path = logsDbPath();
|
|
4035
|
+
if (existsSync(path)) {
|
|
4036
|
+
let store: LogStore | undefined;
|
|
4037
|
+
try {
|
|
4038
|
+
store = LogStore.openReadOnly(path);
|
|
4039
|
+
const join = journeyJoinFields();
|
|
4040
|
+
const rows = [
|
|
4041
|
+
...store.queryByDataKeys({ exactCategories: ['self-implement'], runIds: [runId] }),
|
|
4042
|
+
...(join.journeyKey ? store.queryAll({ exactCategories: ['self-implement'], events: ['pipeline-node-entry'], grep: join.journeyKey }) : []),
|
|
4043
|
+
...(join.queueLaunchId ? store.queryAll({ exactCategories: ['self-implement'], events: ['pipeline-node-entry'], grep: join.queueLaunchId }) : []),
|
|
4044
|
+
];
|
|
4045
|
+
for (const row of rows) {
|
|
4046
|
+
if (row.event !== 'pipeline-node-entry' || !row.data) continue;
|
|
4047
|
+
let data: { runId?: string; journeyKey?: string; queueLaunchId?: string; graphId?: string; graphVersion?: string; node?: string; phase?: string };
|
|
4048
|
+
try { data = JSON.parse(row.data); } catch { continue; }
|
|
4049
|
+
const sameRun = data.runId === runId || (data.runId === undefined && (
|
|
4050
|
+
(join.journeyKey !== undefined && data.journeyKey === join.journeyKey)
|
|
4051
|
+
|| (join.queueLaunchId !== undefined && data.queueLaunchId === join.queueLaunchId)
|
|
4052
|
+
));
|
|
4053
|
+
if (sameRun && data.phase === 'journey' && data.graphId === runGraphTemplate.graphId && data.graphVersion === runGraphTemplate.version && typeof data.node === 'string') {
|
|
4054
|
+
entered.add(data.node);
|
|
4055
|
+
journeyRowsRead = true;
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
} catch { /* observation failure must not fail the run */ }
|
|
4059
|
+
finally { store?.close(); }
|
|
4060
|
+
}
|
|
4061
|
+
// Only nodes this process runs (TERMINAL_STAGES_BY_NODE) can be «skipped» with certainty:
|
|
4062
|
+
// their non-entry here means the condition was false. Nodes run by other processes
|
|
4063
|
+
// (author · intake · dispatch · pod …) that were not joined stay «unjoined» — not
|
|
4064
|
+
// skipped and not accounted — so the walk ratio can fall below 1.0.
|
|
4065
|
+
const skipped = new Set<string>();
|
|
4066
|
+
const unjoined: string[] = [];
|
|
4067
|
+
for (const node of runGraphTemplate.nodes) {
|
|
4068
|
+
if (entered.has(node.nodeId)) continue;
|
|
4069
|
+
if (!Object.hasOwn(TERMINAL_STAGES_BY_NODE, node.nodeId)) { unjoined.push(node.nodeId); continue; }
|
|
4070
|
+
const condition = node.nodeId === 'rework' ? 'gate/review passed'
|
|
4071
|
+
: node.nodeId === 'main-sync' || node.nodeId === 'regate' ? 'mergeMain && commitWork'
|
|
4072
|
+
: 'completion disposition';
|
|
4073
|
+
observeOuter('node-skipped', {
|
|
4074
|
+
...graphIdentityOf(runGraphTemplate), ...journeyJoinFields(),
|
|
4075
|
+
node: node.nodeId, reason: 'condition', condition,
|
|
4076
|
+
}, { category: 'graph.path' });
|
|
4077
|
+
skipped.add(node.nodeId);
|
|
4078
|
+
}
|
|
4079
|
+
const coverage = measureL0Coverage(runGraphTemplate, entered, skipped);
|
|
4080
|
+
observeOuter('l0-coverage', {
|
|
4081
|
+
...graphIdentityOf(runGraphTemplate), ...journeyJoinFields(),
|
|
4082
|
+
...coverage,
|
|
4083
|
+
journeyRowsRead,
|
|
4084
|
+
unjoinedNodes: unjoined,
|
|
4085
|
+
}, { category: 'graph.path' });
|
|
4086
|
+
}
|
|
3985
4087
|
try {
|
|
3986
4088
|
const checked = terminalNode
|
|
3987
4089
|
? (opts.seams.checkPipelineTraversal ?? checkPipelineTraversal)(traversedNodes, terminalNode)
|
|
@@ -4147,6 +4249,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
4147
4249
|
},
|
|
4148
4250
|
observeRoundExecution,
|
|
4149
4251
|
observeReviewFindings,
|
|
4252
|
+
(rework) => { measuredRework = rework; },
|
|
4150
4253
|
setRunGraphIdentity,
|
|
4151
4254
|
(template) => { runGraphTemplate = template; },
|
|
4152
4255
|
// ⭐ 「요구 증거 충족」을 받아 결과에 실어 원장까지 나른다(`JDG-T60`)
|
|
@@ -4200,7 +4303,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
4200
4303
|
if (lastCredentialFailure) {
|
|
4201
4304
|
observeOuter('credential-failure-observed', { count: credentialFailureCount, ...lastCredentialFailure });
|
|
4202
4305
|
}
|
|
4203
|
-
terminalResult = withEvidenceCoverage(addressedResult);
|
|
4306
|
+
terminalResult = withEvidenceCoverage(withMeasuredRework(addressedResult));
|
|
4204
4307
|
// runSelfImplement is the terminal caller: retain every existing PR/ledger/log observation and append one stop fact.
|
|
4205
4308
|
if ((addressedResult.stage === 'aborted' || (addressedResult.mergeReason && addressedResult.mergeReason !== 'auto')) && !runStopRecorded(runId)) {
|
|
4206
4309
|
const reason = addressedResult.mergeReason;
|
|
@@ -4263,7 +4366,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
4263
4366
|
});
|
|
4264
4367
|
observeRunRollup(observeOuter, addressedResult, roundClassifications, roundCount, capturedPrNumber, openPrInvoked);
|
|
4265
4368
|
observeProgressDelivery();
|
|
4266
|
-
return
|
|
4369
|
+
return terminalResult;
|
|
4267
4370
|
} catch (e) {
|
|
4268
4371
|
if (e instanceof StepTimeoutError) {
|
|
4269
4372
|
observeOuter('step-timeout', { step: e.step, ms: e.ms, branch, feature: opts.feature.slice(0, 80) }, { level: 'warn' });
|
|
@@ -4296,7 +4399,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
4296
4399
|
if (lastCredentialFailure) {
|
|
4297
4400
|
observeOuter('credential-failure-observed', { count: credentialFailureCount, ...lastCredentialFailure });
|
|
4298
4401
|
}
|
|
4299
|
-
terminalResult = addressedResult; // ⛔ 이 분기(StepTimeoutError)는 스코프가 달라
|
|
4402
|
+
terminalResult = withMeasuredRework(addressedResult); // ⛔ 이 분기(StepTimeoutError)는 스코프가 달라 달성 근사값은 «없다»(0 으로 채우지 않는다)
|
|
4300
4403
|
observeDeclaredScopeDiff(observeOuter, {
|
|
4301
4404
|
goalFile: opts.goalFile,
|
|
4302
4405
|
changedFiles: opts.goalFile && addressedResult.worktreePath
|
|
@@ -4338,7 +4441,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
4338
4441
|
});
|
|
4339
4442
|
observeRunRollup(observeOuter, addressedResult, roundClassifications, roundCount, capturedPrNumber, openPrInvoked);
|
|
4340
4443
|
observeProgressDelivery();
|
|
4341
|
-
return
|
|
4444
|
+
return terminalResult;
|
|
4342
4445
|
}
|
|
4343
4446
|
recordHitlEvent({
|
|
4344
4447
|
kind: 'escalation',
|
|
@@ -4767,6 +4870,7 @@ async function runSelfImplementInner(
|
|
|
4767
4870
|
onWorktreeCreated: (path: string) => void = () => {},
|
|
4768
4871
|
onRoundExecution: (tier: EscalateTier) => void = () => {},
|
|
4769
4872
|
onReviewFindings: (round: number, mustFix: readonly string[]) => void = () => {},
|
|
4873
|
+
onReworkMeasured: (rework: ReviewRework) => void = () => {},
|
|
4770
4874
|
/** ⭐ 이 런이 «따르는» 그래프 신원 — 맥락이 정해진 «직후» 한 번 불린다.
|
|
4771
4875
|
* ⛔ 이것이 없으면 걸음은 YAML 을 따르고 «신원은 옛 TS 상수»를 따라 둘이 갈린다. */
|
|
4772
4876
|
onGraphResolved: (identity: { graphId: string; graphVersion: string }) => void = () => {},
|
|
@@ -4858,9 +4962,11 @@ async function runSelfImplementInner(
|
|
|
4858
4962
|
writeFileSync(graphFile, stringify({
|
|
4859
4963
|
graph_id: variantSpec.graphId, version: variantSpec.version,
|
|
4860
4964
|
entry_node: variantSpec.entryNode, terminal_nodes: variantSpec.terminalNodes,
|
|
4965
|
+
...(variantSpec.journeyStages ? { journey_stages: variantSpec.journeyStages } : {}),
|
|
4861
4966
|
...(variantSpec.runContract ? { run_contract: variantSpec.runContract } : {}),
|
|
4862
4967
|
nodes: variantSpec.nodes.map(n => ({
|
|
4863
4968
|
node_id: n.nodeId, kind: n.kind, recipe: n.recipe, max_visits: n.maxVisits,
|
|
4969
|
+
...(n.journeyStage ? { journey_stage: n.journeyStage } : {}),
|
|
4864
4970
|
...(n.progress ? { progress: n.progress } : {}), ...(n.phases ? { phases: n.phases } : {}),
|
|
4865
4971
|
...(n.terminalStages ? { terminal_stages: n.terminalStages } : {}),
|
|
4866
4972
|
...(n.fanOut ? { fan_out: n.fanOut } : {}), ...(n.contract ? { contract: n.contract } : {}),
|
|
@@ -4907,12 +5013,12 @@ async function runSelfImplementInner(
|
|
|
4907
5013
|
const lastStop = entries?.slice().reverse().find((entry) => entry.event === 'stop');
|
|
4908
5014
|
if (lastStop && isRunStopClass(lastStop.data.class)) stopClass = readRunStopRecord(lastStop)?.class;
|
|
4909
5015
|
} catch { /* Unreadable STOP-RECORD is unknown, not an invented class. */ }
|
|
4910
|
-
const runtimeOverlayState = {
|
|
5016
|
+
const runtimeOverlayState = buildRuntimeOverlayState({
|
|
4911
5017
|
attempts: round,
|
|
4912
|
-
...(opts.goalId === undefined ? {} : {
|
|
4913
|
-
...(stopClass === undefined ? {} : {
|
|
4914
|
-
...(reviewRepeat === undefined ? {} : {
|
|
4915
|
-
};
|
|
5018
|
+
...(opts.goalId === undefined ? {} : { goalId: opts.goalId }),
|
|
5019
|
+
...(stopClass === undefined ? {} : { stopClass }),
|
|
5020
|
+
...(reviewRepeat === undefined ? {} : { reviewRepeat }),
|
|
5021
|
+
});
|
|
4916
5022
|
const decision = decideTemplate({
|
|
4917
5023
|
goalType: graphGoalType,
|
|
4918
5024
|
authority: graphAuthority,
|
|
@@ -5266,6 +5372,8 @@ async function runSelfImplementInner(
|
|
|
5266
5372
|
/** Review-budget leftovers and the body that carried them, so a later merge drafts from the same text. */
|
|
5267
5373
|
let preparedReviewBudgetPrBody: string | undefined;
|
|
5268
5374
|
const reviewMustFixCountHistory: number[] = [];
|
|
5375
|
+
const reviewedFindings: RecurrenceFinding[][] = [];
|
|
5376
|
+
const structuredRework = (): ReviewRework => measureReviewRework(reviewMustFixCountHistory, reviewedFindings);
|
|
5269
5377
|
const reviewMustFixTrend = (): string => {
|
|
5270
5378
|
if (reviewMustFixCountHistory.length < 2) return '아직 못 잰다';
|
|
5271
5379
|
return reviewMustFixCountHistory.every((count, index) => index === 0 || count < reviewMustFixCountHistory[index - 1]!)
|
|
@@ -6777,6 +6885,25 @@ async function runSelfImplementInner(
|
|
|
6777
6885
|
...(repeatedWithRound === undefined ? {} : { repeatedWithRound }), omittedRounds,
|
|
6778
6886
|
}, { category: 'self-dev.rework' });
|
|
6779
6887
|
}
|
|
6888
|
+
if (round > 0) {
|
|
6889
|
+
// ⛔ 관측 전용 — 계산은 순수 함수가, 기록은 `observe`(= 런 원장)가 한다. 판정·분기·오버레이 상태는 건드리지 않는다.
|
|
6890
|
+
try {
|
|
6891
|
+
const latestEntry = roundSummaryEntries[roundSummaryEntries.length - 1];
|
|
6892
|
+
const gateRoundIds = latestEntry?.kind === 'gate'
|
|
6893
|
+
? roundSummaryEntries.filter((entry) => entry.kind === 'gate').map((entry) => entry.failedIds)
|
|
6894
|
+
: [];
|
|
6895
|
+
observe('flex-signals', {
|
|
6896
|
+
round,
|
|
6897
|
+
...computeFlexSignals({
|
|
6898
|
+
gateFailedIdsByRound: gateRoundIds,
|
|
6899
|
+
failureAttributions: reworkKind === 'gate' ? gate.baselineFailures?.map(({ attribution }) => attribution) : undefined,
|
|
6900
|
+
reviewRepeat,
|
|
6901
|
+
round,
|
|
6902
|
+
plannedRounds: effectiveMax,
|
|
6903
|
+
}),
|
|
6904
|
+
});
|
|
6905
|
+
} catch { /* observation must not interrupt rework */ }
|
|
6906
|
+
}
|
|
6780
6907
|
const refutationGuidanceEligible = round > 0 && refutableFindings.length > 0;
|
|
6781
6908
|
const dispatchedRefutationGuidance = refutationGuidancePresented(feature, refutationGuidanceEligible);
|
|
6782
6909
|
pendingPlanRevision = undefined;
|
|
@@ -7518,6 +7645,8 @@ async function runSelfImplementInner(
|
|
|
7518
7645
|
});
|
|
7519
7646
|
if (review.reviewed) {
|
|
7520
7647
|
reviewMustFixCountHistory.push(review.mustFix.length);
|
|
7648
|
+
reviewedFindings.push(review.mustFix.map((item) => ({ id: stableMustFixId(item), item })));
|
|
7649
|
+
onReworkMeasured(structuredRework());
|
|
7521
7650
|
// The next runtime overlay sees completed review rounds, not consecutive repeats of one finding.
|
|
7522
7651
|
reviewRepeat = reviewMustFixCountHistory.length - 1;
|
|
7523
7652
|
}
|
|
@@ -566,13 +566,83 @@ export function queryRunningRuns(options: { includeTest?: boolean; runIds?: read
|
|
|
566
566
|
}
|
|
567
567
|
}
|
|
568
568
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const partial = (result.count !== undefined && typeof result.count !== 'number' && result.count.count === null)
|
|
569
|
+
function isPartialRunningRuns(result: RunningRunsResult): boolean {
|
|
570
|
+
return (result.count !== undefined && typeof result.count !== 'number' && result.count.count === null)
|
|
572
571
|
|| result.completeness === 'partial' || hasUnreadableLedgerObservation(result.ledger) || (result.unreadable?.length ?? 0) > 0;
|
|
573
|
-
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function runningRunsLowerBound(result: RunningRunsResult): number {
|
|
575
|
+
return result.count !== undefined && typeof result.count !== 'number'
|
|
574
576
|
? result.count.lowerBound : result.counts.running + result.counts['probable-running'];
|
|
575
|
-
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function runningRunsUnreadable(result: RunningRunsResult): readonly UnreadableRunningRunsLedger[] {
|
|
580
|
+
return result.unreadable ?? (result.count !== undefined && typeof result.count !== 'number' ? result.count.unreadable : []);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export interface RunProgressRow {
|
|
584
|
+
readonly runId: string;
|
|
585
|
+
readonly status: RunningRunStatus;
|
|
586
|
+
/** null = no phase observed; never rendered as «none». */
|
|
587
|
+
readonly lastPhase: string | null;
|
|
588
|
+
readonly lastPhaseObservedAt: string | null;
|
|
589
|
+
readonly lastActivityTimestamp: string | null;
|
|
590
|
+
/** Milliseconds since lastActivityTimestamp; null when the timestamp is absent or unparseable. */
|
|
591
|
+
readonly elapsedMs: number | null;
|
|
592
|
+
readonly ptyRefs: readonly string[];
|
|
593
|
+
readonly phaseObservation: RunningRunPhaseObservation;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export function buildRunProgressRows(result: RunningRunsResult, nowMs: number = Date.now()): RunProgressRow[] {
|
|
597
|
+
return result.entries.map((entry) => {
|
|
598
|
+
const activityMs = entry.lastActivityTimestamp === null ? Number.NaN : Date.parse(entry.lastActivityTimestamp);
|
|
599
|
+
return {
|
|
600
|
+
runId: entry.runId,
|
|
601
|
+
status: entry.status,
|
|
602
|
+
lastPhase: entry.lastPhase ?? null,
|
|
603
|
+
lastPhaseObservedAt: entry.lastPhaseObservedAt ?? null,
|
|
604
|
+
lastActivityTimestamp: entry.lastActivityTimestamp,
|
|
605
|
+
elapsedMs: Number.isFinite(activityMs) ? Math.max(0, nowMs - activityMs) : null,
|
|
606
|
+
ptyRefs: entry.ptyRefs.map((ref) => `${ref.instance}:${ref.id}`),
|
|
607
|
+
phaseObservation: entry.phaseObservation ?? 'not-observed',
|
|
608
|
+
};
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function formatElapsed(ms: number): string {
|
|
613
|
+
const totalSeconds = Math.floor(ms / 1000);
|
|
614
|
+
const days = Math.floor(totalSeconds / 86_400);
|
|
615
|
+
const hours = Math.floor((totalSeconds % 86_400) / 3_600);
|
|
616
|
+
const minutes = Math.floor((totalSeconds % 3_600) / 60);
|
|
617
|
+
const seconds = totalSeconds % 60;
|
|
618
|
+
if (days > 0) return `${days}d${hours}h`;
|
|
619
|
+
if (hours > 0) return `${hours}h${minutes}m`;
|
|
620
|
+
if (minutes > 0) return `${minutes}m${seconds}s`;
|
|
621
|
+
return `${seconds}s`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
export function renderRunProgressTable(result: RunningRunsResult, nowMs: number = Date.now()): string {
|
|
625
|
+
const partial = isPartialRunningRuns(result);
|
|
626
|
+
const header = partial
|
|
627
|
+
? `부분 관측 — 최소 ${runningRunsLowerBound(result)} (읽지 못한 원장: ${runningRunsUnreadable(result).map((item) => `${item.dir}:${item.reason}`).join(',') || '없음'})`
|
|
628
|
+
: `도는 런 ${result.counts.running + result.counts['probable-running']} (확인 ${result.counts.running} · 추정 ${result.counts['probable-running']})`;
|
|
629
|
+
const rows = buildRunProgressRows(result, nowMs).map((row) => {
|
|
630
|
+
const phaseUnreadable = row.phaseObservation === 'unreadable' || row.phaseObservation === 'partially-unreadable';
|
|
631
|
+
const phase = row.lastPhase ?? (phaseUnreadable ? '미관측 (단계 저장소 읽기 실패)' : '미관측');
|
|
632
|
+
const phaseAt = row.lastPhaseObservedAt ?? '미관측';
|
|
633
|
+
const progress = row.lastActivityTimestamp === null
|
|
634
|
+
? '미관측'
|
|
635
|
+
: `${row.lastActivityTimestamp} (경과 ${row.elapsedMs === null ? '알 수 없음' : formatElapsed(row.elapsedMs)})`;
|
|
636
|
+
return `${row.runId} · ${row.status} · 단계 ${phase} · 진입 ${phaseAt} · 마지막 진전 ${progress} · pty ${row.ptyRefs.join(',') || '없음'}`;
|
|
637
|
+
});
|
|
638
|
+
return [header, ...rows].join('\n');
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
export function renderRunningRuns(result: RunningRunsResult): string {
|
|
642
|
+
const lingeringLaunchParents = result.lingeringLaunchParents ?? { observation: 'indeterminate' } as const;
|
|
643
|
+
const partial = isPartialRunningRuns(result);
|
|
644
|
+
const lowerBound = runningRunsLowerBound(result);
|
|
645
|
+
const unreadable = runningRunsUnreadable(result);
|
|
576
646
|
return [
|
|
577
647
|
partial
|
|
578
648
|
? `running runs: unknown (at least ${lowerBound}) unreadable: ${unreadable.map((item) => `${item.dir}:${item.reason}`).join(',')}`
|