elanous 0.2.12 → 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/{9906-482c6619b3fdda20.js → 8060-cc008cc6fedfa818.js} +1 -1
- 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-1abfe3aec3be6bdc.js +1 -0
- 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-a67e11ba962337b5.js → page-15374909d8f1aebd.js} +1 -1
- 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-793fa536b3a6ef37.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-e1e47d8d940aba3d.js +1 -0
- 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-e297866051595d81.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/examples/teams/personas/backend.yaml +4 -0
- package/examples/teams/personas/pm.yaml +4 -0
- package/examples/teams/personas/qa.yaml +4 -0
- package/examples/teams/small-product.yaml +20 -0
- package/graphs/lecture/lecture-note.yaml +28 -0
- package/graphs/lecture/recipes.yaml +17 -0
- 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 +2 -1
- package/scripts/audit-test-state-writes.ts +72 -17
- package/scripts/brand/check.ts +186 -0
- package/scripts/context-hooks/emit.ts +36 -14
- package/scripts/coord-channel-watch.sh +1 -0
- package/scripts/coord-post.sh +11 -1
- package/scripts/discord-seat-probe.ts +144 -0
- 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/hq-standby.sh +65 -0
- package/scripts/hq/standby-promote.ts +173 -0
- package/scripts/hq/standby-role.ts +59 -0
- package/scripts/hq/standby-snapshot.ts +226 -0
- package/scripts/hq/standby-verify.ts +223 -0
- package/scripts/lecture-note/node.ts +256 -0
- package/scripts/lessons/handbook-page.ts +85 -0
- package/scripts/release-loop/gate-node.ts +62 -15
- package/scripts/release-loop/ops-upgrade-node.ts +17 -5
- 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 +57 -11
- 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/omni-crawl/SKILL.md +16 -16
- package/skills/omni-crawl/package-lock.json +1 -544
- package/skills/omni-crawl/package.json +3 -4
- package/skills/omni-crawl/scripts/main.ts +13 -13
- package/skills/omni-crawl/scripts/monitor.ts +8 -8
- package/skills/omni-crawl/src/apify.ts +13 -2
- package/skills/omni-crawl/src/capture.ts +6 -0
- package/skills/omni-crawl/src/registry.ts +2 -2
- package/skills/omni-crawl/src/stdout-json.ts +4 -0
- package/skills/omni-digest/SKILL.md +10 -11
- package/skills/omni-digest/package-lock.json +0 -545
- package/skills/omni-digest/package.json +2 -5
- package/skills/omni-digest/scripts/main.ts +3 -3
- package/skills/omni-digest/src/delegate.ts +5 -5
- package/skills/omni-digest/src/fetch.ts +2 -2
- package/skills/omni-digest/src/media.ts +25 -11
- package/skills/omni-digest/src/visual.ts +3 -2
- package/skills/omni-market/scripts/main.ts +1 -1
- package/skills/omni-market/scripts/stdout-json.ts +4 -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/identity-env.ts +11 -0
- package/src/agent-mission/driver.ts +15 -10
- package/src/agent-mission/duo.ts +94 -0
- package/src/agent-mission/pty-decision.ts +1 -1
- package/src/autopilot/build/llm-conflict-merge.ts +45 -8
- package/src/autopilot/build/merge-intent.ts +53 -0
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/autopilot/track-agent.ts +4 -1
- package/src/boot/daemon-prompt-request.ts +3 -0
- package/src/boot/daemon-prompt-turn.ts +6 -2
- package/src/boot/daemon-runtime.ts +48 -16
- 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 +84 -20
- 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/outputs-cli.ts +92 -0
- package/src/cli/persona-cli.ts +29 -0
- package/src/cli/pod-cli.ts +10 -3
- package/src/cli/pr-cli.ts +44 -2
- package/src/cli/private-ledger-commands.ts +54 -4
- package/src/cli/pty-drive-verdict.ts +11 -2
- package/src/cli/release-cli.ts +248 -45
- package/src/cli/rfc-status.ts +91 -0
- package/src/connectors/linear.ts +5 -3
- package/src/context-bus/condense.ts +189 -0
- package/src/context-bus/context-day.ts +82 -0
- package/src/context-bus/context-now-public.ts +86 -0
- package/src/context-bus/context-now-surfaces.ts +70 -4
- package/src/context-bus/context-now.ts +4 -3
- package/src/context-bus/long-term-memory.ts +201 -24
- package/src/context-bus/nightly-condense.ts +1 -1
- 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 +13 -7
- package/src/decisions/decision-ledger.ts +83 -18
- 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-kinds/registry.ts +2 -2
- package/src/graph-runner/graph-approval-card.ts +2 -2
- package/src/graph-runner/graph-grow.ts +64 -11
- package/src/graph-runner/graph-tick.ts +13 -2
- package/src/graph-runner/runner.ts +111 -22
- package/src/harness/harness-cli-command.ts +178 -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 +37 -0
- package/src/harness/harness-queue.ts +494 -0
- package/src/harness/helper-scan.ts +139 -0
- package/src/harness/model-input-observation.ts +37 -0
- package/src/hooks/github-shadow.ts +72 -2
- package/src/hooks/receiver.ts +4 -2
- package/src/hq/hq.ts +318 -0
- package/src/hq/lease.ts +210 -0
- package/src/hq/ledger-write-target.ts +41 -0
- package/src/hq/promote.ts +301 -0
- package/src/index.ts +281 -27
- package/src/intake-plane/discord-seat-work.ts +13 -6
- package/src/intake-plane/linear-wish.ts +72 -0
- package/src/intake-plane/persona-answer.ts +5 -0
- package/src/intake-plane/telegram-seat-work.ts +13 -6
- 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 +180 -0
- package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
- package/src/loops/loop-cli.ts +72 -6
- package/src/loops/neighbors.ts +40 -0
- package/src/loops/observe.ts +59 -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 +180 -0
- package/src/loops/orchestrator/resync-shadow.ts +227 -0
- package/src/loops/orchestrator/tick.ts +302 -0
- package/src/loops/orchestrator/traffic.ts +165 -0
- package/src/loops/package/verify.ts +125 -0
- package/src/loops/registry.ts +454 -9
- package/src/loops/status-rows.ts +118 -0
- package/src/loops/verdict.ts +34 -0
- package/src/maturity/bot-slash-catalog.ts +66 -0
- package/src/maturity/feature-maturity.ts +8 -5
- package/src/memory.ts +37 -4
- package/src/nexus/api/card-followup-route.ts +118 -0
- package/src/nexus/api/dashboard.ts +6 -1
- package/src/nexus/api/decisions-route.ts +68 -0
- package/src/nexus/api/http-server.ts +65 -2
- package/src/nexus/api/merge-approvals-gate.ts +1 -1
- package/src/nexus/api/personas.ts +2 -1
- package/src/nexus/api/schedules-read.ts +5 -1
- package/src/nexus/api/skill-exec-route.ts +97 -0
- package/src/nexus/api/task-cards-api.ts +44 -5
- package/src/nexus/api/workflows.ts +2 -1
- package/src/persona/loader.ts +18 -0
- package/src/persona/persona-todo.ts +14 -9
- package/src/persona/presets.ts +2 -1
- package/src/persona/team.ts +105 -0
- package/src/persona/types.ts +4 -0
- package/src/pod-lease/host-lease.ts +130 -20
- 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/landed-but-yellow.ts +12 -0
- 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 +73 -15
- package/src/seat-dispatch/seat-questions.ts +58 -0
- package/src/seat-loop/seat-loop.ts +495 -46
- package/src/seat-loop/seat-pack.ts +127 -0
- package/src/self-dev/ask-launch-flow.ts +3 -0
- 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/l8-merge-queue.ts +157 -1
- 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/steward/launch.ts +5 -1
- package/src/steward/readiness.ts +125 -0
- package/src/steward/rescue.ts +75 -0
- package/src/steward/triage.ts +48 -9
- package/src/task-cards/card-cli.ts +86 -0
- package/src/task-cards/card-store.ts +10 -6
- package/src/task-orchestrator/surfaces/pod-lease.ts +23 -8
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
- package/src/task-orchestrator/surfaces/pod-pool.ts +27 -10
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +112 -18
- package/src/telegram-commands.ts +27 -3
- package/src/telegram-dispatch-forward.ts +1 -1
- package/src/telegram-fabric-plan.ts +90 -0
- package/src/telegram-loops-command.ts +44 -0
- package/src/telegram-project-command.ts +10 -4
- package/src/telegram.ts +5 -0
- package/src/tool-runtime/elanous-control-runtimes.ts +2 -1
- package/src/user-config.ts +204 -16
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/executor.ts +4 -0
- package/src/workflow-runtime/node-catalog.ts +13 -0
- package/src/workflow-runtime/nodes/knowledge.ts +38 -0
- package/src/workflow-runtime/schema.ts +17 -1
- package/src/workflow-runtime/storage.ts +31 -13
- package/src/workflow-runtime/types.ts +13 -1
- package/apps/pwa/out/_next/static/-6XrAoD3eU7NyMHAE4xPS/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1147-466ec15afacc1a65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1394-ee3795b112907122.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-2e522a8ac5f2afde.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/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/6751-bb3595948b2e311b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7277-0b2fc7c16ba2d64b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7758-0b018a9e4fc56200.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-0259994816604c49.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/9920-5cfdc5e8fd209825.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-cb169620945d1952.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cefe8169e36cf62a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d2e35228bc497119.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5c05b0da4bc1392c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e0ccdb576812b523.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-813c4a52c9d05ca8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4731f2c215358937.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-a022b257950f39e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-a2ffc717e04b8b0a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-b59ee4e70fc18fcf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-c172dabd796eb7a3.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-1efd0cb9e0ed9d13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d5dc7ff69a37b4cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-e71c84189e8c683e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-1d6a533ce5a6efcb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-dfca9b7d2de0b135.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-6dd3bfbbde2a5c2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-490653e8d4cc8852.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-77447356baad75d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-29e961191f6057df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-c822f55b884d9104.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fa4ccfc432424e52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-6d604227e1664b71.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-9c7e8f744a765daf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-4048543b4b7ad5e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4c3a3126abed551a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-64b0aecb1ca75e75.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4292d0149020e8a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-db14d0be7f1c460c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-e5d8072cd7cfef73.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-9547b5cec7cf76bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-e6dcb83265530636.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-45248f08d6aecae5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-2cddeb9e2aeedc55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-baba2e047872fb0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-e895da4ec9cac345.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-4344faf20216233b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-698bb29f11864b89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-bd6a6dc1ddc273f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2e9e2fb01c9da1c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d62ceb13411ebba4.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/{-6XrAoD3eU7NyMHAE4xPS → x6mjI6vqOkVVu0jFuDkz1}/_ssgManifest.js +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { NEXT_MD_PATH, resolveNextMdConflict } from '../release-loop/next-md-merge.js';
|
|
6
|
+
import { getDefaultLogStore } from '../mss/logging/log-store.js';
|
|
6
7
|
|
|
7
8
|
type Command = (bin: string, args: readonly string[], cwd: string) => { status: number | null; stdout: string; stderr: string };
|
|
8
9
|
export type L8MergeQueueInput = { number: number; cwd: string; matchHeadCommit: string };
|
|
@@ -13,6 +14,161 @@ export type L8MergeQueueDeps = {
|
|
|
13
14
|
acquire?: (cwd: string) => Promise<() => void>;
|
|
14
15
|
};
|
|
15
16
|
|
|
17
|
+
export type L8ShadowVerdict = { number: number; head: string; base?: string; verdict: 'pass' | 'conflict' | 'fail'; detail?: string; tests: string[]; at: string };
|
|
18
|
+
export type L8ShadowQueueState = { pending: { number: number; head: string }[]; verdicts: L8ShadowVerdict[] };
|
|
19
|
+
export type L8ShadowQueueDeps = L8MergeQueueDeps & { observe?: (verdict: L8ShadowVerdict) => void | Promise<void> };
|
|
20
|
+
|
|
21
|
+
function shadowCommand(execute: Command, bin: string, args: string[], cwd: string): string {
|
|
22
|
+
const result = execute(bin, args, cwd);
|
|
23
|
+
if (result.status !== 0) throw new Error(`${bin} ${args.join(' ')}: ${(result.stderr || result.stdout || `exit ${result.status}`).trim().slice(0, 500)}`);
|
|
24
|
+
return args.includes('-z') ? result.stdout : result.stdout.trim();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function shadowQueuePath(cwd: string, execute: Command): string {
|
|
28
|
+
const dir = shadowCommand(execute, 'git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], cwd);
|
|
29
|
+
if (!dir) throw new Error('merge queue common git directory unavailable');
|
|
30
|
+
return join(dir, 'elanous-l8-shadow-queue.json');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readShadowQueue(path: string): L8ShadowQueueState {
|
|
34
|
+
if (!existsSync(path)) return { pending: [], verdicts: [] };
|
|
35
|
+
const value: unknown = JSON.parse(readFileSync(path, 'utf8'));
|
|
36
|
+
if (!value || typeof value !== 'object' || !('pending' in value) || !Array.isArray(value.pending)
|
|
37
|
+
|| !value.pending.every((p: { number?: number; head?: string }) => Number.isSafeInteger(p?.number) && p.number! > 0 && typeof p.head === 'string' && /^[0-9a-f]{40}$/i.test(p.head))
|
|
38
|
+
|| !('verdicts' in value) || !Array.isArray(value.verdicts)) throw new Error('invalid shadow queue ledger');
|
|
39
|
+
return value as L8ShadowQueueState;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function writeShadowQueue(path: string, state: L8ShadowQueueState): void {
|
|
43
|
+
const temp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
44
|
+
try {
|
|
45
|
+
writeFileSync(temp, JSON.stringify(state, null, 2) + '\n', { flag: 'wx' });
|
|
46
|
+
renameSync(temp, path);
|
|
47
|
+
} finally { rmSync(temp, { force: true }); }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function withShadowQueue<T>(cwd: string, deps: L8ShadowQueueDeps, fn: (state: L8ShadowQueueState, path: string) => Promise<T>): Promise<T> {
|
|
51
|
+
const release = await (deps.acquire ?? acquireL8MergeQueue)(cwd);
|
|
52
|
+
try {
|
|
53
|
+
const path = shadowQueuePath(cwd, deps.command ?? command);
|
|
54
|
+
return await fn(readShadowQueue(path), path);
|
|
55
|
+
} finally { release(); }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function shadowPrNumber(raw: string): number {
|
|
59
|
+
const text = raw.replace(/^#/, '');
|
|
60
|
+
const number = Number(text);
|
|
61
|
+
if (!/^[1-9]\d*$/.test(text) || !Number.isSafeInteger(number)) throw new Error('expected a positive PR number');
|
|
62
|
+
return number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function statusL8ShadowQueue(cwd: string, deps: L8ShadowQueueDeps = {}): Promise<L8ShadowQueueState> {
|
|
66
|
+
return withShadowQueue(cwd, deps, async (state) => state);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function enqueueL8ShadowQueue(cwd: string, number: number, deps: L8ShadowQueueDeps = {}): Promise<L8ShadowQueueState> {
|
|
70
|
+
if (!Number.isSafeInteger(number) || number <= 0) throw new Error('expected a positive PR number');
|
|
71
|
+
return withShadowQueue(cwd, deps, async (state, path) => {
|
|
72
|
+
const execute = deps.command ?? command;
|
|
73
|
+
const view = JSON.parse(shadowCommand(execute, 'gh', ['pr', 'view', String(number), '--json', 'headRefOid,baseRefName,state,isDraft,isCrossRepository'], cwd)) as {
|
|
74
|
+
headRefOid?: string; baseRefName?: string; state?: string; isDraft?: boolean; isCrossRepository?: boolean;
|
|
75
|
+
};
|
|
76
|
+
if (!view.headRefOid || !/^[0-9a-f]{40}$/i.test(view.headRefOid) || view.baseRefName !== 'main'
|
|
77
|
+
|| view.state !== 'OPEN' || view.isDraft !== false || view.isCrossRepository !== false) throw new Error('PR is not an open, ready, same-repository main PR');
|
|
78
|
+
if (state.pending.some((p) => p.number === number)) throw new Error(`PR #${number} already queued`);
|
|
79
|
+
state.pending.push({ number, head: view.headRefOid });
|
|
80
|
+
writeShadowQueue(path, state);
|
|
81
|
+
return state;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Evaluate one pinned queue head against freshly fetched main. Never push or call a merge API. */
|
|
86
|
+
export async function runL8ShadowQueue(cwd: string, deps: L8ShadowQueueDeps = {}): Promise<L8ShadowVerdict | null> {
|
|
87
|
+
return withShadowQueue(cwd, deps, async (state, path) => {
|
|
88
|
+
const item = state.pending[0];
|
|
89
|
+
if (!item) return null;
|
|
90
|
+
const execute = deps.command ?? command;
|
|
91
|
+
const run = (bin: string, args: string[], where = cwd) => shadowCommand(execute, bin, args, where);
|
|
92
|
+
const verdict: L8ShadowVerdict = { number: item.number, head: item.head, verdict: 'fail', tests: [], at: new Date().toISOString() };
|
|
93
|
+
const view = JSON.parse(run('gh', ['pr', 'view', String(item.number), '--json', 'headRefOid,baseRefName,state,isDraft,isCrossRepository'])) as {
|
|
94
|
+
headRefOid?: string; baseRefName?: string; state?: string; isDraft?: boolean; isCrossRepository?: boolean;
|
|
95
|
+
};
|
|
96
|
+
if (view.headRefOid !== item.head || view.baseRefName !== 'main' || view.state !== 'OPEN'
|
|
97
|
+
|| view.isDraft !== false || view.isCrossRepository !== false) throw new Error('queued PR changed or is not ready');
|
|
98
|
+
run('git', ['fetch', 'origin', 'refs/heads/main']);
|
|
99
|
+
const base = run('git', ['rev-parse', 'FETCH_HEAD']);
|
|
100
|
+
if (!/^[0-9a-f]{40}$/i.test(base)) throw new Error('main commit unavailable');
|
|
101
|
+
verdict.base = base;
|
|
102
|
+
run('git', ['fetch', 'origin', `refs/pull/${item.number}/head`]);
|
|
103
|
+
if (run('git', ['rev-parse', 'FETCH_HEAD']) !== item.head) throw new Error('fetched PR head changed');
|
|
104
|
+
let temp: string | undefined;
|
|
105
|
+
let attached = false;
|
|
106
|
+
try {
|
|
107
|
+
temp = mkdtempSync(join(tmpdir(), 'elanous-l8-shadow-'));
|
|
108
|
+
run('git', ['worktree', 'add', '--detach', temp, base]);
|
|
109
|
+
attached = true;
|
|
110
|
+
const fork = run('git', ['merge-base', base, item.head], temp);
|
|
111
|
+
const changed = run('git', ['diff', '--name-only', '-z', fork, item.head], temp).split('\0').filter(Boolean);
|
|
112
|
+
if (!changed.length) throw new Error('PR changed-file paths unavailable');
|
|
113
|
+
const diff = run('git', ['diff', '--name-only', '-z', '--diff-filter=ACMRT', fork, item.head], temp);
|
|
114
|
+
const files = diff.split('\0').filter(Boolean);
|
|
115
|
+
if (changed.some((file) => file.startsWith('-') || file.startsWith('/') || file.split('/').includes('..') || file.includes('\\'))) throw new Error('unsafe changed-file path');
|
|
116
|
+
verdict.tests = files.filter((file) => /(?:^|\/)[^/]+\.(?:test|spec)\.(?:[cm]?[jt]sx?)$/.test(file));
|
|
117
|
+
const merge = execute('git', ['-c', 'user.name=elanous shadow', '-c', 'user.email=shadow@localhost', 'merge', '--no-ff', '--no-edit', item.head], temp);
|
|
118
|
+
if (merge.status !== 0) {
|
|
119
|
+
const unresolved = run('git', ['diff', '--name-only', '--diff-filter=U'], temp);
|
|
120
|
+
if (!unresolved) throw new Error(`git merge failed: ${merge.stderr.trim() || `exit ${merge.status}`}`);
|
|
121
|
+
verdict.verdict = 'conflict';
|
|
122
|
+
verdict.detail = unresolved;
|
|
123
|
+
} else {
|
|
124
|
+
if (verdict.tests.length) {
|
|
125
|
+
if (verdict.tests.some((file) => !existsSync(join(temp!, file)))) throw new Error('changed test path unavailable');
|
|
126
|
+
run('bun', ['install', '--frozen-lockfile'], temp);
|
|
127
|
+
const result = execute('bun', ['run', 'test:deterministic', ...verdict.tests], temp);
|
|
128
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
129
|
+
const counts = /\b(\d+) pass\b[\s\S]*?\b(\d+) fail\b[\s\S]*?Ran ([1-9]\d*) tests? across ([1-9]\d*) files?/.exec(output);
|
|
130
|
+
if (!counts || Number(counts[1]) + Number(counts[2]) !== Number(counts[3])
|
|
131
|
+
|| Number(counts[4]) !== verdict.tests.length || (result.status !== 0 && Number(counts[2]) === 0)) {
|
|
132
|
+
throw new Error(`changed tests unmeasured: ${output.slice(-400)}`);
|
|
133
|
+
}
|
|
134
|
+
if (Number(counts[2]) > 0 || Number(counts[1]) === 0) {
|
|
135
|
+
verdict.detail = `changed tests failed: ${output.slice(-400)}`;
|
|
136
|
+
} else {
|
|
137
|
+
verdict.verdict = 'pass';
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
verdict.verdict = 'pass';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} finally {
|
|
144
|
+
try {
|
|
145
|
+
if (attached && temp) run('git', ['worktree', 'remove', '--force', temp]);
|
|
146
|
+
} finally {
|
|
147
|
+
if (temp) rmSync(temp, { recursive: true, force: true });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (verdict.verdict === 'pass') {
|
|
151
|
+
run('git', ['fetch', 'origin', 'refs/heads/main']);
|
|
152
|
+
if (run('git', ['rev-parse', 'FETCH_HEAD']) !== verdict.base) throw new Error('main advanced during shadow verification');
|
|
153
|
+
const current = JSON.parse(run('gh', ['pr', 'view', String(item.number), '--json', 'headRefOid,baseRefName,state,isDraft,isCrossRepository'])) as {
|
|
154
|
+
headRefOid?: string; baseRefName?: string; state?: string; isDraft?: boolean; isCrossRepository?: boolean;
|
|
155
|
+
};
|
|
156
|
+
if (current.headRefOid !== item.head || current.baseRefName !== 'main' || current.state !== 'OPEN'
|
|
157
|
+
|| current.isDraft !== false || current.isCrossRepository !== false) throw new Error('PR changed during shadow verification');
|
|
158
|
+
}
|
|
159
|
+
// A rejected insert leaves the pinned item queued for a later evaluation.
|
|
160
|
+
await (deps.observe ?? ((entry) => {
|
|
161
|
+
const store = getDefaultLogStore();
|
|
162
|
+
if (!store) throw new Error('merge-queue observation store unavailable');
|
|
163
|
+
store.insertBatch([{ rec: { ts: new Date().toISOString(), category: 'merge-queue', event: 'shadow-verdict', data: entry }, surface: 'merge-queue' }]);
|
|
164
|
+
}))(verdict);
|
|
165
|
+
state.pending.shift();
|
|
166
|
+
state.verdicts.push(verdict);
|
|
167
|
+
writeShadowQueue(path, state);
|
|
168
|
+
return verdict;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
16
172
|
const command: Command = (bin, args, cwd) => {
|
|
17
173
|
const r = spawnSync(bin, [...args], { cwd, encoding: 'utf8', timeout: 600_000, maxBuffer: 32 * 1024 * 1024 });
|
|
18
174
|
return { status: r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + (r.error?.message ?? '') };
|
|
@@ -68,6 +68,7 @@ import { decideLineageSupersede, lineageSupersedeCloseComment, type LineageSuper
|
|
|
68
68
|
import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
69
69
|
import { enqueueControlMemo, readSoftStopRequestStatus, type SoftStopRequestRead } from '../harness/control-inbox.js';
|
|
70
70
|
import { debug } from '../debug/log.js';
|
|
71
|
+
import { recordFailureEvent } from './heal-intake.js';
|
|
71
72
|
import { releasePathHold, releasePathHoldComment, RELEASE_PATH_LABEL } from '../self-dev/release-path-guard.js';
|
|
72
73
|
import { emitDecision } from '../live/detail-switch.js';
|
|
73
74
|
import { reworkDecision } from './decision-events.js';
|
|
@@ -83,6 +84,7 @@ import { decomposeSelfDevGoal, inferHotPaths, type SelfDevDecomposeOptions } fro
|
|
|
83
84
|
import { boundaryGlobs, boundaryViolations } from './goal-boundary.js';
|
|
84
85
|
import { askAtExecution, type ExecutionClarificationResult } from './execution-clarification.js';
|
|
85
86
|
import { targetScopedGoalText } from './goal-text-path-scope.js';
|
|
87
|
+
import { dropStaleReverts, type RevertGuardResult } from './revert-guard.js';
|
|
86
88
|
import type { SelfDevGoalType } from '../self-dev/orchestrate.js';
|
|
87
89
|
import { DEFAULT_BRANCH_WORKTREE_BASE, gateGitResidue, observeGitResidue } from '../git-fs/worktree.js';
|
|
88
90
|
import { runGitCommand } from '../git-fs/runner.js';
|
|
@@ -96,6 +98,8 @@ import { getHarnessSpace, normalizeSpaceId, resolveRunIdentity } from '../harnes
|
|
|
96
98
|
import { getElanousConfigDir } from '../elanous-config-dir.js';
|
|
97
99
|
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
98
100
|
import { addNextMdReleaseNote, harnessReleaseNote, parseReleaseNoteSection, releaseNotesDir, renderReleaseNoteSection, writeReleaseNote, type ReleaseNoteFragment, type ReleaseNoteSection } from '../release-loop/release-note.js';
|
|
101
|
+
import { landingFreezeMessage } from '../release-loop/landing-freeze.js';
|
|
102
|
+
import { admitLandingMerge, owningRepoRoot } from './frozen-merges.js';
|
|
99
103
|
import { plannedSelfImplBranch } from '../harness/worktree-branch-prefix.js';
|
|
100
104
|
export { slugifyFeature } from '../harness/worktree-branch-prefix.js';
|
|
101
105
|
import { AUTO_REVIEW_LABEL, resolveAutoReviewLabels } from './context-capsule.js';
|
|
@@ -757,6 +761,33 @@ function declaredPathsFromGoalDocumentText(document: string): { readable: boolea
|
|
|
757
761
|
return parseDeclaredTargetPathsFromAsk(originalAsk);
|
|
758
762
|
}
|
|
759
763
|
|
|
764
|
+
/** Only paths explicitly designated as targets in the ask or piece text protect an intentional revert. */
|
|
765
|
+
function declaredRevertGuardPaths(text: string): string[] {
|
|
766
|
+
const lines = targetScopedGoalText(text).split(/\r?\n/);
|
|
767
|
+
const pieceTargets: string[] = [];
|
|
768
|
+
for (let index = 0; index < lines.length; index++) {
|
|
769
|
+
const line = lines[index]!.replace(/^\s*(?:[-*+]\s+|>\s*)+/, '');
|
|
770
|
+
const declaration = /^(?:조각\s+)?(?:대상\s*경로|target\s*paths?)\s*[::]\s*(.*)$/i.exec(line);
|
|
771
|
+
if (!declaration) continue;
|
|
772
|
+
const fragments = [declaration[1]!];
|
|
773
|
+
while (index + 1 < lines.length) {
|
|
774
|
+
const next = lines[index + 1]!.trim().replace(/^(?:[-*+]\s+|>\s*)+/, '');
|
|
775
|
+
const paths = parseDeclaredTargetPathsFromAsk(`대상 경로: ${next}`).paths;
|
|
776
|
+
const tokens = next.split(/[·,]/).map((part) => part.trim().replace(/^`+|`+$/g, ''));
|
|
777
|
+
if (!tokens.length || !tokens.every((token) => token && paths.includes(normalizeDeclaredScopePath(token)))) break;
|
|
778
|
+
fragments.push(next);
|
|
779
|
+
index++;
|
|
780
|
+
}
|
|
781
|
+
pieceTargets.push(...parseDeclaredTargetPathsFromAsk(`대상 경로: ${fragments.join(',')}`).paths);
|
|
782
|
+
}
|
|
783
|
+
return pieceTargets;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function revertGuardTargetPaths(feature: string, goalDocument?: string): string[] {
|
|
787
|
+
const originalAsk = goalDocument ? extractVerbatimOriginalAsk(goalDocument)?.ask : undefined;
|
|
788
|
+
return [...new Set([...declaredRevertGuardPaths(feature), ...(originalAsk ? declaredRevertGuardPaths(originalAsk) : [])])];
|
|
789
|
+
}
|
|
790
|
+
|
|
760
791
|
export function detectDeclaredScopeDiff(input: {
|
|
761
792
|
goalDocument?: string | null;
|
|
762
793
|
changedFiles?: readonly string[] | null;
|
|
@@ -972,7 +1003,7 @@ export interface SelfImplementSeams {
|
|
|
972
1003
|
/** 변경 전 ref의 runtime source 전체. 생략 시 git에서 읽으며, 읽기 실패는 판정 미지로 남긴다. */
|
|
973
1004
|
reviewBaselineObservationSource?: (cwd: string, base: string) => Promise<string | undefined>;
|
|
974
1005
|
/** ④ gate — cwd 에서 bun test/build. postsync=정합 후 재게이트(merge-base 실행 범위). */
|
|
975
|
-
gate: (cwd: string, ctx?: { runId?: string; mode?: 'postsync' }) => Promise<SelfImplementGateResult>;
|
|
1006
|
+
gate: (cwd: string, ctx?: { runId?: string; mode?: 'postsync'; round?: number }) => Promise<SelfImplementGateResult>;
|
|
976
1007
|
/** Gate가 실행된 worktree HEAD가 local origin/main보다 뒤처진 커밋 수. 실패는 undefined(unknown)로 정직하게 남긴다. */
|
|
977
1008
|
gateWorktreeBehindMain?: (cwd: string) => Promise<number | undefined>;
|
|
978
1009
|
/** 변경 파일 목록 — 게이트 «라우터»가 「코드가 바뀌었나」를 판정하는 입력.
|
|
@@ -1029,6 +1060,8 @@ export interface SelfImplementSeams {
|
|
|
1029
1060
|
/** ★ ⑦ 병합 seam(2026-07-21·auto-merge) — 열린 PR 을 실제 병합(gh pr merge --squash). `matchHeadCommit`이 있으면
|
|
1030
1061
|
* 검사한 고정 head 뒤 변경을 gh가 거부한다. autoMerge + 리뷰 clean 일 때만 호출(outward-facing·main 자율병합). */
|
|
1031
1062
|
mergePr?: (opts: { number: number; cwd: string; matchHeadCommit?: string }) => Promise<{ merged: boolean; baseRefName?: string; mergeCommit?: string; detail?: string }>;
|
|
1063
|
+
/** Only successful merges trigger this injected, fail-closed draft inventory and mutation seam. */
|
|
1064
|
+
supersedeDraftsOnMerge?: (number: number, cwd: string) => Promise<void>;
|
|
1032
1065
|
/** Post-merge cleanup is opt-in and injectable so tests never touch the filesystem. */
|
|
1033
1066
|
postMergeCleanup?: {
|
|
1034
1067
|
enabled: boolean;
|
|
@@ -2551,12 +2584,16 @@ function releaseNoteForRun(goalFile: string | undefined, feature: string): Relea
|
|
|
2551
2584
|
}
|
|
2552
2585
|
|
|
2553
2586
|
/** GATE-CALLERS (OP 10-03): caller tests that exceeded the gate cap must stay visible in the PR body even when the gate log is cut. */
|
|
2587
|
+
export function exposeGateLines(gateLog?: string): string[] {
|
|
2588
|
+
return (gateLog ?? '').split('\n').filter((line) => line.startsWith('[expose]'));
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2554
2591
|
export function gateNotRunSection(gateLog?: string): string[] {
|
|
2555
2592
|
const lines = (gateLog ?? '').split('\n').filter((line) => line.startsWith('⚠️ caller test cap exceeded'));
|
|
2556
2593
|
return lines.length ? ['', '## Gate · 안 돈 소비자 시험(상한 초과)', ...lines.map((line) => `- ${line.replace(/^⚠️ caller test cap exceeded — not run: /, '')}`)] : [];
|
|
2557
2594
|
}
|
|
2558
2595
|
|
|
2559
|
-
function prBody(feature: string, implSummary: string, gateLog?: string, review?: SelfImplementReview, reviewIntent?: string, autoReviewDeclineReasons?: readonly string[], evidence?: string, goalFile?: string, planRevision?: ReworkPlanRevision, releaseNote?: ReturnType<typeof harnessReleaseNote
|
|
2596
|
+
function prBody(feature: string, implSummary: string, gateLog?: string, review?: SelfImplementReview, reviewIntent?: string, autoReviewDeclineReasons?: readonly string[], evidence?: string, goalFile?: string, planRevision?: ReworkPlanRevision, releaseNote?: ReturnType<typeof harnessReleaseNote>, revertGuard?: RevertGuardResult): string {
|
|
2560
2597
|
const note = releaseNote ?? releaseNoteForRun(goalFile, feature);
|
|
2561
2598
|
const relaxation = planRevision?.relaxation
|
|
2562
2599
|
? ['', '## 감독 수용 기준 완화', `- 대상: ${planRevision.relaxation.target}`, `- 이전: ${planRevision.relaxation.expected}`, `- 완화: ${planRevision.relaxation.replacement}`, `- 이유: ${planRevision.reason}`, `- 적용: ${planRevision.application?.status ?? 'failed'}${planRevision.application?.detail ? ` (${planRevision.application.detail})` : ''}`, ...(planRevision.disposition ? [`- 충돌 처분: ${planRevision.disposition}`] : [])]
|
|
@@ -2577,7 +2614,14 @@ function prBody(feature: string, implSummary: string, gateLog?: string, review?:
|
|
|
2577
2614
|
...(review.shouldFix.length ? ['', '**should-fix (비블로킹):**', ...review.shouldFix.slice(0, 8).map((f) => `- ${f}`)] : [])] : []),
|
|
2578
2615
|
...(autoReviewDeclineReasons?.length ? ['', '## Auto-review label not applied', 'The requested auto-review label was declined by the autonomy gate:', ...autoReviewDeclineReasons.map((reason) => `- ${reason}`)] : []),
|
|
2579
2616
|
...gateNotRunSection(gateLog),
|
|
2580
|
-
...(gateLog ? ['', '## Gate',
|
|
2617
|
+
...(gateLog || revertGuard ? ['', '## Gate',
|
|
2618
|
+
// 경고는 잘린 gate 로그의 코드블록 밖에서도 한눈에 보인다.
|
|
2619
|
+
...((gateLog ?? '').split('\n').filter((line) => line.startsWith('⚠️ 동작 검증 0:'))),
|
|
2620
|
+
...exposeGateLines(gateLog),
|
|
2621
|
+
...(gateLog ? ['```', gateLog.trim().slice(0, 3000), '```'] : []),
|
|
2622
|
+
...(revertGuard?.reverted.length ? [`- 되돌림 방지: ${revertGuard.reverted.length}파일 제외`] : []),
|
|
2623
|
+
...(revertGuard?.protected.length ? [`- 되돌림 경고: 대상 경로 ${revertGuard.protected.length}파일 유지 (${revertGuard.protected.join(', ')})`] : []),
|
|
2624
|
+
...(revertGuard?.warning ? [`- 되돌림 검사 실패(커밋 계속): ${revertGuard.warning}`] : [])] : []),
|
|
2581
2625
|
...relaxation,
|
|
2582
2626
|
'',
|
|
2583
2627
|
'🤖 self-implement 오케스트레이터 (elanous 자율 구현·리뷰-게이트 병합)',
|
|
@@ -2807,7 +2851,7 @@ function blockedDraftPrBody(feature: string, implSummary: string, state: Blocked
|
|
|
2807
2851
|
...(state.gate === undefined
|
|
2808
2852
|
? ['(게이트 미실행)']
|
|
2809
2853
|
: state.gate.log
|
|
2810
|
-
? ['```', state.gate.log.trim().slice(0, 3000), '```']
|
|
2854
|
+
? [...state.gate.log.split('\n').filter((line) => line.startsWith('⚠️ 동작 검증 0:')), ...exposeGateLines(state.gate.log), '```', state.gate.log.trim().slice(0, 3000), '```'] // 경고는 절단된 로그 밖에도 보존한다.
|
|
2811
2855
|
: ['(게이트 실행됨 · 로그 없음)']),
|
|
2812
2856
|
'',
|
|
2813
2857
|
'## Main-sync typecheck',
|
|
@@ -4087,6 +4131,19 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
|
|
|
4087
4131
|
throw e;
|
|
4088
4132
|
} finally {
|
|
4089
4133
|
if (loopRegistered) registerRunLoop('ended');
|
|
4134
|
+
const terminalStage = terminalResult?.stage ?? 'crashed';
|
|
4135
|
+
try {
|
|
4136
|
+
const mappedStatus = mapStageToRunStatus(terminalStage)?.runStatus;
|
|
4137
|
+
if (!terminalResult || mappedStatus === 'failed' || (mappedStatus === undefined && terminalResult.ok === false) || signalIncompleteState(roundClassifications).latestSignalIncomplete) {
|
|
4138
|
+
recordFailureEvent({
|
|
4139
|
+
source: 'harness-run', kind: terminalStage, ref: runId,
|
|
4140
|
+
summary: opts.feature.split(/\r?\n/, 1)[0]?.trim() || `Harness run ${runId} failed`,
|
|
4141
|
+
at: new Date().toISOString(),
|
|
4142
|
+
});
|
|
4143
|
+
}
|
|
4144
|
+
} catch (error) {
|
|
4145
|
+
logRunAwareFailSoft(runId, 'heal-intake-record-failed', { stage: terminalStage, error: safeErrorDescription(error) });
|
|
4146
|
+
}
|
|
4090
4147
|
if (terminalResult) await recordGoalExecution(terminalResult);
|
|
4091
4148
|
// ⛔⭐⭐⭐ 종결을 «못 적고» 죽는 길을 막는다 (2026-08-11 · 🅣 71차 · 대표 「종료 때 정리가 안 되면 설계 문제」).
|
|
4092
4149
|
// `terminalResult` 는 «두 자리»에서만 선다 — 정상 종료 ⊕ `StepTimeoutError` 분기.
|
|
@@ -5279,6 +5336,13 @@ async function runSelfImplementInner(
|
|
|
5279
5336
|
let finishedChildCommitMissing = false;
|
|
5280
5337
|
let finishedChildCommitFailure: string | undefined;
|
|
5281
5338
|
let finishedChildCommitted = false;
|
|
5339
|
+
const revertGuard: RevertGuardResult = { reverted: [], protected: [] };
|
|
5340
|
+
const inspectStaleReverts = (targets: readonly string[]): void => {
|
|
5341
|
+
const inspected = dropStaleReverts(wt.path, targets, runId, getUserConfig().harness?.revertGuard?.depth);
|
|
5342
|
+
for (const file of inspected.reverted) if (!revertGuard.reverted.includes(file)) revertGuard.reverted.push(file);
|
|
5343
|
+
for (const file of inspected.protected) if (!revertGuard.protected.includes(file)) revertGuard.protected.push(file);
|
|
5344
|
+
if (inspected.warning) revertGuard.warning = inspected.warning;
|
|
5345
|
+
};
|
|
5282
5346
|
const classifyUnfinishedRun = (stage: SelfImplementStage, verdict?: ReworkBudgetVerdict, decomposition?: TerminalDecomposition): AbandonedClassificationResult => {
|
|
5283
5347
|
const quotaExhaustionAssessment = quotaExhaustionAssessmentForRun();
|
|
5284
5348
|
const worktree = unfinishedWorktreeObservation(wt.path);
|
|
@@ -6306,6 +6370,7 @@ async function runSelfImplementInner(
|
|
|
6306
6370
|
.filter((path) => !harnessSeededPaths.includes(path)
|
|
6307
6371
|
&& targets.some((target) => changedFileIsInsideDeclaredTarget(path, target)));
|
|
6308
6372
|
if (eligible.length) {
|
|
6373
|
+
inspectStaleReverts(revertGuardTargetPaths(opts.feature, goalDocument));
|
|
6309
6374
|
const message = `self-implement: ${prTitle(opts.feature)}`;
|
|
6310
6375
|
let outcome: { ok: boolean; out: string };
|
|
6311
6376
|
try {
|
|
@@ -6514,7 +6579,7 @@ async function runSelfImplementInner(
|
|
|
6514
6579
|
return { ...stoppedBeforeGate, ...resolveRunOutcome({ termination: 'abandoned' }), sessionId, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(review ? { review } : {}) };
|
|
6515
6580
|
}
|
|
6516
6581
|
onNodeEntry(node('gate'), round);
|
|
6517
|
-
gate = await stepTimeout(s.gate(wt.path, { runId }), T.gate, 'gate');
|
|
6582
|
+
gate = await stepTimeout(s.gate(wt.path, { runId, round }), T.gate, 'gate');
|
|
6518
6583
|
} else {
|
|
6519
6584
|
observe('gate-skipped-by-graph', { ...graphAuthorityFields(graphAuthority, graphTemplate), round, reason: gateRoute.reason, changedFileCount: gateRouteFiles?.length ?? null });
|
|
6520
6585
|
gate = {
|
|
@@ -7106,6 +7171,11 @@ async function runSelfImplementInner(
|
|
|
7106
7171
|
|
|
7107
7172
|
if (s.mergeMain && s.commitWork) {
|
|
7108
7173
|
onNodeEntry('main-sync', round);
|
|
7174
|
+
let goalDocument: string | undefined;
|
|
7175
|
+
if (opts.goalFile) {
|
|
7176
|
+
try { goalDocument = readFileSync(opts.goalFile, 'utf8'); } catch { /* no documented targets */ }
|
|
7177
|
+
}
|
|
7178
|
+
inspectStaleReverts(revertGuardTargetPaths(opts.feature, goalDocument));
|
|
7109
7179
|
s.commitWork(wt.path, prTitle(opts.feature));
|
|
7110
7180
|
// ⛔⭐ resume 경로와 «같은» 해석기를 쓴다 — 두 자리가 갈리면 같은 원인이 다른 얼굴로 나온다.
|
|
7111
7181
|
const resolvedTarget = resolveDefaultBranchTarget(s.defaultBranchRef ?? defaultBranchRef, wt.path);
|
|
@@ -7139,7 +7209,7 @@ async function runSelfImplementInner(
|
|
|
7139
7209
|
? `${mergeTarget} 충돌해결됨 — ${formatLlmMergeOutcome(sync)} — 통합 결과 full 재-gate…`
|
|
7140
7210
|
: `${mergeTarget} 정합됨 — 통합 결과 full 재-gate…`);
|
|
7141
7211
|
onNodeEntry('regate', round);
|
|
7142
|
-
const regate = await withStepTimeout(s.gate(wt.path, { runId, mode: 'postsync' }), T.gate, 'gate');
|
|
7212
|
+
const regate = await withStepTimeout(s.gate(wt.path, { runId, mode: 'postsync', round }), T.gate, 'gate');
|
|
7143
7213
|
if (!regate.passed) await onFailureVerdict('gate');
|
|
7144
7214
|
// `mode` name/meaning stay: `full` = existing gate including tests. Both
|
|
7145
7215
|
// conflict-resolved and clean-merge reuse this same `s.gate` invocation.
|
|
@@ -7269,7 +7339,7 @@ async function runSelfImplementInner(
|
|
|
7269
7339
|
addNextMdReleaseNote(wt.path, releaseNote);
|
|
7270
7340
|
const preparedPrBody = preparePrBody(
|
|
7271
7341
|
[
|
|
7272
|
-
prBody(opts.feature, impl.summary, gate.log, review, reviewIntent, declineReasons, harvestedForPr(), opts.goalFile, lastPlanRevision, releaseNote),
|
|
7342
|
+
prBody(opts.feature, impl.summary, gate.log, review, reviewIntent, declineReasons, harvestedForPr(), opts.goalFile, lastPlanRevision, releaseNote, revertGuard),
|
|
7273
7343
|
...(prEvidenceDecision.body ? ['', '---', '', prEvidenceDecision.body] : []),
|
|
7274
7344
|
].join('\n'),
|
|
7275
7345
|
s.persistPrBodyArtifact,
|
|
@@ -7418,11 +7488,45 @@ async function runSelfImplementInner(
|
|
|
7418
7488
|
progress('pr-opened', `호스트 재게이트 대기 (#${pr.number})`);
|
|
7419
7489
|
return { ok: true, stage: 'merge-ready', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), checkedHeadCommit: checkedHeadCommit!, prUrl: pr.url, prNumber: pr.number };
|
|
7420
7490
|
}
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
const
|
|
7491
|
+
// Landing freeze: the in-flight marker is written before the freeze is read, so a `freeze on` racing this merge waits.
|
|
7492
|
+
// The resume sweep merges from the repository that owns the run worktree (the worktree itself may be cleaned up).
|
|
7493
|
+
const heldEntry = () => {
|
|
7494
|
+
if (!checkedHeadCommit || !/^[0-9a-f]{40}$/i.test(checkedHeadCommit)) return null;
|
|
7495
|
+
const owner = owningRepoRoot(wt.path);
|
|
7496
|
+
// Without a known owning repository the held PR stays queued for a person; no sweep resumes it from a worktree.
|
|
7497
|
+
return owner ? { prNumber: pr.number, headCommit: checkedHeadCommit, repoRoot: owner } : { prNumber: pr.number, headCommit: checkedHeadCommit, repoRoot: wt.path, manual: true as const };
|
|
7498
|
+
};
|
|
7499
|
+
const landing = admitLandingMerge(heldEntry, undefined, {}, { prNumber: pr.number, repoRoot: owningRepoRoot(wt.path) ?? wt.path, ...(checkedHeadCommit ? { headCommit: checkedHeadCommit } : {}) });
|
|
7500
|
+
if (landing.kind === 'held' && !(checkedHeadCommit && /^[0-9a-f]{40}$/i.test(checkedHeadCommit))) {
|
|
7501
|
+
return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason: 'merge-guard-unevaluated', prUrl: pr.url, prNumber: pr.number, detail: 'freeze: checked head SHA unavailable; PR left ready for review' };
|
|
7502
|
+
}
|
|
7503
|
+
if (landing.kind !== 'merge') {
|
|
7504
|
+
const detail = landing.kind === 'held' ? landingFreezeMessage(landing.freeze) : 'freeze lifted; a resume sweep is merging this PR';
|
|
7505
|
+
debug.log('harness.merge', 'frozen', { pr: pr.number, ...(landing.kind === 'held' ? { reason: landing.freeze.reason, until: landing.freeze.until } : { resumedElsewhere: true }) });
|
|
7506
|
+
progress('pr-opened', `#${pr.number} ready · ${detail}`);
|
|
7507
|
+
return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason: landing.kind === 'held' ? 'frozen' : 'resumed-elsewhere', prUrl: pr.url, prNumber: pr.number, detail };
|
|
7508
|
+
}
|
|
7509
|
+
// The marker and claim live until the merge call itself settles — a step timeout does not stop a merge already
|
|
7510
|
+
// sent — and anything that throws before the merge call is made releases them.
|
|
7511
|
+
let merging: ReturnType<NonNullable<typeof s.mergePr>>;
|
|
7512
|
+
try {
|
|
7513
|
+
onNodeEntry('merge', round);
|
|
7514
|
+
progress('merging', '자동 병합 (리뷰 clean·squash)…');
|
|
7515
|
+
merging = s.mergePr!({ number: pr.number, cwd: wt.path, matchHeadCommit: checkedHeadCommit! });
|
|
7516
|
+
} catch (error) { landing.end(); throw error; }
|
|
7517
|
+
// The default merge seam resolves only after `gh pr view` reads MERGED, so settling is the confirmation.
|
|
7518
|
+
const release = (merged: boolean) => {
|
|
7519
|
+
try { landing.end(merged); } catch (error) { observe('landing-release-failed', { number: pr.number, error: safeErrorDescription(error) }, { level: 'error' }); }
|
|
7520
|
+
};
|
|
7521
|
+
void merging.then((outcome) => release(outcome.merged === true), () => release(false));
|
|
7522
|
+
const m = await withStepTimeout(merging, T.pr, 'pr');
|
|
7424
7523
|
observe('merged', { number: pr.number, merged: m.merged, detail: m.detail ?? null });
|
|
7425
7524
|
if (m.merged) {
|
|
7525
|
+
try {
|
|
7526
|
+
await s.supersedeDraftsOnMerge?.(pr.number, wt.path);
|
|
7527
|
+
} catch (error) {
|
|
7528
|
+
observe('draft-supersede-on-merge-failed', { number: pr.number, error: safeErrorDescription(error) }, { level: 'warn' });
|
|
7529
|
+
}
|
|
7426
7530
|
const directory = releaseNotesDir(elanousStateRoot());
|
|
7427
7531
|
const fragment: ReleaseNoteFragment = {
|
|
7428
7532
|
pr: pr.number,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { lstatSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { debug } from '../debug/log.js';
|
|
5
|
+
|
|
6
|
+
export type RevertGuardResult = { reverted: string[]; protected: string[]; warning?: string };
|
|
7
|
+
|
|
8
|
+
/** Compare the worktree's exact bytes with blobs from recent HEAD ancestry, before add -A. */
|
|
9
|
+
export function dropStaleReverts(cwd: string, targets: readonly string[], runId: string, depth = 50): RevertGuardResult {
|
|
10
|
+
const result: RevertGuardResult = { reverted: [], protected: [] };
|
|
11
|
+
const git = (...args: string[]): Buffer => execFileSync('git', args, { cwd, timeout: 20_000, maxBuffer: 16 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
12
|
+
const reason = (error: unknown): string => (error instanceof Error ? error.message : String(error)).replace(/\s+/g, ' ').slice(0, 240);
|
|
13
|
+
try {
|
|
14
|
+
const limit = Number.isSafeInteger(depth) && depth > 0 ? depth : 50;
|
|
15
|
+
const commits = git('rev-list', `--max-count=${limit + 1}`, 'HEAD').toString('utf8').trim().split('\n');
|
|
16
|
+
const changed = git('diff', '--name-only', '--diff-filter=M', '-z', 'HEAD', '--').toString('utf8').split('\0').filter(Boolean);
|
|
17
|
+
const staged = git('diff', '--cached', '--name-only', '--diff-filter=M', '-z', 'HEAD', '--').toString('utf8').split('\0').filter(Boolean);
|
|
18
|
+
for (const file of new Set([...changed, ...staged])) {
|
|
19
|
+
try {
|
|
20
|
+
if (!lstatSync(join(cwd, file)).isFile()) continue;
|
|
21
|
+
const current = readFileSync(join(cwd, file));
|
|
22
|
+
const rawOid = execFileSync('git', ['hash-object', '--stdin'], { cwd, input: current, timeout: 20_000 }).toString('utf8').trim();
|
|
23
|
+
const headEntry = git('ls-tree', 'HEAD', '--', file).toString('utf8').trim();
|
|
24
|
+
const head = /^\d+ blob ([0-9a-f]+)\t/.exec(headEntry)?.[1];
|
|
25
|
+
if (!head) continue;
|
|
26
|
+
const sameBytes = (oid: string): boolean => oid === rawOid || git('cat-file', 'blob', oid).equals(current);
|
|
27
|
+
if (sameBytes(head)) continue;
|
|
28
|
+
let matchedCommit: string | undefined;
|
|
29
|
+
const inspectedOids = new Set<string>();
|
|
30
|
+
for (const commit of commits.slice(1)) {
|
|
31
|
+
// A path may not have existed in an ancestor; that is not a failed inspection.
|
|
32
|
+
const blob = git('ls-tree', commit, '--', file).toString('utf8').trim();
|
|
33
|
+
const oid = /^\d+ blob ([0-9a-f]+)\t/.exec(blob)?.[1];
|
|
34
|
+
if (oid && !inspectedOids.has(oid)) {
|
|
35
|
+
if (sameBytes(oid)) { matchedCommit = commit; break; }
|
|
36
|
+
inspectedOids.add(oid);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!matchedCommit) continue;
|
|
40
|
+
const scoped = targets.some((target) => {
|
|
41
|
+
const path = target.replace(/^\.\//, '').replace(/\/+$/, '');
|
|
42
|
+
return path !== '' && (file === path || file.startsWith(`${path}/`));
|
|
43
|
+
});
|
|
44
|
+
if (scoped) {
|
|
45
|
+
result.protected.push(file);
|
|
46
|
+
debug.log('self-implement.revert-guard', 'protected-target', { runId, files: [file], matchedCommit }, { level: 'warn' });
|
|
47
|
+
} else {
|
|
48
|
+
git('checkout', 'HEAD', '--', file);
|
|
49
|
+
result.reverted.push(file);
|
|
50
|
+
debug.log('self-implement.revert-guard', 'reverted-stale', { runId, files: [file], matchedCommit });
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
result.warning = `git inspection/checkout failed for ${file}: ${reason(error)}`;
|
|
54
|
+
debug.log('self-implement.revert-guard', 'fail-open', { runId, reason: result.warning }, { level: 'warn' });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch (error) {
|
|
58
|
+
result.warning = `git inspection failed: ${reason(error)}`;
|
|
59
|
+
debug.log('self-implement.revert-guard', 'fail-open', { runId, reason: result.warning }, { level: 'warn' });
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
@@ -276,7 +276,7 @@ interface RunLedgerLookupTarget {
|
|
|
276
276
|
targetName: string | null;
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
function resolveFederatedRunLedgerTargets(options: Pick<FederatedRunLedgerLookupOptions, 'includeTest' | 'targets'>): RunLedgerLookupTarget[] {
|
|
279
|
+
export function resolveFederatedRunLedgerTargets(options: Pick<FederatedRunLedgerLookupOptions, 'includeTest' | 'targets'>): RunLedgerLookupTarget[] {
|
|
280
280
|
const resolved = options.targets ? { targets: [...options.targets] } : resolveLogTargets({ all: true, includeTest: options.includeTest });
|
|
281
281
|
if (resolved.error) throw new Error(resolved.error);
|
|
282
282
|
const targets = new Map<string, string>();
|
|
@@ -37,7 +37,8 @@ import { iosFilesIn, runIosUnitTestGate } from '../../scripts/ci-ios-unit-tests.
|
|
|
37
37
|
import { detectGateCommand } from './detect-gate.js';
|
|
38
38
|
import { writeScopedTypecheckConfig } from './scoped-typecheck-config.js';
|
|
39
39
|
// ⭐ 게이트 스코프 근본 수리(2026-07-26) — 풀 `bun test` 폴백 제거 + 연관 테스트 유도(순수).
|
|
40
|
-
import { resolveGateScope } from './gate-scope.js';
|
|
40
|
+
import { formatNoRelatedTestsWarning, resolveGateScope } from './gate-scope.js';
|
|
41
|
+
import { publicExposureFiles, runExposeGate } from './expose-gate.js';
|
|
41
42
|
import { buildImporterTestIndex, isTestPath } from './importer-test-index.js';
|
|
42
43
|
import { buildRouteConsumerTestIndex } from './route-consumer-test-index.js';
|
|
43
44
|
import { countTestDeclarations } from './test-declarations.js';
|
|
@@ -976,6 +977,7 @@ export interface DefaultSeamsOptions {
|
|
|
976
977
|
runIosUnitTestGate?: (io: { args: readonly string[]; cwd: string }) => number;
|
|
977
978
|
/** Source-policy gate adapter for focused seam tests. */
|
|
978
979
|
runHarnessPolicyGates?: typeof runHarnessPolicyGates;
|
|
980
|
+
runExposeGate?: typeof runExposeGate;
|
|
979
981
|
/** Dependency-wide validation adapter for focused seam tests. */
|
|
980
982
|
runDependencyChangeGate?: typeof runDependencyChangeGate;
|
|
981
983
|
/** 워크트리 시험 묶음(없으면 실패 파일)을 base에서 재실행하는 seam. 실패 경로에서만 호출된다. */
|
|
@@ -1409,6 +1411,10 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
1409
1411
|
},
|
|
1410
1412
|
removeMatchingGoalCopy,
|
|
1411
1413
|
},
|
|
1414
|
+
supersedeDraftsOnMerge: async (number, cwd) => {
|
|
1415
|
+
const { supersedeMergedGoalDrafts } = await import('../harness/harness-cli-command.js');
|
|
1416
|
+
await supersedeMergedGoalDrafts(number, cwd);
|
|
1417
|
+
},
|
|
1412
1418
|
lineageSupersede: {
|
|
1413
1419
|
listOpenDrafts: () => listOpenDraftsForLineage(),
|
|
1414
1420
|
readRunLedger: (runId) => {
|
|
@@ -1810,8 +1816,13 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
1810
1816
|
const observationChanges = gateChangedFiles(cwd, comparisonBase);
|
|
1811
1817
|
const observationChangedFiles = observationChanges.files;
|
|
1812
1818
|
const postsync = ctx?.mode === 'postsync';
|
|
1813
|
-
//
|
|
1814
|
-
|
|
1819
|
+
// 일반 게이트는 기존 worktree 범위를 유지하되 fork 이후 커밋된 시험을 더한다.
|
|
1820
|
+
// HEAD 대비 미커밋 목록만 쓰면 릴리스 노트만 남은 재작업 라운드에서
|
|
1821
|
+
// 커밋된 시험을 놓쳐 docs-only 로 건너뛴다. postsync는 종전처럼 전 변경을 잰다.
|
|
1822
|
+
const changedAll = postsync ? observationChangedFiles : [...new Set([
|
|
1823
|
+
...gitChangedFiles(cwd),
|
|
1824
|
+
...observationChangedFiles.filter((file) => isTestPath(file)),
|
|
1825
|
+
])];
|
|
1815
1826
|
// ⚠️ **regular-file 판정**(리뷰 should-fix 5R) — `existsSync` 는 **디렉터리도 true** 다.
|
|
1816
1827
|
// 계약은 "실존 테스트 **파일**"이므로 파일 여부까지 확인한다(디렉터리를 testArgs 로 넘기면
|
|
1817
1828
|
// 필터 무매치 → "0 files ran" 부당 실패).
|
|
@@ -1824,11 +1835,19 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
1824
1835
|
trackedFiles.filter(isTestPath),
|
|
1825
1836
|
[...new Set([...trackedFiles, ...changedAll, ...observationChangedFiles])].filter((file) => /\.(?:[cm]?[jt]sx?)$/.test(file)),
|
|
1826
1837
|
);
|
|
1827
|
-
|
|
1838
|
+
// 런 전체 재탐색 시 앞 라운드의 변경·삭제 서버 경로도 비교-base 소비자 색인에 있어야 한다.
|
|
1839
|
+
const routeConsumerTestIndex = buildRouteConsumerTestIndex(cwd, trackedFiles.filter(isTestPath),
|
|
1840
|
+
[...new Set([...changedAll, ...observationChangedFiles])], comparisonBase ?? 'HEAD');
|
|
1828
1841
|
// 워크트리에 없는 변경 경로 = 지운 파일 — 미검증으로 세지 않는다(gate-scope `isDeleted` · 2026-09-26 run-7dd4cce6).
|
|
1829
1842
|
const isDeleted = (path: string): boolean => !isFile(path);
|
|
1830
|
-
const scopeOpts = { isDeleted, routeConsumerTestIndex: routeConsumerTestIndex ?? undefined
|
|
1843
|
+
const scopeOpts = { isDeleted, routeConsumerTestIndex: routeConsumerTestIndex ?? undefined,
|
|
1844
|
+
runChanged: observationChangedFiles, round: ctx?.round };
|
|
1831
1845
|
const scope = resolveGateScope(changedAll, isFile, importerTestIndex ?? undefined, postsync ? { ...scopeOpts, mode: 'postsync' } : scopeOpts);
|
|
1846
|
+
// The landing scope is the PR diff, not only this round's uncommitted test files.
|
|
1847
|
+
const expose = publicExposureFiles(observationChangedFiles).length > 0
|
|
1848
|
+
? (o.runExposeGate ?? runExposeGate)(cwd, observationChangedFiles,
|
|
1849
|
+
getUserConfig().harness?.exposeGate === 'strict' ? 'strict' : 'warn')
|
|
1850
|
+
: { passed: true, log: '' };
|
|
1832
1851
|
const comparisonFailureReason = observationChanges.comparisonBaseStatus === 'unavailable'
|
|
1833
1852
|
? 'comparison-base-unavailable'
|
|
1834
1853
|
: observationChanges.comparisonBaseStatus === 'comparison-failed'
|
|
@@ -1865,6 +1884,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
1865
1884
|
debug.log('self-implement', 'gate.scope', {
|
|
1866
1885
|
...(ctx?.runId ? { runId: ctx.runId } : {}),
|
|
1867
1886
|
targeted: gateOpts.testArgs?.length ?? 0, files: (gateOpts.testArgs ?? []).slice(0, 8),
|
|
1887
|
+
'evaluated-changes': changedAll.slice(0, 8), evaluatedChangeCount: changedAll.length,
|
|
1868
1888
|
changedTests: observedChangedTestFiles.slice(0, 8), changedTestCount: observedChangedTestFiles.length,
|
|
1869
1889
|
srcChanged: scope.sourceFiles.length, testStepSkipped,
|
|
1870
1890
|
scopeReason, derived: scope.derived.length, ignoredOutsideSrc: scope.ignoredOutsideSrc,
|
|
@@ -2053,7 +2073,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
2053
2073
|
// `tc`(변경파일 tsc) **전에** 실행해서, tsc 가 깨져 **최종 게이트가 실패한 런에도**
|
|
2054
2074
|
// base 를 다시 돌리고 `gate.log` 를 바꿨다 ⇒ *"게이트 실패 경로 무변경"* 경계 위반.
|
|
2055
2075
|
// ⇒ 조건을 최종 판정과 같은 식으로 묶는다.
|
|
2056
|
-
const gatePassed = !comparisonFailureReason && !unmeasuredPostsync && integrityPassed && tc.passed && policy.passed && (dependencyGate?.passed ?? true);
|
|
2076
|
+
const gatePassed = !comparisonFailureReason && !unmeasuredPostsync && integrityPassed && tc.passed && policy.passed && expose.passed && (dependencyGate?.passed ?? true);
|
|
2057
2077
|
const verifyIdentity = gateVerifyIdentity(ctx?.runId);
|
|
2058
2078
|
if (gatePassed && scope.skipTestStep) {
|
|
2059
2079
|
verifyByBreaking = { ran: false, distinguishes: 0, 'does-not-distinguish': 0, unknown: 0, skippedReason: scope.reason };
|
|
@@ -2204,7 +2224,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
2204
2224
|
+ (scope.reason === 'no-related-tests'
|
|
2205
2225
|
// ⭐ **미검증 파일 목록**(2026-07-27 사후 리뷰 수리) — 종전엔 `sourceFiles` 를 실어
|
|
2206
2226
|
// `package.json`·설정·스크립트 변경 시 **빈 목록**이 나갔다(그 파일들은 소스 확장자가 아니다).
|
|
2207
|
-
?
|
|
2227
|
+
? `\n\n${formatNoRelatedTestsWarning(scope.unverified)}\n\n동작 검증을 못 한 변경: ${scope.unverified.slice(0, 8).join(', ')}`
|
|
2208
2228
|
+ `\n⚠️ 이 라운드는 **동작 검증이 되지 않았다**(변경파일 tsc 만 통과) — "게이트 통과=검증됨"으로 결론내지 말 것.`
|
|
2209
2229
|
// ⚠️ 안내를 일반화(리뷰 should-fix) — `package.json`·lockfile·설정·스크립트에는
|
|
2210
2230
|
// "해당 소스의 테스트"라는 표현이 부정확하다(그 변경엔 대응 테스트 관례가 없다).
|
|
@@ -2229,7 +2249,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
2229
2249
|
: '';
|
|
2230
2250
|
return {
|
|
2231
2251
|
passed: gatePassed,
|
|
2232
|
-
log: `${baseLog}${policyFailureLog}${dependencyFailureLog}${dependencyUnmeasuredNote}${comparisonFailureLog}${baselineNote}${verifyByBreakingNote}${scopeNote}${uncoveredNote}${pulledInRelatedTestsNote}${callerOverflowNote}${routeLookupNote}`,
|
|
2252
|
+
log: `${baseLog}${policyFailureLog}${dependencyFailureLog}${dependencyUnmeasuredNote}${comparisonFailureLog}${baselineNote}${verifyByBreakingNote}${scopeNote}${uncoveredNote}${pulledInRelatedTestsNote}${callerOverflowNote}${routeLookupNote}${expose.log ? `\n\n${expose.log}` : ''}`,
|
|
2233
2253
|
testStepExecuted: !testStepSkipped && testStep !== undefined && !testStep.skipped,
|
|
2234
2254
|
scopeReason,
|
|
2235
2255
|
comparisonBaseStatus: observationChanges.comparisonBaseStatus,
|
package/src/session/index.ts
CHANGED
|
@@ -213,6 +213,8 @@ export interface SessionMeta {
|
|
|
213
213
|
tgBotId?: string;
|
|
214
214
|
/** Stable identity for a persona's resident conversation. */
|
|
215
215
|
personaId?: string;
|
|
216
|
+
/** Optional persona selected for this conversation's replies (not resident identity). */
|
|
217
|
+
chatPersonaId?: string;
|
|
216
218
|
/** Telegram session-key used for lookup by chat, e.g.
|
|
217
219
|
* `telegram:8799226199:dm:123:0` (bot-scoped) or legacy
|
|
218
220
|
* `telegram:dm:123:0`. Constructed once at create time. */
|