elanous 0.2.13 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-6e7e0377c1db0bdc.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/4562-071d8bac44a01069.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-2df489622a52a33a.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/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-74383f890f075511.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-7b2b4f378354869a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-f14987d5850a31e0.js → page-1abfe3aec3be6bdc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-1512d91c7491ed62.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-a627db94ec6904a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-e6b84ab07d3567d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-fed69f9759d248d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-15374909d8f1aebd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-012a742f81218b01.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-fd7e200d4d83fbd7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-99effd9d893a43dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-938de57ae0315e46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-160089123a40dd91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-629e1ede482ec6eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-66c27608f651b866.js → page-2ce5471926761fdd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-27e99b4e54a849d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-32cde713be47cddb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e7e10cd3386222e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-e64caff084f7ce21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-db07eaffba0df7e3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-78b40a344ed22f24.js → page-e1e47d8d940aba3d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-86f91034bd5cabe5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.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-b3759cc14da80908.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-ce4d8a1215ce53a3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-58043db78ba519ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-357750dfb91bf960.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-63d14a4d8fe1e1c1.js → page-274703bc41b71333.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-3b7e821dc492bdfd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-f8da5dc6f3752276.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2f810b18d89ecba6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.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/2e33713c444d5b39.css +1 -0
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +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/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/orchestrator/orchestrator.yaml +59 -0
- package/graphs/orchestrator/recipes.yaml +18 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -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 +186 -0
- package/scripts/coord-post.sh +11 -1
- package/scripts/docs-cli-check.ts +23 -6
- package/scripts/expose-rubric-check.ts +118 -0
- package/scripts/expose-rubric-suggest.ts +163 -0
- package/scripts/heal/run-role.ts +14 -0
- package/scripts/hq/standby-verify.ts +11 -7
- package/scripts/lessons/handbook-page.ts +85 -0
- package/scripts/release-loop/gate-node.ts +9 -0
- 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 +196 -0
- package/scripts/release-story/draft.ts +172 -0
- package/scripts/rhythm/daily.ts +383 -0
- package/scripts/rhythm/weekly.ts +294 -0
- package/scripts/skills/public-check.ts +169 -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-mission/driver.ts +3 -3
- package/src/autopilot/build/llm-conflict-merge.ts +45 -8
- package/src/autopilot/build/merge-intent.ts +53 -0
- package/src/chat/index.ts +2 -0
- package/src/claims/claims-ledger.ts +232 -0
- package/src/claims/claims-render.ts +157 -0
- package/src/cli/claims-cli.ts +94 -0
- package/src/cli/decisions-cli.ts +35 -3
- package/src/cli/docs-cli.ts +2 -0
- package/src/cli/flow-cli.ts +11 -0
- package/src/cli/freeze-cli.ts +45 -0
- 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/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 +144 -21
- package/src/cli/rfc-status.ts +91 -0
- package/src/connectors/linear.ts +5 -3
- package/src/context-bus/condense.ts +15 -20
- package/src/context-bus/context-now-public.ts +86 -0
- package/src/context-bus/context-now-surfaces.ts +55 -2
- package/src/context-bus/context-now.ts +4 -3
- 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-ledger.ts +40 -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/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 +152 -27
- package/src/harness/harness-incidents.ts +43 -12
- package/src/harness/harness-pod-dispatch.ts +2 -0
- package/src/harness/harness-queue-child.ts +3 -3
- package/src/harness/harness-queue.ts +216 -28
- package/src/harness/helper-scan.ts +139 -0
- package/src/harness/model-input-observation.ts +37 -0
- package/src/hq/hq.ts +1 -1
- package/src/hq/promote.ts +301 -0
- package/src/index.ts +83 -21
- package/src/intake-plane/linear-wish.ts +39 -11
- 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/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 +32 -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 +164 -0
- package/src/loops/orchestrator/author-input-check.ts +77 -0
- package/src/loops/orchestrator/author-ledger.ts +230 -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/tick.ts +302 -0
- package/src/loops/orchestrator/traffic.ts +18 -3
- package/src/loops/registry.ts +454 -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 +7 -4
- package/src/nexus/api/dashboard.ts +6 -1
- package/src/nexus/api/http-server.ts +18 -2
- package/src/nexus/api/merge-approvals-gate.ts +1 -1
- package/src/nexus/api/schedules-read.ts +5 -1
- package/src/nexus/api/task-cards-api.ts +44 -5
- package/src/nexus/api/workflows.ts +2 -1
- package/src/release-loop/auto-start.ts +48 -0
- package/src/release-loop/checklist.ts +13 -4
- package/src/release-loop/feature-store.ts +28 -16
- package/src/release-loop/landing-freeze.ts +121 -0
- package/src/release-loop/placement.ts +242 -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-loop/seat-loop.ts +406 -31
- package/src/self-dev/dev-pipeline.ts +6 -2
- package/src/self-dev/draft-sweep.ts +76 -0
- package/src/self-dev/run-store.ts +73 -1
- package/src/self-dev/run-supervisor.ts +8 -0
- 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 +294 -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-lookup.ts +147 -0
- package/src/self-implement/heal-intake.ts +79 -0
- package/src/self-implement/host-regate.ts +25 -2
- package/src/self-implement/orchestrator.ts +114 -10
- package/src/self-implement/revert-guard.ts +62 -0
- package/src/self-implement/run-ledger.ts +1 -1
- package/src/self-implement/seams.ts +28 -8
- package/src/session/index.ts +2 -0
- package/src/task-cards/card-cli.ts +10 -2
- package/src/task-cards/card-store.ts +10 -6
- 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/self-implement-pod.ts +80 -16
- package/src/telegram-commands.ts +27 -3
- package/src/telegram-loops-command.ts +44 -0
- package/src/user-config.ts +120 -16
- package/src/version/packed-revision.json +1 -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/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/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/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 → x6mjI6vqOkVVu0jFuDkz1}/_ssgManifest.js +0 -0
|
@@ -2,14 +2,16 @@ import { execFileSync } from 'node:child_process';
|
|
|
2
2
|
import { dlopen, FFIType } from 'bun:ffi';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { existsSync, readFileSync } from 'node:fs';
|
|
5
|
-
import { constants as osConstants } from 'node:os';
|
|
6
|
-
import { resolve, sep } from 'node:path';
|
|
5
|
+
import { constants as osConstants, hostname } from 'node:os';
|
|
6
|
+
import { basename, dirname, resolve, sep } from 'node:path';
|
|
7
7
|
import { findGitDir } from '../git-fs/locate.js';
|
|
8
8
|
import { LogStore, logsDbPath } from '../mss/logging/log-store.js';
|
|
9
9
|
import { loadSelfDevRun } from '../self-dev/run-store.js';
|
|
10
10
|
import { normalizeRunId } from './harness-space.js';
|
|
11
|
-
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
12
|
-
import {
|
|
11
|
+
import { effectiveInstanceRoot, prodInstanceRoot } from '../instance/resolve.js';
|
|
12
|
+
import { lookupGoal, type GoalLookupOptions, type GoalLookupResult } from '../self-implement/goal-lookup.js';
|
|
13
|
+
import { archiveGoals, type GoalArchiveOptions } from '../self-implement/goal-archive.js';
|
|
14
|
+
import { detectBursts, formatIncidentBurstWarning, incidentLastLines, readRunExits, recentBurst, recordRunExit } from './harness-incidents.js';
|
|
13
15
|
import { Command, Option } from 'commander';
|
|
14
16
|
import { runGitCommand } from '../git-fs/runner.js';
|
|
15
17
|
import { GOAL_TYPES, lintGoalFile, parseGoalId, parseGoalType, tracedPathReferences, type GoalType } from '../self-implement/goal-author.js';
|
|
@@ -29,7 +31,7 @@ import { DEFAULT_FALLBACK_CHAIN } from '../oauth/fallback-chain.js';
|
|
|
29
31
|
import { installDeliverableVerifyCliCommand, type InstallDeliverableVerifyCliDeps } from './deliverable-verify-cli.js';
|
|
30
32
|
import { getUserConfig } from '../user-config.js';
|
|
31
33
|
import { installHarnessCliSinkHook } from './harness-cli-sink.js';
|
|
32
|
-
import { addHarnessQueue, listHarnessQueue, reconcileHarnessQueue, removeHarnessQueue, tickHarnessQueue, type HarnessQueueDeps } from './harness-queue.js';
|
|
34
|
+
import { addHarnessQueue, listHarnessQueue, queueSeatForCwd, reconcileHarnessQueue, removeHarnessQueue, tickHarnessQueue, type HarnessQueueDeps, type QueueSeat } from './harness-queue.js';
|
|
33
35
|
import { resolveHarnessSubstrate, type ResolvedHarnessSubstrate } from './harness-substrate-default.js';
|
|
34
36
|
import { runHarnessPlanRfc } from './harness-plan-rfc.js';
|
|
35
37
|
import { classifyGarbage, isGarbageProcessTarget, type GarbageProcess } from './process-garbage.js';
|
|
@@ -92,6 +94,7 @@ export class HarnessCliInputError extends Error {
|
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
export interface HarnessAskSayOptions {
|
|
97
|
+
seat?: QueueSeat;
|
|
95
98
|
json?: boolean;
|
|
96
99
|
base?: string;
|
|
97
100
|
target?: string;
|
|
@@ -253,6 +256,7 @@ function registerHarnessAskSayOptions(command: Command): Command {
|
|
|
253
256
|
.option('--target <path>', 'self: harness가 작업할 레포 또는 디렉터리(self-mission 전용)')
|
|
254
257
|
.option('--yes', 'git 아닌 프로젝트에 git init 및 첫 커밋을 만들도록 동의')
|
|
255
258
|
.option('--correlation <id>', '요청과 런을 잇는 불투명 correlation 값')
|
|
259
|
+
.addOption(new Option('--seat <seat>', '런 귀속 자리 (OP|TC|MK|UX)').choices(['OP', 'TC', 'MK', 'UX']))
|
|
256
260
|
.addOption(new Option('--goal-type <type>', `골 종류 (${GOAL_TYPES.join('|')})`).choices([...GOAL_TYPES]))
|
|
257
261
|
.option('--force-preflight', '전제 검사 막힘을 명시 요청으로 우회(관측에 남음)')
|
|
258
262
|
.option('--force-gate', '같은 골 실행 중 막힘을 명시 요청으로 우회(관측에 남음; 예산 막힘은 우회 불가)')
|
|
@@ -284,6 +288,7 @@ function registerHarnessMissionOptions(command: Command): Command {
|
|
|
284
288
|
function normalizeHarnessCommonOptions(opts: HarnessAskSayOptions): HarnessAskSayOptions {
|
|
285
289
|
return {
|
|
286
290
|
...(opts.json ? { json: true } : {}),
|
|
291
|
+
...(opts.seat !== undefined ? { seat: opts.seat } : {}),
|
|
287
292
|
...(opts.base !== undefined ? { base: opts.base } : {}),
|
|
288
293
|
...(opts.target !== undefined ? { target: opts.target } : {}),
|
|
289
294
|
...(opts.yes === true ? { yes: true } : {}),
|
|
@@ -328,7 +333,7 @@ function assertHarnessChildLlmModel(opts: HarnessAskSayChildLlmOptions): void {
|
|
|
328
333
|
}
|
|
329
334
|
|
|
330
335
|
/** Shared post-parse gate: validate child model before any ask/say early return (including `--dry-run`). */
|
|
331
|
-
type HarnessSubstrateOpts = { substrate?: 'local' | 'pod'; podPool?: string; podMemory?: string; after?: string; autoMerge?: boolean; base?: string; json?: boolean; target?: string; source?: string };
|
|
336
|
+
type HarnessSubstrateOpts = { substrate?: 'local' | 'pod'; podPool?: string; podMemory?: string; after?: string; autoMerge?: boolean; base?: string; json?: boolean; target?: string; source?: string; seat?: QueueSeat };
|
|
332
337
|
function resolveLaunchSubstrate(opts: HarnessSubstrateOpts): ResolvedHarnessSubstrate {
|
|
333
338
|
const resolved = resolveHarnessSubstrate({ flag: opts, config: getUserConfig(), env: process.env });
|
|
334
339
|
debug.log('harness.substrate', 'resolved', resolved);
|
|
@@ -338,7 +343,17 @@ function resolveLaunchSubstrate(opts: HarnessSubstrateOpts): ResolvedHarnessSubs
|
|
|
338
343
|
return resolved;
|
|
339
344
|
}
|
|
340
345
|
type PodExit = { status: number | null; signal?: string | null };
|
|
341
|
-
type PodExitReason = 'signal' | 'human-stop' | 'supervisor' | 'no-launch' | 'unknown'
|
|
346
|
+
type PodExitReason = 'signal' | 'human-stop' | 'supervisor' | 'no-launch' | 'unknown'
|
|
347
|
+
| 'pod-error' | 'launch-gate-blocked' | 'ENOBUFS' | 'OOMKilled';
|
|
348
|
+
|
|
349
|
+
function unknownPodExitTailReason(output: string): PodExitReason | undefined {
|
|
350
|
+
const tail = output.slice(-16_000);
|
|
351
|
+
if (/\bOOMKilled\b/i.test(tail)) return 'OOMKilled';
|
|
352
|
+
if (/\bENOBUFS\b/i.test(tail)) return 'ENOBUFS';
|
|
353
|
+
if (/launch gate[^\r\n]*blocked|blocked[^\r\n]*launch gate/i.test(tail)) return 'launch-gate-blocked';
|
|
354
|
+
if (/\bpod-error\b/i.test(tail)) return 'pod-error';
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
342
357
|
/** GitHub lists at most this many files for one pull request; a list that reaches it is not provably complete. */
|
|
343
358
|
export const GITHUB_PR_FILES_LIST_CAP = 3000;
|
|
344
359
|
const STOPPING_SUPERVISOR_VERDICTS = new Set(['UNCONVERGEABLE', 'CONTRACT-CONFLICT']);
|
|
@@ -441,34 +456,47 @@ export function classifyHarnessPodExit(
|
|
|
441
456
|
try { applied = (deps.hasJobApplied ?? hasHarnessPodJobApplied)(runId, output); } catch { /* unknown */ }
|
|
442
457
|
const noLaunch = ledgerReadable && !ledger?.some((entry) => LAUNCH_EVIDENCE_EVENTS.has(entry.event))
|
|
443
458
|
&& applied === false && !pr;
|
|
444
|
-
reason = noLaunch ? 'no-launch' : 'unknown';
|
|
459
|
+
reason = noLaunch ? 'no-launch' : unknownPodExitTailReason(output) ?? 'unknown';
|
|
445
460
|
line = noLaunch
|
|
446
461
|
? 'Pod 실행에 닿지 못했다 — 풀·컨텍스트·SSH 연결을 확인하거나 `--substrate local` 로 명시하라'
|
|
447
|
-
: 'Pod 실행 여부 또는 종료 이유를 확인하지 못했다'
|
|
462
|
+
: reason === 'unknown' ? 'Pod 실행 여부 또는 종료 이유를 확인하지 못했다'
|
|
463
|
+
: `Pod 종료 출력에서 확인한 원인: ${reason}`;
|
|
448
464
|
}
|
|
449
465
|
try { debug.log('harness.pod', 'exit-classified', { runId, reason, status: exit.status, signal: signal ?? null }); } catch { /* observation is fail-soft */ }
|
|
450
466
|
return { reason, lines: [line, ...(pr ? [`PR 이 남아 있다: ${pr}`] : [])] };
|
|
451
467
|
}
|
|
452
468
|
|
|
453
469
|
/** The classified status is the same exit handed to the launcher; ledger failure is never an exit failure. */
|
|
454
|
-
export function recordClassifiedHarnessPodExit(runId: string | undefined, reason: string, status: number
|
|
470
|
+
export function recordClassifiedHarnessPodExit(runId: string | undefined, reason: string, status: number,
|
|
471
|
+
context: { entrance?: string; seat?: string; output?: string } = {}): void {
|
|
455
472
|
if (!runId) return;
|
|
456
473
|
try {
|
|
457
474
|
const signal = status > 128
|
|
458
475
|
? Object.entries(osConstants.signals).find(([, number]) => number === status - 128)?.[0] ?? null : null;
|
|
459
|
-
|
|
476
|
+
const cwd = process.cwd();
|
|
477
|
+
const seat = context.seat ?? process.env.ELANOUS_HARNESS_SEAT;
|
|
478
|
+
recordRunExit({ runId, reason, status, signal, at: new Date().toISOString(),
|
|
479
|
+
...(context.entrance ? { entrance: context.entrance } : {}),
|
|
480
|
+
...(['OP', 'TC', 'MK', 'UX'].includes(seat ?? '') ? { seat } : {}),
|
|
481
|
+
cwd: `${basename(dirname(cwd))}/${basename(cwd)}`, hostname: hostname().split('.')[0]!, pid: process.pid,
|
|
482
|
+
...(context.output ? { lastLines: incidentLastLines(context.output) } : {}),
|
|
483
|
+
}, effectiveInstanceRoot());
|
|
460
484
|
} catch { /* incident writes cannot change Pod output or exit */ }
|
|
461
485
|
}
|
|
462
486
|
|
|
463
487
|
/** Every Pod launch entrance (harness ask/say · dev ask --substrate pod) warns once after a recent burst; it never blocks the launch. */
|
|
464
488
|
export function warnRecentIncidentBurst(
|
|
465
489
|
root: string = effectiveInstanceRoot(), now: Date = new Date(), write: (line: string) => void = (line) => console.error(line),
|
|
490
|
+
context: { seat?: string; entrance?: string } = {},
|
|
466
491
|
): boolean {
|
|
467
492
|
try {
|
|
468
|
-
const
|
|
493
|
+
const seat = context.seat ?? process.env.ELANOUS_HARNESS_SEAT;
|
|
494
|
+
const burst = recentBurst(root, now, { entrance: context.entrance,
|
|
495
|
+
...(['OP', 'TC', 'MK', 'UX'].includes(seat ?? '') ? { seat } : {}) });
|
|
469
496
|
if (!burst) return false;
|
|
470
497
|
write(formatIncidentBurstWarning(burst));
|
|
471
|
-
try { debug.log('harness.incident', 'burst', { reason: burst.reason, count: burst.count
|
|
498
|
+
try { debug.log('harness.incident', 'burst', { reason: burst.reason, count: burst.count,
|
|
499
|
+
...(burst.seat ? { seat: burst.seat } : {}), ...(burst.entrance ? { entrance: burst.entrance } : {}) }); } catch { /* observation is fail-soft */ }
|
|
472
500
|
return true;
|
|
473
501
|
} catch { return false; /* incident reads cannot block a launch */ }
|
|
474
502
|
}
|
|
@@ -540,7 +568,8 @@ async function onPod(opts: unknown, entrance: 'cli-harness-ask' | 'cli-harness-s
|
|
|
540
568
|
process.exitCode = 2;
|
|
541
569
|
return;
|
|
542
570
|
}
|
|
543
|
-
warnRecentIncidentBurst()
|
|
571
|
+
warnRecentIncidentBurst(effectiveInstanceRoot(), new Date(), (line) => console.error(line),
|
|
572
|
+
{ entrance, seat: o.seat });
|
|
544
573
|
let dispatchRecorded = false;
|
|
545
574
|
try {
|
|
546
575
|
const goalText = entrance === 'cli-harness-ask' ? readFileSync(input, 'utf8') : input;
|
|
@@ -559,14 +588,15 @@ async function onPod(opts: unknown, entrance: 'cli-harness-ask' | 'cli-harness-s
|
|
|
559
588
|
const { dispatchHarnessOnPod } = await import('./harness-pod-dispatch.js');
|
|
560
589
|
let output = '';
|
|
561
590
|
let runId: string | undefined;
|
|
562
|
-
const status = await dispatchHarnessOnPod({ entrance, input, podPool: pool, ...(dispatchRecorded ? { dispatchRecorded: true } : {}), ...(o.podMemory ? { podMemory: o.podMemory } : {}), ...(o.after ? { after: o.after } : {}), ...(o.autoMerge === false ? { autoMerge: false } : {}), ...(o.base ? { base: o.base } : {}), ...(o.json ? { json: true } : {}), ...(o.source ? { source: o.source } : {}) },
|
|
591
|
+
const status = await dispatchHarnessOnPod({ entrance, input, podPool: pool, ...(dispatchRecorded ? { dispatchRecorded: true } : {}), ...(o.podMemory ? { podMemory: o.podMemory } : {}), ...(o.after ? { after: o.after } : {}), ...(o.autoMerge === false ? { autoMerge: false } : {}), ...(o.base ? { base: o.base } : {}), ...(o.json ? { json: true } : {}), ...(o.source ? { source: o.source } : {}), ...(o.seat ? { seat: o.seat } : {}) },
|
|
563
592
|
{ onOutput: (text) => {
|
|
564
593
|
runId ??= harnessPodRunId(output + text);
|
|
565
594
|
output = (output + text).slice(-16_000);
|
|
566
595
|
} });
|
|
567
596
|
if (status !== 0) {
|
|
568
597
|
const classified = classifyHarnessPodExit({ status }, output, { ...(runId ? { runId } : {}) });
|
|
569
|
-
recordClassifiedHarnessPodExit(runId ?? harnessPodRunId(output), classified.reason, status
|
|
598
|
+
recordClassifiedHarnessPodExit(runId ?? harnessPodRunId(output), classified.reason, status,
|
|
599
|
+
{ entrance, seat: o.seat, output });
|
|
570
600
|
for (const line of classified.lines) console.error(line);
|
|
571
601
|
process.exitCode = status;
|
|
572
602
|
}
|
|
@@ -634,7 +664,7 @@ async function dispatchHarnessAskSay(
|
|
|
634
664
|
readonly wouldStart: string;
|
|
635
665
|
readonly goalPath?: string;
|
|
636
666
|
},
|
|
637
|
-
dispatch: (resolved: ResolvedHarnessSubstrate) => Promise<void>,
|
|
667
|
+
dispatch: (resolved: ResolvedHarnessSubstrate, stampedOpts: HarnessAskSayChildLlmOptions & HarnessDryRunOpts) => Promise<void>,
|
|
638
668
|
gate: PreLaunchGateDeps & { readBudget?: () => Promise<BudgetDecision | 'unknown'> } = {},
|
|
639
669
|
): Promise<void> {
|
|
640
670
|
await runInjectedHarnessHandler(async () => {
|
|
@@ -644,6 +674,25 @@ async function dispatchHarnessAskSay(
|
|
|
644
674
|
printHarnessLaunchDryRun(dryRunPreview);
|
|
645
675
|
return;
|
|
646
676
|
}
|
|
677
|
+
// Resolve the launch directory once; descendants and the checkpoint retain the same identity.
|
|
678
|
+
const queued = process.env.ELANOUS_HARNESS_QUEUE_LAUNCH && process.env.ELANOUS_HARNESS_SEAT;
|
|
679
|
+
const queueSeat = queued === 'OP' || queued === 'TC' || queued === 'MK' || queued === 'UX' ? queued : undefined;
|
|
680
|
+
const assigned = opts.seat ?? queueSeat ?? queueSeatForCwd(process.cwd(), getUserConfig().loops?.orchestrator?.seatTrees);
|
|
681
|
+
const inheritedSeat = process.env.ELANOUS_HARNESS_SEAT;
|
|
682
|
+
const podSeat = resolved.substrate === 'pod' && ['OP', 'TC', 'MK', 'UX'].includes(inheritedSeat ?? '')
|
|
683
|
+
? inheritedSeat as QueueSeat : undefined;
|
|
684
|
+
const stampedSeat = resolved.substrate === 'pod' ? opts.seat ?? queueSeat ?? podSeat ?? assigned : assigned;
|
|
685
|
+
const stampedOpts = stampedSeat ? { ...opts, seat: stampedSeat } : opts;
|
|
686
|
+
const launch = async () => {
|
|
687
|
+
const previous = process.env.ELANOUS_HARNESS_SEAT;
|
|
688
|
+
if (stampedSeat) process.env.ELANOUS_HARNESS_SEAT = stampedSeat;
|
|
689
|
+
else delete process.env.ELANOUS_HARNESS_SEAT;
|
|
690
|
+
try { await dispatch(resolved, stampedOpts); }
|
|
691
|
+
finally {
|
|
692
|
+
if (previous === undefined) delete process.env.ELANOUS_HARNESS_SEAT;
|
|
693
|
+
else process.env.ELANOUS_HARNESS_SEAT = previous;
|
|
694
|
+
}
|
|
695
|
+
};
|
|
647
696
|
// Without a GoalId, match the request to a live run's exact verbatim original ask; still check budget.
|
|
648
697
|
let identity = dryRunPreview.input;
|
|
649
698
|
let goalId: string | null = null;
|
|
@@ -658,7 +707,7 @@ async function dispatchHarnessAskSay(
|
|
|
658
707
|
// CLI wiring tests time out on them. Gate tests inject deps or opt in with ELANOUS_LAUNCH_GATE_LIVE=1.
|
|
659
708
|
if (process.env.NODE_ENV === 'test' && Object.keys(gate).length === 0 && process.env.ELANOUS_LAUNCH_GATE_LIVE !== '1') {
|
|
660
709
|
try { debug.log('execution-loop.launch-gate', 'skipped-test-env', { goalId }); } catch { /* observation is fail-soft */ }
|
|
661
|
-
await
|
|
710
|
+
await launch();
|
|
662
711
|
return;
|
|
663
712
|
}
|
|
664
713
|
const budget = await (gate.readBudget ?? (() => readHarnessLaunchBudget(opts)))();
|
|
@@ -673,7 +722,7 @@ async function dispatchHarnessAskSay(
|
|
|
673
722
|
...(decision.budget === 'unknown' ? ['budget unknown'] : decision.budget.action === 'next-provider' ? [decision.budget.reasons.join(' · ') || 'next-provider'] : []),
|
|
674
723
|
];
|
|
675
724
|
if (warnings.length > 0) console.error(`⚠️ launch gate: ${warnings.join(' · ').split(/\r?\n/, 1)[0]}`);
|
|
676
|
-
await
|
|
725
|
+
await launch();
|
|
677
726
|
});
|
|
678
727
|
}
|
|
679
728
|
|
|
@@ -1606,6 +1655,30 @@ const executeGh: GhExecute = (args) => execFileSync('gh', args, {
|
|
|
1606
1655
|
stdio: ['ignore', 'pipe', 'pipe'], env: process.env,
|
|
1607
1656
|
});
|
|
1608
1657
|
|
|
1658
|
+
/** Merge-time adapter: reuse the sweep's GitHub inventory and worktree liveness without a second triage implementation. */
|
|
1659
|
+
export async function supersedeMergedGoalDrafts(
|
|
1660
|
+
number: number, cwd: string,
|
|
1661
|
+
execute: GhExecute = executeGh, git: DraftSweepGitExecute = runGitCommand,
|
|
1662
|
+
): Promise<void> {
|
|
1663
|
+
const remote = git(cwd, ['config', '--get', 'remote.origin.url']);
|
|
1664
|
+
const repository = remote.status === 0 ? githubRemoteRepository(remote.stdout) : undefined;
|
|
1665
|
+
if (!repository) throw new Error('Merge repository unavailable');
|
|
1666
|
+
// The PR can change between open and merge; never authorize a close using locally reconstructed metadata.
|
|
1667
|
+
const pr = ghJson<GithubPull>(['api', `repos/${repository}/pulls/${number}`], execute);
|
|
1668
|
+
if (!pr || pr.number !== number || pr.state !== 'closed' || !pr.merged_at || !pr.title?.trim()
|
|
1669
|
+
|| !pr.head?.ref?.trim() || !pr.created_at || !Array.isArray(pr.labels)
|
|
1670
|
+
|| !Number.isFinite(Date.parse(pr.created_at)) || !Number.isFinite(Date.parse(pr.merged_at))
|
|
1671
|
+
|| Date.parse(pr.merged_at) < Date.parse(pr.created_at)) {
|
|
1672
|
+
throw new Error('Merged PR metadata unavailable');
|
|
1673
|
+
}
|
|
1674
|
+
const merged: SweepMergedPr & { createdAt: string; mergedAt: string } = {
|
|
1675
|
+
number: pr.number, title: pr.title, body: pr.body ?? undefined, branch: pr.head.ref,
|
|
1676
|
+
createdAt: pr.created_at, mergedAt: pr.merged_at,
|
|
1677
|
+
};
|
|
1678
|
+
const { supersedeDraftsOnMerge } = await import('../self-dev/draft-sweep.js');
|
|
1679
|
+
await supersedeDraftsOnMerge({ repository, merged, adapters: githubDraftSweepAdapters(execute, git, cwd) });
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1609
1682
|
function ghJson<T>(args: string[], execute: GhExecute): T {
|
|
1610
1683
|
return JSON.parse(execute(args)) as T;
|
|
1611
1684
|
}
|
|
@@ -1670,7 +1743,7 @@ function githubRemoteRepository(url: string): string | undefined {
|
|
|
1670
1743
|
return match?.[1];
|
|
1671
1744
|
}
|
|
1672
1745
|
|
|
1673
|
-
export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: DraftSweepGitExecute = runGitCommand): DraftSweepAdapters {
|
|
1746
|
+
export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: DraftSweepGitExecute = runGitCommand, cwd = process.cwd()): DraftSweepAdapters {
|
|
1674
1747
|
const inventory = async (repository: string, state: 'open' | 'closed'): Promise<GithubPull[]> => {
|
|
1675
1748
|
// ⛔ A large repository has tens of thousands of closed PRs (2026-09-28: 21k → «exceeded pagination limit», no sweep at all).
|
|
1676
1749
|
// Merged twins only matter after the oldest open draft was opened, so the closed listing stops at that time.
|
|
@@ -1763,6 +1836,7 @@ export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: Dr
|
|
|
1763
1836
|
return draftSweepRunStatus(ledgerMatches, running, repository, draft.number);
|
|
1764
1837
|
};
|
|
1765
1838
|
return {
|
|
1839
|
+
getPrFiles: async (repository, number) => filesFor(repository, number),
|
|
1766
1840
|
listDrafts: async (page, perPage, repository): Promise<SweepDraft[]> =>
|
|
1767
1841
|
(await listed(open, repository, 'open')).filter((pr) => pr.draft)
|
|
1768
1842
|
.slice((page - 1) * perPage, page * perPage)
|
|
@@ -1811,7 +1885,6 @@ export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: Dr
|
|
|
1811
1885
|
} finally { store.close(); }
|
|
1812
1886
|
},
|
|
1813
1887
|
listLiveBranches: async (repository) => {
|
|
1814
|
-
const cwd = process.cwd();
|
|
1815
1888
|
const remote = git(cwd, ['config', '--get', 'remote.origin.url']);
|
|
1816
1889
|
if (remote.status !== 0 || githubRemoteRepository(remote.stdout)?.toLowerCase() !== repository.toLowerCase()) {
|
|
1817
1890
|
return undefined;
|
|
@@ -1920,6 +1993,55 @@ function installHarnessDraftSweepCommand(harnessCmd: Command, deps: HarnessDraft
|
|
|
1920
1993
|
});
|
|
1921
1994
|
}
|
|
1922
1995
|
|
|
1996
|
+
function renderHarnessGoal(result: GoalLookupResult, universe: '운영' | '시험'): string {
|
|
1997
|
+
const matchedRun = result.runState?.results.find((entry) => entry.runId === result.runId);
|
|
1998
|
+
return [
|
|
1999
|
+
`골: ${result.goalBody?.split(/\r?\n/, 1)[0] || '골 문서에 없음'}`,
|
|
2000
|
+
`runId: ${result.runId ?? '원장에 없음'}`,
|
|
2001
|
+
`칸 id: ${result.kanId ?? '기록 없음'}`,
|
|
2002
|
+
`PR: ${result.record?.prNumber != null ? `#${result.record.prNumber}` : '기록 없음'}`,
|
|
2003
|
+
`stage/결과: ${result.record?.stage ?? '기록 없음'} / ${result.record?.outcome ?? '기록 없음'}`,
|
|
2004
|
+
`종결 사유: ${result.runState?.supervisorStopReason ?? matchedRun?.error?.message ?? '기록 없음'}`,
|
|
2005
|
+
`찾은 우주: ${universe}`,
|
|
2006
|
+
`원장: ${result.runState ? '찾음' : '원장에 없음'}`,
|
|
2007
|
+
`골 문서: ${result.goalFile ? '찾음' : '골 문서에 없음'}`,
|
|
2008
|
+
].join('\n');
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
function installHarnessGoalCommand(harnessCmd: Command, options?: GoalLookupOptions, archiveOptions?: GoalArchiveOptions): Command {
|
|
2012
|
+
const goal = harnessCmd.command('goal').usage('<reference> | archive [--before <date>] [--apply]')
|
|
2013
|
+
.description('골 문서와 실행 결과를 조회하거나 보관한다');
|
|
2014
|
+
goal.command('archive')
|
|
2015
|
+
.description('종료된 골 문서를 미리 보거나 상태 루트에 보관한다 — 저장소는 건드리지 않고 지울 목록(filesForPr)만 낸다')
|
|
2016
|
+
.option('--before <date>', '완료 시각이 이 YYYY-MM-DD 이전인 골만')
|
|
2017
|
+
.option('--apply', '상태 루트에 보관 복사·색인 (저장소 삭제·스테이지·커밋 없음 · 지우기는 filesForPr 로 사람이 PR)')
|
|
2018
|
+
.option('--trust-doc-record', '런 원장이 없을 때 문서에 적힌 종료 실행 기록을 신뢰')
|
|
2019
|
+
.action((opts: { before?: string; apply?: boolean; trustDocRecord?: boolean }) => {
|
|
2020
|
+
try {
|
|
2021
|
+
const result = archiveGoals({ ...archiveOptions, before: opts.before, apply: opts.apply, trustDocRecord: opts.trustDocRecord });
|
|
2022
|
+
console.log(JSON.stringify(result));
|
|
2023
|
+
} catch (error) {
|
|
2024
|
+
console.error(humanErrorLine(error));
|
|
2025
|
+
process.exitCode = 1;
|
|
2026
|
+
}
|
|
2027
|
+
});
|
|
2028
|
+
goal.command('show <reference>', { isDefault: true })
|
|
2029
|
+
.description('PR 번호 또는 runId 로 골 문서와 실행 결과를 조회한다 (읽기 전용)')
|
|
2030
|
+
.option('--json', 'GoalLookupResult 그대로 출력')
|
|
2031
|
+
.action((reference: string, opts: { json?: boolean }) => {
|
|
2032
|
+
try {
|
|
2033
|
+
const result = lookupGoal(reference, options);
|
|
2034
|
+
const universe = effectiveInstanceRoot() === prodInstanceRoot() ? '운영' : '시험';
|
|
2035
|
+
console.log(opts.json ? JSON.stringify(result) : renderHarnessGoal(result, universe));
|
|
2036
|
+
if (!result.goalFile || !result.runState) process.exitCode = 1;
|
|
2037
|
+
} catch (error) {
|
|
2038
|
+
console.error(humanErrorLine(error));
|
|
2039
|
+
process.exitCode = 1;
|
|
2040
|
+
}
|
|
2041
|
+
});
|
|
2042
|
+
return goal;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
1923
2045
|
function installHarnessBudgetCommand(harnessCmd: Command): Command {
|
|
1924
2046
|
return harnessCmd
|
|
1925
2047
|
.command('budget')
|
|
@@ -2106,6 +2228,8 @@ export interface HarnessCliCommandDeps {
|
|
|
2106
2228
|
processObservation?: HarnessProcessObservationDeps;
|
|
2107
2229
|
draftSweep?: HarnessDraftSweepDeps;
|
|
2108
2230
|
queue?: HarnessQueueDeps;
|
|
2231
|
+
goalLookup?: GoalLookupOptions;
|
|
2232
|
+
goalArchive?: GoalArchiveOptions;
|
|
2109
2233
|
ask?: HarnessAskHandler;
|
|
2110
2234
|
say?: HarnessSayHandler;
|
|
2111
2235
|
/** Observe-only Pod host dispatch seam. */
|
|
@@ -2146,8 +2270,8 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2146
2270
|
const bursts = detectBursts(rows).filter((burst) => Date.parse(burst.lastAt) >= now - since * 60_000 && Date.parse(burst.lastAt) <= now);
|
|
2147
2271
|
if (opts.json) console.log(JSON.stringify({ rows: selected, bursts }));
|
|
2148
2272
|
else {
|
|
2149
|
-
for (const row of selected) console.log(`${row.at} run ${row.runId} ·
|
|
2150
|
-
for (const burst of bursts) console.log(`${burst.firstAt}–${burst.lastAt} 묶음 ${burst.reason} ×${burst.count} · ${burst.runIds.join(', ')}`);
|
|
2273
|
+
for (const row of selected) console.log(`${row.at} run ${row.runId} · seat=${row.seat ?? '-'} entrance=${row.entrance ?? '-'} reason=${row.reason} · status=${row.status} signal=${row.signal ?? '-'}`);
|
|
2274
|
+
for (const burst of bursts) console.log(`${burst.firstAt}–${burst.lastAt} 묶음 ${burst.reason} ×${burst.count} · seat=${burst.seat ?? '-'} entrance=${burst.entrance ?? '-'} · ${burst.runIds.join(', ')}`);
|
|
2151
2275
|
}
|
|
2152
2276
|
} catch (error) {
|
|
2153
2277
|
console.error(humanErrorLine(error));
|
|
@@ -2157,6 +2281,7 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2157
2281
|
installDeliverableVerifyCliCommand(harnessCmd, deps.deliverableVerify);
|
|
2158
2282
|
installHarnessProcessObservationCommand(harnessCmd, deps.processObservation);
|
|
2159
2283
|
installHarnessBudgetCommand(harnessCmd);
|
|
2284
|
+
installHarnessGoalCommand(harnessCmd, deps.goalLookup, deps.goalArchive);
|
|
2160
2285
|
installHarnessDraftSweepCommand(harnessCmd, deps.draftSweep);
|
|
2161
2286
|
const queue = harnessCmd.command('queue').description('자리별 영속 발사 대기열');
|
|
2162
2287
|
const queueDeps = deps.queue ?? {};
|
|
@@ -2197,13 +2322,13 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2197
2322
|
...(opts.goalType !== undefined ? { goalType: opts.goalType } : {}),
|
|
2198
2323
|
...(opts.target !== undefined ? { target: opts.target } : {}),
|
|
2199
2324
|
},
|
|
2200
|
-
(resolved) => {
|
|
2325
|
+
(resolved, stampedOpts) => {
|
|
2201
2326
|
if (resolved.substrate !== 'pod' && (opts as { source?: string }).source !== undefined) {
|
|
2202
2327
|
console.error('`--source` 는 `--substrate pod` 와 함께');
|
|
2203
2328
|
process.exitCode = 2;
|
|
2204
2329
|
return Promise.resolve();
|
|
2205
2330
|
}
|
|
2206
|
-
return resolved.substrate === 'pod' ? onPod(
|
|
2331
|
+
return resolved.substrate === 'pod' ? onPod(stampedOpts, 'cli-harness-ask', goalPath, resolved.pool!, deps.podDispatchTask) : ask(goalPath, normalizeHarnessAskSayOptions(stampedOpts));
|
|
2207
2332
|
},
|
|
2208
2333
|
deps.launchGate,
|
|
2209
2334
|
);
|
|
@@ -2222,13 +2347,13 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
|
|
|
2222
2347
|
wouldStart: '골 문서 · 워크트리 · 브랜치 · 자식 · 파이프라인',
|
|
2223
2348
|
...(opts.target !== undefined ? { target: opts.target } : {}),
|
|
2224
2349
|
},
|
|
2225
|
-
(resolved) => {
|
|
2350
|
+
(resolved, stampedOpts) => {
|
|
2226
2351
|
if (resolved.substrate !== 'pod' && (opts as { source?: string }).source !== undefined) {
|
|
2227
2352
|
console.error('`--source` 는 `--substrate pod` 와 함께');
|
|
2228
2353
|
process.exitCode = 2;
|
|
2229
2354
|
return Promise.resolve();
|
|
2230
2355
|
}
|
|
2231
|
-
return resolved.substrate === 'pod' ? onPod(
|
|
2356
|
+
return resolved.substrate === 'pod' ? onPod(stampedOpts, 'cli-harness-say', sentence.join(' '), resolved.pool!, deps.podDispatchTask) : say(sentence, normalizeHarnessAskSayOptions(stampedOpts));
|
|
2232
2357
|
},
|
|
2233
2358
|
deps.launchGate,
|
|
2234
2359
|
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Database } from 'bun:sqlite';
|
|
2
2
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { redactSecretText } from '../debug/log.js';
|
|
4
5
|
|
|
5
6
|
export interface RunExit {
|
|
6
7
|
runId: string;
|
|
@@ -8,6 +9,12 @@ export interface RunExit {
|
|
|
8
9
|
status: number | null;
|
|
9
10
|
signal: string | null;
|
|
10
11
|
at: string;
|
|
12
|
+
entrance?: string;
|
|
13
|
+
seat?: string;
|
|
14
|
+
cwd?: string;
|
|
15
|
+
hostname?: string;
|
|
16
|
+
pid?: number;
|
|
17
|
+
lastLines?: string[];
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
export interface IncidentBurst {
|
|
@@ -16,6 +23,14 @@ export interface IncidentBurst {
|
|
|
16
23
|
firstAt: string;
|
|
17
24
|
lastAt: string;
|
|
18
25
|
runIds: string[];
|
|
26
|
+
entrance?: string;
|
|
27
|
+
seat?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Scrub before bounding: a token crossing the 200-character boundary must not leak. */
|
|
31
|
+
export function incidentLastLines(output: string): string[] {
|
|
32
|
+
return output.split(/\r?\n/).filter((line) => line.trim().length > 0).slice(-3)
|
|
33
|
+
.map((line) => redactSecretText(line).slice(0, 200));
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
const KST_OFFSET_MS = 9 * 60 * 60_000;
|
|
@@ -52,7 +67,15 @@ export function readRunExits(root: string): RunExit[] {
|
|
|
52
67
|
|| !(row.status === null || typeof row.status === 'number')
|
|
53
68
|
|| !(row.signal === null || typeof row.signal === 'string')) continue;
|
|
54
69
|
seen.add(row.runId);
|
|
55
|
-
rows.push(row
|
|
70
|
+
rows.push({ runId: row.runId, reason: row.reason, status: row.status, signal: row.signal, at: row.at,
|
|
71
|
+
...(typeof row.entrance === 'string' ? { entrance: row.entrance } : {}),
|
|
72
|
+
...(typeof row.seat === 'string' ? { seat: row.seat } : {}),
|
|
73
|
+
...(typeof row.cwd === 'string' ? { cwd: row.cwd } : {}),
|
|
74
|
+
...(typeof row.hostname === 'string' ? { hostname: row.hostname } : {}),
|
|
75
|
+
...(typeof row.pid === 'number' && Number.isSafeInteger(row.pid) ? { pid: row.pid } : {}),
|
|
76
|
+
...(Array.isArray(row.lastLines) && row.lastLines.every((line) => typeof line === 'string')
|
|
77
|
+
? { lastLines: row.lastLines } : {}),
|
|
78
|
+
});
|
|
56
79
|
}
|
|
57
80
|
}
|
|
58
81
|
return rows.sort((a, b) => Date.parse(a.at) - Date.parse(b.at) || a.runId.localeCompare(b.runId));
|
|
@@ -71,7 +94,10 @@ export function recordRunExit(row: RunExit, root: string): void {
|
|
|
71
94
|
lock.exec('BEGIN IMMEDIATE');
|
|
72
95
|
try {
|
|
73
96
|
if (!readRunExits(root).some((record) => record.runId === row.runId)) {
|
|
74
|
-
|
|
97
|
+
const safeRow = row.lastLines === undefined ? row : {
|
|
98
|
+
...row, lastLines: row.lastLines.slice(-3).map((line) => redactSecretText(line).slice(0, 200)),
|
|
99
|
+
};
|
|
100
|
+
appendFileSync(join(dir, `${kstDate(at)}.jsonl`), `${JSON.stringify(safeRow)}\n`);
|
|
75
101
|
}
|
|
76
102
|
lock.exec('COMMIT');
|
|
77
103
|
} catch (error) {
|
|
@@ -83,21 +109,23 @@ export function recordRunExit(row: RunExit, root: string): void {
|
|
|
83
109
|
}
|
|
84
110
|
}
|
|
85
111
|
|
|
86
|
-
/** Non-overlapping, maximal same-reason windows; no run contributes to two bursts. */
|
|
112
|
+
/** Non-overlapping, maximal same-reason/seat/entrance windows; no run contributes to two bursts. */
|
|
87
113
|
export function detectBursts(rows: readonly RunExit[], { windowMs = 60_000, threshold = 3 }: {
|
|
88
114
|
windowMs?: number; threshold?: number;
|
|
89
115
|
} = {}): IncidentBurst[] {
|
|
90
116
|
const bursts: IncidentBurst[] = [];
|
|
91
|
-
const
|
|
117
|
+
const byIdentity = new Map<string, RunExit[]>();
|
|
92
118
|
const seen = new Set<string>();
|
|
93
119
|
for (const row of rows) {
|
|
94
120
|
if (!row.runId || seen.has(row.runId) || !Number.isFinite(Date.parse(row.at))) continue;
|
|
95
121
|
seen.add(row.runId);
|
|
96
|
-
const
|
|
122
|
+
const key = JSON.stringify([row.reason, row.seat ?? null, row.entrance ?? null]);
|
|
123
|
+
const group = byIdentity.get(key) ?? [];
|
|
97
124
|
group.push(row);
|
|
98
|
-
|
|
125
|
+
byIdentity.set(key, group);
|
|
99
126
|
}
|
|
100
|
-
for (const
|
|
127
|
+
for (const group of byIdentity.values()) {
|
|
128
|
+
const { reason, seat, entrance } = group[0]!;
|
|
101
129
|
group.sort((a, b) => Date.parse(a.at) - Date.parse(b.at) || a.runId.localeCompare(b.runId));
|
|
102
130
|
for (let start = 0; start < group.length;) {
|
|
103
131
|
let end = start + 1;
|
|
@@ -105,25 +133,28 @@ export function detectBursts(rows: readonly RunExit[], { windowMs = 60_000, thre
|
|
|
105
133
|
if (end - start < threshold) { start++; continue; }
|
|
106
134
|
const members = group.slice(start, end);
|
|
107
135
|
bursts.push({ reason, count: members.length, firstAt: members[0]!.at,
|
|
108
|
-
lastAt: members[members.length - 1]!.at, runIds: members.map((row) => row.runId)
|
|
136
|
+
lastAt: members[members.length - 1]!.at, runIds: members.map((row) => row.runId),
|
|
137
|
+
...(seat ? { seat } : {}), ...(entrance ? { entrance } : {}) });
|
|
109
138
|
start = end;
|
|
110
139
|
}
|
|
111
140
|
}
|
|
112
141
|
return bursts.sort((a, b) => Date.parse(a.firstAt) - Date.parse(b.firstAt) || a.reason.localeCompare(b.reason));
|
|
113
142
|
}
|
|
114
143
|
|
|
115
|
-
export function recentBurst(root: string, now: Date | number, { lookbackMs = 15 * 60_000 }: {
|
|
116
|
-
lookbackMs?: number;
|
|
144
|
+
export function recentBurst(root: string, now: Date | number, { lookbackMs = 15 * 60_000, seat, entrance }: {
|
|
145
|
+
lookbackMs?: number; seat?: string; entrance?: string;
|
|
117
146
|
} = {}): IncidentBurst | null {
|
|
118
147
|
const time = typeof now === 'number' ? now : now.getTime();
|
|
119
148
|
const recent = detectBursts(readRunExits(root)).filter((burst) => {
|
|
120
149
|
const last = Date.parse(burst.lastAt);
|
|
121
|
-
return last <= time && last >= time - lookbackMs
|
|
150
|
+
return last <= time && last >= time - lookbackMs
|
|
151
|
+
&& burst.seat === seat && burst.entrance === entrance;
|
|
122
152
|
});
|
|
123
153
|
return recent.sort((a, b) => Date.parse(a.lastAt) - Date.parse(b.lastAt)).at(-1) ?? null;
|
|
124
154
|
}
|
|
125
155
|
|
|
126
156
|
export function formatIncidentBurstWarning(burst: IncidentBurst): string {
|
|
127
157
|
const time = new Date(Date.parse(burst.lastAt) + KST_OFFSET_MS).toISOString().slice(11, 16);
|
|
128
|
-
|
|
158
|
+
const owner = burst.seat || burst.entrance ? ` [seat=${burst.seat ?? '-'} entrance=${burst.entrance ?? '-'}]` : '';
|
|
159
|
+
return `⚠ 최근 묶음 사고${owner}: ${burst.reason} ×${burst.count} (${time} KST) — 원인 확인 전 재발사는 중복·재사고 위험 · elanous harness incidents`;
|
|
129
160
|
}
|
|
@@ -20,6 +20,7 @@ export interface HarnessPodDispatchInput {
|
|
|
20
20
|
/** say 의 문장, 또는 ask 의 골 문서 경로. */
|
|
21
21
|
readonly input: string;
|
|
22
22
|
readonly podPool?: string;
|
|
23
|
+
readonly seat?: 'OP' | 'TC' | 'MK' | 'UX';
|
|
23
24
|
/** Host already recorded the observe-only dispatch; suppress a second decision in the Pod. */
|
|
24
25
|
readonly dispatchRecorded?: boolean;
|
|
25
26
|
/** Pod 메모리 등급(standard|high) — 오케스트레이터 환경 `ELANOUS_POD_MEMORY_TIER` 로 Job 까지 간다. */
|
|
@@ -80,6 +81,7 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
|
|
|
80
81
|
const args = podOrchestrateArgs(input, goalFile);
|
|
81
82
|
debug.log('harness.substrate', 'goal-file', { entrance: input.entrance, mode: tempDir ? 'temp' : 'path' });
|
|
82
83
|
const env = { ...process.env };
|
|
84
|
+
if (input.seat) env.ELANOUS_HARNESS_SEAT = input.seat;
|
|
83
85
|
delete env.ELANOUS_POD_GOAL_DOC;
|
|
84
86
|
delete env.ELANOUS_DISPATCH_RECORDED;
|
|
85
87
|
if (input.dispatchRecorded) env.ELANOUS_DISPATCH_RECORDED = '1';
|
|
@@ -3,9 +3,9 @@ import { randomUUID } from 'node:crypto';
|
|
|
3
3
|
import { renameSync, writeFileSync } from 'node:fs';
|
|
4
4
|
|
|
5
5
|
/** Only a confirmed terminal observation allows reconciliation to release an uncertain launch. */
|
|
6
|
-
export function writeHarnessQueueReceipt(receiptPath: string, state: 'started' | 'finished' | 'not-started'): void {
|
|
6
|
+
export function writeHarnessQueueReceipt(receiptPath: string, state: 'started' | 'finished' | 'not-started', exitCode?: number): void {
|
|
7
7
|
const temp = `${receiptPath}.${randomUUID()}.tmp`;
|
|
8
|
-
writeFileSync(temp, JSON.stringify({ state, at: new Date().toISOString() }), { flag: 'wx', mode: 0o600 });
|
|
8
|
+
writeFileSync(temp, JSON.stringify({ state, at: new Date().toISOString(), ...(exitCode === undefined ? {} : { exitCode }) }), { flag: 'wx', mode: 0o600 });
|
|
9
9
|
renameSync(temp, receiptPath);
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -22,7 +22,7 @@ export async function runHarnessQueueChild(
|
|
|
22
22
|
child.once('spawn', () => {
|
|
23
23
|
started = true;
|
|
24
24
|
record('started');
|
|
25
|
-
child.once('exit', (code) => {
|
|
25
|
+
child.once('exit', (code) => { writeHarnessQueueReceipt(receiptPath, 'finished', code ?? 1); done(code ?? 1); });
|
|
26
26
|
});
|
|
27
27
|
child.once('error', () => {
|
|
28
28
|
if (!started) { record('not-started'); done(1); }
|