elanous 0.2.14 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/{1307-6e7e0377c1db0bdc.js → 1307-b46d45b705e8c9ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-3cb52e5ea8fc2fa5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-c1e20d7d338713f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-482c5dbde699947c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-8c2d9526164f5d09.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-e6b84ab07d3567d5.js → page-a0e2a6d1bfb52fc5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c53598cfce6156f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-13a63953f53c6a23.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-5d3c8429d04f3831.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-3193bdc0bbab047a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-6107ca8ddd5e3bdd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-dce686e582c23937.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-160089123a40dd91.js → page-1a61c7ca1ceb3ae7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-51f811fa119308b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-4167348049166852.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-de52e8fdf9095f7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-ca3952cd78a83ca2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-e7e10cd3386222e5.js → page-5b042736aa113756.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-1ebfe049c640e171.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-57924393e3e4e539.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-0f5cd57ca928f42b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-abe8e3b74bea1ffc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-d28573d6792b4275.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-58043db78ba519ae.js → page-59f03fda76c03d22.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-357750dfb91bf960.js → page-d751de5f8062ff5e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-fe1759f94003b32b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d64c984e855c8c68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-9afdee66b820c76e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-f8da5dc6f3752276.js → page-1ea64acadd704ace.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-3544f154c87b43ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +1 -0
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
- package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
- package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +15 -15
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +15 -15
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +15 -15
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +15 -15
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +15 -15
- package/apps/pwa/out/ceo/index.html +1 -0
- package/apps/pwa/out/ceo/index.txt +33 -0
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +15 -15
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +15 -15
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +15 -15
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +15 -15
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +15 -15
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +15 -15
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +15 -15
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +15 -15
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +15 -15
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +15 -15
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +15 -15
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +15 -15
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +15 -15
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +15 -15
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +15 -15
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +15 -15
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +15 -15
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +15 -15
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +15 -15
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +15 -15
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +16 -16
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +15 -15
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +15 -15
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +15 -15
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +15 -15
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +15 -15
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +15 -15
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +15 -15
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +15 -15
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +15 -15
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +15 -15
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +15 -15
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +15 -15
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +15 -15
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +15 -15
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +15 -15
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +15 -15
- package/graphs/doc-gen/doc-gen.yaml +24 -0
- package/graphs/doc-gen/recipes.yaml +3 -0
- package/graphs/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/orchestrator/orchestrator.yaml +7 -0
- package/graphs/orchestrator/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -2
- package/graphs/steward/recipes.yaml +2 -1
- package/package.json +1 -1
- package/scripts/brand/check.ts +7 -5
- package/scripts/ci-public-export-import-gate.ts +154 -0
- package/scripts/ci-test-interference-gate.ts +5 -1
- package/scripts/ci-typecheck-changed.ts +82 -31
- package/scripts/docs/doc-rot.ts +164 -0
- package/scripts/docs/generate-graph-anatomy.ts +437 -0
- package/scripts/docs-cli-check.ts +4 -4
- package/scripts/expose-rubric-suggest.ts +18 -12
- package/scripts/goals/goal-census.ts +343 -0
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +30 -3
- package/scripts/marketing/generate-ax-brief.ts +140 -0
- package/scripts/marketing/generate-feature-map.ts +118 -0
- package/scripts/measure/author-section-contribution.ts +84 -0
- package/scripts/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/cut-branch.ts +43 -13
- package/scripts/release-loop/gate-node.ts +82 -14
- package/scripts/release-loop/vault-note-node.ts +257 -8
- package/scripts/release-story/draft.ts +57 -9
- package/scripts/rhythm/daily.ts +263 -76
- package/scripts/rhythm/monthly.ts +97 -0
- package/scripts/rhythm/weekly.ts +30 -9
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/src/acp/client.ts +17 -2
- package/src/agent-env/profile.ts +161 -0
- package/src/agent-mission/driver.ts +51 -1
- package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
- package/src/agent-mission/supervisor-metrics.ts +218 -0
- package/src/agent-substrate/pr-reviewer.ts +2 -0
- package/src/ask-user-question/pending-questions.ts +5 -0
- package/src/auth/github-app-token.ts +112 -13
- package/src/autopilot/build/llm-conflict-merge.ts +32 -6
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/briefing/brief-items.ts +276 -0
- package/src/budget/usage-outcomes.ts +204 -0
- package/src/claims/claims-render.ts +14 -5
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/brief-cli.ts +73 -0
- package/src/cli/decisions-cli.ts +9 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/intake-cli.ts +46 -17
- package/src/cli/know-cli.ts +20 -0
- package/src/cli/logs-cli.ts +43 -1
- package/src/cli/pod-cli.ts +2 -0
- package/src/cli/pr-cli.ts +80 -12
- package/src/cli/release-cli.ts +66 -29
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/usage-cli.ts +26 -0
- package/src/context-bus/context-now-public.ts +16 -6
- package/src/context-bus/context-now.ts +3 -1
- package/src/context-bus/coord-events.ts +6 -4
- package/src/decisions/decision-cards.ts +33 -7
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/discord-self-message.ts +9 -0
- package/src/domains/alert-audit.ts +271 -0
- package/src/domains/core-tools.ts +4 -3
- package/src/domains/ops-facts-tool.ts +31 -1
- package/src/flow-loop/split.ts +24 -0
- package/src/harness/harness-cli-command.ts +147 -17
- package/src/harness/harness-incidents.ts +2 -0
- package/src/harness/harness-pod-dispatch.ts +67 -8
- package/src/harness/harness-queue.ts +380 -50
- package/src/harness/harness-seams.ts +13 -3
- package/src/harness/helper-repair.ts +218 -0
- package/src/harness/helper-scan.ts +27 -4
- package/src/harness/launch-stamp.ts +236 -0
- package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
- package/src/harness/ledger-write-guard.ts +35 -0
- package/src/harness/nested-elanous-policy.ts +114 -0
- package/src/harness/pod-memory-policy.ts +15 -0
- package/src/harness/repository-name.ts +55 -0
- package/src/hq/fence-audit.ts +90 -0
- package/src/hq/hq.ts +70 -6
- package/src/hq/lease.ts +9 -7
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +31 -0
- package/src/hq/transfer.ts +185 -0
- package/src/index.ts +189 -13
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +65 -21
- package/src/intake-plane/front-classifier.ts +6 -0
- package/src/intake-plane/items.ts +3 -2
- package/src/intake-plane/lens.ts +177 -0
- package/src/intake-plane/route.ts +145 -1
- package/src/knowledge/know-find.ts +105 -0
- package/src/llm.ts +75 -19
- package/src/loops/budget.ts +36 -5
- package/src/loops/checker.ts +2 -2
- package/src/loops/orchestrator/author-depth.ts +65 -9
- package/src/loops/orchestrator/author-input-check.ts +8 -3
- package/src/loops/orchestrator/author-ledger.ts +3 -1
- package/src/loops/orchestrator/finish-rate.ts +14 -3
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +107 -29
- package/src/loops/orchestrator/traffic.ts +117 -11
- package/src/loops/registry.ts +142 -61
- package/src/maturity/bot-slash-catalog.ts +11 -2
- package/src/maturity/feature-maturity.ts +4 -3
- package/src/maturity/tui-slash-availability.ts +40 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/http-server.ts +28 -14
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/schedules-read.ts +13 -0
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/oauth/overload-failover-launch.ts +75 -0
- package/src/oauth/overload-failover.ts +137 -0
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +6 -3
- package/src/release-loop/checklist.ts +19 -5
- package/src/release-loop/feature-store.ts +59 -31
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +98 -29
- package/src/seat-dispatch/seat-ask.ts +33 -6
- package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
- package/src/seat-dispatch/seat-request-ledger.ts +133 -0
- package/src/seat-loop/seat-loop.ts +215 -12
- package/src/self-dev/ask-launch-flow.ts +22 -1
- package/src/self-dev/authored-goal-trace.ts +64 -0
- package/src/self-dev/dev-cli.ts +19 -1
- package/src/self-dev/dev-pipeline.ts +14 -9
- package/src/self-dev/draft-sweep.ts +90 -10
- package/src/self-dev/draft-triage-rules.ts +25 -8
- package/src/self-dev/entrance-registry.ts +10 -6
- package/src/self-dev/old-door.ts +73 -0
- package/src/self-dev/orchestrate.ts +15 -1
- package/src/self-dev/review-provider-fallback.ts +6 -1
- package/src/self-dev/run-supervisor.ts +49 -2
- package/src/self-implement/app-api-budget.ts +166 -0
- package/src/self-implement/budget-gate.ts +7 -5
- package/src/self-implement/follow-up-goals.ts +289 -0
- package/src/self-implement/frozen-merges.ts +3 -2
- package/src/self-implement/goal-author.ts +50 -6
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/heal-intake.ts +120 -2
- package/src/self-implement/heal-triage.ts +44 -0
- package/src/self-implement/host-regate.ts +9 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +325 -47
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +42 -7
- package/src/self-implement/run-ledger.ts +6 -0
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +45 -16
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/session-runtime/index.ts +2 -0
- package/src/session-runtime/retry-api.ts +87 -0
- package/src/session-runtime/retry-policy.ts +107 -6
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +20 -2
- package/src/task-cards/card-cli.ts +25 -0
- package/src/task-cards/card-store.ts +5 -3
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
- package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
- package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +70 -9
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +166 -46
- package/src/task-orchestrator/surfaces/self-implement.ts +7 -1
- package/src/telegram-commands.ts +5 -0
- package/src/telegram.ts +72 -4
- package/src/user-config.ts +64 -7
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +0 -1
- package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → rv9cierV0jSOvDBm1g0XB}/_ssgManifest.js +0 -0
|
@@ -21,6 +21,7 @@ import { loadLayeredPersonaDirs, resolveRepositoryPersonaDir, resolveStatePerson
|
|
|
21
21
|
import { orderedPersonaTodos, personaTodoPath, readPersonaTodos, type PersonaTodo } from '../persona/persona-todo.js';
|
|
22
22
|
import { PersonaRegistry } from '../persona/registry.js';
|
|
23
23
|
import { recordSeatAskShadow } from '../seat-dispatch/seat-ask.js';
|
|
24
|
+
import { appendSeatRequestRows, listSeatRequests, withSeatRequestLedgerLock } from '../seat-dispatch/seat-request-ledger.js';
|
|
24
25
|
import { answerCrossCheck, answerSeat, askCrossCheck, askSeat, crossCheckAnswer, deliveredSeatQuestion, hasSeatAnswer, isSeatId, seatCrossChecks, seatQuestions, type CrossCheckJudgment, type SeatId } from '../seat-dispatch/seat-questions.js';
|
|
25
26
|
import { queryRunningRuns } from '../self-implement/running-runs.js';
|
|
26
27
|
import { checklistHistory, listChecklist, type Checklist } from '../release-loop/checklist.js';
|
|
@@ -42,10 +43,18 @@ export type TcCandidate =
|
|
|
42
43
|
| { kind: 'publication-pr-approval-wait'; pr: number; title: string; readyAt: string; approvalWaitAt: string; paths: string[] }
|
|
43
44
|
| { kind: 'other-seat-cell-defect'; version: string; id: string; title: string; to: SeatId };
|
|
44
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 };
|
|
45
|
-
export type SeatEntry = { seat: string; ts?: string; at: string; status: 'shadow' | 'attempting' | 'outcome-unknown' | 'queued' | 'refused' | '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 SeatLoopResult = SeatEntry | { seat: string; status: 'skipped-off' };
|
|
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 };
|
|
47
|
+
export type SeatLoopResult = SeatEntry | { seat: string; status: 'skipped-off'; modeDowngradeReason?: string };
|
|
48
|
+
|
|
49
|
+
export function seatLoopTickLine(result: SeatLoopResult): string {
|
|
50
|
+
const suffix = (result.status === 'shadow' || result.status === 'skipped-off') && result.modeDowngradeReason
|
|
51
|
+
? ` (live-safe 요청 · 이유: ${result.modeDowngradeReason})` : '';
|
|
52
|
+
return `seat loop ${result.seat}: ${result.status}${'runId' in result && result.runId ? ` ${result.runId}` : ''}${suffix}`;
|
|
53
|
+
}
|
|
47
54
|
export type PersonaShadowEntry = { personaId: string; ts: string; status: 'shadow' | 'skipped-empty'; todo: PersonaTodo | null; action?: 'decision' | 'harness' | 'wait'; what?: string };
|
|
48
55
|
export type SeatDeps = {
|
|
56
|
+
/** Version stamp for decision cards the seat loop raises. Default = the ledger's resolver (`git reflog` on the repo, up to 10 s) — tests inject a fixed one. */
|
|
57
|
+
resolveDecisionVersion?: (at: string) => { released: string | null; dev: string | null; codename: string | null };
|
|
49
58
|
personaConfig?: PersonaLoopConfig;
|
|
50
59
|
personaRegistry?: PersonaRegistry;
|
|
51
60
|
personaDir?: string;
|
|
@@ -77,6 +86,8 @@ export type SeatDeps = {
|
|
|
77
86
|
stallDelivery?: (message: MessageEnvelope, key: string, root: string) => boolean;
|
|
78
87
|
/** Open cards from the decision ledger; injected only for isolated ledger tests. */
|
|
79
88
|
pendingDecisions?: () => Array<Pick<DecisionEntry, 'id' | 'title' | 'category' | 'dueAt'>>;
|
|
89
|
+
/** Running-run observation for OP cutoff; incomplete observations cannot establish that a run is absent. */
|
|
90
|
+
cutoffRuns?: (runIds: readonly string[]) => Pick<ReturnType<typeof queryRunningRuns>, 'completeness' | 'entries' | 'pty'>;
|
|
80
91
|
/** Read-only PR snapshot for TC judgment; defaults to GitHub CLI reads. */
|
|
81
92
|
pullRequests?: () => Promise<TcPullRequest[]>;
|
|
82
93
|
ledgerFiles?: (directory: string) => string[];
|
|
@@ -200,6 +211,10 @@ async function pickPersonaShadows(deps: SeatDeps, name?: string): Promise<Person
|
|
|
200
211
|
|
|
201
212
|
export async function runSeatLoopTurn(seat: string, deps: SeatDeps = {}): Promise<SeatLoopResult> {
|
|
202
213
|
const result = await runSeatLoopOnce(seat, deps);
|
|
214
|
+
if ((result.status === 'shadow' || result.status === 'skipped-off') && result.modeDowngradeReason) {
|
|
215
|
+
try { debug.log('seat-loop', 'mode-downgraded', { seat, requested: 'live-safe', effective: result.status, reason: result.modeDowngradeReason }); }
|
|
216
|
+
catch { /* logging cannot change execution */ }
|
|
217
|
+
}
|
|
203
218
|
try { await runPersonaShadowOnce(deps); }
|
|
204
219
|
catch (error) { observe('persona-shadow-error', { error: String(error).slice(0, 200) }); }
|
|
205
220
|
try {
|
|
@@ -243,6 +258,59 @@ function queueKey(seat: string, item: SeatItem): string {
|
|
|
243
258
|
return `seat-loop:${seat}:${createHash('sha256').update(handledItemKey(item)).digest('hex')}`;
|
|
244
259
|
}
|
|
245
260
|
|
|
261
|
+
/** A decision ledger that raises cards — forwards the injected version resolver so tests never run the repo's `git reflog`. */
|
|
262
|
+
function cardLedger(root: string, deps: SeatDeps, now?: Date): DecisionLedger {
|
|
263
|
+
return new DecisionLedger({ stateDir: root, ...(now ? { now: () => now } : {}), ...(deps.resolveDecisionVersion ? { resolveVersion: deps.resolveDecisionVersion } : {}) });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const DEFAULT_REPEAT_STOP = 3;
|
|
267
|
+
|
|
268
|
+
function repeatStopLimit(config: SeatLoopConfig): number {
|
|
269
|
+
const value = config.repeatStop;
|
|
270
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0 ? value : DEFAULT_REPEAT_STOP;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** A finished attempt that never landed (no merged-PR evidence). `attempting` is the intent row of the same launch. A `refused` row whose reason is the AUTOQ retry note is the reconcile of a queued row, not a second try. */
|
|
274
|
+
function unlandedAttempt(entry: SeatEntry): boolean {
|
|
275
|
+
if (!entry.item || entry.action === 'seat-question' || entry.action === 'seat-answer' || entry.inquiry) return false;
|
|
276
|
+
if (entry.status === 'launched' || entry.status === 'hitl' || entry.status === 'held' || entry.status === 'attempting') return false;
|
|
277
|
+
if (entry.status === 'refused' && entry.reason === 'AUTOQ failed or cancelled; eligible for retry') return false;
|
|
278
|
+
return entry.status === 'queued' || entry.status === 'refused' || entry.status === 'outcome-unknown';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function repeatStopReason(attempts: number): string {
|
|
282
|
+
return `같은 칸 ${attempts}회 착지 0 — 사람 판단`;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function personReopened(ledger: readonly SeatEntry[], seat: string, item: SeatItem, root: string): boolean {
|
|
286
|
+
const key = handledItemKey(item);
|
|
287
|
+
const lastHold = [...ledger].reverse().find((entry) => entry.seat === seat && entry.item && handledItemKey(entry.item) === key && entry.status === 'held');
|
|
288
|
+
if (!lastHold) return false;
|
|
289
|
+
const heldAt = Date.parse(lastHold.at || lastHold.ts || '');
|
|
290
|
+
if (!Number.isFinite(heldAt)) return false;
|
|
291
|
+
const ref = decisionRef(seat, item);
|
|
292
|
+
return new DecisionLedger({ stateDir: root }).list({ status: 'all' }).some((card) => {
|
|
293
|
+
if (!card.refs?.includes(ref) || card.status !== 'decided' || !card.decidedAt) return false;
|
|
294
|
+
const decidedAt = Date.parse(card.decidedAt);
|
|
295
|
+
return Number.isFinite(decidedAt) && decidedAt > heldAt;
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** Prior attempts of this seat and handled item that finished without a landing. A queued row whose queue is still retryable has not finished. Queue rows with the same key count when the ledger has no receipt. */
|
|
300
|
+
function unlandedAttemptCount(seat: string, item: SeatItem, ledger: readonly SeatEntry[], root: string, deps: SeatDeps): number {
|
|
301
|
+
const key = handledItemKey(item);
|
|
302
|
+
const idem = queueKey(seat, item);
|
|
303
|
+
const outcome = deps.queueOutcome ?? ((id: string, stateRoot: string) => harnessQueueOutcome(id, { root: stateRoot }));
|
|
304
|
+
const finished = (entry: SeatEntry) => unlandedAttempt(entry) && !(entry.status === 'queued' && entry.queueId && outcome(entry.queueId, root) === 'retryable');
|
|
305
|
+
const fromLedger = ledger.filter((entry) => entry.seat === seat && entry.item && handledItemKey(entry.item) === key && finished(entry));
|
|
306
|
+
const seen = new Set(fromLedger.flatMap((entry) => entry.queueId ? [entry.queueId] : []));
|
|
307
|
+
let rows: ReturnType<typeof listHarnessQueue> = [];
|
|
308
|
+
try { rows = (deps.queueItems ?? ((stateRoot: string) => listHarnessQueue({ root: stateRoot })))(root); }
|
|
309
|
+
catch (error) { observe('repeat-stop-queue-unreadable', { seat, item: item.id, error: String(error).slice(0, 200) }); }
|
|
310
|
+
const fromQueue = rows.filter((row) => row.seat === seat && row.idempotencyKey === idem && row.status === 'finished' && !seen.has(row.id));
|
|
311
|
+
return fromLedger.length + fromQueue.length;
|
|
312
|
+
}
|
|
313
|
+
|
|
246
314
|
/** A crash (or a failed write) between AUTOQ enqueue and the `queued` row leaves an `attempting`/`outcome-unknown` row with no queue id. Find its queue row by the
|
|
247
315
|
* seat-loop idempotency key so the outcome below (retry · launched · still queued) applies to it too (LOOP-LIVE1 review must-fix). */
|
|
248
316
|
function recoverAttemptingQueueIds(ledger: readonly SeatEntry[], root: string, deps: SeatDeps): SeatEntry[] {
|
|
@@ -301,8 +369,18 @@ function decisionRef(seat: string, item: SeatItem): string {
|
|
|
301
369
|
return `seat-loop:${seat}:${encodeURIComponent(itemKey(item))}`;
|
|
302
370
|
}
|
|
303
371
|
|
|
372
|
+
// The decision ledger counts Unicode characters and sentence boundaries before it stores a card.
|
|
373
|
+
function decisionSummary(text: string): string {
|
|
374
|
+
const sentences = text.replace(/\s+/g, ' ').trim().split(/(?<=[.!?。!?])\s+/u).slice(0, 2).join(' ');
|
|
375
|
+
return Array.from(sentences).slice(0, 240).join('').trim();
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function decisionSourceRef(seat: string, item: SeatItem): string {
|
|
379
|
+
return item.source === 'request' && item.id.startsWith('coord:') ? item.id : decisionRef(seat, item);
|
|
380
|
+
}
|
|
381
|
+
|
|
304
382
|
function decisionReceipt(root: string, seat: string, item: SeatItem): DecisionEntry | undefined {
|
|
305
|
-
const ref =
|
|
383
|
+
const ref = decisionSourceRef(seat, item);
|
|
306
384
|
return new DecisionLedger({ stateDir: root }).list({ status: 'all' }).find((card) => card.refs?.includes(ref));
|
|
307
385
|
}
|
|
308
386
|
|
|
@@ -315,7 +393,7 @@ function handledKeys(ledger: readonly SeatEntry[], shadow: boolean, root?: strin
|
|
|
315
393
|
|| (entry.status === 'shadow' && entry.candidate?.kind === 'other-seat-cell-defect')
|
|
316
394
|
|| (entry.status === 'hitl' && (!entry.inquiry || (!!root && !!entry.item && !!decisionReceipt(root, entry.seat, entry.item))))
|
|
317
395
|
|| (entry.inquiry?.to === 'CEO' && !!root && !!entry.item && !!decisionReceipt(root, entry.seat, entry.item))
|
|
318
|
-
|| (!inquiryRow(entry) && (entry.status === 'queued' || entry.status === 'outcome-unknown'))
|
|
396
|
+
|| (!inquiryRow(entry) && (entry.status === 'queued' || entry.status === 'outcome-unknown' || (entry.status === 'held' && !personReopened(ledger, entry.seat, entry.item!, root ?? ''))))
|
|
319
397
|
|| (!inquiryRow(entry) && entry.status === 'attempting' && !(entry.item?.source === 'checklist' && ledger.slice(index + 1).some((later) =>
|
|
320
398
|
later.seat === entry.seat && later.status === 'launched' && later.item?.source === 'checklist'
|
|
321
399
|
&& later.item.version === entry.item?.version && later.item.id === entry.item?.id
|
|
@@ -456,6 +534,62 @@ export async function gatherSeatInputs(seat: string, deps: SeatDeps = {}, ledger
|
|
|
456
534
|
return { requests: pending, checklist, role: read(join(deps.repo ?? repoRoot, 'docs', 'roles', `${seat}.md`)).slice(0, 4_000) };
|
|
457
535
|
}
|
|
458
536
|
|
|
537
|
+
const OP_CUTOFF_WINDOW_MS = 2 * 60 * 60_000;
|
|
538
|
+
const RUN_REFERENCE = /\brun-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi;
|
|
539
|
+
|
|
540
|
+
function checkOpLandingCutoff(deps: SeatDeps, now: Date, path: string, stateRoot: string): SeatEntry | undefined {
|
|
541
|
+
const releaseRoot = deps.root ?? releaseLedgerRoot();
|
|
542
|
+
const shipped = releasedVersion(releaseRoot);
|
|
543
|
+
const schedule = (deps.schedules ?? (() => readSchedules(releaseRoot)))()
|
|
544
|
+
.filter((row) => /^\d+\.\d+\.\d+$/.test(row.version) && (!shipped || versionOrder(row.version, shipped) > 0))
|
|
545
|
+
.sort((a, b) => versionOrder(a.version, b.version))[0];
|
|
546
|
+
if (!schedule?.landBy) return;
|
|
547
|
+
const untilLanding = Date.parse(schedule.landBy) - now.getTime();
|
|
548
|
+
if (!Number.isFinite(untilLanding) || untilLanding < 0 || untilLanding > OP_CUTOFF_WINDOW_MS) return;
|
|
549
|
+
const append = deps.append ?? defaultAppend;
|
|
550
|
+
let items: ReturnType<NonNullable<SeatDeps['checklistItems']>>;
|
|
551
|
+
try { items = (deps.checklistItems ?? ((version: string) => listChecklist(version, releaseRoot).items))(schedule.version); }
|
|
552
|
+
catch (error) {
|
|
553
|
+
const entry: SeatEntry = { seat: 'OP', ts: now.toISOString(), at: now.toISOString(), status: 'shadow',
|
|
554
|
+
reason: `체크리스트 못 읽음: ${schedule.version} (${String(error).slice(0, 200)})`,
|
|
555
|
+
modeDowngradeReason: 'OP 판정은 shadow 기록만 구현' };
|
|
556
|
+
append(path, entry);
|
|
557
|
+
return entry;
|
|
558
|
+
}
|
|
559
|
+
const yellow = items.filter((item) => item.status === 'yellow');
|
|
560
|
+
const runIds = [...new Set(yellow.flatMap((item) => [...(item.evidence ?? '').matchAll(RUN_REFERENCE)].map(([id]) => id.toLowerCase())))];
|
|
561
|
+
let running = new Set<string>();
|
|
562
|
+
if (runIds.length) {
|
|
563
|
+
try {
|
|
564
|
+
const observed = (deps.cutoffRuns ?? ((ids: readonly string[]) => queryRunningRuns({ runIds: ids, caller: 'seat-loop.op-cutoff' })))(runIds);
|
|
565
|
+
if (observed.completeness !== 'complete' || observed.pty.unreadable.length) {
|
|
566
|
+
observe('op-cutoff-runs-unreadable', { version: schedule.version });
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
running = new Set(observed.entries.filter((row) => row.status === 'running' || row.status === 'probable-running' || row.status === 'unknown')
|
|
570
|
+
.map((row) => row.runId.toLowerCase()));
|
|
571
|
+
} catch (error) {
|
|
572
|
+
observe('op-cutoff-runs-unreadable', { version: schedule.version, error: String(error).slice(0, 200) });
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
const candidates = yellow.filter((item) => ![...(item.evidence ?? '').matchAll(RUN_REFERENCE)]
|
|
577
|
+
.some(([id]) => running.has(id.toLowerCase())))
|
|
578
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
579
|
+
.map(({ id, title, owner }) => ({ id, title, owner: owner ?? '미배정' }));
|
|
580
|
+
try { debug.log('seat-loop', 'op-cutoff-check', { version: schedule.version, landBy: schedule.landBy, candidates }); }
|
|
581
|
+
catch { /* Observation cannot change a request. */ }
|
|
582
|
+
if (!candidates.length) return;
|
|
583
|
+
const key = `seat-loop:op-cutoff:${schedule.version}:${seatDay(now)}`;
|
|
584
|
+
const requestPath = join(stateRoot, 'seat-requests', 'requests.jsonl');
|
|
585
|
+
withSeatRequestLedgerLock(requestPath, () => {
|
|
586
|
+
if (listSeatRequests(stateRoot).some((row) => row.key === key)) return;
|
|
587
|
+
appendSeatRequestRows(requestPath, [{ key, seat: 'OP', status: 'pending' as const, queuedAt: now.toISOString(),
|
|
588
|
+
text: `${schedule.version} 착지 마감 ${schedule.landBy} 전 노랑 칸 점검 — 다음 판으로 옮길지 OP 판단:\n${candidates.map((item) => `${item.id} · ${item.title} · 주인 ${item.owner}`).join('\n')}`,
|
|
589
|
+
version: schedule.version, landBy: schedule.landBy, candidates }]);
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
|
|
459
593
|
function opCandidates(deps: SeatDeps, now: Date): OpCandidate[] {
|
|
460
594
|
const releaseRoot = deps.root ?? releaseLedgerRoot();
|
|
461
595
|
const shipped = releasedVersion(releaseRoot);
|
|
@@ -802,7 +936,7 @@ function judgeSeatNeighbors(seat: string, config: SeatLoopConfig, deps: SeatDeps
|
|
|
802
936
|
if (judgment.state === 'absent') {
|
|
803
937
|
if (config.mode === 'live-safe' && judgment.action?.action === 'escalate' && isRescueAllowedAction('decision-card')) {
|
|
804
938
|
const ref = `seat-loop:neighbor:${seat}:${neighbor.id}:${lastSeenAt}`;
|
|
805
|
-
|
|
939
|
+
cardLedger(root, deps, now).raiseOnce({ title: `${seat}: 이웃 ${neighbor.id} 결측`, category: 'other',
|
|
806
940
|
scqa: { s: `${seat} 이웃 ${neighbor.id}의 마지막 맥락 버스 사건: ${lastSeenAt}.`,
|
|
807
941
|
c: `설정한 heartbeat ${neighbor.heartbeat.everyMinutes}분 × ${neighbor.heartbeat.missedTicks}회 동안 새 사건이 없다.`,
|
|
808
942
|
q: '결측 이웃의 업무를 어떻게 처리할까?' },
|
|
@@ -827,10 +961,16 @@ function judgeSeatNeighbors(seat: string, config: SeatLoopConfig, deps: SeatDeps
|
|
|
827
961
|
export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promise<SeatLoopResult> {
|
|
828
962
|
if (!/^(?:MK|OP|TC|UX)$/.test(seat)) throw new Error(`unknown seat: ${seat}`);
|
|
829
963
|
const config = deps.config ?? getUserConfig().loops?.seat ?? { mode: 'shadow' };
|
|
830
|
-
if (config.mode === 'off'
|
|
964
|
+
if (config.mode === 'off') {
|
|
831
965
|
observe('skipped-off', { seat });
|
|
832
966
|
return { seat, status: 'skipped-off' };
|
|
833
967
|
}
|
|
968
|
+
if (!(config.seats ?? ['MK']).includes(seat)) {
|
|
969
|
+
// An excluded seat does not read inputs or run the shadow loop; never report it as shadow.
|
|
970
|
+
observe('skipped-off', { seat });
|
|
971
|
+
return { seat, status: 'skipped-off',
|
|
972
|
+
...(config.mode === 'live-safe' ? { modeDowngradeReason: '자리 목록에 없음 (loops.seat.seats)' } : {}) };
|
|
973
|
+
}
|
|
834
974
|
const now = (deps.now ?? (() => new Date()))();
|
|
835
975
|
const path = seatLedgerPath(seat, deps.root ?? effectiveInstanceRoot(), now);
|
|
836
976
|
const directory = dirname(path);
|
|
@@ -928,6 +1068,7 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
928
1068
|
return entry;
|
|
929
1069
|
}
|
|
930
1070
|
}
|
|
1071
|
+
let tcDowngrade: SeatEntry | undefined;
|
|
931
1072
|
if (seat === 'TC') {
|
|
932
1073
|
const candidates = await tcCandidates(deps, now);
|
|
933
1074
|
for (const candidate of candidates) {
|
|
@@ -938,19 +1079,27 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
938
1079
|
&& row.candidate.version === candidate.version && row.candidate.id === candidate.id && row.candidate.to === candidate.to);
|
|
939
1080
|
if (!inLedger) {
|
|
940
1081
|
const entry: SeatEntry = { seat, ts: now.toISOString(), at: now.toISOString(), status: 'shadow', candidate,
|
|
1082
|
+
...(config.mode === 'live-safe' ? { modeDowngradeReason: 'TC 타 자리 결함 질문은 shadow 기록만 구현' } : {}),
|
|
941
1083
|
item: { source: 'checklist', kind: 'cell', id: candidate.id, title: candidate.title, text: candidate.title, version: candidate.version, seat: candidate.to },
|
|
942
1084
|
action: 'seat-question', inquiry: { to: candidate.to, question } };
|
|
943
1085
|
(deps.append ?? defaultAppend)(path, entry);
|
|
1086
|
+
tcDowngrade ??= entry;
|
|
944
1087
|
}
|
|
945
1088
|
if (!inLedger || recorded) observe('ask-shadow', { from: seat, to: candidate.to, itemId: candidate.id });
|
|
946
1089
|
continue;
|
|
947
1090
|
}
|
|
948
1091
|
if (ledger.some((row) => row.status === 'shadow' && JSON.stringify(row.candidate) === JSON.stringify(candidate))) continue;
|
|
949
|
-
const entry: SeatEntry = { seat, ts: now.toISOString(), at: now.toISOString(), status: 'shadow', candidate
|
|
1092
|
+
const entry: SeatEntry = { seat, ts: now.toISOString(), at: now.toISOString(), status: 'shadow', candidate,
|
|
1093
|
+
...(config.mode === 'live-safe' ? { modeDowngradeReason: 'TC PR 판정은 shadow 기록만 구현' } : {}) };
|
|
950
1094
|
(deps.append ?? defaultAppend)(path, entry);
|
|
1095
|
+
tcDowngrade ??= entry;
|
|
951
1096
|
observe('tc-shadow-candidate', { seat, candidate });
|
|
952
1097
|
}
|
|
953
1098
|
}
|
|
1099
|
+
if (seat === 'OP' && config.mode === 'live-safe') {
|
|
1100
|
+
const unreadable = checkOpLandingCutoff(deps, now, path, stateRoot);
|
|
1101
|
+
if (unreadable) return unreadable;
|
|
1102
|
+
}
|
|
954
1103
|
if (seat === 'OP' && !seatQuestions(stateRoot, 'OP').some((question) =>
|
|
955
1104
|
!hasSeatAnswer(stateRoot, question)
|
|
956
1105
|
&& !handledKeys(ledger, config.mode !== 'on' || (config.questions ?? 'shadow') !== 'on', stateRoot).has(`seat-question::${question.id}`))) {
|
|
@@ -987,7 +1136,8 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
987
1136
|
if (id !== currentDecision.id) candidates.push({ kind: 'decision-delegation', id, verdict: 'none' });
|
|
988
1137
|
}
|
|
989
1138
|
}
|
|
990
|
-
const entries = candidates.map((candidate): SeatEntry => ({ seat, ts: now.toISOString(), at: now.toISOString(), status: 'shadow', candidate
|
|
1139
|
+
const entries = candidates.map((candidate): SeatEntry => ({ seat, ts: now.toISOString(), at: now.toISOString(), status: 'shadow', candidate,
|
|
1140
|
+
...(config.mode === 'live-safe' ? { modeDowngradeReason: 'OP 판정은 shadow 기록만 구현' } : {}) }));
|
|
991
1141
|
for (const entry of entries) {
|
|
992
1142
|
const candidate = entry.candidate!;
|
|
993
1143
|
const previous = [...ledger].reverse().find((row) => row.candidate?.kind === candidate.kind &&
|
|
@@ -1003,7 +1153,12 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1003
1153
|
}
|
|
1004
1154
|
return entries[0]!;
|
|
1005
1155
|
}
|
|
1156
|
+
const failedDecisions = new Set<string>();
|
|
1157
|
+
let lastDecisionFailure: SeatEntry | undefined;
|
|
1158
|
+
while (true) {
|
|
1006
1159
|
const inputs = await gatherSeatInputs(seat, deps, ledger);
|
|
1160
|
+
inputs.requests = inputs.requests.filter((candidate) => !failedDecisions.has(itemKey(candidate)));
|
|
1161
|
+
inputs.checklist = inputs.checklist.filter((candidate) => !failedDecisions.has(itemKey(candidate)));
|
|
1007
1162
|
const questionsMode = config.mode === 'on' ? config.questions ?? 'shadow' : 'shadow';
|
|
1008
1163
|
const questionHandled = handledKeys(ledger, questionsMode !== 'on', stateRoot);
|
|
1009
1164
|
const pendingQuestion = inputs.requests.find((candidate) => candidate.source === 'seat-question'
|
|
@@ -1085,6 +1240,7 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1085
1240
|
entry.action = 'decision';
|
|
1086
1241
|
}
|
|
1087
1242
|
if (questionsMode !== 'on') {
|
|
1243
|
+
if (config.mode === 'live-safe') entry.modeDowngradeReason = '질문 전달 선행 조건 미충족 (questions shadow)';
|
|
1088
1244
|
if (config.mode === 'live-safe' && !isRescueAllowedAction('seat-question')) {
|
|
1089
1245
|
observe('refused', { seat, item: item.id, action: 'seat-question', reason: 'question delivery is shadow-only' });
|
|
1090
1246
|
}
|
|
@@ -1192,9 +1348,11 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1192
1348
|
if (item && config.mode === 'live-safe' && deps.inquire && !isRescueAllowedAction('seat-question')) {
|
|
1193
1349
|
observe('refused', { seat, item: item.id, action: 'seat-question', reason: 'outbound inquiry not in rescue policy' });
|
|
1194
1350
|
}
|
|
1351
|
+
if (!item && config.mode === 'live-safe' && tcDowngrade) return tcDowngrade;
|
|
1195
1352
|
const entry: SeatEntry = { seat, ts: now.toISOString(), at: now.toISOString(), status: item ? 'shadow' : 'skipped-empty',
|
|
1196
1353
|
...(item ? { item, action: planned!.kind, ...(planned!.reason ? { reason: planned!.reason } : {}) } : { action: 'skipped-empty' as const }) };
|
|
1197
1354
|
if (config.mode === 'shadow' || !item) {
|
|
1355
|
+
if (!item && lastDecisionFailure) return lastDecisionFailure;
|
|
1198
1356
|
append(path, entry);
|
|
1199
1357
|
if (config.mode === 'shadow') observe('shadow', { seat, item, status: entry.status });
|
|
1200
1358
|
return entry;
|
|
@@ -1282,11 +1440,14 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1282
1440
|
append(path, { ...entry, status: 'attempting' });
|
|
1283
1441
|
try {
|
|
1284
1442
|
if (action.kind === 'decision') {
|
|
1285
|
-
const title = `${seat}: ${action.text
|
|
1443
|
+
const title = `${seat}: ${decisionSummary(action.text)}`;
|
|
1444
|
+
const source = decisionSourceRef(seat, item);
|
|
1286
1445
|
const raised = await run(['decisions', 'raise', '--title', title, '--category', 'other',
|
|
1287
|
-
'--s', `${seat} 배정 항목: ${action.text.replace(/[\r\n]+/g, ' ')}
|
|
1446
|
+
'--s', decisionSummary(`${seat} 배정 항목: ${action.text.replace(/[\r\n]+/g, ' ')} 역할: ${inputs.role}`),
|
|
1447
|
+
'--c', '자동 실행 금지 문면에 해당하여 사람 결정이 필요하다',
|
|
1288
1448
|
'--option', 'a=사람 승인:승인 후 별도로 집행', '--option', 'b=보류:집행하지 않음',
|
|
1289
|
-
'--skip-recommend', '금지 문면은 자동으로 권고하지 않는다', '--no-xcheck', '자리 루프 · 이웃 교환은 DEC-XCHECK ②',
|
|
1449
|
+
'--skip-recommend', '금지 문면은 자동으로 권고하지 않는다', '--no-xcheck', '자리 루프 · 이웃 교환은 DEC-XCHECK ②',
|
|
1450
|
+
'--agent', 'seat-loop', '--ref', source, '--json']);
|
|
1290
1451
|
const decision: unknown = JSON.parse(raised.trim());
|
|
1291
1452
|
if (!decision || typeof decision !== 'object' || typeof (decision as { id?: unknown }).id !== 'string') {
|
|
1292
1453
|
throw new Error('decisions raise returned no decision id');
|
|
@@ -1297,6 +1458,38 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1297
1458
|
return entry;
|
|
1298
1459
|
}
|
|
1299
1460
|
if (config.mode === 'live-safe') {
|
|
1461
|
+
const limit = repeatStopLimit(config);
|
|
1462
|
+
const attempts = unlandedAttemptCount(seat, item, ledger, stateRoot, deps);
|
|
1463
|
+
if (attempts >= limit && !personReopened(ledger, seat, item, stateRoot)) {
|
|
1464
|
+
const reason = repeatStopReason(attempts);
|
|
1465
|
+
entry.status = 'held';
|
|
1466
|
+
entry.reason = reason;
|
|
1467
|
+
const ref = decisionRef(seat, item);
|
|
1468
|
+
const prior = new DecisionLedger({ stateDir: stateRoot }).list({ status: 'all' }).find((card) => card.refs?.includes(ref) && card.status === 'open');
|
|
1469
|
+
if (!prior) {
|
|
1470
|
+
cardLedger(stateRoot, deps, now).raiseOnce({
|
|
1471
|
+
title: `${seat}: ${item.id} 반복 착지 0`,
|
|
1472
|
+
category: 'other',
|
|
1473
|
+
scqa: {
|
|
1474
|
+
s: `${seat} 칸 ${item.id} 가 착지 없이 ${attempts}회 끝났다.`,
|
|
1475
|
+
c: '같은 칸을 다시 발사하지 않고 사람 판단을 기다린다.',
|
|
1476
|
+
q: '이 칸을 계속할까, 멈출까?',
|
|
1477
|
+
},
|
|
1478
|
+
options: [
|
|
1479
|
+
{ key: 'a', label: '다시 연다', consequence: '결정 뒤에 자리 루프가 한 번 더 시도할 수 있다' },
|
|
1480
|
+
{ key: 'b', label: '멈춘다', consequence: '같은 칸은 다시 발사하지 않는다' },
|
|
1481
|
+
],
|
|
1482
|
+
recommendation: { skipped: true, reason: '착지 없는 반복만으로 계속할지 자동 결정하지 않는다' },
|
|
1483
|
+
raisedBy: { agent: 'seat-loop' },
|
|
1484
|
+
refs: [ref],
|
|
1485
|
+
raisedAt: now.toISOString(),
|
|
1486
|
+
crossCheckSkipped: '반복 정지는 사람 판단',
|
|
1487
|
+
}, ref);
|
|
1488
|
+
}
|
|
1489
|
+
append(path, entry);
|
|
1490
|
+
observe('repeat-stopped', { seat, item: item.id, attempts, lastQueueId: [...ledger].reverse().find((row) => row.seat === seat && row.item && handledItemKey(row.item) === handledItemKey(item) && row.queueId)?.queueId ?? null });
|
|
1491
|
+
return entry;
|
|
1492
|
+
}
|
|
1300
1493
|
let queued: { id: string };
|
|
1301
1494
|
try {
|
|
1302
1495
|
queued = await (deps.enqueue ?? ((assigned, text, root, idempotencyKey) => addHarnessQueue({ seat: assigned, say: text, idempotencyKey }, { root })))(seat, action.text, stateRoot, queueKey(seat, item));
|
|
@@ -1332,9 +1525,19 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
|
|
|
1332
1525
|
observe('launched', { seat, item, runId });
|
|
1333
1526
|
return entry;
|
|
1334
1527
|
} catch (error) {
|
|
1335
|
-
|
|
1528
|
+
const failure: SeatEntry = { ...entry, status: 'outcome-unknown' };
|
|
1529
|
+
append(path, failure);
|
|
1530
|
+
if (action.kind === 'decision') {
|
|
1531
|
+
lastDecisionFailure = failure;
|
|
1532
|
+
const source = decisionSourceRef(seat, item);
|
|
1533
|
+
try { debug.log('seat-loop', 'decision-raise-failed', { source, error: String(error) }); }
|
|
1534
|
+
catch { /* A failed observation cannot interrupt the remaining items. */ }
|
|
1535
|
+
failedDecisions.add(itemKey(item));
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1336
1538
|
throw error;
|
|
1337
1539
|
}
|
|
1540
|
+
}
|
|
1338
1541
|
} catch (error) {
|
|
1339
1542
|
turnError = error;
|
|
1340
1543
|
throw error;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// ⛔ 종료(`process.exit`)는 여기가 «안» 한다 — 호출자가 자기 표면에 맞게 끝낸다.
|
|
12
12
|
// 그래서 결과를 «세 값»으로 낸다: 발사 / 저작 전 중단 / 전제 검사 중단.
|
|
13
13
|
import { linesOutsideFencedCode } from '../self-implement/goal-author.js';
|
|
14
|
+
import { recordAuthoredGoal } from './authored-goal-trace.js';
|
|
14
15
|
import { retiredEntranceNotice, type EntranceDeclaration } from './entrance-registry.js';
|
|
15
16
|
import {
|
|
16
17
|
classifyConcurrentAuthoring,
|
|
@@ -58,7 +59,7 @@ function resolveSelfResolveClarifications(explicit: boolean | undefined): Resolv
|
|
|
58
59
|
/** 저작기 산출 — `runGoalAuthorCli` 의 반환에서 이 흐름이 «실제로 쓰는» 것만. */
|
|
59
60
|
export interface AskAuthoredGoal {
|
|
60
61
|
readonly path: string;
|
|
61
|
-
readonly authored?: { readonly document?: string; readonly grounded?: boolean | null } | null;
|
|
62
|
+
readonly authored?: { readonly document?: string; readonly grounded?: boolean | null; readonly authorRunId?: string } | null;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
export type LaunchingTreeProbe =
|
|
@@ -158,6 +159,8 @@ export interface AskLaunchFlowDeps {
|
|
|
158
159
|
/** ⛔ 못 얻으면 `null` — 같은 이유. */
|
|
159
160
|
recentAuthoringSamples(): RecentAuthoringSample[] | null;
|
|
160
161
|
authorGoal(args: readonly string[], options: Record<string, unknown>): Promise<AskAuthoredGoal>;
|
|
162
|
+
/** AUTHOR-TRACE seam (tests). */
|
|
163
|
+
recordAuthoredGoal?: typeof recordAuthoredGoal;
|
|
161
164
|
/** Pod launches reserve a host slot before goal authoring begins. */
|
|
162
165
|
beforeAuthoring?(): Promise<void>;
|
|
163
166
|
/** 발사 전 권고용 분해 seam. 생략하면 전용 관측명을 준 기존 분해기를 쓴다. */
|
|
@@ -861,6 +864,24 @@ export async function runAskLaunchFlow(
|
|
|
861
864
|
selfResolveClarifications: selfResolveClarifications.value,
|
|
862
865
|
});
|
|
863
866
|
deps.print(`[ask] 저작됨: ${authored.path}`);
|
|
867
|
+
// AUTHOR-TRACE: the authored goal file can be cleaned up later — keep path ⊕ sha256 ⊕ size in the run ledger and
|
|
868
|
+
// archive the body, so authoring length is measurable after the fact (GOAL-BENCH · AUTHOR-LITE).
|
|
869
|
+
const traceIds = {
|
|
870
|
+
...(input.runId === undefined ? {} : { runId: input.runId }),
|
|
871
|
+
...(authored.authored?.authorRunId === undefined ? {} : { authorRunId: authored.authored.authorRunId }),
|
|
872
|
+
};
|
|
873
|
+
if (authored.authored?.document !== undefined && (traceIds.runId !== undefined || traceIds.authorRunId !== undefined)) {
|
|
874
|
+
try {
|
|
875
|
+
const trace = (deps.recordAuthoredGoal ?? recordAuthoredGoal)(traceIds, authored.path, authored.authored.document);
|
|
876
|
+
deps.log('goal-authored-trace', { ...traceIds, ...trace }, 'info');
|
|
877
|
+
} catch (error) {
|
|
878
|
+
deps.log('goal-authored-trace-failed', { ...traceIds, error: error instanceof Error ? error.message : String(error) }, 'warn');
|
|
879
|
+
}
|
|
880
|
+
} else {
|
|
881
|
+
deps.log('goal-authored-trace-skipped', {
|
|
882
|
+
reason: authored.authored?.document === undefined ? 'no-authored-document' : 'no-run-or-author-id', path: authored.path,
|
|
883
|
+
}, 'warn');
|
|
884
|
+
}
|
|
864
885
|
const markerCounts = authored.authored?.document === undefined
|
|
865
886
|
? null
|
|
866
887
|
: countMissingAuthoredConstraintMarkers(input.askText, authored.authored.document);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { appendRunLedgerEntry, runLedgerDir } from '../self-implement/run-ledger.js';
|
|
5
|
+
import { debug } from '../debug/log.js';
|
|
6
|
+
|
|
7
|
+
/** AUTHOR-TRACE: what `harness say`/`ask` authored, kept with the run — the goal file itself may be cleaned up later,
|
|
8
|
+
* so the ledger carries path ⊕ sha256 ⊕ size and the body is archived next to the ledger. */
|
|
9
|
+
export interface AuthoredGoalTrace {
|
|
10
|
+
path: string;
|
|
11
|
+
sha256: string;
|
|
12
|
+
chars: number;
|
|
13
|
+
sections: number;
|
|
14
|
+
archivedAt: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** `## ` headings outside fenced code (a fence closes only on the same marker, at least as long). */
|
|
18
|
+
function countSections(document: string): number {
|
|
19
|
+
let fence: { char: string; length: number } | null = null;
|
|
20
|
+
let count = 0;
|
|
21
|
+
for (const line of document.split('\n')) {
|
|
22
|
+
const marker = /^ {0,3}(`{3,}|~{3,})/.exec(line);
|
|
23
|
+
if (fence) {
|
|
24
|
+
if (marker && marker[1]![0] === fence.char && marker[1]!.length >= fence.length && line.trim() === marker[1]) fence = null;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (marker) { fence = { char: marker[1]![0]!, length: marker[1]!.length }; continue; }
|
|
28
|
+
if (/^ {0,3}##\s/.test(line)) count += 1;
|
|
29
|
+
}
|
|
30
|
+
return count;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function authoredGoalTrace(path: string, document: string, archivedAt: string): AuthoredGoalTrace {
|
|
34
|
+
return {
|
|
35
|
+
path,
|
|
36
|
+
sha256: createHash('sha256').update(document).digest('hex'),
|
|
37
|
+
chars: [...document].length,
|
|
38
|
+
sections: countSections(document),
|
|
39
|
+
archivedAt,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Archive the body, emit one `goal-author`/`goal-authored` observation and — when the run id is already known —
|
|
44
|
+
* append a `goal-authored` run-ledger event. At `harness say` authoring time the run id usually does not exist yet;
|
|
45
|
+
* the authoring run id (`authorRunId`, joined to the goal by `goal-id-assigned`) keys the record then. */
|
|
46
|
+
export function recordAuthoredGoal(
|
|
47
|
+
ids: { runId?: string; authorRunId?: string },
|
|
48
|
+
path: string,
|
|
49
|
+
document: string,
|
|
50
|
+
options: { ledgerDir?: string; archiveDir?: string } = {},
|
|
51
|
+
): AuthoredGoalTrace {
|
|
52
|
+
const key = ids.runId ?? ids.authorRunId;
|
|
53
|
+
if (!key) throw new Error('recordAuthoredGoal needs a runId or an authorRunId');
|
|
54
|
+
const ledgerDir = options.ledgerDir ?? runLedgerDir();
|
|
55
|
+
const archiveDir = options.archiveDir ?? join(dirname(ledgerDir), 'authored-goals');
|
|
56
|
+
const digest = createHash('sha256').update(document).digest('hex');
|
|
57
|
+
const archived = join(archiveDir, `${key}-${digest.slice(0, 12)}.md`);
|
|
58
|
+
if (!existsSync(archiveDir)) mkdirSync(archiveDir, { recursive: true });
|
|
59
|
+
writeFileSync(archived, document, 'utf8');
|
|
60
|
+
const trace = authoredGoalTrace(path, document, archived);
|
|
61
|
+
debug.log('goal-author', 'goal-authored', { ...ids, ...trace });
|
|
62
|
+
if (ids.runId) appendRunLedgerEntry({ runId: ids.runId, event: 'goal-authored', data: { ...trace, ...(ids.authorRunId ? { authorRunId: ids.authorRunId } : {}) } }, ledgerDir);
|
|
63
|
+
return trace;
|
|
64
|
+
}
|
package/src/self-dev/dev-cli.ts
CHANGED
|
@@ -43,6 +43,7 @@ import { observeDeliverables, type DeliverableObservationResult, type Deliverabl
|
|
|
43
43
|
import { debug } from '../debug/log.js';
|
|
44
44
|
import { getUserConfig, type UserConfig } from '../user-config.js';
|
|
45
45
|
import { resolveChildLlmPreference, type ChildLlmPreferenceInput, type ResolvedChildLlmPreference } from '../self-implement/child-llm-preference.js';
|
|
46
|
+
import { applyLaunchOverloadFailover, codexProviderNamed } from '../oauth/overload-failover-launch.js';
|
|
46
47
|
import { readCachedGrokQuota } from '../oauth/codex-account-store.js';
|
|
47
48
|
import { queryAbandonedDraftPrs, type AbandonedDraftPr } from '../cli/logs-abandoned-draft-prs.js';
|
|
48
49
|
import { branchLineageSlug } from '../cli/pr-lineage.js';
|
|
@@ -1631,6 +1632,18 @@ function resolveChildLlmFromParts(
|
|
|
1631
1632
|
return undefined;
|
|
1632
1633
|
}
|
|
1633
1634
|
|
|
1635
|
+
/** Pinned `tools.selfImplement.childLlm` naming codex. Fallback-chain codex is not pinned. */
|
|
1636
|
+
function childLlmCodexPinned(flagProvider: string | undefined): boolean {
|
|
1637
|
+
if (flagProvider !== undefined) return false;
|
|
1638
|
+
try {
|
|
1639
|
+
const preference = resolveChildLlmPreference(getUserConfig());
|
|
1640
|
+
const provider = preference.chain[0]?.provider;
|
|
1641
|
+
return preference.mode === 'pinned' && preference.source.chain === 'pinned' && codexProviderNamed(provider);
|
|
1642
|
+
} catch {
|
|
1643
|
+
return false;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1634
1647
|
export function buildChildLlmSelection(
|
|
1635
1648
|
opts: Pick<DevCliOpts, 'childLlmProvider' | 'childLlmModel' | 'childLlmEffort'> & {
|
|
1636
1649
|
/** 발사 인자가 있을 때 관측용 선호를 읽는다. 없으면 getUserConfig. 기존 두 인자 시그니처는 그대로다. */
|
|
@@ -1967,7 +1980,12 @@ export function buildDevCliSpec(
|
|
|
1967
1980
|
}
|
|
1968
1981
|
|
|
1969
1982
|
if (path === 'self-mission') {
|
|
1970
|
-
const
|
|
1983
|
+
const builtChild = buildChildLlmSelection({ ...opts, readPreference: () => getUserConfig() });
|
|
1984
|
+
const childLlm = applyLaunchOverloadFailover(builtChild, {
|
|
1985
|
+
provider: opts.childLlmProvider,
|
|
1986
|
+
model: opts.childLlmModel,
|
|
1987
|
+
codexPinned: childLlmCodexPinned(opts.childLlmProvider),
|
|
1988
|
+
});
|
|
1971
1989
|
// 잔량 조회(약 3초)는 한 발사에 한 번만 — 자식·부모가 같은 값을 쓴다.
|
|
1972
1990
|
let grokQuotaOnce: ReturnType<typeof readCachedGrokQuota> | undefined;
|
|
1973
1991
|
const readGrokQuotaOnce = () => (grokQuotaOnce ??= readGrokQuota());
|
|
@@ -35,6 +35,7 @@ import type { DocumentReferenceStatus } from '../self-implement/self-implement-r
|
|
|
35
35
|
import type { SelfDevGoal, OrchestrateSelfDevOptions, SelfDevJobResult } from './orchestrate.js';
|
|
36
36
|
import { launchCapabilityObservation, resolveLaunchCapabilities } from './launch-capabilities.js';
|
|
37
37
|
import { lookupEntrance, type EntranceId } from './entrance-registry.js';
|
|
38
|
+
import { resolveLaunchStamp } from '../harness/launch-stamp.js';
|
|
38
39
|
import type { IngestionEntry } from '../agent-substrate/execution/ingestion-policy.js';
|
|
39
40
|
import type { ReviewerContextItem } from '../agent-substrate/pr-reviewer.js';
|
|
40
41
|
import { resolveRunIdentity, type RunIdSource } from '../harness/harness-space.js';
|
|
@@ -42,7 +43,7 @@ import { addSelfDevRunParticipant, processBirthId, saveSelfDevRun } from './run-
|
|
|
42
43
|
import { getUserConfig, resolveRoleLlm } from '../user-config.js';
|
|
43
44
|
import { reviewReasoningEffort } from '../model-tier/review-effort.js';
|
|
44
45
|
import { getProvider, inferProviderFromModel } from '../llm.js';
|
|
45
|
-
import { buildReviewProviderAttempts, runReviewWithFallback, reviewFallbackModelsFromConfig } from './review-provider-fallback.js';
|
|
46
|
+
import { buildReviewProviderAttempts, defaultReviewFallbackModel, runReviewWithFallback, reviewFallbackModelsFromConfig } from './review-provider-fallback.js';
|
|
46
47
|
import type { DefaultSeamsOptions } from '../self-implement/seams.js';
|
|
47
48
|
import { harnessTargetOptions, resolveHarnessTarget, revalidateHarnessTarget, type HarnessTargetResolution } from '../self-implement/harness-target-options.js';
|
|
48
49
|
import { provisionRepository, type RepoProvisionResult } from '../self-implement/repo-provision.js';
|
|
@@ -609,6 +610,7 @@ export function toSelfImplementOptions(text: string, plan: ResolvedDevPlan, seam
|
|
|
609
610
|
...(s.correlationId !== undefined ? { correlationId: s.correlationId } : {}),
|
|
610
611
|
...(s.branchName ? { branchName: s.branchName } : {}),
|
|
611
612
|
...(s.maxReworkRounds !== undefined ? { maxReworkRounds: s.maxReworkRounds } : {}),
|
|
613
|
+
launch: resolveLaunchStamp({ entrance: plan.entrance, queueId: process.env.ELANOUS_HARNESS_QUEUE_LAUNCH }),
|
|
612
614
|
};
|
|
613
615
|
}
|
|
614
616
|
|
|
@@ -1093,10 +1095,12 @@ export async function buildDefaultSelfImplementSeams(
|
|
|
1093
1095
|
provider: inferProviderFromModel(model) ? getProvider(model) : undefined,
|
|
1094
1096
|
label,
|
|
1095
1097
|
});
|
|
1098
|
+
const primaryReviewAttempt = reviewAttemptForModel(reviewModel, 'primary');
|
|
1096
1099
|
const reviewAttempts = buildReviewProviderAttempts(
|
|
1097
|
-
|
|
1098
|
-
reviewFallbackModels
|
|
1099
|
-
|
|
1100
|
+
primaryReviewAttempt,
|
|
1101
|
+
[...reviewFallbackModels, defaultReviewFallbackModel(primaryReviewAttempt.provider?.name)]
|
|
1102
|
+
.map((model) => reviewAttemptForModel(model, model)),
|
|
1103
|
+
).slice(0, 2);
|
|
1100
1104
|
const defaultApiReview = async (prompt: string): Promise<string> => {
|
|
1101
1105
|
const r = await runReviewWithFallback(
|
|
1102
1106
|
reviewAttempts,
|
|
@@ -1859,9 +1863,9 @@ async function observeDevTaskDispatch(spec: DevPipelineSpec, plan: ResolvedDevPl
|
|
|
1859
1863
|
}
|
|
1860
1864
|
}
|
|
1861
1865
|
|
|
1862
|
-
async function inspectReleasePathBeforeMerge(seams: SelfImplementSeams, number: number, cwd: string): Promise<string | undefined> {
|
|
1866
|
+
async function inspectReleasePathBeforeMerge(seams: SelfImplementSeams, number: number, cwd: string, base?: string): Promise<string | undefined> {
|
|
1863
1867
|
if (!seams.readPrFiles) throw new Error('release-path inspection unavailable — automatic merge refused');
|
|
1864
|
-
const files = await seams.readPrFiles({ number, cwd });
|
|
1868
|
+
const files = await seams.readPrFiles({ number, cwd, ...(base ? { base } : {}) });
|
|
1865
1869
|
if (!Array.isArray(files) || !files.every((path) => typeof path === 'string')) throw new Error('invalid PR file list — automatic merge refused');
|
|
1866
1870
|
const path = releasePathHold(files);
|
|
1867
1871
|
if (!path) return undefined;
|
|
@@ -2103,10 +2107,11 @@ async function runDevPipelineDispatch(
|
|
|
2103
2107
|
...assembledSeams,
|
|
2104
2108
|
...(assembledSeams.mergePr ? { mergePr: async (input) => {
|
|
2105
2109
|
let path: string | undefined;
|
|
2106
|
-
try { path = await inspectReleasePathBeforeMerge(assembledSeams, input.number, input.cwd); }
|
|
2110
|
+
try { path = await inspectReleasePathBeforeMerge(assembledSeams, input.number, input.cwd, plan.base); }
|
|
2107
2111
|
catch (error) {
|
|
2108
|
-
|
|
2109
|
-
|
|
2112
|
+
const rateLimited = error instanceof Error && (error as { rateLimited?: boolean }).rateLimited === true;
|
|
2113
|
+
debug.log('self-dev.merge', 'release-path-inspection-failed', { number: input.number, error: String(error), ...(rateLimited ? { rateLimit: true } : {}) });
|
|
2114
|
+
return { merged: false, detail: rateLimited ? `automatic merge refused: rate-limit: ${String(error)}` : `automatic merge refused: ${String(error)}` };
|
|
2110
2115
|
}
|
|
2111
2116
|
if (path) return { merged: false, detail: `OP approval required: ${path}` };
|
|
2112
2117
|
return assembledSeams.mergePr!(input);
|