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
|
@@ -6,11 +6,11 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import { TRACK_AGENT_FORBIDDEN_ACTION_REGEX, universeLaunch } from '../autopilot/track-agent.js';
|
|
8
8
|
import { debug } from '../debug/log.js';
|
|
9
|
-
import { surfaceEventsDbPath } from '../domains/surface-events.js';
|
|
9
|
+
import { openSurfaceEventsDb, recordEvent, surfaceEventsDbPath } from '../domains/surface-events.js';
|
|
10
10
|
import { judgeNeighbor } from '../loops/neighbors.js';
|
|
11
11
|
import { addHarnessQueue, harnessQueueIdForKey, harnessQueueOutcome, listHarnessQueue } from '../harness/harness-queue.js';
|
|
12
12
|
import { isRescueAllowedAction } from '../steward/rescue.js';
|
|
13
|
-
import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
|
|
13
|
+
import { DecisionLedger, type DecisionEntry, type DecisionOption, type Recommendation } from '../decisions/decision-ledger.js';
|
|
14
14
|
import { effectiveInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js';
|
|
15
15
|
import { loopEvent } from '../loops/observe.js';
|
|
16
16
|
import { getUserConfig, type PersonaLoopConfig, type SeatLoopConfig, type UserConfig } from '../user-config.js';
|
|
@@ -43,7 +43,7 @@ export type TcCandidate =
|
|
|
43
43
|
| { kind: 'publication-pr-approval-wait'; pr: number; title: string; readyAt: string; approvalWaitAt: string; paths: string[] }
|
|
44
44
|
| { kind: 'other-seat-cell-defect'; version: string; id: string; title: string; to: SeatId };
|
|
45
45
|
export type TcPullRequest = { number: number; title: string; body?: string; state: 'OPEN' | 'MERGED' | 'CLOSED'; isDraft: boolean; createdAt: string; updatedAt?: string; mergedAt?: string | null; readyAt?: string; approvalWaitAt?: string; paths?: string[]; reviewDecision?: string };
|
|
46
|
-
export type SeatEntry = { seat: string; ts?: string; at: string; modeDowngradeReason?: string; status: 'shadow' | 'attempting' | 'outcome-unknown' | 'queued' | 'refused' | 'held' | 'launched' | 'hitl' | 'wait' | 'asked' | 'answered' | 'awaiting-answer' | 'awaiting-xcheck' | 'awaiting-resolution' | 'resolved-by-neighbor' | 'rejected-no-evidence' | 'skipped-budget' | 'skipped-empty'; item?: SeatItem; candidate?: OpCandidate | TcCandidate; action?: SeatAction; reason?: string; runId?: string; queueId?: string; inquiry?: SeatInquiry; answer?: string; escalated?: boolean; xcheckRequestedAt?: string; xcheckQuestionId?: number; xcheckNeighbor?: SeatId; xcheckNote?: string; xcheckJudgmentEvidence?: string; xcheckWaitMinutes?: number };
|
|
46
|
+
export type SeatEntry = { seat: string; ts?: string; at: string; modeDowngradeReason?: string; status: 'shadow' | 'attempting' | 'outcome-unknown' | 'queued' | 'refused' | 'held' | 'launched' | 'hitl' | 'wait' | 'asked' | 'answered' | 'awaiting-answer' | 'awaiting-xcheck' | 'awaiting-resolution' | 'resolved-by-neighbor' | 'resolved' | 'rejected-no-evidence' | 'skipped-budget' | 'skipped-empty'; item?: SeatItem; candidate?: OpCandidate | TcCandidate; action?: SeatAction; reason?: string; runId?: string; queueId?: string; inquiry?: SeatInquiry; answer?: string; escalated?: boolean; xcheckRequestedAt?: string; xcheckQuestionId?: number; xcheckNeighbor?: SeatId; xcheckNote?: string; xcheckJudgmentEvidence?: string; xcheckWaitMinutes?: number };
|
|
47
47
|
export type SeatLoopResult = SeatEntry | { seat: string; status: 'skipped-off'; modeDowngradeReason?: string };
|
|
48
48
|
|
|
49
49
|
export function seatLoopTickLine(result: SeatLoopResult): string {
|
|
@@ -68,7 +68,7 @@ export type SeatDeps = {
|
|
|
68
68
|
now?: () => Date;
|
|
69
69
|
read?: (path: string) => string;
|
|
70
70
|
run?: (args: string[]) => Promise<string>;
|
|
71
|
-
enqueue?: (seat: string, text: string, root: string, idempotencyKey: string) => Promise<{ id: string }>;
|
|
71
|
+
enqueue?: (seat: string, text: string, root: string, idempotencyKey: string, item: SeatItem) => Promise<{ id: string }>;
|
|
72
72
|
queueOutcome?: (id: string, root: string) => ReturnType<typeof harnessQueueOutcome>;
|
|
73
73
|
queueItems?: (root: string) => ReturnType<typeof listHarnessQueue>;
|
|
74
74
|
queueIdForKey?: (key: string, root: string) => string | undefined;
|
|
@@ -375,6 +375,23 @@ function decisionSummary(text: string): string {
|
|
|
375
375
|
return Array.from(sentences).slice(0, 240).join('').trim();
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
+
function decisionCardMaterial(input: {
|
|
379
|
+
what: string; soFar: string; current: string; question: string; answer: string;
|
|
380
|
+
options: DecisionOption[]; recommendation: Recommendation; ifUntouched: string; evidence: string; confidence: string;
|
|
381
|
+
}): { scqa: { s: string; c: string; q: string; a: string }; pendingQuestion: string } {
|
|
382
|
+
const short = (value: string) => decisionSummary(value) || '관측되지 않음';
|
|
383
|
+
return {
|
|
384
|
+
scqa: { s: short(input.what), c: short(input.current), q: short(input.question), a: short(input.answer) },
|
|
385
|
+
pendingQuestion: [
|
|
386
|
+
`무엇: ${input.what}`, `지금까지: ${input.soFar}`, `지금 상태 재측: ${input.current}`,
|
|
387
|
+
...input.options.map((option) => `선택지 ${option.key} (${option.label}) 결과: ${option.consequence}`),
|
|
388
|
+
'skipped' in input.recommendation ? `권고 비움: ${input.recommendation.reason}` : `권고: ${input.recommendation.option} — ${input.recommendation.why}`,
|
|
389
|
+
`확신: ${input.confidence}`, `그냥 두면: ${input.ifUntouched}`, `근거: ${input.evidence}`,
|
|
390
|
+
`Q: ${input.question}`, `A: ${input.answer}`,
|
|
391
|
+
].join('\n'),
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
378
395
|
function decisionSourceRef(seat: string, item: SeatItem): string {
|
|
379
396
|
return item.source === 'request' && item.id.startsWith('coord:') ? item.id : decisionRef(seat, item);
|
|
380
397
|
}
|
|
@@ -388,7 +405,7 @@ function decisionReceipt(root: string, seat: string, item: SeatItem): DecisionEn
|
|
|
388
405
|
function handledKeys(ledger: readonly SeatEntry[], shadow: boolean, root?: string, liveSafe = false): Set<string> {
|
|
389
406
|
const inquiryRow = (entry: SeatEntry) => entry.action === 'seat-question' || entry.action === 'seat-answer' || !!entry.inquiry;
|
|
390
407
|
return new Set(ledger.filter((entry, index) => entry.status === 'asked' || entry.status === 'answered'
|
|
391
|
-
|| entry.status === 'resolved-by-neighbor'
|
|
408
|
+
|| entry.status === 'resolved-by-neighbor' || entry.status === 'resolved'
|
|
392
409
|
// RM3a: only TC's other-seat defect ask-shadows are deduplicated here — an ordinary inquiry recorded in shadow must still be delivered once questions turn on.
|
|
393
410
|
|| (entry.status === 'shadow' && entry.candidate?.kind === 'other-seat-cell-defect')
|
|
394
411
|
|| (entry.status === 'hitl' && (!entry.inquiry || (!!root && !!entry.item && !!decisionReceipt(root, entry.seat, entry.item))))
|
|
@@ -853,7 +870,7 @@ const SEAT_ACTION_RULES: ReadonlyArray<{ kind: 'wait' | 'harness' | 'decision';
|
|
|
853
870
|
{ kind: 'wait', signal: /실측\s*대기|(?:사람|실제)\s*기기(?!\s*(?:(?:실측|측정|확인)\s*)?완료)(?:\s*(?:실측|측정|확인|대기))?|실물\s*측정(?!\s*완료)/iu, source: 'content', resolvedBy: /(?:실측|측정|(?:사람|실제)\s*기기|실물\s*측정)\s*완료/iu },
|
|
854
871
|
{ kind: 'wait', signal: /\u{1F451}\s*확인\s*대기/iu, source: 'content', resolvedBy: /\u{1F451}\s*확인\s*완료/iu },
|
|
855
872
|
{ kind: 'decision', signal: TRACK_AGENT_FORBIDDEN_ACTION_REGEX, source: 'task' },
|
|
856
|
-
{ kind: 'decision', signal: /(?:마켓|레지스트리)(?:에|로)?\s*(?:게시|등록|발행)|사이트\s*운영\s*반영|SNS(?:에|로)?\s*(?:게시|발행|업로드)|공개\s*발행/iu, source: '
|
|
873
|
+
{ kind: 'decision', signal: /(?:마켓|레지스트리)(?:에|로)?\s*(?:게시|등록|발행)|사이트\s*운영\s*반영|SNS(?:에|로)?\s*(?:게시|발행|업로드)|공개\s*발행/iu, source: 'task' },
|
|
857
874
|
{ kind: 'harness', signal: /(?:마켓|레지스트리|사이트|SNS|공개)?(?:에|로)?\s*(?:게시|발행|배포|등록|운영\s*반영)(?:를?\s*위한)?\s*(?:준비|초안|원고|자료|구현|점검|테스트)|(?:마켓|레지스트리|사이트|SNS|공개)?(?:에|로)?\s*(?:게시|발행|배포|등록)(?:용|를?\s*위한)\s*(?:초안|원고|자료|코드|기능)/iu, source: 'task' },
|
|
858
875
|
];
|
|
859
876
|
|
|
@@ -861,9 +878,9 @@ export function planAction(item: SeatItem, seat?: string): { kind: 'decision' |
|
|
|
861
878
|
const task = `${item.id}\n${item.title}\n${item.text}`;
|
|
862
879
|
const content = `${task}\n${item.evidence ?? ''}`;
|
|
863
880
|
const preparation = SEAT_ACTION_RULES.find(({ kind }) => kind === 'harness')!;
|
|
864
|
-
const prepared = [...
|
|
881
|
+
const prepared = [...item.text.matchAll(new RegExp(preparation.signal.source, `${preparation.signal.flags.replace('g', '')}g`))];
|
|
865
882
|
const rule = SEAT_ACTION_RULES.map(({ kind, signal, source, resolvedBy }) => {
|
|
866
|
-
const text = source === 'content' ? content : task;
|
|
883
|
+
const text = kind === 'decision' ? item.text : source === 'content' ? content : task;
|
|
867
884
|
if (resolvedBy?.test(item.evidence ?? '')) return null;
|
|
868
885
|
const hits = [...text.matchAll(new RegExp(signal.source, `${signal.flags.replace('g', '')}g`))];
|
|
869
886
|
const hit = kind === 'decision' ? hits.find((match) => !prepared.some((prep) =>
|
|
@@ -893,11 +910,11 @@ function neighborLastSeen(root: string, isolated: boolean, now: Date, wanted: Re
|
|
|
893
910
|
if (!existsSync(path)) return last;
|
|
894
911
|
const db = new Database(path, { readonly: true });
|
|
895
912
|
try {
|
|
896
|
-
//
|
|
897
|
-
//
|
|
913
|
+
// Read outbound seat context and loop heartbeats across the whole journal,
|
|
914
|
+
// unlike context day's 24h display window, so a genuinely old ACK can be judged absent.
|
|
898
915
|
// Newest first and stop once every wanted seat has an ACK, so a long journal is not read and sorted in full each turn.
|
|
899
916
|
const statement = db.prepare(`SELECT ts, refs FROM events
|
|
900
|
-
WHERE surface IN ('coord:channel', 'context:session') AND direction='outbound' AND ts<=? ORDER BY ts DESC`);
|
|
917
|
+
WHERE surface IN ('coord:channel', 'context:session', 'loop:heartbeat') AND direction='outbound' AND ts<=? ORDER BY ts DESC`);
|
|
901
918
|
try {
|
|
902
919
|
const events = statement.iterate(now.toISOString()) as Iterable<{ ts: string; refs: string | null }>;
|
|
903
920
|
for (const event of events) {
|
|
@@ -936,13 +953,16 @@ function judgeSeatNeighbors(seat: string, config: SeatLoopConfig, deps: SeatDeps
|
|
|
936
953
|
if (judgment.state === 'absent') {
|
|
937
954
|
if (config.mode === 'live-safe' && judgment.action?.action === 'escalate' && isRescueAllowedAction('decision-card')) {
|
|
938
955
|
const ref = `seat-loop:neighbor:${seat}:${neighbor.id}:${lastSeenAt}`;
|
|
956
|
+
const options = [{ key: 'a', label: '사람 판단 후 별도 조치', consequence: '자리 루프는 대행하지 않는다' },
|
|
957
|
+
{ key: 'b', label: '현상 유지', consequence: '결측 상태를 계속 관측한다' }];
|
|
958
|
+
const recommendation = { option: 'b', why: '새 사건을 확인하기 전에는 이웃 업무를 대행하지 않는다' };
|
|
939
959
|
cardLedger(root, deps, now).raiseOnce({ title: `${seat}: 이웃 ${neighbor.id} 결측`, category: 'other',
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
960
|
+
...decisionCardMaterial({ what: `${seat} 이웃 ${neighbor.id} 결측`, soFar: '마지막 맥락 버스 사건 이후 새 ACK 없음',
|
|
961
|
+
current: `현재 시각 ${now.toISOString()}; 마지막 사건 ${lastSeenAt}; heartbeat ${neighbor.heartbeat.everyMinutes}분 × ${neighbor.heartbeat.missedTicks}회 결측`,
|
|
962
|
+
question: '결측 이웃의 업무를 어떻게 처리할까?', answer: '새 ACK까지 현상 유지하고 이웃 업무는 대행하지 않는다',
|
|
963
|
+
options, recommendation, confidence: '중간 — 사건의 부재만 관측됨',
|
|
964
|
+
ifUntouched: '결측 상태를 계속 관측하며 대행은 하지 않는다', evidence: `맥락 버스 마지막 사건 ${lastSeenAt}; ${ref}` }),
|
|
965
|
+
options, recommendation,
|
|
946
966
|
raisedBy: { agent: 'seat-loop' }, refs: [ref], raisedAt: now.toISOString(),
|
|
947
967
|
crossCheckSkipped: '이웃 결측으로 교차 확인 불가' }, ref);
|
|
948
968
|
action = 'decision-card';
|
|
@@ -976,6 +996,12 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
976
996
|
const directory = dirname(path);
|
|
977
997
|
return withSeatLock(directory, async () => {
|
|
978
998
|
const stateRoot = deps.root ?? effectiveInstanceRoot();
|
|
999
|
+
try {
|
|
1000
|
+
const db = openSurfaceEventsDb(deps.root ? join(stateRoot, 'surface_events.db') : surfaceEventsDbPath());
|
|
1001
|
+
try { recordEvent(db, { surface: 'loop:heartbeat', direction: 'outbound', kind: 'heartbeat',
|
|
1002
|
+
text: `seat loop ${seat} heartbeat`, refs: JSON.stringify({ seat }), ts: now.toISOString() }); }
|
|
1003
|
+
finally { db.close(); }
|
|
1004
|
+
} catch (error) { observe('heartbeat-write-failed', { seat, error: String(error).slice(0, 200) }); }
|
|
979
1005
|
const recorded = readSeatLedger(seat, deps, now);
|
|
980
1006
|
const ledger = config.mode === 'live-safe' ? reconciledQueueLedger(recorded, stateRoot, deps) : recorded;
|
|
981
1007
|
let turnError: unknown;
|
|
@@ -1012,10 +1038,17 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1012
1038
|
const question = proposal.inquiry?.question ?? proposal.item!.title;
|
|
1013
1039
|
const note = `${proposal.xcheckNote ?? ''} — 해결 가능하다고 했으나 ${waited}분 안 해소 안 됨`.trim();
|
|
1014
1040
|
if (!decisionReceipt(stateRoot, seat, proposal.item!)) {
|
|
1041
|
+
const options = [{ key: 'a', label: '승인', consequence: '사람 판단으로 진행' }, { key: 'b', label: '보류', consequence: '진행하지 않음' }];
|
|
1042
|
+
const recommendation = { option: 'b', why: `기한 ${waited}분 뒤에도 ${cell?.status ?? '질문 미해결'}이므로 착지 확인 전에는 진행하지 않는다` };
|
|
1043
|
+
const material = decisionCardMaterial({ what: `${seat} 질문 · ${proposal.item!.title}`, soFar: `이웃 ${neighbor} 해결 제안: ${proposal.xcheckNote ?? '기록 없음'}`,
|
|
1044
|
+
current: `기한 ${waited}분 경과 · 칸 상태 ${cell?.status ?? '질문 미해결'}`, question, answer: '재측상 미해결이므로 보류를 권고한다',
|
|
1045
|
+
options, recommendation, confidence: '중간 — 기한 경과와 미해결을 재측함', ifUntouched: '질문/칸은 미해결로 남는다',
|
|
1046
|
+
evidence: `${proposal.item!.evidence ?? '원천 근거 미기재'} · ${note} · ${decisionRef(seat, proposal.item!)}` });
|
|
1015
1047
|
await (deps.run ?? defaultRun)(['decisions', 'raise', '--title', `${seat}: ${question}`, '--category', 'other',
|
|
1016
|
-
'--s',
|
|
1048
|
+
'--s', material.scqa.s, '--c', material.scqa.c, '--q', material.scqa.q, '--a', material.scqa.a,
|
|
1049
|
+
'--pending-question', material.pendingQuestion,
|
|
1017
1050
|
'--option', 'a=승인:사람 판단으로 진행', '--option', 'b=보류:진행하지 않음',
|
|
1018
|
-
'--
|
|
1051
|
+
'--recommend', recommendation.option, '--why', recommendation.why, '--xcheck', `${neighbor}:${note}`,
|
|
1019
1052
|
'--agent', 'seat-loop', '--ref', decisionRef(seat, proposal.item!), '--json']);
|
|
1020
1053
|
}
|
|
1021
1054
|
const entry: SeatEntry = { ...proposal, ts: now.toISOString(), at: now.toISOString(), status: 'hitl' };
|
|
@@ -1313,10 +1346,18 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1313
1346
|
const check = answer ? ['--xcheck', `${requestedNeighbor}:${answer.note}`,
|
|
1314
1347
|
...(answer.agree ? [] : ['--dissent', `${requestedNeighbor}: ${answer.note}`])]
|
|
1315
1348
|
: ['--no-xcheck', `이웃 ${requestedNeighbor} 무응답 ${entry.xcheckWaitMinutes}분`];
|
|
1349
|
+
const options = [{ key: 'a', label: '승인', consequence: '사람 판단으로 진행' }, { key: 'b', label: '보류', consequence: '진행하지 않음' }];
|
|
1350
|
+
const recommendation = { option: 'b', why: '교차 확인에서 해소 근거가 확보되지 않았다' };
|
|
1351
|
+
const material = decisionCardMaterial({ what: `${seat} 질문 · ${item.title}`, soFar: `이웃 ${requestedNeighbor}에 교차 확인 요청`,
|
|
1352
|
+
current: answer ? `이웃 응답: ${answer.note}` : `이웃 ${requestedNeighbor} 무응답 ${entry.xcheckWaitMinutes}분`,
|
|
1353
|
+
question: ask.question, answer: '해소 근거 확인 전까지 보류를 권고한다', options, recommendation,
|
|
1354
|
+
confidence: answer ? '중간 — 이웃 응답을 확인함' : '낮음 — 기한 내 이웃 응답 없음',
|
|
1355
|
+
ifUntouched: '사람 판단 전 집행하지 않는다', evidence: `${item.evidence ?? '원천 근거 미기재'} · ${decisionRef(seat, item)}` });
|
|
1316
1356
|
await (deps.run ?? defaultRun)(['decisions', 'raise', '--title', `${seat}: ${ask.question}`, '--category', 'other',
|
|
1317
|
-
'--s',
|
|
1357
|
+
'--s', material.scqa.s, '--c', material.scqa.c, '--q', material.scqa.q, '--a', material.scqa.a,
|
|
1358
|
+
'--pending-question', material.pendingQuestion,
|
|
1318
1359
|
'--option', 'a=승인:사람 판단으로 진행', '--option', 'b=보류:진행하지 않음',
|
|
1319
|
-
'--
|
|
1360
|
+
'--recommend', recommendation.option, '--why', recommendation.why, ...check, '--agent', 'seat-loop', '--ref', decisionRef(seat, item), '--json']);
|
|
1320
1361
|
if (!decisionReceipt(root, seat, item)) throw new Error('decisions raise did not deliver a matching card');
|
|
1321
1362
|
entry.status = 'hitl';
|
|
1322
1363
|
xcheckEvent('raised', seat, requestedNeighbor, itemKey(item), answer ? answer.agree ? 'agree' : 'dissent' : 'timed-out');
|
|
@@ -1442,11 +1483,27 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1442
1483
|
if (action.kind === 'decision') {
|
|
1443
1484
|
const title = `${seat}: ${decisionSummary(action.text)}`;
|
|
1444
1485
|
const source = decisionSourceRef(seat, item);
|
|
1486
|
+
const options = [{ key: 'a', label: '사람 승인', consequence: '승인 후 별도로 집행' }, { key: 'b', label: '보류', consequence: '집행하지 않음' }];
|
|
1487
|
+
const recommendation = { option: 'b', why: `요청에서 걸린 문면 «${action.reason}»은 자동 집행 대상이 아니므로 보류한다` };
|
|
1488
|
+
let currentStatus: string | undefined;
|
|
1489
|
+
try {
|
|
1490
|
+
currentStatus = item.source === 'checklist' && item.version
|
|
1491
|
+
? (deps.checklistItems ?? ((version: string) => listChecklist(version, deps.root ?? releaseLedgerRoot()).items))(item.version)
|
|
1492
|
+
.find((cell) => cell.id === item.id && cell.owner === seat)?.status
|
|
1493
|
+
: item.source === 'request' ? listSeatRequests(stateRoot).find((row) => row.key === item.id && row.seat === seat)?.status
|
|
1494
|
+
: undefined;
|
|
1495
|
+
} catch (error) { observe('decision-source-unreadable', { seat, item: item.id, error: String(error).slice(0, 200) }); }
|
|
1496
|
+
const material = decisionCardMaterial({ what: `${seat} 배정 항목: ${item.title} 역할: ${inputs.role || '역할 근거 없음'}`, soFar: `요청 내용: ${item.text}`,
|
|
1497
|
+
current: `밑바탕 재측: ${currentStatus ?? '상태 미확인'}; 요청에서 금지 문면 «${action.reason}» 발견 — 실행 전 사람 승인 필요`,
|
|
1498
|
+
question: `금지 문면 «${action.reason}»이 걸린 요청을 승인할까?`, answer: '승인 전에는 집행하지 않고 보류한다',
|
|
1499
|
+
options, recommendation, confidence: '높음 — 요청에 금지 문면 직접 일치',
|
|
1500
|
+
ifUntouched: '자동 집행되지 않고 해당 칸은 보류 상태로 남는다',
|
|
1501
|
+
evidence: `${item.evidence?.trim() || '추가 근거 미기재'} · 요청 ${source} · 걸린 낱말 ${action.reason}` });
|
|
1445
1502
|
const raised = await run(['decisions', 'raise', '--title', title, '--category', 'other',
|
|
1446
|
-
'--s',
|
|
1447
|
-
'--
|
|
1503
|
+
'--s', material.scqa.s, '--c', material.scqa.c, '--q', material.scqa.q, '--a', material.scqa.a,
|
|
1504
|
+
'--pending-question', material.pendingQuestion,
|
|
1448
1505
|
'--option', 'a=사람 승인:승인 후 별도로 집행', '--option', 'b=보류:집행하지 않음',
|
|
1449
|
-
'--
|
|
1506
|
+
'--recommend', recommendation.option, '--why', recommendation.why, '--no-xcheck', '자리 루프 · 이웃 교환은 DEC-XCHECK ②',
|
|
1450
1507
|
'--agent', 'seat-loop', '--ref', source, '--json']);
|
|
1451
1508
|
const decision: unknown = JSON.parse(raised.trim());
|
|
1452
1509
|
if (!decision || typeof decision !== 'object' || typeof (decision as { id?: unknown }).id !== 'string') {
|
|
@@ -1461,25 +1518,38 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1461
1518
|
const limit = repeatStopLimit(config);
|
|
1462
1519
|
const attempts = unlandedAttemptCount(seat, item, ledger, stateRoot, deps);
|
|
1463
1520
|
if (attempts >= limit && !personReopened(ledger, seat, item, stateRoot)) {
|
|
1521
|
+
// Re-read the underlying item at the decision boundary, not the picked snapshot.
|
|
1522
|
+
const current = item.source === 'checklist' && item.version
|
|
1523
|
+
? (deps.checklistItems ?? ((version: string) => listChecklist(version, deps.root ?? releaseLedgerRoot()).items))(item.version)
|
|
1524
|
+
.find((cell) => cell.id === item.id && cell.owner === seat)?.status
|
|
1525
|
+
: item.source === 'request' ? listSeatRequests(stateRoot).find((row) => row.key === item.id && row.seat === seat)?.status
|
|
1526
|
+
: undefined;
|
|
1527
|
+
if (current === 'green' || current === 'done') {
|
|
1528
|
+
entry.status = 'resolved';
|
|
1529
|
+
entry.reason = `밑바탕 재측: ${current} — 반복 정지 카드 불필요`;
|
|
1530
|
+
append(path, entry);
|
|
1531
|
+
observe('repeat-recovered', { seat, item: item.id, status: current });
|
|
1532
|
+
return entry;
|
|
1533
|
+
}
|
|
1464
1534
|
const reason = repeatStopReason(attempts);
|
|
1465
1535
|
entry.status = 'held';
|
|
1466
1536
|
entry.reason = reason;
|
|
1467
1537
|
const ref = decisionRef(seat, item);
|
|
1468
1538
|
const prior = new DecisionLedger({ stateDir: stateRoot }).list({ status: 'all' }).find((card) => card.refs?.includes(ref) && card.status === 'open');
|
|
1469
1539
|
if (!prior) {
|
|
1540
|
+
const options = [
|
|
1541
|
+
{ key: 'a', label: '다시 연다', consequence: '결정 뒤에 자리 루프가 한 번 더 시도할 수 있다' },
|
|
1542
|
+
{ key: 'b', label: '멈춘다', consequence: '같은 칸은 다시 발사하지 않는다' },
|
|
1543
|
+
];
|
|
1544
|
+
const recommendation = { option: 'b', why: `착지 없는 ${attempts}회 재시도보다 현재 칸을 멈추는 편이 중복 실행을 피한다` };
|
|
1470
1545
|
cardLedger(stateRoot, deps, now).raiseOnce({
|
|
1471
|
-
title: `${seat}: ${item.id} 반복 착지 0`,
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
options: [
|
|
1479
|
-
{ key: 'a', label: '다시 연다', consequence: '결정 뒤에 자리 루프가 한 번 더 시도할 수 있다' },
|
|
1480
|
-
{ key: 'b', label: '멈춘다', consequence: '같은 칸은 다시 발사하지 않는다' },
|
|
1481
|
-
],
|
|
1482
|
-
recommendation: { skipped: true, reason: '착지 없는 반복만으로 계속할지 자동 결정하지 않는다' },
|
|
1546
|
+
title: `${seat}: ${item.id} 반복 착지 0`, category: 'other',
|
|
1547
|
+
...decisionCardMaterial({ what: `${seat} 칸 ${item.id} · ${item.title}`, soFar: `${attempts}회 착지 없이 종료`,
|
|
1548
|
+
current: `발행 직전 밑바탕 재측: ${current ?? '상태 미확인'}; 같은 칸 착지 0`,
|
|
1549
|
+
question: '이 칸을 계속할까, 멈출까?', answer: '중복 발사를 멈추고 사람의 재개 결정을 기다린다',
|
|
1550
|
+
options, recommendation, confidence: current ? '높음 — 밑바탕 상태 재측됨' : '낮음 — 밑바탕 상태 미확인',
|
|
1551
|
+
ifUntouched: '같은 칸은 다시 발사되지 않는다', evidence: `${item.evidence ?? '원천 근거 미기재'} · ${ref} · 완료되지 않은 시도 ${attempts}회` }),
|
|
1552
|
+
options, recommendation,
|
|
1483
1553
|
raisedBy: { agent: 'seat-loop' },
|
|
1484
1554
|
refs: [ref],
|
|
1485
1555
|
raisedAt: now.toISOString(),
|
|
@@ -1492,7 +1562,7 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1492
1562
|
}
|
|
1493
1563
|
let queued: { id: string };
|
|
1494
1564
|
try {
|
|
1495
|
-
queued = await (deps.enqueue ?? ((assigned, text, root, idempotencyKey) => addHarnessQueue({ seat: assigned, say: text, idempotencyKey }, { root })))(seat, action.text, stateRoot, queueKey(seat, item));
|
|
1565
|
+
queued = await (deps.enqueue ?? ((assigned, text, root, idempotencyKey) => addHarnessQueue({ seat: assigned, say: text, idempotencyKey }, { root })))(seat, action.text, stateRoot, queueKey(seat, item), item);
|
|
1496
1566
|
} catch (error) {
|
|
1497
1567
|
// Same key, different task body: never adopt the other work. Refuse this turn; the key frees once that item finishes.
|
|
1498
1568
|
if (!/idempotency key collision/.test(String(error))) throw error;
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
// 그래서 결과를 «세 값»으로 낸다: 발사 / 저작 전 중단 / 전제 검사 중단.
|
|
13
13
|
import { linesOutsideFencedCode } from '../self-implement/goal-author.js';
|
|
14
14
|
import { recordAuthoredGoal } from './authored-goal-trace.js';
|
|
15
|
+
import { findAskPathCandidates, type AskPathCandidateOptions } from './ask-path-candidates.js';
|
|
15
16
|
import { retiredEntranceNotice, type EntranceDeclaration } from './entrance-registry.js';
|
|
16
17
|
import {
|
|
17
18
|
classifyConcurrentAuthoring,
|
|
@@ -35,6 +36,8 @@ import { dirname, isAbsolute as isAbsolutePath, relative as relativePath, resolv
|
|
|
35
36
|
import {
|
|
36
37
|
applyClarificationReply,
|
|
37
38
|
planClarificationIntake,
|
|
39
|
+
selfAnswerGoalDocumentClarifications,
|
|
40
|
+
type GoalClarificationSelfResolver,
|
|
38
41
|
} from '../self-implement/goal-author-clarification.js';
|
|
39
42
|
import { classifyReauthoredAsk, countMissingAuthoredConstraintMarkers, askGoalTypeDeclaration, declaredGoalType, parseGoalId } from '../self-implement/goal-author.js';
|
|
40
43
|
import { observeFrontNodeEntry } from './graph-front-nodes.js';
|
|
@@ -143,6 +146,9 @@ export interface AskLaunchFlowDeps {
|
|
|
143
146
|
/** Optional structured clarification adapter. Omitted callers retain the
|
|
144
147
|
* existing CLI line-input contract exactly. */
|
|
145
148
|
readClarification?(clarification: GoalDocumentClarification): Promise<string>;
|
|
149
|
+
selfResolveClarification?: GoalClarificationSelfResolver;
|
|
150
|
+
/** Replaces the goal document after a self-answer; production entrances pass an atomic writer. */
|
|
151
|
+
writeGoalDocument?(path: string, document: string): void;
|
|
146
152
|
readFile(path: string): string;
|
|
147
153
|
writeFile(path: string, data: string): void;
|
|
148
154
|
cwd(): string;
|
|
@@ -159,6 +165,8 @@ export interface AskLaunchFlowDeps {
|
|
|
159
165
|
/** ⛔ 못 얻으면 `null` — 같은 이유. */
|
|
160
166
|
recentAuthoringSamples(): RecentAuthoringSample[] | null;
|
|
161
167
|
authorGoal(args: readonly string[], options: Record<string, unknown>): Promise<AskAuthoredGoal>;
|
|
168
|
+
/** Code-search seam for the one automatic no-target-paths retry. */
|
|
169
|
+
findPathCandidates?: (askText: string, options: AskPathCandidateOptions) => ReturnType<typeof findAskPathCandidates>;
|
|
162
170
|
/** AUTHOR-TRACE seam (tests). */
|
|
163
171
|
recordAuthoredGoal?: typeof recordAuthoredGoal;
|
|
164
172
|
/** Pod launches reserve a host slot before goal authoring begins. */
|
|
@@ -188,6 +196,7 @@ export interface AskLaunchFlowInput {
|
|
|
188
196
|
readonly askFile?: string;
|
|
189
197
|
/** Optional repository root used only for code grounding during goal authoring. */
|
|
190
198
|
readonly groundingCwd?: string;
|
|
199
|
+
readonly authorGrade?: import('../self-implement/goal-author.js').GoalAuthorGradeSelection;
|
|
191
200
|
readonly liveRunWindowMinutes: number;
|
|
192
201
|
readonly recentChangeWindowDays: number;
|
|
193
202
|
readonly forceRequested: boolean;
|
|
@@ -724,16 +733,27 @@ async function intakeClarifications(
|
|
|
724
733
|
return effectiveGoalFile;
|
|
725
734
|
}
|
|
726
735
|
if (mode === 'deferred-noninteractive') {
|
|
727
|
-
//
|
|
728
|
-
//
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
736
|
+
// The resolver comes in through deps (I/O seam): production entrances pass the author
|
|
737
|
+
// self-resolver; a caller that injects none keeps the previous deferred behaviour.
|
|
738
|
+
if (selfResolveClarifications.value && deps.selfResolveClarification) try {
|
|
739
|
+
const resolution = await selfAnswerGoalDocumentClarifications(document, {
|
|
740
|
+
path: 'ask', ask: input.askText, resolver: deps.selfResolveClarification,
|
|
741
|
+
});
|
|
742
|
+
if (resolution.newlyAnswered > 0) {
|
|
743
|
+
(deps.writeGoalDocument ?? deps.writeFile)(effectiveGoalFile, resolution.document);
|
|
744
|
+
document = resolution.document;
|
|
745
|
+
}
|
|
746
|
+
} catch (error) {
|
|
747
|
+
deps.log('ask-clarify', { goalFile: effectiveGoalFile, reason: 'self-answer-failed', error: error instanceof Error ? error.message : String(error) }, 'warn');
|
|
748
|
+
}
|
|
749
|
+
const remaining = planClarificationIntake(document, false).pending;
|
|
750
|
+
// 미답으로 남은 문항만 기존 답변 창구를 안내한다.
|
|
751
|
+
deps.print(`[ask] ⑴b ⚠️ 미답 되묻기 ${remaining.length}건 — 저작은 그대로 간다 · 구현 전에 LLM 릴레이가 먼저 답하고, 못 하면 미답으로 진행한다 (사람을 기다리지 않는다)`);
|
|
752
|
+
for (const item of remaining) {
|
|
733
753
|
deps.print(`[ask] ❓ [${item.questionId}] ${item.question}`);
|
|
734
754
|
item.options.forEach((option, index) => deps.print(`[ask] ${index}) ${option.label}`));
|
|
735
755
|
}
|
|
736
|
-
if (
|
|
756
|
+
if (remaining.length > 0) {
|
|
737
757
|
const goalArg = deps.relativeToCwd(effectiveGoalFile);
|
|
738
758
|
deps.print(`[ask] ▶ 답하려면: elanous self clarify answer ${goalArg} <questionId> <옵션번호> (자유 답은 --other "<문장>")`);
|
|
739
759
|
deps.print(`[ask] ▶ 답한 뒤 재저작: elanous self author --supersedes ${goalArg}`);
|
|
@@ -771,6 +791,7 @@ async function intakeClarifications(
|
|
|
771
791
|
const reauthored = await deps.authorGoal([], {
|
|
772
792
|
cwd: deps.cwd(),
|
|
773
793
|
...(input.groundingCwd === undefined ? {} : { groundingCwd: input.groundingCwd }),
|
|
794
|
+
...(input.authorGrade === undefined ? {} : { authorGrade: input.authorGrade }),
|
|
774
795
|
fromClarification: `${deps.relativeToCwd(effectiveGoalFile)}#${seedQuestionId}`,
|
|
775
796
|
reauthorFromAnsweredClarification: true,
|
|
776
797
|
selfResolveClarifications: selfResolveClarifications.value,
|
|
@@ -831,7 +852,7 @@ export async function runAskLaunchFlow(
|
|
|
831
852
|
if (launchPreflight === false) return { kind: 'stopped-before-authoring' };
|
|
832
853
|
|
|
833
854
|
await deps.beforeAuthoring?.();
|
|
834
|
-
const authorAndPreflight = async (): Promise<{
|
|
855
|
+
const authorAndPreflight = async (authorAskText: string = input.askText): Promise<{
|
|
835
856
|
readonly goalFile: string;
|
|
836
857
|
readonly decision: ReturnType<typeof decideAskPreflight>;
|
|
837
858
|
}> => {
|
|
@@ -850,9 +871,10 @@ export async function runAskLaunchFlow(
|
|
|
850
871
|
};
|
|
851
872
|
// ⭐ RFC §5 5단계 — 확인된 implement 저작만 활성 구현 그래프에 이름을 잇는다.
|
|
852
873
|
observeFrontNodeEntry('author', { ...frontObservation, provenance: 'authoring-start' });
|
|
853
|
-
const authored = await deps.authorGoal([
|
|
874
|
+
const authored = await deps.authorGoal([authorAskText], {
|
|
854
875
|
cwd: deps.cwd(),
|
|
855
876
|
...(input.groundingCwd === undefined ? {} : { groundingCwd: input.groundingCwd }),
|
|
877
|
+
...(input.authorGrade === undefined ? {} : { authorGrade: input.authorGrade }),
|
|
856
878
|
...(goalType === null ? {} : { goalType }),
|
|
857
879
|
// ⛔ 인라인 ask(`harness say`)는 askFile 이 없다 — 그 칸을 넘기면 저작기가 빈 줄을 쓴다.
|
|
858
880
|
...(() => {
|
|
@@ -992,8 +1014,26 @@ export async function runAskLaunchFlow(
|
|
|
992
1014
|
const first = authored;
|
|
993
1015
|
reauthored = true;
|
|
994
1016
|
deps.print('[ask] ⑴ ⚠️ 대상 경로를 못 찾아 자동 재저작 한 번을 시도한다');
|
|
995
|
-
|
|
1017
|
+
let candidates: ReturnType<typeof findAskPathCandidates> = { tokens: [], paths: [] };
|
|
1018
|
+
if (parseAskTargetPathHintsResult(input.askText).labelMissing) {
|
|
1019
|
+
try {
|
|
1020
|
+
candidates = (deps.findPathCandidates ?? findAskPathCandidates)(input.askText, { cwd: deps.cwd() });
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
candidates = { tokens: [], paths: [], error: error instanceof Error ? error.message : String(error) };
|
|
1023
|
+
}
|
|
1024
|
+
deps.log('ask-auto-target-candidates', {
|
|
1025
|
+
tokens: candidates.tokens, paths: candidates.paths,
|
|
1026
|
+
...(candidates.error === undefined ? {} : { error: candidates.error }),
|
|
1027
|
+
}, candidates.error === undefined ? 'info' : 'warn');
|
|
1028
|
+
}
|
|
1029
|
+
if (candidates.paths.length > 0) {
|
|
1030
|
+
deps.print(`[ask] ⑴ 코드에서 후보 경로 ${candidates.paths.length}개를 찾아 넣고 다시 저작한다: ${candidates.paths.join(' · ')}`);
|
|
1031
|
+
}
|
|
1032
|
+
authored = await authorAndPreflight(candidates.paths.length > 0
|
|
1033
|
+
? `대상 경로: ${candidates.paths.join(' · ')}\n${input.askText}`
|
|
1034
|
+
: input.askText);
|
|
996
1035
|
deps.log('ask-auto-reauthor', {
|
|
1036
|
+
candidatePathCount: candidates.paths.length,
|
|
997
1037
|
firstGoalFile: first.goalFile,
|
|
998
1038
|
secondGoalFile: authored.goalFile,
|
|
999
1039
|
firstPathCount: first.decision.result.paths.length,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { statSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
export interface AskPathCandidateOptions {
|
|
6
|
+
readonly cwd: string;
|
|
7
|
+
readonly run?: (command: string, args: readonly string[], options: { cwd: string; encoding: 'utf8' }) => string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type AskPathCandidates =
|
|
11
|
+
| { readonly tokens: string[]; readonly paths: string[]; readonly error?: undefined }
|
|
12
|
+
| { readonly tokens: string[]; readonly paths: string[]; readonly error: string };
|
|
13
|
+
|
|
14
|
+
const STOP_WORDS = new Set(['the', 'and', 'test', 'src', 'scripts', 'file', 'path', 'paths', 'code', 'with', 'from', 'this', 'that', 'into', 'for', 'then', 'when', 'have', 'does', 'should', 'will', 'please', 'target', 'change']);
|
|
15
|
+
const WORD = /[-/]?[A-Za-z][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_.-]+)*/g;
|
|
16
|
+
|
|
17
|
+
function existingFile(fragment: string, cwd: string): string | null {
|
|
18
|
+
const path = fragment.replace(/^\.\//, '');
|
|
19
|
+
if (!path.includes('/') && !/\.[A-Za-z][A-Za-z0-9]*$/.test(path)) return null;
|
|
20
|
+
const absolute = resolve(cwd, path);
|
|
21
|
+
const rel = relative(cwd, absolute);
|
|
22
|
+
if (!rel || rel.startsWith('..') || isAbsolute(rel)) return null;
|
|
23
|
+
try { return statSync(absolute).isFile() ? rel.split('\\').join('/') : null; }
|
|
24
|
+
catch { return null; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Find existing code paths by lexical evidence from an unmodified ask. */
|
|
28
|
+
export function findAskPathCandidates(askText: string, { cwd, run = execFileSync }: AskPathCandidateOptions): AskPathCandidates {
|
|
29
|
+
const scores = new Map<string, Set<string>>();
|
|
30
|
+
const tokens: string[] = [];
|
|
31
|
+
const seen = new Set<string>();
|
|
32
|
+
const chunks = [...askText.matchAll(/`([^`]+)`/g)].map((match) => match[1]!).concat([askText]);
|
|
33
|
+
for (const chunk of chunks) {
|
|
34
|
+
for (const match of chunk.matchAll(WORD)) {
|
|
35
|
+
const raw = match[0];
|
|
36
|
+
const token = raw.replace(/^\//, '');
|
|
37
|
+
const file = existingFile(token, cwd);
|
|
38
|
+
if (file) {
|
|
39
|
+
const hits = scores.get(file) ?? new Set<string>();
|
|
40
|
+
hits.add(token);
|
|
41
|
+
scores.set(file, hits);
|
|
42
|
+
}
|
|
43
|
+
if (STOP_WORDS.has(token.toLowerCase()) || seen.has(token)) continue;
|
|
44
|
+
if (!file && !token.includes('/') && !token.includes('.') && !token.includes('-') && !token.includes('_')
|
|
45
|
+
&& !/[a-z][A-Z]/.test(token) && token.length < 4) continue;
|
|
46
|
+
seen.add(token);
|
|
47
|
+
tokens.push(token);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const token of tokens) {
|
|
51
|
+
let matches: string;
|
|
52
|
+
try {
|
|
53
|
+
matches = run('rg', ['-l', '-F', '--glob', '!*.test.ts', '--', token, 'src', 'scripts'], { cwd, encoding: 'utf8' });
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if ((error as { status?: number }).status === 1) continue; // rg: no matches
|
|
56
|
+
return { tokens, paths: [], error: error instanceof Error ? error.message : String(error) };
|
|
57
|
+
}
|
|
58
|
+
for (const path of matches.split(/\r?\n/).filter(Boolean)) {
|
|
59
|
+
const hits = scores.get(path) ?? new Set<string>();
|
|
60
|
+
hits.add(token);
|
|
61
|
+
scores.set(path, hits);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
tokens,
|
|
66
|
+
paths: [...scores].sort(([a, aHits], [b, bHits]) => bHits.size - aHits.size || a.localeCompare(b, 'en')).slice(0, 5).map(([path]) => path),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -721,6 +721,8 @@ export function buildSelfImplementDevSpec(o: {
|
|
|
721
721
|
ground?: boolean;
|
|
722
722
|
draft: boolean;
|
|
723
723
|
autoMerge?: boolean;
|
|
724
|
+
/** Pod child: stop at merge-ready so the launching host re-gates (and checks the landing freeze) before merging. */
|
|
725
|
+
mergeByHost?: boolean;
|
|
724
726
|
openPr?: boolean;
|
|
725
727
|
autoReview?: boolean;
|
|
726
728
|
autoReviewSource?: DevSelectionSource;
|
|
@@ -755,6 +757,8 @@ export function buildSelfImplementDevSpec(o: {
|
|
|
755
757
|
...(o.runId ? { runId: o.runId } : {}),
|
|
756
758
|
self: {
|
|
757
759
|
draft: o.draft,
|
|
760
|
+
// FREEZE-POD: this flag used to be dropped here, so `self implement --merge-by-host` in a Pod merged by itself (#24478).
|
|
761
|
+
...(o.mergeByHost === true ? { mergeByHost: true } : {}),
|
|
758
762
|
...(o.childLlm ? { childLlm: o.childLlm } : {}),
|
|
759
763
|
...(o.maxWaitSec !== undefined ? { maxWaitSec: o.maxWaitSec } : {}),
|
|
760
764
|
...(o.ground ? { ground: true } : {}),
|
|
@@ -31,7 +31,7 @@ export type DraftDecision = { action: 'close' | 'keep'; reason: string };
|
|
|
31
31
|
|
|
32
32
|
// Labels the sweeper must never close: an approval-waiting state and the human «keep» / release holds (R-PRK3).
|
|
33
33
|
// RELGUARD's release-path hold waits for OP approval — a long wait is not «stale» (TC review #23217).
|
|
34
|
-
const PROTECTED_LABELS: ReadonlySet<string> = new Set<string>([...PR_LABELS
|
|
34
|
+
export const PROTECTED_LABELS: ReadonlySet<string> = new Set<string>([...PR_LABELS
|
|
35
35
|
.filter((label) => label.sweep.action === 'exclude' || (label.sweep.action === 'none' && (label.axis === 'state' || label.axis === 'addon')))
|
|
36
36
|
.map((label) => label.name), RELEASE_PATH_LABEL]);
|
|
37
37
|
|
|
@@ -16,6 +16,7 @@ import { resolveOrchestrateConcurrency } from './orchestrate.js';
|
|
|
16
16
|
import type { FailureClassification, SelfDevGoal, SelfDevJobResult } from './orchestrate.js';
|
|
17
17
|
import { superviseRun, type SupervisorDecision } from './run-supervisor.js';
|
|
18
18
|
import { readDecomposeProposals, applyDecomposeProposals } from './decompose-proposal.js';
|
|
19
|
+
import { createRoundPrTracker, defaultRoundPrAdapters, type RoundPrAdapters } from './round-pr-supersede.js';
|
|
19
20
|
import { createHash } from 'node:crypto';
|
|
20
21
|
import { debug } from '../debug/log.js';
|
|
21
22
|
|
|
@@ -211,6 +212,8 @@ export interface OrchestrateCliDeps {
|
|
|
211
212
|
pipelineDeps?: DevPipelineDeps;
|
|
212
213
|
/** 분해 제안과 골 개정 시도 되채움 조회(테스트 주입). 기본 readDecomposeProposals(원장). */
|
|
213
214
|
readProposals?: typeof readDecomposeProposals;
|
|
215
|
+
/** DRAFT-TRIAGE — gh seams for superseding the previous supervisor round's draft PR (tests). */
|
|
216
|
+
roundPrAdapters?: RoundPrAdapters;
|
|
214
217
|
}
|
|
215
218
|
|
|
216
219
|
/**
|
|
@@ -399,6 +402,23 @@ export async function runSelfOrchestrateCliCommand(
|
|
|
399
402
|
};
|
|
400
403
|
// ⭐ 루프는 «공용»이다(run-supervisor.superviseRun) — 단일 실행도 같은 자를 쓴다.
|
|
401
404
|
// ⛔ 여기가 아는 것은 「어떻게 다시 거나」뿐이고, 「걸까 말까」는 그 자가 안다.
|
|
405
|
+
// DRAFT-TRIAGE — a rework round of the same goal opens a new PR; the previous round's draft is superseded.
|
|
406
|
+
let roundPrTracker: ReturnType<typeof createRoundPrTracker> | undefined;
|
|
407
|
+
let supervisedRound = 0;
|
|
408
|
+
const recordRoundPrs = async (rs: readonly SelfDevJobResult[]): Promise<void> => {
|
|
409
|
+
try {
|
|
410
|
+
// Once a PR is tracked, every round runs — an unfinished supersede is retried even without a new PR.
|
|
411
|
+
if (!roundPrTracker && !rs.some((r) => r.prUrl)) return;
|
|
412
|
+
roundPrTracker ??= createRoundPrTracker({
|
|
413
|
+
runId: process.env.ELANOUS_RUN_ID ?? rs.find((r) => r.runId?.trim())?.runId ?? null,
|
|
414
|
+
adapters: deps.roundPrAdapters ?? await defaultRoundPrAdapters(),
|
|
415
|
+
});
|
|
416
|
+
roundPrTracker.record(rs, supervisedRound);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
try { debug.log('self-dev.supervisor', 'round-pr-record-failed', { round: supervisedRound, error: String(error) }); } catch { /* fail-open */ }
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
await recordRoundPrs(results);
|
|
402
422
|
results = await superviseRun({
|
|
403
423
|
initial: results,
|
|
404
424
|
limits,
|
|
@@ -467,6 +487,8 @@ export async function runSelfOrchestrateCliCommand(
|
|
|
467
487
|
const resumeHold = currentDecision?.needsHuman ?? [];
|
|
468
488
|
const next = await exec(spec, { ...baseRuntime, resumeFrom: [...previous], ...(resumeHold.length > 0 ? { resumeHold } : {}) }, pipelineDeps);
|
|
469
489
|
exitCode = next.exitCode;
|
|
490
|
+
supervisedRound += 1;
|
|
491
|
+
await recordRoundPrs(next.results);
|
|
470
492
|
return next.results;
|
|
471
493
|
},
|
|
472
494
|
});
|