elanous 0.2.18 → 0.2.20
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-30dff22c9e1f3073.js → 1307-77fad80cf54477c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2116-6429c188eb846ab8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/224-e7be8f762fe74986.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5755-ea7ed88eb956f47b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-94ed6f78e9814b2b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7748-6e51bfe927019f5c.js +25 -0
- package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-33c0689b70ed8cf3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-d92f7ee3994e84c7.js → page-c855c24de7db74fe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-4733c967b56a4277.js → page-85b316bb4235980b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/{page-d22fd849cc22f104.js → page-9b2b26a33cb2cd42.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-bb742f995c6769e7.js → page-c9955fde67b1bcb5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-02b073a2ac793b83.js → page-7a852956c74015a3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-8a9b3594bc23289b.js → page-4ba4abe096b6c2a8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-575d4a8b4f7becb2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-308111abadf35670.js → page-f5d56ff5f45ad164.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-2260e9eef0357421.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-5d97b2901e0d1ea6.js → page-7c058069c5e7fa63.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-8c3d8e63ae2e1c98.js → page-466bf0ee16ad1432.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-9b72a07d424701c6.js → page-a23bc71713bcc08f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ed4a6f799f005d09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-a572f58cd3ca5864.js → page-9cd565005b93aca6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-be5ea965a07fbc41.js → page-474cb5888ea0e8f3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-35bfec2e631d3eae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/{page-fa244bd146a00ae8.js → page-9ca21f9ad3208a61.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-44d56d6391965a6a.js → page-f9ccb1b1adbea14d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-83a700d4c754d802.js → page-ec102c9b8b691e37.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-7b3170f374f77b9d.js → not-found-a9c2cfa8dda922a5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6da35144878feb6d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-f6055495e9ed7ea8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-25204694fa9984a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a60e035f151d84d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-189c587f36874538.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-c56634bde705a65d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-1467047104141297.js → page-db03ae3589a81c9e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4a6463b97f0ad94f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-102310930791197a.js → page-75bffecc9aa1004e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a8d2fe0c669e0de4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-6ed0b73bae8351d8.js → page-31d5b875760bb62d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c26fa10aac6c21c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-43336055451f6e37.js → page-68793c6e10331d66.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-3cc47dca0922ab9a.js → page-46d5201d2207a511.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-a77ee355a2304ff0.js → page-d81c07a7b4f618ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-b17cd46cf0e9b8cf.js → page-be1c4e8697f22360.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-cf0a4b4abead71ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-a4717e2c4bf7a28f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-7bf54c9bb9274af8.js → page-0fd895fab9b62c1b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8b6c1454a8e8f306.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c6e4364d9de8f717.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-0acc8e198f7b0dfb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-134da468538baf48.js → page-34882c84f84021c5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-c2782b330447ffb6.js → webpack-c260571513bf43c2.js} +1 -1
- package/apps/pwa/out/_next/static/css/{e99e3cff94ced78a.css → 599369d853c61df7.css} +1 -1
- package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +1 -0
- package/apps/pwa/out/_next/static/css/d3427996da11e480.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +14 -14
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +16 -15
- 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 +16 -15
- 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 +24 -23
- 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/demo/editor-recipes.yaml +22 -0
- package/graphs/ops/guardian.yaml +26 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/retro/daily.yaml +25 -0
- package/graphs/retro/recipes.yaml +24 -0
- package/package.json +1 -1
- package/scripts/ask-marker-check.ts +3 -1
- package/scripts/coord-channel-watch.sh +2 -2
- package/scripts/discord-seat-probe.ts +3 -1
- package/scripts/land-global-checks.ts +89 -0
- package/scripts/lib/pch-regress-map.ts +24 -0
- package/scripts/morning-report.ts +18 -4
- package/scripts/pch-regress.ts +88 -0
- package/scripts/release-loop/gate-diff.ts +18 -0
- package/scripts/release-loop/gate-node.ts +628 -65
- package/scripts/release-loop/gate-partial.ts +106 -0
- package/scripts/release-loop/gate-timing.ts +229 -0
- package/scripts/release-loop/preflight.ts +6 -4
- package/scripts/release-loop/publish-window.ts +141 -0
- package/scripts/release-loop/release-docs.ts +1 -4
- package/scripts/release-loop/release-universe.ts +38 -0
- package/scripts/release-loop/resume-release.ts +67 -5
- package/scripts/release-loop/shard-plan.ts +65 -0
- package/scripts/release-loop/unattended-release.ts +12 -2
- package/scripts/release-story/draft.ts +5 -1
- package/scripts/retro/shadow-compare.ts +71 -0
- package/scripts/rhythm/daily.ts +6 -1
- package/scripts/video/scene-table.ts +159 -0
- package/src/acp/core-turn-bridge.ts +39 -2
- package/src/agent-mission/driver.ts +34 -6
- package/src/autopilot/build/llm-conflict-merge.ts +132 -7
- package/src/autopilot/build/merge-intent.ts +135 -7
- package/src/away/presence.ts +40 -0
- package/src/away/release-watch.ts +272 -0
- package/src/chat/index.ts +2 -1
- package/src/chat/short-question.ts +3 -40
- package/src/cli/away-cli.ts +68 -0
- package/src/cli/config-drift.ts +513 -0
- package/src/cli/guardian-cli.ts +49 -0
- package/src/cli/intake-cli.ts +13 -7
- package/src/cli/log-event-names.ts +5 -0
- package/src/cli/pod-cli.ts +5 -1
- package/src/cli/pr-cli.ts +20 -0
- package/src/cli/pty-drive-cli.ts +14 -3
- package/src/cli/release-cli.ts +52 -12
- package/src/cli/retro-cli.ts +78 -0
- package/src/cli/setup-cli.ts +31 -1
- package/src/cli/tasks-cli.ts +193 -14
- package/src/context-bus/context-now-public.ts +1 -1
- package/src/context-bus/context-now-surfaces.ts +16 -6
- package/src/context-bus/context-now.ts +97 -3
- package/src/core-turn/short-question.ts +100 -0
- package/src/dashboard/dashboard-status-lines.ts +24 -4
- package/src/dashboard/index.ts +50 -5
- package/src/dashboard/input/slash-executor.ts +6 -2
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +68 -40
- package/src/dashboard/slash-runtime/decide-slash.ts +60 -0
- package/src/dashboard/slash-runtime/help-from-registry.ts +62 -23
- package/src/dashboard/slash-runtime/help-groups.ts +44 -0
- package/src/dashboard/slash-runtime/model-choices.ts +125 -0
- package/src/domains/guardian-tick.ts +138 -0
- package/src/domains/outbound-alert.ts +44 -13
- package/src/domains/outbound-log-probe.ts +6 -1
- package/src/domains/schedule-registry.ts +23 -0
- package/src/domains/telegram-kind-route.ts +39 -0
- package/src/domains/watch-default-scheduler.ts +95 -0
- package/src/esc-abort-gate.ts +44 -0
- package/src/graph-runner/graph-run-api.ts +205 -0
- package/src/graph-runner/runner.ts +36 -6
- package/src/harness/harness-cli-command.ts +420 -51
- package/src/harness/harness-queue.ts +315 -25
- package/src/harness/harness-salvage-cli.ts +140 -0
- package/src/harness/harness-worktree-add.ts +68 -0
- package/src/harness/ledger-write-guard.ts +6 -16
- package/src/hq/fence-outcomes.ts +63 -0
- package/src/hq/fence-wrapper.ts +33 -0
- package/src/hq/hq.ts +17 -2
- package/src/index.ts +118 -6
- package/src/instance/test-write-guard-spy-first.fixture.ts +13 -0
- package/src/instance/test-write-guard.ts +200 -0
- package/src/intake-plane/digest.ts +39 -15
- package/src/intake-plane/harness-target.ts +4 -3
- package/src/intake-plane/intake-sources.ts +1 -0
- package/src/intake-plane/items.ts +1 -0
- package/src/loops/orchestrator/degradation.ts +57 -0
- package/src/loops/orchestrator/finish-rate.ts +33 -3
- package/src/loops/orchestrator/tick.ts +166 -45
- package/src/maturity/feature-maturity.ts +6 -4
- package/src/maturity/tui-slash-maturity.ts +1 -0
- package/src/mss/logging/log-store.ts +3 -0
- package/src/nexus/api/graph-kinds.ts +10 -4
- package/src/nexus/api/graphs-api.ts +178 -6
- package/src/nexus/api/http-server.ts +54 -8
- package/src/nexus/api/loop-edges.ts +292 -17
- package/src/nexus/api/ops-api.ts +112 -29
- package/src/nexus/api/ops-board-route.ts +48 -0
- package/src/nexus/index.ts +7 -0
- package/src/onboarding/entry-hints.ts +5 -0
- package/src/onboarding.ts +24 -6
- package/src/release-loop/checklist.ts +2 -0
- package/src/release-loop/feature-store.ts +68 -18
- package/src/release-loop/gate-shards.ts +158 -0
- package/src/release-loop/light-rc.ts +173 -0
- package/src/release-loop/placement.ts +51 -17
- package/src/release-loop/release-schedule.ts +6 -2
- package/src/release-loop/rubric.ts +57 -1
- package/src/retro/daily.ts +372 -0
- package/src/seat-loop/op-decide.ts +173 -0
- package/src/seat-loop/seat-loop.ts +60 -3
- package/src/seat-loop/seat-report.ts +58 -3
- package/src/self-dev/ask-launch-flow.ts +5 -4
- package/src/self-dev/ask-launch-io.ts +49 -1
- package/src/self-dev/ask-path-candidates.ts +40 -2
- package/src/self-dev/dev-cli.ts +36 -0
- package/src/self-dev/draft-sweep.ts +371 -16
- package/src/self-dev/draft-triage-rules.ts +19 -4
- package/src/self-dev/orchestrate.ts +1 -0
- package/src/self-dev/pr-preview.ts +247 -0
- package/src/self-dev/shard-run.ts +23 -7
- package/src/self-implement/blocked-draft-policy.ts +101 -0
- package/src/self-implement/execution-clarification.ts +11 -0
- package/src/self-implement/gate-cli.ts +29 -8
- package/src/self-implement/gate-env-retry.ts +83 -0
- package/src/self-implement/goal-author-clarification.ts +88 -13
- package/src/self-implement/goal-clarification-escalation.ts +2 -0
- package/src/self-implement/orchestrator.ts +220 -6
- package/src/self-implement/run-stop.ts +123 -0
- package/src/self-implement/run-ttl.ts +258 -0
- package/src/self-implement/salvage-branches.ts +254 -0
- package/src/self-implement/salvage-retention.ts +123 -0
- package/src/self-implement/seams.ts +4 -0
- package/src/self-implement/self-implement-cli.ts +2 -0
- package/src/self-implement/sibling-resync.ts +223 -0
- package/src/self-implement/stop-autoheal.ts +204 -0
- package/src/task-agent/actions.ts +63 -2
- package/src/task-agent/board.ts +132 -0
- package/src/task-agent/card-evidence.ts +292 -0
- package/src/task-agent/completion-evidence.ts +126 -0
- package/src/task-agent/judge.ts +25 -0
- package/src/task-agent/live-moves.ts +276 -0
- package/src/task-agent/mission.ts +33 -7
- package/src/task-agent/shadow.ts +192 -15
- package/src/task-agent/task-hand.ts +124 -17
- package/src/task-orchestrator/surfaces/pod-command-job.ts +82 -8
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +4 -0
- package/src/task-orchestrator/surfaces/pod-install-slots.ts +89 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +233 -21
- package/src/task-orchestrator/surfaces/pod-pool.ts +155 -34
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +21 -5
- package/src/telegram-away-command.ts +38 -0
- package/src/telegram-commands.ts +52 -0
- package/src/telegram-report-log-probe.ts +21 -0
- package/src/telegram-report.ts +27 -3
- package/src/testing/real-cli-spawn.ts +106 -0
- package/src/tui-client/dashboard-session.ts +1 -0
- package/src/user-config.ts +190 -5
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +0 -25
- package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-7865aba0f4d2ee49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-a0eb02638b1e6f31.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c39554c321576982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c130b63862437814.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-a3c58f5c72a5f3f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e8e1ecaea7cd71d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-2da6f9b8a38c6978.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-44d5f767491692bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-61553851aa606146.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-a1f585e154e61c59.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-5150ef17b5383a57.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +0 -1
- package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.css +0 -1
- /package/apps/pwa/out/_next/static/{P6YTVgzuZz5oTZhY4Din_ → Bam1YziK2iH9bN4K0utGk}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{P6YTVgzuZz5oTZhY4Din_ → Bam1YziK2iH9bN4K0utGk}/_ssgManifest.js +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { withFileLockSync } from '../../storage/file-lock.js';
|
|
2
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { CardStore, taskCardsDir, type TaskCard } from '../../task-cards/card-store.js';
|
|
5
5
|
import { Database } from 'bun:sqlite';
|
|
6
6
|
import { type ChecklistItem } from '../../release-loop/checklist.js';
|
|
7
7
|
import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
8
8
|
import { getUserConfig, type OrchestratorLoopConfig } from '../../user-config.js';
|
|
9
|
+
import { rubricPriority } from '../../release-loop/rubric.js';
|
|
9
10
|
import { debug } from '../../debug/log.js';
|
|
10
11
|
import { listSelfDevRuns, runSummaryLine, selfDevRunsDir } from '../../self-dev/run-store.js';
|
|
11
12
|
import { planAction, runSeatLoopOnce, type SeatDeps, type SeatItem } from '../../seat-loop/seat-loop.js';
|
|
@@ -19,12 +20,19 @@ export type Window = '08' | '12' | '18';
|
|
|
19
20
|
export type Node = 'intake' | 'split' | 'place' | 'delegate' | 'launch' | 'reconcile' | 'report';
|
|
20
21
|
export type Mode = 'off' | 'shadow' | 'live';
|
|
21
22
|
const SEAT_LOOP_IDS: Readonly<Record<string, string>> = { OP: 'op-seat', MK: 'cmo-seat', TC: 'tc-seat', UX: 'ux-seat' };
|
|
22
|
-
export interface Cell { id: string; title: string; kind?: string; seat?: string; host?: string; gridHost?: boolean; version?: string; cardId: string; origin: 'candidate' | 'flow1a'; priority?: 'P0' | 'P1' | 'P2'; predecessors?: string[] }
|
|
23
|
+
export interface Cell { id: string; title: string; kind?: string; seat?: string; host?: string; gridHost?: boolean; version?: string; cardId: string; origin: 'candidate' | 'flow1a'; priority?: 'P0' | 'P1' | 'P2' | 'P3' | 'P4'; predecessors?: string[] }
|
|
23
24
|
export interface TickState {
|
|
24
25
|
runId: string; window: Window; mode: Mode; day: string;
|
|
25
26
|
cards: TaskCard[]; skippedCards: Array<{ id: string; reason: string }>; cells: Cell[]; placed: number; unplaced?: number; delegated: number; wouldDelegate: number; rebalanced?: number; rebalanceBlocked?: Array<{ id: string; reason: string }>;
|
|
26
27
|
reconciled: { reached: number; progressing: number; blocked: number; unknown: number };
|
|
27
28
|
runSummaries?: string[];
|
|
29
|
+
degradation?: { level: 1 | 2; reason: string };
|
|
30
|
+
/** The highest degradation actually applied in this run — the report keeps it even if the signal recovers before report. */
|
|
31
|
+
degradationPeak?: { level: 1 | 2; reason: string };
|
|
32
|
+
deferredCells?: Cell[];
|
|
33
|
+
/** place ran at L2 (skipped) in this run — its unplaced cells are carried even if the board is back by delegate. */
|
|
34
|
+
placeDegraded?: boolean;
|
|
35
|
+
delegatedKeys?: string[];
|
|
28
36
|
launched?: Array<{ seat: string; status: string; key?: string; queueId?: string; runId?: string }>;
|
|
29
37
|
/** ORCH-TA-HAND — cells handed to TASK-AGENT this run (card id · mode · launched). */
|
|
30
38
|
handed?: Array<{ key: string; cell: string; cardId: string; mode: 'shadow' | 'live'; launched: boolean }>;
|
|
@@ -42,6 +50,7 @@ export type RebalanceAdapter = (version: string) => Promise<{ decisions: unknown
|
|
|
42
50
|
export interface TickDeps {
|
|
43
51
|
root?: string; now?: Date; mode?: Mode; runId?: string; window?: Window;
|
|
44
52
|
cards?: () => TaskCard[]; split?: SplitAdapter; placeCell?: PlaceAdapter;
|
|
53
|
+
degradation?: () => { llm: boolean; board: boolean };
|
|
45
54
|
gridHosts?: readonly GridHost[];
|
|
46
55
|
seatTurn?: (seat: string, root: string, host?: string, options?: Pick<SeatDeps, 'enqueue'>) => Promise<{ status: string; queueId?: string; runId?: string; item?: { source: string; id: string } }>;
|
|
47
56
|
queueOutcome?: (queueId: string, root: string) => 'pending' | 'unknown' | 'retryable' | 'succeeded';
|
|
@@ -132,7 +141,9 @@ export function reportLine(state: TickState): string {
|
|
|
132
141
|
const r = state.reconciled;
|
|
133
142
|
const line = `orchestrator ${state.window} cards=${state.cards.length} cells=${state.cells.length} placed=${state.missing.includes('relplan1-absent') ? 'missing' : state.placed} delegated=${state.mode === 'shadow' ? `would ${state.wouldDelegate}` : state.delegated} reconciled=${r.reached}/${r.progressing}/${r.blocked}/${r.unknown} mode=${state.mode}`;
|
|
134
143
|
const details = [...(state.rebalanceBlocked ?? []).map(row => `⛔ 이월 거부 ${row.id}: ${row.reason}`), ...(state.runSummaries ?? [])];
|
|
135
|
-
|
|
144
|
+
const body = details.length ? `${line}\n${details.join('\n')}` : line;
|
|
145
|
+
const applied = state.degradationPeak ?? state.degradation;
|
|
146
|
+
return applied ? `강등 L${applied.level}: ${applied.reason}\n${body}` : body;
|
|
136
147
|
}
|
|
137
148
|
function readCardSnapshot(root: string): { cards: TaskCard[]; skippedCards: TickState['skippedCards'] } {
|
|
138
149
|
const dir = taskCardsDir(root);
|
|
@@ -321,6 +332,22 @@ function restorePrevious(root: string, day: string, mode: Mode): Pick<TickState,
|
|
|
321
332
|
}
|
|
322
333
|
return { cards: [...cards.values()], cells: [...cells.values()], launched: [...launched.values()] };
|
|
323
334
|
}
|
|
335
|
+
function previousDelegations(root: string, day: string, mode: Mode, runId: string): { deferred: Cell[]; keys: string[] } {
|
|
336
|
+
const dir = join(root, 'loop', 'orchestrator');
|
|
337
|
+
if (!existsSync(dir)) return { deferred: [], keys: [] };
|
|
338
|
+
const states = readdirSync(dir).filter(name => /^[a-zA-Z0-9._-]+\.json$/.test(name))
|
|
339
|
+
.flatMap(name => {
|
|
340
|
+
try {
|
|
341
|
+
const path = join(dir, name);
|
|
342
|
+
const state = JSON.parse(readFileSync(path, 'utf8')) as TickState;
|
|
343
|
+
return state.runId !== runId && state.day <= day && state.mode === mode && state.window === '08' &&
|
|
344
|
+
state.nodes?.delegate === 'ok' && Array.isArray(state.deferredCells)
|
|
345
|
+
? [{ name, state, mtime: statSync(path).mtimeMs }] : [];
|
|
346
|
+
} catch { return []; }
|
|
347
|
+
});
|
|
348
|
+
states.sort((a, b) => a.state.day.localeCompare(b.state.day) || a.mtime - b.mtime || a.name.localeCompare(b.name));
|
|
349
|
+
return { deferred: states.at(-1)?.state.deferredCells ?? [], keys: states.flatMap(({ state }) => Array.isArray(state.delegatedKeys) ? state.delegatedKeys : []) };
|
|
350
|
+
}
|
|
324
351
|
function initial(runId: string, window: Window, mode: Mode, day: string): TickState {
|
|
325
352
|
return { runId, window, mode, day, cards: [], skippedCards: [], cells: [], placed: 0, delegated: 0, wouldDelegate: 0,
|
|
326
353
|
reconciled: { reached: 0, progressing: 0, blocked: 0, unknown: 0 }, steps: [], nodes: {}, missing: [] };
|
|
@@ -349,11 +376,20 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
349
376
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
350
377
|
const runId = deps.runId ?? graphRunId() ?? `${kstDay(now)}-${window}`;
|
|
351
378
|
const path = stateFile(root, runId);
|
|
352
|
-
const
|
|
379
|
+
const orchestratorConfig = getUserConfig().loops?.orchestrator;
|
|
380
|
+
const configured = deps.mode ?? orchestratorConfig?.mode;
|
|
353
381
|
const mode: Mode = configured === 'off' || configured === 'live' ? configured : 'shadow';
|
|
354
382
|
const state: TickState = existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) as TickState : initial(runId, window, mode, kstDay(now));
|
|
355
383
|
state.steps ??= [];
|
|
356
384
|
if (state.window !== window || state.mode !== mode) throw new Error('orchestrator run window/mode changed');
|
|
385
|
+
if (deps.degradation) {
|
|
386
|
+
const signal = deps.degradation();
|
|
387
|
+
state.degradation = !signal.board ? { level: 2, reason: signal.llm ? 'board 불가' : 'LLM·board 불가' }
|
|
388
|
+
: !signal.llm ? { level: 1, reason: 'LLM 불가' } : undefined;
|
|
389
|
+
// Only nodes that act on the degradation (split · place · delegate · launch) count toward the reported peak.
|
|
390
|
+
const applies = (['split', 'place', 'delegate', 'launch'] as Node[]).includes(node) && activeNode(node, window);
|
|
391
|
+
if (applies && state.degradation && (!state.degradationPeak || state.degradation.level >= state.degradationPeak.level)) state.degradationPeak = state.degradation;
|
|
392
|
+
}
|
|
357
393
|
const predecessor: Partial<Record<Node, Node>> = { split: 'intake', place: 'split', delegate: 'place', launch: 'delegate', reconcile: 'launch', report: 'reconcile' };
|
|
358
394
|
const preceding = predecessor[node];
|
|
359
395
|
if (preceding && !state.nodes[preceding]) throw new Error(`orchestrator ${node}: missing ${preceding} result`);
|
|
@@ -380,14 +416,17 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
380
416
|
observe('exchange', 'wish-cards', state.cards.length);
|
|
381
417
|
} else if (node === 'split') {
|
|
382
418
|
// Shadow also asks the splitter (it only proposes cells); only live records `orch:split` on the card.
|
|
383
|
-
const
|
|
384
|
-
|
|
419
|
+
const ruleOnly = (state.degradation?.level ?? 0) >= 1;
|
|
420
|
+
const split = ruleOnly ? undefined : deps.split ?? await adapter<SplitAdapter>('../../flow-loop/split.js', 'splitCard');
|
|
421
|
+
if (!split && !ruleOnly) { state.missing.push('flow1a-absent'); observe('node-missing', 'flow1a-absent', state.cards.length); }
|
|
385
422
|
state.cells = [];
|
|
386
423
|
for (const card of state.cards) {
|
|
387
|
-
const cells = split ? await split(card, { shadow: mode === 'shadow' }) : [{ id: card.id, title: card.title }];
|
|
424
|
+
const cells = ruleOnly ? [{ id: card.id, title: card.title }] : split ? await split(card, { shadow: mode === 'shadow' }) : [{ id: card.id, title: card.title }];
|
|
388
425
|
for (const cell of cells) {
|
|
389
426
|
if (!cell.id?.trim() || !cell.title?.trim()) throw new Error('invalid split cell');
|
|
390
|
-
state.cells.push({ cardId: card.id, id: cell.id, title: cell.title, origin: split ? 'flow1a' : 'candidate',
|
|
427
|
+
state.cells.push({ cardId: card.id, id: cell.id, title: cell.title, origin: split ? 'flow1a' : 'candidate',
|
|
428
|
+
...(ruleOnly ? { priority: rubricPriority(card.sections.map(section => section.content).join('\n')) } : {}),
|
|
429
|
+
...(cell.kind !== undefined ? { kind: cell.kind } : {}), ...(cell.seat ? { seat: cell.seat } : {}), ...(cell.host !== undefined ? { host: cell.host } : {}) });
|
|
391
430
|
}
|
|
392
431
|
observe('exchange', `split:${card.id}`, cells.length);
|
|
393
432
|
if (split && cells.length && mode === 'live') {
|
|
@@ -398,33 +437,54 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
398
437
|
}
|
|
399
438
|
}
|
|
400
439
|
} else if (node === 'place') {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
// 담당 없는 칸은 배치를 부르든 안 부르든 센다 — 배치 호출 여부와 집계를 묶지 않는다.
|
|
409
|
-
for (const cell of state.cells) {
|
|
410
|
-
if (cell.origin === 'flow1a' && (!cell.seat || !SEAT_LOOP_IDS[cell.seat])) { state.unplaced = (state.unplaced ?? 0) + 1; observe('exchange', 'unplaced-no-owner', 1); }
|
|
440
|
+
// Carry unfinished assignments into the same place/delegate path before applying this tick's cap.
|
|
441
|
+
const carry = previousDelegations(root, state.day, mode, runId).deferred;
|
|
442
|
+
if (carry.length) {
|
|
443
|
+
// Carried cells keep their place ahead of this tick's new cells (deduplicated) — a cell deferred by the cap is not
|
|
444
|
+
// pushed back again by a newly split cell.
|
|
445
|
+
const carried = new Set(carry.map(cell => `orch:${cell.cardId}:${cell.id}`));
|
|
446
|
+
state.cells = [...carry, ...state.cells.filter(cell => !carried.has(`orch:${cell.cardId}:${cell.id}`))];
|
|
411
447
|
}
|
|
412
|
-
if (
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
448
|
+
if (state.degradation?.level === 2) {
|
|
449
|
+
state.placeDegraded = true;
|
|
450
|
+
observe('degraded-skip', 'place', 0);
|
|
451
|
+
} else {
|
|
452
|
+
const gridHosts = resolveGridHosts(deps, observe);
|
|
453
|
+
refreshGridHosts(state.cells, gridHosts, mode, root, observe);
|
|
454
|
+
if (mode === 'live') await syncQueuedPoolHints(root, state.launched ?? [], deps, observe);
|
|
455
|
+
const place = deps.placeCell ?? await adapter<PlaceAdapter>('../../release-loop/placement.js', 'placeCell');
|
|
456
|
+
const available = typeof place === 'function';
|
|
457
|
+
if (!available) { state.missing.push('relplan1-absent'); observe('node-missing', 'relplan1-absent', state.cells.length); }
|
|
458
|
+
if (mode === 'shadow' && deps.placeCell) observe('exchange', 'relplan1-shadow-not-invoked', state.cells.length);
|
|
459
|
+
// 담당 없는 칸은 배치를 부르든 안 부르든 센다 — 배치 호출 여부와 집계를 묶지 않는다.
|
|
460
|
+
for (const cell of state.cells) {
|
|
461
|
+
if (cell.origin === 'flow1a' && (!cell.seat || !SEAT_LOOP_IDS[cell.seat])) { state.unplaced = (state.unplaced ?? 0) + 1; observe('exchange', 'unplaced-no-owner', 1); }
|
|
462
|
+
}
|
|
463
|
+
if (available && (mode === 'live' || !deps.placeCell)) for (const cell of state.cells) {
|
|
464
|
+
if (cell.origin !== 'flow1a' || !cell.seat || !SEAT_LOOP_IDS[cell.seat]) continue;
|
|
465
|
+
// RELPLAN's dryRun calculates the same release without writing a checklist in shadow.
|
|
466
|
+
try {
|
|
467
|
+
const priority = cell.priority === 'P3' || cell.priority === 'P4' ? 'P2' : cell.priority ?? 'P2';
|
|
468
|
+
const placed = await place({ id: cell.id, title: cell.title, owner: cell.seat, priority, predecessors: cell.predecessors ?? [] }, { dryRun: mode === 'shadow', now });
|
|
469
|
+
if (validVersion(placed?.version)) {
|
|
470
|
+
cell.version = placed.version;
|
|
471
|
+
state.placed++;
|
|
472
|
+
observe('exchange', mode === 'shadow' ? `would-place:${cell.id}:${cell.version}` : `placed:${cell.id}:${cell.version}`, 1);
|
|
473
|
+
} else { state.unplaced = (state.unplaced ?? 0) + 1; observe('exchange', 'unplaced-no-release', 1); }
|
|
474
|
+
} catch (error) {
|
|
475
|
+
state.unplaced = (state.unplaced ?? 0) + 1;
|
|
476
|
+
observe('exchange', `unplaced:${cell.id}:${String(error).slice(0, 160)}`, 1);
|
|
477
|
+
}
|
|
425
478
|
}
|
|
426
479
|
}
|
|
427
480
|
} else if (node === 'delegate') {
|
|
481
|
+
if (state.degradation?.level === 2) observe('degraded-skip', 'delegate', 0);
|
|
482
|
+
const cap = orchestratorConfig?.maxDelegatePerTick;
|
|
483
|
+
const previous = previousDelegations(root, state.day, mode, runId);
|
|
484
|
+
if (cap !== undefined || state.degradation?.level === 2 || state.placeDegraded || previous.deferred.length) {
|
|
485
|
+
state.deferredCells = [];
|
|
486
|
+
state.delegatedKeys = [];
|
|
487
|
+
}
|
|
428
488
|
const path = journal(root);
|
|
429
489
|
// 모드와 칸 필터는 따로 푼다 — 주입으로 모드만 줘도 설정의 칸 필터는 그대로 먹는다.
|
|
430
490
|
const handConfig: Partial<OrchestratorLoopConfig> = deps.handToTaskAgent !== undefined && deps.handToTaskAgentCell !== undefined ? {} : getUserConfig().loops?.orchestrator ?? {};
|
|
@@ -432,7 +492,9 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
432
492
|
// ⭐ 넘김 claim 과 자리 저널 기록은 «같은 잠금»(자리 저널 잠금) 아래에서 서로를 다시 읽는다 — 동시 틱이 낡은 값을 보지 않게.
|
|
433
493
|
const ledger = handedLedger(root);
|
|
434
494
|
const liveHanded = handedKeys(ledger);
|
|
435
|
-
|
|
495
|
+
// Earlier runs' (would-)assignments only count while a cap is configured — they are what the cap carried over.
|
|
496
|
+
// With no cap the tick behaves as before: shadow would-delegates every cell again, live dedups on the seat journal.
|
|
497
|
+
const keys = new Set([...readRequests(root).map(row => row.key), ...(cap !== undefined ? previous.keys : [])]);
|
|
436
498
|
// 넘김 뒤 상태 저장 전에 죽은 같은 run 이 다시 돌면 원장에서 handed[] 를 되살린다.
|
|
437
499
|
const ledgerHanded = handedRows(ledger);
|
|
438
500
|
const restoreHanded = (handKey: string, key: string, rows: Map<string, HandedRow> = handedRows(ledger)): void => {
|
|
@@ -441,6 +503,26 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
441
503
|
if (!row || row.runId !== runId || (state.handed ?? []).some(entry => entry.key === key && entry.mode === row.mode)) return;
|
|
442
504
|
(state.handed ??= []).push({ key, cell: row.cell, cardId: row.cardId, mode: row.mode, launched: row.launched });
|
|
443
505
|
};
|
|
506
|
+
// A retried delegate (died after assigning, before saving state) rebuilds this run's cap usage from what it already
|
|
507
|
+
// wrote: seat rows tagged with this run and live hands claimed by this run.
|
|
508
|
+
if (cap !== undefined && state.delegatedKeys) {
|
|
509
|
+
const used = new Set(state.delegatedKeys);
|
|
510
|
+
if (existsSync(path)) for (const line of readFileSync(path, 'utf8').split('\n')) {
|
|
511
|
+
if (!line) continue;
|
|
512
|
+
let row: { key?: unknown; source?: unknown; orchRunId?: unknown };
|
|
513
|
+
try { row = JSON.parse(line) as typeof row; } catch { throw new Error('invalid seat request journal'); }
|
|
514
|
+
if (row.source === 'orchestrator' && row.orchRunId === runId && typeof row.key === 'string') used.add(row.key);
|
|
515
|
+
}
|
|
516
|
+
for (const row of handedRows(ledger).values()) if (row.runId === runId && row.mode === 'live') used.add(row.key);
|
|
517
|
+
if (existsSync(ledger)) for (const line of readFileSync(ledger, 'utf8').split('\n')) {
|
|
518
|
+
if (!line) continue;
|
|
519
|
+
try {
|
|
520
|
+
const claim = JSON.parse(line) as { key?: unknown; runId?: unknown; mode?: unknown; status?: unknown };
|
|
521
|
+
if (claim.runId === runId && claim.mode === 'live' && claim.status === 'claimed' && typeof claim.key === 'string') used.add(claim.key);
|
|
522
|
+
} catch { /* handedKeys already rejects a broken ledger */ }
|
|
523
|
+
}
|
|
524
|
+
state.delegatedKeys = [...used];
|
|
525
|
+
}
|
|
444
526
|
const journalHas = (key: string): boolean => {
|
|
445
527
|
if (!existsSync(path)) return false;
|
|
446
528
|
return readFileSync(path, 'utf8').split('\n').some(line => {
|
|
@@ -458,13 +540,24 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
458
540
|
restoreHanded(`shadow:${key}`, key, ledgerHanded);
|
|
459
541
|
if (keys.has(key)) continue;
|
|
460
542
|
if (liveHanded.has(key)) { keys.add(key); observe('exchange', `handed-to-task-agent:${key}`, 1, 'task-agent'); continue; }
|
|
543
|
+
// COORD-HA ②: a live tick skips unplaced cells for assignment; L2 assigns nothing new (live hands included) and the
|
|
544
|
+
// per-tick cap defers the rest to the next tick — both before the TASK-AGENT hand so a hand never bypasses them.
|
|
545
|
+
const unplacedLive = mode === 'live' && !validVersion(cell.version);
|
|
546
|
+
// L2 carries every cell — placed or not — to the next tick: split already marked the card, so a cell skipped here
|
|
547
|
+
// would never be split again.
|
|
548
|
+
if (state.degradation?.level === 2 || (unplacedLive && state.placeDegraded)) {
|
|
549
|
+
state.deferredCells?.push(cell);
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
// The cap limits assignments (a live hand or a seat row); a shadow card is not an assignment and is still written.
|
|
553
|
+
const capped = !unplacedLive && cap !== undefined && state.delegatedKeys!.length >= cap;
|
|
461
554
|
// ORCH-TA-HAND: hand the picked cell to TASK-AGENT. shadow = card only (the seat path below is unchanged);
|
|
462
555
|
// live (tick live ⊕ hand live) = launch through TASK-AGENT and skip the seat journal so the cell launches once.
|
|
463
556
|
const handMode = deps.handToTaskAgent ?? handConfig.handToTaskAgent ?? 'off';
|
|
464
557
|
const handCell = deps.handToTaskAgentCell ?? handConfig.handToTaskAgentCell;
|
|
465
558
|
if (handMode !== 'off' && (!handCell || handCell === cell.id) && (TASK_SEATS as readonly string[]).includes(cell.seat)) {
|
|
466
559
|
// live 는 tick 도 live 이고 배치(유효한 판)가 된 칸만 — 나머지는 카드만(shadow · 배치 전 칸도 표본이 된다).
|
|
467
|
-
const handLive = handMode === 'live' && mode === 'live' && validVersion(cell.version);
|
|
560
|
+
const handLive = handMode === 'live' && mode === 'live' && validVersion(cell.version) && !capped;
|
|
468
561
|
// shadow 표본은 따로 센다 — 자리 경로로 아직 안 간 칸(shadow 틱·배치 전)이 나중에 live 로 바뀌면 그때 뜬다.
|
|
469
562
|
// ⚠️ 이미 자리 저널에 들어간 칸은 live 로 바꿔도 다시 안 넘긴다(keys.has 에서 끝) — 한 칸의 발사 주체는 하나다(의도).
|
|
470
563
|
const handKey = handLive ? key : `shadow:${key}`;
|
|
@@ -482,12 +575,13 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
482
575
|
const result = await (deps.handTask ?? (async (opts: HandTaskOptions) => (await import('../../task-agent/task-hand.js')).handTask(opts)))({
|
|
483
576
|
text: cell.title, seat: cell.seat as TaskSeat, checklistId: cell.id, live: handLive, now: () => now,
|
|
484
577
|
statePath: join(root, 'task-agent-actions.json'),
|
|
485
|
-
...(handLive ? { launcher: deps.taskLauncher ?? (async (args: string[]) => (await import('../../cli/tasks-cli.js')).defaultTaskLauncher(args)) } : {}),
|
|
578
|
+
...(handLive ? { launcher: deps.taskLauncher ?? (async (args: string[], _cwd?: string, context?: Parameters<TaskLauncher>[2]) => (await import('../../cli/tasks-cli.js')).defaultTaskLauncher(args, undefined, context)) } : {}),
|
|
486
579
|
});
|
|
487
580
|
withFileLockSync(`${path}.lock`, () => appendFileSync(ledger, `${JSON.stringify({ key: handKey, cell: cell.id, cardId: result.card.id, mode: result.mode, launched: result.launched, status: 'handed', runId, at: now.toISOString() })}\n`));
|
|
488
581
|
(state.handed ??= []).push({ key, cell: cell.id, cardId: result.card.id, mode: result.mode, launched: result.launched });
|
|
489
582
|
observe('hand-to-task-agent', `${key}:${result.card.id}:${result.mode}${result.launched ? ':launched' : ''}`, 1, 'task-agent');
|
|
490
|
-
|
|
583
|
+
// A live hand is an assignment: it counts toward maxDelegatePerTick like a seat row (COORD-HA ②).
|
|
584
|
+
if (handLive) { keys.add(key); state.delegatedKeys?.push(key); continue; }
|
|
491
585
|
} catch (error) {
|
|
492
586
|
observe('hand-to-task-agent-failed', `${key}:${String(error).slice(0, 160)}`, 1, 'task-agent');
|
|
493
587
|
// A failed live hand stays claimed (no relaunch loop) and does not fall back to the seat path — one launcher per cell.
|
|
@@ -514,11 +608,17 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
514
608
|
if (handLive) { keys.add(key); continue; }
|
|
515
609
|
}
|
|
516
610
|
}
|
|
611
|
+
if (capped) {
|
|
612
|
+
state.deferredCells!.push(cell);
|
|
613
|
+
observe('cap-deferred', key, 1, targetLoopId);
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
if (unplacedLive) continue;
|
|
517
617
|
if (!validVersion(cell.version)) {
|
|
518
|
-
if (mode === 'shadow') { keys.add(key); state.wouldDelegate++; observe('would-delegate', `${key} (unplaced)`, 1, targetLoopId); }
|
|
618
|
+
if (mode === 'shadow') { keys.add(key); state.wouldDelegate++; state.delegatedKeys?.push(key); observe('would-delegate', `${key} (unplaced)`, 1, targetLoopId); }
|
|
519
619
|
continue;
|
|
520
620
|
}
|
|
521
|
-
if (mode === 'shadow') { keys.add(key); state.wouldDelegate++; observe('would-delegate', key, 1, targetLoopId); continue; }
|
|
621
|
+
if (mode === 'shadow') { keys.add(key); state.wouldDelegate++; state.delegatedKeys?.push(key); observe('would-delegate', key, 1, targetLoopId); continue; }
|
|
522
622
|
keys.add(key);
|
|
523
623
|
mkdirSync(dirname(path), { recursive: true });
|
|
524
624
|
// TC review must-fix ①: the seat-requests journal contract needs receiptId, and appends are serialized under the
|
|
@@ -527,19 +627,23 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
527
627
|
if (journalHas(key)) return false;
|
|
528
628
|
// 잠금 안에서 다시 본다 — 동시 틱이 이 칸을 방금 live 로 넘겼으면 자리 경로로 보내지 않는다.
|
|
529
629
|
if (handedKeys(ledger).has(key)) return 'handed';
|
|
530
|
-
appendFileSync(path, `${JSON.stringify({ key, receiptId: key, seat: cell.seat, ...(cell.kind !== undefined ? { kind: cell.kind } : {}), ...(cell.host !== undefined ? { host: cell.host } : {}), ...(cell.gridHost ? { gridHost: true } : {}), loopId: targetLoopId, text: cell.title, status: 'queued', queuedAt: now.toISOString(), source: 'orchestrator', cell: cell.id, version: cell.version })}\n`);
|
|
630
|
+
appendFileSync(path, `${JSON.stringify({ key, receiptId: key, seat: cell.seat, ...(cell.kind !== undefined ? { kind: cell.kind } : {}), ...(cell.host !== undefined ? { host: cell.host } : {}), ...(cell.gridHost ? { gridHost: true } : {}), loopId: targetLoopId, text: cell.title, status: 'queued', queuedAt: now.toISOString(), source: 'orchestrator', cell: cell.id, version: cell.version, orchRunId: runId })}\n`);
|
|
531
631
|
return true;
|
|
532
632
|
});
|
|
533
633
|
if (appended === 'handed') { observe('exchange', `seat-suppressed-handed:${key}`, 1, 'task-agent'); continue; }
|
|
534
634
|
if (!appended) continue;
|
|
535
635
|
state.delegated++;
|
|
636
|
+
state.delegatedKeys?.push(key);
|
|
536
637
|
observe('exchange', 'queued', 1, targetLoopId);
|
|
537
638
|
}
|
|
538
639
|
} else if (node === 'launch') {
|
|
640
|
+
if (state.degradation?.level === 2) observe('degraded-skip', 'launch', 0);
|
|
539
641
|
// shadow 는 저널을 읽지 않는다 — 깨진 live 저널이 관측 전용 틱을 넘어뜨리지 않게.
|
|
540
|
-
const latestRequests = new Map(mode === 'live' ? readRequests(root).map(row => [row.key, row]) : []);
|
|
541
|
-
const eligible = state.cells.filter(cell => cell.origin === 'flow1a' && cell.seat && SEAT_LOOP_IDS[cell.seat]
|
|
542
|
-
&& validVersion(cell.version) && (mode === 'shadow' || latestRequests.get(`orch:${cell.cardId}:${cell.id}`)?.status === 'queued')
|
|
642
|
+
const latestRequests = new Map(mode === 'live' && state.degradation?.level !== 2 ? readRequests(root).map(row => [row.key, row]) : []);
|
|
643
|
+
const eligible = state.cells.filter(cell => state.degradation?.level !== 2 && cell.origin === 'flow1a' && cell.seat && SEAT_LOOP_IDS[cell.seat]
|
|
644
|
+
&& validVersion(cell.version) && (mode === 'shadow' || latestRequests.get(`orch:${cell.cardId}:${cell.id}`)?.status === 'queued')
|
|
645
|
+
&& (orchestratorConfig?.maxDelegatePerTick === undefined || state.delegatedKeys?.includes(`orch:${cell.cardId}:${cell.id}`)
|
|
646
|
+
|| (mode === 'live' && latestRequests.has(`orch:${cell.cardId}:${cell.id}`))));
|
|
543
647
|
const seats = [...new Set(eligible.map(cell => cell.seat!))];
|
|
544
648
|
state.launched = [];
|
|
545
649
|
for (const seat of seats) {
|
|
@@ -637,19 +741,36 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
|
|
|
637
741
|
return state;
|
|
638
742
|
}
|
|
639
743
|
|
|
744
|
+
/** COORD-HA — the production supply of `TickDeps.degradation` (config `loops.orchestrator.degradationSignal`). Shadow (the
|
|
745
|
+
* default) probes and logs what the tick would have done; only live hands the signal to the tick. */
|
|
746
|
+
export async function degradationDeps(node: Node, deps: { config?: () => Partial<OrchestratorLoopConfig> | undefined; root?: string;
|
|
747
|
+
probe?: (root: string) => { llm: boolean; board: boolean; why: string[] } } = {}): Promise<Pick<TickDeps, 'degradation'>> {
|
|
748
|
+
const mode = (deps.config ?? (() => getUserConfig().loops?.orchestrator))()?.degradationSignal ?? 'shadow';
|
|
749
|
+
if (mode === 'off') return {};
|
|
750
|
+
const root = deps.root ?? effectiveInstanceRoot();
|
|
751
|
+
const signal = (deps.probe ?? (await import('./degradation.js')).probeDegradation)(root);
|
|
752
|
+
const level = !signal.board ? 2 : !signal.llm ? 1 : 0;
|
|
753
|
+
debug.log('loop.orchestrator', 'degradation-signal', { node, mode, llm: signal.llm, board: signal.board, level, why: signal.why });
|
|
754
|
+
return mode === 'live' ? { degradation: () => ({ llm: signal.llm, board: signal.board }) } : {};
|
|
755
|
+
}
|
|
756
|
+
|
|
640
757
|
if (import.meta.main) {
|
|
641
758
|
const node = process.argv[2];
|
|
642
759
|
const index = process.argv.indexOf('--window');
|
|
643
|
-
|
|
760
|
+
// ORCH-LIVE-1008: `graph run` recipes pass no flags, so a manual/rehearsal tick picks its window through the
|
|
761
|
+
// environment (`ELANOUS_ORCH_WINDOW=08 elanous graph run graphs/orchestrator/orchestrator.yaml`). The flag wins.
|
|
762
|
+
const window = index < 0 ? process.env.ELANOUS_ORCH_WINDOW : process.argv[index + 1];
|
|
763
|
+
// A given flag (even without a value) or a set variable must name a real window — never fall back to «now».
|
|
764
|
+
const windowGiven = index >= 0 || process.env.ELANOUS_ORCH_WINDOW !== undefined;
|
|
644
765
|
if (!['intake', 'split', 'place', 'delegate', 'launch', 'reconcile', 'report'].includes(node ?? '') ||
|
|
645
|
-
(
|
|
646
|
-
console.error('orchestrator: expected <intake|split|place|delegate|launch|reconcile|report> [--window 08|12|18]');
|
|
766
|
+
(windowGiven && !['08', '12', '18'].includes(window ?? ''))) {
|
|
767
|
+
console.error('orchestrator: expected <intake|split|place|delegate|launch|reconcile|report> [--window 08|12|18] (or ELANOUS_ORCH_WINDOW=08|12|18)');
|
|
647
768
|
process.exitCode = 2;
|
|
648
769
|
} else {
|
|
649
770
|
void (async () => {
|
|
650
771
|
try { await (await import('../../domains/standalone-log-sink.js')).registerStandaloneLogSink('orchestrator'); }
|
|
651
772
|
catch { /* local execution can still use the per-run file */ }
|
|
652
|
-
await runOrchestratorNode(node as Node, { window: window as Window | undefined });
|
|
773
|
+
await runOrchestratorNode(node as Node, { window: window as Window | undefined, ...await degradationDeps(node as Node) });
|
|
653
774
|
})().catch(error => { console.error(`orchestrator ${node}: ${String(error)}`); process.exitCode = 1; });
|
|
654
775
|
}
|
|
655
776
|
}
|
|
@@ -48,6 +48,7 @@ export const FEATURE_MATURITY = {
|
|
|
48
48
|
'/ceo': { pwa: 'ops' },
|
|
49
49
|
'/decisions': { pwa: 'ops' },
|
|
50
50
|
'/dashboard': { pwa: 'ops' },
|
|
51
|
+
'/pitch': { pwa: 'ops' }, // PITCH-ROOM — 투자자 피치 방(운영자 전용 · 공개 내보내기 제외)
|
|
51
52
|
'/bots': { pwa: 'ops' },
|
|
52
53
|
'/botlab': { pwa: 'ops' },
|
|
53
54
|
'/morning': { pwa: 'broken' },
|
|
@@ -57,7 +58,7 @@ export const FEATURE_MATURITY = {
|
|
|
57
58
|
},
|
|
58
59
|
// Canonical CLI command names only; aliases inherit their command's grade.
|
|
59
60
|
cliRoot: {
|
|
60
|
-
pty: 'beta', pr: 'beta', repo: 'beta', role: 'beta', machine: 'beta', 'agent-env': 'beta',
|
|
61
|
+
preview: 'beta', pty: 'beta', pr: 'beta', repo: 'beta', role: 'beta', machine: 'beta', 'agent-env': 'beta',
|
|
61
62
|
where: 'stable', shadow: 'beta', llm: 'beta', storage: 'beta', leader: 'beta',
|
|
62
63
|
pod: 'beta', browser: 'beta', questions: 'beta', usage: 'stable', live: 'beta',
|
|
63
64
|
research: 'beta', release: 'beta', know: 'beta', freeze: 'beta', flow: 'beta', seat: 'beta', hq: 'beta', steward: 'beta', coord: 'beta', context: 'beta', 'model-watch': 'beta', doctor: 'stable',
|
|
@@ -73,10 +74,10 @@ export const FEATURE_MATURITY = {
|
|
|
73
74
|
loops: 'beta', buzz: 'beta', harness: 'stable', self: 'beta', factcheck: 'beta',
|
|
74
75
|
provider: 'stable', 'provider:set': 'beta', 'provider:restore': 'beta',
|
|
75
76
|
'provider:rotate': 'beta', 'provider:use': 'beta', 'status-bar': 'beta',
|
|
76
|
-
schedule: 'beta', intake: 'beta', logs: 'stable', ad: 'beta', docs: 'beta',
|
|
77
|
+
schedule: 'beta', guardian: 'beta', intake: 'beta', logs: 'stable', retro: 'beta', ad: 'beta', docs: 'beta',
|
|
77
78
|
decisions: 'beta', directives: 'beta', claims: 'beta', lesson: 'beta', fleet: 'beta', ops: 'stable', outputs: 'beta', publish: 'beta',
|
|
78
79
|
env: 'beta', 'agent-mission': 'beta', dev: 'beta', keys: 'beta', finance: 'beta',
|
|
79
|
-
config: 'stable', tasks: 'stable', brief: 'beta', notify: 'beta', labels: 'beta', msg: 'beta', ask: 'stable',
|
|
80
|
+
config: 'stable', tasks: 'stable', brief: 'beta', notify: 'beta', away: 'beta', labels: 'beta', msg: 'beta', ask: 'stable',
|
|
80
81
|
onboarding: 'beta', login: 'stable', session: 'beta', chat: 'stable', repl: 'stable',
|
|
81
82
|
agent: 'stable', registry: 'beta', tier: 'stable', local: 'beta', scheduler: 'beta',
|
|
82
83
|
attach: 'beta', wf: 'beta', nexus: 'stable', phone: 'beta', token: 'beta',
|
|
@@ -99,6 +100,7 @@ export const FEATURE_MATURITY = {
|
|
|
99
100
|
'run-skill': 'stable',
|
|
100
101
|
ad: 'beta',
|
|
101
102
|
design: 'beta',
|
|
103
|
+
decide: 'beta',
|
|
102
104
|
provider: 'stable',
|
|
103
105
|
reasoning: 'stable',
|
|
104
106
|
local: 'tool',
|
|
@@ -158,7 +160,7 @@ export const FEATURE_MATURITY = {
|
|
|
158
160
|
ping: 'stable', provider: 'stable', sessions: 'stable', fork: 'stable', resume: 'stable',
|
|
159
161
|
decisions: 'stable', work: 'stable', cancel: 'stable',
|
|
160
162
|
skills: 'beta', skill: 'beta', digest: 'beta', intake: 'beta', ad: 'beta',
|
|
161
|
-
taste: 'beta', missions: 'beta', attach: 'beta', detach: 'beta', now: 'beta', loops: 'beta', project: 'beta', wish: 'beta',
|
|
163
|
+
taste: 'beta', missions: 'beta', attach: 'beta', detach: 'beta', now: 'beta', loops: 'beta', runs: 'beta', release: 'beta', away: 'beta', project: 'beta', wish: 'beta',
|
|
162
164
|
brain: 'tool', cc: 'tool', cdx: 'tool', gem: 'tool', local: 'tool',
|
|
163
165
|
harness: 'ops',
|
|
164
166
|
// Existing commands outside the CXO four-grade menu list are owner-only.
|
|
@@ -12,6 +12,7 @@ export function readTuiSlashAudience(raw: unknown = getUserConfig().raw?.tui): {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function slashMaturity(name: string): Maturity | undefined {
|
|
15
|
+
// Aliases inherit the canonical grade (including /dec from /decide).
|
|
15
16
|
const command = SLASH_COMMANDS.find((entry) => entry.name === name || entry.aliases?.includes(name));
|
|
16
17
|
if (!command) return undefined;
|
|
17
18
|
return FEATURE_MATURITY.tuiSlash[command.name as keyof typeof FEATURE_MATURITY.tuiSlash];
|
|
@@ -26,6 +26,7 @@ import type { LogSink } from './sink.js';
|
|
|
26
26
|
import { setInstanceName, resolveInstanceName } from '../../instance-identity.js';
|
|
27
27
|
import * as nestDepth from '../../agent/nest-depth.js';
|
|
28
28
|
import { debug } from '../../debug/log.js';
|
|
29
|
+
import { assertNotTestWritingOps } from '../../instance/test-write-guard.js';
|
|
29
30
|
|
|
30
31
|
/** 스토어 경로 — `ELANOUS_STATE_DIR` 존중(격리 테스트/`nexus run --test` 의
|
|
31
32
|
* 기존 knob 그대로), 기본 `~/.elanous/logs/logs.db`. lazy 함수 — env 를
|
|
@@ -209,6 +210,8 @@ export class LogStore {
|
|
|
209
210
|
this.db.run(`PRAGMA busy_timeout = ${LOG_STORE_READONLY_BUSY_TIMEOUT_MS}`);
|
|
210
211
|
return;
|
|
211
212
|
}
|
|
213
|
+
// TEST-PROD-LEAK: a test process never opens the ops logs.db for write, preload or not.
|
|
214
|
+
assertNotTestWritingOps(path, 'open logs.db for write');
|
|
212
215
|
if (path !== ':memory:') mkdirSync(dirname(path), { recursive: true });
|
|
213
216
|
const openedAt = Date.now();
|
|
214
217
|
for (let attempt = 0; ; attempt++) {
|
|
@@ -58,12 +58,18 @@ export async function handleGraphsValidatePost(req: Request, opts: MetaApiOpts):
|
|
|
58
58
|
return jsonResponse({ error: 'bad_request', reason: 'graph (harness|workflow) and yaml (string) required' }, 400);
|
|
59
59
|
}
|
|
60
60
|
const { graph, yaml } = body as { graph: GraphKind; yaml: string };
|
|
61
|
+
const { ok, errors, ignoredKeys } = validateGraphYaml(graph, yaml);
|
|
62
|
+
debug.log('graph.kinds', 'validate', { graph, ok, errors, ignored: ignoredKeys });
|
|
63
|
+
return jsonResponse({ ok, errors, ignoredKeys }, ok ? 200 : 422);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The one validator behind `POST /v1/graphs/validate` and the graph save path. */
|
|
67
|
+
export function validateGraphYaml(graph: GraphKind, yaml: string): { ok: boolean; errors: readonly unknown[]; ignoredKeys: string[]; graphId?: string } {
|
|
61
68
|
const result = graph === 'harness' ? parseGraphTemplateYaml(yaml) : parseWorkflowYaml(yaml);
|
|
62
|
-
const errors = graph === 'harness' ? (result as ReturnType<typeof parseGraphTemplateYaml>).errors : (result as ReturnType<typeof parseWorkflowYaml>).issues;
|
|
69
|
+
const errors: readonly unknown[] = graph === 'harness' ? (result as ReturnType<typeof parseGraphTemplateYaml>).errors : (result as ReturnType<typeof parseWorkflowYaml>).issues;
|
|
63
70
|
let input: unknown;
|
|
64
71
|
try { input = parseYaml(yaml); } catch { /* The parser already reports the YAML error. */ }
|
|
65
72
|
const ignoredKeys = ignoredPaths(input, graph === 'harness' ? harnessShape : workflowShape);
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
return jsonResponse({ ok, errors, ignoredKeys }, ok ? 200 : 422);
|
|
73
|
+
const graphId = graph === 'harness' ? (result as ReturnType<typeof parseGraphTemplateYaml>).template?.graphId : undefined;
|
|
74
|
+
return { ok: errors.length === 0, errors, ignoredKeys, ...(graphId === undefined ? {} : { graphId }) };
|
|
69
75
|
}
|