elanous 0.2.13 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/LZcstaO9_NXypPFmujolv/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-33ffcc49ab45123b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9656-c1540088f5859c80.js → 1844-9ec4b1d12f6e80eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{390.c373e31b3b9d0c41.js → 1948.15f356b15a625597.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
- package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4685-c3c1c625b71accd1.js → 4614-21403aac730d4586.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{8039-df92e08fede5de8f.js → 4795-f372490b178583fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1452.93700483b9d4fc61.js → 6110.1297d4093fdfbb0d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +1 -0
- package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1829-b453a1b22d3999bb.js → 7327-2d69fb321ba73389.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +25 -0
- package/apps/pwa/out/_next/static/chunks/{8421-1c4acfb1395a0edf.js → 8949-ecb4128a0858b911.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9103-7a3ae7f60af3b99f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3869-3cd46526879f454c.js → 9247-29d889b809288e65.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{79f8be9e-957d68913b590b08.js → a9f2b54d-7865aba0f4d2ee49.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-0e2c57fe6add4b2a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e05a77df7e005b04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b8c49f6b4fa2df91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-976813c91d7394ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-72d3e7ff35a8c954.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-60c7a8e1a4fe62ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-2a52b316cb143d9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-c3ce483c8d5db370.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-96f074f6538e8a2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-e97be5334d83a9f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-955ec2da3be4c560.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-c1343f9499d86f3f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-8739a2e0ae793130.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-d23b12bb92242558.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c7fea475488201a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e2ee2896811e98d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-5efbf81124c474ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f1a1fc0a82738c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-06ee266953d9c879.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-153c2b3bcc89e8de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-7d4182964407c09c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-0d6d23b40263a7ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-63d14a4d8fe1e1c1.js → page-f5597e894c02cd33.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-73735b64bfe1ae67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8fcec4c9ea2e7f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f7e631e9b4daf66e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9817c7f9007466cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5cd7e3a9-404fd6b8d0ecd44c.js → f6231284-45726550aba08fca.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f48170cc-d05d28bf87a43bf4.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +1 -0
- package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -0
- package/apps/pwa/out/ceo/index.txt +33 -0
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -0
- package/apps/pwa/out/loops/index.txt +30 -0
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/orchestrator/orchestrator.yaml +59 -0
- package/graphs/orchestrator/recipes.yaml +18 -0
- package/graphs/release/recipes.yaml +6 -0
- package/graphs/release/release-loop.yaml +6 -1
- package/graphs/rhythm/daily.yaml +38 -0
- package/graphs/rhythm/recipes.yaml +18 -0
- package/graphs/rhythm/weekly.yaml +39 -0
- package/graphs/video/recipes.yaml +3 -0
- package/graphs/video/youtube-summary.yaml +17 -0
- package/package.json +1 -1
- package/scripts/audit-test-state-writes.ts +72 -17
- package/scripts/brand/check.ts +188 -0
- package/scripts/ci-typecheck-changed.ts +50 -18
- package/scripts/coord-post.sh +11 -1
- package/scripts/docs/generate-graph-anatomy.ts +315 -0
- package/scripts/docs-cli-check.ts +23 -6
- package/scripts/expose-rubric-check.ts +118 -0
- package/scripts/expose-rubric-suggest.ts +169 -0
- package/scripts/heal/run-role.ts +14 -0
- package/scripts/hq/standby-verify.ts +11 -7
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +112 -0
- package/scripts/marketing/generate-ax-brief.ts +140 -0
- package/scripts/marketing/generate-feature-map.ts +118 -0
- package/scripts/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/gate-node.ts +89 -14
- package/scripts/release-loop/publish-node.ts +14 -1
- package/scripts/release-loop/unattended-release.ts +23 -1
- package/scripts/release-loop/vault-note-node.ts +428 -0
- package/scripts/release-story/draft.ts +220 -0
- package/scripts/rhythm/daily.ts +570 -0
- package/scripts/rhythm/weekly.ts +315 -0
- package/scripts/skills/public-check.ts +169 -0
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/scripts/test-deterministic.ts +4 -8
- package/scripts/test-diet/effectiveness.ts +195 -0
- package/scripts/test-diet/lib.ts +16 -8
- package/scripts/test-diet/node.ts +77 -9
- package/scripts/ux/demo-rehearsal.ts +2 -2
- package/scripts/ux/lib/rehearsal-verdict.ts +1 -0
- package/scripts/video/youtube-summary.ts +133 -0
- package/skills/diagram-master/SKILL.md +42 -49
- package/skills/diagram-master/references/pyproject.toml +4 -1
- package/skills/diagram-master/references/routing-guide.md +4 -2
- package/skills/grill-me/SKILL.md +1 -0
- package/skills/youtube-master/SKILL.md +13 -13
- package/skills/youtube-master/package-lock.json +0 -545
- package/skills/youtube-master/package.json +2 -5
- package/skills/youtube-master/scripts/main.ts +2 -2
- package/skills/youtube-master/scripts/transcribe-local.ts +1 -1
- package/skills/youtube-master/src/util.ts +30 -7
- package/src/agent-env/profile.ts +161 -0
- package/src/agent-mission/driver.ts +3 -3
- package/src/agent-substrate/pr-reviewer.ts +2 -0
- package/src/ask-user-question/pending-questions.ts +5 -0
- package/src/auth/github-app-token.ts +112 -13
- package/src/autopilot/build/llm-conflict-merge.ts +73 -10
- package/src/autopilot/build/merge-intent.ts +53 -0
- package/src/briefing/brief-items.ts +121 -0
- package/src/chat/index.ts +2 -0
- package/src/claims/claims-ledger.ts +232 -0
- package/src/claims/claims-render.ts +166 -0
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/claims-cli.ts +94 -0
- package/src/cli/decisions-cli.ts +44 -3
- package/src/cli/docs-cli.ts +2 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/flow-cli.ts +11 -0
- package/src/cli/freeze-cli.ts +45 -0
- package/src/cli/intake-cli.ts +46 -17
- package/src/cli/lesson-cli.ts +108 -0
- package/src/cli/logs-cli.ts +104 -2
- package/src/cli/logs-model-input.ts +131 -0
- package/src/cli/pod-cli.ts +2 -0
- package/src/cli/pr-cli.ts +29 -1
- package/src/cli/private-ledger-commands.ts +54 -4
- package/src/cli/pty-drive-verdict.ts +11 -2
- package/src/cli/release-cli.ts +190 -33
- package/src/cli/rfc-status.ts +91 -0
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/connectors/linear.ts +5 -3
- package/src/context-bus/condense.ts +15 -20
- package/src/context-bus/context-now-public.ts +96 -0
- package/src/context-bus/context-now-surfaces.ts +55 -2
- package/src/context-bus/context-now.ts +6 -3
- package/src/context-bus/coord-events.ts +6 -4
- package/src/context-bus/long-term-memory.ts +92 -11
- package/src/dashboard/index.ts +75 -22
- package/src/dashboard/slash-runtime/chat-persona.ts +66 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +99 -0
- package/src/dashboard/slash-runtime/loops-table.ts +77 -0
- package/src/decisions/decision-cards.ts +33 -7
- package/src/decisions/decision-ledger.ts +40 -1
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/discord/sprint21-runtime.ts +16 -0
- package/src/discord-self-message.ts +8 -0
- package/src/discord-slash-wire.ts +31 -2
- package/src/domains/alert-audit.ts +271 -0
- package/src/domains/core-tools.ts +4 -3
- package/src/domains/ops-facts-tool.ts +31 -1
- package/src/domains/outbound-alert.ts +1 -1
- package/src/elanous-config-dir.ts +29 -4
- package/src/execution-loop/launch-gate.ts +29 -14
- package/src/flow/cards.ts +239 -0
- package/src/graph-runner/graph-tick.ts +4 -0
- package/src/graph-runner/runner.ts +30 -5
- package/src/harness/harness-cli-command.ts +259 -38
- package/src/harness/harness-incidents.ts +45 -12
- package/src/harness/harness-pod-dispatch.ts +102 -11
- package/src/harness/harness-queue-child.ts +3 -3
- package/src/harness/harness-queue.ts +583 -65
- package/src/harness/helper-repair.ts +87 -0
- package/src/harness/helper-scan.ts +151 -0
- package/src/harness/launch-stamp.ts +236 -0
- package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
- package/src/harness/ledger-write-guard.ts +35 -0
- package/src/harness/model-input-observation.ts +37 -0
- package/src/harness/pod-memory-policy.ts +15 -0
- package/src/harness/repository-name.ts +55 -0
- package/src/hq/fence-audit.ts +90 -0
- package/src/hq/hq.ts +63 -5
- package/src/hq/lease.ts +4 -4
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +332 -0
- package/src/index.ts +159 -27
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +37 -15
- package/src/intake-plane/front-classifier.ts +6 -0
- package/src/intake-plane/items.ts +3 -2
- package/src/intake-plane/lens.ts +177 -0
- package/src/intake-plane/linear-wish.ts +39 -11
- package/src/intake-plane/route.ts +145 -1
- package/src/intake-plane/wish-benchmark-ledger.ts +129 -0
- package/src/intake-plane/wish-card.ts +53 -2
- package/src/intake-plane/wish-folder.ts +5 -2
- package/src/intake-plane/wish-reply.ts +72 -0
- package/src/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
- package/src/launch-head/seat-goal-queue.ts +198 -0
- package/src/lessons/lesson-import.ts +106 -0
- package/src/lessons/lesson-ledger.ts +206 -0
- package/src/loops/activity.ts +203 -0
- package/src/loops/budget.ts +63 -0
- package/src/loops/checker.ts +5 -3
- package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
- package/src/loops/loop-cli.ts +36 -4
- package/src/loops/neighbors.ts +40 -0
- package/src/loops/observe.ts +10 -0
- package/src/loops/orchestrator/author-depth.ts +220 -0
- package/src/loops/orchestrator/author-input-check.ts +82 -0
- package/src/loops/orchestrator/author-ledger.ts +232 -0
- package/src/loops/orchestrator/finish-rate.ts +175 -0
- package/src/loops/orchestrator/overlap.ts +51 -12
- package/src/loops/orchestrator/resync-shadow.ts +227 -0
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +309 -0
- package/src/loops/orchestrator/traffic.ts +134 -13
- package/src/loops/registry.ts +535 -9
- package/src/loops/status-rows.ts +118 -0
- package/src/loops/verdict.ts +17 -5
- package/src/maturity/bot-slash-catalog.ts +66 -0
- package/src/maturity/feature-maturity.ts +9 -5
- package/src/maturity/tui-slash-availability.ts +31 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/dashboard.ts +6 -1
- package/src/nexus/api/http-server.ts +33 -3
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/merge-approvals-gate.ts +1 -1
- package/src/nexus/api/schedules-read.ts +18 -1
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/nexus/api/task-cards-api.ts +44 -5
- package/src/nexus/api/workflows.ts +2 -1
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +51 -0
- package/src/release-loop/checklist.ts +22 -4
- package/src/release-loop/feature-store.ts +71 -32
- package/src/release-loop/landing-freeze.ts +121 -0
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +302 -0
- package/src/release-loop/release-schedule.ts +3 -1
- package/src/seat-dispatch/ceo-commands.ts +2 -2
- package/src/seat-dispatch/seat-ask.ts +16 -3
- package/src/seat-dispatch/seat-questions.ts +58 -0
- package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
- package/src/seat-dispatch/seat-request-ledger.ts +133 -0
- package/src/seat-loop/seat-loop.ts +531 -40
- package/src/self-dev/dev-pipeline.ts +14 -6
- package/src/self-dev/draft-sweep.ts +162 -6
- package/src/self-dev/draft-triage-rules.ts +25 -8
- package/src/self-dev/orchestrate.ts +15 -1
- package/src/self-dev/review-provider-fallback.ts +6 -1
- package/src/self-dev/run-store.ts +73 -1
- package/src/self-dev/run-supervisor.ts +57 -2
- package/src/self-dev/self-orchestrate-runtime.ts +5 -1
- package/src/self-implement/budget-gate.ts +29 -0
- package/src/self-implement/expose-gate.ts +47 -0
- package/src/self-implement/frozen-merges.ts +295 -0
- package/src/self-implement/gate-cli.ts +10 -4
- package/src/self-implement/gate-scope.ts +46 -9
- package/src/self-implement/goal-archive.ts +222 -0
- package/src/self-implement/goal-author.ts +43 -5
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/goal-lookup.ts +147 -0
- package/src/self-implement/heal-intake.ts +197 -0
- package/src/self-implement/host-regate.ts +32 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +299 -48
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +97 -0
- package/src/self-implement/run-ledger.ts +3 -1
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +29 -9
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/session/index.ts +2 -0
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +4 -1
- package/src/task-cards/card-cli.ts +10 -2
- package/src/task-cards/card-store.ts +10 -6
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
- package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
- package/src/task-orchestrator/surfaces/pod-pool.ts +60 -8
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +224 -49
- package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
- package/src/telegram-commands.ts +27 -3
- package/src/telegram-loops-command.ts +44 -0
- package/src/telegram.ts +25 -4
- package/src/user-config.ts +146 -18
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/src/workflow-runtime/storage.ts +31 -13
- package/apps/pwa/out/_next/static/D51l8jUBplJrIpWvENsDK/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1394-931c9827067b2276.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3992-fe5c06d6b31f8b25.js +0 -25
- package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +0 -2
- package/apps/pwa/out/_next/static/chunks/5250-c78ca295585bbf95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6511-ff8360be3d65aaa4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8605-9da4678f8a310d18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9269.d2a4795bc6812717.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9906-e7b8b5310a169dfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-e5b4e1b256f41076.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e49b9b76c565a0b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-f14987d5850a31e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-4c1dbb21861beab2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-f6bc66b919b2ea7d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-60603c1e056c9b25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-a0ea8f5a2341ee6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-cb2eb0fd809980da.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-31012264da8a341f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-50309aeb0bdd554a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-0696ee3fac9fe56c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66c27608f651b866.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-6ddd2109b7fc3a80.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-7a2bf830038a2264.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1f327f18694fabca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e5cef2fe6e4028df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cf596128d2b420ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-f672536f14f0969f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-29a709616df9b050.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-fee842d7c2151129.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-78b40a344ed22f24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-a011dd5b0bff29c5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-d92e32d51ab5157e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-5f21aed6932ef375.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-1e1a53946804debf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-a1833cd43d832601.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-2a511d320f762e06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-1c3e03f88dac1e21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c4d5289c2ea97a89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b91f8952deaeaf27.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-71b43a2588489987.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +0 -1
- package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +0 -1
- /package/apps/pwa/out/_next/static/{D51l8jUBplJrIpWvENsDK → LZcstaO9_NXypPFmujolv}/_ssgManifest.js +0 -0
|
@@ -31,8 +31,32 @@
|
|
|
31
31
|
import { importerTestsNotInRunSet, type ImporterTestIndex } from './importer-test-index.js';
|
|
32
32
|
import type { RouteConsumerTestIndex } from './route-consumer-test-index.js';
|
|
33
33
|
import { rankGateCallerCandidates } from './gate-candidate-ranking.js';
|
|
34
|
+
import { debug } from '../debug/log.js';
|
|
34
35
|
|
|
35
36
|
export const GATE_CALLER_TEST_LIMIT = 30;
|
|
37
|
+
|
|
38
|
+
/** Command registries and their non-import-linked gate companions. Kept outside the caller cap. */
|
|
39
|
+
const PAIRED_REGISTRY_TESTS = {
|
|
40
|
+
registries: [
|
|
41
|
+
'src/chat/index.ts',
|
|
42
|
+
'src/telegram-commands.ts',
|
|
43
|
+
'src/discord-slash-wire.ts',
|
|
44
|
+
'src/index.ts',
|
|
45
|
+
],
|
|
46
|
+
tests: [
|
|
47
|
+
'src/maturity/feature-maturity.test.ts',
|
|
48
|
+
'src/maturity/tui-slash-maturity.test.ts',
|
|
49
|
+
'src/maturity/bot-command-maturity.test.ts',
|
|
50
|
+
'src/maturity/cli-maturity.test.ts',
|
|
51
|
+
'test/slash-filter.test.ts',
|
|
52
|
+
'test/bot-slash-parity.test.ts',
|
|
53
|
+
],
|
|
54
|
+
} as const;
|
|
55
|
+
|
|
56
|
+
/** 게이트 로그와 PR Gate 절에서 공통으로 쓰는 미검증 경고(파일 목록은 자르지 않는다). */
|
|
57
|
+
export function formatNoRelatedTestsWarning(files: readonly string[]): string {
|
|
58
|
+
return `⚠️ 동작 검증 0: ${files.join(', ')} — 사람 확인 필요`;
|
|
59
|
+
}
|
|
36
60
|
export type GateCallerReason = 'import' | 'route';
|
|
37
61
|
|
|
38
62
|
/** 테스트 파일 판정(기존 `seams.ts` 정규식과 동일 — 어휘 분기 금지). */
|
|
@@ -225,7 +249,7 @@ export function deriveRelatedTests(
|
|
|
225
249
|
/** 왜 이 범위로 돌았나 — 로그만으로 답할 수 있게 하는 관측 필드(제1원칙).
|
|
226
250
|
* ⚠️ 비-export 유지(리뷰 must-fix) — 소비처가 이 모듈 안뿐이다. 구조적 사용(호출부의 `scope.reason`)은
|
|
227
251
|
* export 없이도 성립하므로 불필요한 공개 surface 를 만들지 않는다. */
|
|
228
|
-
type GateScopeReason = 'changed-tests' | 'derived' | 'no-changes' | 'docs-only' | 'no-related-tests' | 'unmeasured';
|
|
252
|
+
type GateScopeReason = 'changed-tests' | 'derived' | 'paired-registry' | 'no-changes' | 'docs-only' | 'no-related-tests' | 'unmeasured';
|
|
229
253
|
|
|
230
254
|
export type GateScopeMode = 'worktree' | 'postsync';
|
|
231
255
|
|
|
@@ -279,7 +303,7 @@ export function resolveGateScope(
|
|
|
279
303
|
changed: readonly string[],
|
|
280
304
|
exists: (path: string) => boolean,
|
|
281
305
|
importerTestIndex?: ImporterTestIndex,
|
|
282
|
-
opts?: { mode?: GateScopeMode; isDeleted?: (path: string) => boolean; routeConsumerTestIndex?: RouteConsumerTestIndex },
|
|
306
|
+
opts?: { mode?: GateScopeMode; isDeleted?: (path: string) => boolean; routeConsumerTestIndex?: RouteConsumerTestIndex; runChanged?: readonly string[]; round?: number },
|
|
283
307
|
): GateScopeDecision {
|
|
284
308
|
// ⚠️ **실존 검증(리뷰 must-fix 4R)** — `gitChangedFiles` 는 `git diff --name-only HEAD` 라
|
|
285
309
|
// **삭제·rename 前 경로도 포함**한다. 그걸 그대로 `testArgs` 로 넘기면 필터가 아무것도 매치하지
|
|
@@ -292,9 +316,9 @@ export function resolveGateScope(
|
|
|
292
316
|
const missingTestFiles = changedTests.length - testFiles.length;
|
|
293
317
|
const sourceFiles = userChanged.filter((f) => SOURCE_RE.test(f));
|
|
294
318
|
const ignoredOutsideSrc = sourceFiles.filter((f) => !isTestFile(f) && !f.startsWith('src/')).length;
|
|
295
|
-
/** ⭐ **문서가 아닌 사용자 변경** —
|
|
296
|
-
const documentPaths = userChanged.filter(isDocPath);
|
|
297
|
-
const nonDoc = userChanged.filter((f) => !isDocPath(f));
|
|
319
|
+
/** ⭐ **문서가 아닌 사용자 변경** — 시험 파일도 명시적으로 docs-only 대상에서 제외한다. */
|
|
320
|
+
const documentPaths = userChanged.filter((f) => isDocPath(f) && !isTestFile(f));
|
|
321
|
+
const nonDoc = userChanged.filter((f) => isTestFile(f) || !isDocPath(f));
|
|
298
322
|
/**
|
|
299
323
|
* ⭐ **이 변경 중 무엇이 검증되지 않나** — 분기와 **무관하게** 계산한다(리뷰 must-fix 2026-07-27).
|
|
300
324
|
*
|
|
@@ -323,6 +347,8 @@ export function resolveGateScope(
|
|
|
323
347
|
if (runSet.includes(f)) return true;
|
|
324
348
|
if (opts?.isDeleted?.(f)) return true;
|
|
325
349
|
if (importerTestIndex && (importerTestIndex.testsBySource.get(f) ?? []).some((t) => runSet.includes(t))) return true;
|
|
350
|
+
if (PAIRED_REGISTRY_TESTS.registries.some((registry) => registry === f)
|
|
351
|
+
&& PAIRED_REGISTRY_TESTS.tests.some((test) => runSet.includes(test))) return true;
|
|
326
352
|
const related = relatedFor(f);
|
|
327
353
|
return related.length > 0 && related.every((t) => runSet.includes(t));
|
|
328
354
|
};
|
|
@@ -354,7 +380,9 @@ export function resolveGateScope(
|
|
|
354
380
|
importerTestIndex?.testsBySource ?? new Map());
|
|
355
381
|
const callerTests = candidates.slice(0, GATE_CALLER_TEST_LIMIT);
|
|
356
382
|
const callerTestsOverflow = candidates.slice(GATE_CALLER_TEST_LIMIT);
|
|
357
|
-
const
|
|
383
|
+
const pairedRegistryTests = userChanged.some((file) => PAIRED_REGISTRY_TESTS.registries.some((registry) => registry === file))
|
|
384
|
+
? PAIRED_REGISTRY_TESTS.tests.filter(exists) : [];
|
|
385
|
+
const runWithCallers = (tests: readonly string[]) => [...new Set([...tests, ...callerTests.map(({ file }) => file), ...pairedRegistryTests])];
|
|
358
386
|
const base = { sourceFiles, documentPaths, ignoredOutsideSrc, missingTestFiles, elanousRuntimeArtifacts, callerTests, callerTestsOverflow,
|
|
359
387
|
unverified: [] as readonly string[], derived: [] as readonly string[], pulledInRelatedTests: [] as readonly string[] };
|
|
360
388
|
const withDocumentNonContribution = (runSet: readonly string[]) => ({
|
|
@@ -381,8 +409,17 @@ export function resolveGateScope(
|
|
|
381
409
|
const derived = conventional;
|
|
382
410
|
const runSet = runWithCallers(derived);
|
|
383
411
|
if (runSet.length > 0) {
|
|
384
|
-
return { ...base, ...withDocumentNonContribution(runSet), derived, testArgs: runSet, unverified: unverifiedFor(runSet), importerTestsNotRun: observationFor(runSet), skipTestStep: false, reason: 'derived' };
|
|
412
|
+
return { ...base, ...withDocumentNonContribution(runSet), derived, testArgs: runSet, unverified: unverifiedFor(runSet), importerTestsNotRun: observationFor(runSet), skipTestStep: false, reason: derived.length === 0 && callerTests.length === 0 ? 'paired-registry' : 'derived' };
|
|
413
|
+
}
|
|
414
|
+
// 마지막 라운드에 짝이 없어도 원 PR의 앞 라운드에서 편집한 소스·시험을 같은 규칙으로 한 번 더 잰다.
|
|
415
|
+
if (opts?.runChanged?.some((file) => !changed.includes(file))) {
|
|
416
|
+
const wholeRun = resolveGateScope([...new Set([...changed, ...opts.runChanged])], exists, importerTestIndex,
|
|
417
|
+
{ ...opts, runChanged: undefined });
|
|
418
|
+
return wholeRun;
|
|
385
419
|
}
|
|
386
|
-
|
|
387
|
-
|
|
420
|
+
const unverified = unverifiedFor([]);
|
|
421
|
+
try {
|
|
422
|
+
debug.log('self-implement.gate-scope', 'no-related-tests', { files: unverified, round: opts?.round ?? null }, { level: 'warn' });
|
|
423
|
+
} catch { /* 관측 장애는 게이트 판정을 바꾸지 않는다. */ }
|
|
424
|
+
return { ...base, ...withDocumentNonContribution([]), unverified, importerTestsNotRun: observationFor([]), skipTestStep: true, reason: 'no-related-tests' };
|
|
388
425
|
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { appendFileSync, closeSync, copyFileSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, rmdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { basename, dirname, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
5
|
+
import { debug } from '../debug/log.js';
|
|
6
|
+
import { isGoalAuthorFileName } from './goal-document.js';
|
|
7
|
+
import { hasTerminalRunStatus, loadRunLedger, queryUnfinishedRunLedgers, runLedgerDir } from './run-ledger.js';
|
|
8
|
+
|
|
9
|
+
export interface GoalArchiveOptions {
|
|
10
|
+
repoRoot?: string;
|
|
11
|
+
stateRoot?: string;
|
|
12
|
+
before?: string;
|
|
13
|
+
apply?: boolean;
|
|
14
|
+
trustDocRecord?: boolean;
|
|
15
|
+
/** Failure-injection seam for the two write stages (copy · index); defaults use the real filesystem.
|
|
16
|
+
* The repository is never written: removing the originals is a human PR from `filesForPr`. */
|
|
17
|
+
writes?: {
|
|
18
|
+
copy?: (source: string, destination: string) => void;
|
|
19
|
+
appendIndex?: (path: string, line: string) => void;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface GoalArchiveEntry {
|
|
24
|
+
goalId: string;
|
|
25
|
+
runId: string;
|
|
26
|
+
pr: number | null;
|
|
27
|
+
outcome: 'merged' | 'abandoned' | 'superseded';
|
|
28
|
+
source: 'ledger' | 'doc-record';
|
|
29
|
+
originalPath: string;
|
|
30
|
+
archivePath: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface GoalArchiveResult {
|
|
34
|
+
candidates: GoalArchiveEntry[];
|
|
35
|
+
applied: GoalArchiveEntry[];
|
|
36
|
+
skipped: Record<string, number>;
|
|
37
|
+
trustDocRecordAdds: number;
|
|
38
|
+
filesForPr: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function gitRead(root: string, ...args: string[]): string {
|
|
42
|
+
return execFileSync('git', args, {
|
|
43
|
+
cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
|
|
44
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: '0' },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function applyArchiveEntry(index: string, source: string,
|
|
49
|
+
entry: GoalArchiveEntry, document: string, writes: GoalArchiveOptions['writes']): void {
|
|
50
|
+
const directory = dirname(entry.archivePath);
|
|
51
|
+
const hadDirectory = existsSync(directory);
|
|
52
|
+
const priorIndex = existsSync(index) ? readFileSync(index) : null;
|
|
53
|
+
let ownedCopy = false;
|
|
54
|
+
let indexAttempted = false;
|
|
55
|
+
try {
|
|
56
|
+
mkdirSync(directory, { recursive: true });
|
|
57
|
+
const reservation = openSync(entry.archivePath, 'wx');
|
|
58
|
+
ownedCopy = true;
|
|
59
|
+
closeSync(reservation);
|
|
60
|
+
(writes?.copy ?? copyFileSync)(source, entry.archivePath);
|
|
61
|
+
if (readFileSync(entry.archivePath, 'utf8') !== document) throw new Error(`archive copy mismatch: ${entry.originalPath}`);
|
|
62
|
+
indexAttempted = true;
|
|
63
|
+
(writes?.appendIndex ?? ((path, line) => appendFileSync(path, line, 'utf8')))(index, `${JSON.stringify(entry)}\n`);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
try {
|
|
66
|
+
if (indexAttempted) {
|
|
67
|
+
if (priorIndex === null) rmSync(index, { force: true });
|
|
68
|
+
else if (!readFileSync(index).equals(priorIndex)) writeFileSync(index, priorIndex);
|
|
69
|
+
}
|
|
70
|
+
if (ownedCopy) rmSync(entry.archivePath, { force: true });
|
|
71
|
+
if (ownedCopy && !hadDirectory && existsSync(directory)) rmdirSync(directory);
|
|
72
|
+
} catch (rollbackError) {
|
|
73
|
+
throw new Error(`archive failed for ${entry.originalPath}: ${String(error)}; recovery required (archive rollback: ${String(rollbackError)}). Preserve ${entry.archivePath} and ${index}; reconcile the index before retry.`);
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function beforeDate(value: string | undefined): string | undefined {
|
|
80
|
+
if (value === undefined) return undefined;
|
|
81
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value) || Number.isNaN(Date.parse(`${value}T00:00:00Z`))
|
|
82
|
+
|| new Date(`${value}T00:00:00Z`).toISOString().slice(0, 10) !== value) {
|
|
83
|
+
throw new Error(`--before requires a valid YYYY-MM-DD date: ${value}`);
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function ledgerGoalFileContradicts(goalFile: unknown, originalPath: string): boolean {
|
|
89
|
+
if (typeof goalFile !== 'string' || !goalFile.trim()) return false;
|
|
90
|
+
const normalized = goalFile.replaceAll('\\', '/');
|
|
91
|
+
return normalized !== originalPath && !normalized.endsWith(`/${originalPath}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function lastExecution(document: string): Record<string, string> | null {
|
|
95
|
+
const sections = document.split(/^## 실행 기록\s*$/m).slice(1);
|
|
96
|
+
const block = sections.at(-1)?.split(/^#{1,2} /m, 1)[0];
|
|
97
|
+
const run = block?.split(/^- runId: /m).slice(1).at(-1);
|
|
98
|
+
if (!run) return null;
|
|
99
|
+
const id = run.split(/\r?\n/, 1)[0]?.trim();
|
|
100
|
+
if (!id) return null;
|
|
101
|
+
const fields: Record<string, string> = { runId: id };
|
|
102
|
+
for (const [, key, value] of run.matchAll(/^ (stage|outcome|prNumber|completedAt): ([^\r\n]*)$/gm)) fields[key!] = value!.trim();
|
|
103
|
+
return fields;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Preview by default. --apply only copies to the state archive and indexes; it never removes or stages anything in the repository —
|
|
107
|
+
* `filesForPr` is the list a human removes in one PR. A missing ledger requires explicit trust in the document's finished-run record. */
|
|
108
|
+
export function archiveGoals(options: GoalArchiveOptions = {}): GoalArchiveResult {
|
|
109
|
+
const root = resolve(options.repoRoot ?? gitRead(process.cwd(), 'rev-parse', '--show-toplevel').trim());
|
|
110
|
+
const state = resolve(options.stateRoot ?? elanousStateRoot());
|
|
111
|
+
const cutoff = beforeDate(options.before);
|
|
112
|
+
const goals = join(root, 'docs', 'goals');
|
|
113
|
+
const archive = join(state, 'goal-archive');
|
|
114
|
+
const index = join(archive, 'index.jsonl');
|
|
115
|
+
const result: GoalArchiveResult = { candidates: [], applied: [], skipped: {}, trustDocRecordAdds: 0, filesForPr: [] };
|
|
116
|
+
const skip = (reason: string): void => { result.skipped[reason] = (result.skipped[reason] ?? 0) + 1; };
|
|
117
|
+
const lock = join(archive, '.apply.lock');
|
|
118
|
+
if (options.apply) {
|
|
119
|
+
mkdirSync(archive, { recursive: true });
|
|
120
|
+
try { mkdirSync(lock); }
|
|
121
|
+
catch (error) {
|
|
122
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
123
|
+
throw new Error(`EEXIST: goal archive --apply is already running or a previous run stopped mid-way (${lock}). `
|
|
124
|
+
+ `If no other archive is running: compare ${index} with ${archive}/<YYYY-MM>/ copies, drop index lines whose copy is missing, then remove ${lock} and retry.`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const tracked = new Set(gitRead(root, 'ls-files', '-z', '--', 'docs/goals').split('\0').filter(Boolean));
|
|
129
|
+
const pendingRemovals = new Set(gitRead(root, 'diff', '--cached', '--name-only', '--diff-filter=D', '-z', '--', 'docs/goals').split('\0').filter(Boolean));
|
|
130
|
+
const unfinished = queryUnfinishedRunLedgers({ dir: runLedgerDir(state), goalsDir: goals, noCache: true });
|
|
131
|
+
if (unfinished.unreadableLedgerCount > 0) throw new Error('run ledger contains unreadable runs; archive refused');
|
|
132
|
+
const activePaths = new Set(unfinished.entries.filter((entry) => entry.goalDocumentPath)
|
|
133
|
+
.map((entry) => resolve(root, entry.goalDocumentPath!)));
|
|
134
|
+
const activeRunIds = new Set(unfinished.entries.map((entry) => entry.runId));
|
|
135
|
+
const indexed = new Set<string>();
|
|
136
|
+
if (existsSync(index)) {
|
|
137
|
+
for (const line of readFileSync(index, 'utf8').split('\n').filter(Boolean)) {
|
|
138
|
+
const entry = JSON.parse(line) as { originalPath: string };
|
|
139
|
+
indexed.add(entry.originalPath);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
let names: string[];
|
|
143
|
+
try { names = readdirSync(goals).sort(); }
|
|
144
|
+
catch (error) {
|
|
145
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
146
|
+
names = [];
|
|
147
|
+
}
|
|
148
|
+
for (const name of names) {
|
|
149
|
+
if (!isGoalAuthorFileName(name)) continue;
|
|
150
|
+
const path = join(goals, name);
|
|
151
|
+
const originalPath = relative(root, path).split(sep).join('/');
|
|
152
|
+
if (!tracked.has(originalPath) || pendingRemovals.has(originalPath)) { skip('untracked'); continue; }
|
|
153
|
+
const fileStat = lstatSync(path, { throwIfNoEntry: false });
|
|
154
|
+
if (!fileStat?.isFile() || fileStat.isSymbolicLink()) { skip('not-file'); continue; }
|
|
155
|
+
if (indexed.has(originalPath)) { skip('already-indexed'); continue; }
|
|
156
|
+
if (activePaths.has(path)) { skip('active-goal-run'); continue; }
|
|
157
|
+
const document = readFileSync(path, 'utf8');
|
|
158
|
+
const fields = lastExecution(document);
|
|
159
|
+
if (!fields) { skip('no-execution-record'); continue; }
|
|
160
|
+
const runId = fields.runId!;
|
|
161
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(runId)) { skip('invalid-run-id'); continue; }
|
|
162
|
+
if (activeRunIds.has(runId)) { skip('active-goal-run'); continue; }
|
|
163
|
+
const outcome = fields.stage === 'merged' && (fields.outcome === 'completed' || fields.outcome === 'merged') ? 'merged'
|
|
164
|
+
: fields.outcome === 'abandoned' ? 'abandoned'
|
|
165
|
+
: fields.outcome === 'superseded' ? 'superseded' : null;
|
|
166
|
+
if (!outcome) { skip('unfinished'); continue; }
|
|
167
|
+
let ledger;
|
|
168
|
+
try { ledger = loadRunLedger(runId, runLedgerDir(state)); }
|
|
169
|
+
catch (error) {
|
|
170
|
+
skip(error instanceof Error && /runId does not match /.test(error.message) ? 'ledger-run-id-mismatch' : 'unreadable-ledger');
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const missingLedger = !ledger?.length;
|
|
174
|
+
if (missingLedger && (!fields.stage || !fields.outcome)) { skip('unfinished'); continue; }
|
|
175
|
+
const documentGoalId = /^- GoalId:\s*([0-9a-f]{16})\s*$/m.exec(document.split(/^## 실행 기록\s*$/m, 1)[0] ?? '')?.[1];
|
|
176
|
+
if (!documentGoalId) { skip('missing-goal-id'); continue; }
|
|
177
|
+
if (!missingLedger) {
|
|
178
|
+
const entries = ledger!;
|
|
179
|
+
const ledgerGoalIds = new Set(entries.map((entry) => entry.goalId).filter((id): id is string => typeof id === 'string'));
|
|
180
|
+
if (ledgerGoalIds.size !== 1 || !ledgerGoalIds.has(documentGoalId)) { skip('ledger-goal-id-mismatch'); continue; }
|
|
181
|
+
const starts = entries.filter((entry) => entry.event === 'start');
|
|
182
|
+
if (starts.length === 0 || starts.some((entry) => ledgerGoalFileContradicts(entry.data.goalFile, originalPath))) {
|
|
183
|
+
skip('ledger-goal-mismatch'); continue;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const latest = ledger?.filter((entry) => entry.event === 'run-status').at(-1);
|
|
187
|
+
if (!missingLedger) {
|
|
188
|
+
if (!latest || !hasTerminalRunStatus(ledger!) || !['completed', 'failed', 'cancelled'].includes(String(latest.data.runStatus))) {
|
|
189
|
+
skip('running-or-unconfirmed'); continue;
|
|
190
|
+
}
|
|
191
|
+
if (outcome === 'merged' && (latest.data.runStatus !== 'completed' || (latest.data.stage !== undefined && latest.data.stage !== 'merged'))
|
|
192
|
+
|| outcome === 'abandoned' && latest.data.runStatus === 'completed'
|
|
193
|
+
|| outcome === 'superseded' && latest.data.runStatus === 'failed') {
|
|
194
|
+
skip('status-mismatch'); continue;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const completedAt = missingLedger ? fields.completedAt : fields.completedAt ?? latest?.timestamp;
|
|
198
|
+
if (!completedAt || !Number.isFinite(Date.parse(completedAt))) { skip('no-completion-date'); continue; }
|
|
199
|
+
const date = new Date(completedAt).toISOString().slice(0, 10);
|
|
200
|
+
if (cutoff && date >= cutoff) { skip('after-before'); continue; }
|
|
201
|
+
const prRaw = fields.prNumber;
|
|
202
|
+
const pr = prRaw && /^[1-9]\d*$/.test(prRaw) && Number.isSafeInteger(Number(prRaw)) ? Number(prRaw) : null;
|
|
203
|
+
const archivePath = join(archive, date.slice(0, 7), basename(path));
|
|
204
|
+
if (existsSync(archivePath)) { skip('archive-collision'); continue; }
|
|
205
|
+
if (gitRead(root, 'status', '--porcelain', '--', originalPath).trim()) { skip('dirty'); continue; }
|
|
206
|
+
if (missingLedger && !options.trustDocRecord) { skip('missing-ledger'); result.trustDocRecordAdds += 1; continue; }
|
|
207
|
+
const entry: GoalArchiveEntry = { goalId: documentGoalId, runId, pr, outcome, source: missingLedger ? 'doc-record' : 'ledger', originalPath, archivePath };
|
|
208
|
+
result.candidates.push(entry);
|
|
209
|
+
if (options.apply) {
|
|
210
|
+
applyArchiveEntry(index, path, entry, document, options.writes);
|
|
211
|
+
result.applied.push(entry);
|
|
212
|
+
result.filesForPr.push(originalPath);
|
|
213
|
+
} else {
|
|
214
|
+
result.filesForPr.push(originalPath);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
try { debug.log('harness.goal', 'archive', { candidates: result.candidates.length, applied: result.applied.length, skipped: result.skipped }); } catch { /* observation is fail-soft */ }
|
|
218
|
+
return result;
|
|
219
|
+
} finally {
|
|
220
|
+
if (options.apply) rmdirSync(lock);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -6,6 +6,7 @@ import { dirname, isAbsolute, join, normalize, relative, resolve, sep } from 'no
|
|
|
6
6
|
import { groundMissionInCodebase, isRepositoryImplementationCandidate, type CodebaseGrounding } from '../autopilot/mission-codebase-gate.js';
|
|
7
7
|
import { extractSlugSource, generateMissionSlug, slugify } from '../autopilot/mission-registry.js';
|
|
8
8
|
import { enhancePrompt, type EnhanceOpts, type EnhanceResult } from '../prompt-enhance/enhance.js';
|
|
9
|
+
import type { LLMUsage } from '../prompt-cache/types.js';
|
|
9
10
|
import { debug } from '../debug/log.js';
|
|
10
11
|
|
|
11
12
|
function observeGoalAuthor(
|
|
@@ -324,9 +325,12 @@ function startGoalAuthorPhase(phase: GoalAuthorTimedPhase, authorRunId: string,
|
|
|
324
325
|
return Date.now();
|
|
325
326
|
}
|
|
326
327
|
|
|
327
|
-
|
|
328
|
+
type GoalAuthorTokens = { inputTokens: number | null; outputTokens: number | null };
|
|
329
|
+
const UNKNOWN_GOAL_AUTHOR_TOKENS: GoalAuthorTokens = { inputTokens: null, outputTokens: null };
|
|
330
|
+
|
|
331
|
+
function endGoalAuthorPhase(phase: GoalAuthorTimedPhase, startedAt: number, authorRunId: string, onProgress?: GoalAuthorDeps['onProgress'], tokens: GoalAuthorTokens = UNKNOWN_GOAL_AUTHOR_TOKENS): void {
|
|
328
332
|
const elapsedMs = Date.now() - startedAt;
|
|
329
|
-
observeGoalAuthor('goal-author', 'phase-end', { phase, authorRunId, elapsedMs });
|
|
333
|
+
observeGoalAuthor('goal-author', 'phase-end', { phase, authorRunId, elapsedMs, ...tokens });
|
|
330
334
|
if (GOAL_AUTHOR_PHASES.includes(phase as GoalAuthorPhase)) {
|
|
331
335
|
if (onProgress) onProgress(phase as GoalAuthorPhase, 'end');
|
|
332
336
|
else process.stderr.write(`[goal-author] ${phase} ended in ${elapsedMs}ms\n`);
|
|
@@ -1156,6 +1160,21 @@ function observeAuthoredGroundingPathSections(document: string, paths: readonly
|
|
|
1156
1160
|
}
|
|
1157
1161
|
}
|
|
1158
1162
|
|
|
1163
|
+
function authoredGoalSectionChars(document: string): Record<string, number> {
|
|
1164
|
+
const headings = linesOutsideFencedCode(document)
|
|
1165
|
+
.filter(({ text }) => /^## (?:\S)/.test(text))
|
|
1166
|
+
.map(({ text, start }) => ({ heading: text.slice(3).trim(), start }));
|
|
1167
|
+
const sections: Record<string, number> = { preamble: [...document.slice(0, headings[0]?.start ?? document.length)].length };
|
|
1168
|
+
for (let index = 0; index < headings.length; index += 1) {
|
|
1169
|
+
const { heading, start } = headings[index];
|
|
1170
|
+
sections[heading] = (sections[heading] ?? 0) + [...document.slice(start, headings[index + 1]?.start ?? document.length)].length;
|
|
1171
|
+
}
|
|
1172
|
+
for (const heading of ['PROBLEM', 'WHAT TO BUILD', 'ACCEPTANCE CRITERIA', 'TRACED PATHS', 'SCOPE BOUNDARY', 'STEPS', '답하지 못하는 것']) {
|
|
1173
|
+
sections[heading] ??= 0;
|
|
1174
|
+
}
|
|
1175
|
+
return sections;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1159
1178
|
function canonicalBlocks(document: string): string[] {
|
|
1160
1179
|
return linesOutsideFencedCode(document)
|
|
1161
1180
|
.filter((line) => /^##(?:\s|$)/.test(line.text))
|
|
@@ -4906,6 +4925,13 @@ async function authorGoalWithSupersededRootIntent(
|
|
|
4906
4925
|
}
|
|
4907
4926
|
// ⭐ 이 저작 «한 번»의 id. goalId 보다 «먼저» 있어야 ground·enhance 의 소요를 그 골에 붙일 수 있다.
|
|
4908
4927
|
const authorRunId = generateGoalId();
|
|
4928
|
+
const authorStartedAt = Date.now();
|
|
4929
|
+
const enhanceTokens: GoalAuthorTokens = { inputTokens: null, outputTokens: null };
|
|
4930
|
+
const onEnhanceUsage = (usage: LLMUsage): void => {
|
|
4931
|
+
if (usage.inputTokens !== undefined) enhanceTokens.inputTokens = (enhanceTokens.inputTokens ?? 0) + usage.inputTokens;
|
|
4932
|
+
if (usage.outputTokens !== undefined) enhanceTokens.outputTokens = (enhanceTokens.outputTokens ?? 0) + usage.outputTokens;
|
|
4933
|
+
try { deps.enhanceOpts?.onUsage?.(usage); } catch { /* usage observation is fail-soft */ }
|
|
4934
|
+
};
|
|
4909
4935
|
let facts: CodebaseGrounding | null = null;
|
|
4910
4936
|
let groundingError = false;
|
|
4911
4937
|
const persistentGrounding = resolveGoalAuthorPersistentGrounding('persistentGrounding' in deps ? deps.persistentGrounding : undefined);
|
|
@@ -4937,8 +4963,8 @@ async function authorGoalWithSupersededRootIntent(
|
|
|
4937
4963
|
? await deps.enhance(
|
|
4938
4964
|
enhanceAsk,
|
|
4939
4965
|
persistentFacts.length > 0
|
|
4940
|
-
? { ...deps.enhanceOpts, groundedFacts: persistentFacts }
|
|
4941
|
-
: deps.enhanceOpts,
|
|
4966
|
+
? { ...deps.enhanceOpts, groundedFacts: persistentFacts, onUsage: onEnhanceUsage }
|
|
4967
|
+
: { ...deps.enhanceOpts, onUsage: onEnhanceUsage },
|
|
4942
4968
|
)
|
|
4943
4969
|
: { original: ask, checklist: [], verbatimPreserved: true };
|
|
4944
4970
|
// ⭐ goal-context 규범이 «어디에도 안 실린다»는 것을 관측으로 남긴다.
|
|
@@ -4956,7 +4982,7 @@ async function authorGoalWithSupersededRootIntent(
|
|
|
4956
4982
|
placement: 'omitted',
|
|
4957
4983
|
});
|
|
4958
4984
|
} finally {
|
|
4959
|
-
endGoalAuthorPhase('enhance', enhanceStartedAt, authorRunId, deps.onProgress);
|
|
4985
|
+
endGoalAuthorPhase('enhance', enhanceStartedAt, authorRunId, deps.onProgress, enhanceTokens);
|
|
4960
4986
|
}
|
|
4961
4987
|
if (!enhancement.verbatimPreserved || (enhanceAsk && enhancement.original !== enhanceAsk)) {
|
|
4962
4988
|
throw new Error('goal author refused an enhancement that does not preserve its input verbatim');
|
|
@@ -5431,6 +5457,18 @@ async function authorGoalWithSupersededRootIntent(
|
|
|
5431
5457
|
} finally {
|
|
5432
5458
|
endGoalAuthorPhase('lint', lintStartedAt, authorRunId, deps.onProgress);
|
|
5433
5459
|
}
|
|
5460
|
+
try {
|
|
5461
|
+
observeGoalAuthor('goal-author', 'summary', {
|
|
5462
|
+
authorRunId,
|
|
5463
|
+
elapsedMs: Date.now() - authorStartedAt,
|
|
5464
|
+
inputTokens: enhanceTokens.inputTokens,
|
|
5465
|
+
outputTokens: enhanceTokens.outputTokens,
|
|
5466
|
+
totalTokens: enhanceTokens.inputTokens === null || enhanceTokens.outputTokens === null
|
|
5467
|
+
? null : enhanceTokens.inputTokens + enhanceTokens.outputTokens,
|
|
5468
|
+
goalChars: [...document].length,
|
|
5469
|
+
sectionChars: authoredGoalSectionChars(document),
|
|
5470
|
+
});
|
|
5471
|
+
} catch { /* observation is fail-soft */ }
|
|
5434
5472
|
return { document, facts, grounded: !groundingError && hasEvidence, authorRunId };
|
|
5435
5473
|
} finally {
|
|
5436
5474
|
endAssembleSubphase();
|
|
@@ -35,6 +35,8 @@ type GoalClarificationAnsweredBy = 'human' | 'agent' | 'none' | 'mixed';
|
|
|
35
35
|
|
|
36
36
|
export interface GoalClarificationEscalationInput {
|
|
37
37
|
goalFile: string;
|
|
38
|
+
/** Explicit provenance for isolated test fixtures; never inferred from goalFile. */
|
|
39
|
+
testOrigin?: true;
|
|
38
40
|
delivery?: GoalClarificationDelivery;
|
|
39
41
|
resolvedDelivery?: GoalClarificationObservedDelivery;
|
|
40
42
|
dispatch?: (
|
|
@@ -577,6 +579,9 @@ export async function escalateGoalDocumentClarifications(
|
|
|
577
579
|
}],
|
|
578
580
|
},
|
|
579
581
|
input.dispatchContext?.sessionId,
|
|
582
|
+
{},
|
|
583
|
+
undefined,
|
|
584
|
+
{ testOrigin: input.testOrigin },
|
|
580
585
|
);
|
|
581
586
|
pendingQuestionPersistence.write(pending);
|
|
582
587
|
pendingWritten += 1;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { selfDevRunsDir, type SelfDevRunState } from '../self-dev/run-store.js';
|
|
4
|
+
import { isGoalAuthorFileName } from './goal-document.js';
|
|
5
|
+
|
|
6
|
+
export interface GoalLookupOptions {
|
|
7
|
+
/** Defaults to the current repository's docs/goals directory. */
|
|
8
|
+
goalsDir?: string;
|
|
9
|
+
/** Defaults to the current instance's self-dev-runs directory. */
|
|
10
|
+
runsDir?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface GoalLookupRecord {
|
|
14
|
+
runId: string;
|
|
15
|
+
stage: string | null;
|
|
16
|
+
outcome: string | null;
|
|
17
|
+
prNumber: number | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The two sources are independent: null means that side did not match. */
|
|
21
|
+
export interface GoalLookupResult {
|
|
22
|
+
goalBody: string | null;
|
|
23
|
+
goalFile: string | null;
|
|
24
|
+
kanId: string | null;
|
|
25
|
+
runId: string | null;
|
|
26
|
+
record: GoalLookupRecord | null;
|
|
27
|
+
runState: SelfDevRunState | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function filesIn(directory: string): string[] {
|
|
31
|
+
try {
|
|
32
|
+
return readdirSync(directory, { withFileTypes: true })
|
|
33
|
+
.filter((entry) => entry.isFile())
|
|
34
|
+
.map((entry) => entry.name)
|
|
35
|
+
.sort();
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return [];
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type ValidResult = { runId: string; stage?: string | null; prNumber?: number | null };
|
|
43
|
+
|
|
44
|
+
function isValidResult(result: unknown): result is ValidResult {
|
|
45
|
+
if (result === null || typeof result !== 'object' || Array.isArray(result)) return false;
|
|
46
|
+
const r = result as { runId?: unknown; stage?: unknown; prNumber?: unknown };
|
|
47
|
+
return typeof r.runId === 'string' && r.runId.length > 0
|
|
48
|
+
&& (r.stage == null || typeof r.stage === 'string')
|
|
49
|
+
&& (r.prNumber == null || (typeof r.prNumber === 'number' && Number.isSafeInteger(r.prNumber) && r.prNumber > 0));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function prNumber(value: string | undefined): number | null {
|
|
53
|
+
if (!value || !/^[1-9]\d*$/.test(value)) return null;
|
|
54
|
+
const number = Number(value);
|
|
55
|
+
return Number.isSafeInteger(number) ? number : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function executionRecords(document: string): GoalLookupRecord[] {
|
|
59
|
+
const sections = document.split(/^## 실행 기록\s*$/m).slice(1);
|
|
60
|
+
return sections.flatMap((section) => {
|
|
61
|
+
const block = section.split(/^#{1,2} /m, 1)[0] ?? '';
|
|
62
|
+
const entries = block.split(/^- runId: /m).slice(1);
|
|
63
|
+
return entries.flatMap((entry) => {
|
|
64
|
+
const runId = entry.split(/\r?\n/, 1)[0]?.trim();
|
|
65
|
+
if (!runId) return [];
|
|
66
|
+
const field = (name: string): string | undefined =>
|
|
67
|
+
new RegExp(`^ ${name}: ([^\\r\\n]*)$`, 'm').exec(entry)?.[1]?.trim();
|
|
68
|
+
return [{ runId, stage: field('stage') ?? null, outcome: field('outcome') ?? null, prNumber: prNumber(field('prNumber')) }];
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Local, read-only lookup. A bare positive integer (or #number) is a PR; all other input is an exact run ID. */
|
|
74
|
+
export function lookupGoal(reference: string, options: GoalLookupOptions = {}): GoalLookupResult {
|
|
75
|
+
const input = reference.trim();
|
|
76
|
+
const requestedPr = prNumber(input.replace(/^#/, ''));
|
|
77
|
+
const requestedRunId = requestedPr === null ? input : null;
|
|
78
|
+
const runsDir = options.runsDir ?? selfDevRunsDir();
|
|
79
|
+
const findRun = (matches: (result: ValidResult) => boolean, wantedRunId: string | null) => {
|
|
80
|
+
for (const name of filesIn(runsDir)) {
|
|
81
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_-]*\.json$/.test(name)) continue;
|
|
82
|
+
let candidate: SelfDevRunState;
|
|
83
|
+
try {
|
|
84
|
+
candidate = JSON.parse(readFileSync(join(runsDir, name), 'utf8')) as SelfDevRunState;
|
|
85
|
+
} catch { continue; }
|
|
86
|
+
if (!candidate || candidate.runId !== name.slice(0, -'.json'.length) || !Array.isArray(candidate.results)) continue;
|
|
87
|
+
const matchingResult = (candidate.results as unknown[]).find((result): result is ValidResult => isValidResult(result) && matches(result));
|
|
88
|
+
if (matchingResult || (wantedRunId !== null && candidate.runId === wantedRunId)) {
|
|
89
|
+
const runId = matchingResult?.runId ?? candidate.runId;
|
|
90
|
+
const record: GoalLookupRecord | null = matchingResult
|
|
91
|
+
? { runId, stage: matchingResult.stage ?? null, outcome: null, prNumber: matchingResult.prNumber ?? null }
|
|
92
|
+
: null;
|
|
93
|
+
return { runState: candidate, runId, record };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
};
|
|
98
|
+
const found = findRun((result) => requestedPr !== null ? result.prNumber === requestedPr : result.runId === requestedRunId, requestedRunId);
|
|
99
|
+
let runState: SelfDevRunState | null = found?.runState ?? null;
|
|
100
|
+
const resultRunId: string | null = found?.runId ?? null;
|
|
101
|
+
let runRecord: GoalLookupRecord | null = found?.record ?? null;
|
|
102
|
+
|
|
103
|
+
const goalsDir = options.goalsDir ?? resolve(process.cwd(), 'docs', 'goals');
|
|
104
|
+
let fallback: { goalFile: string; document: string; record: GoalLookupRecord } | null = null;
|
|
105
|
+
let selected: { goalFile: string; document: string; record: GoalLookupRecord } | null = null;
|
|
106
|
+
for (const name of filesIn(goalsDir)) {
|
|
107
|
+
if (!isGoalAuthorFileName(name)) continue;
|
|
108
|
+
const goalFile = join(goalsDir, name);
|
|
109
|
+
let document: string;
|
|
110
|
+
try { document = readFileSync(goalFile, 'utf8'); } catch { continue; }
|
|
111
|
+
const records = executionRecords(document);
|
|
112
|
+
const direct = records.find((candidate) => requestedPr !== null
|
|
113
|
+
? candidate.prNumber === requestedPr
|
|
114
|
+
: candidate.runId === requestedRunId);
|
|
115
|
+
if (direct) { selected = { goalFile, document, record: direct }; break; }
|
|
116
|
+
if (requestedPr !== null && resultRunId !== null && fallback === null) {
|
|
117
|
+
const record = records.find((candidate) => candidate.prNumber === null && candidate.runId === resultRunId);
|
|
118
|
+
if (record) fallback = { goalFile, document, record };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const match = selected ?? fallback;
|
|
122
|
+
if (match) {
|
|
123
|
+
const { goalFile, document, record } = match;
|
|
124
|
+
// The returned run state must belong to the selected document record's run (review must-fix): re-resolve, or drop it.
|
|
125
|
+
if (resultRunId !== record.runId) {
|
|
126
|
+
const again = findRun((result) => result.runId === record.runId, record.runId);
|
|
127
|
+
runState = again?.runState ?? null;
|
|
128
|
+
runRecord = again?.record ?? null;
|
|
129
|
+
}
|
|
130
|
+
const body = document.split(/^## 실행 기록\s*$/m, 1)[0]!.replace(/\r?\n$/, '');
|
|
131
|
+
// A kan ID is only reported when the authored document explicitly names one.
|
|
132
|
+
const kanId = /^- Kan(?:Id|-ID):\s*(\S+)\s*$/im.exec(body)?.[1] ?? null;
|
|
133
|
+
return {
|
|
134
|
+
goalBody: body, goalFile, kanId, runId: record.runId,
|
|
135
|
+
record: {
|
|
136
|
+
...record,
|
|
137
|
+
...(runRecord?.runId === record.runId ? {
|
|
138
|
+
stage: record.stage ?? runRecord.stage,
|
|
139
|
+
outcome: record.outcome ?? runRecord.outcome,
|
|
140
|
+
prNumber: record.prNumber ?? runRecord.prNumber,
|
|
141
|
+
} : {}),
|
|
142
|
+
},
|
|
143
|
+
runState,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return { goalBody: null, goalFile: null, kanId: null, runId: resultRunId ?? requestedRunId, record: runRecord, runState };
|
|
147
|
+
}
|