elanous 0.2.16 → 0.2.18
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/P6YTVgzuZz5oTZhY4Din_/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1307-b46d45b705e8c9ae.js → 1307-30dff22c9e1f3073.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6158.9c0449a9a8d0064b.js → 6158.af2e93b7adff709b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d92f7ee3994e84c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-3cb52e5ea8fc2fa5.js → page-4733c967b56a4277.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d22fd849cc22f104.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c1e20d7d338713f1.js → page-bb742f995c6769e7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-482c5dbde699947c.js → page-02b073a2ac793b83.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-8a9b3594bc23289b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-a0eb02638b1e6f31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-a0e2a6d1bfb52fc5.js → page-308111abadf35670.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-c53598cfce6156f2.js → page-5d97b2901e0d1ea6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-13a63953f53c6a23.js → page-c39554c321576982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-5d3c8429d04f3831.js → page-8c3d8e63ae2e1c98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-3193bdc0bbab047a.js → page-9b72a07d424701c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-6107ca8ddd5e3bdd.js → page-a572f58cd3ca5864.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-be5ea965a07fbc41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c130b63862437814.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-fa244bd146a00ae8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-44d56d6391965a6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-4167348049166852.js → page-83a700d4c754d802.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-de52e8fdf9095f7a.js → not-found-7b3170f374f77b9d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-ca3952cd78a83ca2.js → page-a3c58f5c72a5f3f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e8e1ecaea7cd71d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-1ebfe049c640e171.js → page-1467047104141297.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-2da6f9b8a38c6978.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-102310930791197a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-6ed0b73bae8351d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-abe8e3b74bea1ffc.js → page-44d5f767491692bf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-43336055451f6e37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-d28573d6792b4275.js → page-3cc47dca0922ab9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a77ee355a2304ff0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-d751de5f8062ff5e.js → page-b17cd46cf0e9b8cf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-fe1759f94003b32b.js → page-61553851aa606146.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/{page-d64c984e855c8c68.js → page-a1f585e154e61c59.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-7bf54c9bb9274af8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-9afdee66b820c76e.js → page-5150ef17b5383a57.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-3544f154c87b43ee.js → page-134da468538baf48.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-539cde99554be20f.js → webpack-c2782b330447ffb6.js} +1 -1
- package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.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 -0
- package/apps/pwa/out/decisions/index.txt +30 -0
- 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/intake/intake-daily.yaml +9 -2
- package/graphs/intake/news-sources.yaml +4 -0
- package/graphs/intake/recipes.yaml +6 -1
- package/package.json +2 -1
- package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
- package/samples/workflows/templates/chat-driven-research.yaml +35 -0
- package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
- package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
- package/samples/workflows/templates/manual-quickstart.yaml +21 -0
- package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
- package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
- package/scripts/__fixtures__/intake-news.xml +7 -0
- package/scripts/ci-public-export-import-gate.ts +7 -2
- package/scripts/ci-typecheck-changed.ts +21 -6
- package/scripts/discord-seat-probe.ts +3 -2
- package/scripts/docs/generate-cli-anatomy.ts +162 -0
- package/scripts/goals/authoring-metrics.ts +73 -0
- package/scripts/intake-news.ts +169 -0
- package/scripts/landing-heal/must-fix-match.ts +7 -7
- package/scripts/landing-heal/run-mission.ts +3 -2
- package/scripts/public-export-test-run.ts +37 -2
- package/scripts/release-loop/checklist-gate-node.ts +13 -11
- package/scripts/release-loop/cut-branch.ts +2 -1
- package/scripts/release-loop/cutoff.ts +4 -2
- package/scripts/release-loop/docs-land-node.ts +77 -11
- package/scripts/release-loop/docs-node.ts +1 -1
- package/scripts/release-loop/gate-node.ts +99 -14
- package/scripts/release-loop/node-verdict.ts +2 -1
- package/scripts/release-loop/npm-publish-node.ts +11 -7
- package/scripts/release-loop/ops-upgrade-node.ts +8 -1
- package/scripts/release-loop/preflight.ts +246 -0
- package/scripts/release-loop/publish-node.ts +10 -3
- package/scripts/release-loop/release-docs.ts +134 -0
- package/scripts/release-loop/release-readiness.ts +11 -6
- package/scripts/release-loop/release-version.ts +48 -0
- package/scripts/release-loop/resume-release.ts +115 -0
- package/scripts/release-loop/unattended-release.ts +43 -7
- package/scripts/release-loop/version-node.ts +149 -41
- package/scripts/release-story/draft.ts +42 -14
- package/scripts/rhythm/daily.ts +25 -4
- package/scripts/skills/public-check.ts +62 -12
- package/scripts/test-deterministic.ts +51 -33
- package/scripts/test-diet/effectiveness.ts +62 -1
- package/scripts/test-diet/lib.ts +1 -1
- package/scripts/test-diet/node.ts +8 -1
- package/src/agent-mission/driver.ts +53 -10
- package/src/agent-mission/review-loop.ts +4 -3
- package/src/agent-substrate/pr-comment-meta.ts +20 -0
- package/src/autopilot/build/llm-conflict-merge.ts +28 -8
- package/src/autopilot/track-agent.ts +5 -3
- package/src/briefing/brief-items.ts +86 -17
- package/src/cli/autopilot-cli.ts +86 -1
- package/src/cli/brief-cli.ts +36 -2
- package/src/cli/decisions-cli.ts +4 -1
- package/src/cli/freeze-cli.ts +3 -2
- package/src/cli/intake-cli.ts +74 -2
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/notify-cli.ts +75 -0
- package/src/cli/pr-cli.ts +12 -1
- package/src/cli/provider-cli.ts +24 -0
- package/src/cli/release-cli.ts +101 -17
- package/src/cli/self-update-canary.ts +46 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/tasks-cli.ts +309 -3
- package/src/context-bus/condense.ts +19 -3
- package/src/context-bus/context-day.ts +77 -24
- package/src/context-bus/context-first.ts +33 -0
- package/src/context-card/index.ts +45 -0
- package/src/context-card/task.ts +76 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
- package/src/decisions/decision-ledger.ts +10 -1
- package/src/discord-self-message.ts +20 -2
- package/src/discord-slash-wire.ts +1 -0
- package/src/domains/outbound-alert.ts +200 -27
- package/src/domains/outbound-log-probe.ts +16 -1
- package/src/domains/release-tool.ts +2 -2
- package/src/execution-loop/gates.ts +11 -2
- package/src/flow-loop/split.ts +8 -4
- package/src/git-fs/worktree.ts +23 -3
- package/src/harness/harness-cli-command.ts +151 -11
- package/src/harness/harness-pod-dispatch.ts +11 -0
- package/src/harness/harness-queue.ts +466 -32
- package/src/harness/helper-scan.ts +10 -3
- package/src/harness/nested-elanous-policy.ts +23 -4
- package/src/hq/fence-wrapper.ts +123 -0
- package/src/hq/hq.ts +8 -0
- package/src/hq/seats.ts +369 -0
- package/src/index.ts +190 -23
- package/src/intake-plane/adopt-gate.ts +49 -0
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/collect-github.ts +57 -25
- package/src/intake-plane/digest.ts +39 -4
- package/src/intake-plane/discord-seat-work.ts +8 -2
- package/src/intake-plane/requirement-funnel.ts +316 -0
- package/src/knowledge/know-find.ts +10 -1
- package/src/loops/budget.ts +36 -0
- package/src/loops/orchestrator/finish-rate.ts +73 -1
- package/src/loops/orchestrator/grid-place.ts +11 -0
- package/src/loops/orchestrator/tick.ts +295 -20
- package/src/maturity/feature-maturity.ts +3 -2
- package/src/nexus/api/decisions-route.ts +11 -1
- package/src/nexus/api/grid.ts +61 -0
- package/src/nexus/api/http-server.ts +19 -1
- package/src/nexus/api/loop-edges.ts +69 -13
- package/src/nexus/api/ops-seats-sources.ts +2 -2
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/nexus/api/workflow-templates.ts +3 -0
- package/src/oauth/codex.ts +120 -2
- package/src/onboarding/first-chat-gate.ts +29 -0
- package/src/release-loop/checklist.ts +104 -6
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/landing-freeze.ts +24 -14
- package/src/release-loop/placement.ts +13 -3
- package/src/release-loop/rubric.ts +80 -0
- package/src/seat-dispatch/seat-ask.ts +79 -16
- package/src/seat-loop/seat-loop.ts +107 -37
- package/src/self-dev/ask-launch-flow.ts +50 -10
- package/src/self-dev/ask-path-candidates.ts +68 -0
- package/src/self-dev/dev-pipeline.ts +4 -0
- package/src/self-dev/draft-triage-rules.ts +1 -1
- package/src/self-dev/orchestrate-cli.ts +22 -0
- package/src/self-dev/round-pr-supersede.ts +244 -0
- package/src/self-dev/run-store.ts +5 -1
- package/src/self-dev/run-supervisor.ts +21 -1
- package/src/self-dev/supervisor-verdict-edges.ts +46 -0
- package/src/self-implement/follow-up-goals.ts +2 -2
- package/src/self-implement/frozen-merges.ts +25 -20
- package/src/self-implement/gate-remote.ts +427 -0
- package/src/self-implement/goal-author-clarification.ts +81 -0
- package/src/self-implement/goal-author-cli.ts +33 -5
- package/src/self-implement/goal-author.ts +93 -12
- package/src/self-implement/goal-clarification-escalation.ts +37 -1
- package/src/self-implement/goal-document-write.ts +28 -0
- package/src/self-implement/heal-intake.ts +4 -0
- package/src/self-implement/host-regate.ts +3 -1
- package/src/self-implement/l8-merge-queue.ts +29 -5
- package/src/self-implement/orchestrator.ts +78 -13
- package/src/self-implement/prior-draft-findings.ts +4 -3
- package/src/skills/skill-feedback.ts +76 -0
- package/src/task-agent/actions.ts +151 -0
- package/src/task-agent/judge.ts +122 -0
- package/src/task-agent/mission.ts +423 -0
- package/src/task-agent/shadow.ts +90 -0
- package/src/task-agent/task-hand.ts +235 -0
- package/src/task-cards/card-store.ts +4 -1
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -4
- package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
- package/src/task-orchestrator/surfaces/pod-pool.ts +81 -13
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +225 -35
- package/src/telegram-commands.ts +1 -1
- package/src/telegram-loops-command.ts +22 -15
- package/src/telegram.ts +5 -20
- package/src/user-config.ts +86 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-29d889b809288e65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-8c2d9526164f5d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-dce686e582c23937.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1a61c7ca1ceb3ae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-51f811fa119308b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-5b042736aa113756.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-57924393e3e4e539.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0f5cd57ca928f42b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-59f03fda76c03d22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-1ea64acadd704ace.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +0 -1
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
- package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{rv9cierV0jSOvDBm1g0XB → P6YTVgzuZz5oTZhY4Din_}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// DRAFT-TRIAGE (TC 10-07) — a supervisor rework round of the same run and goal opens a new PR
|
|
2
|
+
// (new task id → new goal hash → new branch). Continuing the branch would mean pinning the shard
|
|
3
|
+
// identity and the Pod child's run suffix, so this closes the previous round's draft instead:
|
|
4
|
+
// label `elanous:superseded`, comment with the newer PR link, close. Same run = one tracker per
|
|
5
|
+
// supervised invocation; same goal = `resumeKey(feature)`, the key the resume path already uses.
|
|
6
|
+
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { debug } from '../debug/log.js';
|
|
9
|
+
import { PR_LABELS } from '../github/pr-labels.js';
|
|
10
|
+
import { PROTECTED_LABELS } from './draft-triage-rules.js';
|
|
11
|
+
import { resumeKey, type SelfDevJobResult } from './orchestrate.js';
|
|
12
|
+
|
|
13
|
+
const SUPERSEDED_LABEL = PR_LABELS.find((label) => label.name.endsWith(':superseded'))!.name;
|
|
14
|
+
|
|
15
|
+
export interface RoundPrRef {
|
|
16
|
+
readonly repository: string;
|
|
17
|
+
readonly number: number;
|
|
18
|
+
readonly url: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RoundPrView {
|
|
22
|
+
readonly state: string;
|
|
23
|
+
readonly isDraft: boolean;
|
|
24
|
+
readonly labels: readonly string[];
|
|
25
|
+
readonly mergedAt?: string | null;
|
|
26
|
+
/** Comment bodies — a link already posted (even by a write that reported failure) is not posted again.
|
|
27
|
+
* An adapter that cannot read them returns a null view, so nothing is written. */
|
|
28
|
+
readonly comments: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** gh seams. A null view or a false write means «not observed / not applied» — never a reason to close. */
|
|
32
|
+
export interface RoundPrAdapters {
|
|
33
|
+
view(pr: RoundPrRef): RoundPrView | null;
|
|
34
|
+
addLabel(pr: RoundPrRef, label: string): boolean;
|
|
35
|
+
comment(pr: RoundPrRef, body: string): boolean;
|
|
36
|
+
close(pr: RoundPrRef): boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type SupersedeOutcome =
|
|
40
|
+
| { kind: 'superseded'; round: number; prevPr: number; pr: number }
|
|
41
|
+
| { kind: 'kept'; round: number; prevPr: number; pr: number; reason: string };
|
|
42
|
+
|
|
43
|
+
type RoundPrOutcome = SupersedeOutcome | { kind: 'continued'; round: number; prevPr: number; pr: number };
|
|
44
|
+
|
|
45
|
+
export function roundPrRef(prUrl: string | undefined): RoundPrRef | null {
|
|
46
|
+
if (!prUrl) return null;
|
|
47
|
+
try {
|
|
48
|
+
const url = new URL(prUrl.trim());
|
|
49
|
+
const match = /^\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/.exec(url.pathname);
|
|
50
|
+
return url.protocol === 'https:' && url.hostname === 'github.com' && match
|
|
51
|
+
// GitHub owner/repo names are case-insensitive: one PR has one identity.
|
|
52
|
+
? { repository: `${match[1]}/${match[2]}`.toLowerCase(), number: Number(match[3]), url: `https://github.com/${match[1]}/${match[2]}/pull/${match[3]}` }
|
|
53
|
+
: null;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const prId = (pr: RoundPrRef): string => `${pr.repository}#${pr.number}`;
|
|
60
|
+
|
|
61
|
+
const commentMarker = (prev: RoundPrRef, next: RoundPrRef): string => `<!-- elanous:round-pr-superseded ${prId(prev)} -> ${prId(next)} -->`;
|
|
62
|
+
|
|
63
|
+
function roundPrSupersedeComment(input: { runId: string | null; round: number; prev?: RoundPrRef; next: RoundPrRef }): string {
|
|
64
|
+
return `${input.prev ? `${commentMarker(input.prev, input.next)}\n` : ''}Supervisor round ${input.round}${input.runId ? ` of ${input.runId}` : ''}: superseded by #${input.next.number} (${input.next.url}) — same run and goal, the rework round opened a new PR. Branch preserved.`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* One tracker per supervised run. `record(results, round)` after each round: for every goal whose newest PR
|
|
69
|
+
* differs from a PR an earlier round of this run opened, the earlier one is superseded exactly once —
|
|
70
|
+
* only while it is an open draft, unmerged, without a protected label, in the same repository.
|
|
71
|
+
*/
|
|
72
|
+
export function createRoundPrTracker(input: {
|
|
73
|
+
runId: string | null;
|
|
74
|
+
adapters: RoundPrAdapters;
|
|
75
|
+
observe?: (event: string, data: Record<string, unknown>) => void;
|
|
76
|
+
}) {
|
|
77
|
+
const byGoal = new Map<string, Array<{ ref: RoundPrRef; merged: boolean; round: number }>>();
|
|
78
|
+
const handled = new Set<string>();
|
|
79
|
+
// The link comment is written once per PR even when the close after it fails and is retried next round.
|
|
80
|
+
const commented = new Set<string>();
|
|
81
|
+
const emit = (event: string, data: Record<string, unknown>): void => {
|
|
82
|
+
try { (input.observe ?? ((e, d) => debug.log('self-dev.supervisor', e, d)))(event, data); } catch { /* observation must not interrupt supervision */ }
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const supersede = (prev: RoundPrRef, next: RoundPrRef, round: number): SupersedeOutcome => {
|
|
86
|
+
const base = { round, prevPr: prev.number, pr: next.number };
|
|
87
|
+
if (prev.repository !== next.repository) return { kind: 'kept', ...base, reason: 'other-repository' };
|
|
88
|
+
if (prId(prev) === prId(next)) return { kind: 'kept', ...base, reason: 'same-pr' };
|
|
89
|
+
let view: RoundPrView | null = null;
|
|
90
|
+
try { view = input.adapters.view(prev); } catch { view = null; }
|
|
91
|
+
if (!view) return { kind: 'kept', ...base, reason: 'view-unobserved' };
|
|
92
|
+
if (view.state !== 'OPEN' || view.mergedAt) return { kind: 'kept', ...base, reason: `state:${view.mergedAt ? 'MERGED' : view.state}` };
|
|
93
|
+
if (!view.isDraft) return { kind: 'kept', ...base, reason: 'not-draft' };
|
|
94
|
+
const held = view.labels.find((label) => PROTECTED_LABELS.has(label));
|
|
95
|
+
if (held) return { kind: 'kept', ...base, reason: `label:${held}` };
|
|
96
|
+
// Label first: a closed but unlabelled PR would fall outside every later sweep (draft-sweep rule).
|
|
97
|
+
let labelled = view.labels.includes(SUPERSEDED_LABEL);
|
|
98
|
+
if (!labelled) {
|
|
99
|
+
try { labelled = input.adapters.addLabel(prev, SUPERSEDED_LABEL); } catch { labelled = false; }
|
|
100
|
+
}
|
|
101
|
+
if (!labelled) return { kind: 'kept', ...base, reason: 'label-failed' };
|
|
102
|
+
const id = prId(prev);
|
|
103
|
+
if (view.comments.some((body) => body.includes(commentMarker(prev, next)))) commented.add(id);
|
|
104
|
+
if (!commented.has(id)) {
|
|
105
|
+
let ok = false;
|
|
106
|
+
try { ok = input.adapters.comment(prev, roundPrSupersedeComment({ runId: input.runId, round, prev, next })); } catch { ok = false; }
|
|
107
|
+
if (!ok) return { kind: 'kept', ...base, reason: 'comment-failed' };
|
|
108
|
+
commented.add(id);
|
|
109
|
+
}
|
|
110
|
+
let closed = false;
|
|
111
|
+
try { closed = input.adapters.close(prev); } catch { closed = false; }
|
|
112
|
+
return closed ? { kind: 'superseded', ...base } : { kind: 'kept', ...base, reason: 'close-failed' };
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/** Unfinished supersedes, keyed by the earlier PR; `next` stays the first successor so retries link the same PR. */
|
|
116
|
+
const pending = new Map<string, { prev: { ref: RoundPrRef; merged: boolean }; next: RoundPrRef }>();
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
/** Record one round's results, then (re)try every unfinished supersede — even when this round has no PR. */
|
|
120
|
+
record(results: readonly SelfDevJobResult[], round: number): RoundPrOutcome[] {
|
|
121
|
+
const outcomes: RoundPrOutcome[] = [];
|
|
122
|
+
for (const result of results) {
|
|
123
|
+
const ref = roundPrRef(result.prUrl);
|
|
124
|
+
if (!ref) continue;
|
|
125
|
+
const key = resumeKey(result.feature);
|
|
126
|
+
const seen = byGoal.get(key) ?? [];
|
|
127
|
+
// A PR already seen in this run is never a new successor — only a new PR is.
|
|
128
|
+
const known = seen.find((entry) => prId(entry.ref) === prId(ref));
|
|
129
|
+
if (known) {
|
|
130
|
+
known.merged ||= result.merged === true;
|
|
131
|
+
// A carried (skipped) shard is not a new round's work.
|
|
132
|
+
if (result.resumeDisposition !== 'skip' && known.round < round) {
|
|
133
|
+
outcomes.push({ kind: 'continued', round, prevPr: known.ref.number, pr: ref.number });
|
|
134
|
+
emit('round-pr-continued', { runId: input.runId, round, prevPr: known.ref.number, pr: ref.number });
|
|
135
|
+
}
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
// A carried result this run has not seen before was opened by an earlier process — not this run's PR,
|
|
139
|
+
// so it is neither recorded nor allowed to supersede anything.
|
|
140
|
+
if (result.resumeDisposition === 'skip') continue;
|
|
141
|
+
// Only PRs opened in an earlier round are superseded — never a sibling of the same round.
|
|
142
|
+
for (const prev of seen) {
|
|
143
|
+
const id = prId(prev.ref);
|
|
144
|
+
if (prev.round >= round || handled.has(id) || pending.has(id)) continue;
|
|
145
|
+
if (prev.merged) {
|
|
146
|
+
handled.add(id);
|
|
147
|
+
outcomes.push({ kind: 'kept', round, prevPr: prev.ref.number, pr: ref.number, reason: 'merged' });
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
pending.set(id, { prev, next: ref });
|
|
151
|
+
}
|
|
152
|
+
seen.push({ ref, merged: result.merged === true, round });
|
|
153
|
+
byGoal.set(key, seen);
|
|
154
|
+
}
|
|
155
|
+
for (const [id, { prev: entry, next }] of [...pending]) {
|
|
156
|
+
const prev = entry.ref;
|
|
157
|
+
// A result may have reported the earlier PR merged after it was queued — re-checked right before any write.
|
|
158
|
+
const outcome: SupersedeOutcome = entry.merged
|
|
159
|
+
? { kind: 'kept', round, prevPr: prev.number, pr: next.number, reason: 'merged' }
|
|
160
|
+
: supersede(prev, next, round);
|
|
161
|
+
outcomes.push(outcome);
|
|
162
|
+
// Retry only what was not observed or not applied; a decided keep/supersede is final for this run.
|
|
163
|
+
if (outcome.kind === 'superseded' || !/^(?:view-unobserved|label-failed|comment-failed|close-failed)$/.test(outcome.reason)) {
|
|
164
|
+
handled.add(id);
|
|
165
|
+
pending.delete(id);
|
|
166
|
+
}
|
|
167
|
+
if (outcome.kind === 'superseded') {
|
|
168
|
+
emit('round-pr-superseded', { runId: input.runId, round, prevPr: prev.number, pr: next.number });
|
|
169
|
+
} else {
|
|
170
|
+
emit('round-pr-supersede-kept', { runId: input.runId, round, prevPr: prev.number, pr: next.number, reason: outcome.reason });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return outcomes;
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function ghEnv(): Record<string, string> {
|
|
179
|
+
const env: Record<string, string> = {};
|
|
180
|
+
for (const [k, v] of Object.entries(process.env)) if (v !== undefined && !/^(https?|all|no)_proxy$/i.test(k)) env[k] = v;
|
|
181
|
+
return env;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Real gh adapters: proxy variables removed, App identity when available (never the personal login by choice). */
|
|
185
|
+
export async function defaultRoundPrAdapters(deps: {
|
|
186
|
+
appToken?: () => string | null;
|
|
187
|
+
run?: (args: string[], env: Record<string, string>) => { status: number | null; stdout: string };
|
|
188
|
+
inTest?: () => boolean;
|
|
189
|
+
} = {}): Promise<RoundPrAdapters> {
|
|
190
|
+
const env = ghEnv();
|
|
191
|
+
let tokenSource: () => string | null = () => null;
|
|
192
|
+
try {
|
|
193
|
+
tokenSource = deps.appToken ?? (await import('../auth/github-app-token.js')).githubAutomationToken;
|
|
194
|
+
} catch { /* no App credential module — every write refuses */ }
|
|
195
|
+
const freshToken = (): string | null => { try { return tokenSource(); } catch { return null; } };
|
|
196
|
+
const gh = (args: string[], token?: string | null) => {
|
|
197
|
+
const callEnv = { ...env };
|
|
198
|
+
if (token) callEnv.GH_TOKEN = token;
|
|
199
|
+
return deps.run
|
|
200
|
+
? deps.run(args, callEnv)
|
|
201
|
+
: spawnSync('gh', args, { encoding: 'utf8', env: callEnv as NodeJS.ProcessEnv, timeout: 30_000 });
|
|
202
|
+
};
|
|
203
|
+
// Writes (label · comment · close) run only as the automation App — the token is fetched per write, so a
|
|
204
|
+
// token that was missing or expired earlier in the run recovers; without it the write refuses and is retried.
|
|
205
|
+
// Reads may use the machine login.
|
|
206
|
+
const write = (op: string, pr: RoundPrRef, args: string[]): boolean => {
|
|
207
|
+
const token = freshToken();
|
|
208
|
+
if (!token) {
|
|
209
|
+
try { debug.log('self-dev.supervisor', 'round-pr-write-refused', { op, pr: pr.number, reason: 'no-app-token' }); } catch { /* fail-open */ }
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
return gh(args, token).status === 0;
|
|
213
|
+
};
|
|
214
|
+
const inTest = deps.inTest ?? ((): boolean => process.env.NODE_ENV === 'test');
|
|
215
|
+
return {
|
|
216
|
+
view(pr) {
|
|
217
|
+
if (inTest()) return null;
|
|
218
|
+
const out = gh(['pr', 'view', String(pr.number), '--repo', pr.repository, '--json', 'state,isDraft,labels,mergedAt,comments'], freshToken());
|
|
219
|
+
if (out.status !== 0) return null;
|
|
220
|
+
try {
|
|
221
|
+
const parsed = JSON.parse(out.stdout) as { state?: unknown; isDraft?: unknown; labels?: unknown; mergedAt?: unknown; comments?: unknown };
|
|
222
|
+
if (typeof parsed.state !== 'string' || typeof parsed.isDraft !== 'boolean' || !Array.isArray(parsed.labels)) return null;
|
|
223
|
+
const labels = parsed.labels.map((label) => (label as { name?: unknown })?.name);
|
|
224
|
+
if (labels.some((name) => typeof name !== 'string')) return null;
|
|
225
|
+
// Unverifiable comments make the whole view unobserved — no write may follow it.
|
|
226
|
+
if (!Array.isArray(parsed.comments)) return null;
|
|
227
|
+
const comments = parsed.comments.map((comment) => (comment as { body?: unknown })?.body);
|
|
228
|
+
if (comments.some((body) => typeof body !== 'string')) return null;
|
|
229
|
+
return { state: parsed.state, isDraft: parsed.isDraft, labels: labels as string[], mergedAt: typeof parsed.mergedAt === 'string' && parsed.mergedAt ? parsed.mergedAt : null, comments: comments as string[] };
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
addLabel(pr, label) {
|
|
235
|
+
return !inTest() && write('label', pr, ['pr', 'edit', String(pr.number), '--repo', pr.repository, '--add-label', label]);
|
|
236
|
+
},
|
|
237
|
+
comment(pr, body) {
|
|
238
|
+
return !inTest() && write('comment', pr, ['pr', 'comment', String(pr.number), '--repo', pr.repository, '--body', body]);
|
|
239
|
+
},
|
|
240
|
+
close(pr) {
|
|
241
|
+
return !inTest() && write('close', pr, ['pr', 'close', String(pr.number), '--repo', pr.repository]);
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
@@ -28,6 +28,7 @@ import type { RunIdSource } from '../harness/harness-space.js';
|
|
|
28
28
|
import { withFileLockSync } from '../storage/file-lock.js';
|
|
29
29
|
import type { SelfDevJobResult, SelfDevGoal } from './orchestrate.js';
|
|
30
30
|
import type { SupervisorStopReason } from './run-supervisor.js';
|
|
31
|
+
import type { SupervisorNext } from './supervisor-verdict-edges.js';
|
|
31
32
|
import { loadMirroredRunRecord, mirrorRunRecord } from './run-record-mirror.js';
|
|
32
33
|
|
|
33
34
|
export interface SelfDevRunParticipant {
|
|
@@ -92,6 +93,8 @@ export interface SelfDevRunState {
|
|
|
92
93
|
parkedResolution?: SelfDevRunResolution;
|
|
93
94
|
/** The closed supervisor termination reason, persisted independently from per-goal results. */
|
|
94
95
|
supervisorStopReason?: SupervisorStopReason;
|
|
96
|
+
/** Named next step at supervisor termination; legacy checkpoints omit it. */
|
|
97
|
+
next?: SupervisorNext;
|
|
95
98
|
/** ★ C(2026-07-21·HITL 정황 품질) — 이 run 을 구동하는 오케스트레이터 프로세스 pid. 살아있으면
|
|
96
99
|
* non-terminal goal 은 **실행중(running)** 이지 막힌(interrupted) 게 아니다. 죽었으면(오케스트레이터
|
|
97
100
|
* killed) 진짜 interrupted. isPidAlive 로 구분해 라이브 잡을 parked/repair-signals 에서 제외. */
|
|
@@ -311,13 +314,14 @@ export function recordSelfDevRunSupervisorStop(
|
|
|
311
314
|
runId: string,
|
|
312
315
|
supervisorStopReason: SupervisorStopReason,
|
|
313
316
|
dir = selfDevRunsDir(),
|
|
317
|
+
next?: SupervisorNext,
|
|
314
318
|
): SupervisorStopPersistenceOutcome {
|
|
315
319
|
try {
|
|
316
320
|
if (!existsSync(dir)) return { outcome: 'missing-directory' };
|
|
317
321
|
return withFileLockSync(join(dir, `${runId}.lock`), () => {
|
|
318
322
|
const state = loadSelfDevRunFromPath(runPath(runId, dir));
|
|
319
323
|
if (!state) return { outcome: 'missing-record' };
|
|
320
|
-
writeSelfDevRun({ ...state, updatedAt: Date.now(), supervisorStopReason }, dir);
|
|
324
|
+
writeSelfDevRun({ ...state, updatedAt: Date.now(), supervisorStopReason, ...(next === undefined ? {} : { next }) }, dir);
|
|
321
325
|
return { outcome: 'persisted' };
|
|
322
326
|
});
|
|
323
327
|
} catch {
|
|
@@ -6,6 +6,8 @@ import type { SelfDevJobResult } from './orchestrate.js';
|
|
|
6
6
|
import { recordSelfDevRunSupervisorStop, selfDevRunsDir } from './run-store.js';
|
|
7
7
|
import { debug } from '../debug/log.js';
|
|
8
8
|
import { sweepFrozenMerges } from '../self-implement/frozen-merges.js';
|
|
9
|
+
import { routeSupervisorVerdict, type SupervisorNext } from './supervisor-verdict-edges.js';
|
|
10
|
+
import { recordTaskAgentShadowMove, type TaskAgentShadowInput } from '../task-agent/shadow.js';
|
|
9
11
|
|
|
10
12
|
export interface SupervisorRound {
|
|
11
13
|
round: number;
|
|
@@ -186,6 +188,7 @@ function formatGoalPlanRevisionObservation(results: readonly SelfDevJobResult[])
|
|
|
186
188
|
export type SupervisorJobResult = SelfDevJobResult & {
|
|
187
189
|
reviewReason?: string;
|
|
188
190
|
salvage?: 'launched' | 'parked';
|
|
191
|
+
next?: SupervisorNext;
|
|
189
192
|
};
|
|
190
193
|
|
|
191
194
|
export function decideNextRun(input: {
|
|
@@ -459,6 +462,11 @@ export interface SuperviseRunOptions {
|
|
|
459
462
|
runStore?: { runId?: string; operatorDir?: string; dir?: string; isolatedDir?: string };
|
|
460
463
|
observe?: (event: string, data: Record<string, unknown>) => void;
|
|
461
464
|
sweepPendingMerges?: typeof sweepFrozenMerges;
|
|
465
|
+
/**
|
|
466
|
+
* TASK-AGENT-SHADOW — 멈춤이 확정되면 판단부를 그림자로 부른다(기본 `recordTaskAgentShadowMove`).
|
|
467
|
+
* `false` 면 끈다. 던져도 런의 결말은 바뀌지 않는다(fail-soft).
|
|
468
|
+
*/
|
|
469
|
+
taskAgentShadow?: ((input: TaskAgentShadowInput) => unknown) | false;
|
|
462
470
|
observeDeliverables?: () => Promise<{
|
|
463
471
|
readonly deployFindings: ReadonlyMap<string, { target: string; findings?: readonly DeployVerifyFinding[] | readonly unknown[] }>;
|
|
464
472
|
readonly unmeasured: readonly unknown[];
|
|
@@ -592,9 +600,13 @@ export async function superviseRun(opts: SuperviseRunOptions): Promise<Superviso
|
|
|
592
600
|
} catch { /* fail-open */ }
|
|
593
601
|
|
|
594
602
|
if (decision.action === 'stop') {
|
|
603
|
+
const reason = decision.stopReason ?? 'unregistered-stop';
|
|
604
|
+
const next = routeSupervisorVerdict(reason, results).next;
|
|
605
|
+
try { debug.log('self-dev.supervisor', 'verdict-edge', { runId, reason, next }); } catch { /* observation must not interrupt supervision */ }
|
|
606
|
+
results = results.map((result) => ({ ...result, next }));
|
|
595
607
|
if (runId && decision.stopReason) {
|
|
596
608
|
const storage = resolveSupervisorStopStorage(opts.runStore);
|
|
597
|
-
const persistence = recordSelfDevRunSupervisorStop(runId, decision.stopReason, storage.path);
|
|
609
|
+
const persistence = recordSelfDevRunSupervisorStop(runId, decision.stopReason, storage.path, next);
|
|
598
610
|
const storageObservation = {
|
|
599
611
|
storagePath: storage.path,
|
|
600
612
|
storageSource: storage.source,
|
|
@@ -608,6 +620,14 @@ export async function superviseRun(opts: SuperviseRunOptions): Promise<Superviso
|
|
|
608
620
|
});
|
|
609
621
|
} catch { /* fail-open */ }
|
|
610
622
|
}
|
|
623
|
+
if (decision.stopReason && opts.taskAgentShadow !== false) {
|
|
624
|
+
// TASK-AGENT-SHADOW — 판단부를 그림자로 부른다(실행 0). 어떤 오류도 런의 결말을 바꾸지 않는다.
|
|
625
|
+
try {
|
|
626
|
+
await (opts.taskAgentShadow ?? recordTaskAgentShadowMove)({ runId, stopReason: decision.stopReason, results });
|
|
627
|
+
} catch (error) {
|
|
628
|
+
try { debug.log('task-agent', 'shadow-move-failed', { runId, stopReason: decision.stopReason, error: String(error) }); } catch { /* fail-open */ }
|
|
629
|
+
}
|
|
630
|
+
}
|
|
611
631
|
return results;
|
|
612
632
|
}
|
|
613
633
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { SelfDevJobResult } from './orchestrate.js';
|
|
2
|
+
import type { SupervisorStopReason } from './run-supervisor.js';
|
|
3
|
+
|
|
4
|
+
export type SupervisorNext = 'harvest' | 'scope-decompose' | 'self-review' | 'human-gate' | 'proposal';
|
|
5
|
+
export type SupervisorVerdictEdge = { readonly meaning: string; readonly next: SupervisorNext };
|
|
6
|
+
|
|
7
|
+
// 목표 규칙: 사람 관문은 결제·공개·자격·비가역 결정에만. 지금 max-rounds·provider-exhausted·step-timeout 이 human-gate 인 것은
|
|
8
|
+
// 그 규칙의 예외로 «그대로» 둔 값이다(시험에 고정 — 바꾸면 보인다).
|
|
9
|
+
export const SUPERVISOR_VERDICT_EDGES = {
|
|
10
|
+
converged: { meaning: 'run converged', next: 'harvest' },
|
|
11
|
+
'human-stopped': { meaning: 'human stopped the run', next: 'human-gate' },
|
|
12
|
+
'parent-signals-red': { meaning: 'parent decision signals are red', next: 'human-gate' },
|
|
13
|
+
'needs-human': { meaning: 'unresolved human or harvest decision', next: 'human-gate' },
|
|
14
|
+
'no-actionable-work': { meaning: 'no actionable work remains', next: 'self-review' },
|
|
15
|
+
'harvestable-awaiting-human': { meaning: 'implementation awaits harvest', next: 'harvest' },
|
|
16
|
+
'handed-off-to-salvage': { meaning: 'salvage already owns the work', next: 'harvest' },
|
|
17
|
+
'max-rounds': { meaning: 'round budget exhausted', next: 'human-gate' },
|
|
18
|
+
'no-progress': { meaning: 'run stalled', next: 'scope-decompose' },
|
|
19
|
+
'provider-exhausted': { meaning: 'provider quota exhausted', next: 'human-gate' },
|
|
20
|
+
'step-timeout': { meaning: 'step budget exhausted', next: 'human-gate' },
|
|
21
|
+
'decomposable-no-progress': { meaning: 'stalled with a decomposition proposal', next: 'scope-decompose' },
|
|
22
|
+
'review-unobserved': { meaning: 'review was not observed', next: 'self-review' },
|
|
23
|
+
'deliverable-unobserved': { meaning: 'deliverable was not observed', next: 'self-review' },
|
|
24
|
+
'deliverable-merged': { meaning: 'deliverable is merged', next: 'harvest' },
|
|
25
|
+
} as const satisfies Record<SupervisorStopReason, SupervisorVerdictEdge>;
|
|
26
|
+
|
|
27
|
+
const HUMAN_GATE_MERGE_REASONS: ReadonlySet<string> = new Set(['decision-signal-red', 'signal-incomplete', 'parent-unlanded', 'human-gate']);
|
|
28
|
+
|
|
29
|
+
/** Unknown stop vocabularies are proposals, never silently treated as approved edges. */
|
|
30
|
+
export function routeSupervisorVerdict(
|
|
31
|
+
reason: string,
|
|
32
|
+
results: readonly Partial<Pick<SelfDevJobResult, 'status' | 'stage' | 'merged' | 'prNumber' | 'prUrl' | 'blockReason' | 'mergeReason'>>[] = [],
|
|
33
|
+
): SupervisorVerdictEdge {
|
|
34
|
+
const edge = Object.hasOwn(SUPERVISOR_VERDICT_EDGES, reason)
|
|
35
|
+
? SUPERVISOR_VERDICT_EDGES[reason as SupervisorStopReason]
|
|
36
|
+
: undefined;
|
|
37
|
+
if (!edge) return { meaning: 'unregistered supervisor stop reason', next: 'proposal' };
|
|
38
|
+
if (reason !== 'needs-human') return edge;
|
|
39
|
+
if (results.some((result) => result.blockReason === 'parent-unlanded')) return edge;
|
|
40
|
+
// A human-gate reason on ANY result in the run keeps the whole run at the human gate.
|
|
41
|
+
if (results.some((result) => HUMAN_GATE_MERGE_REASONS.has(String(result.mergeReason)))) return edge;
|
|
42
|
+
const openPrs = results.filter((result) => result.status === 'done' && result.stage === 'pr-opened'
|
|
43
|
+
&& result.merged !== true && (result.prNumber !== undefined || Boolean(result.prUrl)));
|
|
44
|
+
if (openPrs.length === 0) return edge;
|
|
45
|
+
return { meaning: 'open PR ready for harvest', next: 'harvest' };
|
|
46
|
+
}
|
|
@@ -12,7 +12,7 @@ import { addHarnessQueue, type QueueSeat } from '../harness/harness-queue.js';
|
|
|
12
12
|
import { debug } from '../debug/log.js';
|
|
13
13
|
import { withFileLockSync } from '../storage/file-lock.js';
|
|
14
14
|
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
15
|
-
import {
|
|
15
|
+
import { checklistDevVersion, listChecklist, parseOwner } from '../release-loop/checklist.js';
|
|
16
16
|
import { getUserConfig } from '../user-config.js';
|
|
17
17
|
|
|
18
18
|
export const FOLLOW_UP_DEPTH_LIMIT = 3;
|
|
@@ -193,7 +193,7 @@ function defaultAppendDraft(stateRoot: string, record: FollowUpDraftRecord): voi
|
|
|
193
193
|
|
|
194
194
|
function defaultCellOwner(cellId: string): string | undefined {
|
|
195
195
|
try {
|
|
196
|
-
return listChecklist(
|
|
196
|
+
return listChecklist(checklistDevVersion()).items.find((item) => item.id === cellId)?.owner;
|
|
197
197
|
} catch {
|
|
198
198
|
return undefined;
|
|
199
199
|
}
|
|
@@ -2,9 +2,9 @@ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync,
|
|
|
2
2
|
import { dlopen, FFIType } from 'bun:ffi';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { dirname, join, resolve } from 'node:path';
|
|
5
|
-
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
5
|
+
import { effectiveInstanceRoot, prodInstanceRoot } from '../instance/resolve.js';
|
|
6
6
|
import { debug } from '../debug/log.js';
|
|
7
|
-
import { beginLandingMerge,
|
|
7
|
+
import { beginLandingMerge, type LandingFreeze } from '../release-loop/landing-freeze.js';
|
|
8
8
|
|
|
9
9
|
/** `manual`: the owning repository could not be determined — a sweep never resumes it; a person does. */
|
|
10
10
|
export interface FrozenMerge { prNumber: number; headCommit: string; repoRoot: string; manual?: true; /** Host goal document, so a resumed merge can still apply its «이 칸 완료» declaration (CL-AUTO). */ goalFile?: string }
|
|
@@ -182,8 +182,10 @@ export function admitLandingMerge(
|
|
|
182
182
|
root = effectiveInstanceRoot(),
|
|
183
183
|
hooks: { afterQueue?: () => void } = {},
|
|
184
184
|
claimKey?: { prNumber: number; repoRoot: string; headCommit?: string },
|
|
185
|
+
opts: { prodFreezeRoot?: string; forceReason?: string } = {},
|
|
185
186
|
): LandingAdmission {
|
|
186
187
|
let entry: FrozenMerge | null | undefined;
|
|
188
|
+
const prodFreezeRoot = opts.prodFreezeRoot ?? prodInstanceRoot();
|
|
187
189
|
// A held PR is merged by exactly one runner: the claim (repository + PR) is held from admission until the lander's
|
|
188
190
|
// merge has ended. Its queue entry stays until that merge is confirmed — `end(true)` removes this lander's own head;
|
|
189
191
|
// a failed or interrupted merge leaves it for the next resume sweep.
|
|
@@ -201,7 +203,7 @@ export function admitLandingMerge(
|
|
|
201
203
|
} finally { landing.end(); held?.release(); }
|
|
202
204
|
};
|
|
203
205
|
for (;;) {
|
|
204
|
-
const landing = beginLandingMerge(root);
|
|
206
|
+
const landing = beginLandingMerge(root, new Date(), prodFreezeRoot, opts.forceReason);
|
|
205
207
|
if (!landing.frozen) {
|
|
206
208
|
if (!claim && claimKey) {
|
|
207
209
|
const key = { prNumber: claimKey.prNumber, repoRoot: repoIdentity(claimKey.repoRoot), headCommit: claimKey.headCommit };
|
|
@@ -222,17 +224,19 @@ export function admitLandingMerge(
|
|
|
222
224
|
if (!entry) return { kind: 'held', freeze: landing.frozen };
|
|
223
225
|
queueFrozenMerge(entry, root);
|
|
224
226
|
hooks.afterQueue?.();
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
227
|
+
const afterQueue = beginLandingMerge(root, new Date(), prodFreezeRoot, opts.forceReason);
|
|
228
|
+
if (afterQueue.frozen) return { kind: 'held', freeze: afterQueue.frozen };
|
|
229
|
+
try {
|
|
230
|
+
const mine = { ...entry, repoRoot: repoIdentity(entry.repoRoot) };
|
|
231
|
+
claim = claimFrozenMerge(mine, root);
|
|
232
|
+
if (!claim) return { kind: 'taken' };
|
|
233
|
+
// A resume sweep already merged and dequeued it between our queueing and our claim: nothing left to merge.
|
|
234
|
+
const stillHeld = readQueue(root).some((item) => sameEntry(item, mine));
|
|
235
|
+
if (!stillHeld) { claim.release(); return { kind: 'taken' }; }
|
|
236
|
+
ownHead = mine;
|
|
237
|
+
debug.log('harness.merge', 'freeze-lifted-while-queueing', { pr: entry.prNumber });
|
|
238
|
+
} catch (error) { claim?.release(); throw error; }
|
|
239
|
+
finally { afterQueue.end(); }
|
|
236
240
|
}
|
|
237
241
|
}
|
|
238
242
|
|
|
@@ -259,16 +263,17 @@ export async function sweepFrozenMerges(
|
|
|
259
263
|
},
|
|
260
264
|
root = effectiveInstanceRoot(),
|
|
261
265
|
alreadyMerged: (entry: FrozenMerge) => boolean | Promise<boolean> = githubMergedAt,
|
|
266
|
+
prodFreezeRoot = prodInstanceRoot(),
|
|
262
267
|
): Promise<{ pending: number; merged: number }> {
|
|
263
|
-
const frozen = readLandingFreeze(root);
|
|
264
|
-
if (frozen) return { pending: readQueue(root).length, merged: 0 };
|
|
265
268
|
const pending = readQueue(root);
|
|
266
269
|
let merged = 0;
|
|
267
270
|
for (const entry of pending) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
+
const guard = beginLandingMerge(root, new Date(), prodFreezeRoot);
|
|
272
|
+
if (guard.frozen) break;
|
|
273
|
+
let claim: { release: () => void } | null = null;
|
|
271
274
|
try {
|
|
275
|
+
claim = claimFrozenMerge(entry, root);
|
|
276
|
+
if (!claim) continue; // another sweep or the original lander is on it
|
|
272
277
|
if (!readQueue(root).some((item) => sameEntry(item, entry))) continue; // taken back by its lander
|
|
273
278
|
if (entry.manual) { debug.log('harness.merge', 'resume-manual', { pr: entry.prNumber, repoRoot: entry.repoRoot }); continue; }
|
|
274
279
|
if (!(await alreadyMerged(entry))) {
|
|
@@ -289,7 +294,7 @@ export async function sweepFrozenMerges(
|
|
|
289
294
|
});
|
|
290
295
|
} catch (error) {
|
|
291
296
|
debug.log('harness.merge', 'resume-failed', { pr: entry.prNumber, error: String(error) });
|
|
292
|
-
} finally { claim
|
|
297
|
+
} finally { guard.end(); claim?.release(); }
|
|
293
298
|
}
|
|
294
299
|
return { pending: readQueue(root).length, merged };
|
|
295
300
|
}
|