elanous 0.2.13 → 0.2.15
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/LZcstaO9_NXypPFmujolv/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-33ffcc49ab45123b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9656-c1540088f5859c80.js → 1844-9ec4b1d12f6e80eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{390.c373e31b3b9d0c41.js → 1948.15f356b15a625597.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
- package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.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/3916-3d02dd1fc257dd3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4685-c3c1c625b71accd1.js → 4614-21403aac730d4586.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{8039-df92e08fede5de8f.js → 4795-f372490b178583fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1452.93700483b9d4fc61.js → 6110.1297d4093fdfbb0d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +1 -0
- package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1829-b453a1b22d3999bb.js → 7327-2d69fb321ba73389.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.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/8682-7061dd4fa7bd62ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +25 -0
- package/apps/pwa/out/_next/static/chunks/{8421-1c4acfb1395a0edf.js → 8949-ecb4128a0858b911.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9103-7a3ae7f60af3b99f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3869-3cd46526879f454c.js → 9247-29d889b809288e65.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{79f8be9e-957d68913b590b08.js → a9f2b54d-7865aba0f4d2ee49.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-0e2c57fe6add4b2a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e05a77df7e005b04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b8c49f6b4fa2df91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-976813c91d7394ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-72d3e7ff35a8c954.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-60c7a8e1a4fe62ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-2a52b316cb143d9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-c3ce483c8d5db370.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-13c7b53e0c2519a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-96f074f6538e8a2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-e97be5334d83a9f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-955ec2da3be4c560.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-c1343f9499d86f3f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-8739a2e0ae793130.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-d23b12bb92242558.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c7fea475488201a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e2ee2896811e98d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-5efbf81124c474ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f1a1fc0a82738c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-06ee266953d9c879.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-153c2b3bcc89e8de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.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-7d4182964407c09c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-0d6d23b40263a7ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-63d14a4d8fe1e1c1.js → page-f5597e894c02cd33.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-73735b64bfe1ae67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8fcec4c9ea2e7f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f7e631e9b4daf66e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9817c7f9007466cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5cd7e3a9-404fd6b8d0ecd44c.js → f6231284-45726550aba08fca.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f48170cc-d05d28bf87a43bf4.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-539cde99554be20f.js +1 -0
- package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.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 -0
- package/apps/pwa/out/ceo/index.txt +33 -0
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +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/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 -0
- package/apps/pwa/out/loops/index.txt +30 -0
- 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/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/orchestrator/orchestrator.yaml +59 -0
- package/graphs/orchestrator/recipes.yaml +18 -0
- package/graphs/release/recipes.yaml +6 -0
- package/graphs/release/release-loop.yaml +6 -1
- package/graphs/rhythm/daily.yaml +38 -0
- package/graphs/rhythm/recipes.yaml +18 -0
- package/graphs/rhythm/weekly.yaml +39 -0
- package/graphs/video/recipes.yaml +3 -0
- package/graphs/video/youtube-summary.yaml +17 -0
- package/package.json +1 -1
- package/scripts/audit-test-state-writes.ts +72 -17
- package/scripts/brand/check.ts +188 -0
- package/scripts/ci-typecheck-changed.ts +50 -18
- package/scripts/coord-post.sh +11 -1
- package/scripts/docs/generate-graph-anatomy.ts +315 -0
- package/scripts/docs-cli-check.ts +23 -6
- package/scripts/expose-rubric-check.ts +118 -0
- package/scripts/expose-rubric-suggest.ts +169 -0
- package/scripts/heal/run-role.ts +14 -0
- package/scripts/hq/standby-verify.ts +11 -7
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +112 -0
- package/scripts/marketing/generate-ax-brief.ts +140 -0
- package/scripts/marketing/generate-feature-map.ts +118 -0
- package/scripts/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/gate-node.ts +89 -14
- package/scripts/release-loop/publish-node.ts +14 -1
- package/scripts/release-loop/unattended-release.ts +23 -1
- package/scripts/release-loop/vault-note-node.ts +428 -0
- package/scripts/release-story/draft.ts +220 -0
- package/scripts/rhythm/daily.ts +570 -0
- package/scripts/rhythm/weekly.ts +315 -0
- package/scripts/skills/public-check.ts +169 -0
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/scripts/test-deterministic.ts +4 -8
- package/scripts/test-diet/effectiveness.ts +195 -0
- package/scripts/test-diet/lib.ts +16 -8
- package/scripts/test-diet/node.ts +77 -9
- package/scripts/ux/demo-rehearsal.ts +2 -2
- package/scripts/ux/lib/rehearsal-verdict.ts +1 -0
- package/scripts/video/youtube-summary.ts +133 -0
- package/skills/diagram-master/SKILL.md +42 -49
- package/skills/diagram-master/references/pyproject.toml +4 -1
- package/skills/diagram-master/references/routing-guide.md +4 -2
- package/skills/grill-me/SKILL.md +1 -0
- package/skills/youtube-master/SKILL.md +13 -13
- package/skills/youtube-master/package-lock.json +0 -545
- package/skills/youtube-master/package.json +2 -5
- package/skills/youtube-master/scripts/main.ts +2 -2
- package/skills/youtube-master/scripts/transcribe-local.ts +1 -1
- package/skills/youtube-master/src/util.ts +30 -7
- package/src/agent-env/profile.ts +161 -0
- package/src/agent-mission/driver.ts +3 -3
- package/src/agent-substrate/pr-reviewer.ts +2 -0
- package/src/ask-user-question/pending-questions.ts +5 -0
- package/src/auth/github-app-token.ts +112 -13
- package/src/autopilot/build/llm-conflict-merge.ts +73 -10
- package/src/autopilot/build/merge-intent.ts +53 -0
- package/src/briefing/brief-items.ts +121 -0
- package/src/chat/index.ts +2 -0
- package/src/claims/claims-ledger.ts +232 -0
- package/src/claims/claims-render.ts +166 -0
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/claims-cli.ts +94 -0
- package/src/cli/decisions-cli.ts +44 -3
- package/src/cli/docs-cli.ts +2 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/flow-cli.ts +11 -0
- package/src/cli/freeze-cli.ts +45 -0
- package/src/cli/intake-cli.ts +46 -17
- package/src/cli/lesson-cli.ts +108 -0
- package/src/cli/logs-cli.ts +104 -2
- package/src/cli/logs-model-input.ts +131 -0
- package/src/cli/pod-cli.ts +2 -0
- package/src/cli/pr-cli.ts +29 -1
- package/src/cli/private-ledger-commands.ts +54 -4
- package/src/cli/pty-drive-verdict.ts +11 -2
- package/src/cli/release-cli.ts +190 -33
- package/src/cli/rfc-status.ts +91 -0
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/connectors/linear.ts +5 -3
- package/src/context-bus/condense.ts +15 -20
- package/src/context-bus/context-now-public.ts +96 -0
- package/src/context-bus/context-now-surfaces.ts +55 -2
- package/src/context-bus/context-now.ts +6 -3
- package/src/context-bus/coord-events.ts +6 -4
- package/src/context-bus/long-term-memory.ts +92 -11
- package/src/dashboard/index.ts +75 -22
- package/src/dashboard/slash-runtime/chat-persona.ts +66 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +99 -0
- package/src/dashboard/slash-runtime/loops-table.ts +77 -0
- package/src/decisions/decision-cards.ts +33 -7
- package/src/decisions/decision-ledger.ts +40 -1
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/discord/sprint21-runtime.ts +16 -0
- package/src/discord-self-message.ts +8 -0
- package/src/discord-slash-wire.ts +31 -2
- package/src/domains/alert-audit.ts +271 -0
- package/src/domains/core-tools.ts +4 -3
- package/src/domains/ops-facts-tool.ts +31 -1
- package/src/domains/outbound-alert.ts +1 -1
- package/src/elanous-config-dir.ts +29 -4
- package/src/execution-loop/launch-gate.ts +29 -14
- package/src/flow/cards.ts +239 -0
- package/src/graph-runner/graph-tick.ts +4 -0
- package/src/graph-runner/runner.ts +30 -5
- package/src/harness/harness-cli-command.ts +259 -38
- package/src/harness/harness-incidents.ts +45 -12
- package/src/harness/harness-pod-dispatch.ts +102 -11
- package/src/harness/harness-queue-child.ts +3 -3
- package/src/harness/harness-queue.ts +583 -65
- package/src/harness/helper-repair.ts +87 -0
- package/src/harness/helper-scan.ts +151 -0
- package/src/harness/launch-stamp.ts +236 -0
- package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
- package/src/harness/ledger-write-guard.ts +35 -0
- package/src/harness/model-input-observation.ts +37 -0
- package/src/harness/pod-memory-policy.ts +15 -0
- package/src/harness/repository-name.ts +55 -0
- package/src/hq/fence-audit.ts +90 -0
- package/src/hq/hq.ts +63 -5
- package/src/hq/lease.ts +4 -4
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +332 -0
- package/src/index.ts +159 -27
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +37 -15
- package/src/intake-plane/front-classifier.ts +6 -0
- package/src/intake-plane/items.ts +3 -2
- package/src/intake-plane/lens.ts +177 -0
- package/src/intake-plane/linear-wish.ts +39 -11
- package/src/intake-plane/route.ts +145 -1
- package/src/intake-plane/wish-benchmark-ledger.ts +129 -0
- package/src/intake-plane/wish-card.ts +53 -2
- package/src/intake-plane/wish-folder.ts +5 -2
- package/src/intake-plane/wish-reply.ts +72 -0
- package/src/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
- package/src/launch-head/seat-goal-queue.ts +198 -0
- package/src/lessons/lesson-import.ts +106 -0
- package/src/lessons/lesson-ledger.ts +206 -0
- package/src/loops/activity.ts +203 -0
- package/src/loops/budget.ts +63 -0
- package/src/loops/checker.ts +5 -3
- package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
- package/src/loops/loop-cli.ts +36 -4
- package/src/loops/neighbors.ts +40 -0
- package/src/loops/observe.ts +10 -0
- package/src/loops/orchestrator/author-depth.ts +220 -0
- package/src/loops/orchestrator/author-input-check.ts +82 -0
- package/src/loops/orchestrator/author-ledger.ts +232 -0
- package/src/loops/orchestrator/finish-rate.ts +175 -0
- package/src/loops/orchestrator/overlap.ts +51 -12
- package/src/loops/orchestrator/resync-shadow.ts +227 -0
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +309 -0
- package/src/loops/orchestrator/traffic.ts +134 -13
- package/src/loops/registry.ts +535 -9
- package/src/loops/status-rows.ts +118 -0
- package/src/loops/verdict.ts +17 -5
- package/src/maturity/bot-slash-catalog.ts +66 -0
- package/src/maturity/feature-maturity.ts +9 -5
- package/src/maturity/tui-slash-availability.ts +31 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/dashboard.ts +6 -1
- package/src/nexus/api/http-server.ts +33 -3
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/merge-approvals-gate.ts +1 -1
- package/src/nexus/api/schedules-read.ts +18 -1
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/nexus/api/task-cards-api.ts +44 -5
- package/src/nexus/api/workflows.ts +2 -1
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +51 -0
- package/src/release-loop/checklist.ts +22 -4
- package/src/release-loop/feature-store.ts +71 -32
- package/src/release-loop/landing-freeze.ts +121 -0
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +302 -0
- package/src/release-loop/release-schedule.ts +3 -1
- package/src/seat-dispatch/ceo-commands.ts +2 -2
- package/src/seat-dispatch/seat-ask.ts +16 -3
- package/src/seat-dispatch/seat-questions.ts +58 -0
- package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
- package/src/seat-dispatch/seat-request-ledger.ts +133 -0
- package/src/seat-loop/seat-loop.ts +531 -40
- package/src/self-dev/dev-pipeline.ts +14 -6
- package/src/self-dev/draft-sweep.ts +162 -6
- package/src/self-dev/draft-triage-rules.ts +25 -8
- package/src/self-dev/orchestrate.ts +15 -1
- package/src/self-dev/review-provider-fallback.ts +6 -1
- package/src/self-dev/run-store.ts +73 -1
- package/src/self-dev/run-supervisor.ts +57 -2
- package/src/self-dev/self-orchestrate-runtime.ts +5 -1
- package/src/self-implement/budget-gate.ts +29 -0
- package/src/self-implement/expose-gate.ts +47 -0
- package/src/self-implement/frozen-merges.ts +295 -0
- package/src/self-implement/gate-cli.ts +10 -4
- package/src/self-implement/gate-scope.ts +46 -9
- package/src/self-implement/goal-archive.ts +222 -0
- package/src/self-implement/goal-author.ts +43 -5
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/goal-lookup.ts +147 -0
- package/src/self-implement/heal-intake.ts +197 -0
- package/src/self-implement/host-regate.ts +32 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +299 -48
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +97 -0
- package/src/self-implement/run-ledger.ts +3 -1
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +29 -9
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/session/index.ts +2 -0
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +4 -1
- package/src/task-cards/card-cli.ts +10 -2
- package/src/task-cards/card-store.ts +10 -6
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
- package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
- package/src/task-orchestrator/surfaces/pod-pool.ts +60 -8
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +224 -49
- package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
- package/src/telegram-commands.ts +27 -3
- package/src/telegram-loops-command.ts +44 -0
- package/src/telegram.ts +25 -4
- package/src/user-config.ts +146 -18
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/src/workflow-runtime/storage.ts +31 -13
- package/apps/pwa/out/_next/static/D51l8jUBplJrIpWvENsDK/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1394-931c9827067b2276.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3992-fe5c06d6b31f8b25.js +0 -25
- package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +0 -2
- package/apps/pwa/out/_next/static/chunks/5250-c78ca295585bbf95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6511-ff8360be3d65aaa4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8605-9da4678f8a310d18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9269.d2a4795bc6812717.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9906-e7b8b5310a169dfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-e5b4e1b256f41076.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e49b9b76c565a0b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-f14987d5850a31e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-4c1dbb21861beab2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-f6bc66b919b2ea7d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-60603c1e056c9b25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-a0ea8f5a2341ee6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-cb2eb0fd809980da.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-31012264da8a341f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-50309aeb0bdd554a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-0696ee3fac9fe56c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66c27608f651b866.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-6ddd2109b7fc3a80.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-7a2bf830038a2264.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1f327f18694fabca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e5cef2fe6e4028df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cf596128d2b420ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-f672536f14f0969f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-29a709616df9b050.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-fee842d7c2151129.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-78b40a344ed22f24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-a011dd5b0bff29c5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-d92e32d51ab5157e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-5f21aed6932ef375.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-1e1a53946804debf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-a1833cd43d832601.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-2a511d320f762e06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-1c3e03f88dac1e21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c4d5289c2ea97a89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b91f8952deaeaf27.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-71b43a2588489987.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +0 -1
- package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +0 -1
- /package/apps/pwa/out/_next/static/{D51l8jUBplJrIpWvENsDK → LZcstaO9_NXypPFmujolv}/_ssgManifest.js +0 -0
|
@@ -15,10 +15,10 @@ import { podSkillsDigest, readSkillEnvFiles, resolvePodSkills } from './pod-skil
|
|
|
15
15
|
import { podSourceScript, type PodSource } from './pod-source-receive.js';
|
|
16
16
|
import { GROUNDING_TOKEN_ENV, GROUNDING_URL_ENV, mintGroundingToken, revokeGroundingRun, type GroundingTokenScope } from '../../grounding/token.js';
|
|
17
17
|
import { POD_CREDENTIAL_GROK_PATH, POD_CREDENTIAL_GITHUB_PATH, POD_GITHUB_CREDENTIAL_TOKEN_ENV, POD_GITHUB_CREDENTIAL_URL_ENV, installationRepositories } from '../../nexus/api/pod-credential-api.js';
|
|
18
|
-
import {
|
|
18
|
+
import { coalescedInstallationCredential } from '../../auth/github-app-token.js';
|
|
19
19
|
import { POD_CREDENTIAL_TOKEN_ENV, POD_CREDENTIAL_URL_ENV } from '../../grok/credential.js';
|
|
20
20
|
import { collectPodLedgers, createPodLedgerFollower } from './pod-ledger-collect.js';
|
|
21
|
-
import { collectPodArtifacts } from './pod-artifact-return.js';
|
|
21
|
+
import { collectPodArtifacts, podNoResultDiagnostic } from './pod-artifact-return.js';
|
|
22
22
|
import { isValidAccountName, resolveCodexAccount } from '../../oauth/codex-account.js';
|
|
23
23
|
import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
24
24
|
import { controlInboxEnv } from '../../harness/control-inbox.js';
|
|
@@ -40,7 +40,7 @@ export const POD_JOB_DEADLINE_SECONDS = 10_800;
|
|
|
40
40
|
* 예약만 실사용에 맞춘다. */
|
|
41
41
|
export const POD_CHILD_REQUESTS = { cpu: '1', memory: '4Gi' } as const;
|
|
42
42
|
import type { PodPoolMember, PodPoolScheduler } from './pod-pool.js';
|
|
43
|
-
import { measurePoolLease, recommendConcurrency, POD_HOST_LEASE_ANNOTATION } from './pod-lease.js';
|
|
43
|
+
import { measurePoolLease, recommendConcurrency, POD_HOST_LEASE_ANNOTATION, LEASE_KUBECTL_MAX_BUFFER } from './pod-lease.js';
|
|
44
44
|
import { ACTUAL_SUBSTRATE_ENV, RUN_CONTRACT_ENV, carryRunContract, completionFloorFor } from '../../self-implement/graph-run-contract.js';
|
|
45
45
|
import { spawnSync } from 'node:child_process';
|
|
46
46
|
import { createHash } from 'node:crypto';
|
|
@@ -58,9 +58,11 @@ import { envLiteral } from '../../platform/env-literal.js';
|
|
|
58
58
|
import { LLM_TIER_MAP_BY_PROVIDER, lookupLlmTierSpec, type LlmTierProvider } from '../../model-tier/llm-tier-map.js';
|
|
59
59
|
import { parseSelfImplementJson, type SelfImplementJobDone, type SelfImplementJobSpawn } from './self-implement.js';
|
|
60
60
|
import { codexQuotaPolicyFromConfig } from '../../oauth/codex-account-store.js';
|
|
61
|
-
import { goalTypeOf } from '../../../scripts/measure-pod-memory-by-goal.js';
|
|
61
|
+
import { goalTypeOf, type GoalType } from '../../../scripts/measure-pod-memory-by-goal.js';
|
|
62
|
+
import { declaredGoalType, type GoalType as DeclaredGoalType } from '../../self-implement/goal-author.js';
|
|
62
63
|
import { readPodMemoryAdvice, type PodMemoryAdvice } from '../../cli/pod-memory-advice.js';
|
|
63
64
|
import { getUserConfig } from '../../user-config.js';
|
|
65
|
+
import { extractPodFailureReason } from './pod-failure-reason.js';
|
|
64
66
|
|
|
65
67
|
export type Kubectl = (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
|
|
66
68
|
|
|
@@ -83,6 +85,11 @@ export interface PodSpawnOptions {
|
|
|
83
85
|
rotationAccounts?: readonly string[];
|
|
84
86
|
/** 미지정이면 종전 Codex Job. */
|
|
85
87
|
provider?: 'openai-codex' | 'grok';
|
|
88
|
+
/** Child model/effort chosen at launch (`--child-llm-model/--child-llm-effort`) — carried into the Pod child. */
|
|
89
|
+
childModel?: string;
|
|
90
|
+
childEffort?: string;
|
|
91
|
+
/** True when the child provider was named at launch — the child gets explicit flags even for codex. */
|
|
92
|
+
childProviderExplicit?: boolean;
|
|
86
93
|
/** 🔐 API 키 과금 허용은 명시 true 뿐. */
|
|
87
94
|
grokApiKeyOptIn?: boolean;
|
|
88
95
|
namespace?: string;
|
|
@@ -108,7 +115,7 @@ export interface PodSpawnOptions {
|
|
|
108
115
|
pollMs?: number;
|
|
109
116
|
kubectl?: Kubectl;
|
|
110
117
|
/** Host-side regate (injected for Pod tests). */
|
|
111
|
-
hostRegate?: (input: { prNumber: number; headCommit: string; repoRoot: string }) => Promise<HostRegateResult>;
|
|
118
|
+
hostRegate?: (input: { prNumber: number; headCommit: string; repoRoot: string; goalFile?: string }) => Promise<HostRegateResult>;
|
|
112
119
|
/** PR comment for host-regate failures that never reached runHostRegate (injectable for tests). */
|
|
113
120
|
ghComment?: (prNumber: number, body: string) => void;
|
|
114
121
|
/** 🔑 Pod 필수 스킬의 키(.env)를 이 런의 Secret 으로 넘긴다 — 명시 opt-in(유료 크레딧을 쓴다). */
|
|
@@ -123,7 +130,8 @@ export interface PodSpawnOptions {
|
|
|
123
130
|
mintGrounding?: (claims: { runId: string; job: string; ttlMs: number; scope?: GroundingTokenScope; repository?: string }) => Promise<{ token: string; exp: number }>;
|
|
124
131
|
revokeGrounding?: (runId: string) => void;
|
|
125
132
|
/** Test seam for the host's repository-scoped GitHub App installation token. */
|
|
126
|
-
|
|
133
|
+
/** `fresh` = do not reuse a coalesced token (the exit-7 retry must not get the token that just failed back). */
|
|
134
|
+
githubInstallation?: (repository: string, opts?: { fresh?: boolean }) => { token: string; expires_at?: string; expiresAt?: string | number } | null;
|
|
127
135
|
/** GitHub installation repository lookup; required to prove the scoped token matches owner/name. */
|
|
128
136
|
githubRepositories?: (token: string) => Promise<readonly string[] | null>;
|
|
129
137
|
now?: () => number;
|
|
@@ -145,8 +153,11 @@ export function defaultKubectl(args: readonly string[], input?: string): { statu
|
|
|
145
153
|
// 셸 프록시가 kubectl 의 로컬 API 요청을 가로챈다(2026-09-25 실측) — 자식 env 에서 뺀다.
|
|
146
154
|
const env = { ...process.env };
|
|
147
155
|
for (const k of ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy']) delete env[k];
|
|
148
|
-
const r = spawnSync('kubectl', [...args], { encoding: 'utf8', input, env, timeout: 120_000 });
|
|
149
|
-
|
|
156
|
+
const r = spawnSync('kubectl', [...args], { encoding: 'utf8', input, env, timeout: 120_000, maxBuffer: LEASE_KUBECTL_MAX_BUFFER });
|
|
157
|
+
const error = (r.error as NodeJS.ErrnoException | undefined)?.code === 'ENOBUFS'
|
|
158
|
+
? `kubectl 출력 너무 큼 (ENOBUFS; maxBuffer=${LEASE_KUBECTL_MAX_BUFFER} bytes)`
|
|
159
|
+
: r.error ? String(r.error) : '';
|
|
160
|
+
return { status: r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + error };
|
|
150
161
|
}
|
|
151
162
|
|
|
152
163
|
/** Job·Secret 이름 — 런마다 다르게(병렬) · k8s 이름 규칙(소문자·숫자·하이픈 · 63자 이하). */
|
|
@@ -181,6 +192,17 @@ export function hostCredentials(account: string, storePath: string = authStorePa
|
|
|
181
192
|
|
|
182
193
|
/** A Pod gets a refresh-less access copy, so the copy must outlive the Job: same rule as the Codex path (3 h). */
|
|
183
194
|
export const POD_GROK_MIN_VALIDITY_MS = 3 * 3600_000;
|
|
195
|
+
/** PODCRED1 (10-05): an App installation token lives ~60 min and the host renews it in the Pod at the ten-minute
|
|
196
|
+
* boundary; below this remaining life at launch the token is observed (`github-app-short-lived`). */
|
|
197
|
+
export const POD_APP_TOKEN_MIN_START_MS = 20 * 60_000;
|
|
198
|
+
/** Exit 7 (in-Pod GitHub login failed) is retried once after this spread backoff — bursts were simultaneous launches. */
|
|
199
|
+
export const POD_GH_LOGIN_RETRY_BACKOFF_MS = 60_000;
|
|
200
|
+
/** Base backoff plus a 0-59 s spread derived from the job name, so jobs that failed together retry apart. */
|
|
201
|
+
export function podGhLoginRetryBackoffMs(job: string): number {
|
|
202
|
+
let spread = 0;
|
|
203
|
+
for (const ch of job) spread = (spread * 31 + ch.charCodeAt(0)) % 60;
|
|
204
|
+
return POD_GH_LOGIN_RETRY_BACKOFF_MS + spread * 1000;
|
|
205
|
+
}
|
|
184
206
|
|
|
185
207
|
/** resolveGrokCredential 과 동일한 출처를 읽되 Pod 로는 refresh 없는 access 사본만 보낸다.
|
|
186
208
|
* POD4 (10-01): the copy used to ship whatever the host had — an AUTH1 shard copied a token that had expired
|
|
@@ -220,6 +242,46 @@ export function hostGrokCredentials(opts: {
|
|
|
220
242
|
throw new Error('grok: 구독 자격 없음 · API 키 opt-in 꺼짐 또는 키 없음');
|
|
221
243
|
}
|
|
222
244
|
|
|
245
|
+
export type PodGrokSkipReason = 'missing' | 'expiring' | 'refresh-failed';
|
|
246
|
+
|
|
247
|
+
/** GROK-OPTIONAL (10-05) — may this launch plan put grok in the Pod's chain at all?
|
|
248
|
+
* The Pod copy must outlive the Job (POD_GROK_MIN_VALIDITY_MS), so an expiring subscription that the host cannot
|
|
249
|
+
* refresh is «not available» here: the plan drops grok and goes on with codex instead of failing every launch. */
|
|
250
|
+
export function podGrokSubscriptionUsable(opts: {
|
|
251
|
+
home?: string; env?: NodeJS.ProcessEnv;
|
|
252
|
+
refresh?: () => void; isExpiring?: () => boolean; expiresAt?: () => string | null;
|
|
253
|
+
log?: (category: string, event: string, data: Record<string, unknown>) => void;
|
|
254
|
+
} = {}): { usable: true } | { usable: false; reason: PodGrokSkipReason; expiresAt: string | null } {
|
|
255
|
+
const log = opts.log ?? ((category, event, data) => debug.log(category, event, data));
|
|
256
|
+
const expiresAt = () => {
|
|
257
|
+
if (opts.expiresAt) return opts.expiresAt();
|
|
258
|
+
try {
|
|
259
|
+
const scopes = JSON.parse(readFileSync(grokAuthFilePath(opts.home), 'utf8')) as Record<string, { expires_at?: unknown }>;
|
|
260
|
+
const all = Object.values(scopes).flatMap((s) => (s && typeof s.expires_at === 'string' ? [s.expires_at] : []));
|
|
261
|
+
return all.sort().at(-1) ?? null;
|
|
262
|
+
} catch { return null; }
|
|
263
|
+
};
|
|
264
|
+
const skip = (reason: PodGrokSkipReason) => {
|
|
265
|
+
const at = expiresAt();
|
|
266
|
+
try { log('pod.grok-credentials', 'skipped', { reason, expiresAt: at }); } catch { /* observation is fail-soft */ }
|
|
267
|
+
return { usable: false as const, reason, expiresAt: at };
|
|
268
|
+
};
|
|
269
|
+
const credential = resolveGrokCredential({ home: opts.home, env: opts.env });
|
|
270
|
+
if (credential?.kind !== 'subscription') return skip('missing');
|
|
271
|
+
const expiring = opts.isExpiring ?? (() => isGrokSubscriptionExpiring({ home: opts.home, bufferMs: POD_GROK_MIN_VALIDITY_MS }));
|
|
272
|
+
if (!expiring()) return { usable: true };
|
|
273
|
+
let refreshed = true;
|
|
274
|
+
try { (opts.refresh ?? (() => { refreshGrokSubscriptionToken({ home: opts.home }); }))(); } catch { refreshed = false; }
|
|
275
|
+
if (!expiring()) return { usable: true };
|
|
276
|
+
return skip(refreshed ? 'expiring' : 'refresh-failed');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** The one launch line for a skipped grok copy — the reader sees why grok left this run's chain. */
|
|
280
|
+
export function podGrokSkippedLine(skip: { reason: PodGrokSkipReason; expiresAt: string | null }): string {
|
|
281
|
+
const why = skip.reason === 'missing' ? '구독 자격 없음' : skip.reason === 'expiring' ? '만료 임박' : '만료 임박 · 호스트 갱신 실패';
|
|
282
|
+
return `⚠️ grok 자격 없음(${why}${skip.expiresAt ? ` · 만료 ${skip.expiresAt}` : ''}) — 이 런의 폴백 체인에서 grok 을 뺐다`;
|
|
283
|
+
}
|
|
284
|
+
|
|
223
285
|
/** 키 캐시 관례: `~/.cache/<env 이름 소문자>`(예: OPENROUTER_API_KEY → ~/.cache/openrouter_api_key · src/config.ts 와 같다). */
|
|
224
286
|
function defaultReadKeyCache(name: string): string | undefined {
|
|
225
287
|
try { const v = readFileSync(join(homedir(), '.cache', name.toLowerCase()), 'utf8').trim(); return v || undefined; } catch { return undefined; }
|
|
@@ -410,15 +472,29 @@ export function podRunLabels(o: { runId?: string; parentRunId?: string }): Recor
|
|
|
410
472
|
|
|
411
473
|
/** Pod 메모리 «등급» — 대표 09-27: PWA Pod 골이 16Gi 에서 OOM 으로 죽는다 ⇒ «high» 등급을 옵션으로 고른다.
|
|
412
474
|
* 등급 = `standard`(`ELANOUS_POD_MEMORY` 또는 16Gi) · `high`(`ELANOUS_POD_MEMORY_HIGH` 또는 32Gi · standard 보다 작아지지 않는다).
|
|
413
|
-
* 고르는 순서(앞이 이긴다): ① 발사 옵션 `--pod-memory <등급>`(→ `ELANOUS_POD_MEMORY_TIER`) ② 조각 문면
|
|
475
|
+
* 고르는 순서(앞이 이긴다): ① 발사 옵션 `--pod-memory <등급>`(→ `ELANOUS_POD_MEMORY_TIER`) ② 조각 문면 `Pod 메모리: lite|standard|high` ③ 조각의 `apps/pwa/` ④ 부모의 명시 메모리 줄·PWA 경로 ⑤ 실측 권고 ⑥ 권고 없는 research/document 는 lite ⑦ standard.
|
|
414
476
|
* node-b 노드 할당 가능 약 251Gi. */
|
|
415
477
|
export const POD_MEMORY_DEFAULT = '16Gi';
|
|
416
478
|
export const POD_MEMORY_HIGH_DEFAULT = '32Gi';
|
|
417
|
-
/** POD7 — `lite`(`ELANOUS_POD_MEMORY_LITE` 또는 2Gi):
|
|
479
|
+
/** POD7 — `lite`(`ELANOUS_POD_MEMORY_LITE` 또는 2Gi): research/document 기본값; OOM 시 standard 로 한 단계 승급한다. */
|
|
418
480
|
export const POD_MEMORY_LITE_DEFAULT = '2Gi';
|
|
481
|
+
|
|
482
|
+
/** Reserve from measured per-run peaks, never from the advisory limit tier. Sparse or missing measurements keep the old reservation. */
|
|
483
|
+
export function podMemoryRequestFor(kind: GoalType | null, advice?: PodMemoryAdvice): string {
|
|
484
|
+
const evidence = advice?.byGoalType.find((entry) => entry.goalType === kind)?.evidence;
|
|
485
|
+
const peak = evidence?.peakMiB.p95;
|
|
486
|
+
const reason = !kind ? 'unknown-kind' : !evidence ? 'no-advice'
|
|
487
|
+
: evidence.measured < 3 || evidence.runs < 3 || evidence.measured * 2 < evidence.runs ? 'insufficient-sample'
|
|
488
|
+
: peak === null || peak === undefined || !Number.isFinite(peak) || peak < 0 ? 'no-peak' : null;
|
|
489
|
+
if (reason) {
|
|
490
|
+
debug.log('pod.memory', 'request-default', { kind, reason });
|
|
491
|
+
return POD_CHILD_REQUESTS.memory;
|
|
492
|
+
}
|
|
493
|
+
return `${Math.max(1, Math.ceil(peak! * 1.25 / 1024))}Gi`;
|
|
494
|
+
}
|
|
419
495
|
export const POD_MEMORY_TIERS = ['lite', 'standard', 'high'] as const;
|
|
420
496
|
export type PodMemoryTier = (typeof POD_MEMORY_TIERS)[number];
|
|
421
|
-
export type PodMemorySource = 'option' | 'goal-line' | 'pwa-auto' | 'default' | 'parent-goal-line' | 'parent-pwa-auto' | 'advise';
|
|
497
|
+
export type PodMemorySource = 'option' | 'goal-line' | 'pwa-auto' | 'default' | 'parent-goal-line' | 'parent-pwa-auto' | 'advise' | 'goal-type-auto';
|
|
422
498
|
const PWA_PATH = /(?:^|[\s`'"(,·])apps\/pwa\//m;
|
|
423
499
|
const GOAL_LINE = /^[ \t]*(?:Pod 메모리|pod-memory)[ \t]*:[ \t]*(lite|standard|high)[ \t]*\r?$/im;
|
|
424
500
|
|
|
@@ -454,6 +530,10 @@ export function parsePodMemoryTier(v: string | undefined): PodMemoryTier | null
|
|
|
454
530
|
return t && (POD_MEMORY_TIERS as readonly string[]).includes(t) ? (t as PodMemoryTier) : null;
|
|
455
531
|
}
|
|
456
532
|
|
|
533
|
+
function parsePodGoalType(value: string | undefined): DeclaredGoalType | null {
|
|
534
|
+
return value === 'implement' || value === 'research' || value === 'document' || value === 'operate' ? value : null;
|
|
535
|
+
}
|
|
536
|
+
|
|
457
537
|
function memoryGi(v: string): number | null {
|
|
458
538
|
const m = /^(\d+(?:\.\d+)?)(Gi|Mi)$/.exec(v);
|
|
459
539
|
if (!m) return null;
|
|
@@ -464,9 +544,13 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
464
544
|
const option = parsePodMemoryTier(env.ELANOUS_POD_MEMORY_TIER);
|
|
465
545
|
const line = parsePodMemoryTier(GOAL_LINE.exec(feature)?.[1]);
|
|
466
546
|
const parentLine = !option && !line ? parsePodMemoryTier(GOAL_LINE.exec(parentGoal ?? '')?.[1]) : null;
|
|
547
|
+
const declared = parsePodGoalType(env.ELANOUS_POD_GOAL_TYPE)
|
|
548
|
+
?? declaredGoalType(feature) ?? declaredGoalType(parentGoal ?? '');
|
|
549
|
+
// 선언 «document» 는 실측 분류의 «docs» 다 — 낱말 판정(goalTypeOf)이 «document» 를 못 읽어 권고가 안 이어지던 구멍
|
|
467
550
|
const goalType = advice && !option && !line && !parentLine
|
|
468
551
|
? (broadPodTestWarning(feature) ? 'test' : goalTypeOf(feature))
|
|
469
552
|
?? (parentGoal ? (broadPodTestWarning(parentGoal) ? 'test' : goalTypeOf(parentGoal)) : null)
|
|
553
|
+
?? (declared === 'document' ? 'docs' : null)
|
|
470
554
|
: null;
|
|
471
555
|
const recommendation = advice?.byGoalType.find((entry) => entry.goalType === goalType && entry.evidence.runs > 0)?.recommended;
|
|
472
556
|
const [tier, source]: [PodMemoryTier, PodMemorySource] = option ? [option, 'option']
|
|
@@ -474,8 +558,11 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
474
558
|
// 조각 자신의 PWA 경로가 부모 줄·실측 권고보다 먼저다 — 부모 «standard» 가 PWA 조각을 16Gi 로 내리면 OOM(POD7 · #23569 순서 회귀)
|
|
475
559
|
: goalTouchesPwa(feature) ? ['high', 'pwa-auto']
|
|
476
560
|
: parentLine ? [parentLine, 'parent-goal-line']
|
|
477
|
-
: recommendation ? [recommendation, 'advise']
|
|
478
561
|
: parentGoal && goalTouchesPwa(parentGoal) ? ['high', 'parent-pwa-auto']
|
|
562
|
+
// 실측이 high(OOM 증거)를 권하면 선언 종류의 자동 lite 보다 먼저다 — 문서 골도 OOM 이 났으면 다시 2Gi 로 띄우지 않는다
|
|
563
|
+
: recommendation === 'high' ? ['high', 'advise']
|
|
564
|
+
: declared === 'research' || declared === 'document' ? ['lite', 'goal-type-auto']
|
|
565
|
+
: recommendation ? [recommendation, 'advise']
|
|
479
566
|
: ['standard', 'default'];
|
|
480
567
|
if (tier === 'lite') return { limit: env.ELANOUS_POD_MEMORY_LITE?.trim() || POD_MEMORY_LITE_DEFAULT, tier, source };
|
|
481
568
|
const base = env.ELANOUS_POD_MEMORY?.trim() || POD_MEMORY_DEFAULT;
|
|
@@ -485,7 +572,29 @@ export function podMemoryLimitFor(feature: string, env: NodeJS.ProcessEnv | Read
|
|
|
485
572
|
return { limit: a !== null && b !== null && a > b ? base : high, tier, source };
|
|
486
573
|
}
|
|
487
574
|
|
|
488
|
-
|
|
575
|
+
/** A named child provider decides the Pod provider (10-05 PODPROVIDER) — grok needs a usable credential, other names are refused. */
|
|
576
|
+
export function podNamedChildProvider(named: string | undefined, credential: { grokSubscription: boolean; grokApiKey: boolean }): { provider?: 'openai-codex' | 'grok'; refuse?: string } {
|
|
577
|
+
if (!named) return {};
|
|
578
|
+
if (named === 'openai-codex' || named === 'codex') return { provider: 'openai-codex' };
|
|
579
|
+
if (named === 'grok') return credential.grokSubscription || credential.grokApiKey ? { provider: 'grok' } : { refuse: 'pod: Pod 에 grok 자격 없음 — 구독 자격이 쓸 수 없거나(만료 임박·갱신 불가) API 키 과금 동의(harness.pod.grokApiKeyOptIn)가 없다 · codex 로 조용히 돌리지 않는다' };
|
|
580
|
+
return { refuse: `pod: Pod 자식 provider 는 openai-codex|grok 만 — 받음 ${named}` };
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/** Fallback credentials for the codex plan — a launch that named openai-codex never falls back to grok (post-review must-fix). */
|
|
584
|
+
export function podFallbackCredentials(named: 'openai-codex' | 'grok' | undefined, fallback: { grokSubscription: boolean; grokApiKey: boolean }): { grokSubscription: boolean; grokApiKey: boolean } {
|
|
585
|
+
return named === 'openai-codex' ? { grokSubscription: false, grokApiKey: false } : fallback;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/** Child LLM flags for the Pod child — grok always names its model; codex only when the launch named it (10-05 PODPROVIDER). */
|
|
589
|
+
export function podChildLlmArgs(options: Pick<PodSpawnOptions, 'provider' | 'childModel' | 'childEffort' | 'childProviderExplicit'>): string[] {
|
|
590
|
+
const provider = options.provider === 'grok' ? 'grok' : options.childProviderExplicit ? 'openai-codex' : undefined;
|
|
591
|
+
if (!provider) return [];
|
|
592
|
+
const model = options.childModel?.trim() || (provider === 'grok' ? defaultGrokModel().id : undefined);
|
|
593
|
+
// `self implement` registers no --child-llm-effort — passing it kills the Pod at argument parsing, so effort stays host-only.
|
|
594
|
+
return ['--child-llm-provider', provider, ...(model ? ['--child-llm-model', model] : [])];
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export function podJobManifest(o: { name: string; namespace: string; image: string; /** 레지스트리 이미지면 IfNotPresent(노드가 pull) · 반입 이미지면 Never. */ imagePullPolicy?: 'Never' | 'IfNotPresent'; repoUrl: string; source?: PodSource; hostMirror?: string; args: readonly string[]; passEnv: readonly string[]; deadlineSeconds: number; runId?: string; parentRunId?: string; armEnv?: Readonly<Record<string, string>>; hostId?: string; imageCommit?: string | null; skillEnvs?: readonly string[]; memoryLimit?: string; memoryRequest?: string; goalDoc?: string; grokCredential?: 'subscription' | 'api_key'; codexAccounts?: readonly string[]; appCredential?: boolean; /** Test seam — default POD_GH_STALE_SECONDS. */ githubStaleSeconds?: number; /** Which goal execution and attempt this Job is — a resumed host verifies it before following the Job (POD9). */ execution?: { key: string; attempt: number }; hostLeaseAdmitted?: boolean }): Record<string, unknown> {
|
|
489
598
|
const quoted = o.args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(' ');
|
|
490
599
|
const goalPath = o.goalDoc ? `'${(o.goalDoc.startsWith('-') ? `./${o.goalDoc}` : o.goalDoc).replace(/'/g, `'\\''`)}'` : undefined;
|
|
491
600
|
const askBaseIndex = o.goalDoc ? o.args.indexOf('--base') : -1;
|
|
@@ -598,8 +707,8 @@ if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
|
|
|
598
707
|
name: 'child', image: o.image, imagePullPolicy: o.imagePullPolicy ?? 'Never',
|
|
599
708
|
// 📏 09-25: 6Gi 는 빠듯했다 — 자식이 6,127Mi 에 붙어 OOMKilled(137) → 12Gi.
|
|
600
709
|
// 📏 09-27: 12Gi 에서도 OOMKilled 셋(구현 노드 도중) — 같은 골을 16Gi 로 다시 쏘니 끝까지 갔다(#20930) → 기본 16Gi.
|
|
601
|
-
//
|
|
602
|
-
resources: { requests: { ...POD_CHILD_REQUESTS }, limits: { memory: o.memoryLimit ?? POD_MEMORY_DEFAULT, cpu: '4' } },
|
|
710
|
+
// 요청은 실측 피크별로 고르고 상한은 독립적으로 유지한다. ELANOUS_POD_MEMORY 로 상한 조정.
|
|
711
|
+
resources: { requests: { ...POD_CHILD_REQUESTS, memory: o.memoryRequest ?? POD_CHILD_REQUESTS.memory }, limits: { memory: o.memoryLimit ?? POD_MEMORY_DEFAULT, cpu: '4' } },
|
|
603
712
|
command: ['bash', '-c'], args: [script],
|
|
604
713
|
env: [
|
|
605
714
|
...(o.runId ? [{ name: 'ELANOUS_RUN_ID', value: o.runId }] : []),
|
|
@@ -674,11 +783,12 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
674
783
|
let advice: PodMemoryAdvice | undefined;
|
|
675
784
|
const explicitMemory = Boolean(parsePodMemoryTier(env.ELANOUS_POD_MEMORY_TIER) || GOAL_LINE.test(input.feature) || GOAL_LINE.test(parentGoal ?? ''));
|
|
676
785
|
const useAdvice = options.adviseDefaults ?? getUserConfig().pod?.memory?.adviseDefaults === true;
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
786
|
+
try { advice = (options.memoryAdvice ?? readPodMemoryAdvice)(); }
|
|
787
|
+
catch (error) { debug.log('self-implement.pod', 'memory-advice-unavailable', { spaceId: input.spaceId, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
|
|
788
|
+
const { limit: selectedMemoryLimit, tier: memoryTier, source: memorySource } = podMemoryLimitFor(input.feature, env, parentGoal, useAdvice && !explicitMemory ? advice : undefined);
|
|
789
|
+
const requestKind = broadPodTestWarning(input.feature) ? 'test' : goalTypeOf(input.feature)
|
|
790
|
+
?? (parentGoal ? (broadPodTestWarning(parentGoal) ? 'test' : goalTypeOf(parentGoal)) : null)
|
|
791
|
+
?? ((parsePodGoalType(env.ELANOUS_POD_GOAL_TYPE) ?? declaredGoalType(input.feature) ?? declaredGoalType(parentGoal ?? '')) === 'document' ? 'docs' : null);
|
|
682
792
|
for (const [where, text] of [['feature', input.feature], ['parent-goal', parentGoal]] as const) {
|
|
683
793
|
if (text && text.split(/\r?\n/u).some((line) => /Pod 메모리/u.test(line) && !GOAL_LINE.test(line))) {
|
|
684
794
|
const reason = '한 줄 단독 `Pod 메모리: high|standard` 형식이 아님';
|
|
@@ -692,9 +802,14 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
692
802
|
const after = input.after ?? podPredecessorFor(input.feature, env, parentGoal);
|
|
693
803
|
if (after !== undefined) debug.log('self-implement.pod', 'predecessor', { spaceId: input.spaceId, after, source: input.after !== undefined ? 'input' : 'goal-or-option' });
|
|
694
804
|
let oomRetried = false;
|
|
805
|
+
let retryTier: PodMemoryTier = memoryTier === 'lite' ? 'standard' : 'high';
|
|
695
806
|
let retryFromChildRunId: string | undefined;
|
|
696
807
|
let retryFromMemoryLimit: string | undefined;
|
|
697
|
-
|
|
808
|
+
const selectedGoalType = parsePodGoalType(env.ELANOUS_POD_GOAL_TYPE) ?? declaredGoalType(input.feature) ?? declaredGoalType(parentGoal ?? '');
|
|
809
|
+
const memoryReason = memorySource === 'option' && ['explicit-option', 'goal-type-default', 'existing-tier'].includes(env.ELANOUS_POD_MEMORY_REASON ?? '')
|
|
810
|
+
? env.ELANOUS_POD_MEMORY_REASON!
|
|
811
|
+
: memorySource === 'goal-type-auto' ? `declared goal type ${selectedGoalType}` : memorySource;
|
|
812
|
+
debug.log('self-implement.pod', 'memory-limit', { spaceId: input.spaceId, goalType: requestKind, declaredGoalType: selectedGoalType, memoryLimit, tier: memoryTier, source: memorySource, reason: memoryReason,
|
|
698
813
|
...(memorySource.startsWith('parent-') ? { inheritedFrom } : {}), ...(parentGoalUnreadable ? { parentGoal: 'unreadable' } : {}) });
|
|
699
814
|
const childRunId = mintRunId();
|
|
700
815
|
const executionKey = createHash('sha256').update(JSON.stringify({ spaceId: input.spaceId, feature: input.feature, base: input.base, autoMerge: input.autoMerge, autoReview: input.autoReview, openPr: input.openPr, draft: input.draft })).digest('hex');
|
|
@@ -712,6 +827,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
712
827
|
if (typeof intent.data.to !== 'string' || memoryGi(intent.data.to) === null) throw new Error('invalid OOM retry memory limit');
|
|
713
828
|
oomRetried = true;
|
|
714
829
|
memoryLimit = intent.data.to;
|
|
830
|
+
retryTier = parsePodMemoryTier(typeof intent.data.tier === 'string' ? intent.data.tier : undefined)
|
|
831
|
+
?? ((memoryGi(intent.data.to) ?? 0) >= (memoryGi(podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'high' }).limit) ?? Infinity) ? 'high' : 'standard');
|
|
715
832
|
retryFromChildRunId = typeof intent.data.fromChildRunId === 'string' ? intent.data.fromChildRunId : undefined;
|
|
716
833
|
retryFromMemoryLimit = typeof intent.data.from === 'string' ? intent.data.from : undefined;
|
|
717
834
|
}
|
|
@@ -726,7 +843,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
726
843
|
...(input.draft === false ? ['--no-draft'] : []),
|
|
727
844
|
...(hostSupervised ? ['--no-supervise'] : []),
|
|
728
845
|
...(options.extraArgs ?? []),
|
|
729
|
-
...(options
|
|
846
|
+
...podChildLlmArgs(options),
|
|
730
847
|
];
|
|
731
848
|
debug.log('pod.self-implement', 'child-supervise', { hostSupervised, childSupervise: !hostSupervised });
|
|
732
849
|
const done = (async (): Promise<SelfImplementJobDone> => {
|
|
@@ -743,7 +860,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
743
860
|
if (!options.pool) return null;
|
|
744
861
|
for (;;) {
|
|
745
862
|
if (input.signal?.aborted) return null;
|
|
746
|
-
const acquired = options.pool.tryAcquire();
|
|
863
|
+
const acquired = await options.pool.tryAcquire();
|
|
747
864
|
if (acquired) {
|
|
748
865
|
debug.log('self-implement.pod', 'pool-slot', { spaceId: input.spaceId, context: acquired.context, inflight: options.pool.snapshot() });
|
|
749
866
|
return acquired;
|
|
@@ -811,6 +928,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
811
928
|
let retrySamples: ReturnType<typeof parseMemSamples> = [];
|
|
812
929
|
let failedAccount: string | undefined;
|
|
813
930
|
let usageLimitRetried = false;
|
|
931
|
+
let ghLoginRetried = false;
|
|
814
932
|
for (;;) {
|
|
815
933
|
try {
|
|
816
934
|
const grok = options.provider === 'grok';
|
|
@@ -873,10 +991,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
873
991
|
debug.log('self-implement.pod', 'credential-relay-skipped', { reason: 'no-host-url' });
|
|
874
992
|
}
|
|
875
993
|
const now = options.now ?? Date.now;
|
|
876
|
-
const issueVerifiedAppCredential = async (): Promise<{ token: string; expiresAt: number } | null> => {
|
|
994
|
+
const issueVerifiedAppCredential = async (fresh = false): Promise<{ token: string; expiresAt: number } | null> => {
|
|
877
995
|
if (!repository) return null;
|
|
878
996
|
// This is the same repository-scoped, contents+pull_requests installation request on launch and refresh.
|
|
879
|
-
const candidate = (options.githubInstallation ?? ((repo) =>
|
|
997
|
+
const candidate = (options.githubInstallation ?? ((repo, o) => coalescedInstallationCredential({ repository: repo.split('/')[1]! }, { fresh: o?.fresh })))(repository, { fresh });
|
|
880
998
|
if (!candidate?.token) return null;
|
|
881
999
|
const expiry = candidate.expires_at ?? ('expiresAt' in candidate ? candidate.expiresAt : undefined);
|
|
882
1000
|
const expiresAt = typeof expiry === 'number' ? expiry : Date.parse(expiry ?? '');
|
|
@@ -886,8 +1004,13 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
886
1004
|
? { token: candidate.token, expiresAt } : null;
|
|
887
1005
|
};
|
|
888
1006
|
let appCredential: Awaited<ReturnType<typeof issueVerifiedAppCredential>> = null;
|
|
889
|
-
try { appCredential = await issueVerifiedAppCredential(); }
|
|
1007
|
+
try { appCredential = await issueVerifiedAppCredential(ghLoginRetried); }
|
|
890
1008
|
catch { debug.log('self-implement.pod', 'github-app-unavailable', { job: name, reason: 'mint-or-verification-failed' }); }
|
|
1009
|
+
// PODCRED1: a short token is still shipped — the host renews it in the Pod on the first live poll — but it is
|
|
1010
|
+
// observed, so an exit-7 burst can be checked against the remaining life the Pods actually started with.
|
|
1011
|
+
if (appCredential && appCredential.expiresAt - now() < POD_APP_TOKEN_MIN_START_MS) {
|
|
1012
|
+
debug.log('self-implement.pod', 'github-app-short-lived', { job: name, expiresAt: new Date(appCredential.expiresAt).toISOString(), requiredSeconds: POD_APP_TOKEN_MIN_START_MS / 1000 });
|
|
1013
|
+
}
|
|
891
1014
|
const githubRelay = appCredential && groundingUrl && repository
|
|
892
1015
|
? await mint({ runId: groundingRunId, job: name, ttlMs: groundingTtlMs, scope: 'gh-credential', repository })
|
|
893
1016
|
: null;
|
|
@@ -985,7 +1108,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
985
1108
|
// 🩸 종전엔 여기서 delete 부터 해서, 재개가 원격에서 멀쩡히 돌던 Job 을 죽였다. 실패로 끝난 Job 만 지우고 다시 만든다.
|
|
986
1109
|
// ☸️ 노드 레지스트리에 이 판이 있으면 그것을 pull 한다(델타 · 대표 2026-09-26) — 라벨 판정은 로컬 이미지 이름으로 한다.
|
|
987
1110
|
const jobImage = member?.imageRef ?? image;
|
|
988
|
-
const launchRunId = retryAccount || oomRetried ? mintRunId() : childRunId;
|
|
1111
|
+
const launchRunId = retryAccount || oomRetried || ghLoginRetried ? mintRunId() : childRunId;
|
|
989
1112
|
let liveChildRunId = launchRunId;
|
|
990
1113
|
let reattachedMemoryLimit: string | undefined;
|
|
991
1114
|
let existingExecutionKey: string | undefined;
|
|
@@ -1026,7 +1149,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1026
1149
|
}
|
|
1027
1150
|
if (failedExisting) {
|
|
1028
1151
|
const pods = kubectl(['-n', namespace, 'get', 'pods', '-l', `job-name=${name}`, '-o', 'jsonpath={range .items[*]}{.metadata.creationTimestamp}{"\\t"}{range .status.containerStatuses[?(@.name=="child")]}{.state.terminated.reason}{"\\t"}{.state.terminated.exitCode}{end}{"\\n"}{end}']);
|
|
1029
|
-
const
|
|
1152
|
+
const latestPod = pods.status === 0 ? pods.stdout.trimEnd().split('\n').map((line) => line.split('\t'))
|
|
1153
|
+
.filter(([created]) => created && !Number.isNaN(Date.parse(created)))
|
|
1154
|
+
.sort((a, b) => Date.parse(b[0]!) - Date.parse(a[0]!))[0] : undefined;
|
|
1155
|
+
const failedOom = latestPod?.[1]?.trim() === 'OOMKilled' && latestPod[2]?.trim() === '137';
|
|
1030
1156
|
// Once attempt 2 exists, consume its terminal result even if it failed for a non-OOM reason.
|
|
1031
1157
|
// Only the old, lower-memory attempt may be removed to make room for high.
|
|
1032
1158
|
if (failedOom || oomRetried) reattach = !oomRetried || (memoryGi(reattachedMemoryLimit ?? '') ?? 0) >= (memoryGi(memoryLimit) ?? Infinity);
|
|
@@ -1039,21 +1165,22 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1039
1165
|
debug.log('self-implement.pod', 'job-reattach', { job: name, conditions: existingConditions.join(' ') || 'running', spaceId: input.spaceId });
|
|
1040
1166
|
}
|
|
1041
1167
|
if (!hostRefresh && ghTokenExpiresAt === null && !githubRelay) debug.log('self-implement.pod', 'gh-token-app-skipped', { job: name, reason: 'no-verified-app-or-refresh-path' });
|
|
1168
|
+
let appliedMemoryRequest: string | undefined;
|
|
1042
1169
|
if (!reattach) {
|
|
1043
1170
|
if (oomRetried && hasExistingJob && !failedExisting && memoryGi(reattachedMemoryLimit ?? '') === null) {
|
|
1044
1171
|
return { exitCode: 1, output: '', error: { code: 'pod-memory-unavailable', message: `Job ${name} 실제 메모리 한도를 확인할 수 없다` } };
|
|
1045
1172
|
}
|
|
1046
1173
|
if (oomRetried && !retryAccount) {
|
|
1047
|
-
const
|
|
1048
|
-
if ((memoryGi(memoryLimit) ?? 0) < (memoryGi(
|
|
1174
|
+
const retryLimit = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: retryTier }).limit;
|
|
1175
|
+
if ((memoryGi(memoryLimit) ?? 0) < (memoryGi(retryLimit) ?? Infinity)) throw new Error(`pod: OOM 재시도 한도가 ${retryTier} 와 다름: ${memoryLimit}`);
|
|
1049
1176
|
const leaseMembers = member ? [member] : [{ context, capacity: 1, k3dCluster: '' }];
|
|
1050
1177
|
const lease = recommendConcurrency(measurePoolLease(leaseMembers, { kubectl: (args) => baseKubectl(args) }), {
|
|
1051
|
-
capacity: leaseMembers.reduce((sum, item) => sum + item.capacity, 0), perGoalMemory:
|
|
1178
|
+
capacity: leaseMembers.reduce((sum, item) => sum + item.capacity, 0), perGoalMemory: retryLimit, accounts: 0, perAccount: 0,
|
|
1052
1179
|
});
|
|
1053
1180
|
if (lease.recommended === null || lease.placeableSlots === null || lease.placeableSlots < 1 || lease.memorySlots === null || lease.memorySlots < 1) {
|
|
1054
|
-
return { exitCode: 1, output: '', error: { code: 'pod-lease', message: `OOM 재시도
|
|
1181
|
+
return { exitCode: 1, output: '', error: { code: 'pod-lease', message: `OOM 재시도 ${retryTier} 자리 판정 실패: ${lease.reason ?? lease.limitedBy ?? 'no capacity'}\n${formatLastMemSample(retrySamples.at(-1))}` } };
|
|
1055
1182
|
}
|
|
1056
|
-
debug.log('self-implement.pod', 'oom-retry-lease', { job: name, context, memoryLimit:
|
|
1183
|
+
debug.log('self-implement.pod', 'oom-retry-lease', { job: name, context, memoryLimit: retryLimit, tier: retryTier, recommended: lease.recommended });
|
|
1057
1184
|
}
|
|
1058
1185
|
if (failedExisting && !reattach && !oomRetried && collectFullLogs('failed-job-logs-unavailable').status === 'incomplete') {
|
|
1059
1186
|
return { exitCode: 1, output: '', error: { code: 'pod-collection-incomplete', message: `Job ${name} recovery incomplete — old Job retained` } };
|
|
@@ -1074,17 +1201,30 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1074
1201
|
const detailUntil = detail && (detail.scope === 'all' || detail.scope === parentRunId || detail.scope === launchRunId) && detail.until > Date.now()
|
|
1075
1202
|
? String(detail.until) : undefined;
|
|
1076
1203
|
const armEnv = Object.fromEntries(Object.entries(options.armEnv ?? {}).filter(([key]) => key !== 'ELANOUS_LIVE_DETAIL_UNTIL' && key !== POD_GITHUB_CREDENTIAL_TOKEN_ENV && key !== POD_GITHUB_CREDENTIAL_URL_ENV && (!hostRefresh || !['GH_TOKEN', 'GITHUB_TOKEN', 'GH_CONFIG_DIR'].includes(key))));
|
|
1077
|
-
const
|
|
1204
|
+
const seat = env.ELANOUS_HARNESS_SEAT;
|
|
1205
|
+
const jobArmEnv = grounding || credentialRelay || githubRelay || env.ELANOUS_DISPATCH_RECORDED === '1' || options.armEnv || detailUntil || seat
|
|
1078
1206
|
? {
|
|
1079
1207
|
...armEnv,
|
|
1080
1208
|
...(detailUntil ? { ELANOUS_LIVE_DETAIL_UNTIL: detailUntil } : {}),
|
|
1081
1209
|
...(env.ELANOUS_DISPATCH_RECORDED === '1' ? { ELANOUS_DISPATCH_RECORDED: '1' } : {}),
|
|
1210
|
+
...(seat === 'OP' || seat === 'TC' || seat === 'MK' || seat === 'UX' ? { ELANOUS_HARNESS_SEAT: seat } : {}),
|
|
1082
1211
|
...(grounding ? { [GROUNDING_URL_ENV]: groundingUrl! } : {}),
|
|
1083
1212
|
...(credentialRelay ? { [POD_CREDENTIAL_URL_ENV]: `${new URL(groundingUrl!).origin}${POD_CREDENTIAL_GROK_PATH}` } : {}),
|
|
1084
1213
|
...(githubRelay ? { [POD_GITHUB_CREDENTIAL_URL_ENV]: `${new URL(groundingUrl!).origin}${POD_CREDENTIAL_GITHUB_PATH}` } : {}),
|
|
1085
1214
|
}
|
|
1086
1215
|
: undefined;
|
|
1087
|
-
const
|
|
1216
|
+
const measuredRequest = podMemoryRequestFor(requestKind, advice);
|
|
1217
|
+
const requestGi = memoryGi(measuredRequest);
|
|
1218
|
+
const limitGi = memoryGi(memoryLimit);
|
|
1219
|
+
const documentDefaultLite = !oomRetried && selectedGoalType === 'document' && memoryTier === 'lite'
|
|
1220
|
+
&& (memorySource === 'goal-type-auto' || memoryReason === 'goal-type-default');
|
|
1221
|
+
const memoryRequest = documentDefaultLite ? memoryLimit
|
|
1222
|
+
: requestGi !== null && limitGi !== null && requestGi > limitGi ? memoryLimit : measuredRequest;
|
|
1223
|
+
appliedMemoryRequest = memoryRequest;
|
|
1224
|
+
debug.log('self-implement.pod', 'memory-request', { spaceId: input.spaceId, job: name,
|
|
1225
|
+
tier: oomRetried ? retryTier : memoryTier, reason: oomRetried ? 'OOMKilled' : memoryReason,
|
|
1226
|
+
memoryLimit, memoryRequest });
|
|
1227
|
+
const job = podJobManifest({ name, namespace, image: jobImage, ...(member?.imageRef ? { imagePullPolicy: 'IfNotPresent' as const } : {}), repoUrl, ...(options.source ? { source: options.source } : {}), ...(hostMirror ? { hostMirror } : {}), args, passEnv: jobPassEnv, deadlineSeconds: options.deadlineSeconds ?? POD_JOB_DEADLINE_SECONDS, ...(goalDoc ? { goalDoc } : {}), runId: launchRunId, ...(parentRunId ? { parentRunId } : {}), ...(jobArmEnv ? { armEnv: jobArmEnv } : {}), hostId: resolveHostId(env), skillEnvs: Object.keys(skillEnvs), memoryLimit, memoryRequest, imageCommit: options.imageCommit !== undefined ? options.imageCommit : options.kubectl ? null : podImageFreshness({ image }).imageCommit, ...(grok ? { grokCredential: 'grokAuth' in creds && creds.grokAuth ? 'subscription' as const : 'api_key' as const } : {}), ...(codexAccounts ? { codexAccounts } : {}), ...(hostRefresh ? { appCredential: true } : {}), execution: { key: executionKey, attempt: oomRetried ? 2 : 1 }, hostLeaseAdmitted: !!releaseAdmission }); // kubectl 주입(=시험)이면 docker 를 부르지 않는다
|
|
1088
1228
|
const a = kubectl(['apply', '-f', '-'], JSON.stringify(job));
|
|
1089
1229
|
if (a.status !== 0) { cleanupSecret(); return { exitCode: 1, output: a.stderr, error: { code: 'pod-apply', message: a.stderr.trim() } }; }
|
|
1090
1230
|
releaseAdmission?.applied?.(name, context, namespace);
|
|
@@ -1120,7 +1260,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1120
1260
|
}
|
|
1121
1261
|
try {
|
|
1122
1262
|
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-child-run', data: { childRunId: liveChildRunId, job: name, attempt: oomRetried ? 2 : 1, ...(goalFile ? { goalFile } : {}) } }, ledgerDir);
|
|
1123
|
-
|
|
1263
|
+
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-memory-selected', data: { job: name, childRunId: liveChildRunId, attempt: oomRetried ? 2 : 1, limit: memoryLimit, ...(appliedMemoryRequest ? { request: appliedMemoryRequest } : {}), tier: oomRetried ? retryTier : memoryTier, source: oomRetried ? 'oom-retry' : memorySource, reason: oomRetried ? 'OOMKilled' : memoryReason, declaredGoalType: selectedGoalType } }, ledgerDir);
|
|
1264
|
+
if (oomRetried && retryFromChildRunId) appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-oom-retry', data: { attempt: 2, from: retryFromMemoryLimit ?? selectedMemoryLimit, to: memoryLimit, tier: retryTier, reason: 'OOMKilled', job: name, childRunId: liveChildRunId, fromChildRunId: retryFromChildRunId } }, ledgerDir);
|
|
1124
1265
|
} catch (error) {
|
|
1125
1266
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1126
1267
|
debug.log('self-implement.pod', 'parent-ledger-unavailable', { job: name, reason, attempt: oomRetried ? 2 : 1 });
|
|
@@ -1230,6 +1371,21 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1230
1371
|
const parsed = parseSelfImplementJson(logs);
|
|
1231
1372
|
const terminal = parseSelfImplementJson(logs.trimEnd().split('\n').at(-1) ?? '');
|
|
1232
1373
|
const nextAccount = codexAccounts?.find((candidate) => candidate !== account);
|
|
1374
|
+
if (state === 'failed' && containerExitCode === 7 && !input.signal?.aborted) {
|
|
1375
|
+
debug.log('self-implement.pod', 'gh-login-failed', { job: name, attempt: ghLoginRetried ? 2 : 1, tokenExpiresInSec: ghTokenExpiresAt === null ? null : Math.floor((ghTokenExpiresAt - now()) / 1000) });
|
|
1376
|
+
if (!ghLoginRetried) {
|
|
1377
|
+
const deleted = kubectl(['-n', namespace, 'delete', 'job', name, '--ignore-not-found', '--wait=true']);
|
|
1378
|
+
if (deleted.status !== 0) throw new Error(`pod: GitHub 로그인 실패 Job 삭제 실패: ${deleted.stderr.trim()}`);
|
|
1379
|
+
// Bursts came from simultaneous launches (10-05 12:49Z · 5 deaths in 2 s) — wait, spread by job name, then
|
|
1380
|
+
// retry once with a newly issued token so the retries do not collide again.
|
|
1381
|
+
const backoffMs = podGhLoginRetryBackoffMs(name);
|
|
1382
|
+
debug.log('self-implement.pod', 'gh-login-retry-backoff', { job: name, backoffMs });
|
|
1383
|
+
await sleep(backoffMs);
|
|
1384
|
+
if (input.signal?.aborted) return { exitCode: null, output: '', error: { code: 'aborted', message: 'aborted before GitHub login retry' } };
|
|
1385
|
+
ghLoginRetried = true;
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1233
1389
|
if (!usageLimitRetried && !input.signal?.aborted && state !== 'aborted' && terminal?.ok === false
|
|
1234
1390
|
&& /\b429\b/.test(terminal.error ?? '') && /\busage_limit_reached\b/.test(terminal.error ?? '') && nextAccount) {
|
|
1235
1391
|
debug.log('self-implement.pod', 'usage-limit-retry', { job: name, spaceId: input.spaceId, from: account, to: nextAccount });
|
|
@@ -1253,7 +1409,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1253
1409
|
try {
|
|
1254
1410
|
const hostRoot = findGitDir(process.cwd())?.root;
|
|
1255
1411
|
if (!hostRoot) throw new Error('host repository unavailable');
|
|
1256
|
-
regate = await (options.hostRegate ?? runHostRegate)({ prNumber, headCommit, repoRoot: hostRoot });
|
|
1412
|
+
regate = await (options.hostRegate ?? runHostRegate)({ prNumber, headCommit, repoRoot: hostRoot, ...(goalFile ? { goalFile } : {}) });
|
|
1257
1413
|
} catch (error) {
|
|
1258
1414
|
debug.log('harness.host-regate', 'unmeasured', { pr: prNumber, files: [], os: process.platform, failedStep: 'host-regate' });
|
|
1259
1415
|
regate = { passed: false, failures: [{ step: 'host-regate', detail: error instanceof Error ? error.message : String(error) }], os: process.platform };
|
|
@@ -1267,11 +1423,21 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1267
1423
|
catch (error) { debug.log('harness.host-regate', 'unmeasured', { pr: prNumber, files: [], os: process.platform, failedStep: 'comment', detail: error instanceof Error ? error.message : String(error) }); }
|
|
1268
1424
|
}
|
|
1269
1425
|
const releaseHold = regate.failures[0]?.step === 'release-path-hold';
|
|
1270
|
-
|
|
1426
|
+
const frozen = regate.status === 'frozen';
|
|
1427
|
+
disposition = { ...disposition, stage: frozen || releaseHold ? 'pr-opened' : regate.passed ? 'merged' : 'host-regate-failed', merged: regate.passed && !frozen, hostRegate: regate, ok: regate.passed || releaseHold || frozen };
|
|
1271
1428
|
}
|
|
1272
1429
|
const childFailure = state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded'
|
|
1273
1430
|
? lastPodChildFailure(logs) : null;
|
|
1431
|
+
let noResultDiagnostic: string | null = null;
|
|
1432
|
+
if (state === 'failed' && containerExitCode !== null && containerExitCode !== 0 && !disposition && !oomKilled && failedReason !== 'DeadlineExceeded' && failedReason !== 'BackoffLimitExceeded') {
|
|
1433
|
+
try { noResultDiagnostic = podNoResultDiagnostic(readFileSync(runLedgerPath(liveChildRunId, ledgerDir), 'utf8')); }
|
|
1434
|
+
catch { /* ledger not returned: do not invent a stage */ }
|
|
1435
|
+
noResultDiagnostic ??= 'child terminal result missing; last ledger stage=unknown; round=unknown; mustFix=unknown';
|
|
1436
|
+
}
|
|
1274
1437
|
if (childFailure) debug.log('self-implement.pod', 'child-error', { job: name, stage: childFailure.stage, error: childFailure.error }, { compact: { stringMax: 500 } });
|
|
1438
|
+
if (noResultDiagnostic && !noResultDiagnostic.includes('stage=unknown')) {
|
|
1439
|
+
debug.log('self-implement.pod', 'no-result-ledger-diagnostic', { job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic }, { compact: { stringMax: 500 } });
|
|
1440
|
+
}
|
|
1275
1441
|
debug.log('self-implement.pod', 'job-finished', { job: name, ...(member ? { context: member.context } : {}), state, containerReason, stage: disposition?.stage ?? null, prUrl: disposition?.prUrl ?? null, childRunId: liveChildRunId, ledgerCompleteness,
|
|
1276
1442
|
...(state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded'
|
|
1277
1443
|
? { ...(childFailure ? { childStage: childFailure.stage } : {}), childError: childFailure?.error ?? 'no-result-line' } : {}) },
|
|
@@ -1280,22 +1446,25 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1280
1446
|
return { exitCode: 1, output: '', error: { code: 'pod-memory-unavailable', message: `Job ${name} 실제 메모리 한도를 확인할 수 없다` } };
|
|
1281
1447
|
}
|
|
1282
1448
|
if (oomKilled && failedReason !== 'DeadlineExceeded' && !input.signal?.aborted && !oomRetried) {
|
|
1283
|
-
const high = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'high' }).limit;
|
|
1284
1449
|
const actual = memoryGi(reattachedMemoryLimit ?? memoryLimit);
|
|
1450
|
+
const standardLimit = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'standard' }).limit;
|
|
1451
|
+
const nextTier: PodMemoryTier = actual !== null && actual < (memoryGi(standardLimit) ?? 0) ? 'standard' : 'high';
|
|
1452
|
+
const nextLimit = podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: nextTier }).limit;
|
|
1285
1453
|
if (actual === null) {
|
|
1286
1454
|
return { exitCode: 1, output: '', error: { code: 'pod-memory-unavailable', message: `Job ${name} 실제 메모리 한도를 판정할 수 없다: ${memoryLimit}` } };
|
|
1287
1455
|
}
|
|
1288
|
-
if (actual < (memoryGi(
|
|
1456
|
+
if (actual < (memoryGi(nextLimit) ?? actual)) {
|
|
1289
1457
|
const from = reattachedMemoryLimit ?? memoryLimit;
|
|
1290
|
-
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-oom-retry-intent', data: { attempt: 2, from, to:
|
|
1291
|
-
debug.log('self-implement.pod', 'oom-retry', { from, to:
|
|
1458
|
+
appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-oom-retry-intent', data: { attempt: 2, from, to: nextLimit, tier: nextTier, reason: 'OOMKilled', job: name, executionKey, fromChildRunId: liveChildRunId } }, ledgerDir);
|
|
1459
|
+
debug.log('self-implement.pod', 'oom-retry', { from, to: nextLimit, tier: nextTier, reason: 'OOMKilled', job: name });
|
|
1292
1460
|
const deleted = kubectl(['-n', namespace, 'delete', 'job', name, '--ignore-not-found', '--wait=true']);
|
|
1293
1461
|
if (deleted.status !== 0) throw new Error(`pod: OOM Job 삭제 실패: ${deleted.stderr.trim()}`);
|
|
1294
1462
|
oomRetried = true;
|
|
1295
1463
|
retryFromChildRunId = liveChildRunId;
|
|
1296
1464
|
retryFromMemoryLimit = from;
|
|
1297
1465
|
retrySamples = samples;
|
|
1298
|
-
memoryLimit =
|
|
1466
|
+
memoryLimit = nextLimit;
|
|
1467
|
+
retryTier = nextTier;
|
|
1299
1468
|
if (member) { options.pool!.release(member); member = null; }
|
|
1300
1469
|
releaseAdmission?.();
|
|
1301
1470
|
releaseAdmission = undefined;
|
|
@@ -1314,7 +1483,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1314
1483
|
const record: GoalExecutionRecord = {
|
|
1315
1484
|
runId: liveChildRunId,
|
|
1316
1485
|
stage: (state === 'aborted' || !disposition ? 'aborted' : disposition.stage) as GoalExecutionRecord['stage'],
|
|
1317
|
-
outcome: (!disposition ? 'pod-no-result' : state === 'complete' && disposition.merged ? 'merged' : state === 'complete' && disposition.ok ? 'completed' : 'abandoned') as GoalExecutionRecord['outcome'],
|
|
1486
|
+
outcome: (!disposition ? noResultDiagnostic ? 'abandoned' : 'pod-no-result' : state === 'complete' && disposition.merged ? 'merged' : state === 'complete' && disposition.ok ? 'completed' : 'abandoned') as GoalExecutionRecord['outcome'],
|
|
1318
1487
|
ok: state === 'complete' && (disposition?.ok ?? false),
|
|
1319
1488
|
...(disposition?.prNumber !== undefined ? { prNumber: disposition.prNumber } : {}),
|
|
1320
1489
|
completedAt: new Date().toISOString(),
|
|
@@ -1338,16 +1507,22 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
|
|
|
1338
1507
|
const finishedDisposition = deadlineExceeded && disposition
|
|
1339
1508
|
? { ...disposition, failureClassification: 'run-deadline-exceeded' as const }
|
|
1340
1509
|
: disposition;
|
|
1510
|
+
const podExitCode = state === 'complete' && disposition?.stage !== 'host-regate-failed' && disposition?.ok !== false ? 0 : 1;
|
|
1511
|
+
// SCHED-CONTRACT: a failed child must say why outside the Pod, not «no error diagnostic».
|
|
1512
|
+
const podReason = podExitCode === 1 ? extractPodFailureReason({ logs, logTailReason, containerReason, jobReason: failedReason || undefined, deadlineSeconds }) : undefined;
|
|
1513
|
+
if (podReason) debug.log('self-implement.pod', 'failure-reason', { job: name, reason: podReason });
|
|
1341
1514
|
return {
|
|
1342
|
-
exitCode:
|
|
1515
|
+
exitCode: podExitCode,
|
|
1343
1516
|
output: tail,
|
|
1344
1517
|
...(deadlineExceeded
|
|
1345
1518
|
? { error: { code: 'pod-deadline-exceeded', message: `Job ${name} 이 수명 상한 ${deadlineSeconds}초에 닿았다` } }
|
|
1346
1519
|
: state === 'failed'
|
|
1347
|
-
?
|
|
1348
|
-
? { error: { code: 'pod-
|
|
1349
|
-
|
|
1350
|
-
|
|
1520
|
+
? containerExitCode === 7
|
|
1521
|
+
? { error: { code: 'pod-gh-login-failed', message: `Job ${name} GitHub 앱 로그인 실패 (container=${containerReason ?? 'unknown'}/7, attempt=${ghLoginRetried ? 2 : 1}) — 새 토큰으로 ${ghLoginRetried ? '1회 재시도했으나 다시 실패' : '재시도 불가'}` } }
|
|
1522
|
+
: oomKilled
|
|
1523
|
+
? { error: { code: 'pod-oom-killed', message: `Job ${name} failed (OOMKilled/${containerExitCode ?? 'unknown'}, memoryLimit=${memoryLimit})${oomRetried || (memoryGi(reattachedMemoryLimit ?? memoryLimit) ?? 0) >= (memoryGi(podMemoryLimitFor(input.feature, { ...env, ELANOUS_POD_MEMORY_TIER: 'high' }).limit) ?? Infinity) ? ` — OOM · ${oomRetried ? retryTier : 'high'} 에서도` : ''}\n${formatLastMemSample(samples.at(-1))}` } }
|
|
1524
|
+
: { error: { code: 'pod-job-failed', message: `Job ${name} failed${failedReason || containerReason ? ` (${[failedReason, containerReason ? `container=${containerReason}/${containerExitCode ?? 'unknown'}` : ''].filter(Boolean).join(', ')})` : ''}${childFailure ? ` — childStage=${childFailure.stage} · childError=${childFailure.error}` : ` — childError=no-result-line · reason=${podReason}`}${noResultDiagnostic ? ` · ${noResultDiagnostic}` : ''}` } }
|
|
1525
|
+
: podReason && !childFailure?.error ? { error: { code: 'pod-child-failed', message: podReason } } : {}),
|
|
1351
1526
|
...(finishedDisposition ? { disposition: finishedDisposition } : {}),
|
|
1352
1527
|
};
|
|
1353
1528
|
} catch (err) {
|