elanous 0.2.21 → 0.2.22
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-08b716476f1d50af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2489-ff55a35b202c0f94.js → 2489-d238d3e2eb5b2bf5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-88459e0903da2803.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-d781be3e3a9cfd74.js → page-d85c43f46f58716b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/{page-b0a80997903852d8.js → page-4e645600579ae952.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-2b1263165630d9c8.js → page-87edbfd19117823b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-e2d6bed48ac20120.js → page-cbaf66b1e1804adf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-111cabe4d1fd69ae.js → page-80ed511e02c81412.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/{page-544dd4bd4847eeb7.js → page-9f5610caaa06178c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-3ee191319bf97d87.js → page-ec8f3575e339de74.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-a449f1ed472b81c2.js → page-2a19c5cd3b562580.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-9045094d6d3b5cd0.js → page-b6a09cde2e07521a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/{page-c06673596849ef45.js → page-f7eae1518ca0c353.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{layout-9f81233f29dcd409.js → layout-8462d6a543cbb2d9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-7a12f61be4a579d1.js → page-786fb4504d0f2fa9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-c85b0589591a99c9.js → not-found-75209f14597dcb8f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-725a4da02dd129eb.js → page-bf30affe3a85af7e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{page-21bec4afeb2f0917.js → page-76cf838050a8d681.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-dc39ee7a36d26385.js → page-7ec8e289b4c14df6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-84ae759f76053672.js → page-8c8ac17d52be2a79.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-3db58ddb61fe2be9.js → page-d359ab048d63a9d9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-c5315e30f3528dc4.js → page-b8b345c880bf78d1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-2fe368efb6de3b6a.js → page-f151cc10ad026940.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-c69762987b751174.js → page-bf495a1e284f4e00.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-7f8d7fcdf058a845.js → page-69cb93d1a2e57e0e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-22163556cd38bc88.js → page-72ec543928d67c3e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +1 -0
- package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +14 -14
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +14 -14
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/graphs/catalog/catalog.yaml +1 -1
- package/graphs/drafts/recipes.yaml +2 -2
- package/graphs/test-diet/recipes.yaml +3 -0
- package/graphs/test-diet/test-diet.yaml +3 -1
- package/package.json +1 -1
- package/scripts/f-session-monitor.ts +69 -0
- package/scripts/hq/standby-snapshot.ts +7 -3
- package/scripts/knowledge-compress.ts +11 -0
- package/scripts/nightly-docops.ts +90 -3
- package/scripts/research-coo-admin-dates.ts +39 -0
- package/scripts/skills/public-check.ts +7 -2
- package/scripts/smart-merge/resync-shadow.ts +5 -3
- package/scripts/test-diet/lib.ts +81 -1
- package/scripts/test-diet/node.ts +48 -2
- package/scripts/verify-coo-admin-evidence.ts +83 -0
- package/src/acp/client.ts +4 -2
- package/src/agent/run-context.ts +4 -0
- package/src/agent-env/profile.ts +25 -5
- package/src/agent-mission/driver.ts +26 -12
- package/src/agent-mission/supervisor-metrics.ts +17 -2
- package/src/agent-substrate/acp-reviewer.ts +12 -5
- package/src/agent-substrate/pr-reviewer.ts +11 -4
- package/src/agent-substrate/review-intent.ts +102 -3
- package/src/autopilot/build/llm-conflict-merge.ts +22 -8
- package/src/autopilot/pr-manager.ts +18 -8
- package/src/away/release-watch.ts +47 -8
- package/src/briefing/brief-items.ts +104 -11
- package/src/chat/index.ts +1 -0
- package/src/cli/brief-cli.ts +20 -2
- package/src/cli/config-classify.ts +83 -0
- package/src/cli/control-cli.ts +14 -0
- package/src/cli/decisions-cli.ts +26 -16
- package/src/cli/logs-model-input.ts +1 -1
- package/src/cli/pr-cli.ts +65 -8
- package/src/cli/release-cli.ts +21 -4
- package/src/cli/tasks-cli.ts +108 -1
- package/src/connectors/types.ts +6 -6
- package/src/context-bus/context-now-surfaces.ts +18 -0
- package/src/context-bus/coord-events.ts +32 -4
- package/src/context-bus/long-term-memory.ts +23 -6
- package/src/context-bus/nightly-condense.ts +169 -21
- package/src/control/control-status.ts +87 -0
- package/src/control-plane/light-member.ts +5 -3
- package/src/control-plane/member.ts +172 -7
- package/src/core-turn/run-goal-loop.ts +22 -1
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +9 -0
- package/src/decisions/decision-cards.ts +20 -1
- package/src/decisions/decision-ledger.ts +11 -8
- package/src/decisions/web-push-decision-cards.ts +19 -3
- package/src/discord-self-message.ts +2 -2
- package/src/domains/coo-admin-dates.evidence.json +876 -0
- package/src/domains/coo-admin-tool.ts +48 -14
- package/src/domains/ops-status.ts +53 -0
- package/src/domains/outbound-alert.ts +24 -1
- package/src/exec-requests/planner.ts +6 -0
- package/src/graph-kinds/registry.ts +6 -6
- package/src/graph-runner/graph-cli.ts +34 -2
- package/src/graph-runner/graph-run-api.ts +7 -0
- package/src/graph-runner/graph-tick.ts +73 -12
- package/src/graph-runner/runner.ts +43 -11
- package/src/graph-unify/wf-to-graph.ts +79 -0
- package/src/graph-wizard/generate.ts +34 -2
- package/src/graph-wizard/steps.ts +13 -0
- package/src/harness/harness-cli-command.ts +121 -23
- package/src/harness/harness-queue.ts +58 -14
- package/src/harness/launch-advice.ts +23 -0
- package/src/index.ts +36 -7
- package/src/knowledge/conclusion-compress.ts +93 -0
- package/src/knowledge/know-find.ts +9 -2
- package/src/knowledge/query.ts +20 -0
- package/src/llm.ts +26 -16
- package/src/loops/neighbor-runtime.ts +88 -0
- package/src/loops/orchestrator/author-depth.ts +29 -11
- package/src/loops/orchestrator/overlap.ts +7 -1
- package/src/loops/orchestrator/resync-shadow.ts +1 -1
- package/src/loops/orchestrator/tick.ts +35 -2
- package/src/loops/orchestrator/traffic.ts +2 -0
- package/src/loops/registry.ts +5 -0
- package/src/market/publish.ts +17 -1
- package/src/maturity/feature-maturity.ts +2 -1
- package/src/mss/logging/redaction.ts +17 -0
- package/src/nexus/api/draft-metrics.ts +22 -8
- package/src/nexus/api/graph-peer-edit.ts +77 -0
- package/src/nexus/api/graph-wizard-api.ts +9 -1
- package/src/nexus/api/graphs-api.ts +145 -7
- package/src/nexus/api/http-server.ts +93 -8
- package/src/nexus/api/plugins-market.ts +24 -0
- package/src/nexus/index.ts +3 -2
- package/src/oauth/claude-pty-maturity.ts +24 -0
- package/src/oauth/fallback-chain.ts +27 -14
- package/src/plugins/maker/market-bundle.ts +9 -0
- package/src/plugins/maker/plugin-maker.ts +9 -3
- package/src/plugins/maker/wizard-generate.ts +73 -9
- package/src/policy/llm-policy.ts +11 -6
- package/src/release-loop/checklist-board.ts +27 -0
- package/src/release-loop/checklist.ts +38 -0
- package/src/seat-address/seat-address.ts +1 -0
- package/src/seat-loop/seat-loop.ts +174 -20
- package/src/seat-loop/seat-report.ts +29 -1
- package/src/self-dev/dev-cli.ts +90 -8
- package/src/self-dev/dev-pipeline.ts +20 -1
- package/src/self-dev/draft-sweep.ts +45 -8
- package/src/self-dev/orchestrate.ts +3 -0
- package/src/self-dev/review-provider-fallback.ts +83 -0
- package/src/self-dev/run-supervisor.ts +5 -1
- package/src/self-implement/harness-policy-gates.ts +7 -5
- package/src/self-implement/host-regate.ts +28 -14
- package/src/self-implement/l8-merge-queue.ts +80 -8
- package/src/self-implement/orchestrator.ts +68 -27
- package/src/self-implement/seams.ts +5 -3
- package/src/self-implement/self-implement-cli.ts +4 -0
- package/src/shell-env-bootstrap.ts +19 -4
- package/src/steward/triage.ts +25 -8
- package/src/storage/file-lock.ts +33 -4
- package/src/task-agent/completion-evidence.ts +31 -3
- package/src/task-agent/cover.ts +61 -0
- package/src/task-agent/live-moves.ts +126 -3
- package/src/task-agent/shadow.ts +19 -4
- package/src/task-agent/task-hand.ts +2 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +138 -15
- package/src/task-orchestrator/surfaces/self-implement.ts +7 -0
- package/src/telegram-commands.ts +8 -0
- package/src/telegram-project-command.ts +6 -0
- package/src/telegram.ts +2 -2
- package/src/typecheck-ratchet.ts +2 -1
- package/src/user-config.ts +35 -4
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/executor.ts +24 -2
- package/src/workflow-runtime/triggers/registry.ts +27 -0
- package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
- package/apps/pwa/out/_next/static/chunks/1307-c97eba9fb72a9a5e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ea998f7f5d156bea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-be8b4615c919e36c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8cc707bd09a497d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8d04132dd0ac5e10.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-f23484de467c9ea3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d75f7cd65845ce98.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-a6eb73b8a4840f1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6c0b88068c143f15.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +0 -1
- package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +0 -1
- /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → aBi6EyMtLsrl7J8eH4kF7}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → aBi6EyMtLsrl7J8eH4kF7}/_ssgManifest.js +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { NEXT_MD_PATH, resolveNextMdConflict } from '../release-loop/next-md-merge.js';
|
|
6
|
+
import { defaultLlmResolve, mergeMainWithLlmResolve, type MergeGitSeam } from '../autopilot/build/llm-conflict-merge.js';
|
|
6
7
|
import { getDefaultLogStore } from '../mss/logging/log-store.js';
|
|
7
8
|
|
|
8
9
|
type Command = (bin: string, args: readonly string[], cwd: string) => { status: number | null; stdout: string; stderr: string };
|
|
@@ -30,7 +31,10 @@ export type L8MergeQueueDeps = {
|
|
|
30
31
|
|
|
31
32
|
export type L8ShadowVerdict = { number: number; head: string; base?: string; verdict: 'pass' | 'conflict' | 'fail'; detail?: string; tests: string[]; at: string };
|
|
32
33
|
export type L8ShadowQueueState = { pending: { number: number; head: string }[]; verdicts: L8ShadowVerdict[] };
|
|
33
|
-
export type L8ShadowQueueDeps = L8MergeQueueDeps & {
|
|
34
|
+
export type L8ShadowQueueDeps = L8MergeQueueDeps & {
|
|
35
|
+
observe?: (verdict: L8ShadowVerdict) => void | Promise<void>;
|
|
36
|
+
resolveConflict?: (file: string, conflicted: string, target: string, worktree: string) => Promise<string>;
|
|
37
|
+
};
|
|
34
38
|
|
|
35
39
|
function shadowCommand(execute: Command, bin: string, args: string[], cwd: string): string {
|
|
36
40
|
const result = execute(bin, args, cwd);
|
|
@@ -129,7 +133,8 @@ export async function runL8ShadowQueue(cwd: string, deps: L8ShadowQueueDeps = {}
|
|
|
129
133
|
if (changed.some((file) => file.startsWith('-') || file.startsWith('/') || file.split('/').includes('..') || file.includes('\\'))) throw new Error('unsafe changed-file path');
|
|
130
134
|
verdict.tests = files.filter((file) => /(?:^|\/)[^/]+\.(?:test|spec)\.(?:[cm]?[jt]sx?)$/.test(file));
|
|
131
135
|
const merge = execute('git', ['-c', 'user.name=elanous shadow', '-c', 'user.email=shadow@localhost', 'merge', '--no-ff', '--no-edit', item.head], temp);
|
|
132
|
-
let
|
|
136
|
+
let integrated = merge.status === 0;
|
|
137
|
+
let resolvedNextMd = false;
|
|
133
138
|
if (merge.status !== 0) {
|
|
134
139
|
const unresolved = run('git', ['diff', '--name-only', '--diff-filter=U'], temp).split('\n').filter(Boolean);
|
|
135
140
|
if (!unresolved.length) throw new Error(`git merge failed: ${merge.stderr.trim() || `exit ${merge.status}`}`);
|
|
@@ -148,14 +153,81 @@ export async function runL8ShadowQueue(cwd: string, deps: L8ShadowQueueDeps = {}
|
|
|
148
153
|
writeFileSync(join(temp, NEXT_MD_PATH), resolved);
|
|
149
154
|
run('git', ['add', '--', NEXT_MD_PATH], temp);
|
|
150
155
|
run('git', ['-c', 'user.name=elanous shadow', '-c', 'user.email=shadow@localhost', 'commit', '-m', 'Integrate PR for L8 shadow evaluation'], temp);
|
|
151
|
-
|
|
156
|
+
resolvedNextMd = true;
|
|
157
|
+
integrated = true;
|
|
152
158
|
}
|
|
153
159
|
} else {
|
|
154
|
-
|
|
155
|
-
|
|
160
|
+
const unsafe = unresolved.some((file) => file.startsWith('-') || file.startsWith('/') || file.split('/').includes('..') || file.includes('\\'));
|
|
161
|
+
if (unsafe) throw new Error('unsafe conflict path');
|
|
162
|
+
const staged = (stage: number, file: string): string | null => {
|
|
163
|
+
const result = execute('git', ['show', `:${stage}:${file}`], temp!);
|
|
164
|
+
return result.status === 0 ? result.stdout : null;
|
|
165
|
+
};
|
|
166
|
+
// Reject unsupported index shapes before consulting either resolver.
|
|
167
|
+
// A directory, submodule or unreadable path is an unsupported conflict shape, not an evaluator failure.
|
|
168
|
+
const hasTextMarkers = (file: string): boolean => {
|
|
169
|
+
try {
|
|
170
|
+
const path = join(temp!, file);
|
|
171
|
+
return existsSync(path) && statSync(path).isFile() && readFileSync(path, 'utf8').includes('<<<<<<< ');
|
|
172
|
+
} catch {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const unsupported = unresolved.some((file) => file !== NEXT_MD_PATH && !hasTextMarkers(file));
|
|
177
|
+
// A non-append-only next.md conflict must not fall through to the LLM even when other files conflict.
|
|
178
|
+
const nextStages = !unsupported && unresolved.includes(NEXT_MD_PATH)
|
|
179
|
+
? [staged(1, NEXT_MD_PATH), staged(2, NEXT_MD_PATH), staged(3, NEXT_MD_PATH)] as const
|
|
180
|
+
: undefined;
|
|
181
|
+
const nextMd = nextStages
|
|
182
|
+
? nextStages.some((value) => value === null) ? null
|
|
183
|
+
: resolveNextMdConflict(nextStages[0]!, nextStages[1]!, nextStages[2]!)
|
|
184
|
+
: undefined;
|
|
185
|
+
if (unsupported || nextMd === null || unresolved.some((file) => file !== NEXT_MD_PATH
|
|
186
|
+
&& (staged(1, file) === null || staged(2, file) === null || staged(3, file) === null))) {
|
|
187
|
+
verdict.verdict = 'conflict';
|
|
188
|
+
verdict.detail = nextMd === null ? 'release/next.md conflict is not append-only' : unresolved.join('\n');
|
|
189
|
+
} else {
|
|
190
|
+
const git = (args: string[]) => execute('git', args, temp!);
|
|
191
|
+
const mergeGit: MergeGitSeam = {
|
|
192
|
+
isConfiguredRemote: () => false, // item.head is the fetched, pinned commit, not a remote branch.
|
|
193
|
+
fetch: () => false,
|
|
194
|
+
merge: () => ({ ok: false, conflict: true, stdout: merge.stdout, errorDetail: merge.stderr.trim() }),
|
|
195
|
+
conflictedFiles: () => run('git', ['diff', '--name-only', '--diff-filter=U'], temp!).split('\n').filter(Boolean),
|
|
196
|
+
readIndexStage: (_wt, stage, file) => {
|
|
197
|
+
const result = git(['show', `:${stage}:${file}`]);
|
|
198
|
+
if (result.status !== 0) throw new Error(`index stage ${stage} unavailable: ${file}`);
|
|
199
|
+
return result.stdout;
|
|
200
|
+
},
|
|
201
|
+
readFile: (file) => readFileSync(file, 'utf8'),
|
|
202
|
+
writeFile: (file, content) => writeFileSync(file, content),
|
|
203
|
+
add: (_wt, file) => { run('git', ['add', '--', file], temp!); },
|
|
204
|
+
commit: () => {
|
|
205
|
+
const result = git(['-c', 'user.name=elanous shadow', '-c', 'user.email=shadow@localhost', 'commit', '-m', 'Integrate PR for L8 shadow evaluation']);
|
|
206
|
+
return { ok: result.status === 0, errorDetail: result.stderr.trim() };
|
|
207
|
+
},
|
|
208
|
+
abort: () => { run('git', ['merge', '--abort'], temp!); },
|
|
209
|
+
};
|
|
210
|
+
const resolved = await mergeMainWithLlmResolve(temp, item.head, async (file, conflicted) => {
|
|
211
|
+
if (file === NEXT_MD_PATH) {
|
|
212
|
+
const next = resolveNextMdConflict(mergeGit.readIndexStage(temp!, 1, file), mergeGit.readIndexStage(temp!, 2, file), mergeGit.readIndexStage(temp!, 3, file));
|
|
213
|
+
if (next === null) throw new Error('release/next.md conflict is not append-only');
|
|
214
|
+
return next;
|
|
215
|
+
}
|
|
216
|
+
return (deps.resolveConflict ?? ((path, content, target, worktree) => defaultLlmResolve(path, content, target, { worktreePath: worktree, mode: 'on' })))(file, conflicted, item.head, temp!);
|
|
217
|
+
}, mergeGit);
|
|
218
|
+
if (resolved.status === 'conflict-unresolved') {
|
|
219
|
+
verdict.verdict = 'conflict';
|
|
220
|
+
verdict.detail = resolved.failedFile === NEXT_MD_PATH ? 'release/next.md conflict is not append-only'
|
|
221
|
+
: `${resolved.failedFile ?? 'merge conflict'}: ${resolved.reason ?? 'context resolution failed'}`;
|
|
222
|
+
} else if (resolved.status === 'error') {
|
|
223
|
+
throw new Error(`git merge failed: ${resolved.errorStep}: ${resolved.errorDetail ?? 'unknown error'}`);
|
|
224
|
+
}
|
|
225
|
+
integrated = resolved.status === 'llm-resolved';
|
|
226
|
+
resolvedNextMd = integrated && resolved.resolvedFiles?.includes(NEXT_MD_PATH) === true;
|
|
227
|
+
}
|
|
156
228
|
}
|
|
157
229
|
}
|
|
158
|
-
if (
|
|
230
|
+
if (integrated) {
|
|
159
231
|
if (verdict.tests.length) {
|
|
160
232
|
if (verdict.tests.some((file) => !existsSync(join(temp!, file)))) throw new Error('changed test path unavailable');
|
|
161
233
|
run('bun', ['install', '--frozen-lockfile'], temp);
|
|
@@ -174,7 +246,7 @@ export async function runL8ShadowQueue(cwd: string, deps: L8ShadowQueueDeps = {}
|
|
|
174
246
|
} else {
|
|
175
247
|
verdict.verdict = 'pass';
|
|
176
248
|
}
|
|
177
|
-
if (
|
|
249
|
+
if (resolvedNextMd) {
|
|
178
250
|
verdict.detail = verdict.detail ? `release/next.md auto-resolved; ${verdict.detail}` : 'release/next.md auto-resolved';
|
|
179
251
|
}
|
|
180
252
|
}
|
|
@@ -434,7 +434,7 @@ function resolveDefaultBranchTarget(
|
|
|
434
434
|
* 🔑 실패일 때만 두 칸을 더한다 — 성공 경로 payload 를 바꾸면 기존 소비자가 흔들린다.
|
|
435
435
|
* `moduleLoadFailure` 는 ***「산출이 상했다」와 「그 순간 트리가 불일치했다」***를 가르는 값이다.
|
|
436
436
|
*/
|
|
437
|
-
type PostSyncStatus = 'merged' | 'llm-resolved';
|
|
437
|
+
type PostSyncStatus = 'merged' | 'llm-resolved' | 'deterministic-resolved';
|
|
438
438
|
|
|
439
439
|
type PostSyncGatePolicy = {
|
|
440
440
|
exempted: boolean;
|
|
@@ -495,7 +495,7 @@ function postSyncGatePolicy(
|
|
|
495
495
|
&& !introduced && childResponsibility === 'none'
|
|
496
496
|
&& syncStatus === 'llm-resolved';
|
|
497
497
|
const exempted = !passed
|
|
498
|
-
&& (timeoutOnly || (!introduced && childResponsibility === 'none' && syncStatus === 'merged'));
|
|
498
|
+
&& (timeoutOnly || (!introduced && childResponsibility === 'none' && (syncStatus === 'merged' || syncStatus === 'deterministic-resolved')));
|
|
499
499
|
return {
|
|
500
500
|
exempted,
|
|
501
501
|
exemptionWithheld,
|
|
@@ -2256,6 +2256,8 @@ export interface SelfImplementResult {
|
|
|
2256
2256
|
branch?: string;
|
|
2257
2257
|
gate?: SelfImplementGateResult;
|
|
2258
2258
|
review?: SelfImplementReview;
|
|
2259
|
+
/** Worktree commit observed while this review ran; not inferred from the later PR head. */
|
|
2260
|
+
reviewedHeadCommit?: string;
|
|
2259
2261
|
completionDisposition?: SelfImplementCompletionDisposition;
|
|
2260
2262
|
/** Explicit supervisor rework verdict retained independently of workflow execution verdicts. */
|
|
2261
2263
|
supervisorVerdict?: ReworkBudgetVerdict;
|
|
@@ -2362,7 +2364,18 @@ function truncatePrTitle(text: string): string {
|
|
|
2362
2364
|
return points.length <= 72 ? text : `${points.slice(0, 69).join('')}...`;
|
|
2363
2365
|
}
|
|
2364
2366
|
|
|
2365
|
-
export function
|
|
2367
|
+
export function goalChecklistIds(feature: string, goalFile?: string): string[] {
|
|
2368
|
+
const document = goalFile ? goalDocumentForReleaseNote(goalFile) : feature;
|
|
2369
|
+
const declaration = document.split(/\r?\n/).find((line) => line.startsWith('- 칸 id: '));
|
|
2370
|
+
const declared = declaration?.slice('- 칸 id: '.length).trim();
|
|
2371
|
+
if (declared && declared !== 'not declared') {
|
|
2372
|
+
return [...new Set(declared.split(' · ').map((id) => id.trim()).filter(Boolean))];
|
|
2373
|
+
}
|
|
2374
|
+
const firstLine = feature.split(/\r?\n/, 1)[0] ?? '';
|
|
2375
|
+
return [...new Set(Array.from(firstLine.matchAll(/\b[A-Z][A-Z0-9]+-[A-Z0-9]+\b/g), (match) => match[0]))];
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
function prTitleWithoutChecklistId(feature: string): string {
|
|
2366
2379
|
const proseTitle = parseAskProseTitle(feature);
|
|
2367
2380
|
if (proseTitle) return truncatePrTitle(proseTitle);
|
|
2368
2381
|
|
|
@@ -2405,6 +2418,13 @@ export function prTitle(feature: string): string {
|
|
|
2405
2418
|
return truncatePrTitle(firstFileName);
|
|
2406
2419
|
}
|
|
2407
2420
|
|
|
2421
|
+
export function prTitle(feature: string, goalFile?: string): string {
|
|
2422
|
+
const title = prTitleWithoutChecklistId(feature);
|
|
2423
|
+
const id = goalChecklistIds(feature, goalFile)[0];
|
|
2424
|
+
if (!id) return title;
|
|
2425
|
+
return truncatePrTitle(title.startsWith(`${id}: `) ? title : `${id}: ${title}`);
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2408
2428
|
const BLOCKED_DRAFT_PR_HUMAN_JUDGMENT_ANCHOR = '## 사람 판단 필요';
|
|
2409
2429
|
|
|
2410
2430
|
/** 사람이 읽는 중단 사유 상한. 관측 `reason.slice(0, 240)` 과 같은 길이라 표시가 앞부분을 버리지 않는다. */
|
|
@@ -2665,6 +2685,7 @@ function prBody(feature: string, implSummary: string, gateLog?: string, review?:
|
|
|
2665
2685
|
? ['', '## 감독 수용 기준 완화', `- 대상: ${planRevision.relaxation.target}`, `- 이전: ${planRevision.relaxation.expected}`, `- 완화: ${planRevision.relaxation.replacement}`, `- 이유: ${planRevision.reason}`, `- 적용: ${planRevision.application?.status ?? 'failed'}${planRevision.application?.detail ? ` (${planRevision.application.detail})` : ''}`, ...(planRevision.disposition ? [`- 충돌 처분: ${planRevision.disposition}`] : [])]
|
|
2666
2686
|
: [];
|
|
2667
2687
|
return [
|
|
2688
|
+
`칸: ${goalChecklistIds(feature, goalFile).join(' · ') || '없음'}`,
|
|
2668
2689
|
'## 요청',
|
|
2669
2690
|
...(goalFile ? [`- 골 파일: ${formatGoalFileForPrBody(goalFile)}`] : []),
|
|
2670
2691
|
feature.trim(),
|
|
@@ -2882,6 +2903,7 @@ export function formatBlockedDraftClassificationSection(
|
|
|
2882
2903
|
|
|
2883
2904
|
function blockedDraftPrBody(feature: string, implSummary: string, state: BlockedDraftPrState, goalFile?: string): string {
|
|
2884
2905
|
return [
|
|
2906
|
+
`칸: ${goalChecklistIds(feature, goalFile).join(' · ') || '없음'}`,
|
|
2885
2907
|
BLOCKED_DRAFT_PR_HUMAN_JUDGMENT_ANCHOR,
|
|
2886
2908
|
state.salvageStatusExpected
|
|
2887
2909
|
? BLOCKED_DRAFT_PR_SALVAGE_STATUS_INSTRUCTION
|
|
@@ -3638,7 +3660,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
3638
3660
|
}
|
|
3639
3661
|
try {
|
|
3640
3662
|
const pushed = opts.seams.preserveBlockedBranch
|
|
3641
|
-
? await opts.seams.preserveBlockedBranch({ cwd: worktreePath, branch, title: prTitle(opts.feature), commit: preSyncCommit })
|
|
3663
|
+
? await opts.seams.preserveBlockedBranch({ cwd: worktreePath, branch, title: prTitle(opts.feature, opts.goalFile), commit: preSyncCommit })
|
|
3642
3664
|
: runGitCommand(worktreePath, ['push', '-u', 'origin', `${preSyncCommit}:refs/heads/${branch}`]).status === 0;
|
|
3643
3665
|
if (!pushed) throw new Error('branch push failed');
|
|
3644
3666
|
// The worktree is left untouched: a late merge may still be writing it. The harvest source is the pinned commit.
|
|
@@ -4963,7 +4985,7 @@ async function runSelfImplementInner(
|
|
|
4963
4985
|
role: describeRole({ role: 'executor', executorKind: 'self' }),
|
|
4964
4986
|
entry: siEntry, capabilities: [...siCaps.active], enhanceActive,
|
|
4965
4987
|
});
|
|
4966
|
-
progress('start', `🔨 self-implement 시작 — ${prTitle(opts.feature)}`);
|
|
4988
|
+
progress('start', `🔨 self-implement 시작 — ${prTitle(opts.feature, opts.goalFile)}`);
|
|
4967
4989
|
|
|
4968
4990
|
let sessionId: string | undefined;
|
|
4969
4991
|
if (opts.parentSessionId && s.forkSession) {
|
|
@@ -5202,6 +5224,7 @@ async function runSelfImplementInner(
|
|
|
5202
5224
|
/** GATE-ENV-RETRY: 재시도 뒤에도 남은 환경 결손의 종류. 정의되면 이 라운드의 gate 실패는 «수확 대기»로 끝난다. */
|
|
5203
5225
|
let gateEnvDeficitKind: GateEnvDeficitKind | undefined;
|
|
5204
5226
|
let review: SelfImplementReview | undefined;
|
|
5227
|
+
let reviewedHeadCommit: string | undefined;
|
|
5205
5228
|
/** Review-budget leftovers and the body that carried them, so a later merge drafts from the same text. */
|
|
5206
5229
|
let preparedReviewBudgetPrBody: string | undefined;
|
|
5207
5230
|
const reviewMustFixCountHistory: number[] = [];
|
|
@@ -5791,11 +5814,11 @@ async function runSelfImplementInner(
|
|
|
5791
5814
|
throw new Error(`worktree not found: ${wt.path}`);
|
|
5792
5815
|
}
|
|
5793
5816
|
if (s.preserveBlockedBranch) {
|
|
5794
|
-
pushed = await s.preserveBlockedBranch({ cwd: wt.path, branch: wt.branch, title: prTitle(opts.feature) });
|
|
5817
|
+
pushed = await s.preserveBlockedBranch({ cwd: wt.path, branch: wt.branch, title: prTitle(opts.feature, opts.goalFile) });
|
|
5795
5818
|
if (!pushed) throw new Error('브랜치 push가 성공하지 않았음');
|
|
5796
5819
|
} else {
|
|
5797
5820
|
preservationErrorStep = 'commit';
|
|
5798
|
-
const commit = commitWorktree(wt.path, prTitle(opts.feature));
|
|
5821
|
+
const commit = commitWorktree(wt.path, prTitle(opts.feature, opts.goalFile));
|
|
5799
5822
|
if (!commit.ok && !/nothing to commit/.test(commit.out)) throw new Error(commit.out);
|
|
5800
5823
|
committed = commit.ok;
|
|
5801
5824
|
if (runGitCommand(wt.path, ['remote', 'get-url', 'origin']).status === 0) {
|
|
@@ -5875,10 +5898,10 @@ async function runSelfImplementInner(
|
|
|
5875
5898
|
let salvageError: string | undefined;
|
|
5876
5899
|
try {
|
|
5877
5900
|
if (s.preserveBlockedBranch) {
|
|
5878
|
-
if (!await s.preserveBlockedBranch({ cwd: wt.path, branch: salvageBranch, title: prTitle(opts.feature) })) throw new Error('수확 가지 push가 성공하지 않았음');
|
|
5901
|
+
if (!await s.preserveBlockedBranch({ cwd: wt.path, branch: salvageBranch, title: prTitle(opts.feature, opts.goalFile) })) throw new Error('수확 가지 push가 성공하지 않았음');
|
|
5879
5902
|
} else {
|
|
5880
5903
|
if (!existsSync(wt.path)) throw new Error(`worktree not found: ${wt.path}`);
|
|
5881
|
-
const commit = commitWorktree(wt.path, prTitle(opts.feature));
|
|
5904
|
+
const commit = commitWorktree(wt.path, prTitle(opts.feature, opts.goalFile));
|
|
5882
5905
|
if (!commit.ok && !/nothing to commit/.test(commit.out)) throw new Error(`로컬 커밋 실패: ${commit.out}`);
|
|
5883
5906
|
const push = runGitCommand(wt.path, ['push', 'origin', `HEAD:refs/heads/${salvageBranch}`]);
|
|
5884
5907
|
if (push.status !== 0) throw new Error(push.stderr || push.stdout || 'git push failed');
|
|
@@ -5915,10 +5938,10 @@ async function runSelfImplementInner(
|
|
|
5915
5938
|
let pushed = false;
|
|
5916
5939
|
try {
|
|
5917
5940
|
if (s.preserveBlockedBranch) {
|
|
5918
|
-
pushed = await s.preserveBlockedBranch({ cwd: wt.path, branch: wt.branch, title: prTitle(opts.feature) });
|
|
5941
|
+
pushed = await s.preserveBlockedBranch({ cwd: wt.path, branch: wt.branch, title: prTitle(opts.feature, opts.goalFile) });
|
|
5919
5942
|
} else {
|
|
5920
5943
|
if (commitFirst) {
|
|
5921
|
-
const commit = commitWorktree(wt.path, prTitle(opts.feature));
|
|
5944
|
+
const commit = commitWorktree(wt.path, prTitle(opts.feature, opts.goalFile));
|
|
5922
5945
|
if (!commit.ok && !/nothing to commit/.test(commit.out)) throw new Error(`로컬 브랜치 커밋 실패: ${commit.out}`);
|
|
5923
5946
|
}
|
|
5924
5947
|
const push = runGitCommand(wt.path, ['push', '-u', 'origin', `HEAD:refs/heads/${wt.branch}`]);
|
|
@@ -5942,7 +5965,7 @@ async function runSelfImplementInner(
|
|
|
5942
5965
|
if (stage === 'review-blocked' && podReviewBlockedDraft) {
|
|
5943
5966
|
let commitError: string | undefined;
|
|
5944
5967
|
try {
|
|
5945
|
-
const commit = commitWorktree(wt.path, prTitle(opts.feature));
|
|
5968
|
+
const commit = commitWorktree(wt.path, prTitle(opts.feature, opts.goalFile));
|
|
5946
5969
|
if (!commit.ok && !/nothing to commit/.test(commit.out)) commitError = commit.out.trim() || 'git commit failed';
|
|
5947
5970
|
} catch (error) {
|
|
5948
5971
|
commitError = safeErrorDescription(error);
|
|
@@ -5954,7 +5977,7 @@ async function runSelfImplementInner(
|
|
|
5954
5977
|
let pr: { url: string; number: number };
|
|
5955
5978
|
try {
|
|
5956
5979
|
pr = await withStepTimeout(s.openPr({
|
|
5957
|
-
title: prTitle(opts.feature),
|
|
5980
|
+
title: prTitle(opts.feature, opts.goalFile),
|
|
5958
5981
|
body: assembled.body,
|
|
5959
5982
|
head: wt.branch,
|
|
5960
5983
|
...(opts.base ? { base: opts.base } : {}),
|
|
@@ -6110,7 +6133,7 @@ async function runSelfImplementInner(
|
|
|
6110
6133
|
);
|
|
6111
6134
|
preparedReviewBudgetPrBody = preparedPrBody.body;
|
|
6112
6135
|
const pr = await withStepTimeout(s.openPr({
|
|
6113
|
-
title: prTitle(opts.feature),
|
|
6136
|
+
title: prTitle(opts.feature, opts.goalFile),
|
|
6114
6137
|
body: preparedPrBody.body,
|
|
6115
6138
|
head: wt.branch,
|
|
6116
6139
|
...(opts.base ? { base: opts.base } : {}),
|
|
@@ -6826,7 +6849,7 @@ async function runSelfImplementInner(
|
|
|
6826
6849
|
&& targets.some((target) => changedFileIsInsideDeclaredTarget(path, target)));
|
|
6827
6850
|
if (eligible.length) {
|
|
6828
6851
|
inspectStaleReverts(revertGuardTargetPaths(opts.feature, goalDocument));
|
|
6829
|
-
const message = `self-implement: ${prTitle(opts.feature)}`;
|
|
6852
|
+
const message = `self-implement: ${prTitle(opts.feature, opts.goalFile)}`;
|
|
6830
6853
|
let outcome: { ok: boolean; out: string };
|
|
6831
6854
|
try {
|
|
6832
6855
|
outcome = (s.commitFinishedChildWork ?? ((cwd, paths, title) => {
|
|
@@ -7347,6 +7370,7 @@ async function runSelfImplementInner(
|
|
|
7347
7370
|
lastReviewContext = reviewContext;
|
|
7348
7371
|
reviewIntent = buildReviewIntent(reviewContext);
|
|
7349
7372
|
|
|
7373
|
+
reviewedHeadCommit = undefined;
|
|
7350
7374
|
if (s.reviewDiff) {
|
|
7351
7375
|
try {
|
|
7352
7376
|
const stoppedBeforeReview = honorParentSoftStop('review');
|
|
@@ -7356,7 +7380,18 @@ async function runSelfImplementInner(
|
|
|
7356
7380
|
}
|
|
7357
7381
|
onNodeEntry(node('review'), round);
|
|
7358
7382
|
progress('reviewing', '내부 리뷰 (리뷰어 비평)…');
|
|
7383
|
+
const readReviewHead = s.headCommit ?? ((cwd: string) => {
|
|
7384
|
+
const commit = runGitCommand(cwd, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8', timeout: 10_000 });
|
|
7385
|
+
return commit.status === 0 ? commit.stdout.trim() : undefined;
|
|
7386
|
+
});
|
|
7387
|
+
const headBeforeReview = readReviewHead(wt.path);
|
|
7388
|
+
const statusBeforeReview = runGitCommand(wt.path, ['status', '--porcelain'], { encoding: 'utf8', timeout: 10_000 });
|
|
7359
7389
|
review = await withStepTimeout(s.reviewDiff(wt.path, reviewContext), T.review, 'review');
|
|
7390
|
+
const statusAfterReview = runGitCommand(wt.path, ['status', '--porcelain'], { encoding: 'utf8', timeout: 10_000 });
|
|
7391
|
+
reviewedHeadCommit = review.reviewed && headBeforeReview && /^[0-9a-f]{40}$/i.test(headBeforeReview)
|
|
7392
|
+
&& statusBeforeReview.status === 0 && statusBeforeReview.stdout.trim() === ''
|
|
7393
|
+
&& statusAfterReview.status === 0 && statusAfterReview.stdout.trim() === ''
|
|
7394
|
+
&& headBeforeReview === readReviewHead(wt.path) ? headBeforeReview : undefined;
|
|
7360
7395
|
} catch (error) {
|
|
7361
7396
|
finalizeSupervisorDeliveries(error instanceof StepTimeoutError ? 'next-round-review-timeout' : 'next-round-review-failed');
|
|
7362
7397
|
throw error;
|
|
@@ -7595,6 +7630,7 @@ async function runSelfImplementInner(
|
|
|
7595
7630
|
? { completionDisposition }
|
|
7596
7631
|
: {};
|
|
7597
7632
|
const reviewReal = !!review?.reviewed;
|
|
7633
|
+
const reviewedHeadResult = reviewReal && reviewedHeadCommit ? { reviewedHeadCommit } : {};
|
|
7598
7634
|
const reviewDiffComplete = review?.diffTruncated === false;
|
|
7599
7635
|
const requiredEvidenceMissing = evidenceCoverage !== undefined && evidenceCoverage.uncoveredCount > 0;
|
|
7600
7636
|
const { latestSignalIncomplete } = signalIncompleteState(roundClassifications);
|
|
@@ -7680,7 +7716,7 @@ async function runSelfImplementInner(
|
|
|
7680
7716
|
try { goalDocument = readFileSync(opts.goalFile, 'utf8'); } catch { /* no documented targets */ }
|
|
7681
7717
|
}
|
|
7682
7718
|
inspectStaleReverts(revertGuardTargetPaths(opts.feature, goalDocument));
|
|
7683
|
-
s.commitWork(wt.path, prTitle(opts.feature));
|
|
7719
|
+
s.commitWork(wt.path, prTitle(opts.feature, opts.goalFile));
|
|
7684
7720
|
// ⛔⭐ resume 경로와 «같은» 해석기를 쓴다 — 두 자리가 갈리면 같은 원인이 다른 얼굴로 나온다.
|
|
7685
7721
|
const resolvedTarget = resolveDefaultBranchTarget(s.defaultBranchRef ?? defaultBranchRef, wt.path);
|
|
7686
7722
|
const mergeTarget = resolvedTarget.target;
|
|
@@ -7725,9 +7761,9 @@ async function runSelfImplementInner(
|
|
|
7725
7761
|
&& await preserveMergeHarvest(sync.reason, sync.inputChars);
|
|
7726
7762
|
return { ok: false, stage: 'merge-conflict', node: 'main-sync', ...resolveRunOutcome({ termination: 'abandoned' }), ...decisionSignalResult, sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...(harvestable ? { harvestable: true } : {}), detail: harvestable ? sync.reason === 'conflict-input-too-large' ? `충돌 큼 — 사람 수확 (${wt.branch}, 입력 ${sync.inputChars}자)` : `충돌 해결 중단 — 사람 수확 (${wt.branch})` : `pre-PR main sync ${sync.status}` };
|
|
7727
7763
|
}
|
|
7728
|
-
if (sync.status === 'llm-resolved' || sync.status === 'merged') {
|
|
7764
|
+
if (sync.status === 'llm-resolved' || sync.status === 'merged' || sync.status === 'deterministic-resolved') {
|
|
7729
7765
|
preservationBase = opts.base ?? mergeTarget;
|
|
7730
|
-
progress('gating', sync.status === 'llm-resolved'
|
|
7766
|
+
progress('gating', sync.status === 'llm-resolved' || sync.status === 'deterministic-resolved'
|
|
7731
7767
|
? `${mergeTarget} 충돌해결됨 — ${formatLlmMergeOutcome(sync)} — 통합 결과 full 재-gate…`
|
|
7732
7768
|
: `${mergeTarget} 정합됨 — 통합 결과 full 재-gate…`);
|
|
7733
7769
|
onNodeEntry('regate', round);
|
|
@@ -7880,7 +7916,7 @@ async function runSelfImplementInner(
|
|
|
7880
7916
|
let pr: { url: string; number: number };
|
|
7881
7917
|
try {
|
|
7882
7918
|
pr = await withStepTimeout(s.openPr({
|
|
7883
|
-
title: prTitle(opts.feature),
|
|
7919
|
+
title: prTitle(opts.feature, opts.goalFile),
|
|
7884
7920
|
body: preparedPrBody.body,
|
|
7885
7921
|
head: wt.branch,
|
|
7886
7922
|
...(opts.base ? { base: opts.base } : {}),
|
|
@@ -7902,6 +7938,11 @@ async function runSelfImplementInner(
|
|
|
7902
7938
|
progress('aborted', formatPrOpenFailedProgress(location, prErrorFirstLine));
|
|
7903
7939
|
throw new Error(`${location}\n${prError}`);
|
|
7904
7940
|
}
|
|
7941
|
+
// PR creation may commit generated body/release notes or sync the base after review. Never bind an old review to that new head.
|
|
7942
|
+
if (reviewedHeadCommit) {
|
|
7943
|
+
const current = runGitCommand(wt.path, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8', timeout: 10_000 });
|
|
7944
|
+
if (current.status !== 0 || current.stdout.trim() !== reviewedHeadCommit) delete reviewedHeadResult.reviewedHeadCommit;
|
|
7945
|
+
}
|
|
7905
7946
|
observe('pr-opened', {
|
|
7906
7947
|
url: pr.url,
|
|
7907
7948
|
number: pr.number,
|
|
@@ -7951,7 +7992,7 @@ async function runSelfImplementInner(
|
|
|
7951
7992
|
}
|
|
7952
7993
|
recordMergeDecision();
|
|
7953
7994
|
progress('pr-opened', `OP 승인 대기 (#${pr.number}): ${releaseHoldPath}`);
|
|
7954
|
-
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, detail: `OP approval required: ${releaseHoldPath}` };
|
|
7995
|
+
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 } : {}), ...reviewedHeadResult, mergeReason, prUrl: pr.url, prNumber: pr.number, detail: `OP approval required: ${releaseHoldPath}` };
|
|
7955
7996
|
}
|
|
7956
7997
|
if (!releasePathInspectionError && labels?.includes(AUTO_REVIEW_LABEL)) {
|
|
7957
7998
|
try {
|
|
@@ -7969,7 +8010,7 @@ async function runSelfImplementInner(
|
|
|
7969
8010
|
: 'release-path-inspection-failed';
|
|
7970
8011
|
recordMergeDecision();
|
|
7971
8012
|
progress('pr-opened', `OP 승인 대기 (#${pr.number}): PR 경로 조회 실패`);
|
|
7972
|
-
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 };
|
|
8013
|
+
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 } : {}), ...reviewedHeadResult, mergeReason, prUrl: pr.url, prNumber: pr.number };
|
|
7973
8014
|
}
|
|
7974
8015
|
|
|
7975
8016
|
if (canAuto && (s.mergePr || opts.mergeByHost)) {
|
|
@@ -8019,12 +8060,12 @@ async function runSelfImplementInner(
|
|
|
8019
8060
|
// 판정(mergeReason)이 아니라 «집행 직전 가드»가 막았으므로 그 사실을 값으로 말한다.
|
|
8020
8061
|
// ⛔ 안 그러면 마지막 줄이 침묵하고, 그건 이 착지가 없애려던 상태다(리뷰 must-fix 2R).
|
|
8021
8062
|
const guardSkipReason = mergeSkipReason ?? (guardFailure ? 'merge-guard-unevaluated' : 'docs-deletion-threshold');
|
|
8022
|
-
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: guardSkipReason, prUrl: pr.url, prNumber: pr.number, detail };
|
|
8063
|
+
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 } : {}), ...reviewedHeadResult, mergeReason: guardSkipReason, prUrl: pr.url, prNumber: pr.number, detail };
|
|
8023
8064
|
}
|
|
8024
8065
|
if (opts.mergeByHost) {
|
|
8025
8066
|
observe('merge-ready', { number: pr.number, checkedHeadCommit });
|
|
8026
8067
|
progress('pr-opened', `호스트 재게이트 대기 (#${pr.number})`);
|
|
8027
|
-
return { ok: true, stage: 'merge-ready', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), checkedHeadCommit: checkedHeadCommit!, prUrl: pr.url, prNumber: pr.number };
|
|
8068
|
+
return { ok: true, stage: 'merge-ready', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...reviewedHeadResult, checkedHeadCommit: checkedHeadCommit!, prUrl: pr.url, prNumber: pr.number };
|
|
8028
8069
|
}
|
|
8029
8070
|
// Landing freeze: the in-flight marker is written before the freeze is read, so a `freeze on` racing this merge waits.
|
|
8030
8071
|
// The resume sweep merges from the repository that owns the run worktree (the worktree itself may be cleaned up).
|
|
@@ -8036,13 +8077,13 @@ async function runSelfImplementInner(
|
|
|
8036
8077
|
};
|
|
8037
8078
|
const landing = admitLandingMerge(heldEntry, undefined, {}, { prNumber: pr.number, repoRoot: owningRepoRoot(wt.path) ?? wt.path, ...(checkedHeadCommit ? { headCommit: checkedHeadCommit } : {}) });
|
|
8038
8079
|
if (landing.kind === 'held' && !(checkedHeadCommit && /^[0-9a-f]{40}$/i.test(checkedHeadCommit))) {
|
|
8039
|
-
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: 'merge-guard-unevaluated', prUrl: pr.url, prNumber: pr.number, detail: 'freeze: checked head SHA unavailable; PR left ready for review' };
|
|
8080
|
+
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 } : {}), ...reviewedHeadResult, mergeReason: 'merge-guard-unevaluated', prUrl: pr.url, prNumber: pr.number, detail: 'freeze: checked head SHA unavailable; PR left ready for review' };
|
|
8040
8081
|
}
|
|
8041
8082
|
if (landing.kind !== 'merge') {
|
|
8042
8083
|
const detail = landing.kind === 'held' ? landingFreezeMessage(landing.freeze) : 'freeze lifted; a resume sweep is merging this PR';
|
|
8043
8084
|
debug.log('harness.merge', 'frozen', { pr: pr.number, ...(landing.kind === 'held' ? { reason: landing.freeze.reason, until: landing.freeze.until } : { resumedElsewhere: true }) });
|
|
8044
8085
|
progress('pr-opened', `#${pr.number} ready · ${detail}`);
|
|
8045
|
-
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: landing.kind === 'held' ? 'frozen' : 'resumed-elsewhere', prUrl: pr.url, prNumber: pr.number, detail };
|
|
8086
|
+
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 } : {}), ...reviewedHeadResult, mergeReason: landing.kind === 'held' ? 'frozen' : 'resumed-elsewhere', prUrl: pr.url, prNumber: pr.number, detail };
|
|
8046
8087
|
}
|
|
8047
8088
|
// The marker and claim live until the merge call itself settles — a step timeout does not stop a merge already
|
|
8048
8089
|
// sent — and anything that throws before the merge call is made releases them.
|
|
@@ -8132,10 +8173,10 @@ async function runSelfImplementInner(
|
|
|
8132
8173
|
// 그대로 두면 마지막 산출 줄이 «침묵»한다 — 이 착지가 없애려던 바로 그 상태다(리뷰 must-fix).
|
|
8133
8174
|
// ⇒ 판정이 아니라 «집행»에서 막힌 것이므로 그 사실을 별도 값으로 말한다.
|
|
8134
8175
|
const mergeAttemptSkipReason = mergeSkipReason ?? 'merge-attempt-failed';
|
|
8135
|
-
return { ok: true, stage: 'pr-opened', node: 'merge', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason: mergeAttemptSkipReason, merged: false, prUrl: pr.url, prNumber: pr.number, ...(m.detail ? { detail: m.detail } : {}) };
|
|
8176
|
+
return { ok: true, stage: 'pr-opened', node: 'merge', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...reviewedHeadResult, mergeReason: mergeAttemptSkipReason, merged: false, prUrl: pr.url, prNumber: pr.number, ...(m.detail ? { detail: m.detail } : {}) };
|
|
8136
8177
|
}
|
|
8137
8178
|
progress('pr-opened', `✅ PR 개설 (#${pr.number})`);
|
|
8138
|
-
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 } : {}), ...(lastSupervisorReason ? { supervisorReason: lastSupervisorReason } : {}), ...(mergeSkipReason ? { mergeReason: mergeSkipReason } : {}), prUrl: pr.url, prNumber: pr.number };
|
|
8179
|
+
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 } : {}), ...reviewedHeadResult, ...(lastSupervisorReason ? { supervisorReason: lastSupervisorReason } : {}), ...(mergeSkipReason ? { mergeReason: mergeSkipReason } : {}), prUrl: pr.url, prNumber: pr.number };
|
|
8139
8180
|
}
|
|
8140
8181
|
|
|
8141
8182
|
/** 결정적 요청 거부 — 같은 요청을 다시 보내면 «같은» 답이 온다(BACKLOG B6).
|
|
@@ -986,7 +986,7 @@ export interface DefaultSeamsOptions {
|
|
|
986
986
|
runAndroidUnitTestGate?: (io: { args: readonly string[]; cwd: string }) => number;
|
|
987
987
|
runIosUnitTestGate?: (io: { args: readonly string[]; cwd: string }) => number;
|
|
988
988
|
/** Source-policy gate adapter for focused seam tests. */
|
|
989
|
-
runHarnessPolicyGates?: typeof runHarnessPolicyGates
|
|
989
|
+
runHarnessPolicyGates?: (input: Parameters<typeof runHarnessPolicyGates>[0]) => Awaited<ReturnType<typeof runHarnessPolicyGates>> | ReturnType<typeof runHarnessPolicyGates>;
|
|
990
990
|
runExposeGate?: typeof runExposeGate;
|
|
991
991
|
/** Dependency-wide validation adapter for focused seam tests. */
|
|
992
992
|
runDependencyChangeGate?: typeof runDependencyChangeGate;
|
|
@@ -1020,6 +1020,8 @@ export interface DefaultSeamsOptions {
|
|
|
1020
1020
|
* agent-substrate·staged 하니스와 동일 엔진) 배선. 미주입 시 reviewDiff 미노출(gate 통과=바로 병합결정·
|
|
1021
1021
|
* 리뷰 스킵). dev-harness 의 llmReview 와 동일 형태(streamLLM 래퍼). */
|
|
1022
1022
|
llmReview?: (prompt: string) => Promise<string>;
|
|
1023
|
+
/** Optional independent reviewer for reviewDiff only; other judgment calls retain llmReview. */
|
|
1024
|
+
subscriptionReviewLLM?: (prompt: string) => Promise<string>;
|
|
1023
1025
|
/**
|
|
1024
1026
|
* 이 리뷰 심이 파일을 스스로 읽을 수 있나. **선택 선언** — 안 주면 reviewDiff 가 칸을 만들지 않고
|
|
1025
1027
|
* 오케스트레이터 관측은 `'unknown'` 이다(`false` 와 같은 값으로 접지 않는다).
|
|
@@ -2073,7 +2075,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
2073
2075
|
// tsc-깨짐(F1 refFacts 미정의)을 결정론 차단. 변경 전체(.ts·테스트 포함)를 tsc 로 검사(변경 경로만 카운트).
|
|
2074
2076
|
const tc = changedFileTypecheck(cwd, changedAll);
|
|
2075
2077
|
debug.log('self-implement', 'gate.tsc', { checked: tc.checked, passed: tc.passed, errors: tc.errors, exempted: tc.exempted, noInspectionReason: tc.noInspectionReason }, { level: tc.passed ? 'info' : 'warn' });
|
|
2076
|
-
const policy = (o.runHarnessPolicyGates ?? runHarnessPolicyGates)({ cwd, changedFiles: changedAll });
|
|
2078
|
+
const policy = await (o.runHarnessPolicyGates ?? runHarnessPolicyGates)({ cwd, changedFiles: changedAll });
|
|
2077
2079
|
debug.log('self-implement', 'gate.policy', {
|
|
2078
2080
|
passed: policy.passed, failures: policy.failures.map((failure) => failure.gate), skipped: policy.skipped,
|
|
2079
2081
|
});
|
|
@@ -2530,7 +2532,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
2530
2532
|
...(ctx.round !== undefined ? { round: ctx.round } : {}),
|
|
2531
2533
|
},
|
|
2532
2534
|
} : {}),
|
|
2533
|
-
}, o.llmReview!);
|
|
2535
|
+
}, o.subscriptionReviewLLM ?? o.llmReview!);
|
|
2534
2536
|
debug.log('self-implement', 'review.done', {
|
|
2535
2537
|
verdict: rr.verdict, reviewed: rr.reviewed ?? false, mustFix: rr.mustFix.length, shouldFix: rr.shouldFix.length,
|
|
2536
2538
|
gateEvidenceLines: evidenceNote?.split(/\r?\n/).length ?? 0,
|
|
@@ -111,6 +111,7 @@ export function singleRunAsJobResult(feature: string, r: SelfImplementResultWith
|
|
|
111
111
|
...(r.harvestable === true && r.branch ? { harvestable: true } : {}),
|
|
112
112
|
...(r.worktreePath ? { worktreePath: r.worktreePath } : {}),
|
|
113
113
|
...(r.prUrl ? { prUrl: r.prUrl } : {}),
|
|
114
|
+
...(r.prNumber !== undefined ? { prNumber: r.prNumber } : {}),
|
|
114
115
|
// TA-LIVE-REVIEW-POD — 작업 트리 없는 런(Pod)의 live review 가 «런이 낸 PR 머리»를 확인하는 근거.
|
|
115
116
|
...(r.checkedHeadCommit !== undefined ? { checkedHeadCommit: r.checkedHeadCommit } : {}),
|
|
116
117
|
...(r.merged !== undefined ? { merged: r.merged } : {}),
|
|
@@ -121,6 +122,9 @@ export function singleRunAsJobResult(feature: string, r: SelfImplementResultWith
|
|
|
121
122
|
...(r.decomposeProposal ? { decomposeProposal: r.decomposeProposal } : {}),
|
|
122
123
|
...(r.goalPlanRevision ? { goalPlanRevision: r.goalPlanRevision } : {}),
|
|
123
124
|
...(r.salvage !== undefined ? { salvage: r.salvage } : {}),
|
|
125
|
+
...(r.review?.reviewed === true && r.review.verdict === 'pass'
|
|
126
|
+
&& r.reviewedHeadCommit && /^[0-9a-f]{40}$/i.test(r.reviewedHeadCommit)
|
|
127
|
+
? { selfReview: { verdict: r.review.verdict, head: r.reviewedHeadCommit } } : {}),
|
|
124
128
|
...(r.review?.reviewed !== undefined ? { reviewed: r.review.reviewed } : {}),
|
|
125
129
|
...(r.review?.failureReason !== undefined ? { reviewReason: r.review.failureReason } : {}),
|
|
126
130
|
// ⭐ 대표 지시(2026-09-08) — 걸음을 슈퍼바이저까지 나른다.
|
|
@@ -65,6 +65,12 @@ const POSIX_SHELLS = new Set(['zsh', 'bash', 'fish', 'sh', 'dash', 'ksh']);
|
|
|
65
65
|
let cached: Record<string, string> | null = null;
|
|
66
66
|
let captured = false;
|
|
67
67
|
|
|
68
|
+
// Both the daemon warm path (nexus/index.ts) and PTY's first synchronous capture
|
|
69
|
+
// pass through here. Keep the existing capture events intact; these are additive timings.
|
|
70
|
+
function logBootPhase(phase: string, start: number, outcome: string): void {
|
|
71
|
+
debug.log('boot.phase', phase, { ms: performance.now() - start, outcome });
|
|
72
|
+
}
|
|
73
|
+
|
|
68
74
|
/** Parse `KEY=VALUE\n` output from `printenv`. Tolerates values that
|
|
69
75
|
* themselves contain `=`; does not attempt to unescape newlines
|
|
70
76
|
* (bash/zsh `printenv` emits them literally, which is fine for
|
|
@@ -121,10 +127,13 @@ function acceptCapture(stdout: string): Record<string, string> | null {
|
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
export function runPrintenvCapture(): Record<string, string> | null {
|
|
130
|
+
const seedStart = performance.now();
|
|
124
131
|
const seed = captureSeed();
|
|
132
|
+
logBootPhase('login-env.seed', seedStart, seed ? 'ready' : 'unavailable');
|
|
125
133
|
if (!seed) return null;
|
|
126
134
|
const { shell, seedEnv } = seed;
|
|
127
135
|
|
|
136
|
+
const shellStart = performance.now();
|
|
128
137
|
let res: ReturnType<typeof spawnSync>;
|
|
129
138
|
try {
|
|
130
139
|
res = spawnSync(shell, ['-l', '-i', '-c', 'printenv'], {
|
|
@@ -137,13 +146,14 @@ export function runPrintenvCapture(): Record<string, string> | null {
|
|
|
137
146
|
// OSC sequences would corrupt our TUI output otherwise.
|
|
138
147
|
});
|
|
139
148
|
} catch {
|
|
149
|
+
logBootPhase('login-env.shell', shellStart, 'error');
|
|
140
150
|
return null;
|
|
141
151
|
}
|
|
142
152
|
// ETIMEDOUT with status 0 = the shell outlived the timeout; never trust a late capture.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return
|
|
153
|
+
const parsed = !res.error && (res.status === 0 || res.status === null) && !res.signal
|
|
154
|
+
? acceptCapture(typeof res.stdout === 'string' ? res.stdout : '') : null;
|
|
155
|
+
logBootPhase('login-env.shell', shellStart, parsed ? 'ok' : 'failed');
|
|
156
|
+
return parsed;
|
|
147
157
|
}
|
|
148
158
|
|
|
149
159
|
/** Fill the login-env cache off the event loop (daemon boot). The first web-terminal spawn otherwise ran the
|
|
@@ -152,9 +162,12 @@ export function runPrintenvCapture(): Record<string, string> | null {
|
|
|
152
162
|
export async function warmCapturedEnv(spawn: typeof Bun.spawn = Bun.spawn): Promise<boolean> {
|
|
153
163
|
if (captured) return cached !== null;
|
|
154
164
|
if (process.env.ELANOUS_SKIP_LOGIN_ENV === '1' || process.env.TERM_PROGRAM === 'monad-agent-nested') return false;
|
|
165
|
+
const seedStart = performance.now();
|
|
155
166
|
const seed = captureSeed();
|
|
167
|
+
logBootPhase('login-env.seed', seedStart, seed ? 'ready' : 'unavailable');
|
|
156
168
|
if (!seed) return false;
|
|
157
169
|
const start = Date.now();
|
|
170
|
+
const shellStart = performance.now();
|
|
158
171
|
try {
|
|
159
172
|
const proc = spawn([seed.shell, '-l', '-i', '-c', 'printenv'], { env: seed.seedEnv as Record<string, string>, stdin: 'ignore', stdout: 'pipe', stderr: 'ignore' });
|
|
160
173
|
// A grandchild (e.g. a pyenv rehash) can keep the stdout pipe open after the shell is killed,
|
|
@@ -167,6 +180,7 @@ export async function warmCapturedEnv(spawn: typeof Bun.spawn = Bun.spawn): Prom
|
|
|
167
180
|
const result = await Promise.race([finished, timedOut]);
|
|
168
181
|
clearTimeout(timer);
|
|
169
182
|
const parsed = result && result[1] === 0 ? acceptCapture(result[0]) : null;
|
|
183
|
+
logBootPhase('login-env.shell', shellStart, parsed ? 'ok' : result ? 'failed' : 'timeout');
|
|
170
184
|
if (captured) return cached !== null; // a synchronous capture won the race
|
|
171
185
|
if (!parsed) return false;
|
|
172
186
|
cached = parsed;
|
|
@@ -174,6 +188,7 @@ export async function warmCapturedEnv(spawn: typeof Bun.spawn = Bun.spawn): Prom
|
|
|
174
188
|
debug.log('shell.envbootstrap.capture', 'warmed', { ok: true, ms: Date.now() - start, keyCount: Object.keys(parsed).length });
|
|
175
189
|
return true;
|
|
176
190
|
} catch {
|
|
191
|
+
logBootPhase('login-env.shell', shellStart, 'error');
|
|
177
192
|
return false;
|
|
178
193
|
}
|
|
179
194
|
}
|
package/src/steward/triage.ts
CHANGED
|
@@ -57,6 +57,8 @@ export interface StewardDeps {
|
|
|
57
57
|
cardStore?: StewardCardDeps['store'];
|
|
58
58
|
warn?: (message: string) => void;
|
|
59
59
|
ask?: StewardAsk;
|
|
60
|
+
/** Maximum time for each working-backwards wish draft in schedule (milliseconds). */
|
|
61
|
+
draftItemMs?: number;
|
|
60
62
|
launchSettings?: Pick<StewardSettings, 'mode' | 'launch' | 'maxParallel' | 'podPool'>;
|
|
61
63
|
launchCommand?: LaunchDeps['command'];
|
|
62
64
|
spawnLaunch?: LaunchDeps['spawn'];
|
|
@@ -413,20 +415,35 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
413
415
|
const wishes = rows.filter(row => present.has(row.issue) && row.capability === 'new-capability' && row.rung !== 'hitl');
|
|
414
416
|
const store = wishes.length ? deps.cardStore ?? new CardStore(root) : undefined;
|
|
415
417
|
const invalid = new Set<string>();
|
|
418
|
+
const carried = new Set<string>();
|
|
416
419
|
if (store) try {
|
|
417
420
|
recordTriageOnCards(wishes, issues, { root, store, now: deps.now });
|
|
418
421
|
for (const wish of wishes) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
422
|
+
const timedOut = Symbol('draft-timeout');
|
|
423
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
424
|
+
const timeout = new Promise<never>((_, reject) => {
|
|
425
|
+
timer = setTimeout(() => reject(timedOut), deps.draftItemMs ?? 120_000);
|
|
426
|
+
});
|
|
427
|
+
// The draft may finish without ever racing this promise (no ask needed) — never leave its rejection unhandled.
|
|
428
|
+
timeout.catch(() => undefined);
|
|
429
|
+
try {
|
|
430
|
+
// Bound the ask itself so a late answer cannot write to a store closed after this tick.
|
|
431
|
+
const ask: StewardAsk = (prompt, role) => Promise.race([(deps.ask ?? askSteward)(prompt, role), timeout]);
|
|
432
|
+
await recordWorkingBackwardsOnCards([wish], issues, store, ask);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
if (error instanceof InvalidWorkingBackwardsDraft) {
|
|
435
|
+
invalid.add(wish.issue);
|
|
436
|
+
debug.log('steward.stage', 'draft-invalid', { issue: wish.issue });
|
|
437
|
+
} else {
|
|
438
|
+
carried.add(wish.issue);
|
|
439
|
+
debug.log('steward.schedule', 'draft-carried', { issue: wish.issue, reason: error === timedOut ? 'timeout' : 'error' });
|
|
440
|
+
}
|
|
441
|
+
} finally { if (timer) clearTimeout(timer); }
|
|
425
442
|
}
|
|
426
443
|
} finally { if (!deps.cardStore) store.close(); }
|
|
427
|
-
if (invalid.size) rows = rows.map(row => invalid.has(row.issue)
|
|
444
|
+
if (invalid.size || carried.size) rows = rows.map(row => invalid.has(row.issue)
|
|
428
445
|
? { ...row, rung: 'hitl' as const, disposition: 'hitl' as const, hitlReason: 'other' as const, why: 'working-backwards draft invalid' }
|
|
429
|
-
: row);
|
|
446
|
+
: carried.has(row.issue) ? { ...row, deferred: true as const, disposition: 'wait' as const } : row);
|
|
430
447
|
}
|
|
431
448
|
for (const row of rows) try { debug.log('steward.schedule', 'decision', {
|
|
432
449
|
kind: row.disposition, target: row.issue, wouldAct: row.disposition === 'now' && liveLike(settings.mode),
|