elanous 0.2.19 → 0.2.20
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/{J5H7BvqgSI8DUjtRRcA_S → Bam1YziK2iH9bN4K0utGk}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 1268.0840ff91e083fcbd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-77fad80cf54477c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +5 -0
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2977-e38442e86289f1d3.js → 211-9c9090c59e2c5907.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2116-6429c188eb846ab8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
- package/apps/pwa/out/_next/static/chunks/224-e7be8f762fe74986.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5755-ea7ed88eb956f47b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-94ed6f78e9814b2b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4847.56f5bc454fbcc827.js → 6110.1297d4093fdfbb0d.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/6158.af2e93b7adff709b.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1470.98a6ab61ccb4131c.js → 726.83532d69452ee24b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 7327-2d69fb321ba73389.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{8870-9614a213b990b10b.js → 7748-6e51bfe927019f5c.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7647.2fcd30d894d5ee30.js → 935.4ddaa99fc1d1126c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{ae787e4c-e5b1ef00b2dbceca.js → a9f2b54d-33c0689b70ed8cf3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-c855c24de7db74fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-85b316bb4235980b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-9b2b26a33cb2cd42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-c9955fde67b1bcb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-7a852956c74015a3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-4ba4abe096b6c2a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-575d4a8b4f7becb2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f5d56ff5f45ad164.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-2260e9eef0357421.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-7c058069c5e7fa63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-466bf0ee16ad1432.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-a23bc71713bcc08f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/{page-5b29d9b06ceadade.js → page-ed4a6f799f005d09.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-9cd565005b93aca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-474cb5888ea0e8f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-35bfec2e631d3eae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-9ca21f9ad3208a61.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-f9ccb1b1adbea14d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-ec102c9b8b691e37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-a9c2cfa8dda922a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6da35144878feb6d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-f6055495e9ed7ea8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-25204694fa9984a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a60e035f151d84d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-189c587f36874538.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-c56634bde705a65d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-db03ae3589a81c9e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4a6463b97f0ad94f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-75bffecc9aa1004e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a8d2fe0c669e0de4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-31d5b875760bb62d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c26fa10aac6c21c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-68793c6e10331d66.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-46d5201d2207a511.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-d81c07a7b4f618ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-be1c4e8697f22360.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-cf0a4b4abead71ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-a4717e2c4bf7a28f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-0fd895fab9b62c1b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8b6c1454a8e8f306.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c6e4364d9de8f717.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-0acc8e198f7b0dfb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-34882c84f84021c5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → f6231284-45726550aba08fca.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2ea96958.e8c7e112de50b067.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-c260571513bf43c2.js +1 -0
- package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/demo/editor-recipes.yaml +22 -0
- package/graphs/retro/daily.yaml +25 -0
- package/graphs/retro/recipes.yaml +24 -0
- package/package.json +1 -1
- package/scripts/ask-marker-check.ts +3 -1
- package/scripts/land-global-checks.ts +89 -0
- package/scripts/lib/pch-regress-map.ts +2 -1
- package/scripts/release-loop/gate-node.ts +296 -25
- package/scripts/release-loop/gate-timing.ts +116 -7
- package/scripts/release-loop/preflight.ts +6 -4
- package/scripts/release-loop/publish-window.ts +141 -0
- package/scripts/release-loop/release-docs.ts +1 -4
- package/scripts/release-loop/release-universe.ts +38 -0
- package/scripts/release-loop/resume-release.ts +4 -1
- package/scripts/release-loop/unattended-release.ts +10 -2
- package/scripts/release-story/draft.ts +5 -1
- package/scripts/retro/shadow-compare.ts +71 -0
- package/scripts/rhythm/daily.ts +6 -1
- package/src/agent-mission/driver.ts +34 -6
- package/src/autopilot/build/llm-conflict-merge.ts +132 -7
- package/src/autopilot/build/merge-intent.ts +135 -7
- package/src/away/release-watch.ts +20 -3
- package/src/cli/config-drift.ts +513 -0
- package/src/cli/log-event-names.ts +4 -0
- package/src/cli/pr-cli.ts +20 -0
- package/src/cli/release-cli.ts +20 -5
- package/src/cli/retro-cli.ts +78 -0
- package/src/cli/tasks-cli.ts +94 -8
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -4
- package/src/dashboard/slash-runtime/help-from-registry.ts +57 -31
- package/src/domains/outbound-log-probe.ts +6 -1
- package/src/graph-runner/graph-run-api.ts +205 -0
- package/src/graph-runner/runner.ts +11 -1
- package/src/harness/harness-cli-command.ts +419 -52
- package/src/harness/harness-queue.ts +315 -25
- package/src/harness/harness-salvage-cli.ts +62 -0
- package/src/hq/fence-outcomes.ts +63 -0
- package/src/hq/fence-wrapper.ts +33 -0
- package/src/hq/hq.ts +11 -1
- package/src/index.ts +69 -6
- package/src/maturity/feature-maturity.ts +2 -2
- package/src/nexus/api/graph-kinds.ts +10 -4
- package/src/nexus/api/graphs-api.ts +178 -6
- package/src/nexus/api/http-server.ts +38 -3
- package/src/nexus/api/loop-edges.ts +4 -2
- package/src/nexus/api/ops-api.ts +22 -7
- package/src/nexus/api/ops-board-route.ts +48 -0
- package/src/onboarding/entry-hints.ts +5 -0
- package/src/onboarding.ts +24 -6
- package/src/release-loop/feature-store.ts +14 -11
- package/src/release-loop/gate-shards.ts +158 -0
- package/src/release-loop/release-schedule.ts +6 -2
- package/src/retro/daily.ts +372 -0
- package/src/seat-loop/op-decide.ts +173 -0
- package/src/seat-loop/seat-loop.ts +60 -3
- package/src/seat-loop/seat-report.ts +58 -3
- package/src/self-dev/ask-launch-flow.ts +5 -4
- package/src/self-dev/ask-launch-io.ts +49 -1
- package/src/self-dev/ask-path-candidates.ts +40 -2
- package/src/self-dev/draft-sweep.ts +371 -16
- package/src/self-dev/draft-triage-rules.ts +19 -4
- package/src/self-dev/pr-preview.ts +84 -16
- package/src/self-implement/blocked-draft-policy.ts +101 -0
- package/src/self-implement/execution-clarification.ts +11 -0
- package/src/self-implement/gate-env-retry.ts +83 -0
- package/src/self-implement/orchestrator.ts +218 -5
- package/src/self-implement/run-stop.ts +123 -0
- package/src/self-implement/run-ttl.ts +258 -0
- package/src/self-implement/salvage-retention.ts +123 -0
- package/src/self-implement/seams.ts +4 -0
- package/src/self-implement/self-implement-cli.ts +2 -0
- package/src/self-implement/sibling-resync.ts +223 -0
- package/src/self-implement/stop-autoheal.ts +204 -0
- package/src/task-agent/board.ts +1 -1
- package/src/task-agent/card-evidence.ts +99 -10
- package/src/task-agent/judge.ts +7 -0
- package/src/task-agent/live-moves.ts +120 -20
- package/src/task-agent/mission.ts +1 -1
- package/src/task-agent/shadow.ts +10 -1
- package/src/task-agent/task-hand.ts +7 -2
- package/src/task-orchestrator/surfaces/pod-command-job.ts +19 -5
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +4 -0
- package/src/task-orchestrator/surfaces/pod-install-slots.ts +89 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +93 -10
- package/src/task-orchestrator/surfaces/pod-pool.ts +147 -34
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +19 -3
- package/src/telegram-commands.ts +41 -0
- package/src/testing/real-cli-spawn.ts +106 -0
- package/src/user-config.ts +56 -3
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +0 -1
- package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-e061bb24cd2b5cde.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +0 -2
- package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +0 -1
- package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-b70cd005d9169e25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +0 -1
- package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +0 -1
- /package/apps/pwa/out/_next/static/{J5H7BvqgSI8DUjtRRcA_S → Bam1YziK2iH9bN4K0utGk}/_ssgManifest.js +0 -0
|
@@ -64,12 +64,15 @@ import { GoalRunStore, insertGoalRunRecord, type GoalPriorRuns } from './goal-ru
|
|
|
64
64
|
import { writeGoalRunRecordFragment } from './goal-execution-records.js';
|
|
65
65
|
import { getUserConfig } from '../user-config.js';
|
|
66
66
|
import { appendRunLedgerEntry, loadRunLedger, parseRunShardIdentity, queryRunChain, type RunChainShardSibling, type RunLedgerEntry, type RunLedgerWriter, type RunOriginData, type RunShardIdentity } from './run-ledger.js';
|
|
67
|
+
import { classifyMergeHoldStop, forgetRunStop, readRunStopRecord, recordRunStop, runStopRecorded } from './run-stop.js';
|
|
68
|
+
import { autohealFromStop, parseAutohealMode, type AutohealActions, type AutohealResult } from './stop-autoheal.js';
|
|
67
69
|
import { emitSessionEvent, type SessionEventInput } from '../context-bus/session-events.js';
|
|
68
70
|
import { decideLineageSupersede, lineageSupersedeCloseComment, type LineageSupersedeOpenDraft } from './lineage-supersede.js';
|
|
69
71
|
import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
70
72
|
import { homedir } from 'node:os';
|
|
71
73
|
import { enqueueControlMemo, readSoftStopRequestStatus, type SoftStopRequestRead } from '../harness/control-inbox.js';
|
|
72
74
|
import { debug } from '../debug/log.js';
|
|
75
|
+
import { classifyGateEnvDeficit, DEFAULT_GATE_ENV_RETRY, GATE_ENV_DEFICIT_HARVESTABLE_MARK, type GateEnvDeficitKind } from './gate-env-retry.js';
|
|
73
76
|
import { recordFailureEvent } from './heal-intake.js';
|
|
74
77
|
import { classifyReworkCapExhaustion, type ReworkCapHealDecision } from './heal-triage.js';
|
|
75
78
|
import { releasePathHold, releasePathHoldComment, RELEASE_PATH_LABEL } from '../self-dev/release-path-guard.js';
|
|
@@ -210,7 +213,7 @@ import { hasModuleLoadFailure, type GateTestFailure } from './gate-baseline.js';
|
|
|
210
213
|
import { commitWorktree, defaultBranchRef, preservationHasChanges } from './seams.js';
|
|
211
214
|
import { detectUncommittedWork } from './uncommitted-work.js';
|
|
212
215
|
import { spawnSync } from 'node:child_process';
|
|
213
|
-
import { blockedDraftDisposition } from './blocked-draft-policy.js';
|
|
216
|
+
import { blockedDraftDisposition, decideDraftOnStop, nextMoveFor, parseDraftOnStopMode, salvageBranchForRun, type DraftOnStopMode } from './blocked-draft-policy.js';
|
|
214
217
|
import { formatLlmMergeOutcome } from '../autopilot/build/llm-conflict-merge.js';
|
|
215
218
|
import { FOUND_CITED_PATH_REFUTATION_QUOTE, MISSING_CITED_PATH_REFUTATION_QUOTE, MUST_FIX_REFUTATION_ACKNOWLEDGEMENT_WITH_REASON, REFUTATION_QUOTE_GRAMMAR, observeMustFixCitedPaths, parseMustFixRefutationAcknowledgement, parseMustFixRefutations, renderMustFixCitedPathFacts, snapshotMustFixFindings, stableMustFixId, type ReflectEvidenceFacts, type ReflectGateFacts, type MustFixCitedPathFact, type MustFixFinding, type MustFixRecurrenceHistory, type MustFixRefutation, type MustFixRefutationKind } from './reflect-mustfix.js';
|
|
216
219
|
import type { IngestionEntry } from '../agent-substrate/execution/ingestion-policy.js';
|
|
@@ -762,7 +765,8 @@ function changedFileIsInsideDeclaredTarget(changedFile: string, declaredPath: st
|
|
|
762
765
|
const file = normalizeDeclaredScopePath(changedFile);
|
|
763
766
|
const target = normalizeDeclaredScopePath(declaredPath);
|
|
764
767
|
if (!file || !target) return false;
|
|
765
|
-
return file === target || file.startsWith(`${target}/`)
|
|
768
|
+
return file === target || file.startsWith(`${target}/`)
|
|
769
|
+
|| (target.endsWith('.ts') && !target.endsWith('.test.ts') && file === `${target.slice(0, -3)}.test.ts`);
|
|
766
770
|
}
|
|
767
771
|
|
|
768
772
|
function parseDeclaredTargetPathsFromAsk(askText: string): { readable: boolean; paths: readonly string[] } {
|
|
@@ -930,6 +934,12 @@ export type ImplementTerminalStatus = {
|
|
|
930
934
|
export interface SelfImplementSeams {
|
|
931
935
|
/** Run-observer ledger sink. Tests inject an in-memory sink to avoid touching user state. */
|
|
932
936
|
writeRunLedger?: RunLedgerWriter;
|
|
937
|
+
/** STOP-AUTOHEAL — heal action seams (live mode only runs actions given here; missing = needs-owner). */
|
|
938
|
+
stopAutohealActions?: AutohealActions;
|
|
939
|
+
/** STOP-AUTOHEAL — one-attempt guard reader. Default: real run ledger, or none when `writeRunLedger` is injected. */
|
|
940
|
+
stopAutohealLoadLedger?: (runId: string) => readonly RunLedgerEntry[] | null;
|
|
941
|
+
/** STOP-AUTOHEAL — observes each dispatcher result (tests). */
|
|
942
|
+
onStopAutoheal?: (result: AutohealResult) => void;
|
|
933
943
|
/** Optional context-journal sink for isolated runs. */
|
|
934
944
|
emitContextEvent?: (input: SessionEventInput) => void;
|
|
935
945
|
/** Registers this run as an ephemeral loop-agent asset; failures are observed but never affect execution. */
|
|
@@ -1028,6 +1038,11 @@ export interface SelfImplementSeams {
|
|
|
1028
1038
|
reviewBaselineObservationSource?: (cwd: string, base: string) => Promise<string | undefined>;
|
|
1029
1039
|
/** ④ gate — cwd 에서 bun test/build. postsync=정합 후 재게이트(merge-base 실행 범위). */
|
|
1030
1040
|
gate: (cwd: string, ctx?: { runId?: string; mode?: 'postsync'; round?: number }) => Promise<SelfImplementGateResult>;
|
|
1041
|
+
/**
|
|
1042
|
+
* GATE-ENV-RETRY — 환경 결손 gate 실패에 «게이트만» 다시 도는 정책. 생략 시 기본(2회 · 20초).
|
|
1043
|
+
* `attempts: 0` 이면 재시도하지 않는다. 시험은 `sleep` 으로 대기를 주입한다.
|
|
1044
|
+
*/
|
|
1045
|
+
gateEnvRetry?: { attempts?: number; delayMs?: number; sleep?: (ms: number) => Promise<void> };
|
|
1031
1046
|
/** Gate가 실행된 worktree HEAD가 local origin/main보다 뒤처진 커밋 수. 실패는 undefined(unknown)로 정직하게 남긴다. */
|
|
1032
1047
|
gateWorktreeBehindMain?: (cwd: string) => Promise<number | undefined>;
|
|
1033
1048
|
/** 변경 파일 목록 — 게이트 «라우터»가 「코드가 바뀌었나」를 판정하는 입력.
|
|
@@ -1068,6 +1083,8 @@ export interface SelfImplementSeams {
|
|
|
1068
1083
|
headCommit?: (cwd: string) => string | undefined;
|
|
1069
1084
|
/** `commit` pins the pushed state; without it the worktree HEAD is pushed. */
|
|
1070
1085
|
preserveBlockedBranch?: (ctx: { cwd: string; branch: string; title: string; commit?: string }) => boolean | Promise<boolean>;
|
|
1086
|
+
/** DRAFT-NOT-ARCHIVE — `tools.selfImplement.draftOnStop` 읽기 seam. 생략하면 user-config(실패·부재 = 'always' · 종전). */
|
|
1087
|
+
draftOnStopMode?: () => DraftOnStopMode;
|
|
1071
1088
|
/** Hard-cap salvage evidence: a clean worktree and commits ahead of origin/main. */
|
|
1072
1089
|
readReworkSalvageEvidence?: (cwd: string) => Promise<ReworkSalvageEvidence>;
|
|
1073
1090
|
/** Starts exactly one detached `elanous dev --file <goal> --base <branch>` follow-up. */
|
|
@@ -1092,6 +1109,8 @@ export interface SelfImplementSeams {
|
|
|
1092
1109
|
mergePr?: (opts: { number: number; cwd: string; matchHeadCommit?: string }) => Promise<{ merged: boolean; baseRefName?: string; mergeCommit?: string; detail?: string }>;
|
|
1093
1110
|
/** Only successful merges trigger this injected, fail-closed draft inventory and mutation seam. */
|
|
1094
1111
|
supersedeDraftsOnMerge?: (number: number, cwd: string) => Promise<void>;
|
|
1112
|
+
/** SIBLING-RESYNC-ON-MERGE — after a successful merge, tell open sibling PRs on the same paths to rebase + regate now. Failure is observation-only. */
|
|
1113
|
+
resyncSiblingsOnMerge?: (number: number, cwd: string) => Promise<void>;
|
|
1095
1114
|
/** Post-merge cleanup is opt-in and injectable so tests never touch the filesystem. */
|
|
1096
1115
|
postMergeCleanup?: {
|
|
1097
1116
|
enabled: boolean;
|
|
@@ -3479,6 +3498,28 @@ function runOriginData(env: NodeJS.ProcessEnv = process.env): RunOriginData {
|
|
|
3479
3498
|
};
|
|
3480
3499
|
}
|
|
3481
3500
|
|
|
3501
|
+
/**
|
|
3502
|
+
* STOP-AUTOHEAL — a recorded stop goes straight to the dispatcher (shadow by default:
|
|
3503
|
+
* `tools.selfImplement.autoheal.mode`). Never throws and never changes the run outcome.
|
|
3504
|
+
*/
|
|
3505
|
+
async function autohealRecordedStop(stopEntry: RunLedgerEntry, s: SelfImplementSeams | undefined): Promise<void> {
|
|
3506
|
+
try {
|
|
3507
|
+
const record = readRunStopRecord(stopEntry);
|
|
3508
|
+
if (!record) return;
|
|
3509
|
+
const mode = parseAutohealMode((await import('../user-config.js')).getUserConfig().tools.selfImplement.autoheal?.mode);
|
|
3510
|
+
const loadLedger = s?.stopAutohealLoadLedger ?? (s?.writeRunLedger ? () => null : undefined);
|
|
3511
|
+
const result = await autohealFromStop({ runId: stopEntry.runId, ...(stopEntry.goalId ? { goalId: stopEntry.goalId } : {}), record }, {
|
|
3512
|
+
mode,
|
|
3513
|
+
...(s?.stopAutohealActions ? { actions: s.stopAutohealActions } : {}),
|
|
3514
|
+
...(loadLedger ? { loadLedger } : {}),
|
|
3515
|
+
...(s?.writeRunLedger ? { writeLedger: s.writeRunLedger } : {}),
|
|
3516
|
+
});
|
|
3517
|
+
s?.onStopAutoheal?.(result);
|
|
3518
|
+
} catch (error) {
|
|
3519
|
+
try { debug.log('self-implement.autoheal', 'dispatch-failed', { runId: stopEntry.runId, error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3482
3523
|
export async function runSelfImplement(opts: SelfImplementOptions): Promise<SelfImplementResult> {
|
|
3483
3524
|
if (opts.childLlm && (typeof opts.childLlm.provider !== 'string' || !opts.childLlm.provider.trim() || typeof opts.childLlm.model !== 'string' || !opts.childLlm.model.trim())) {
|
|
3484
3525
|
throw new Error('childLlm.provider and childLlm.model must both be non-empty');
|
|
@@ -3500,6 +3541,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
3500
3541
|
const attemptOrdinal = incrementRunAttemptOrdinal(runId);
|
|
3501
3542
|
const shardIdentity = parseRunShardIdentity(opts.feature);
|
|
3502
3543
|
const observeOuter = makeRunObserver(runId, opts.goalId, debug.log.bind(debug), opts.seams.writeRunLedger, shardIdentity);
|
|
3544
|
+
forgetRunStop(runId); // STOP-RECORD: one stop per run «attempt» — a rerun with the same id may record again
|
|
3503
3545
|
try {
|
|
3504
3546
|
// 10-05 PODPROVIDER: record which child provider this run uses — «default» when the launch named none.
|
|
3505
3547
|
const launch = opts.launch ?? resolveLaunchStamp();
|
|
@@ -4108,6 +4150,21 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
4108
4150
|
observeOuter('credential-failure-observed', { count: credentialFailureCount, ...lastCredentialFailure });
|
|
4109
4151
|
}
|
|
4110
4152
|
terminalResult = withEvidenceCoverage(addressedResult);
|
|
4153
|
+
// runSelfImplement is the terminal caller: retain every existing PR/ledger/log observation and append one stop fact.
|
|
4154
|
+
if ((addressedResult.stage === 'aborted' || (addressedResult.mergeReason && addressedResult.mergeReason !== 'auto')) && !runStopRecorded(runId)) {
|
|
4155
|
+
const reason = addressedResult.mergeReason;
|
|
4156
|
+
const aborted = addressedResult.stage === 'aborted';
|
|
4157
|
+
let stopEntry: RunLedgerEntry | undefined;
|
|
4158
|
+
try {
|
|
4159
|
+
stopEntry = recordRunStop({ runId, ...(opts.goalId ? { goalId: opts.goalId } : {}), shardIdentity,
|
|
4160
|
+
site: aborted ? 'implement-aborted' : 'merge-hold',
|
|
4161
|
+
class: aborted ? 'unclassified' : classifyMergeHoldStop(reason), cause: aborted ? 'implement aborted' : `merge held: ${reason}`,
|
|
4162
|
+
evidenceRef: addressedResult.prUrl ?? `run-ledger:${runId}:${aborted ? 'implemented' : 'merge-decision'}`,
|
|
4163
|
+
nextMove: aborted ? 'Inspect preserved worktree and child summary' : 'Review merge decision and PR before landing',
|
|
4164
|
+
}, opts.seams.writeRunLedger ?? appendRunLedgerEntry);
|
|
4165
|
+
} catch { /* stop observation cannot change the run outcome */ }
|
|
4166
|
+
if (stopEntry) await autohealRecordedStop(stopEntry, opts.seams);
|
|
4167
|
+
}
|
|
4111
4168
|
observeDeclaredScopeDiff(observeOuter, {
|
|
4112
4169
|
goalFile: opts.goalFile,
|
|
4113
4170
|
changedFiles: opts.goalFile && addressedResult.worktreePath
|
|
@@ -5134,6 +5191,8 @@ async function runSelfImplementInner(
|
|
|
5134
5191
|
limitationCount: number;
|
|
5135
5192
|
} | undefined;
|
|
5136
5193
|
let gate!: SelfImplementGateResult;
|
|
5194
|
+
/** GATE-ENV-RETRY: 재시도 뒤에도 남은 환경 결손의 종류. 정의되면 이 라운드의 gate 실패는 «수확 대기»로 끝난다. */
|
|
5195
|
+
let gateEnvDeficitKind: GateEnvDeficitKind | undefined;
|
|
5137
5196
|
let review: SelfImplementReview | undefined;
|
|
5138
5197
|
/** Review-budget leftovers and the body that carried them, so a later merge drafts from the same text. */
|
|
5139
5198
|
let preparedReviewBudgetPrBody: string | undefined;
|
|
@@ -5556,6 +5615,15 @@ async function runSelfImplementInner(
|
|
|
5556
5615
|
});
|
|
5557
5616
|
};
|
|
5558
5617
|
const podReviewBlockedDraft = process.env.ELANOUS_SUBSTRATE === 'pod';
|
|
5618
|
+
/** DRAFT-NOT-ARCHIVE: 멈춤 산출을 PR 대신 수확 가지로 보냈으면 그 가지 — hard-cap 재발사의 base 가 된다. */
|
|
5619
|
+
let salvagedStopBranch: string | undefined;
|
|
5620
|
+
const readDraftOnStopMode = (): DraftOnStopMode => {
|
|
5621
|
+
try {
|
|
5622
|
+
return s.draftOnStopMode ? s.draftOnStopMode() : parseDraftOnStopMode(getUserConfig().tools?.selfImplement?.draftOnStop).mode;
|
|
5623
|
+
} catch {
|
|
5624
|
+
return 'always';
|
|
5625
|
+
}
|
|
5626
|
+
};
|
|
5559
5627
|
let blockedDraftNoChanges = false;
|
|
5560
5628
|
let blockedDraftPrFailure: string | undefined;
|
|
5561
5629
|
const reviewBlockedResult = (pr: { url: string; number: number } | undefined) => ({
|
|
@@ -5768,6 +5836,72 @@ async function runSelfImplementInner(
|
|
|
5768
5836
|
progress(stage, '보존할 변경이 없어 draft PR을 열지 않음');
|
|
5769
5837
|
return undefined;
|
|
5770
5838
|
}
|
|
5839
|
+
// DRAFT-NOT-ARCHIVE (RFC R1): harvestable 멈춤은 draft PR 대신 수확 가지 ⊕ 런 원장(사유·다음 수).
|
|
5840
|
+
// ⛔ Pod review-blocked 는 결과 stage 가 PR 유무로 갈리므로 이 조각에서는 건드리지 않는다.
|
|
5841
|
+
const draftOnStopMode = readDraftOnStopMode();
|
|
5842
|
+
let launchSeat: string | undefined;
|
|
5843
|
+
try { launchSeat = (opts.launch ?? resolveLaunchStamp()).actor; } catch { launchSeat = undefined; }
|
|
5844
|
+
const draftOnStop = decideDraftOnStop({ mode: draftOnStopMode, classification: classificationRecord.classification, seat: launchSeat });
|
|
5845
|
+
const stopCard = {
|
|
5846
|
+
stage,
|
|
5847
|
+
mode: draftOnStop.mode,
|
|
5848
|
+
stopClass: draftOnStop.stopClass,
|
|
5849
|
+
classification: classificationRecord.classification,
|
|
5850
|
+
reason: reason.slice(0, 240),
|
|
5851
|
+
nextMove: nextMoveFor(classificationRecord.classification),
|
|
5852
|
+
branch: wt.branch,
|
|
5853
|
+
};
|
|
5854
|
+
if (draftOnStop.action === 'salvage-branch' && !(stage === 'review-blocked' && podReviewBlockedDraft)) {
|
|
5855
|
+
const salvageBranch = salvageBranchForRun(runId, wt.branch);
|
|
5856
|
+
let artifactPath: string | undefined;
|
|
5857
|
+
let artifactError: string | undefined;
|
|
5858
|
+
try {
|
|
5859
|
+
artifactPath = persistFullPrBody(s.persistPrBodyArtifact, {
|
|
5860
|
+
origin: 'self-implement-draft-not-archive',
|
|
5861
|
+
body: assembled.body,
|
|
5862
|
+
originalChars: assembled.originalChars,
|
|
5863
|
+
});
|
|
5864
|
+
} catch (persistenceError) {
|
|
5865
|
+
artifactError = persistenceError instanceof Error ? persistenceError.message : String(persistenceError);
|
|
5866
|
+
}
|
|
5867
|
+
let salvageError: string | undefined;
|
|
5868
|
+
try {
|
|
5869
|
+
if (s.preserveBlockedBranch) {
|
|
5870
|
+
if (!await s.preserveBlockedBranch({ cwd: wt.path, branch: salvageBranch, title: prTitle(opts.feature) })) throw new Error('수확 가지 push가 성공하지 않았음');
|
|
5871
|
+
} else {
|
|
5872
|
+
if (!existsSync(wt.path)) throw new Error(`worktree not found: ${wt.path}`);
|
|
5873
|
+
const commit = commitWorktree(wt.path, prTitle(opts.feature));
|
|
5874
|
+
if (!commit.ok && !/nothing to commit/.test(commit.out)) throw new Error(`로컬 커밋 실패: ${commit.out}`);
|
|
5875
|
+
const push = runGitCommand(wt.path, ['push', 'origin', `HEAD:refs/heads/${salvageBranch}`]);
|
|
5876
|
+
if (push.status !== 0) throw new Error(push.stderr || push.stdout || 'git push failed');
|
|
5877
|
+
}
|
|
5878
|
+
} catch (error) {
|
|
5879
|
+
salvageError = safeErrorDescription(error);
|
|
5880
|
+
}
|
|
5881
|
+
const artifactFields = { ...(artifactPath ? { prBodyArtifactPath: artifactPath } : {}), ...(artifactError ? { prBodyArtifactError: artifactError } : {}) };
|
|
5882
|
+
if (salvageError === undefined) {
|
|
5883
|
+
salvagedStopBranch = salvageBranch;
|
|
5884
|
+
observe('salvaged', { ...stopCard, salvageBranch, ...artifactFields }, { category: 'self-implement.draft-not-archive' });
|
|
5885
|
+
observe('rework-blocked-draft-pr', { ...observation, skipped: 'draft-not-archive', salvageBranch, nextMove: stopCard.nextMove, ...artifactFields });
|
|
5886
|
+
progress(stage, `멈춤 — draft PR 없이 수확 가지로 보존: ${salvageBranch} · 사유: ${stopCard.reason.slice(0, 160)} · 다음 수: ${stopCard.nextMove}${artifactPath ? ` · 판정 본문: ${artifactPath}` : ''}`);
|
|
5887
|
+
return undefined;
|
|
5888
|
+
}
|
|
5889
|
+
// 수확 가지를 못 남기면 산출을 잃지 않도록 종전대로 draft PR 로 간다.
|
|
5890
|
+
observe('salvage-failed', { ...stopCard, salvageBranch, error: salvageError, fallback: 'open-draft', ...artifactFields }, { category: 'self-implement.draft-not-archive', level: 'warn' });
|
|
5891
|
+
} else if (draftOnStop.mode === 'needs-owner-only') {
|
|
5892
|
+
const ownerLabel = draftOnStop.action === 'open-draft' ? draftOnStop.ownerLabel : undefined;
|
|
5893
|
+
// 자리를 모르면 «모름»으로 남긴다(⛔ OP 로 추측하지 않는다).
|
|
5894
|
+
observe(draftOnStop.stopClass === 'landable' ? 'landable-draft' : 'needs-owner-draft', { ...stopCard, ownerSeat: ownerLabel ? ownerLabel.slice('elanous:seat-'.length) : 'unknown', ...(ownerLabel ? { ownerLabel } : {}) }, { category: 'self-implement.draft-not-archive' });
|
|
5895
|
+
}
|
|
5896
|
+
// 'always' 는 종전 라벨 그대로 · needs-owner-only 의 needs-owner 는 `elanous:needs-owner` ⊕ (아는 경우만) 자리 라벨.
|
|
5897
|
+
// landable(승인된 산출)은 자리 라벨만 — 다음 수가 `pr land` 라 needs-owner 가 아니다.
|
|
5898
|
+
const needsOwnerDraft = draftOnStop.mode === 'needs-owner-only' && draftOnStop.stopClass === 'needs-owner';
|
|
5899
|
+
const seatLabelled = draftOnStop.mode === 'needs-owner-only' && draftOnStop.stopClass !== 'harvestable';
|
|
5900
|
+
const draftLabels = [
|
|
5901
|
+
...(stage === 'review-blocked' ? ['review-blocked'] : []),
|
|
5902
|
+
...(seatLabelled && draftOnStop.action === 'open-draft' && draftOnStop.ownerLabel ? [draftOnStop.ownerLabel] : []),
|
|
5903
|
+
...(needsOwnerDraft ? ['elanous:needs-owner'] : []),
|
|
5904
|
+
];
|
|
5771
5905
|
// Pod review-blocked: a failed commit or PR still pushes the branch, and the result detail says what survived.
|
|
5772
5906
|
const preserveReviewBlockedBranch = async (cause: string, commitFirst: boolean, suffix = ''): Promise<undefined> => {
|
|
5773
5907
|
let pushed = false;
|
|
@@ -5817,7 +5951,7 @@ async function runSelfImplementInner(
|
|
|
5817
5951
|
head: wt.branch,
|
|
5818
5952
|
...(opts.base ? { base: opts.base } : {}),
|
|
5819
5953
|
draft: true,
|
|
5820
|
-
...(
|
|
5954
|
+
...(draftLabels.length > 0 ? { labels: draftLabels } : {}),
|
|
5821
5955
|
cwd: wt.path,
|
|
5822
5956
|
}), T.pr, 'pr');
|
|
5823
5957
|
} catch (error) {
|
|
@@ -5839,6 +5973,17 @@ async function runSelfImplementInner(
|
|
|
5839
5973
|
...(artifactPath ? { prBodyArtifactPath: artifactPath } : {}),
|
|
5840
5974
|
...(artifactError ? { prBodyArtifactError: artifactError } : {}),
|
|
5841
5975
|
}, { level: 'warn' });
|
|
5976
|
+
// STOP-RECORD: a failed blocked-draft PR open (e.g. same-branch rerun with a PR already open) left no stop reason.
|
|
5977
|
+
let draftStopEntry: RunLedgerEntry | undefined;
|
|
5978
|
+
try {
|
|
5979
|
+
const prExists = /already exists|pull request for branch/i.test(prError);
|
|
5980
|
+
draftStopEntry = recordRunStop({ runId, ...(opts.goalId ? { goalId: opts.goalId } : {}), site: 'draft-pr-open-failed', class: 'unclassified',
|
|
5981
|
+
cause: prExists ? 'draft PR open failed: a PR already exists for this branch' : `draft PR open failed: ${prError}`,
|
|
5982
|
+
evidenceRef: artifactPath ?? `branch:${wt.branch}`,
|
|
5983
|
+
nextMove: prExists ? '기존 PR 갱신 (update the existing PR for this branch)' : 'Retry draft PR open from the preserved branch',
|
|
5984
|
+
}, s.writeRunLedger ?? appendRunLedgerEntry);
|
|
5985
|
+
} catch { /* stop observation cannot change the run outcome */ }
|
|
5986
|
+
if (draftStopEntry) await autohealRecordedStop(draftStopEntry, s);
|
|
5842
5987
|
if (stage === 'review-blocked' && podReviewBlockedDraft) {
|
|
5843
5988
|
return preserveReviewBlockedBranch(`draft PR 생성 실패: ${prError}`, true,
|
|
5844
5989
|
`; ${artifactPath ? `판정 본문: ${artifactPath}` : `판정 본문 보존 실패: ${artifactError ?? 'unknown error'}`}`);
|
|
@@ -5873,6 +6018,13 @@ async function runSelfImplementInner(
|
|
|
5873
6018
|
observe('pr.comment.seam-missing', { number: pr.number, salvageAction: action }, { level: 'warn' });
|
|
5874
6019
|
return;
|
|
5875
6020
|
}
|
|
6021
|
+
// QUIET-PR-COMMENTS: a parked outcome is history of this run, not a new notification — fold it into the
|
|
6022
|
+
// single edited status comment (an edit mails nobody). Without the status seams it stays a separate comment.
|
|
6023
|
+
if (s.findPrComment && s.editPrComment) {
|
|
6024
|
+
bufferPrComment(round, 'author', `Rework salvage status: ${action}.`, reason ? [`reason: ${reason}`] : []);
|
|
6025
|
+
await flushPrComments(pr);
|
|
6026
|
+
return;
|
|
6027
|
+
}
|
|
5876
6028
|
const body = [
|
|
5877
6029
|
formatPrComment({ role: 'author', round, run: runId }),
|
|
5878
6030
|
`Rework salvage status: ${action}.`,
|
|
@@ -6046,7 +6198,7 @@ async function runSelfImplementInner(
|
|
|
6046
6198
|
observe('rework-salvage', { action: 'launching', ...launchObservation });
|
|
6047
6199
|
try {
|
|
6048
6200
|
if (!s.launchReworkSalvage) throw new Error('launch seam unavailable');
|
|
6049
|
-
await s.launchReworkSalvage({ goalFile: opts.goalFile!, base: wt.branch, salvageAttempt: (opts.salvageAttempt ?? 0) + 1 });
|
|
6201
|
+
await s.launchReworkSalvage({ goalFile: opts.goalFile!, base: salvagedStopBranch ?? wt.branch, salvageAttempt: (opts.salvageAttempt ?? 0) + 1 });
|
|
6050
6202
|
observe('rework-salvage', { action: 'launched', ...launchObservation });
|
|
6051
6203
|
await postSalvageStatus(pr, 'launched', environmentReason ?? (hardCapBlockedExtend ? 'hard-cap-extend' : undefined));
|
|
6052
6204
|
return 'launched';
|
|
@@ -6843,7 +6995,7 @@ async function runSelfImplementInner(
|
|
|
6843
6995
|
{ label: 'Stay in scope', description: 'Do not accept out-of-scope changes.', recommended: true },
|
|
6844
6996
|
{ label: 'Expand scope', description: 'Accept the changed files outside the target paths.' },
|
|
6845
6997
|
],
|
|
6846
|
-
}, { runId }, { writeRunLedger: s.writeRunLedger });
|
|
6998
|
+
}, { runId, outsideFiles: scope.outsideNames, outsideCount: scope.outsideCount, declaredPaths: declaredPathsFromGoalDocumentText(goalDocument!).paths }, { writeRunLedger: s.writeRunLedger });
|
|
6847
6999
|
if ('parked' in result || 'deferred' in result || result.choice !== 'Expand scope') {
|
|
6848
7000
|
finalizeSupervisorDeliveries('execution-scope-not-approved');
|
|
6849
7001
|
observe('execution-scope-held', { outsideCount: scope.outsideCount, disposition: 'parked' in result ? 'parked' : 'in-scope-only' });
|
|
@@ -6875,6 +7027,31 @@ async function runSelfImplementInner(
|
|
|
6875
7027
|
}
|
|
6876
7028
|
onNodeEntry(node('gate'), round);
|
|
6877
7029
|
gate = await stepTimeout(s.gate(wt.path, { runId, round }), T.gate, 'gate');
|
|
7030
|
+
// ⭐ GATE-ENV-RETRY — 「잰 쪽」이 넘어진 실패(시험 결과 미수신·index.lock·OOM·측정 불가·네트워크)는
|
|
7031
|
+
// 구현을 버리지 않고 짧게 쉰 뒤 gate «만» 다시 돈다. ⛔ 도입 실패·자식 책임은 판정자가 거짓을 낸다.
|
|
7032
|
+
gateEnvDeficitKind = undefined;
|
|
7033
|
+
let envVerdict = classifyGateEnvDeficit(gate);
|
|
7034
|
+
const envRetryAttempts = Math.max(0, s.gateEnvRetry?.attempts ?? DEFAULT_GATE_ENV_RETRY.attempts);
|
|
7035
|
+
const envRetryDelayMs = Math.max(0, s.gateEnvRetry?.delayMs ?? DEFAULT_GATE_ENV_RETRY.delayMs);
|
|
7036
|
+
const envRetrySleep = s.gateEnvRetry?.sleep ?? ((ms: number) => new Promise<void>((done) => setTimeout(done, ms)));
|
|
7037
|
+
for (let attempt = 1; envVerdict.envDeficit && attempt <= envRetryAttempts; attempt++) {
|
|
7038
|
+
const kind = envVerdict.kind;
|
|
7039
|
+
debug.log('self-implement.gate-env-retry', 'retry-scheduled', { runId, round, attempt, attempts: envRetryAttempts, kind, delayMs: envRetryDelayMs });
|
|
7040
|
+
observe('gate-env-retry', { round, attempt, attempts: envRetryAttempts, kind, delayMs: envRetryDelayMs }, { level: 'warn' });
|
|
7041
|
+
progress('gating', `gate 환경 결손(${kind}) — 구현 유지 · ${Math.round(envRetryDelayMs / 1000)}초 뒤 gate 만 재측정 (${attempt}/${envRetryAttempts})`);
|
|
7042
|
+
await envRetrySleep(envRetryDelayMs);
|
|
7043
|
+
gate = await stepTimeout(s.gate(wt.path, { runId, round }), T.gate, 'gate');
|
|
7044
|
+
envVerdict = classifyGateEnvDeficit(gate);
|
|
7045
|
+
debug.log('self-implement.gate-env-retry', 'retry-result', {
|
|
7046
|
+
runId, round, attempt, kind, passed: gate.passed,
|
|
7047
|
+
stillEnvDeficit: envVerdict.envDeficit,
|
|
7048
|
+
...(envVerdict.envDeficit ? { nextKind: envVerdict.kind } : { verdict: envVerdict.reason }),
|
|
7049
|
+
});
|
|
7050
|
+
}
|
|
7051
|
+
if (envVerdict.envDeficit) {
|
|
7052
|
+
gateEnvDeficitKind = envVerdict.kind;
|
|
7053
|
+
debug.log('self-implement.gate-env-retry', 'exhausted', { runId, round, attempts: envRetryAttempts, kind: envVerdict.kind });
|
|
7054
|
+
}
|
|
6878
7055
|
} else {
|
|
6879
7056
|
observe('gate-skipped-by-graph', { ...graphAuthorityFields(graphAuthority, graphTemplate), round, reason: gateRoute.reason, changedFileCount: gateRouteFiles?.length ?? null });
|
|
6880
7057
|
gate = {
|
|
@@ -6961,6 +7138,26 @@ async function runSelfImplementInner(
|
|
|
6961
7138
|
// 타임아웃«뿐» — 도입 0 · 미분류 0 · 자식 책임 없음. 재작업으로 보내지 않고 리뷰로 진행한다.
|
|
6962
7139
|
// 게이트를 통과로 접지하지 않는다(allowsBaselineOnlyFailure 는 그대로 timedOut===0 을 요구).
|
|
6963
7140
|
// 이 런의 병합은 뒤에서 GATE_TIMEOUT_UNMEASURED_MERGE_REASON 으로 hitl 에 고정한다.
|
|
7141
|
+
if (gateEnvDeficitKind !== undefined) {
|
|
7142
|
+
// ⭐ GATE-ENV-RETRY — 재측정 뒤에도 환경 결손이면 구현을 «버리지 않는다».
|
|
7143
|
+
// 브랜치·draft PR 을 남기고 `harvestable` 로 끝낸다(재작업은 이 신호를 못 바꾼다).
|
|
7144
|
+
const envKind = gateEnvDeficitKind;
|
|
7145
|
+
finalizeSupervisorDeliveries('gate-env-deficit-harvestable');
|
|
7146
|
+
const reason = `gate failed on an environment deficit (${envKind}) after gate-only retries; ${GATE_ENV_DEFICIT_HARVESTABLE_MARK} — implementation kept without rework`;
|
|
7147
|
+
bufferPrComment(round, 'reviewer', `Round ${round}: ${reason}.`, [
|
|
7148
|
+
`Gate: ${gate.log?.trim() || '(gate log unavailable)'}`,
|
|
7149
|
+
]);
|
|
7150
|
+
observe('gate-env-deficit-harvestable', {
|
|
7151
|
+
round, kind: envKind,
|
|
7152
|
+
...(facts ? { introduced: facts.introduced, preexisting: facts.preexisting, unknown: facts.unknown } : {}),
|
|
7153
|
+
...(facts?.unknownReason !== undefined ? { unknownReason: facts.unknownReason } : {}),
|
|
7154
|
+
reason,
|
|
7155
|
+
}, { level: 'warn' });
|
|
7156
|
+
debug.log('self-implement.gate-env-retry', 'harvestable', { runId, round, kind: envKind, branch: wt.branch });
|
|
7157
|
+
progress('gate-failed', `중단 — 환경 결손(${envKind})이 재측정 뒤에도 남음 · 구현 유지 · 수확 대기`);
|
|
7158
|
+
const pr = await preserveBlockedArtifacts({ stage: 'gate-failed', reason, gate, salvageStatusExpected: false });
|
|
7159
|
+
return { ok: false, stage: 'gate-failed', node: 'rework', ...resolveRunOutcome({ termination: 'abandoned' }), harvestable: true, sessionId, worktreePath: wt.path, branch: wt.branch, gate, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(pr ? { prUrl: pr.url, prNumber: pr.number } : {}), detail: reason };
|
|
7160
|
+
}
|
|
6964
7161
|
if (isTimeoutOnlyUnmeasuredGate(facts)) {
|
|
6965
7162
|
observe('gate-timeout-only-continued-to-review', {
|
|
6966
7163
|
round,
|
|
@@ -7537,6 +7734,16 @@ async function runSelfImplementInner(
|
|
|
7537
7734
|
sync.status,
|
|
7538
7735
|
);
|
|
7539
7736
|
observe('gate.postsync', postSyncGateObservation(regate, wt.branch, hasModuleLoadFailure, sync.status));
|
|
7737
|
+
// MERGE-INTENT-RESOLVE — join the resolver's sibling-intent decisions with the regate verdict (OP metric).
|
|
7738
|
+
if (sync.intentResolve?.length) {
|
|
7739
|
+
debug.log('self-implement.main-sync', 'intent-resolve', {
|
|
7740
|
+
phase: 'regate', runId, branch: wt.branch, mergeTarget,
|
|
7741
|
+
adopted: [...new Set(sync.intentResolve.map((entry) => entry.adopted))],
|
|
7742
|
+
siblingIntentAdopted: sync.intentResolve.some((entry) => entry.adopted === 'sibling-intent'),
|
|
7743
|
+
files: sync.intentResolve,
|
|
7744
|
+
regatePassed: regate.passed,
|
|
7745
|
+
});
|
|
7746
|
+
}
|
|
7540
7747
|
if (timeoutOnlyPostSync) {
|
|
7541
7748
|
const facts = regate.reflectGateFacts!;
|
|
7542
7749
|
observe('post-sync-gate-timeout-only-continued', {
|
|
@@ -7860,6 +8067,12 @@ async function runSelfImplementInner(
|
|
|
7860
8067
|
} catch (error) {
|
|
7861
8068
|
observe('draft-supersede-on-merge-failed', { number: pr.number, error: safeErrorDescription(error) }, { level: 'warn' });
|
|
7862
8069
|
}
|
|
8070
|
+
// Runs after supersede: a sibling the merge already closed is no longer open, so it gets no resync.
|
|
8071
|
+
try {
|
|
8072
|
+
await s.resyncSiblingsOnMerge?.(pr.number, wt.path);
|
|
8073
|
+
} catch (error) {
|
|
8074
|
+
observe('sibling-resync-on-merge-failed', { number: pr.number, error: safeErrorDescription(error) }, { level: 'warn' });
|
|
8075
|
+
}
|
|
7863
8076
|
const directory = releaseNotesDir(elanousStateRoot());
|
|
7864
8077
|
const fragment: ReleaseNoteFragment = {
|
|
7865
8078
|
pr: pr.number,
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STOP-RECORD (0.2.20 P0) — 런이 «멈춘» 자리마다 런 원장에 `event='stop'` 한 줄을 «더한다».
|
|
3
|
+
*
|
|
4
|
+
* 멈추는 자리 넷이 이 함수 하나를 부른다:
|
|
5
|
+
* ① 병합 보류(`mergeReason !== 'auto'`) · ② implement aborted — `orchestrator.ts` `runSelfImplement`
|
|
6
|
+
* ③ Pod exit 비0 — `harness-cli-command.ts` `onPod`
|
|
7
|
+
* ④ 발사 직후 자식 비0 종료(launch-failed) — `tasks-cli.ts` `defaultTaskLauncher`
|
|
8
|
+
*
|
|
9
|
+
* ⛔ 보존 계약: 기존 PR 본문·런 원장 항목·logs.db 사건(exit-classified 등)은 손대지 않고 «더하기»만 한다.
|
|
10
|
+
* ⭐ 이 스키마(`RunStopRecord`·`RUN_STOP_CLASSES`)는 STOP-AUTOHEAL 이 읽는다 — 칸 이름·닫힌 목록을 바꾸지 않는다(더하기만).
|
|
11
|
+
*/
|
|
12
|
+
import { debug } from '../debug/log.js';
|
|
13
|
+
import { appendRunLedgerEntry, type RunLedgerEntry, type RunLedgerWriter, type RunShardIdentity } from './run-ledger.js';
|
|
14
|
+
|
|
15
|
+
/** 닫힌 목록 — 못 고르면 `unclassified`. */
|
|
16
|
+
export const RUN_STOP_CLASSES = [
|
|
17
|
+
'env-unrelated', 'main-sync', 'pod-died', 'review-repeat',
|
|
18
|
+
'review-out-of-scope', 'evidence-uncovered', 'fabric', 'launch-failed', 'unclassified',
|
|
19
|
+
] as const;
|
|
20
|
+
export type RunStopClass = typeof RUN_STOP_CLASSES[number];
|
|
21
|
+
|
|
22
|
+
/** 런 원장 `event='stop'` 줄의 `data` 모양(STOP-AUTOHEAL 이 소비한다). */
|
|
23
|
+
export interface RunStopRecord {
|
|
24
|
+
class: RunStopClass;
|
|
25
|
+
/** 한 줄 · ≤120자 · 결정 라벨(원 로그 꼬리 금지). */
|
|
26
|
+
cause: string;
|
|
27
|
+
/** 근거 위치 — PR URL · `run-ledger:<runId>:<event>` · 파일 경로. */
|
|
28
|
+
evidenceRef: string;
|
|
29
|
+
/** 다음 한 수(사람·힐러가 읽는다). */
|
|
30
|
+
nextMove: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const RUN_STOP_EVENT = 'stop';
|
|
34
|
+
export const RUN_STOP_CAUSE_MAX = 120;
|
|
35
|
+
|
|
36
|
+
export interface RunStopInput {
|
|
37
|
+
runId: string;
|
|
38
|
+
goalId?: string;
|
|
39
|
+
shardIdentity?: RunShardIdentity;
|
|
40
|
+
/** 닫힌 목록 밖이거나 비면 `unclassified`. */
|
|
41
|
+
class?: string;
|
|
42
|
+
cause: string;
|
|
43
|
+
evidenceRef: string;
|
|
44
|
+
nextMove: string;
|
|
45
|
+
/** 관측용 — 어느 자리가 불렀나(merge-hold · implement-aborted · draft-pr-open-failed · pod-exit · launch-failed). */
|
|
46
|
+
site?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function isRunStopClass(value: unknown): value is RunStopClass {
|
|
50
|
+
return typeof value === 'string' && (RUN_STOP_CLASSES as readonly string[]).includes(value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 원 로그 꼬리가 아니라 «한 줄»로 — 첫 비어 있지 않은 줄 · ANSI 제거 · 공백 접기 · ≤120자(코드 포인트). */
|
|
54
|
+
export function boundRunStopCause(raw: string): string {
|
|
55
|
+
// eslint-disable-next-line no-control-regex
|
|
56
|
+
const plain = raw.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '').replace(/[\x00-\x08\x0b-\x1f\x7f]/g, (c) => (c === '\r' ? '\n' : ' '));
|
|
57
|
+
const first = plain.split('\n').map((line) => line.replace(/\s+/g, ' ').trim()).find((line) => line.length > 0) ?? '';
|
|
58
|
+
return Array.from(first).slice(0, RUN_STOP_CAUSE_MAX).join('') || 'stop reason unclassified';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 병합 보류 사유 → 닫힌 목록. 모르면 `unclassified`. */
|
|
62
|
+
export function classifyMergeHoldStop(mergeReason: string | undefined): RunStopClass {
|
|
63
|
+
const reason = mergeReason ?? '';
|
|
64
|
+
if (reason.startsWith('main-sync') || reason === 'default-branch-unresolved') return 'main-sync';
|
|
65
|
+
if (reason === 'required-evidence-uncovered') return 'evidence-uncovered';
|
|
66
|
+
if (reason === 'review-must-fix' || reason.startsWith('review-budget')) return 'review-repeat';
|
|
67
|
+
if (reason.startsWith('review-diff') || reason === 'no-real-review') return 'review-out-of-scope';
|
|
68
|
+
return 'unclassified';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Pod exit 분류 사유 → 닫힌 목록. */
|
|
72
|
+
export function classifyPodExitStop(reason: string | undefined): RunStopClass {
|
|
73
|
+
switch (reason) {
|
|
74
|
+
case 'pod-failure': case 'pod-error': case 'OOMKilled': case 'ENOBUFS': case 'signal': return 'pod-died';
|
|
75
|
+
case 'no-launch': return 'launch-failed';
|
|
76
|
+
default: return 'unclassified';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** 이 프로세스에서 stop 을 이미 쓴 런 — 한 런이 두 자리(예: draft PR 실패 ⊕ aborted)에서 두 줄을 쓰지 않게. */
|
|
81
|
+
const recordedThisProcess = new Set<string>();
|
|
82
|
+
const RECORDED_MAX = 4096;
|
|
83
|
+
|
|
84
|
+
/** 이 프로세스가 그 런에 stop 을 이미 썼나(원장 재독 없이 · 같은 프로세스 안 중복 방지용). */
|
|
85
|
+
export function runStopRecorded(runId: string): boolean {
|
|
86
|
+
return recordedThisProcess.has(runId);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 런 시작 때 부른다 — 같은 runId 로 다시 도는 런(재시도·시험)이 지난 판의 표시에 막히지 않게. */
|
|
90
|
+
export function forgetRunStop(runId: string): void {
|
|
91
|
+
recordedThisProcess.delete(runId);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** 멈춘 런에 stop 한 줄을 더한다 — 기존 관측은 바꾸지 않는다. 쓴 줄을 돌려준다. */
|
|
95
|
+
export function recordRunStop(input: RunStopInput, write: RunLedgerWriter = appendRunLedgerEntry): RunLedgerEntry {
|
|
96
|
+
const record: RunStopRecord = {
|
|
97
|
+
class: isRunStopClass(input.class) ? input.class : 'unclassified',
|
|
98
|
+
cause: boundRunStopCause(input.cause),
|
|
99
|
+
evidenceRef: input.evidenceRef,
|
|
100
|
+
nextMove: input.nextMove,
|
|
101
|
+
};
|
|
102
|
+
const entry: RunLedgerEntry = {
|
|
103
|
+
timestamp: new Date().toISOString(), runId: input.runId, event: RUN_STOP_EVENT,
|
|
104
|
+
data: { ...record },
|
|
105
|
+
...(input.goalId ? { goalId: input.goalId } : {}),
|
|
106
|
+
...input.shardIdentity,
|
|
107
|
+
};
|
|
108
|
+
write(entry);
|
|
109
|
+
if (recordedThisProcess.size >= RECORDED_MAX) recordedThisProcess.clear();
|
|
110
|
+
recordedThisProcess.add(input.runId);
|
|
111
|
+
try {
|
|
112
|
+
debug.log('self-implement.run-stop', 'recorded', { runId: input.runId, site: input.site ?? null, ...record, requestedClass: input.class ?? null });
|
|
113
|
+
} catch { /* observation is fail-soft */ }
|
|
114
|
+
return entry;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** 원장 항목이 stop 줄이면 그 레코드를 돌려준다(STOP-AUTOHEAL 소비용). */
|
|
118
|
+
export function readRunStopRecord(entry: RunLedgerEntry): RunStopRecord | undefined {
|
|
119
|
+
if (entry.event !== RUN_STOP_EVENT) return undefined;
|
|
120
|
+
const data = entry.data as Partial<RunStopRecord>;
|
|
121
|
+
if (typeof data.cause !== 'string' || typeof data.evidenceRef !== 'string' || typeof data.nextMove !== 'string') return undefined;
|
|
122
|
+
return { class: isRunStopClass(data.class) ? data.class : 'unclassified', cause: data.cause, evidenceRef: data.evidenceRef, nextMove: data.nextMove };
|
|
123
|
+
}
|