elanous 0.2.14 → 0.2.15
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/LZcstaO9_NXypPFmujolv/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1307-6e7e0377c1db0bdc.js → 1307-33ffcc49ab45123b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-74383f890f075511.js → page-0e2c57fe6add4b2a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-e05a77df7e005b04.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-b8c49f6b4fa2df91.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-976813c91d7394ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-fb260b9283dbf646.js → page-28b9448091776c06.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-72d3e7ff35a8c954.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-60c7a8e1a4fe62ce.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-2a52b316cb143d9c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-c3ce483c8d5db370.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-96f074f6538e8a2c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-e97be5334d83a9f5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-955ec2da3be4c560.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-c1343f9499d86f3f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-8739a2e0ae793130.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-d23b12bb92242558.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c7fea475488201a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-04250f0e2c0d03df.js → page-e2ee2896811e98d0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-5efbf81124c474ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-85f1a1fc0a82738c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-8ac8d71c2adf9c8a.js → page-da6c08aae0f92768.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-06ee266953d9c879.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-153c2b3bcc89e8de.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-94dc383d3d54db1a.js → page-87a11e6a05f15199.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-b3759cc14da80908.js → page-7d4182964407c09c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-0d6d23b40263a7ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-f5597e894c02cd33.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-35bdeae2e2c17147.js → page-73735b64bfe1ae67.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-8fcec4c9ea2e7f70.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f7e631e9b4daf66e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-9817c7f9007466cc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-134c1bc762aee62c.js → page-97f9f43172a3a90e.js} +1 -1
- package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.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 -0
- package/apps/pwa/out/ceo/index.txt +33 -0
- 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/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/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/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -2
- package/package.json +1 -1
- package/scripts/brand/check.ts +7 -5
- package/scripts/ci-typecheck-changed.ts +50 -18
- package/scripts/docs/generate-graph-anatomy.ts +315 -0
- package/scripts/expose-rubric-suggest.ts +18 -12
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +30 -3
- package/scripts/marketing/generate-ax-brief.ts +140 -0
- package/scripts/marketing/generate-feature-map.ts +118 -0
- package/scripts/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/gate-node.ts +80 -14
- package/scripts/release-loop/vault-note-node.ts +240 -8
- package/scripts/release-story/draft.ts +57 -9
- package/scripts/rhythm/daily.ts +263 -76
- package/scripts/rhythm/weekly.ts +30 -9
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/src/agent-env/profile.ts +161 -0
- package/src/agent-substrate/pr-reviewer.ts +2 -0
- package/src/ask-user-question/pending-questions.ts +5 -0
- package/src/auth/github-app-token.ts +112 -13
- package/src/autopilot/build/llm-conflict-merge.ts +32 -6
- package/src/briefing/brief-items.ts +121 -0
- package/src/claims/claims-render.ts +14 -5
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/decisions-cli.ts +9 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/intake-cli.ts +46 -17
- package/src/cli/pod-cli.ts +2 -0
- package/src/cli/release-cli.ts +61 -27
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/context-bus/context-now-public.ts +16 -6
- package/src/context-bus/context-now.ts +3 -1
- package/src/context-bus/coord-events.ts +6 -4
- package/src/decisions/decision-cards.ts +33 -7
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/domains/alert-audit.ts +271 -0
- package/src/domains/core-tools.ts +4 -3
- package/src/domains/ops-facts-tool.ts +31 -1
- package/src/harness/harness-cli-command.ts +111 -15
- package/src/harness/harness-incidents.ts +2 -0
- package/src/harness/harness-pod-dispatch.ts +100 -11
- package/src/harness/harness-queue.ts +380 -50
- package/src/harness/helper-repair.ts +87 -0
- package/src/harness/helper-scan.ts +16 -4
- package/src/harness/launch-stamp.ts +236 -0
- package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
- package/src/harness/ledger-write-guard.ts +35 -0
- package/src/harness/pod-memory-policy.ts +15 -0
- package/src/harness/repository-name.ts +55 -0
- package/src/hq/fence-audit.ts +90 -0
- package/src/hq/hq.ts +62 -4
- package/src/hq/lease.ts +4 -4
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +31 -0
- package/src/index.ts +77 -7
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +37 -15
- package/src/intake-plane/front-classifier.ts +6 -0
- package/src/intake-plane/items.ts +3 -2
- package/src/intake-plane/lens.ts +177 -0
- package/src/intake-plane/route.ts +145 -1
- package/src/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
- package/src/launch-head/seat-goal-queue.ts +198 -0
- package/src/loops/budget.ts +36 -5
- package/src/loops/checker.ts +2 -2
- package/src/loops/orchestrator/author-depth.ts +65 -9
- package/src/loops/orchestrator/author-input-check.ts +8 -3
- package/src/loops/orchestrator/author-ledger.ts +3 -1
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +11 -4
- package/src/loops/orchestrator/traffic.ts +117 -11
- package/src/loops/registry.ts +142 -61
- package/src/maturity/feature-maturity.ts +2 -1
- package/src/maturity/tui-slash-availability.ts +31 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/http-server.ts +28 -14
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/schedules-read.ts +13 -0
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +6 -3
- package/src/release-loop/checklist.ts +13 -4
- package/src/release-loop/feature-store.ts +58 -31
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +86 -26
- package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
- package/src/seat-dispatch/seat-request-ledger.ts +133 -0
- package/src/seat-loop/seat-loop.ts +126 -10
- package/src/self-dev/dev-pipeline.ts +8 -4
- package/src/self-dev/draft-sweep.ts +90 -10
- package/src/self-dev/draft-triage-rules.ts +25 -8
- package/src/self-dev/orchestrate.ts +15 -1
- package/src/self-dev/review-provider-fallback.ts +6 -1
- package/src/self-dev/run-supervisor.ts +49 -2
- package/src/self-implement/frozen-merges.ts +3 -2
- package/src/self-implement/goal-author.ts +43 -5
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/heal-intake.ts +120 -2
- package/src/self-implement/host-regate.ts +9 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +187 -40
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +42 -7
- package/src/self-implement/run-ledger.ts +2 -0
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +1 -1
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +4 -1
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
- package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +60 -8
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +147 -36
- package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
- package/src/telegram.ts +25 -4
- package/src/user-config.ts +29 -5
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-e6b84ab07d3567d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-160089123a40dd91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e7e10cd3386222e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-58043db78ba519ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-357750dfb91bf960.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-f8da5dc6f3752276.js +0 -1
- package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → LZcstaO9_NXypPFmujolv}/_ssgManifest.js +0 -0
|
@@ -8,6 +8,20 @@ import { getDefaultLogStore } from '../mss/logging/log-store.js';
|
|
|
8
8
|
type Command = (bin: string, args: readonly string[], cwd: string) => { status: number | null; stdout: string; stderr: string };
|
|
9
9
|
export type L8MergeQueueInput = { number: number; cwd: string; matchHeadCommit: string };
|
|
10
10
|
export type L8MergeQueueResult = { merged: boolean; baseRefName?: string; mergeCommit?: string; detail?: string };
|
|
11
|
+
/** Read-only integration report. Never a landing decision and never a remote write. */
|
|
12
|
+
export type L8ChangedTestResult = { file: string; passed: boolean; pass: number; fail: number; detail?: string };
|
|
13
|
+
export type L8IntegrationEvaluation = {
|
|
14
|
+
number: number;
|
|
15
|
+
head: string;
|
|
16
|
+
base?: string;
|
|
17
|
+
/** True only when the candidate integrates (next.md-only conflicts resolved) and every changed test passed. */
|
|
18
|
+
feasible: boolean;
|
|
19
|
+
conflictingFiles: string[];
|
|
20
|
+
/** True when there is no conflict, or every conflict path is exactly release/next.md. */
|
|
21
|
+
conflictsLimitedToNextMd: boolean;
|
|
22
|
+
changedTests: L8ChangedTestResult[];
|
|
23
|
+
detail?: string;
|
|
24
|
+
};
|
|
11
25
|
export type L8MergeQueueDeps = {
|
|
12
26
|
command?: Command;
|
|
13
27
|
/** Must identify the same shared repository across all harness processes. */
|
|
@@ -195,6 +209,124 @@ export async function acquireL8MergeQueue(cwd: string): Promise<() => void> {
|
|
|
195
209
|
}
|
|
196
210
|
}
|
|
197
211
|
|
|
212
|
+
const TEST_PATH = /(?:^|\/)[^/]+\.(?:test|spec)\.(?:[cm]?[jt]sx?)$/;
|
|
213
|
+
|
|
214
|
+
function parseChangedTestCounts(output: string): { pass: number; fail: number; ran: number; files: number } | null {
|
|
215
|
+
const counts = /\b(\d+) pass\b[\s\S]*?\b(\d+) fail\b[\s\S]*?Ran ([1-9]\d*) tests? across ([1-9]\d*) files?/.exec(output);
|
|
216
|
+
if (!counts) return null;
|
|
217
|
+
return { pass: Number(counts[1]), fail: Number(counts[2]), ran: Number(counts[3]), files: Number(counts[4]) };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Report whether one pinned PR can integrate onto freshly fetched main.
|
|
222
|
+
* Resolves an append-only release/next.md conflict in a disposable worktree only.
|
|
223
|
+
* Never pushes, never calls a merge API, and never moves a remote ref.
|
|
224
|
+
*/
|
|
225
|
+
export async function evaluateL8Integration(input: L8MergeQueueInput, deps: L8MergeQueueDeps = {}): Promise<L8IntegrationEvaluation> {
|
|
226
|
+
const report: L8IntegrationEvaluation = {
|
|
227
|
+
number: input.number,
|
|
228
|
+
head: input.matchHeadCommit,
|
|
229
|
+
feasible: false,
|
|
230
|
+
conflictingFiles: [],
|
|
231
|
+
conflictsLimitedToNextMd: false,
|
|
232
|
+
changedTests: [],
|
|
233
|
+
};
|
|
234
|
+
if (!Number.isSafeInteger(input.number) || input.number <= 0 || !/^[0-9a-f]{40}$/i.test(input.matchHeadCommit)) {
|
|
235
|
+
return { ...report, detail: 'invalid PR number or pinned head' };
|
|
236
|
+
}
|
|
237
|
+
const execute = deps.command ?? command;
|
|
238
|
+
const run = (bin: string, args: readonly string[], cwd: string): string => {
|
|
239
|
+
const result = execute(bin, args, cwd);
|
|
240
|
+
if (result.status !== 0) throw new Error(`${bin} ${args.join(' ')}: ${(result.stderr || result.stdout || `exit ${result.status}`).trim().slice(0, 500)}`);
|
|
241
|
+
return args.includes('-z') ? result.stdout : result.stdout.trim();
|
|
242
|
+
};
|
|
243
|
+
const refuseRemote = (bin: string, args: readonly string[]) => {
|
|
244
|
+
if (bin === 'git' && args.some((arg) => arg === 'push' || arg === 'update-ref')) throw new Error('evaluation must not push');
|
|
245
|
+
if (bin === 'gh' && args.some((arg) => arg === 'merge')) throw new Error('evaluation must not merge');
|
|
246
|
+
};
|
|
247
|
+
const guarded: Command = (bin, args, cwd) => {
|
|
248
|
+
refuseRemote(bin, args);
|
|
249
|
+
return execute(bin, args, cwd);
|
|
250
|
+
};
|
|
251
|
+
let temp: string | undefined;
|
|
252
|
+
let attached = false;
|
|
253
|
+
try {
|
|
254
|
+
type View = { headRefOid?: string; headRefName?: string; baseRefName?: string; state?: string; isDraft?: boolean; isCrossRepository?: boolean };
|
|
255
|
+
const view = JSON.parse(run('gh', ['pr', 'view', String(input.number), '--json', 'headRefOid,headRefName,baseRefName,state,isDraft,isCrossRepository'], input.cwd)) as View;
|
|
256
|
+
if (view.headRefOid !== input.matchHeadCommit || view.baseRefName !== 'main' || view.state !== 'OPEN' || view.isDraft !== false || view.isCrossRepository !== false) {
|
|
257
|
+
throw new Error('PR head, base, or ready/open state changed');
|
|
258
|
+
}
|
|
259
|
+
run('git', ['fetch', 'origin', 'refs/heads/main'], input.cwd);
|
|
260
|
+
const base = run('git', ['rev-parse', 'FETCH_HEAD'], input.cwd);
|
|
261
|
+
if (!/^[0-9a-f]{40}$/i.test(base)) throw new Error('main commit unavailable');
|
|
262
|
+
report.base = base;
|
|
263
|
+
run('git', ['fetch', 'origin', `refs/pull/${input.number}/head`], input.cwd);
|
|
264
|
+
if (run('git', ['rev-parse', 'FETCH_HEAD'], input.cwd) !== input.matchHeadCommit) throw new Error('fetched PR head changed');
|
|
265
|
+
temp = mkdtempSync(join(tmpdir(), 'elanous-l8-eval-'));
|
|
266
|
+
run('git', ['worktree', 'add', '--detach', temp, input.matchHeadCommit], input.cwd);
|
|
267
|
+
attached = true;
|
|
268
|
+
const fork = run('git', ['merge-base', base, input.matchHeadCommit], temp);
|
|
269
|
+
const paths = run('git', ['diff', '--name-only', '-z', fork, input.matchHeadCommit], temp).split('\0').filter(Boolean);
|
|
270
|
+
if (!paths.length) throw new Error('PR changed-file paths unavailable');
|
|
271
|
+
if (paths.some((path) => path.startsWith('-') || path.startsWith('/') || path.split('/').includes('..') || path.includes('\\'))) throw new Error('unsafe PR changed-file path');
|
|
272
|
+
const livePaths = run('git', ['diff', '--name-only', '-z', '--diff-filter=ACMRT', fork, input.matchHeadCommit], temp).split('\0').filter(Boolean);
|
|
273
|
+
const tests = livePaths.filter((path) => TEST_PATH.test(path));
|
|
274
|
+
const merge = guarded('git', ['-c', 'user.name=elanous merge queue', '-c', 'user.email=queue@localhost', 'merge', '--no-ff', '--no-edit', base], temp);
|
|
275
|
+
if (merge.status !== 0) {
|
|
276
|
+
const unresolved = run('git', ['diff', '--name-only', '--diff-filter=U'], temp).split('\n').filter(Boolean);
|
|
277
|
+
if (!unresolved.length) throw new Error(`git merge failed: ${(merge.stderr || `exit ${merge.status}`).trim()}`);
|
|
278
|
+
report.conflictingFiles = unresolved;
|
|
279
|
+
report.conflictsLimitedToNextMd = unresolved.length === 1 && unresolved[0] === NEXT_MD_PATH;
|
|
280
|
+
if (!report.conflictsLimitedToNextMd) {
|
|
281
|
+
report.detail = `merge conflicts: ${unresolved.join(', ')}`;
|
|
282
|
+
return report;
|
|
283
|
+
}
|
|
284
|
+
const stage = (n: number): string => {
|
|
285
|
+
const result = guarded('git', ['show', `:${n}:${NEXT_MD_PATH}`], temp!);
|
|
286
|
+
if (result.status !== 0) throw new Error(`release/next.md stage ${n} unavailable: ${result.stderr}`);
|
|
287
|
+
return result.stdout;
|
|
288
|
+
};
|
|
289
|
+
const resolved = resolveNextMdConflict(stage(1), stage(3), stage(2));
|
|
290
|
+
if (resolved === null) {
|
|
291
|
+
report.feasible = false;
|
|
292
|
+
report.detail = 'release/next.md conflict is not append-only';
|
|
293
|
+
return report;
|
|
294
|
+
}
|
|
295
|
+
writeFileSync(join(temp, NEXT_MD_PATH), resolved);
|
|
296
|
+
run('git', ['add', '--', NEXT_MD_PATH], temp);
|
|
297
|
+
run('git', ['-c', 'user.name=elanous merge queue', '-c', 'user.email=queue@localhost', 'commit', '-m', 'Integrate latest main for L8 evaluation'], temp);
|
|
298
|
+
} else {
|
|
299
|
+
report.conflictsLimitedToNextMd = true;
|
|
300
|
+
}
|
|
301
|
+
if (tests.length) {
|
|
302
|
+
const unavailable = tests.filter((path) => !existsSync(join(temp!, path)));
|
|
303
|
+
if (unavailable.length) throw new Error(`changed test paths unavailable: ${unavailable.join(', ')}`);
|
|
304
|
+
run('bun', ['install', '--frozen-lockfile'], temp);
|
|
305
|
+
for (const file of tests) {
|
|
306
|
+
const result = guarded('bun', ['test', file], temp);
|
|
307
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
308
|
+
const counts = parseChangedTestCounts(output);
|
|
309
|
+
if (!counts || counts.pass + counts.fail !== counts.ran || counts.files !== 1 || (result.status !== 0 && counts.fail === 0)) {
|
|
310
|
+
throw new Error(`changed tests unmeasured: ${output.slice(-400)}`);
|
|
311
|
+
}
|
|
312
|
+
const passed = result.status === 0 && counts.fail === 0 && counts.pass > 0;
|
|
313
|
+
report.changedTests.push({ file, passed, pass: counts.pass, fail: counts.fail, ...(passed ? {} : { detail: output.slice(-400) }) });
|
|
314
|
+
}
|
|
315
|
+
if (report.changedTests.some((entry) => !entry.passed)) {
|
|
316
|
+
report.detail = 'changed tests failed';
|
|
317
|
+
return report;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
report.feasible = true;
|
|
321
|
+
return report;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
return { ...report, feasible: false, detail: String(error) };
|
|
324
|
+
} finally {
|
|
325
|
+
if (attached && temp) execute('git', ['worktree', 'remove', '--force', temp], input.cwd);
|
|
326
|
+
if (temp) rmSync(temp, { recursive: true, force: true });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
198
330
|
/** One PR per shared git directory, integrated on a freshly fetched main in a disposable worktree. */
|
|
199
331
|
export async function runL8MergeQueue(input: L8MergeQueueInput, deps: L8MergeQueueDeps = {}): Promise<L8MergeQueueResult> {
|
|
200
332
|
if (!Number.isSafeInteger(input.number) || input.number <= 0 || !/^[0-9a-f]{40}$/i.test(input.matchHeadCommit)) {
|
|
@@ -94,6 +94,7 @@ import type { DocumentReferenceStatus } from './self-implement-runtime.js';
|
|
|
94
94
|
import { DEV_PIPELINE_SINK_SURFACE } from './self-cli-sink-surface.js';
|
|
95
95
|
import { registerLoopAgentSafe, type LoopAgentInput } from '../domains/loop-agent-registry.js';
|
|
96
96
|
import { originObservationFields } from '../agent/origin-observation.js';
|
|
97
|
+
import { resolveLaunchStamp, type LaunchStamp } from '../harness/launch-stamp.js';
|
|
97
98
|
import { getHarnessSpace, normalizeSpaceId, resolveRunIdentity } from '../harness/harness-space.js';
|
|
98
99
|
import { getElanousConfigDir } from '../elanous-config-dir.js';
|
|
99
100
|
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
@@ -183,7 +184,7 @@ import type { RunOutcome, ReworkBudgetVerdict } from './run-outcome.js';
|
|
|
183
184
|
import type { EscalateTier } from './rework-policy.js';
|
|
184
185
|
import type { ClassifyShadowCallLLM } from './classify-shadow.js';
|
|
185
186
|
import { hasModuleLoadFailure, type GateTestFailure } from './gate-baseline.js';
|
|
186
|
-
import { commitWorktree, defaultBranchRef } from './seams.js';
|
|
187
|
+
import { commitWorktree, defaultBranchRef, preservationHasChanges } from './seams.js';
|
|
187
188
|
import { detectUncommittedWork } from './uncommitted-work.js';
|
|
188
189
|
import { spawnSync } from 'node:child_process';
|
|
189
190
|
import { blockedDraftDisposition } from './blocked-draft-policy.js';
|
|
@@ -1037,10 +1038,13 @@ export interface SelfImplementSeams {
|
|
|
1037
1038
|
/** Read-only cited-path observer. Omission uses the deterministic target-worktree filesystem observation. */
|
|
1038
1039
|
observeCitedPathFacts?: (mustFix: readonly string[], cwd: string, round: number) => MustFixCitedPathFact[] | Promise<MustFixCitedPathFact[]>;
|
|
1039
1040
|
/** PR을 열기 전에 보존할 산출물이 있는지 판별한다. `base` 대비 branch 고유 변경과 미커밋 변경을 함께 본다.
|
|
1040
|
-
* 생략 시
|
|
1041
|
+
* Pod에서 생략 시 실제 git 상태로 판정한다. */
|
|
1041
1042
|
preservationHasChanges?: (ctx: { cwd: string; base?: string }) => boolean | Promise<boolean>;
|
|
1042
1043
|
/** Test seam for blocked environment branch persistence; return whether the branch was pushed. */
|
|
1043
|
-
|
|
1044
|
+
/** Test seam — the worktree commit to pin before main sync (default `git rev-parse HEAD`). */
|
|
1045
|
+
headCommit?: (cwd: string) => string | undefined;
|
|
1046
|
+
/** `commit` pins the pushed state; without it the worktree HEAD is pushed. */
|
|
1047
|
+
preserveBlockedBranch?: (ctx: { cwd: string; branch: string; title: string; commit?: string }) => boolean | Promise<boolean>;
|
|
1044
1048
|
/** Hard-cap salvage evidence: a clean worktree and commits ahead of origin/main. */
|
|
1045
1049
|
readReworkSalvageEvidence?: (cwd: string) => Promise<ReworkSalvageEvidence>;
|
|
1046
1050
|
/** Starts exactly one detached `elanous dev --file <goal> --base <branch>` follow-up. */
|
|
@@ -1995,6 +1999,8 @@ export interface SelfImplementOptions {
|
|
|
1995
1999
|
graphOverlays?: readonly GraphOverlaySpec[];
|
|
1996
2000
|
/** Validated, run-local graph variant injected at launch; rejected plans never start the pipeline. */
|
|
1997
2001
|
graphVariantPlan?: import('./graph-variant.js').GraphVariantPlan;
|
|
2002
|
+
/** Door this run was launched through. Absent means the ledger stamp reads `unknown`. */
|
|
2003
|
+
launch?: LaunchStamp;
|
|
1998
2004
|
/** ★ K run-identity — 이 self-implement 호출의 per-run join anchor. 미지정 시 상속(env)→canonical mint.
|
|
1999
2005
|
* 리워크 라운드 전부가 이 값을 공유한다(`elanous self run <runId>` 가 호출 전체를 조인). */
|
|
2000
2006
|
runId?: string;
|
|
@@ -2163,6 +2169,8 @@ export interface SelfImplementResult {
|
|
|
2163
2169
|
runId: string;
|
|
2164
2170
|
ok: boolean;
|
|
2165
2171
|
stage: SelfImplementStage;
|
|
2172
|
+
/** Verbatim must-fix findings retained on a review-blocked terminal result. */
|
|
2173
|
+
mustFix?: readonly string[];
|
|
2166
2174
|
/** Pipeline node that produced this terminal result. */
|
|
2167
2175
|
node: PipelineNodeId;
|
|
2168
2176
|
/** Structured terminal disposition for every normal run exit. */
|
|
@@ -2212,6 +2220,8 @@ export interface SelfImplementResult {
|
|
|
2212
2220
|
quotaExhaustionAssessment?: QuotaExhaustionAssessment;
|
|
2213
2221
|
/** Automatic merge was authorized before this run reached its terminal result. */
|
|
2214
2222
|
mergeApprovalReceived?: true;
|
|
2223
|
+
/** Completed implementation survived a failed main sync on the pushed branch. */
|
|
2224
|
+
harvestable?: true;
|
|
2215
2225
|
merged?: boolean;
|
|
2216
2226
|
/** Observed PR base for a confirmed merge; never inferred from the requested launch base. */
|
|
2217
2227
|
mergedBase?: string;
|
|
@@ -2619,7 +2629,9 @@ function prBody(feature: string, implSummary: string, gateLog?: string, review?:
|
|
|
2619
2629
|
...((gateLog ?? '').split('\n').filter((line) => line.startsWith('⚠️ 동작 검증 0:'))),
|
|
2620
2630
|
...exposeGateLines(gateLog),
|
|
2621
2631
|
...(gateLog ? ['```', gateLog.trim().slice(0, 3000), '```'] : []),
|
|
2622
|
-
...(revertGuard?.reverted.length ? [`- 되돌림 방지: ${revertGuard.reverted.length}파일 제외`] : []),
|
|
2632
|
+
...(revertGuard?.reverted.length && revertGuard.reverted.length > (revertGuard.staleDeleted?.length ?? 0) ? [`- 되돌림 방지: ${revertGuard.reverted.length - (revertGuard.staleDeleted?.length ?? 0)}파일 제외`] : []),
|
|
2633
|
+
...(revertGuard?.staleDeleted?.length ? [`- 되돌림 방지: 삭제 ${revertGuard.staleDeleted.length}파일 제외`] : []),
|
|
2634
|
+
...(revertGuard?.outsideDeleted?.length ? [`- 삭제 경고: 대상 밖 ${revertGuard.outsideDeleted.length}파일 유지 (${revertGuard.outsideDeleted.join(', ')})`] : []),
|
|
2623
2635
|
...(revertGuard?.protected.length ? [`- 되돌림 경고: 대상 경로 ${revertGuard.protected.length}파일 유지 (${revertGuard.protected.join(', ')})`] : []),
|
|
2624
2636
|
...(revertGuard?.warning ? [`- 되돌림 검사 실패(커밋 계속): ${revertGuard.warning}`] : [])] : []),
|
|
2625
2637
|
...relaxation,
|
|
@@ -2968,6 +2980,7 @@ export function assembleBlockedDraftPrBody(
|
|
|
2968
2980
|
JSON.stringify(classificationRecord),
|
|
2969
2981
|
...(state.reasonTruncated ? ['- reason truncated: true'] : []),
|
|
2970
2982
|
...(state.terminalStatus ? [`- child terminal status: reached=${state.terminalStatus.reached}; changed=${state.terminalStatus.changed}; toolCalls=${state.terminalStatus.toolCalls}; timedOut=${state.terminalStatus.timedOut}`] : []),
|
|
2983
|
+
...(state.review?.mustFix.length ? ['## 마지막 리뷰 must-fix', ...state.review.mustFix.map((item) => `- ${item}`)] : []),
|
|
2971
2984
|
].join('\n');
|
|
2972
2985
|
return preparePrBody(raw, persist, 'self-implement-blocked-draft-pr', essentialSuffix);
|
|
2973
2986
|
}
|
|
@@ -3413,7 +3426,9 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
3413
3426
|
const shardIdentity = parseRunShardIdentity(opts.feature);
|
|
3414
3427
|
const observeOuter = makeRunObserver(runId, opts.goalId, debug.log.bind(debug), opts.seams.writeRunLedger, shardIdentity);
|
|
3415
3428
|
try {
|
|
3416
|
-
|
|
3429
|
+
// 10-05 PODPROVIDER: record which child provider this run uses — «default» when the launch named none.
|
|
3430
|
+
const launch = opts.launch ?? resolveLaunchStamp();
|
|
3431
|
+
const origin = { ...runOriginData(), childProvider: opts.childLlm?.provider ?? 'default', ...(opts.childLlm?.model ? { childModel: opts.childLlm.model } : {}), launch };
|
|
3417
3432
|
(opts.seams.writeRunLedger ?? appendRunLedgerEntry)({
|
|
3418
3433
|
timestamp: new Date().toISOString(), runId, event: 'run-origin', data: origin,
|
|
3419
3434
|
...(opts.goalId ? { goalId: opts.goalId } : {}), ...shardIdentity,
|
|
@@ -3491,6 +3506,30 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
3491
3506
|
let loopRegistered = false;
|
|
3492
3507
|
let mergeApprovalReceived = false;
|
|
3493
3508
|
let autoMergeAuthorizedBeforeSync = false;
|
|
3509
|
+
let finishedImplementationAtMainSync = false;
|
|
3510
|
+
// The finished implementation's commit, pinned before main sync starts. A timed-out mergeMain is not
|
|
3511
|
+
// cancellable and may keep moving HEAD in the same worktree, so the harvest pushes this SHA, never HEAD.
|
|
3512
|
+
let preSyncCommit: string | undefined;
|
|
3513
|
+
const preserveMergeHarvest = async (reason: string, inputChars?: number): Promise<boolean> => {
|
|
3514
|
+
if (!finishedImplementationAtMainSync || !worktreePath || !branch) return false;
|
|
3515
|
+
if (!preSyncCommit) {
|
|
3516
|
+
observeOuter('merge-harvest-push-failed', { branch, reason, error: 'pre-sync commit was not pinned' }, { level: 'warn' });
|
|
3517
|
+
return false;
|
|
3518
|
+
}
|
|
3519
|
+
try {
|
|
3520
|
+
const pushed = opts.seams.preserveBlockedBranch
|
|
3521
|
+
? await opts.seams.preserveBlockedBranch({ cwd: worktreePath, branch, title: prTitle(opts.feature), commit: preSyncCommit })
|
|
3522
|
+
: runGitCommand(worktreePath, ['push', '-u', 'origin', `${preSyncCommit}:refs/heads/${branch}`]).status === 0;
|
|
3523
|
+
if (!pushed) throw new Error('branch push failed');
|
|
3524
|
+
// The worktree is left untouched: a late merge may still be writing it. The harvest source is the pinned commit.
|
|
3525
|
+
try { observeOuter('merge-abandoned-harvestable', { branch, reason, inputChars: inputChars ?? null, commit: preSyncCommit, worktree: 'left-as-is' }); }
|
|
3526
|
+
catch { /* observation cannot undo a confirmed push */ }
|
|
3527
|
+
return true;
|
|
3528
|
+
} catch (error) {
|
|
3529
|
+
observeOuter('merge-harvest-push-failed', { branch, reason, error: safeErrorDescription(error) }, { level: 'warn' });
|
|
3530
|
+
return false;
|
|
3531
|
+
}
|
|
3532
|
+
};
|
|
3494
3533
|
let lastParsedSupervisorVerdict: ReworkBudgetVerdict | undefined;
|
|
3495
3534
|
let lastParsedSupervisorReason: string | undefined;
|
|
3496
3535
|
let providerErrorCount = 0;
|
|
@@ -3970,6 +4009,8 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
3970
4009
|
() => credentialFailureCount > 0,
|
|
3971
4010
|
hasCurrentProviderError,
|
|
3972
4011
|
hasCurrentCredentialFailure,
|
|
4012
|
+
(commit: string | undefined) => { finishedImplementationAtMainSync = true; preSyncCommit = commit; },
|
|
4013
|
+
preserveMergeHarvest,
|
|
3973
4014
|
);
|
|
3974
4015
|
terminalNode = result.node;
|
|
3975
4016
|
await mergeChildProviderErrors();
|
|
@@ -4042,9 +4083,11 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
4042
4083
|
} catch (e) {
|
|
4043
4084
|
if (e instanceof StepTimeoutError) {
|
|
4044
4085
|
observeOuter('step-timeout', { step: e.step, ms: e.ms, branch, feature: opts.feature.slice(0, 80) }, { level: 'warn' });
|
|
4086
|
+
const harvestable = e.step === 'merge' && await preserveMergeHarvest('step-timeout');
|
|
4045
4087
|
const result: SelfImplementResult = {
|
|
4046
4088
|
runId,
|
|
4047
4089
|
ok: false,
|
|
4090
|
+
...(harvestable ? { harvestable: true } : {}),
|
|
4048
4091
|
stage: 'timed-out',
|
|
4049
4092
|
node: 'implement',
|
|
4050
4093
|
...resolveRunOutcome({ termination: 'abandoned' }),
|
|
@@ -4052,7 +4095,9 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
4052
4095
|
...(worktreePath ? { worktreePath } : {}),
|
|
4053
4096
|
...(lastParsedSupervisorReason ? { supervisorReason: lastParsedSupervisorReason } : {}),
|
|
4054
4097
|
...(mergeApprovalReceived || (e.step === 'merge' && autoMergeAuthorizedBeforeSync) ? { mergeApprovalReceived: true } : {}),
|
|
4055
|
-
detail:
|
|
4098
|
+
detail: harvestable
|
|
4099
|
+
? `자율 단계 '${e.step}' wall-clock ${e.ms}ms 초과 — 구현 완료 · 수확 대기 (${branch}, worktree: ${worktreePath})`
|
|
4100
|
+
: worktreePath
|
|
4056
4101
|
? `자율 단계 '${e.step}' wall-clock ${e.ms}ms 초과 — hang 방지 종결(worktree 회수 경로: ${worktreePath})`
|
|
4057
4102
|
: `자율 단계 '${e.step}' wall-clock ${e.ms}ms 초과 — hang 방지 종결(worktree 생성 전)`,
|
|
4058
4103
|
};
|
|
@@ -4545,6 +4590,8 @@ async function runSelfImplementInner(
|
|
|
4545
4590
|
hasObservedCredentialFailure: () => boolean = hasCredentialFailure,
|
|
4546
4591
|
hasCurrentProviderError: () => boolean = hasProviderError,
|
|
4547
4592
|
hasCurrentCredentialFailure: () => boolean = hasCredentialFailure,
|
|
4593
|
+
onMainSyncStarted: (commit: string | undefined) => void = () => {},
|
|
4594
|
+
preserveMergeHarvest: (reason: string, inputChars?: number) => Promise<boolean> = async () => false,
|
|
4548
4595
|
): Promise<Omit<SelfImplementResult, 'runId'>> {
|
|
4549
4596
|
const s = opts.seams;
|
|
4550
4597
|
const pipelineStartedAtMs = Date.now();
|
|
@@ -5338,9 +5385,11 @@ async function runSelfImplementInner(
|
|
|
5338
5385
|
let finishedChildCommitted = false;
|
|
5339
5386
|
const revertGuard: RevertGuardResult = { reverted: [], protected: [] };
|
|
5340
5387
|
const inspectStaleReverts = (targets: readonly string[]): void => {
|
|
5341
|
-
const inspected = dropStaleReverts(wt.path, targets, runId, getUserConfig().harness?.revertGuard?.depth);
|
|
5388
|
+
const inspected = dropStaleReverts(wt.path, targets, runId, getUserConfig().harness?.revertGuard?.depth, wt.resolvedBase);
|
|
5342
5389
|
for (const file of inspected.reverted) if (!revertGuard.reverted.includes(file)) revertGuard.reverted.push(file);
|
|
5343
5390
|
for (const file of inspected.protected) if (!revertGuard.protected.includes(file)) revertGuard.protected.push(file);
|
|
5391
|
+
for (const file of inspected.staleDeleted ?? []) if (!revertGuard.staleDeleted?.includes(file)) (revertGuard.staleDeleted ??= []).push(file);
|
|
5392
|
+
for (const file of inspected.outsideDeleted ?? []) if (!revertGuard.outsideDeleted?.includes(file)) (revertGuard.outsideDeleted ??= []).push(file);
|
|
5344
5393
|
if (inspected.warning) revertGuard.warning = inspected.warning;
|
|
5345
5394
|
};
|
|
5346
5395
|
const classifyUnfinishedRun = (stage: SelfImplementStage, verdict?: ReworkBudgetVerdict, decomposition?: TerminalDecomposition): AbandonedClassificationResult => {
|
|
@@ -5363,6 +5412,14 @@ async function runSelfImplementInner(
|
|
|
5363
5412
|
mustFixReported: (review?.mustFix.length ?? 0) > 0,
|
|
5364
5413
|
});
|
|
5365
5414
|
};
|
|
5415
|
+
const podReviewBlockedDraft = process.env.ELANOUS_SUBSTRATE === 'pod';
|
|
5416
|
+
let blockedDraftNoChanges = false;
|
|
5417
|
+
let blockedDraftPrFailure: string | undefined;
|
|
5418
|
+
const reviewBlockedResult = (pr: { url: string; number: number } | undefined) => ({
|
|
5419
|
+
stage: blockedDraftNoChanges && podReviewBlockedDraft ? 'no-changes' as const : podReviewBlockedDraft && !pr ? 'aborted' as const : 'review-blocked' as const,
|
|
5420
|
+
mustFix: [...(review?.mustFix ?? [])],
|
|
5421
|
+
...(pr ? { prUrl: pr.url, prNumber: pr.number } : {}),
|
|
5422
|
+
});
|
|
5366
5423
|
const preserveBlockedArtifacts = async ({ stage, reason, verdict, gate: preservedGate, typecheck, decomposition, childSummary, reasonTruncated, failureKind, terminalStatus, salvageStatusExpected }: { stage: 'review-blocked' | 'gate-failed' | 'aborted'; reason: string; verdict?: ReworkBudgetVerdict; gate?: SelfImplementGateResult; typecheck?: { passed: boolean; log?: string }; decomposition?: TerminalDecomposition; childSummary?: string; reasonTruncated?: boolean; failureKind?: ReturnType<typeof classifyReviewProviderFailure> | null; terminalStatus?: ImplementTerminalStatus; salvageStatusExpected: boolean }): Promise<{ url: string; number: number } | undefined> => {
|
|
5367
5424
|
let childSummaryArtifact: ChildSummaryArtifactRef | undefined;
|
|
5368
5425
|
if (childSummary !== undefined) {
|
|
@@ -5455,6 +5512,7 @@ async function runSelfImplementInner(
|
|
|
5455
5512
|
...(childSummaryArtifact ? { childSummaryArtifactPath: childSummaryArtifact.path } : {}),
|
|
5456
5513
|
};
|
|
5457
5514
|
if (s.preservationHasChanges && !await s.preservationHasChanges({ cwd: wt.path, base: preservationBase })) {
|
|
5515
|
+
if (stage === 'review-blocked' && podReviewBlockedDraft) blockedDraftNoChanges = true;
|
|
5458
5516
|
observe('rework-blocked-draft-pr', { ...observation, skipped: 'no-changes' });
|
|
5459
5517
|
progress(stage, '보존할 변경이 없어 draft PR을 열지 않음');
|
|
5460
5518
|
return undefined;
|
|
@@ -5488,8 +5546,9 @@ async function runSelfImplementInner(
|
|
|
5488
5546
|
s.persistPrBodyArtifact,
|
|
5489
5547
|
);
|
|
5490
5548
|
// no-changes wins; worktree-only remains local even for environment stops.
|
|
5491
|
-
const environmentStop = blockedDraftDisposition(classificationRecord.classification) === 'preserve-branch'
|
|
5492
|
-
|
|
5549
|
+
const environmentStop = blockedDraftDisposition(classificationRecord.classification) === 'preserve-branch'
|
|
5550
|
+
&& !(stage === 'review-blocked' && podReviewBlockedDraft);
|
|
5551
|
+
const worktreeOnly = opts.completion === 'worktree-only' && !(stage === 'review-blocked' && podReviewBlockedDraft);
|
|
5493
5552
|
if (worktreeOnly || environmentStop) {
|
|
5494
5553
|
let artifactPath: string | undefined;
|
|
5495
5554
|
let artifactError: string | undefined;
|
|
@@ -5559,7 +5618,54 @@ async function runSelfImplementInner(
|
|
|
5559
5618
|
}
|
|
5560
5619
|
return undefined;
|
|
5561
5620
|
}
|
|
5621
|
+
if (stage === 'review-blocked' && podReviewBlockedDraft && !s.preservationHasChanges
|
|
5622
|
+
&& !preservationHasChanges(wt.path, preservationBase)) {
|
|
5623
|
+
blockedDraftNoChanges = true;
|
|
5624
|
+
observe('rework-blocked-draft-pr', { ...observation, skipped: 'no-changes' });
|
|
5625
|
+
progress(stage, '보존할 변경이 없어 draft PR을 열지 않음');
|
|
5626
|
+
return undefined;
|
|
5627
|
+
}
|
|
5628
|
+
// Pod review-blocked: a failed commit or PR still pushes the branch, and the result detail says what survived.
|
|
5629
|
+
const preserveReviewBlockedBranch = async (cause: string, commitFirst: boolean, suffix = ''): Promise<undefined> => {
|
|
5630
|
+
let pushed = false;
|
|
5631
|
+
try {
|
|
5632
|
+
if (s.preserveBlockedBranch) {
|
|
5633
|
+
pushed = await s.preserveBlockedBranch({ cwd: wt.path, branch: wt.branch, title: prTitle(opts.feature) });
|
|
5634
|
+
} else {
|
|
5635
|
+
if (commitFirst) {
|
|
5636
|
+
const commit = commitWorktree(wt.path, prTitle(opts.feature));
|
|
5637
|
+
if (!commit.ok && !/nothing to commit/.test(commit.out)) throw new Error(`로컬 브랜치 커밋 실패: ${commit.out}`);
|
|
5638
|
+
}
|
|
5639
|
+
const push = runGitCommand(wt.path, ['push', '-u', 'origin', `HEAD:refs/heads/${wt.branch}`]);
|
|
5640
|
+
if (push.status !== 0) throw new Error(push.stderr || push.stdout || 'git push failed');
|
|
5641
|
+
pushed = true;
|
|
5642
|
+
}
|
|
5643
|
+
if (!pushed) throw new Error('브랜치 push가 성공하지 않았음');
|
|
5644
|
+
} catch (pushError) {
|
|
5645
|
+
const pushReason = safeErrorDescription(pushError);
|
|
5646
|
+
blockedDraftPrFailure = `${cause}; 원격 브랜치 보존 실패: ${pushReason}; worktree: ${wt.path}`;
|
|
5647
|
+
observe('rework-blocked-draft-pr', { ...observation, error: cause, pushed: false, pushError: pushReason, branch: wt.branch }, { level: 'warn' });
|
|
5648
|
+
progress('aborted', blockedDraftPrFailure);
|
|
5649
|
+
return undefined;
|
|
5650
|
+
}
|
|
5651
|
+
blockedDraftPrFailure = `${cause}; 원격 브랜치 보존: ${wt.branch}${suffix}`;
|
|
5652
|
+
observe('rework-blocked-draft-pr', { ...observation, error: cause, pushed: true, branch: wt.branch }, { level: 'warn' });
|
|
5653
|
+
progress('aborted', blockedDraftPrFailure);
|
|
5654
|
+
return undefined;
|
|
5655
|
+
};
|
|
5562
5656
|
progress('pr-opening', '중단 산출물을 draft PR로 보존…');
|
|
5657
|
+
if (stage === 'review-blocked' && podReviewBlockedDraft) {
|
|
5658
|
+
let commitError: string | undefined;
|
|
5659
|
+
try {
|
|
5660
|
+
const commit = commitWorktree(wt.path, prTitle(opts.feature));
|
|
5661
|
+
if (!commit.ok && !/nothing to commit/.test(commit.out)) commitError = commit.out.trim() || 'git commit failed';
|
|
5662
|
+
} catch (error) {
|
|
5663
|
+
commitError = safeErrorDescription(error);
|
|
5664
|
+
}
|
|
5665
|
+
if (commitError !== undefined) {
|
|
5666
|
+
return preserveReviewBlockedBranch(`review-blocked 작업 커밋 실패: ${commitError}`, false, `; 미커밋 작업은 worktree 에 남음: ${wt.path}`);
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5563
5669
|
let pr: { url: string; number: number };
|
|
5564
5670
|
try {
|
|
5565
5671
|
pr = await withStepTimeout(s.openPr({
|
|
@@ -5568,6 +5674,7 @@ async function runSelfImplementInner(
|
|
|
5568
5674
|
head: wt.branch,
|
|
5569
5675
|
...(opts.base ? { base: opts.base } : {}),
|
|
5570
5676
|
draft: true,
|
|
5677
|
+
...(stage === 'review-blocked' ? { labels: ['review-blocked'] } : {}),
|
|
5571
5678
|
cwd: wt.path,
|
|
5572
5679
|
}), T.pr, 'pr');
|
|
5573
5680
|
} catch (error) {
|
|
@@ -5589,6 +5696,10 @@ async function runSelfImplementInner(
|
|
|
5589
5696
|
...(artifactPath ? { prBodyArtifactPath: artifactPath } : {}),
|
|
5590
5697
|
...(artifactError ? { prBodyArtifactError: artifactError } : {}),
|
|
5591
5698
|
}, { level: 'warn' });
|
|
5699
|
+
if (stage === 'review-blocked' && podReviewBlockedDraft) {
|
|
5700
|
+
return preserveReviewBlockedBranch(`draft PR 생성 실패: ${prError}`, true,
|
|
5701
|
+
`; ${artifactPath ? `판정 본문: ${artifactPath}` : `판정 본문 보존 실패: ${artifactError ?? 'unknown error'}`}`);
|
|
5702
|
+
}
|
|
5592
5703
|
progress('aborted', artifactPath
|
|
5593
5704
|
? `중단 산출물의 draft PR 생성 실패(worktree·branch 보존 · 판정 본문 보존: ${artifactPath})`
|
|
5594
5705
|
: `중단 산출물의 draft PR 생성 실패(worktree·branch 보존 · 판정 본문 보존 실패: ${artifactError ?? 'unknown error'})`);
|
|
@@ -5601,6 +5712,9 @@ async function runSelfImplementInner(
|
|
|
5601
5712
|
...(assembled.truncated ? { prBodyTruncated: true, prBodyOriginalChars: assembled.originalChars } : {}),
|
|
5602
5713
|
});
|
|
5603
5714
|
await flushPrComments(pr);
|
|
5715
|
+
if (stage === 'review-blocked') {
|
|
5716
|
+
try { debug.log('self-implement.terminal', 'review-blocked-pr', { runId, prNumber: pr.number, round: Math.max(0, round - 1) }); } catch { /* observation is fail-soft */ }
|
|
5717
|
+
}
|
|
5604
5718
|
progress('pr-opened', `중단 산출물 draft PR 개설 (#${pr.number}·사람 판단 대기)`);
|
|
5605
5719
|
return pr;
|
|
5606
5720
|
};
|
|
@@ -6113,7 +6227,7 @@ async function runSelfImplementInner(
|
|
|
6113
6227
|
if (reflectRejectedAudit.length && review) review.shouldFix = [...review.shouldFix, ...reflectRejectedAudit];
|
|
6114
6228
|
const pr = await preserveBlockedArtifacts({ stage: 'review-blocked', verdict: parsedDecision.verdict, reason: parsedDecision.reason, gate, decomposition: terminalDecomposition, salvageStatusExpected: false });
|
|
6115
6229
|
const quotaExhaustionAssessment = quotaExhaustionAssessmentForRun();
|
|
6116
|
-
return { ok: false,
|
|
6230
|
+
return { ok: false, node: 'rework', ...terminal, ...supervisorOutcome, sessionId, worktreePath: wt.path, branch: wt.branch, gate, review, quotaExhaustionAssessment, ...(lastSupervisorReason ? { supervisorReason: lastSupervisorReason } : {}), ...reviewBlockedResult(pr), detail: `${blockedDraftPrFailure ? `${blockedDraftPrFailure}; ` : ''}rework judged unconvergeable after ${round} rework round(s): ${parsedDecision.reason}` };
|
|
6117
6231
|
}
|
|
6118
6232
|
const baselineStop = noRetryBaseline();
|
|
6119
6233
|
const reason = baselineStop.terminationReason ?? parsedDecision.reason;
|
|
@@ -6141,7 +6255,7 @@ async function runSelfImplementInner(
|
|
|
6141
6255
|
if (isReviewRework(reworkKind)) {
|
|
6142
6256
|
if (reflectRejectedAudit.length && review) review.shouldFix = [...review.shouldFix, ...reflectRejectedAudit];
|
|
6143
6257
|
const pr = await preserveBlockedArtifacts({ stage: 'review-blocked', verdict: lastReworkVerdict, reason, gate, salvageStatusExpected: false });
|
|
6144
|
-
return { ok: false,
|
|
6258
|
+
return { ok: false, node: 'rework', ...terminal, sessionId, worktreePath: wt.path, branch: wt.branch, gate, review, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(lastSupervisorVerdict === 'CONTRACT-CONFLICT' ? { supervisorVerdict: lastSupervisorVerdict } : {}), ...reviewBlockedResult(pr), detail: blockedDraftPrFailure ?? reason };
|
|
6145
6259
|
}
|
|
6146
6260
|
const baselineStop = noRetryBaseline();
|
|
6147
6261
|
const pr = await preserveBlockedArtifacts({ stage: 'gate-failed', verdict: lastReworkVerdict, reason: baselineStop.terminationReason ?? reason, gate, salvageStatusExpected: false });
|
|
@@ -6157,32 +6271,34 @@ async function runSelfImplementInner(
|
|
|
6157
6271
|
if (isReviewRework(reworkKind)) {
|
|
6158
6272
|
if (reflectRejectedAudit.length && review) review.shouldFix = [...review.shouldFix, ...reflectRejectedAudit];
|
|
6159
6273
|
const reason = `review must-fix unresolved after ${round - 1} rework round(s)`;
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6274
|
+
if (!podReviewBlockedDraft) {
|
|
6275
|
+
const acceptance = await acceptReviewBudget(
|
|
6276
|
+
review?.mustFix ?? [],
|
|
6277
|
+
reworkParts.some((part) => part.source === 'supervisor'),
|
|
6278
|
+
);
|
|
6279
|
+
if (acceptance.decision.action === 'accept') {
|
|
6280
|
+
return {
|
|
6281
|
+
ok: true,
|
|
6282
|
+
stage: 'pr-opened',
|
|
6283
|
+
node: 'open-pr',
|
|
6284
|
+
...terminal,
|
|
6285
|
+
sessionId,
|
|
6286
|
+
worktreePath: wt.path,
|
|
6287
|
+
branch: wt.branch,
|
|
6288
|
+
gate,
|
|
6289
|
+
review,
|
|
6290
|
+
mergeReason: 'review-budget-follow-up-required',
|
|
6291
|
+
followUpMustFix: acceptance.decision.unresolvedMustFix,
|
|
6292
|
+
followUpMustFixCount: acceptance.decision.unresolvedMustFix.length,
|
|
6293
|
+
...(acceptance.pr ? { prUrl: acceptance.pr.url, prNumber: acceptance.pr.number } : {}),
|
|
6294
|
+
detail: `${reason}; accepted with ${acceptance.decision.unresolvedMustFix.length} follow-up must-fix item(s)`,
|
|
6295
|
+
};
|
|
6296
|
+
}
|
|
6181
6297
|
}
|
|
6182
6298
|
const pr = await preserveBlockedArtifacts({ stage: 'review-blocked', reason, gate, salvageStatusExpected: true });
|
|
6183
|
-
const salvage = await salvageHardCap(canSalvage, pr);
|
|
6299
|
+
const salvage = blockedDraftNoChanges || blockedDraftPrFailure ? 'parked' : await salvageHardCap(canSalvage, pr);
|
|
6184
6300
|
const disposition = { ...terminal, ...(salvage ? { salvage } : {}) };
|
|
6185
|
-
return { ok: false,
|
|
6301
|
+
return { ok: false, node: 'rework', ...disposition, sessionId, worktreePath: wt.path, branch: wt.branch, gate, review, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(lastSupervisorVerdict === 'CONTRACT-CONFLICT' ? { supervisorVerdict: lastSupervisorVerdict } : {}), ...reviewBlockedResult(pr), detail: blockedDraftPrFailure ?? reason };
|
|
6186
6302
|
}
|
|
6187
6303
|
const baselineStop = noRetryBaseline();
|
|
6188
6304
|
const reason = baselineStop.terminationReason ?? `gate failed after ${round - 1} rework round(s)`;
|
|
@@ -7073,9 +7189,13 @@ async function runSelfImplementInner(
|
|
|
7073
7189
|
`Child completion: ${impl.summary.trim() || '(summary unavailable)'}`,
|
|
7074
7190
|
]);
|
|
7075
7191
|
if (review) {
|
|
7076
|
-
|
|
7077
|
-
`
|
|
7078
|
-
|
|
7192
|
+
if (review.reviewed) {
|
|
7193
|
+
bufferPrComment(round, 'reviewer', `Round ${round}: review completed (${review.verdict}).`, [
|
|
7194
|
+
`Review summary: ${review.summary.trim() || '(summary unavailable)'}`,
|
|
7195
|
+
]);
|
|
7196
|
+
} else {
|
|
7197
|
+
bufferPrComment(round, 'reviewer', `Round ${round}: 리뷰 못 함(사유: ${review.failureReason?.trim() || '사유 미상'}) — 통과 판정 아님`, []);
|
|
7198
|
+
}
|
|
7079
7199
|
}
|
|
7080
7200
|
finalizeSupervisorDeliveries('no-next-rework-round');
|
|
7081
7201
|
break;
|
|
@@ -7193,7 +7313,23 @@ async function runSelfImplementInner(
|
|
|
7193
7313
|
return { ok: false, stage: 'merge-conflict', node: 'main-sync', ...resolveRunOutcome({ termination: 'abandoned' }), ...decisionSignalResult, sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), detail: 'pre-PR main sync default-branch-unresolved' };
|
|
7194
7314
|
}
|
|
7195
7315
|
progress('gating', `최신 ${mergeTarget} 정합 (병렬 드리프트·LLM 충돌해결)…`);
|
|
7196
|
-
|
|
7316
|
+
onMainSyncStarted((s.headCommit ?? ((cwd: string) => {
|
|
7317
|
+
const head = runGitCommand(cwd, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8' });
|
|
7318
|
+
return head.status === 0 ? String(head.stdout).trim() || undefined : undefined;
|
|
7319
|
+
}))(wt.path));
|
|
7320
|
+
let sync: Awaited<ReturnType<NonNullable<SelfImplementSeams['mergeMain']>>>;
|
|
7321
|
+
try {
|
|
7322
|
+
sync = await withStepTimeout(s.mergeMain(wt.path, mergeTarget), T.merge, 'merge');
|
|
7323
|
+
} catch (error) {
|
|
7324
|
+
if (error instanceof StepTimeoutError) throw error;
|
|
7325
|
+
const harvestable = await preserveMergeHarvest('merge-interrupted');
|
|
7326
|
+
if (!harvestable) throw error;
|
|
7327
|
+
canAuto = false;
|
|
7328
|
+
mergeReason = 'main-sync-interrupted';
|
|
7329
|
+
recordMergeDecision();
|
|
7330
|
+
progress('merge-conflict', `⚠️ ${mergeTarget} 정합 중단 — 사람 수확 (${wt.branch})`);
|
|
7331
|
+
return { ok: false, stage: 'merge-conflict', node: 'main-sync', ...resolveRunOutcome({ termination: 'abandoned' }), ...decisionSignalResult, sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), harvestable: true, detail: `pre-PR main sync interrupted: ${safeErrorDescription(error)}` };
|
|
7332
|
+
}
|
|
7197
7333
|
// ⛔⭐ `OBS-T96` 🅐 — 「무엇을 봤는지」를 남긴다. 수만 남기면 정합 실패가 «재현 불가»가 된다.
|
|
7198
7334
|
observe('pre-pr-sync', mainSyncObservation(sync, mergeTarget, opts.base, wt.branch));
|
|
7199
7335
|
if (sync.status === 'conflict-unresolved' || sync.status === 'error') {
|
|
@@ -7201,7 +7337,9 @@ async function runSelfImplementInner(
|
|
|
7201
7337
|
canAuto = false;
|
|
7202
7338
|
mergeReason = `main-sync-${sync.status}`;
|
|
7203
7339
|
recordMergeDecision();
|
|
7204
|
-
|
|
7340
|
+
const harvestable = (sync.reason === 'conflict-input-too-large' || sync.reason === 'conflict-resolver-interrupted')
|
|
7341
|
+
&& await preserveMergeHarvest(sync.reason, sync.inputChars);
|
|
7342
|
+
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}` };
|
|
7205
7343
|
}
|
|
7206
7344
|
if (sync.status === 'llm-resolved' || sync.status === 'merged') {
|
|
7207
7345
|
preservationBase = opts.base ?? mergeTarget;
|
|
@@ -7522,6 +7660,16 @@ async function runSelfImplementInner(
|
|
|
7522
7660
|
const m = await withStepTimeout(merging, T.pr, 'pr');
|
|
7523
7661
|
observe('merged', { number: pr.number, merged: m.merged, detail: m.detail ?? null });
|
|
7524
7662
|
if (m.merged) {
|
|
7663
|
+
const mergedBase = m.baseRefName?.trim() || observedPrBase;
|
|
7664
|
+
if (mergedBase && displayBranchName(mergedBase) === 'main') {
|
|
7665
|
+
try {
|
|
7666
|
+
const { syncMergedPrChecklist } = await import('../release-loop/merged-pr-checklist.js');
|
|
7667
|
+
const goalFile = opts.goalFile ?? (opts.goalId ? s.goalDocumentPathByGoalId?.(opts.goalId) : undefined);
|
|
7668
|
+
syncMergedPrChecklist(pr.number, wt.path, goalFile);
|
|
7669
|
+
} catch (error) {
|
|
7670
|
+
observe('merged-pr-checklist-sync-failed', { number: pr.number, error: safeErrorDescription(error) }, { level: 'warn' });
|
|
7671
|
+
}
|
|
7672
|
+
}
|
|
7525
7673
|
try {
|
|
7526
7674
|
await s.supersedeDraftsOnMerge?.(pr.number, wt.path);
|
|
7527
7675
|
} catch (error) {
|
|
@@ -7540,7 +7688,6 @@ async function runSelfImplementInner(
|
|
|
7540
7688
|
observe('release-note-write-failed', { number: pr.number, prUrl: pr.url, directory, fragment, error: safeErrorDescription(error) }, { level: 'error' });
|
|
7541
7689
|
throw new MergedReleaseNoteWriteError(pr.url, directory, fragment, error);
|
|
7542
7690
|
}
|
|
7543
|
-
const mergedBase = m.baseRefName?.trim() || observedPrBase;
|
|
7544
7691
|
const defaultBranch = resolveDefaultBranchTarget(s.defaultBranchRef ?? defaultBranchRef, wt.path).target ?? undefined;
|
|
7545
7692
|
progress('merged', formatAutoMergeSuccessMessage({ prNumber: pr.number, mergedBase, defaultBranch }));
|
|
7546
7693
|
await runPostMergeCleanup(s.postMergeCleanup, wt.path, wt.branch, opts.goalFile, mergedBase && defaultBranch && displayBranchName(mergedBase) === displayBranchName(defaultBranch) ? defaultBranch : undefined, observe);
|
|
@@ -16,7 +16,7 @@ export const TERMINAL_STAGES_BY_NODE = {
|
|
|
16
16
|
implement: ['aborted', 'timed-out', 'soft-stopped', 'parked'],
|
|
17
17
|
gate: [],
|
|
18
18
|
review: ['review-blocked'],
|
|
19
|
-
rework: ['gate-failed', 'review-blocked'],
|
|
19
|
+
rework: ['gate-failed', 'review-blocked', 'no-changes'],
|
|
20
20
|
'main-sync': ['merge-conflict'],
|
|
21
21
|
regate: ['gate-failed'],
|
|
22
22
|
'open-pr': ['pr-declined', 'pr-opened', 'worktree-completed'],
|