elanous 0.2.15 → 0.2.17
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/{1307-33ffcc49ab45123b.js → 1307-aac7332576c4d6fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6877-7c6208705c284124.js → 6877-fe94e76a5841ee9b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-0e2c57fe6add4b2a.js → page-3143b07a817d1da9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-e05a77df7e005b04.js → page-17ba086ef82d339b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-7eb661ff23af7da2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-b8c49f6b4fa2df91.js → page-6e5c3a867557298b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-976813c91d7394ea.js → page-9a41d65355fc25b9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-5e77cd1802567c89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-1d4901eb7c2d8822.js → page-41afec0b255ed2bb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-72d3e7ff35a8c954.js → page-4187666132fc98c4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-60c7a8e1a4fe62ce.js → page-09328d499162bae4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-2a52b316cb143d9c.js → page-29ff3a8f84f03b6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-c3ce483c8d5db370.js → page-80ec7401bc83f511.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-96f074f6538e8a2c.js → page-fc7d86467291ae38.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-e97be5334d83a9f5.js → layout-de43a186e5ba5683.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-0e2d6985fb42cd2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-13e4108999f58d13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e8296ee284aa1293.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-c1343f9499d86f3f.js → page-043a9111e74f6b24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-8739a2e0ae793130.js → not-found-23a4601ceb7f452e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-d23b12bb92242558.js → page-10d958605a77b3bd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-c7fea475488201a8.js → page-a04260b8ccc968d4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-7a1d85e121065cc8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-5efbf81124c474ae.js → page-12916918988ac97e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d8a3347a9d0c3f4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-06ee266953d9c879.js → page-5a64cb5208c6a8fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-153c2b3bcc89e8de.js → page-1407ec83ea64c1f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-1d824510df025d18.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-0d6d23b40263a7ea.js → page-7f49fe645d5132f7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-27d71f131acd9320.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-73735b64bfe1ae67.js → page-424659bacfb8cdd7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-f7e631e9b4daf66e.js → page-fc2df261f8a2c7df.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-9817c7f9007466cc.js → page-5d4fef9ccd7d406b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +1 -0
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
- package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +15 -15
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +15 -15
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +15 -15
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +15 -15
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +15 -15
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +15 -15
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +15 -15
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +15 -15
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +15 -15
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +15 -15
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +15 -15
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +15 -15
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +15 -15
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +15 -15
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +15 -15
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +15 -15
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +15 -15
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +15 -15
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +15 -15
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +15 -15
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +15 -15
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +15 -15
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +15 -15
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +15 -15
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +15 -15
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +15 -15
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +16 -16
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +15 -15
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +15 -15
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +15 -15
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +15 -15
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +15 -15
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +15 -15
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +15 -15
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +15 -15
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +15 -15
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +15 -15
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +15 -15
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +15 -15
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +15 -15
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +15 -15
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +15 -15
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +15 -15
- package/graphs/doc-gen/doc-gen.yaml +24 -0
- package/graphs/doc-gen/recipes.yaml +3 -0
- package/graphs/intake/intake-daily.yaml +9 -2
- package/graphs/intake/news-sources.yaml +4 -0
- package/graphs/intake/recipes.yaml +6 -1
- package/graphs/orchestrator/orchestrator.yaml +7 -0
- package/graphs/orchestrator/recipes.yaml +3 -0
- package/graphs/steward/recipes.yaml +2 -1
- package/package.json +1 -1
- package/scripts/__fixtures__/intake-news.xml +7 -0
- package/scripts/ci-public-export-import-gate.ts +154 -0
- package/scripts/ci-test-interference-gate.ts +5 -1
- package/scripts/ci-typecheck-changed.ts +34 -15
- package/scripts/docs/doc-rot.ts +164 -0
- package/scripts/docs/generate-graph-anatomy.ts +131 -9
- package/scripts/docs-cli-check.ts +4 -4
- package/scripts/goals/goal-census.ts +343 -0
- package/scripts/intake-news.ts +169 -0
- package/scripts/measure/author-section-contribution.ts +84 -0
- package/scripts/release-loop/cut-branch.ts +43 -13
- package/scripts/release-loop/docs-land-node.ts +69 -10
- package/scripts/release-loop/gate-node.ts +3 -1
- package/scripts/release-loop/publish-node.ts +2 -2
- package/scripts/release-loop/release-docs.ts +134 -0
- package/scripts/release-loop/vault-note-node.ts +19 -2
- package/scripts/release-loop/version-node.ts +7 -4
- package/scripts/release-story/draft.ts +42 -14
- package/scripts/rhythm/monthly.ts +97 -0
- package/src/acp/client.ts +17 -2
- package/src/agent-mission/driver.ts +104 -11
- package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
- package/src/agent-mission/supervisor-metrics.ts +218 -0
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/autopilot/track-agent.ts +5 -3
- package/src/briefing/brief-items.ts +191 -36
- package/src/budget/usage-outcomes.ts +204 -0
- package/src/cli/autopilot-cli.ts +66 -0
- package/src/cli/brief-cli.ts +73 -0
- package/src/cli/decisions-cli.ts +4 -1
- package/src/cli/intake-cli.ts +1 -1
- package/src/cli/know-cli.ts +20 -0
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/logs-cli.ts +43 -1
- package/src/cli/pr-cli.ts +92 -13
- package/src/cli/provider-cli.ts +14 -0
- package/src/cli/release-cli.ts +11 -8
- package/src/cli/self-update-canary.ts +46 -0
- package/src/cli/self-update.ts +399 -1
- package/src/cli/usage-cli.ts +26 -0
- package/src/context-card/index.ts +45 -0
- package/src/context-card/task.ts +76 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
- package/src/decisions/decision-ledger.ts +10 -1
- package/src/discord-self-message.ts +9 -0
- package/src/flow-loop/split.ts +24 -0
- package/src/harness/harness-cli-command.ts +178 -12
- package/src/harness/harness-pod-dispatch.ts +36 -55
- package/src/harness/harness-queue.ts +86 -7
- package/src/harness/harness-seams.ts +13 -3
- package/src/harness/helper-repair.ts +139 -8
- package/src/harness/helper-scan.ts +13 -2
- package/src/harness/nested-elanous-policy.ts +133 -0
- package/src/hq/fence-wrapper.ts +123 -0
- package/src/hq/hq.ts +16 -2
- package/src/hq/lease.ts +5 -3
- package/src/hq/transfer.ts +185 -0
- package/src/index.ts +168 -13
- package/src/intake-plane/digest.ts +85 -28
- package/src/knowledge/know-find.ts +105 -0
- package/src/llm.ts +75 -19
- package/src/loops/orchestrator/finish-rate.ts +14 -3
- package/src/loops/orchestrator/tick.ts +96 -25
- package/src/maturity/bot-slash-catalog.ts +11 -2
- package/src/maturity/feature-maturity.ts +2 -2
- package/src/maturity/tui-slash-availability.ts +9 -0
- package/src/nexus/api/grid.ts +61 -0
- package/src/nexus/api/http-server.ts +19 -1
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/oauth/codex.ts +82 -2
- package/src/oauth/overload-failover-launch.ts +75 -0
- package/src/oauth/overload-failover.ts +137 -0
- package/src/release-loop/checklist.ts +10 -5
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/landing-freeze.ts +18 -10
- package/src/release-loop/placement.ts +17 -8
- package/src/seat-dispatch/seat-ask.ts +33 -6
- package/src/seat-loop/seat-loop.ts +172 -21
- package/src/self-dev/ask-launch-flow.ts +48 -8
- package/src/self-dev/authored-goal-trace.ts +64 -0
- package/src/self-dev/dev-cli.ts +19 -1
- package/src/self-dev/dev-pipeline.ts +6 -5
- package/src/self-dev/entrance-registry.ts +10 -6
- package/src/self-dev/old-door.ts +73 -0
- package/src/self-dev/run-store.ts +5 -1
- package/src/self-dev/run-supervisor.ts +7 -1
- package/src/self-dev/supervisor-verdict-edges.ts +46 -0
- package/src/self-implement/app-api-budget.ts +166 -0
- package/src/self-implement/budget-gate.ts +7 -5
- package/src/self-implement/follow-up-goals.ts +289 -0
- package/src/self-implement/frozen-merges.ts +25 -20
- package/src/self-implement/goal-author-clarification.ts +81 -0
- package/src/self-implement/goal-author-cli.ts +33 -5
- package/src/self-implement/goal-author.ts +107 -25
- package/src/self-implement/goal-clarification-escalation.ts +37 -1
- package/src/self-implement/goal-document-write.ts +28 -0
- package/src/self-implement/heal-intake.ts +4 -0
- package/src/self-implement/heal-triage.ts +44 -0
- package/src/self-implement/host-regate.ts +3 -1
- package/src/self-implement/orchestrator.ts +157 -8
- package/src/self-implement/run-ledger.ts +4 -0
- package/src/self-implement/seams.ts +44 -15
- package/src/session-runtime/index.ts +2 -0
- package/src/session-runtime/retry-api.ts +87 -0
- package/src/session-runtime/retry-policy.ts +107 -6
- package/src/steward/working-backwards.ts +16 -1
- package/src/task-agent/actions.ts +118 -0
- package/src/task-agent/judge.ts +96 -0
- package/src/task-cards/card-cli.ts +25 -0
- package/src/task-cards/card-store.ts +9 -4
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +33 -4
- package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
- package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +95 -18
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +168 -37
- package/src/task-orchestrator/surfaces/self-implement.ts +2 -0
- package/src/telegram-commands.ts +6 -1
- package/src/telegram-loops-command.ts +21 -14
- package/src/telegram.ts +47 -0
- package/src/user-config.ts +59 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-29d889b809288e65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-955ec2da3be4c560.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e2ee2896811e98d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f1a1fc0a82738c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-7d4182964407c09c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-f5597e894c02cd33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8fcec4c9ea2e7f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.js +0 -1
- package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +0 -1
- package/src/launch-head/seat-goal-queue-lock.fixture.ts +0 -15
- package/src/launch-head/seat-goal-queue.ts +0 -198
- /package/apps/pwa/out/_next/static/{LZcstaO9_NXypPFmujolv → yRA5ur6DcPl0YHSRheH2O}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{LZcstaO9_NXypPFmujolv → yRA5ur6DcPl0YHSRheH2O}/_ssgManifest.js +0 -0
|
@@ -34,6 +34,7 @@ import { judgePredictionAccuracy } from './judge-prediction-accuracy.js';
|
|
|
34
34
|
import { measureReviewFindingRecurrence, type ReviewFindingRecurrence } from './review-finding-recurrence.js';
|
|
35
35
|
import { createHash } from 'node:crypto';
|
|
36
36
|
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync } from 'node:fs';
|
|
37
|
+
import { writeGoalDocumentAtomic } from './goal-document-write.js';
|
|
37
38
|
import { hostname, loadavg } from 'node:os';
|
|
38
39
|
import { resolveHostId } from '../platform/host-id.js';
|
|
39
40
|
import { resolveInstanceName } from '../instance-identity.js';
|
|
@@ -66,9 +67,11 @@ import { appendRunLedgerEntry, loadRunLedger, parseRunShardIdentity, queryRunCha
|
|
|
66
67
|
import { emitSessionEvent, type SessionEventInput } from '../context-bus/session-events.js';
|
|
67
68
|
import { decideLineageSupersede, lineageSupersedeCloseComment, type LineageSupersedeOpenDraft } from './lineage-supersede.js';
|
|
68
69
|
import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
70
|
+
import { homedir } from 'node:os';
|
|
69
71
|
import { enqueueControlMemo, readSoftStopRequestStatus, type SoftStopRequestRead } from '../harness/control-inbox.js';
|
|
70
72
|
import { debug } from '../debug/log.js';
|
|
71
73
|
import { recordFailureEvent } from './heal-intake.js';
|
|
74
|
+
import { classifyReworkCapExhaustion, type ReworkCapHealDecision } from './heal-triage.js';
|
|
72
75
|
import { releasePathHold, releasePathHoldComment, RELEASE_PATH_LABEL } from '../self-dev/release-path-guard.js';
|
|
73
76
|
import { emitDecision } from '../live/detail-switch.js';
|
|
74
77
|
import { reworkDecision } from './decision-events.js';
|
|
@@ -101,6 +104,26 @@ import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
|
101
104
|
import { addNextMdReleaseNote, harnessReleaseNote, parseReleaseNoteSection, releaseNotesDir, renderReleaseNoteSection, writeReleaseNote, type ReleaseNoteFragment, type ReleaseNoteSection } from '../release-loop/release-note.js';
|
|
102
105
|
import { landingFreezeMessage } from '../release-loop/landing-freeze.js';
|
|
103
106
|
import { admitLandingMerge, owningRepoRoot } from './frozen-merges.js';
|
|
107
|
+
import { recordFollowUpOnMerge, type FollowUpMergeInput, type FollowUpSeams } from './follow-up-goals.js';
|
|
108
|
+
|
|
109
|
+
/** Merge result and exit stay untouched: a draft failure is one debug line. */
|
|
110
|
+
async function draftFollowUpAfterMerge(input: FollowUpMergeInput, followUp: FollowUpSeams | undefined): Promise<void> {
|
|
111
|
+
try {
|
|
112
|
+
await recordFollowUpOnMerge(input, {
|
|
113
|
+
...(followUp ? {
|
|
114
|
+
...(followUp.mode ? { mode: followUp.mode } : {}),
|
|
115
|
+
...(followUp.readDrafts ? { readDrafts: followUp.readDrafts } : {}),
|
|
116
|
+
...(followUp.appendDraft ? { appendDraft: followUp.appendDraft } : {}),
|
|
117
|
+
...(followUp.enqueue ? { enqueue: followUp.enqueue } : {}),
|
|
118
|
+
...(followUp.cellOwner ? { cellOwner: followUp.cellOwner } : {}),
|
|
119
|
+
...(followUp.stateRoot ? { stateRoot: followUp.stateRoot } : {}),
|
|
120
|
+
} : {}),
|
|
121
|
+
log: (category, event, data) => { debug.log(category, event, data); },
|
|
122
|
+
});
|
|
123
|
+
} catch (error) {
|
|
124
|
+
debug.log('self-implement.follow-up', 'draft-failed', { prNumber: input.prNumber, error: error instanceof Error ? error.message : String(error) });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
104
127
|
import { plannedSelfImplBranch } from '../harness/worktree-branch-prefix.js';
|
|
105
128
|
export { slugifyFeature } from '../harness/worktree-branch-prefix.js';
|
|
106
129
|
import { AUTO_REVIEW_LABEL, resolveAutoReviewLabels } from './context-capsule.js';
|
|
@@ -220,7 +243,7 @@ export type { QuotaAccountAvailabilityEvidence };
|
|
|
220
243
|
import { decideReworkSalvage, type ReworkSalvageEvidence } from './rework-salvage.js';
|
|
221
244
|
import { type SupervisorGoalFillEvidence, fillUnverifiableGoalSlots, type SupervisorGoalFillResult } from './goal-supervisor-fill.js';
|
|
222
245
|
import { escalateGoalDocumentClarifications, recordSupervisorPlanRevision, type GoalClarificationEscalationResult, type SupervisorPlanRevisionRelaxation, type SupervisorPlanRevisionResult } from './goal-clarification-escalation.js';
|
|
223
|
-
import { parseGoalDocumentClarifications } from './goal-author-clarification.js';
|
|
246
|
+
import { parseGoalDocumentClarifications, selfAnswerGoalDocumentClarifications, type GoalClarificationSelfResolver } from './goal-author-clarification.js';
|
|
224
247
|
import {
|
|
225
248
|
createFileAskUserQuestionResolver,
|
|
226
249
|
type AskUserQuestionDispatchContext,
|
|
@@ -1056,7 +1079,7 @@ export interface SelfImplementSeams {
|
|
|
1056
1079
|
/** PR 생성 뒤 런의 라운드 대화를 게시한다. 실패는 PR 개설을 되돌리지 않는다. */
|
|
1057
1080
|
postPrComment?: (opts: { number: number; body: string; cwd: string }) => Promise<void>;
|
|
1058
1081
|
addPrLabel?: (opts: { number: number; label: string; cwd: string }) => Promise<void>;
|
|
1059
|
-
readPrFiles?: (opts: { number: number; cwd: string }) => Promise<string[]>;
|
|
1082
|
+
readPrFiles?: (opts: { number: number; cwd: string; base?: string }) => Promise<string[]>;
|
|
1060
1083
|
/** PR의 base/head SHA와 관측된 PR base를 한 응답에서 고정한다. 미주입·실패·빈 SHA면 검사 대상을 증명할 수 없어 fail-closed로 PR을 열어 둔다. */
|
|
1061
1084
|
readPrCommitShas?: (opts: { number: number; cwd: string }) => Promise<{ baseCommit: string; headCommit: string; baseRefName?: string }>;
|
|
1062
1085
|
/** 고정한 base/head SHA 쌍의 unified diff. 가변 PR 조회를 다시 사용하면 ABA head 변경에 안전하지 않으므로 두 SHA는 필수 입력이다. */
|
|
@@ -1090,6 +1113,12 @@ export interface SelfImplementSeams {
|
|
|
1090
1113
|
readRunLedger?: (runId: string) => RunLedgerEntry[] | null | Promise<RunLedgerEntry[] | null>;
|
|
1091
1114
|
closeDraft: (input: { number: number; comment: string }) => void | Promise<void>;
|
|
1092
1115
|
};
|
|
1116
|
+
/**
|
|
1117
|
+
* FOLLOWUP-LOOP① — after a successful merge, leftover findings become one follow-up draft.
|
|
1118
|
+
* Tests inject every store. Omission writes nothing (no operational ledger, no queue).
|
|
1119
|
+
* Production `defaultSeams` fills the state-root ledger and, only in `live`, the harness queue.
|
|
1120
|
+
*/
|
|
1121
|
+
followUp?: FollowUpSeams;
|
|
1093
1122
|
/** ⑥ HITL 게이트 — PR open 전 승인. ★fail-closed: 생략/false 면 PR 안 열림(자동 승인 없음). review 동봉
|
|
1094
1123
|
* (2026-07-21) — HITL 이 리뷰 verdict 를 보고 판단, auto 모드는 verdict 로 자동 결정. */
|
|
1095
1124
|
approvePr?: (summary: { branch: string; gateLog?: string; implSummary: string; review?: SelfImplementReview }) => Promise<boolean>;
|
|
@@ -1126,6 +1155,8 @@ export interface SelfImplementSeams {
|
|
|
1126
1155
|
/** ⭐ 「이 표면에 답할 사람이 있나」. 기본은 실제 stdin 의 TTY 여부다 —
|
|
1127
1156
|
* ⛔ 실물에 매이면 테스트가 «환경»을 검사하게 되므로 주입 가능하게 둔다. */
|
|
1128
1157
|
stdinIsInteractive?: () => boolean;
|
|
1158
|
+
/** Optional author self-answer seam for non-TTY Pods; never installs a terminal HITL resolver. */
|
|
1159
|
+
selfResolveClarification?: GoalClarificationSelfResolver;
|
|
1129
1160
|
}
|
|
1130
1161
|
|
|
1131
1162
|
/** onProgress seam 이 통지하는 단계(관측·UX 라벨). */
|
|
@@ -3394,7 +3425,26 @@ function mergeDecisionEvidenceCoverage(evidenceCoverage: {
|
|
|
3394
3425
|
: { evidenceCoverageMeasured: false };
|
|
3395
3426
|
}
|
|
3396
3427
|
|
|
3428
|
+
function installedBuildForOrigin(env: NodeJS.ProcessEnv): { installedVersion: string; installedCommit: string } {
|
|
3429
|
+
const root = env.ELANOUS_TEST_HOME?.trim()
|
|
3430
|
+
? join(env.ELANOUS_TEST_HOME.trim(), '.local/share/elanous')
|
|
3431
|
+
: join(homedir(), '.local/share/elanous');
|
|
3432
|
+
try {
|
|
3433
|
+
const parsed: unknown = JSON.parse(readFileSync(join(root, 'install.json'), 'utf8'));
|
|
3434
|
+
if (typeof parsed !== 'object' || parsed === null) return { installedVersion: 'unknown', installedCommit: 'unknown' };
|
|
3435
|
+
const version = (parsed as { version?: unknown }).version;
|
|
3436
|
+
const commit = (parsed as { commit?: unknown }).commit;
|
|
3437
|
+
return {
|
|
3438
|
+
installedVersion: typeof version === 'string' && version.trim() ? version.trim() : 'unknown',
|
|
3439
|
+
installedCommit: typeof commit === 'string' && commit.trim() ? commit.trim() : 'unknown',
|
|
3440
|
+
};
|
|
3441
|
+
} catch {
|
|
3442
|
+
return { installedVersion: 'unknown', installedCommit: 'unknown' };
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3397
3446
|
function runOriginData(env: NodeJS.ProcessEnv = process.env): RunOriginData {
|
|
3447
|
+
const installed = installedBuildForOrigin(env);
|
|
3398
3448
|
return {
|
|
3399
3449
|
hostId: resolveHostId(env),
|
|
3400
3450
|
hostname: hostname(),
|
|
@@ -3403,6 +3453,8 @@ function runOriginData(env: NodeJS.ProcessEnv = process.env): RunOriginData {
|
|
|
3403
3453
|
substrate: env.ELANOUS_SUBSTRATE || 'host',
|
|
3404
3454
|
instance: resolveInstanceName(),
|
|
3405
3455
|
elanousVersion: (JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')) as { version: string }).version,
|
|
3456
|
+
installedVersion: installed.installedVersion,
|
|
3457
|
+
installedCommit: installed.installedCommit,
|
|
3406
3458
|
...(env.ELANOUS_POD_NAME ? { podName: env.ELANOUS_POD_NAME } : {}),
|
|
3407
3459
|
...(env.ELANOUS_NODE_NAME ? { nodeName: env.ELANOUS_NODE_NAME } : {}),
|
|
3408
3460
|
...(env.ELANOUS_POD_NAMESPACE ? { podNamespace: env.ELANOUS_POD_NAMESPACE } : {}),
|
|
@@ -3616,6 +3668,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
3616
3668
|
},
|
|
3617
3669
|
} satisfies LogSink);
|
|
3618
3670
|
let traversalFinalized = false;
|
|
3671
|
+
reworkCapProceed = false;
|
|
3619
3672
|
const progressDelivery = { delivered: 0, unwired: 0, callbackFailed: 0 };
|
|
3620
3673
|
let capturedPrNumber: number | undefined;
|
|
3621
3674
|
let openPrInvoked = false;
|
|
@@ -4179,10 +4232,31 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
4179
4232
|
const terminalStage = terminalResult?.stage ?? 'crashed';
|
|
4180
4233
|
try {
|
|
4181
4234
|
const mappedStatus = mapStageToRunStatus(terminalStage)?.runStatus;
|
|
4182
|
-
|
|
4235
|
+
const reworkCapStop = terminalResult?.stage === 'pr-opened' && (
|
|
4236
|
+
terminalResult.outcome === 'budget-exhausted'
|
|
4237
|
+
|| (terminalResult.outcome === 'completed' && reworkCapProceed)
|
|
4238
|
+
);
|
|
4239
|
+
if (!terminalResult || mappedStatus === 'failed' || (mappedStatus === undefined && terminalResult.ok === false) || signalIncompleteState(roundClassifications).latestSignalIncomplete || reworkCapStop) {
|
|
4240
|
+
const reworkCap = reworkCapStop ? classifyReworkCapExhaustion({
|
|
4241
|
+
introduced: terminalResult?.gate?.reflectGateFacts?.introduced
|
|
4242
|
+
? [`introduced=${terminalResult.gate.reflectGateFacts.introduced}`]
|
|
4243
|
+
: [],
|
|
4244
|
+
mustFix: terminalResult?.review?.mustFix ?? terminalResult?.mustFix,
|
|
4245
|
+
summary: terminalResult?.review?.summary,
|
|
4246
|
+
}) : undefined;
|
|
4247
|
+
if (reworkCap) {
|
|
4248
|
+
observeOuter('rework-cap-heal', {
|
|
4249
|
+
stage: terminalStage,
|
|
4250
|
+
defectClass: reworkCap.defectClass,
|
|
4251
|
+
nextAction: reworkCap.nextAction,
|
|
4252
|
+
evidence: reworkCap.evidence,
|
|
4253
|
+
});
|
|
4254
|
+
}
|
|
4183
4255
|
recordFailureEvent({
|
|
4184
|
-
source: 'harness-run', kind: terminalStage, ref: runId,
|
|
4185
|
-
summary:
|
|
4256
|
+
source: 'harness-run', kind: reworkCapStop ? 'rework-cap-exhausted' : terminalStage, ref: runId,
|
|
4257
|
+
summary: reworkCap
|
|
4258
|
+
? reworkCapFailureSummary(opts.feature, reworkCap)
|
|
4259
|
+
: opts.feature.split(/\r?\n/, 1)[0]?.trim() || `Harness run ${runId} failed`,
|
|
4186
4260
|
at: new Date().toISOString(),
|
|
4187
4261
|
});
|
|
4188
4262
|
}
|
|
@@ -4544,6 +4618,14 @@ function observeRunRollup(
|
|
|
4544
4618
|
}, effectiveOutcome.runStatus === 'failed' ? { level: 'warn' } : undefined);
|
|
4545
4619
|
}
|
|
4546
4620
|
|
|
4621
|
+
let reworkCapProceed = false;
|
|
4622
|
+
|
|
4623
|
+
function reworkCapFailureSummary(feature: string, decision: ReworkCapHealDecision): string {
|
|
4624
|
+
const title = feature.split(/\\r?\\n/, 1)[0]?.trim() || 'rework cap exhausted';
|
|
4625
|
+
const evidence = decision.evidence.length ? decision.evidence.join(' | ') : 'no gate introduced list and no review finding';
|
|
4626
|
+
return `${title} [${decision.defectClass} → ${decision.nextAction}] ${evidence}`;
|
|
4627
|
+
}
|
|
4628
|
+
|
|
4547
4629
|
async function runSelfImplementInner(
|
|
4548
4630
|
opts: SelfImplementOptions,
|
|
4549
4631
|
identity?: { runId: string; source: ReturnType<typeof resolveRunIdentity>['source'] },
|
|
@@ -5030,6 +5112,8 @@ async function runSelfImplementInner(
|
|
|
5030
5112
|
} | undefined;
|
|
5031
5113
|
let gate!: SelfImplementGateResult;
|
|
5032
5114
|
let review: SelfImplementReview | undefined;
|
|
5115
|
+
/** Review-budget leftovers and the body that carried them, so a later merge drafts from the same text. */
|
|
5116
|
+
let preparedReviewBudgetPrBody: string | undefined;
|
|
5033
5117
|
const reviewMustFixCountHistory: number[] = [];
|
|
5034
5118
|
const reviewMustFixTrend = (): string => {
|
|
5035
5119
|
if (reviewMustFixCountHistory.length < 2) return '아직 못 잰다';
|
|
@@ -5283,6 +5367,7 @@ async function runSelfImplementInner(
|
|
|
5283
5367
|
? { resolvedDelivery: installDecision.delivery }
|
|
5284
5368
|
: {}),
|
|
5285
5369
|
...(s.escalateGoalClarifications ? { dispatch: s.escalateGoalClarifications } : {}),
|
|
5370
|
+
...(!stdinIsInteractive && !clarificationResolverLease.installed ? { selfAnswerPath: 'ask' as const, selfResolveClarification: s.selfResolveClarification } : {}),
|
|
5286
5371
|
fallback: (message) => process.stdout.write(`${message}\n`),
|
|
5287
5372
|
});
|
|
5288
5373
|
} finally {
|
|
@@ -5365,6 +5450,23 @@ async function runSelfImplementInner(
|
|
|
5365
5450
|
observe('pr.comment.seam-missing', { number: pr.number, buffered: prCommentBuffer.length }, { level: 'warn' });
|
|
5366
5451
|
return;
|
|
5367
5452
|
}
|
|
5453
|
+
const marker = '<!-- elanous:run-status -->';
|
|
5454
|
+
if (s.findPrComment && s.editPrComment) {
|
|
5455
|
+
try {
|
|
5456
|
+
const existing = await s.findPrComment({ number: pr.number, marker, cwd: wt.path });
|
|
5457
|
+
const history = prCommentBuffer.join('\n\n');
|
|
5458
|
+
const body = existing
|
|
5459
|
+
? existing.body.includes('</details>')
|
|
5460
|
+
? existing.body.replace(/<\/details>(?![\s\S]*<\/details>)/, `\n\n${history}\n</details>`)
|
|
5461
|
+
: `${existing.body}\n\n<details>\n<summary>Round history</summary>\n\n${history}\n</details>`
|
|
5462
|
+
: `${marker}\n<details>\n<summary>Round history</summary>\n\n${history}\n</details>`;
|
|
5463
|
+
if (existing) await s.editPrComment({ id: existing.id, body, cwd: wt.path });
|
|
5464
|
+
else await s.postPrComment({ number: pr.number, body, cwd: wt.path });
|
|
5465
|
+
} catch (error) {
|
|
5466
|
+
// Unknown lookup/edit outcome is not proof of absence: never create a second status comment.
|
|
5467
|
+
observe('pr.comment.status-failed', { number: pr.number, error: error instanceof Error ? error.message : String(error) }, { level: 'warn' });
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5368
5470
|
for (const body of prCommentBuffer) {
|
|
5369
5471
|
try {
|
|
5370
5472
|
await s.postPrComment({ number: pr.number, body, cwd: wt.path });
|
|
@@ -5798,6 +5900,7 @@ async function runSelfImplementInner(
|
|
|
5798
5900
|
'self-implement-review-budget-pr',
|
|
5799
5901
|
followUpMustFix,
|
|
5800
5902
|
);
|
|
5903
|
+
preparedReviewBudgetPrBody = preparedPrBody.body;
|
|
5801
5904
|
const pr = await withStepTimeout(s.openPr({
|
|
5802
5905
|
title: prTitle(opts.feature),
|
|
5803
5906
|
body: preparedPrBody.body,
|
|
@@ -6076,6 +6179,20 @@ async function runSelfImplementInner(
|
|
|
6076
6179
|
if (relaxation) pendingPlanRevision = { ...pendingPlanRevision!, relaxation };
|
|
6077
6180
|
if (opts.goalFile) {
|
|
6078
6181
|
try {
|
|
6182
|
+
if (!(s.stdinIsInteractive ?? (() => Boolean(process.stdin.isTTY && process.stdout.isTTY)))()) {
|
|
6183
|
+
try {
|
|
6184
|
+
const source = readFileSync(opts.goalFile, 'utf8');
|
|
6185
|
+
// Always run the shared self-answer so every rework pass leaves one observation, even with nothing pending.
|
|
6186
|
+
const resolved = await selfAnswerGoalDocumentClarifications(source, {
|
|
6187
|
+
path: 'rework', ask: source, resolver: s.selfResolveClarification,
|
|
6188
|
+
});
|
|
6189
|
+
if (resolved.newlyAnswered > 0) writeGoalDocumentAtomic(opts.goalFile, resolved.document);
|
|
6190
|
+
} catch (error) {
|
|
6191
|
+
debug.log('self-implement.clarification-escalation', 'self-answer-failed', {
|
|
6192
|
+
path: 'rework', reason: error instanceof Error ? error.message : String(error),
|
|
6193
|
+
});
|
|
6194
|
+
}
|
|
6195
|
+
}
|
|
6079
6196
|
const recorded: SupervisorPlanRevisionResult = recordSupervisorPlanRevision(opts.goalFile, { reason: parsedDecision.reason, round, ...(relaxation ? { relaxation } : {}) });
|
|
6080
6197
|
pendingPlanRevision = { ...pendingPlanRevision!, ...(relaxation ? { relaxation } : {}), application: {
|
|
6081
6198
|
status: recorded.status === 'applied' || recorded.status === 'already-applied' ? recorded.status : 'failed',
|
|
@@ -6211,6 +6328,7 @@ async function runSelfImplementInner(
|
|
|
6211
6328
|
if (budgetDecision.stop) {
|
|
6212
6329
|
if (budgetDecision.exit === 'proceed') {
|
|
6213
6330
|
finalizeSupervisorDeliveries('supervisor-rework-proceed');
|
|
6331
|
+
if (round > effectiveMax) reworkCapProceed = true;
|
|
6214
6332
|
break;
|
|
6215
6333
|
}
|
|
6216
6334
|
finalizeSupervisorDeliveries('supervisor-rework-blocked');
|
|
@@ -6271,12 +6389,25 @@ async function runSelfImplementInner(
|
|
|
6271
6389
|
if (isReviewRework(reworkKind)) {
|
|
6272
6390
|
if (reflectRejectedAudit.length && review) review.shouldFix = [...review.shouldFix, ...reflectRejectedAudit];
|
|
6273
6391
|
const reason = `review must-fix unresolved after ${round - 1} rework round(s)`;
|
|
6392
|
+
// Pod keeps review-blocked work as a labelled draft (or aborts with the preserved branch) instead of
|
|
6393
|
+
// accepting the review budget into a non-draft PR — restored after #24341 (0.2.16 gate shard 5).
|
|
6274
6394
|
if (!podReviewBlockedDraft) {
|
|
6275
6395
|
const acceptance = await acceptReviewBudget(
|
|
6276
6396
|
review?.mustFix ?? [],
|
|
6277
6397
|
reworkParts.some((part) => part.source === 'supervisor'),
|
|
6278
6398
|
);
|
|
6279
6399
|
if (acceptance.decision.action === 'accept') {
|
|
6400
|
+
if (acceptance.pr) {
|
|
6401
|
+
await draftFollowUpAfterMerge({
|
|
6402
|
+
prNumber: acceptance.pr.number,
|
|
6403
|
+
followUpMustFix: acceptance.decision.unresolvedMustFix,
|
|
6404
|
+
shouldFix: review?.shouldFix,
|
|
6405
|
+
prBody: preparedReviewBudgetPrBody,
|
|
6406
|
+
feature: opts.feature,
|
|
6407
|
+
...(opts.goalId ? { cellId: opts.goalId } : {}),
|
|
6408
|
+
...(opts.goalFile ? { goalFile: opts.goalFile } : {}),
|
|
6409
|
+
}, s.followUp);
|
|
6410
|
+
}
|
|
6280
6411
|
return {
|
|
6281
6412
|
ok: true,
|
|
6282
6413
|
stage: 'pr-opened',
|
|
@@ -7523,11 +7654,15 @@ async function runSelfImplementInner(
|
|
|
7523
7654
|
let releasePathInspectionError: string | undefined;
|
|
7524
7655
|
if (s.readPrFiles) {
|
|
7525
7656
|
try {
|
|
7526
|
-
|
|
7657
|
+
// Rate-limit recovery may wait up to 15 minutes for X-RateLimit-Reset; the PR step cap must not cut that wait.
|
|
7658
|
+
const files = await s.readPrFiles({ number: pr.number, cwd: wt.path, ...(opts.base ? { base: opts.base } : {}) });
|
|
7527
7659
|
if (!Array.isArray(files) || !files.every((path) => typeof path === 'string')) throw new Error('invalid PR file list');
|
|
7528
7660
|
releaseHoldPath = releasePathHold(files);
|
|
7529
7661
|
} catch (error) {
|
|
7530
|
-
|
|
7662
|
+
const rateLimited = error instanceof Error && (error as { rateLimited?: boolean }).rateLimited === true;
|
|
7663
|
+
releasePathInspectionError = rateLimited
|
|
7664
|
+
? `rate-limit: ${safeErrorDescription(error)}`
|
|
7665
|
+
: safeErrorDescription(error);
|
|
7531
7666
|
}
|
|
7532
7667
|
} else if (canAuto || labels?.includes(AUTO_REVIEW_LABEL)) {
|
|
7533
7668
|
releasePathInspectionError = 'readPrFiles seam unavailable';
|
|
@@ -7566,7 +7701,9 @@ async function runSelfImplementInner(
|
|
|
7566
7701
|
if (releasePathInspectionError && (canAuto || labels?.includes(AUTO_REVIEW_LABEL))) {
|
|
7567
7702
|
observe('release-path-inspection-failed', { number: pr.number, error: releasePathInspectionError }, { level: 'error' });
|
|
7568
7703
|
canAuto = false;
|
|
7569
|
-
mergeSkipReason = mergeReason = '
|
|
7704
|
+
mergeSkipReason = mergeReason = releasePathInspectionError.startsWith('rate-limit:')
|
|
7705
|
+
? 'release-path-inspection-failed: rate-limit'
|
|
7706
|
+
: 'release-path-inspection-failed';
|
|
7570
7707
|
recordMergeDecision();
|
|
7571
7708
|
progress('pr-opened', `OP 승인 대기 (#${pr.number}): PR 경로 조회 실패`);
|
|
7572
7709
|
return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason, prUrl: pr.url, prNumber: pr.number };
|
|
@@ -7700,6 +7837,18 @@ async function runSelfImplementInner(
|
|
|
7700
7837
|
prNumber: pr.number,
|
|
7701
7838
|
openedAt: openedAtFromLedger(mergedLedger, new Date().toISOString()),
|
|
7702
7839
|
}, observe);
|
|
7840
|
+
// FOLLOWUP-LOOP① — leftover findings become one follow-up draft. Failure is a log line only;
|
|
7841
|
+
// this return (stage, merged, exit via ok) stays the merge result.
|
|
7842
|
+
await draftFollowUpAfterMerge({
|
|
7843
|
+
prNumber: pr.number,
|
|
7844
|
+
// 정상 병합은 리뷰를 통과한 뒤라 미해결 must-fix 가 없다 — 남은 것은 should-fix ⊕ PR 본문 «남은 조각».
|
|
7845
|
+
// 미해결 must-fix 는 리뷰 예산 수락 경로가 직접 넘긴다(위 acceptance 분기).
|
|
7846
|
+
shouldFix: review?.shouldFix,
|
|
7847
|
+
prBody: [preparedReviewBudgetPrBody, preparedPrBody.body].filter(Boolean).join('\n'),
|
|
7848
|
+
feature: opts.feature,
|
|
7849
|
+
...(opts.goalId ? { cellId: opts.goalId } : {}),
|
|
7850
|
+
...(opts.goalFile ? { goalFile: opts.goalFile } : {}),
|
|
7851
|
+
}, s.followUp);
|
|
7703
7852
|
return { ok: true, stage: 'merged', node: 'merge', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), merged: true, ...(mergedBase ? { mergedBase } : {}), prUrl: pr.url, prNumber: pr.number };
|
|
7704
7853
|
}
|
|
7705
7854
|
progress('pr-opened', `병합 실패 — PR 개설됨(#${pr.number}·수동 병합). ${m.detail ?? ''}`);
|
|
@@ -94,6 +94,10 @@ export interface RunOriginData extends Record<string, unknown> {
|
|
|
94
94
|
substrate: string;
|
|
95
95
|
instance: string;
|
|
96
96
|
elanousVersion: string;
|
|
97
|
+
/** install.json version. 못 읽으면 «unknown». */
|
|
98
|
+
installedVersion: string;
|
|
99
|
+
/** install.json commit. 못 읽으면 «unknown». */
|
|
100
|
+
installedCommit: string;
|
|
97
101
|
podName?: string;
|
|
98
102
|
nodeName?: string;
|
|
99
103
|
podNamespace?: string;
|
|
@@ -24,6 +24,9 @@ import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
|
24
24
|
import { promisify } from 'node:util';
|
|
25
25
|
import { forkSessionById, HARNESS_SESSION_ORIGIN, isHarnessSessionOrigin } from '../session/index.js';
|
|
26
26
|
import { configuredWorktreeRoot, getUserConfig, resolveRoleLlm } from '../user-config.js';
|
|
27
|
+
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
28
|
+
import { addHarnessQueue } from '../harness/harness-queue.js';
|
|
29
|
+
import { readFollowUpMode } from './follow-up-goals.js';
|
|
27
30
|
import { getProviderForConfig } from '../llm.js';
|
|
28
31
|
import { createWorktree, DEFAULT_BRANCH_WORKTREE_BASE, linkWorktreeDependencies, removeWorktree, resolveDefaultBranchBase, resolveMainRepoRoot } from '../git-fs/worktree.js';
|
|
29
32
|
import { isWorktreeInUse, listActiveTerminalDirectories } from '../harness/harness-clean.js';
|
|
@@ -77,7 +80,8 @@ import { tscEnv, assessTypecheckExecution, classifyTypecheckErrors, diffTypechec
|
|
|
77
80
|
import { nestCapReached, childNestEnv, nestInfo } from '../agent/nest-depth.js';
|
|
78
81
|
import { harnessBoundaryEnv, harnessBoundaryRequestsEnv, harnessBoundaryResponsesEnv, harnessSpaceEnv, getHarnessSpace, normalizeSpaceId, getHarnessRunId, resolveRunIdentity } from '../harness/harness-space.js';
|
|
79
82
|
import { debug } from '../debug/log.js';
|
|
80
|
-
import { RELEASE_PATH_HOLD_MARKER, RELEASE_PATH_LABEL, releasePathHoldShouldPost, releasePathHoldCommentsArgs
|
|
83
|
+
import { RELEASE_PATH_HOLD_MARKER, RELEASE_PATH_LABEL, releasePathHoldShouldPost, releasePathHoldCommentsArgs } from '../self-dev/release-path-guard.js';
|
|
84
|
+
import { readPrFilesWithRateLimitFallback, runGhApp } from './app-api-budget.js';
|
|
81
85
|
import { emitDecision } from '../live/detail-switch.js';
|
|
82
86
|
import { gateDecision, reviewDecision, mergeDecision } from './decision-events.js';
|
|
83
87
|
import { LogStore } from '../mss/logging/log-store.js';
|
|
@@ -110,6 +114,10 @@ declare module './orchestrator.js' {
|
|
|
110
114
|
/** PR close(+브랜치 삭제). 기본 구성은 PrManager.closePr 위임(같은 인자·같은 반환).
|
|
111
115
|
* 오케스트레이터는 아직 부르지 않는다 — 그 배선은 조각 ②다. */
|
|
112
116
|
closePr?: (prUrl: string, comment?: string) => boolean;
|
|
117
|
+
/** Find an issue comment on this PR by its body marker; absence is distinct from a failed lookup. */
|
|
118
|
+
findPrComment?: (opts: { number: number; marker: string; cwd: string; comments?: unknown }) => Promise<{ id: number; body: string } | undefined>;
|
|
119
|
+
/** Replace an existing PR issue comment by ID, using the same GitHub App identity as postPrComment. */
|
|
120
|
+
editPrComment?: (opts: { id: number; body: string; cwd: string }) => Promise<void>;
|
|
113
121
|
}
|
|
114
122
|
}
|
|
115
123
|
import { harnessPolicyEnv } from './harness-policy.js';
|
|
@@ -1415,6 +1423,11 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
1415
1423
|
const { supersedeMergedGoalDrafts } = await import('../harness/harness-cli-command.js');
|
|
1416
1424
|
await supersedeMergedGoalDrafts(number, cwd);
|
|
1417
1425
|
},
|
|
1426
|
+
followUp: {
|
|
1427
|
+
mode: readFollowUpMode(),
|
|
1428
|
+
stateRoot: elanousStateRoot(),
|
|
1429
|
+
enqueue: (item) => addHarnessQueue({ seat: item.seat, say: item.say, idempotencyKey: item.idempotencyKey }),
|
|
1430
|
+
},
|
|
1418
1431
|
lineageSupersede: {
|
|
1419
1432
|
listOpenDrafts: () => listOpenDraftsForLineage(),
|
|
1420
1433
|
readRunLedger: (runId) => {
|
|
@@ -2394,46 +2407,62 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
2394
2407
|
postPrComment: async ({ number, body, cwd }) => {
|
|
2395
2408
|
// Round comments go out as the GitHub App like the PR itself — a personal `gh` login made the owner a
|
|
2396
2409
|
// participant and mailed them on merge (#22089).
|
|
2397
|
-
|
|
2410
|
+
if (body.startsWith(RELEASE_PATH_HOLD_MARKER)) {
|
|
2398
2411
|
// Several merge surfaces may hold the same PR; post the release-path hold comment once.
|
|
2399
2412
|
let history: unknown;
|
|
2400
2413
|
try {
|
|
2401
|
-
history = JSON.parse((await
|
|
2414
|
+
history = JSON.parse((await runGhApp('post-pr-comment-history', releasePathHoldCommentsArgs(number), {
|
|
2402
2415
|
cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, maxBuffer: 64 * 1024 * 1024, env: ghAutomationEnv(process.env),
|
|
2403
2416
|
})).stdout);
|
|
2404
2417
|
} catch { history = undefined; }
|
|
2405
2418
|
if (!releasePathHoldShouldPost(() => history)) return;
|
|
2406
2419
|
}
|
|
2407
|
-
await
|
|
2420
|
+
await runGhApp('post-pr-comment', ['pr', 'comment', String(number), '--body', body], {
|
|
2421
|
+
cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, maxBuffer: 64 * 1024 * 1024, env: ghAutomationEnv(process.env),
|
|
2422
|
+
});
|
|
2423
|
+
},
|
|
2424
|
+
|
|
2425
|
+
findPrComment: async ({ number, marker, cwd, comments }) => {
|
|
2426
|
+
const pages: unknown = comments ?? JSON.parse((await runGhApp('find-pr-comment', releasePathHoldCommentsArgs(number), {
|
|
2427
|
+
cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, maxBuffer: 64 * 1024 * 1024, env: ghAutomationEnv(process.env),
|
|
2428
|
+
})).stdout);
|
|
2429
|
+
if (!Array.isArray(pages)) throw new Error('gh api PR comments returned an invalid list');
|
|
2430
|
+
const entries = pages.flatMap((page: unknown) => Array.isArray(page) ? page : [page]);
|
|
2431
|
+
for (const entry of entries) {
|
|
2432
|
+
if (!entry || typeof entry !== 'object' || !('body' in entry) || typeof entry.body !== 'string'
|
|
2433
|
+
|| !('id' in entry) || typeof entry.id !== 'number' || !Number.isSafeInteger(entry.id) || entry.id <= 0) {
|
|
2434
|
+
throw new Error('gh api PR comments returned an invalid comment');
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
return entries.find((entry: { id: number; body: string }) => entry.body.includes(marker));
|
|
2438
|
+
},
|
|
2439
|
+
|
|
2440
|
+
editPrComment: async ({ id, body, cwd }) => {
|
|
2441
|
+
await runGhApp('edit-pr-comment', ['api', '--method', 'PATCH', `repos/{owner}/{repo}/issues/comments/${id}`, '-f', `body=${body}`], {
|
|
2408
2442
|
cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, maxBuffer: 64 * 1024 * 1024, env: ghAutomationEnv(process.env),
|
|
2409
2443
|
});
|
|
2410
2444
|
},
|
|
2411
2445
|
|
|
2412
2446
|
addPrLabel: async ({ number, label, cwd }) => {
|
|
2413
2447
|
if (label === RELEASE_PATH_LABEL) {
|
|
2414
|
-
const existing = await
|
|
2448
|
+
const existing = await runGhApp('add-pr-label-list', ['label', 'list', '--search', label, '--json', 'name'], {
|
|
2415
2449
|
cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, env: ghAutomationEnv(process.env),
|
|
2416
2450
|
});
|
|
2417
2451
|
const names: unknown = JSON.parse(existing.stdout);
|
|
2418
2452
|
if (!Array.isArray(names)) throw new Error('gh label list returned an invalid list');
|
|
2419
2453
|
if (!names.some((item: unknown) => !!item && typeof item === 'object' && 'name' in item && item.name === label)) {
|
|
2420
|
-
await
|
|
2454
|
+
await runGhApp('add-pr-label-create', ['label', 'create', label, '--color', 'D93F0B', '--description', 'Release path requires OP approval'], {
|
|
2421
2455
|
cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, env: ghAutomationEnv(process.env),
|
|
2422
2456
|
});
|
|
2423
2457
|
}
|
|
2424
2458
|
}
|
|
2425
|
-
await
|
|
2459
|
+
await runGhApp('add-pr-label', ['pr', 'edit', String(number), '--add-label', label], {
|
|
2426
2460
|
cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, env: ghAutomationEnv(process.env),
|
|
2427
2461
|
});
|
|
2428
2462
|
},
|
|
2429
|
-
readPrFiles: async ({ number, cwd }) => {
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
'api', '--paginate', '--slurp', '--method', 'GET', '-f', 'per_page=100',
|
|
2433
|
-
`repos/{owner}/{repo}/pulls/${number}/files`,
|
|
2434
|
-
], { cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, maxBuffer: 64 * 1024 * 1024, env: ghAutomationEnv(process.env) });
|
|
2435
|
-
return releasePrFilePaths(JSON.parse(stdout));
|
|
2436
|
-
},
|
|
2463
|
+
readPrFiles: async ({ number, cwd, base }) => readPrFilesWithRateLimitFallback({
|
|
2464
|
+
number, cwd, env: ghAutomationEnv(process.env), ...(base ? { base } : {}),
|
|
2465
|
+
}),
|
|
2437
2466
|
|
|
2438
2467
|
...(o.approvePr ? { approvePr: o.approvePr } : {}),
|
|
2439
2468
|
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
decideRetry,
|
|
26
26
|
DoomLoopTracker,
|
|
27
27
|
fingerprintError,
|
|
28
|
+
llmCallOutcomeKind,
|
|
29
|
+
type LlmCallOutcomeKind,
|
|
28
30
|
type RetryAction,
|
|
29
31
|
} from './retry-policy.js';
|
|
30
32
|
|
|
@@ -75,6 +77,35 @@ export interface FetchApiWithRetryOpts {
|
|
|
75
77
|
* row" pattern within a single request. Pass a session-scoped
|
|
76
78
|
* tracker if you want cross-request doom detection. */
|
|
77
79
|
doomTracker?: DoomLoopTracker;
|
|
80
|
+
/** Fallbacks still unused. When > 0, k consecutive overload-class
|
|
81
|
+
* failures throw OverloadFailoverError before another retry sleep. */
|
|
82
|
+
remainingFallbacks?: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Raised when overload repeats k times and a fallback is still unused.
|
|
86
|
+
* The caller switches provider; this is not a retry exhaustion. */
|
|
87
|
+
export class OverloadFailoverError extends Error {
|
|
88
|
+
readonly provider: string;
|
|
89
|
+
readonly status: number;
|
|
90
|
+
readonly kind: LlmCallOutcomeKind;
|
|
91
|
+
readonly streak: number;
|
|
92
|
+
readonly cause: unknown;
|
|
93
|
+
|
|
94
|
+
constructor(opts: {
|
|
95
|
+
provider: string;
|
|
96
|
+
status: number;
|
|
97
|
+
kind: LlmCallOutcomeKind;
|
|
98
|
+
streak: number;
|
|
99
|
+
cause: unknown;
|
|
100
|
+
}) {
|
|
101
|
+
super(`overload failover after ${opts.streak} ${opts.kind} from ${opts.provider} (status ${opts.status})`);
|
|
102
|
+
this.name = 'OverloadFailoverError';
|
|
103
|
+
this.provider = opts.provider;
|
|
104
|
+
this.status = opts.status;
|
|
105
|
+
this.kind = opts.kind;
|
|
106
|
+
this.streak = opts.streak;
|
|
107
|
+
this.cause = opts.cause;
|
|
108
|
+
}
|
|
78
109
|
}
|
|
79
110
|
|
|
80
111
|
/** POST a fetch with the standard retry policy. On 2xx returns the
|
|
@@ -91,13 +122,17 @@ export async function fetchApiWithRetry(
|
|
|
91
122
|
): Promise<Response> {
|
|
92
123
|
const maxAttempts = opts.maxAttempts ?? 4;
|
|
93
124
|
const tracker = opts.doomTracker ?? new DoomLoopTracker();
|
|
125
|
+
const remainingFallbacks = opts.remainingFallbacks ?? 0;
|
|
94
126
|
let attempt = 0;
|
|
127
|
+
let overloadStreak = 0;
|
|
95
128
|
let lastErr: unknown;
|
|
96
129
|
while (attempt < maxAttempts) {
|
|
97
130
|
let response: Response | null = null;
|
|
98
131
|
try {
|
|
99
132
|
response = await fetch(url, init);
|
|
100
133
|
if (response.ok) {
|
|
134
|
+
recordLlmCallOutcome(opts.provider, response.status, 'ok');
|
|
135
|
+
overloadStreak = 0;
|
|
101
136
|
return response;
|
|
102
137
|
}
|
|
103
138
|
// Drain body so the error can carry the API's explanation. We
|
|
@@ -136,6 +171,11 @@ export async function fetchApiWithRetry(
|
|
|
136
171
|
// would couple the helper to the caller's signal.
|
|
137
172
|
if (isAbortLike(err)) throw err;
|
|
138
173
|
|
|
174
|
+
const status = err instanceof ApiHttpError ? err.status : 0;
|
|
175
|
+
const kind = llmCallOutcomeKind(status, err instanceof Error ? err.message : String(err));
|
|
176
|
+
recordLlmCallOutcome(opts.provider, status, kind);
|
|
177
|
+
if (kind === 'overloaded' || kind === '5xx' || kind === 'rate-limit') overloadStreak += 1;
|
|
178
|
+
else overloadStreak = 0;
|
|
139
179
|
const fp = fingerprintError(err, `api:${opts.provider}`);
|
|
140
180
|
const doomStatus = tracker.record(fp);
|
|
141
181
|
const retryAfter = err instanceof ApiHttpError ? err.retryAfter : undefined;
|
|
@@ -143,6 +183,8 @@ export async function fetchApiWithRetry(
|
|
|
143
183
|
attempt,
|
|
144
184
|
doomStatus,
|
|
145
185
|
retryAfter,
|
|
186
|
+
remainingFallbacks,
|
|
187
|
+
overloadStreak,
|
|
146
188
|
});
|
|
147
189
|
debug.log('llm.retry', decisionEvent(decision.action), {
|
|
148
190
|
provider: opts.provider,
|
|
@@ -151,7 +193,19 @@ export async function fetchApiWithRetry(
|
|
|
151
193
|
reason: decision.reason,
|
|
152
194
|
delayMs: decision.delayMs,
|
|
153
195
|
status: err instanceof ApiHttpError ? err.status : undefined,
|
|
196
|
+
kind,
|
|
197
|
+
overloadStreak,
|
|
198
|
+
remainingFallbacks,
|
|
154
199
|
});
|
|
200
|
+
if (decision.action === 'switch-provider') {
|
|
201
|
+
throw new OverloadFailoverError({
|
|
202
|
+
provider: opts.provider,
|
|
203
|
+
status,
|
|
204
|
+
kind,
|
|
205
|
+
streak: overloadStreak,
|
|
206
|
+
cause: err,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
155
209
|
if (decision.action !== 'retry') {
|
|
156
210
|
throw err;
|
|
157
211
|
}
|
|
@@ -172,6 +226,39 @@ export async function fetchApiWithRetry(
|
|
|
172
226
|
throw lastErr ?? new Error(`${opts.errorPrefix}: retry loop terminated unexpectedly`);
|
|
173
227
|
}
|
|
174
228
|
|
|
229
|
+
type OutcomeWriter = (row: { provider: string; status: number; kind: LlmCallOutcomeKind }) => void;
|
|
230
|
+
|
|
231
|
+
let outcomeWriterForTesting: OutcomeWriter | undefined;
|
|
232
|
+
|
|
233
|
+
/** Test seam. Production uses the log store. `undefined` restores it. */
|
|
234
|
+
export function setLlmCallOutcomeWriterForTesting(writer: OutcomeWriter | undefined): void {
|
|
235
|
+
outcomeWriterForTesting = writer;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** The one event launch failover is allowed to read.
|
|
239
|
+
* Category `llm.call`, event `outcome`, payload {provider,status,kind}.
|
|
240
|
+
* Written straight to the log store — `debug.log` drops the row when no
|
|
241
|
+
* sink is armed, which is the normal launch path. */
|
|
242
|
+
export function recordLlmCallOutcome(provider: string, status: number, kind: LlmCallOutcomeKind): void {
|
|
243
|
+
const data = { provider, status, kind };
|
|
244
|
+
// Trace only — a store sink would otherwise write a second `llm.call`/`outcome` row and halve the streak threshold.
|
|
245
|
+
debug.log('llm.call', 'outcome-trace', data);
|
|
246
|
+
if (outcomeWriterForTesting) {
|
|
247
|
+
outcomeWriterForTesting(data);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (process.env.NODE_ENV === 'test') return;
|
|
251
|
+
try {
|
|
252
|
+
const { getDefaultLogStore } = require('../mss/logging/log-store.js') as typeof import('../mss/logging/log-store.js');
|
|
253
|
+
getDefaultLogStore()?.insertBatch([{
|
|
254
|
+
rec: { ts: new Date().toISOString(), category: 'llm.call', event: 'outcome', data },
|
|
255
|
+
surface: 'llm',
|
|
256
|
+
}]);
|
|
257
|
+
} catch {
|
|
258
|
+
// A failed observation must not fail the call. Launch then sees no sample.
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
175
262
|
function decisionEvent(action: RetryAction): string {
|
|
176
263
|
// Stable category names for log filtering. The action is the most
|
|
177
264
|
// useful axis ("how many turns hit retry vs abort"); the category
|