elanous 0.2.19 → 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/{J5H7BvqgSI8DUjtRRcA_S → Bam1YziK2iH9bN4K0utGk}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 1268.0840ff91e083fcbd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-77fad80cf54477c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +5 -0
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2977-e38442e86289f1d3.js → 211-9c9090c59e2c5907.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2116-6429c188eb846ab8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.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/2537-6c289376caa88ca1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.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/{4847.56f5bc454fbcc827.js → 6110.1297d4093fdfbb0d.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/6158.af2e93b7adff709b.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1470.98a6ab61ccb4131c.js → 726.83532d69452ee24b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 7327-2d69fb321ba73389.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{8870-9614a213b990b10b.js → 7748-6e51bfe927019f5c.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7647.2fcd30d894d5ee30.js → 935.4ddaa99fc1d1126c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{ae787e4c-e5b1ef00b2dbceca.js → a9f2b54d-33c0689b70ed8cf3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-c855c24de7db74fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-85b316bb4235980b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-9b2b26a33cb2cd42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-c9955fde67b1bcb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-7a852956c74015a3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-4ba4abe096b6c2a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-575d4a8b4f7becb2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f5d56ff5f45ad164.js +1 -0
- 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-7c058069c5e7fa63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-466bf0ee16ad1432.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-a23bc71713bcc08f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/{page-5b29d9b06ceadade.js → page-ed4a6f799f005d09.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-9cd565005b93aca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-474cb5888ea0e8f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-35bfec2e631d3eae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-9ca21f9ad3208a61.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-f9ccb1b1adbea14d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-ec102c9b8b691e37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-a9c2cfa8dda922a5.js +1 -0
- 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-db03ae3589a81c9e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4a6463b97f0ad94f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-75bffecc9aa1004e.js +1 -0
- 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-31d5b875760bb62d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c26fa10aac6c21c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-68793c6e10331d66.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-46d5201d2207a511.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-d81c07a7b4f618ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-be1c4e8697f22360.js +1 -0
- 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-0fd895fab9b62c1b.js +1 -0
- 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-34882c84f84021c5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → f6231284-45726550aba08fca.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2ea96958.e8c7e112de50b067.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-c260571513bf43c2.js +1 -0
- package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/demo/editor-recipes.yaml +22 -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/land-global-checks.ts +89 -0
- package/scripts/lib/pch-regress-map.ts +2 -1
- package/scripts/release-loop/gate-node.ts +296 -25
- package/scripts/release-loop/gate-timing.ts +116 -7
- 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 +4 -1
- package/scripts/release-loop/unattended-release.ts +10 -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/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/release-watch.ts +20 -3
- package/src/cli/config-drift.ts +513 -0
- package/src/cli/log-event-names.ts +4 -0
- package/src/cli/pr-cli.ts +20 -0
- package/src/cli/release-cli.ts +20 -5
- package/src/cli/retro-cli.ts +78 -0
- package/src/cli/tasks-cli.ts +94 -8
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -4
- package/src/dashboard/slash-runtime/help-from-registry.ts +57 -31
- package/src/domains/outbound-log-probe.ts +6 -1
- package/src/graph-runner/graph-run-api.ts +205 -0
- package/src/graph-runner/runner.ts +11 -1
- package/src/harness/harness-cli-command.ts +419 -52
- package/src/harness/harness-queue.ts +315 -25
- package/src/harness/harness-salvage-cli.ts +62 -0
- package/src/hq/fence-outcomes.ts +63 -0
- package/src/hq/fence-wrapper.ts +33 -0
- package/src/hq/hq.ts +11 -1
- package/src/index.ts +69 -6
- package/src/maturity/feature-maturity.ts +2 -2
- 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 +38 -3
- package/src/nexus/api/loop-edges.ts +4 -2
- package/src/nexus/api/ops-api.ts +22 -7
- package/src/nexus/api/ops-board-route.ts +48 -0
- package/src/onboarding/entry-hints.ts +5 -0
- package/src/onboarding.ts +24 -6
- package/src/release-loop/feature-store.ts +14 -11
- package/src/release-loop/gate-shards.ts +158 -0
- package/src/release-loop/release-schedule.ts +6 -2
- 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/draft-sweep.ts +371 -16
- package/src/self-dev/draft-triage-rules.ts +19 -4
- package/src/self-dev/pr-preview.ts +84 -16
- package/src/self-implement/blocked-draft-policy.ts +101 -0
- package/src/self-implement/execution-clarification.ts +11 -0
- package/src/self-implement/gate-env-retry.ts +83 -0
- package/src/self-implement/orchestrator.ts +218 -5
- package/src/self-implement/run-stop.ts +123 -0
- package/src/self-implement/run-ttl.ts +258 -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/board.ts +1 -1
- package/src/task-agent/card-evidence.ts +99 -10
- package/src/task-agent/judge.ts +7 -0
- package/src/task-agent/live-moves.ts +120 -20
- package/src/task-agent/mission.ts +1 -1
- package/src/task-agent/shadow.ts +10 -1
- package/src/task-agent/task-hand.ts +7 -2
- package/src/task-orchestrator/surfaces/pod-command-job.ts +19 -5
- 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 +93 -10
- package/src/task-orchestrator/surfaces/pod-pool.ts +147 -34
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +19 -3
- package/src/telegram-commands.ts +41 -0
- package/src/testing/real-cli-spawn.ts +106 -0
- package/src/user-config.ts +56 -3
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +0 -1
- package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-e061bb24cd2b5cde.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +0 -2
- package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +0 -1
- package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-b70cd005d9169e25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +0 -1
- package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +0 -1
- /package/apps/pwa/out/_next/static/{J5H7BvqgSI8DUjtRRcA_S → Bam1YziK2iH9bN4K0utGk}/_ssgManifest.js +0 -0
|
@@ -44,8 +44,12 @@ export interface TaskCard {
|
|
|
44
44
|
goal?: string;
|
|
45
45
|
milestone?: string;
|
|
46
46
|
createdAt: string;
|
|
47
|
-
/**
|
|
48
|
-
|
|
47
|
+
/**
|
|
48
|
+
* launch-failed = 발사기가 띄우기에 실패했다(사유는 history 마지막 칸) — 다음 수는 다시 발사.
|
|
49
|
+
* failed = 발사된 런이 PR·수확 가지 없이 죽었거나 끝내 안 떴다(TA-JUDGE-DEAD-RUN · `card-evidence.ts`) — 사유
|
|
50
|
+
* (`failed/needs-owner|needs-relaunch — …`)는 history 마지막 칸 · 자동 재발사하지 않는다(사람/판단부 몫).
|
|
51
|
+
*/
|
|
52
|
+
status: 'handed' | 'launched' | 'launch-failed' | 'failed';
|
|
49
53
|
history: TaskCardEvent[];
|
|
50
54
|
/** 미션 카드만 — 조각 카드 id(순서대로) · 쪼갠 출처(`mission.ts` splitMission). */
|
|
51
55
|
pieces?: string[];
|
|
@@ -178,6 +182,7 @@ export function nextMoveFor(card: TaskCard, landed?: ReadonlySet<string>): TaskC
|
|
|
178
182
|
if (card.status !== 'launched' && unmet.length > 0) return { kind: 'wait', reason: `after ${unmet.join(', ')}${landed ? '' : ' · 착지 미확인'}` };
|
|
179
183
|
if (card.status === 'handed') return { kind: 'launch', command: launchArgs(card), reason: '넘겨받은 과제 — 첫 발사' };
|
|
180
184
|
if (card.status === 'launch-failed') return { kind: 'launch', command: launchArgs(card), reason: '직전 발사 실패 — 다시 발사' };
|
|
185
|
+
if (card.status === 'failed') return { kind: 'wait', reason: card.history.at(-1)?.detail ?? '런 실패 — PR·수확 가지 없음' };
|
|
181
186
|
return { kind: 'wait', reason: '발사됨 — 런 멈춤을 슈퍼바이저 그림자 판단(task-agent.shadow-move)이 받는다' };
|
|
182
187
|
}
|
|
183
188
|
|
|
@@ -14,6 +14,7 @@ import { debug } from '../../debug/log.js';
|
|
|
14
14
|
import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
15
15
|
import { collectPodArtifacts } from './pod-artifact-return.js';
|
|
16
16
|
import { podBunCacheVolume } from './pod-bun-cache.js';
|
|
17
|
+
import { podHostDirsInit, podInstallSlotsVolume } from './pod-install-slots.js';
|
|
17
18
|
import { type PodPoolMember, PodPoolScheduler, resolvePodPoolSpec, parsePodPool, checkPodPool, syncPoolImages, type PoolKubectl } from './pod-pool.js';
|
|
18
19
|
import { podSkillsDigest, readSkillEnvFiles, resolvePodSkills } from './pod-skills.js';
|
|
19
20
|
import { podSourceScript, type PodSource } from './pod-source-receive.js';
|
|
@@ -176,6 +177,8 @@ export interface PodCommandJobInput {
|
|
|
176
177
|
clone?: boolean;
|
|
177
178
|
hostMirror?: string;
|
|
178
179
|
bunCache?: string;
|
|
180
|
+
/** GATE-INSTALL-CACHE — hostPath of the node-wide install tokens (release gate shards only · `pod-install-slots.ts`). */
|
|
181
|
+
installSlots?: string;
|
|
179
182
|
/** 컨테이너 메모리 한도(기본 16Gi) — 게이트가 파일 하나 격리 Job 에 올린다. */
|
|
180
183
|
memoryLimit?: string;
|
|
181
184
|
/** GATE-SPEED A3①: CPU request/limit override (release gate shards only). Omitted = request 1 / limit 4. */
|
|
@@ -197,6 +200,9 @@ export function podCommandJobManifest(o: PodCommandJobInput): Record<string, unk
|
|
|
197
200
|
}
|
|
198
201
|
const grok = o.llm === 'grok';
|
|
199
202
|
const bunCache = o.bunCache ? podBunCacheVolume(o.bunCache) : undefined;
|
|
203
|
+
const installSlots = o.installSlots ? podInstallSlotsVolume(o.installSlots) : undefined;
|
|
204
|
+
// hostPath directories the uid-1000 child writes: a root init container hands them over (DirectoryOrCreate is root 0755).
|
|
205
|
+
const hostDirMounts = [...(bunCache ? [bunCache.volumeMount] : []), ...(installSlots ? [installSlots.volumeMount] : [])];
|
|
200
206
|
const cpu = resolvePodCpu(o.cpu);
|
|
201
207
|
const quotedArgs = o.command.map(bashSingleQuote).join(' ');
|
|
202
208
|
const credLines = [
|
|
@@ -233,7 +239,10 @@ export function podCommandJobManifest(o: PodCommandJobInput): Record<string, unk
|
|
|
233
239
|
spec: {
|
|
234
240
|
restartPolicy: 'Never',
|
|
235
241
|
securityContext: { runAsUser: 1000, fsGroup: 1000 },
|
|
236
|
-
initContainers: [
|
|
242
|
+
initContainers: [
|
|
243
|
+
{ name: 'isolation-gate', image: o.image, imagePullPolicy: o.imagePullPolicy ?? 'Never', command: ['bash', '-c'], args: [GATE] },
|
|
244
|
+
...(hostDirMounts.length ? [podHostDirsInit(o.image, o.imagePullPolicy ?? 'Never', hostDirMounts)] : []),
|
|
245
|
+
],
|
|
237
246
|
containers: [{
|
|
238
247
|
name: 'child', image: o.image, imagePullPolicy: o.imagePullPolicy ?? 'Never',
|
|
239
248
|
resources: { requests: { ...POD_CHILD_REQUESTS, cpu: cpu.request, memory: memoryRequestWithin(o.memoryLimit) }, limits: { memory: o.memoryLimit ?? '16Gi', cpu: cpu.limit } },
|
|
@@ -243,16 +252,17 @@ export function podCommandJobManifest(o: PodCommandJobInput): Record<string, unk
|
|
|
243
252
|
{ name: 'ELANOUS_SUBSTRATE', value: 'pod' },
|
|
244
253
|
{ name: 'ELANOUS_POD_NAME', valueFrom: { fieldRef: { fieldPath: 'metadata.name' } } },
|
|
245
254
|
],
|
|
246
|
-
...(secretKeys || o.hostMirror ||
|
|
255
|
+
...(secretKeys || o.hostMirror || hostDirMounts.length ? { volumeMounts: [
|
|
247
256
|
...(secretKeys ? [{ name: 'creds', mountPath: '/creds', readOnly: true }] : []),
|
|
248
257
|
...(o.hostMirror ? [{ name: 'host-mirror', mountPath: '/host-mirror', readOnly: true }] : []),
|
|
249
|
-
...
|
|
258
|
+
...hostDirMounts,
|
|
250
259
|
] } : {}),
|
|
251
260
|
}],
|
|
252
|
-
...(secretKeys || o.hostMirror ||
|
|
261
|
+
...(secretKeys || o.hostMirror || hostDirMounts.length ? { volumes: [
|
|
253
262
|
...(secretKeys ? [{ name: 'creds', secret: { secretName: `${o.name}-creds`, defaultMode: 0o400 } }] : []),
|
|
254
263
|
...(o.hostMirror ? [{ name: 'host-mirror', hostPath: { path: o.hostMirror, type: 'Directory' } }] : []),
|
|
255
264
|
...(bunCache ? [bunCache.volume] : []),
|
|
265
|
+
...(installSlots ? [installSlots.volume] : []),
|
|
256
266
|
] } : {}),
|
|
257
267
|
},
|
|
258
268
|
},
|
|
@@ -321,6 +331,8 @@ export interface RunPodCommandOptions {
|
|
|
321
331
|
clone?: boolean;
|
|
322
332
|
hostMirror?: string;
|
|
323
333
|
bunCache?: string;
|
|
334
|
+
/** GATE-INSTALL-CACHE — hostPath of the node-wide install tokens (absolute). */
|
|
335
|
+
installSlots?: string;
|
|
324
336
|
/** 컨테이너 메모리 한도(기본 16Gi · lite 면 2Gi). */
|
|
325
337
|
memoryLimit?: string;
|
|
326
338
|
/** GATE-SPEED A3①: CPU request/limit override — only the release gate passes it. Omitted = request 1 / limit 4. */
|
|
@@ -410,6 +422,8 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
410
422
|
if (hostMirror && !isAbsolute(hostMirror)) throw new Error('pod.hostMirror must be an absolute directory path');
|
|
411
423
|
const bunCache = options.bunCache?.trim() || undefined;
|
|
412
424
|
if (bunCache && !isAbsolute(bunCache)) throw new Error('pod.bunCache must be an absolute directory path');
|
|
425
|
+
const installSlots = options.installSlots?.trim() || undefined;
|
|
426
|
+
if (installSlots && !isAbsolute(installSlots)) throw new Error('pod installSlots must be an absolute directory path');
|
|
413
427
|
// Fail before any Secret/Job is applied — a bad quantity must not reach the cluster.
|
|
414
428
|
if (options.cpu) resolvePodCpu(options.cpu);
|
|
415
429
|
|
|
@@ -486,7 +500,7 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
486
500
|
name, namespace, launch, image: imageRef ?? image,
|
|
487
501
|
...(imageRef ? { imagePullPolicy: 'IfNotPresent' as const } : {}),
|
|
488
502
|
repoUrl, command, skills, ...(llm ? { llm } : {}), ...(options.clone ? { clone: true } : {}),
|
|
489
|
-
...(options.source ? { source: options.source } : {}), ...(hostMirror ? { hostMirror } : {}), ...(bunCache ? { bunCache } : {}), ...(memoryLimit ? { memoryLimit } : {}), ...(options.cpu ? { cpu: options.cpu } : {}), deadlineSeconds,
|
|
503
|
+
...(options.source ? { source: options.source } : {}), ...(hostMirror ? { hostMirror } : {}), ...(bunCache ? { bunCache } : {}), ...(installSlots ? { installSlots } : {}), ...(memoryLimit ? { memoryLimit } : {}), ...(options.cpu ? { cpu: options.cpu } : {}), deadlineSeconds,
|
|
490
504
|
...(options.runId ? { runId: options.runId } : {}),
|
|
491
505
|
});
|
|
492
506
|
kubectl(['-n', namespace, 'delete', 'job', '-l', own, '--ignore-not-found']);
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** STOP-RECORD stop_class values used by the card terminal classifier. */
|
|
2
|
+
export const STOP_CLASS_POD_FAILURE = 'pod-failure' as const;
|
|
3
|
+
export const STOP_CLASS_NO_LAUNCH = 'no-launch' as const;
|
|
4
|
+
|
|
1
5
|
/** Executor kinds of the dev child's `--json` result line (`src/index.ts` writeStdoutJson · DevExecutor.kind). */
|
|
2
6
|
const CHILD_RESULT_KINDS = new Set(['self', 'external']);
|
|
3
7
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// GATE-INSTALL-CACHE (0.2.20) — a node-wide throttle for the release-gate shards' `bun install`.
|
|
2
|
+
//
|
|
3
|
+
// Cause, measured on the 0.2.19 gate (`elanous logs --category release-loop.gate --event pod-shard --all --include-test`):
|
|
4
|
+
// · no cache is «not» the cause — the harness image carries a warm Bun cache, and a Pod that installed alone
|
|
5
|
+
// (10-07 10:48–10:55 · 12:13 · 12:16) took 2.4–5.0 s with `pod-bun-cache source=none`;
|
|
6
|
+
// · the same install took 150–340 s when 24 shard Pods (or ~20 isolation Pods) started together on the one 32-CPU
|
|
7
|
+
// node-b node (09:3x round 1: 221–337 s · 11:4x round 3: 150–240 s · 10:34 isolation wave: 124–188 s).
|
|
8
|
+
// ⇒ concurrent installs thrash the node (24 × copying node_modules out of the image cache + CPU), so a shared
|
|
9
|
+
// hostPath Bun cache would not help (the bytes are already local) — fewer installs «at once» does.
|
|
10
|
+
//
|
|
11
|
+
// Concurrency safety: the hostPath holds only throttle tokens (`slot-<n>` directories), never packages. Each Pod still
|
|
12
|
+
// installs into its own container (its own node_modules, its own image-local Bun cache), so correctness never depends on
|
|
13
|
+
// the token: a lost token (Pod killed mid-install) only lowers throughput until it goes stale (`staleSeconds`), a doubled
|
|
14
|
+
// token (two waiters reclaiming one stale slot in the same instant) only lets one extra install run, and a waiter that
|
|
15
|
+
// cannot get a token within `waitSeconds` — or finds the directory unwritable — installs anyway (fail-open).
|
|
16
|
+
// `mkdir` is atomic on one filesystem, and every Pod on a node sees the same hostPath directory.
|
|
17
|
+
|
|
18
|
+
/** Default hostPath of the install tokens on each gate node. */
|
|
19
|
+
export const POD_INSTALL_SLOTS_HOST_PATH = '/var/lib/elanous/gate-install-slots';
|
|
20
|
+
/** Default concurrent installs per node (`release.loop.gatePodInstallSlots` · 0 = off). Alone ≈ 3–5 s ⇒ 24 shards ≈ 6 waves. */
|
|
21
|
+
export const POD_INSTALL_SLOTS_DEFAULT = 4;
|
|
22
|
+
export const POD_INSTALL_SLOTS_MOUNT = '/gate-install-slots';
|
|
23
|
+
|
|
24
|
+
/** hostPath volume ⊕ mount for the tokens. `DirectoryOrCreate` makes a root-owned 0755 directory, so the Pod also needs
|
|
25
|
+
* {@link podHostDirsInit} (the child runs as uid 1000). */
|
|
26
|
+
export function podInstallSlotsVolume(hostPath: string = POD_INSTALL_SLOTS_HOST_PATH) {
|
|
27
|
+
return {
|
|
28
|
+
volume: { name: 'install-slots', hostPath: { path: hostPath, type: 'DirectoryOrCreate' } },
|
|
29
|
+
volumeMount: { name: 'install-slots', mountPath: POD_INSTALL_SLOTS_MOUNT, readOnly: false },
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A root init container that hands the mounted hostPath directories to the child's uid. Without it a `DirectoryOrCreate`
|
|
35
|
+
* hostPath stays root:root 0755 and the child's `[ -w … ]` guard silently skips it (the Bun cache option was a no-op).
|
|
36
|
+
*/
|
|
37
|
+
export function podHostDirsInit(image: string, imagePullPolicy: string, mounts: ReadonlyArray<{ name: string; mountPath: string; readOnly: boolean }>) {
|
|
38
|
+
const paths = mounts.map((mount) => `'${mount.mountPath.replace(/'/g, `'\\''`)}'`).join(' ');
|
|
39
|
+
return {
|
|
40
|
+
name: 'host-dirs', image, imagePullPolicy,
|
|
41
|
+
securityContext: { runAsUser: 0 },
|
|
42
|
+
command: ['sh', '-c'],
|
|
43
|
+
// Never fails the Pod: an unchanged directory only means the child falls back (no cache · no throttle).
|
|
44
|
+
args: [`for d in ${paths}; do mkdir -p "$d" && chown 1000:1000 "$d" && chmod 0775 "$d" || echo "[host-dirs] could not prepare $d"; done; exit 0`],
|
|
45
|
+
volumeMounts: mounts.map((mount) => ({ ...mount })),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const int = (value: number, label: string) => {
|
|
50
|
+
if (!Number.isSafeInteger(value) || value < 1) throw new Error(`invalid ${label}: ${value}`);
|
|
51
|
+
return value;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Bash that defines `install_slot_acquire` / `install_slot_release`. Acquire waits for one of `slots` tokens under `dir`
|
|
56
|
+
* (polling every `pollSeconds`, default 2 s), reclaims a token older than `staleSeconds` (minute granularity), and gives up after `waitSeconds` (install runs anyway).
|
|
57
|
+
* It prints `[gate] install-slot <n> waited <s>s` or `[gate] install-slot none …` for the shard log.
|
|
58
|
+
*/
|
|
59
|
+
export function installSlotScript(o: { dir?: string; slots: number; waitSeconds?: number; staleSeconds?: number; pollSeconds?: number }): string {
|
|
60
|
+
const dir = `'${(o.dir ?? POD_INSTALL_SLOTS_MOUNT).replace(/'/g, `'\\''`)}'`;
|
|
61
|
+
const slots = int(o.slots, 'install slots');
|
|
62
|
+
const wait = int(o.waitSeconds ?? 600, 'install slot wait');
|
|
63
|
+
const poll = o.pollSeconds ?? 2;
|
|
64
|
+
if (!Number.isFinite(poll) || poll <= 0 || poll > 60) throw new Error(`invalid install slot poll: ${poll}`);
|
|
65
|
+
const staleMinutes = Math.max(1, Math.ceil(int(o.staleSeconds ?? 900, 'install slot stale age') / 60));
|
|
66
|
+
return [
|
|
67
|
+
'install_slot=',
|
|
68
|
+
'install_slot_acquire() {',
|
|
69
|
+
` local d=${dir} t0 now n`,
|
|
70
|
+
' t0=$(date +%s)',
|
|
71
|
+
' if [ ! -d "$d" ] || [ ! -w "$d" ]; then echo "[gate] install-slot none (directory not writable)"; return 0; fi',
|
|
72
|
+
' while :; do',
|
|
73
|
+
` for n in $(seq 0 ${slots - 1}); do`,
|
|
74
|
+
' if mkdir "$d/slot-$n" 2>/dev/null; then',
|
|
75
|
+
' install_slot="$d/slot-$n"',
|
|
76
|
+
' echo "[gate] install-slot $n waited $(( $(date +%s) - t0 ))s"; return 0',
|
|
77
|
+
' fi',
|
|
78
|
+
// A token whose holder died (directory mtime older than the stale age · `find -mmin` is the same on GNU and BSD) is
|
|
79
|
+
// renamed away first (rename is atomic: one reclaimer wins) and the slot is tried again on the next pass.
|
|
80
|
+
` if [ -n "$(find "$d/slot-$n" -maxdepth 0 -mmin +${staleMinutes} 2>/dev/null)" ]; then mv "$d/slot-$n" "$d/stale-$n-$$" 2>/dev/null && rm -rf "$d/stale-$n-$$"; fi`,
|
|
81
|
+
' done',
|
|
82
|
+
' now=$(date +%s)',
|
|
83
|
+
` if [ $(( now - t0 )) -ge ${wait} ]; then echo "[gate] install-slot none (waited $(( now - t0 ))s)"; return 0; fi`,
|
|
84
|
+
` sleep ${poll}`,
|
|
85
|
+
' done',
|
|
86
|
+
'}',
|
|
87
|
+
'install_slot_release() { if [ -n "$install_slot" ]; then rm -rf "$install_slot"; install_slot=; fi; }',
|
|
88
|
+
].join('\n');
|
|
89
|
+
}
|
|
@@ -52,6 +52,18 @@ export interface PodLeaseMember {
|
|
|
52
52
|
*/
|
|
53
53
|
harnessUsageBytes?: number[] | null;
|
|
54
54
|
usageReason?: string | null;
|
|
55
|
+
/** GATE-RESERVE-AUTO — non-terminal release-gate shard Jobs (`gate-<uuid>`, kind=command). */
|
|
56
|
+
gateJobs?: number | null;
|
|
57
|
+
/** GATE-RESERVE-AUTO — of those, Jobs whose Pod is not bound to a node yet (not created, or Pending unscheduled). */
|
|
58
|
+
gateUnboundJobs?: number | null;
|
|
59
|
+
/** GATE-RESERVE-AUTO — CPU requested by the non-terminal gate Jobs' Pod templates (observability; Infinity: unreadable). */
|
|
60
|
+
gateCpuMillicores?: number | null;
|
|
61
|
+
/** GATE-RESERVE-AUTO — allocatable CPU of the schedulable (Ready, untainted, cordon-free) nodes. */
|
|
62
|
+
schedulableCpuMillicores?: number | null;
|
|
63
|
+
/** GATE-RESERVE-AUTO — per schedulable node: allocatable CPU − requests of every non-terminal Pod bound to it (gate Pods too). */
|
|
64
|
+
cpuFreeByNodeMillicores?: number[] | null;
|
|
65
|
+
/** GATE-RESERVE-AUTO — CPU requested by harness Pods still waiting for a node (they take CPU somewhere once bound). */
|
|
66
|
+
pendingHarnessCpuMillicores?: number | null;
|
|
55
67
|
/** Pool-wide harness reservation: Σ min(limit, max(request, usage×1.5)), or max(request, limit) when the Pod's usage is unmeasured. */
|
|
56
68
|
memoryReservedBytes?: number | null;
|
|
57
69
|
/** Parallel to availableMemoryByNodeBytes: each schedulable node's allocatable memory. */
|
|
@@ -138,16 +150,19 @@ function object(value: unknown): Record<string, unknown> | null {
|
|
|
138
150
|
return value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : null;
|
|
139
151
|
}
|
|
140
152
|
|
|
141
|
-
/**
|
|
142
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Effective scheduling reservation: max(sum of app requests, largest init request, Pod-level request) + overhead.
|
|
155
|
+
* A container without a request reserves its limit (Kubernetes defaults the request to the limit), or 0 with neither.
|
|
156
|
+
*/
|
|
157
|
+
function requestedResource(spec: Record<string, unknown>, unit: 'memory' | 'cpu'): number | null {
|
|
143
158
|
const request = (resources: unknown): number | null => {
|
|
144
159
|
const r = object(resources);
|
|
145
160
|
if (resources !== undefined && !r) return null;
|
|
146
161
|
const requests = object(r?.requests);
|
|
147
162
|
const limits = object(r?.limits);
|
|
148
163
|
if ((r?.requests !== undefined && !requests) || (r?.limits !== undefined && !limits)) return null;
|
|
149
|
-
const raw = requests?.
|
|
150
|
-
return raw === undefined ? 0 : typeof raw === 'string' ? quantity(raw,
|
|
164
|
+
const raw = requests?.[unit] ?? limits?.[unit];
|
|
165
|
+
return raw === undefined ? 0 : typeof raw === 'string' ? quantity(raw, unit) : null;
|
|
151
166
|
};
|
|
152
167
|
const containers = spec.containers;
|
|
153
168
|
if (!Array.isArray(containers) || !containers.length) return null;
|
|
@@ -167,12 +182,16 @@ function requestedMemory(spec: Record<string, unknown>): number | null {
|
|
|
167
182
|
init = Math.max(init, v);
|
|
168
183
|
}
|
|
169
184
|
const pod = spec.resources === undefined ? 0 : request(spec.resources);
|
|
170
|
-
const overheadRaw = object(spec.overhead)?.
|
|
185
|
+
const overheadRaw = object(spec.overhead)?.[unit];
|
|
171
186
|
const overhead = spec.overhead !== undefined && !object(spec.overhead) ? null
|
|
172
|
-
: overheadRaw === undefined ? 0 : typeof overheadRaw === 'string' ? quantity(overheadRaw,
|
|
187
|
+
: overheadRaw === undefined ? 0 : typeof overheadRaw === 'string' ? quantity(overheadRaw, unit) : null;
|
|
173
188
|
return pod === null || overhead === null ? null : Math.max(app, init, pod) + overhead;
|
|
174
189
|
}
|
|
175
190
|
|
|
191
|
+
const requestedMemory = (spec: Record<string, unknown>): number | null => requestedResource(spec, 'memory');
|
|
192
|
+
/** GATE-RESERVE-AUTO — the CPU the scheduler reserves for a Pod (same rule as memory). */
|
|
193
|
+
const requestedCpu = (spec: Record<string, unknown>): number | null => requestedResource(spec, 'cpu');
|
|
194
|
+
|
|
176
195
|
function runningLimit(spec: Record<string, unknown>): number | null {
|
|
177
196
|
if (!Array.isArray(spec.containers) || !spec.containers.length) return null;
|
|
178
197
|
let total = 0;
|
|
@@ -325,6 +344,8 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
325
344
|
};
|
|
326
345
|
const nodeItems = parsedItems(nodes);
|
|
327
346
|
const nodeFree = new Map<string, number>();
|
|
347
|
+
let schedulableCpu = 0;
|
|
348
|
+
const nodeCpuFree = new Map<string, number>();
|
|
328
349
|
const nodeNames = new Set<string>();
|
|
329
350
|
if (!nodeItems?.length) reasons.push(`cluster nodes: ${nodes.status === 0 ? 'invalid/empty response' : nodes.stderr.trim().split('\n').pop() || `rc=${nodes.status}`}`);
|
|
330
351
|
else {
|
|
@@ -350,11 +371,11 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
350
371
|
}
|
|
351
372
|
nodeNames.add(name);
|
|
352
373
|
const repels = taintList.some((t) => t!.effect === 'NoSchedule' || t!.effect === 'NoExecute');
|
|
353
|
-
if (spec?.unschedulable !== true && !repels && object(ready[0])?.status === 'True') { nodeFree.set(name, mem); nodeAllocatable.set(name, mem); }
|
|
374
|
+
if (spec?.unschedulable !== true && !repels && object(ready[0])?.status === 'True') { nodeFree.set(name, mem); nodeAllocatable.set(name, mem); nodeCpuFree.set(name, cores); schedulableCpu += cores; }
|
|
354
375
|
memory += mem; cpu += cores;
|
|
355
376
|
}
|
|
356
|
-
if (valid) { result.allocatableMemoryBytes = memory; result.allocatableCpuMillicores = cpu; }
|
|
357
|
-
else { nodeFree.clear(); nodeAllocatable.clear(); nodeNames.clear(); reasons.push('cluster nodes: invalid name/allocatable memory/cpu/readiness'); }
|
|
377
|
+
if (valid) { result.allocatableMemoryBytes = memory; result.allocatableCpuMillicores = cpu; result.schedulableCpuMillicores = schedulableCpu; }
|
|
378
|
+
else { nodeFree.clear(); nodeAllocatable.clear(); nodeCpuFree.clear(); nodeNames.clear(); reasons.push('cluster nodes: invalid name/allocatable memory/cpu/readiness'); }
|
|
358
379
|
}
|
|
359
380
|
// POD-ADMIT-BY-USAGE: every schedulable node needs a real-usage reading, or the member stays on limits.
|
|
360
381
|
const unreadNode = podUsage && nodeUsage ? [...nodeFree.keys()].find((n) => !nodeUsage!.has(n)) : undefined;
|
|
@@ -366,6 +387,8 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
366
387
|
if (podItems && jobItems) {
|
|
367
388
|
const jobNames = new Set<string>();
|
|
368
389
|
const leasedJobs = new Set<string>();
|
|
390
|
+
const gateJobs = new Map<string, number>();
|
|
391
|
+
const boundGateJobs = new Set<string>();
|
|
369
392
|
let jobsValid = true;
|
|
370
393
|
for (const job of jobItems) {
|
|
371
394
|
const meta = object(job.metadata);
|
|
@@ -373,12 +396,27 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
373
396
|
if (object(meta.labels)?.['elanous.substrate'] !== 'pod') continue;
|
|
374
397
|
if (typeof meta.name !== 'string' || !meta.name) { jobsValid = false; continue; }
|
|
375
398
|
jobNames.add(meta.name);
|
|
399
|
+
// GATE-RESERVE-AUTO: a release-gate shard Job (`gate-<uuid>`, kind=command) reserves its Pod template's CPU
|
|
400
|
+
// from creation until it is terminal — also while its Pod is still Pending or not created yet.
|
|
401
|
+
if (meta.name.startsWith('gate-') && object(meta.labels)?.['elanous.kind'] === 'command') {
|
|
402
|
+
const status = object(job.status);
|
|
403
|
+
const conditions = Array.isArray(status?.conditions) ? status.conditions as unknown[] : [];
|
|
404
|
+
const terminal = !!status?.completionTime || Number(status?.succeeded) > 0 ||
|
|
405
|
+
conditions.some((c) => (object(c)?.type === 'Failed' || object(c)?.type === 'Complete') && object(c)?.status === 'True');
|
|
406
|
+
if (!terminal) {
|
|
407
|
+
const spec = object(object(object(job.spec)?.template)?.spec);
|
|
408
|
+
// An unreadable request cannot be reserved as 0: it blocks harness admission until the gate ends.
|
|
409
|
+
gateJobs.set(meta.name, (spec ? requestedCpu(spec) : null) ?? Number.POSITIVE_INFINITY);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
376
412
|
if (object(meta.annotations)?.[POD_HOST_LEASE_ANNOTATION] === 'true') leasedJobs.add(meta.name);
|
|
377
413
|
}
|
|
378
414
|
if (!jobsValid) reasons.push('cluster jobs: missing name/labels');
|
|
379
415
|
let running = 0, pending = 0, unleasedRunning = 0, limits = 0, reserved = 0, limitsValid = true, reservationsValid = nodeItems !== null && nodeNames.size === nodeItems.length;
|
|
380
416
|
const pendingJobs = new Map<string, PendingJobIdentity>();
|
|
381
417
|
let unknownPhase = false;
|
|
418
|
+
let pendingHarnessCpu = 0;
|
|
419
|
+
let cpuValid = true;
|
|
382
420
|
const seenPods = new Set<string>();
|
|
383
421
|
for (const pod of podItems) {
|
|
384
422
|
const metadata = object(pod.metadata);
|
|
@@ -393,7 +431,9 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
393
431
|
if (seenPods.has(identity)) { reservationsValid = false; continue; }
|
|
394
432
|
seenPods.add(identity);
|
|
395
433
|
} else reservationsValid = false;
|
|
396
|
-
const
|
|
434
|
+
const ownerJob = String(labels?.['elanous.job'] ?? labels?.['job-name'] ?? '');
|
|
435
|
+
const gatePod = metadata?.namespace === 'elanous-test' && gateJobs.has(ownerJob);
|
|
436
|
+
const harness = metadata?.namespace === 'elanous-test' && labels?.['elanous.probe'] !== 'dns' && !gatePod &&
|
|
397
437
|
(labels?.['elanous.substrate'] === 'pod' || jobNames.has(String(labels?.['elanous.job'] ?? '')));
|
|
398
438
|
const spec = object(pod.spec);
|
|
399
439
|
let limit: number | null = null;
|
|
@@ -425,6 +465,17 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
425
465
|
}
|
|
426
466
|
// Pending Pods with no assigned node have no node reservation yet. They already occupy a pool slot.
|
|
427
467
|
const nodeName = spec?.nodeName;
|
|
468
|
+
// GATE-RESERVE-AUTO: CPU is placed per node (a free core split across two nodes is not one slot). Every Pod
|
|
469
|
+
// bound to a schedulable node — gate Pods too — takes its request there; harness Pods without a node yet
|
|
470
|
+
// take CPU somewhere once bound. A gate Pod bound to any node marks its Job as placed.
|
|
471
|
+
const boundNode = typeof nodeName === 'string' && nodeName !== '' ? nodeName : null;
|
|
472
|
+
if (gatePod && boundNode !== null && nodeNames.has(boundNode)) boundGateJobs.add(ownerJob);
|
|
473
|
+
if ((boundNode !== null && nodeCpuFree.has(boundNode)) || (boundNode === null && harness && phase === 'Pending')) {
|
|
474
|
+
const cpu = spec ? requestedCpu(spec) : null;
|
|
475
|
+
if (cpu === null) cpuValid = false;
|
|
476
|
+
else if (boundNode !== null) nodeCpuFree.set(boundNode, nodeCpuFree.get(boundNode)! - cpu);
|
|
477
|
+
else pendingHarnessCpu += cpu;
|
|
478
|
+
}
|
|
428
479
|
if ((nodeName === undefined || nodeName === '') && phase === 'Pending' && spec && typeof metadata?.namespace === 'string' &&
|
|
429
480
|
requestedMemory(spec) !== null) continue;
|
|
430
481
|
const requested = spec ? requestedMemory(spec) : null;
|
|
@@ -432,6 +483,15 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
432
483
|
if (nodeFree.has(nodeName)) nodeFree.set(nodeName, nodeFree.get(nodeName)! - Math.max(requested, reserve ?? limit ?? 0));
|
|
433
484
|
}
|
|
434
485
|
if (unknownPhase) reasons.push('cluster pods: missing/Unknown phase');
|
|
486
|
+
if (jobsValid && !unknownPhase && result.schedulableCpuMillicores !== undefined) {
|
|
487
|
+
result.gateJobs = gateJobs.size;
|
|
488
|
+
result.gateUnboundJobs = [...gateJobs.keys()].filter((job) => !boundGateJobs.has(job)).length;
|
|
489
|
+
result.gateCpuMillicores = [...gateJobs.values()].reduce((sum, cpu) => sum + cpu, 0);
|
|
490
|
+
}
|
|
491
|
+
if (cpuValid && !unknownPhase && result.schedulableCpuMillicores !== undefined) {
|
|
492
|
+
result.cpuFreeByNodeMillicores = [...nodeCpuFree.values()];
|
|
493
|
+
result.pendingHarnessCpuMillicores = pendingHarnessCpu;
|
|
494
|
+
}
|
|
435
495
|
if (jobsValid && !unknownPhase) { result.running = running; result.pending = pending; result.pendingJobs = [...pendingJobs.values()]; result.unleasedRunning = unleasedRunning; }
|
|
436
496
|
if (limitsValid && jobsValid && !unknownPhase) {
|
|
437
497
|
result.memoryLimitBytes = limits;
|
|
@@ -464,6 +524,29 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
464
524
|
}) };
|
|
465
525
|
}
|
|
466
526
|
|
|
527
|
+
/**
|
|
528
|
+
* GATE-RESERVE-AUTO — while a release-gate shard Job is not terminal, how many more harness Pods (each requesting
|
|
529
|
+
* `POD_CHILD_REQUESTS.cpu`) fit beside it. The gate gets its CPU first: while any gate shard is still waiting for a
|
|
530
|
+
* node, no harness Pod is admitted (the scheduler may place new harness Pods where the shard would have gone). Once
|
|
531
|
+
* every shard is bound its CPU sits on its node, and the answer is Σ per-node ⌊free ÷ request⌋ minus the harness Pods
|
|
532
|
+
* still waiting for a node. `undefined`: no gate Job is active (normal admission) · `null`: a gate is active but CPU
|
|
533
|
+
* could not be measured — a Pod's or a gate template's request is unreadable (admit nothing more). The count comes from this one reading, never from earlier ones.
|
|
534
|
+
*/
|
|
535
|
+
export function harnessCpuSlotsBesideGate(measure: PoolLeaseMeasure): number | null | undefined {
|
|
536
|
+
if (!measure.members.some((m) => (m.gateJobs ?? 0) > 0)) return undefined;
|
|
537
|
+
const perPod = quantity(POD_CHILD_REQUESTS.cpu, 'cpu')!;
|
|
538
|
+
let slots = 0;
|
|
539
|
+
for (const m of measure.members) {
|
|
540
|
+
if (m.gateJobs == null || m.gateUnboundJobs == null || m.cpuFreeByNodeMillicores == null || m.pendingHarnessCpuMillicores == null) return null;
|
|
541
|
+
// A gate Job whose Pod template CPU cannot be read is unmeasured even when its Pod is bound (fail closed).
|
|
542
|
+
if (m.gateCpuMillicores == null || !Number.isFinite(m.gateCpuMillicores)) return null;
|
|
543
|
+
if (m.gateUnboundJobs > 0) return 0;
|
|
544
|
+
const placeable = m.cpuFreeByNodeMillicores.reduce((sum, free) => sum + Math.max(0, Math.floor(free / perPod)), 0);
|
|
545
|
+
slots += Math.max(0, placeable - Math.ceil(m.pendingHarnessCpuMillicores / perPod));
|
|
546
|
+
}
|
|
547
|
+
return slots;
|
|
548
|
+
}
|
|
549
|
+
|
|
467
550
|
/** Per-node memory and per-member slots cannot be exchanged across nodes/clusters. */
|
|
468
551
|
export function recommendConcurrency(measure: PoolLeaseMeasure, options: { capacity: number; perGoalMemory?: string; liteGoalMemory?: string; accounts: number; perAccount: number }): PoolLeaseRecommendation {
|
|
469
552
|
const empty: PoolLeaseRecommendation = { recommended: null, limitedBy: null, reason: null, capacitySlots: null, memorySlots: null, liteMemorySlots: null, placeableSlots: null, accountSlots: null, running: null, pending: null, unleasedRunning: null };
|