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
|
@@ -16,7 +16,7 @@ export const TERMINAL_STAGES_BY_NODE = {
|
|
|
16
16
|
implement: ['aborted', 'timed-out', 'soft-stopped', 'parked'],
|
|
17
17
|
gate: [],
|
|
18
18
|
review: ['review-blocked'],
|
|
19
|
-
rework: ['gate-failed', 'review-blocked'],
|
|
19
|
+
rework: ['gate-failed', 'review-blocked', 'no-changes'],
|
|
20
20
|
'main-sync': ['merge-conflict'],
|
|
21
21
|
regate: ['gate-failed'],
|
|
22
22
|
'open-pr': ['pr-declined', 'pr-opened', 'worktree-completed'],
|
|
@@ -0,0 +1,97 @@
|
|
|
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[]; staleDeleted?: string[]; outsideDeleted?: string[]; warning?: string };
|
|
7
|
+
|
|
8
|
+
function isTargetPath(file: string, targets: readonly string[]): boolean {
|
|
9
|
+
return targets.some((target) => {
|
|
10
|
+
const path = target.replace(/^\.\//, '').replace(/\/+$/, '');
|
|
11
|
+
return path !== '' && (file === path || file.startsWith(`${path}/`));
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Compare the worktree's exact bytes with blobs from recent HEAD ancestry, before add -A. */
|
|
16
|
+
export function dropStaleReverts(cwd: string, targets: readonly string[], runId: string, depth = 50, base?: string): RevertGuardResult {
|
|
17
|
+
const result: RevertGuardResult = { reverted: [], protected: [] };
|
|
18
|
+
const git = (...args: string[]): Buffer => execFileSync('git', args, { cwd, timeout: 20_000, maxBuffer: 16 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
19
|
+
const reason = (error: unknown): string => (error instanceof Error ? error.message : String(error)).replace(/\s+/g, ' ').slice(0, 240);
|
|
20
|
+
try {
|
|
21
|
+
const limit = Number.isSafeInteger(depth) && depth > 0 ? depth : 50;
|
|
22
|
+
const commits = git('rev-list', `--max-count=${limit + 1}`, 'HEAD').toString('utf8').trim().split('\n');
|
|
23
|
+
const changed = git('diff', '--name-only', '--diff-filter=M', '-z', 'HEAD', '--').toString('utf8').split('\0').filter(Boolean);
|
|
24
|
+
const staged = git('diff', '--cached', '--name-only', '--diff-filter=M', '-z', 'HEAD', '--').toString('utf8').split('\0').filter(Boolean);
|
|
25
|
+
const deleted = new Set([
|
|
26
|
+
...git('diff', '--name-only', '--diff-filter=D', '-z', 'HEAD', '--').toString('utf8').split('\0').filter(Boolean),
|
|
27
|
+
...git('diff', '--cached', '--name-only', '--diff-filter=D', '-z', 'HEAD', '--').toString('utf8').split('\0').filter(Boolean),
|
|
28
|
+
]);
|
|
29
|
+
for (const file of deleted) {
|
|
30
|
+
if (isTargetPath(file, targets)) continue;
|
|
31
|
+
try {
|
|
32
|
+
// add -A stages the worktree: even a dangling symlink replaces the staged deletion.
|
|
33
|
+
try {
|
|
34
|
+
lstatSync(join(cwd, file));
|
|
35
|
+
continue;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
38
|
+
}
|
|
39
|
+
// ls-tree returning an empty entry means absent; a failed lookup must not look like absence.
|
|
40
|
+
const headEntry = git('ls-tree', 'HEAD', '--', file).toString('utf8').trim();
|
|
41
|
+
if (!/^\d+ blob [0-9a-f]+\t/.test(headEntry)) continue;
|
|
42
|
+
const baseEntry = base ? git('ls-tree', base, '--', file).toString('utf8').trim() : null;
|
|
43
|
+
if (baseEntry === '' && base) {
|
|
44
|
+
git('checkout', 'HEAD', '--', file);
|
|
45
|
+
(result.staleDeleted ??= []).push(file);
|
|
46
|
+
result.reverted.push(file);
|
|
47
|
+
debug.log('self-implement.revert-guard', 'reverted-stale-delete', { runId, files: [file] });
|
|
48
|
+
} else {
|
|
49
|
+
(result.outsideDeleted ??= []).push(file);
|
|
50
|
+
debug.log('self-implement.revert-guard', 'outside-delete', { runId, files: [file] }, { level: 'warn' });
|
|
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
|
+
for (const file of new Set([...changed, ...staged])) {
|
|
58
|
+
try {
|
|
59
|
+
if (!lstatSync(join(cwd, file)).isFile()) continue;
|
|
60
|
+
const current = readFileSync(join(cwd, file));
|
|
61
|
+
const rawOid = execFileSync('git', ['hash-object', '--stdin'], { cwd, input: current, timeout: 20_000 }).toString('utf8').trim();
|
|
62
|
+
const headEntry = git('ls-tree', 'HEAD', '--', file).toString('utf8').trim();
|
|
63
|
+
const head = /^\d+ blob ([0-9a-f]+)\t/.exec(headEntry)?.[1];
|
|
64
|
+
if (!head) continue;
|
|
65
|
+
const sameBytes = (oid: string): boolean => oid === rawOid || git('cat-file', 'blob', oid).equals(current);
|
|
66
|
+
if (sameBytes(head)) continue;
|
|
67
|
+
let matchedCommit: string | undefined;
|
|
68
|
+
const inspectedOids = new Set<string>();
|
|
69
|
+
for (const commit of commits.slice(1)) {
|
|
70
|
+
// A path may not have existed in an ancestor; that is not a failed inspection.
|
|
71
|
+
const blob = git('ls-tree', commit, '--', file).toString('utf8').trim();
|
|
72
|
+
const oid = /^\d+ blob ([0-9a-f]+)\t/.exec(blob)?.[1];
|
|
73
|
+
if (oid && !inspectedOids.has(oid)) {
|
|
74
|
+
if (sameBytes(oid)) { matchedCommit = commit; break; }
|
|
75
|
+
inspectedOids.add(oid);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!matchedCommit) continue;
|
|
79
|
+
if (isTargetPath(file, targets)) {
|
|
80
|
+
result.protected.push(file);
|
|
81
|
+
debug.log('self-implement.revert-guard', 'protected-target', { runId, files: [file], matchedCommit }, { level: 'warn' });
|
|
82
|
+
} else {
|
|
83
|
+
git('checkout', 'HEAD', '--', file);
|
|
84
|
+
result.reverted.push(file);
|
|
85
|
+
debug.log('self-implement.revert-guard', 'reverted-stale', { runId, files: [file], matchedCommit });
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
result.warning = `git inspection/checkout failed for ${file}: ${reason(error)}`;
|
|
89
|
+
debug.log('self-implement.revert-guard', 'fail-open', { runId, reason: result.warning }, { level: 'warn' });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
result.warning = `git inspection failed: ${reason(error)}`;
|
|
94
|
+
debug.log('self-implement.revert-guard', 'fail-open', { runId, reason: result.warning }, { level: 'warn' });
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync } from 'node:fs';
|
|
2
2
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
3
|
+
import { refuseProductionLedgerWriteInTest } from '../harness/ledger-write-guard.js';
|
|
3
4
|
import { tracedPathReferences, verbatimOriginalAsk } from './goal-author.js';
|
|
4
5
|
import { parseAskTargetPathHintsResult } from '../self-dev/launch-preflight.js';
|
|
5
6
|
import { isRunStatus } from './run-status-mapping.js';
|
|
@@ -276,7 +277,7 @@ interface RunLedgerLookupTarget {
|
|
|
276
277
|
targetName: string | null;
|
|
277
278
|
}
|
|
278
279
|
|
|
279
|
-
function resolveFederatedRunLedgerTargets(options: Pick<FederatedRunLedgerLookupOptions, 'includeTest' | 'targets'>): RunLedgerLookupTarget[] {
|
|
280
|
+
export function resolveFederatedRunLedgerTargets(options: Pick<FederatedRunLedgerLookupOptions, 'includeTest' | 'targets'>): RunLedgerLookupTarget[] {
|
|
280
281
|
const resolved = options.targets ? { targets: [...options.targets] } : resolveLogTargets({ all: true, includeTest: options.includeTest });
|
|
281
282
|
if (resolved.error) throw new Error(resolved.error);
|
|
282
283
|
const targets = new Map<string, string>();
|
|
@@ -402,6 +403,7 @@ export function describeFederatedMissingRunLedger(runId: string, options: Federa
|
|
|
402
403
|
|
|
403
404
|
/** Append one observer event. Callers own fail-soft handling. */
|
|
404
405
|
export function appendRunLedgerEntry(entry: RunLedgerEntry, dir = runLedgerDir()): void {
|
|
406
|
+
if (refuseProductionLedgerWriteInTest(dir, 'run-ledger')) return;
|
|
405
407
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
406
408
|
const live = runLedgerPath(entry.runId, dir);
|
|
407
409
|
withRunLedgerLock(dir, entry.runId, () => {
|
|
@@ -16,6 +16,7 @@ export type SelfImplementStage =
|
|
|
16
16
|
| 'worktree-completed'
|
|
17
17
|
| 'gate-failed'
|
|
18
18
|
| 'review-blocked'
|
|
19
|
+
| 'no-changes'
|
|
19
20
|
| 'merge-conflict'
|
|
20
21
|
| 'aborted'
|
|
21
22
|
| 'timed-out'
|
|
@@ -30,6 +31,7 @@ const STAGE_OUTCOMES = {
|
|
|
30
31
|
'worktree-completed': { runStatus: 'completed' },
|
|
31
32
|
'gate-failed': { runStatus: 'failed', failureKind: 'gate' },
|
|
32
33
|
'review-blocked': { runStatus: 'failed', failureKind: 'review' },
|
|
34
|
+
'no-changes': { runStatus: 'failed', failureKind: 'review' },
|
|
33
35
|
'merge-conflict': { runStatus: 'failed', failureKind: 'merge-conflict' },
|
|
34
36
|
aborted: { runStatus: 'failed', failureKind: 'aborted' },
|
|
35
37
|
'timed-out': { runStatus: 'failed', failureKind: 'timed-out' },
|
|
@@ -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,
|
|
@@ -2493,7 +2513,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
|
|
|
2493
2513
|
verdict: rr.verdict,
|
|
2494
2514
|
mustFix: rr.mustFix,
|
|
2495
2515
|
shouldFix: rr.shouldFix,
|
|
2496
|
-
summary: renderReview(rr).slice(0, 2000)
|
|
2516
|
+
summary: rr.reviewed ? renderReview(rr).slice(0, 2000) : `리뷰 못 함(사유: ${rr.failureReason ?? '사유 미상'})`,
|
|
2497
2517
|
reviewed: rr.reviewed ?? false,
|
|
2498
2518
|
// ⛔⭐ 이 한 줄이 `orchestrator.ts` 의 「찍는 줄」에 값을 «흘린다». 빼면 그 줄은 살아 있는데 값이 안 온다.
|
|
2499
2519
|
...(rr.failureReason !== undefined ? { failureReason: rr.failureReason } : {}),
|
|
@@ -108,6 +108,7 @@ export function singleRunAsJobResult(feature: string, r: SelfImplementResultWith
|
|
|
108
108
|
stage: r.stage,
|
|
109
109
|
...(!r.ok && r.detail ? { error: { message: r.detail } } : {}),
|
|
110
110
|
...(r.branch ? { branch: r.branch } : {}),
|
|
111
|
+
...(r.harvestable === true && r.branch ? { harvestable: true } : {}),
|
|
111
112
|
...(r.worktreePath ? { worktreePath: r.worktreePath } : {}),
|
|
112
113
|
...(r.prUrl ? { prUrl: r.prUrl } : {}),
|
|
113
114
|
...(r.merged !== undefined ? { merged: r.merged } : {}),
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { prodInstanceRoot } from '../instance/resolve.js';
|
|
6
|
+
import { seatOfTree } from '../loops/orchestrator/traffic.js';
|
|
7
|
+
import { getUserConfig, ORCHESTRATOR_DEFAULTS } from '../user-config.js';
|
|
8
|
+
import { isRunStatus } from './run-status-mapping.js';
|
|
9
|
+
import { loadRunLedger, runLedgerDir, type RunLedgerEntry, type RunLedgerReader } from './run-ledger.js';
|
|
10
|
+
|
|
11
|
+
export type SpeedClaimsOutcome = 'merged' | 'failed' | 'abandoned' | 'completed-without-merge';
|
|
12
|
+
export type SpeedClaimsSeat = 'OP' | 'TC' | 'MK' | 'UX' | 'unknown';
|
|
13
|
+
export interface SpeedClaimsRow {
|
|
14
|
+
seat: SpeedClaimsSeat;
|
|
15
|
+
outcome: SpeedClaimsOutcome;
|
|
16
|
+
count: number;
|
|
17
|
+
underOneMinuteCount: number;
|
|
18
|
+
medianMinutes: number;
|
|
19
|
+
q1Minutes: number;
|
|
20
|
+
q3Minutes: number;
|
|
21
|
+
}
|
|
22
|
+
export interface SpeedClaimsMeasurement {
|
|
23
|
+
scope: 'self-implement-run-ledger';
|
|
24
|
+
ledgerDirectory: string;
|
|
25
|
+
status: 'measured' | 'cannot-measure';
|
|
26
|
+
completedCount: number;
|
|
27
|
+
inProgressCount: number;
|
|
28
|
+
cannotMeasureCount: number;
|
|
29
|
+
unknownSeatCount: number;
|
|
30
|
+
rows: SpeedClaimsRow[];
|
|
31
|
+
note: string;
|
|
32
|
+
parent: ParentSpeedClaimsMeasurement;
|
|
33
|
+
}
|
|
34
|
+
export interface ParentSpeedClaimsEvent {
|
|
35
|
+
ts: string;
|
|
36
|
+
category: string;
|
|
37
|
+
event: string;
|
|
38
|
+
data: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ParentSpeedClaimsRow {
|
|
42
|
+
seat: 'OP' | 'TC' | 'MK' | 'UX' | 'direct' | 'unknown';
|
|
43
|
+
stage: string;
|
|
44
|
+
merged: boolean;
|
|
45
|
+
count: number;
|
|
46
|
+
medianMinutes: number;
|
|
47
|
+
q1Minutes: number;
|
|
48
|
+
q3Minutes: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ParentSpeedClaimsMeasurement {
|
|
52
|
+
scope: 'self-dev.orchestrate-parent-logs';
|
|
53
|
+
status: 'measured' | 'cannot-measure';
|
|
54
|
+
completedCount: number;
|
|
55
|
+
inProgressCount: number;
|
|
56
|
+
cannotMeasureCount: number;
|
|
57
|
+
rows: ParentSpeedClaimsRow[];
|
|
58
|
+
note: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface SpeedClaimsOptions {
|
|
62
|
+
dir?: string;
|
|
63
|
+
list?: (path: string) => string[];
|
|
64
|
+
read?: RunLedgerReader;
|
|
65
|
+
readSeat?: (runId: string, ledgerDirectory: string, entries: readonly RunLedgerEntry[]) => SpeedClaimsSeat;
|
|
66
|
+
readParentEvents?: () => readonly ParentSpeedClaimsEvent[];
|
|
67
|
+
readParentQueue?: () => unknown;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const NOTE = 'Child-ledger elapsed minutes from the first child event (or checkpoint creation when unavailable) to the recorded child end, including retries and resumes; this is not the parent launch-to-done interval. Only finished runs enter quartiles (linear interpolation at ranks (n-1)*p). In-progress runs (including parked) and missing, unreadable or invalid timestamps are excluded, not assigned zero. Seats come from the same state root checkpoint, launch queue or recorded launch tree; unresolved seats are counted separately. A completed run without a recorded merge is not a merge.';
|
|
71
|
+
const CANONICAL_RUN_ID = /^run-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
72
|
+
const SEATS: readonly SpeedClaimsSeat[] = ['OP', 'TC', 'MK', 'UX', 'unknown'];
|
|
73
|
+
|
|
74
|
+
function checkpointFor(runId: string, ledgerDirectory: string): Record<string, unknown> | null {
|
|
75
|
+
try {
|
|
76
|
+
const value: unknown = JSON.parse(readFileSync(join(dirname(ledgerDirectory), 'self-dev-runs', `${runId}.json`), 'utf8'));
|
|
77
|
+
return value && typeof value === 'object' && !Array.isArray(value) && 'runId' in value && value.runId === runId
|
|
78
|
+
? value as Record<string, unknown> : null;
|
|
79
|
+
} catch { return null; }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function seatFromLaunch(runId: string, ledgerDirectory: string, entries: readonly RunLedgerEntry[]): SpeedClaimsSeat {
|
|
83
|
+
const checkpoint = checkpointFor(runId, ledgerDirectory);
|
|
84
|
+
if (SEATS.includes(checkpoint?.seat as SpeedClaimsSeat) && checkpoint?.seat !== 'unknown') return checkpoint!.seat as SpeedClaimsSeat;
|
|
85
|
+
// Queue ids and launch ids are not run ids. Only an exact, unique ask-file match can attribute a queued launch.
|
|
86
|
+
const goalFiles = new Set(entries.filter((entry) => entry.event === 'start')
|
|
87
|
+
.map((entry) => entry.data.goalFile).filter((file): file is string => typeof file === 'string' && isAbsolute(file)));
|
|
88
|
+
if (goalFiles.size === 1) {
|
|
89
|
+
try {
|
|
90
|
+
const queue: unknown = JSON.parse(readFileSync(join(dirname(ledgerDirectory), 'harness', 'queue.json'), 'utf8'));
|
|
91
|
+
if (Array.isArray(queue)) {
|
|
92
|
+
const matched = queue.filter((row) => row && typeof row === 'object' && row.kind === 'ask'
|
|
93
|
+
&& (row.status === 'launching' || row.status === 'launched' || row.status === 'finished')
|
|
94
|
+
&& row.input === [...goalFiles][0] && SEATS.includes(row.seat));
|
|
95
|
+
if (matched.length === 1 && matched[0]!.seat !== 'unknown') return matched[0]!.seat as SpeedClaimsSeat;
|
|
96
|
+
}
|
|
97
|
+
} catch { /* An absent or unreadable queue cannot establish attribution. */ }
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const cfg = getUserConfig().loops?.orchestrator ?? ORCHESTRATOR_DEFAULTS;
|
|
101
|
+
const paths = entries.flatMap((entry) => entry.event === 'start' ? [entry.data.targetRoot] : entry.event === 'worktree' ? [entry.data.path] : []);
|
|
102
|
+
const seats = new Set<SpeedClaimsSeat>();
|
|
103
|
+
for (const path of paths) {
|
|
104
|
+
if (typeof path !== 'string' || !isAbsolute(path)) continue;
|
|
105
|
+
try {
|
|
106
|
+
const seat = seatOfTree(path, cfg);
|
|
107
|
+
if (seat) seats.add(seat);
|
|
108
|
+
} catch { /* A removed or unreadable launch tree does not prove a seat. */ }
|
|
109
|
+
}
|
|
110
|
+
if (seats.size === 1) return [...seats][0]!;
|
|
111
|
+
} catch { /* Unavailable seat configuration does not erase a readable run. */ }
|
|
112
|
+
return 'unknown';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function parseTime(timestamp: unknown): number | null {
|
|
116
|
+
if (typeof timestamp !== 'string') return null;
|
|
117
|
+
const match = /^(\d{4})-(\d\d)-(\d\d)T\d\d:\d\d:\d\d(?:\.\d+)?(?:Z|[+-]\d\d:\d\d)$/.exec(timestamp);
|
|
118
|
+
if (!match) return null;
|
|
119
|
+
const year = Number(match[1]);
|
|
120
|
+
const month = Number(match[2]);
|
|
121
|
+
const day = Number(match[3]);
|
|
122
|
+
const days = [31, year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
123
|
+
if (month < 1 || month > 12 || day < 1 || day > days[month - 1]!) return null;
|
|
124
|
+
const parsed = Date.parse(timestamp);
|
|
125
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function time(entry: RunLedgerEntry | undefined): number | null {
|
|
129
|
+
return parseTime(entry?.timestamp);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function percentile(sorted: readonly number[], p: number): number {
|
|
133
|
+
const rank = (sorted.length - 1) * p;
|
|
134
|
+
const low = Math.floor(rank);
|
|
135
|
+
return sorted[low]! + (sorted[Math.ceil(rank)]! - sorted[low]!) * (rank - low);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function endOfRun(entries: readonly RunLedgerEntry[]): { entry: RunLedgerEntry; outcome: SpeedClaimsOutcome } | null {
|
|
139
|
+
let end: { entry: RunLedgerEntry; outcome: SpeedClaimsOutcome } | null = null;
|
|
140
|
+
for (const entry of entries) {
|
|
141
|
+
if (entry.event === 'start') { end = null; continue; }
|
|
142
|
+
if (entry.event === 'merged' && entry.data.merged === true) end = { entry, outcome: 'merged' };
|
|
143
|
+
if (end?.outcome === 'merged') continue;
|
|
144
|
+
if (entry.event === 'human-stop') end = { entry, outcome: 'abandoned' };
|
|
145
|
+
if (entry.event === 'run-status' && isRunStatus(entry.data.runStatus)) {
|
|
146
|
+
const status = entry.data.runStatus;
|
|
147
|
+
if (status === 'completed') end = { entry, outcome: 'completed-without-merge' };
|
|
148
|
+
else if (status === 'failed') end = { entry, outcome: entry.data.stage === 'aborted' ? 'abandoned' : 'failed' };
|
|
149
|
+
else if (status === 'cancelled') end = { entry, outcome: 'abandoned' };
|
|
150
|
+
else end = null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return end;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const PARENT_NOTE = 'Elapsed minutes from the earliest valid parent self-dev.orchestrate start log to the latest valid done log, grouped by that done.data.outcomes[].stage and merged flag. Only runs with both events and valid timestamps enter quartiles. A start without done is in progress. Seats use the same-runId queue seat when available, or direct when a readable queue has no matching entry; an unreadable queue remains unknown. Parent log queries are read-only and independent of child run ledgers.';
|
|
157
|
+
type ParentSeat = ParentSpeedClaimsRow['seat'];
|
|
158
|
+
const PARENT_SEATS: readonly ParentSeat[] = ['OP', 'TC', 'MK', 'UX', 'direct', 'unknown'];
|
|
159
|
+
|
|
160
|
+
function object(value: unknown): Record<string, unknown> | null {
|
|
161
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Read the first-class logs CLI rather than opening logs.db in the measurement process. */
|
|
165
|
+
function readParentEventsFromLogs(): ParentSpeedClaimsEvent[] {
|
|
166
|
+
const command = fileURLToPath(new URL('../../bin/elanous.mjs', import.meta.url));
|
|
167
|
+
const events: ParentSpeedClaimsEvent[] = [];
|
|
168
|
+
let before: string | undefined;
|
|
169
|
+
const seenCursors = new Set<string>();
|
|
170
|
+
for (;;) {
|
|
171
|
+
const result = spawnSync(process.execPath, [command, `--test=${fileURLToPath(new URL('../../', import.meta.url))}`, 'logs', '--instance', 'prod', '--category', 'self-dev.orchestrate', '--json', '--json-data', '--limit', '5000', ...(before ? ['--before', before] : [])], {
|
|
172
|
+
encoding: 'utf8', maxBuffer: 64 * 1024 * 1024,
|
|
173
|
+
});
|
|
174
|
+
if (result.error || result.status !== 0) throw new Error(result.stderr || result.error?.message || 'parent logs query failed');
|
|
175
|
+
const lines = result.stdout.trim().split('\n').filter(Boolean).map((line) => JSON.parse(line) as Record<string, unknown>);
|
|
176
|
+
if (lines.some((line) => object(line._meta)?.type === 'log-query-unreadable-instances')) throw new Error('parent logs query incomplete');
|
|
177
|
+
events.push(...lines.filter((line): line is Record<string, unknown> & ParentSpeedClaimsEvent =>
|
|
178
|
+
line.store === 'prod' && line.category === 'self-dev.orchestrate' && typeof line.ts === 'string' && typeof line.event === 'string'));
|
|
179
|
+
const limit = lines.find((line) => object(line._meta)?.type === 'log-query-limit');
|
|
180
|
+
if (!limit) return events;
|
|
181
|
+
const meta = object(limit._meta);
|
|
182
|
+
const cursor = meta?.nextCursors ?? meta?.nextCursor;
|
|
183
|
+
if (typeof cursor !== 'number' && !object(cursor)) throw new Error('parent logs pagination incomplete');
|
|
184
|
+
const next = typeof cursor === 'number' ? String(cursor) : JSON.stringify(cursor);
|
|
185
|
+
if (seenCursors.has(next)) throw new Error('parent logs pagination repeated a cursor');
|
|
186
|
+
seenCursors.add(next);
|
|
187
|
+
before = next;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function readParentQueueFromState(): unknown {
|
|
192
|
+
return JSON.parse(readFileSync(join(prodInstanceRoot(), 'harness', 'queue.json'), 'utf8')) as unknown;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Parent launch-to-done census; no child ledger id is used as a join key. */
|
|
196
|
+
export function measureParentSpeedClaims(
|
|
197
|
+
events: readonly ParentSpeedClaimsEvent[], queue?: unknown,
|
|
198
|
+
): ParentSpeedClaimsMeasurement {
|
|
199
|
+
const result: ParentSpeedClaimsMeasurement = {
|
|
200
|
+
scope: 'self-dev.orchestrate-parent-logs', status: 'measured', completedCount: 0,
|
|
201
|
+
inProgressCount: 0, cannotMeasureCount: 0, rows: [], note: PARENT_NOTE,
|
|
202
|
+
};
|
|
203
|
+
const queueRows = Array.isArray(queue) ? queue : object(queue)?.items;
|
|
204
|
+
const queueByRun = new Map<string, ParentSeat | null>();
|
|
205
|
+
if (Array.isArray(queueRows)) for (const raw of queueRows) {
|
|
206
|
+
const entry = object(raw);
|
|
207
|
+
if (entry && typeof entry.runId === 'string' && PARENT_SEATS.includes(entry.seat as ParentSeat) && entry.seat !== 'direct') {
|
|
208
|
+
const seat = entry.seat as ParentSeat;
|
|
209
|
+
queueByRun.set(entry.runId, queueByRun.has(entry.runId) && queueByRun.get(entry.runId) !== seat ? null : seat);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const runs = new Map<string, { start?: number; end?: number; outcomes?: unknown }>();
|
|
213
|
+
for (const event of events) {
|
|
214
|
+
if (event.category !== 'self-dev.orchestrate' || (event.event !== 'start' && event.event !== 'done')) continue;
|
|
215
|
+
const data = object(event.data);
|
|
216
|
+
const runId = data?.runId;
|
|
217
|
+
if (typeof runId !== 'string' || !CANONICAL_RUN_ID.test(runId)) { result.cannotMeasureCount++; continue; }
|
|
218
|
+
const run = runs.get(runId) ?? {};
|
|
219
|
+
const at = parseTime(event.ts);
|
|
220
|
+
if (event.event === 'start') {
|
|
221
|
+
if (at !== null) run.start = run.start === undefined || !Number.isFinite(run.start) ? at : Math.min(run.start, at);
|
|
222
|
+
else if (run.start === undefined) run.start = NaN;
|
|
223
|
+
} else if (at !== null && (run.end === undefined || !Number.isFinite(run.end) || at > run.end)) {
|
|
224
|
+
run.end = at;
|
|
225
|
+
run.outcomes = data?.outcomes;
|
|
226
|
+
} else if (at === null && run.end === undefined) {
|
|
227
|
+
run.end = NaN;
|
|
228
|
+
}
|
|
229
|
+
runs.set(runId, run);
|
|
230
|
+
}
|
|
231
|
+
const groups = new Map<string, { seat: ParentSeat; stage: string; merged: boolean; values: number[] }>();
|
|
232
|
+
for (const [runId, run] of runs) {
|
|
233
|
+
if (run.start === undefined && run.end !== undefined) { result.cannotMeasureCount++; continue; }
|
|
234
|
+
if (run.end === undefined) { result.inProgressCount++; continue; }
|
|
235
|
+
if (run.start === undefined || !Number.isFinite(run.start) || !Number.isFinite(run.end) || run.end < run.start || !Array.isArray(run.outcomes) || run.outcomes.length === 0) {
|
|
236
|
+
result.cannotMeasureCount++;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const seat = queueByRun.has(runId) ? (queueByRun.get(runId) ?? 'unknown') : Array.isArray(queueRows) ? 'direct' : 'unknown';
|
|
240
|
+
const outcomes = run.outcomes.map(object);
|
|
241
|
+
if (outcomes.some((outcome) => !outcome)) {
|
|
242
|
+
result.cannotMeasureCount++;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
for (const outcome of outcomes) {
|
|
246
|
+
const stage = typeof outcome!.stage === 'string' ? outcome!.stage : 'unknown';
|
|
247
|
+
const merged = outcome!.merged === true;
|
|
248
|
+
const key = JSON.stringify([seat, stage, merged]);
|
|
249
|
+
const group = groups.get(key) ?? { seat, stage, merged, values: [] };
|
|
250
|
+
group.values.push((run.end - run.start!) / 60_000);
|
|
251
|
+
groups.set(key, group);
|
|
252
|
+
}
|
|
253
|
+
result.completedCount++;
|
|
254
|
+
}
|
|
255
|
+
result.rows = [...groups.values()].map(({ seat, stage, merged, values }) => {
|
|
256
|
+
values.sort((a, b) => a - b);
|
|
257
|
+
return { seat, stage, merged, count: values.length, medianMinutes: percentile(values, 0.5), q1Minutes: percentile(values, 0.25), q3Minutes: percentile(values, 0.75) };
|
|
258
|
+
}).sort((a, b) => PARENT_SEATS.indexOf(a.seat) - PARENT_SEATS.indexOf(b.seat) || a.stage.localeCompare(b.stage));
|
|
259
|
+
if (result.cannotMeasureCount > 0) result.status = 'cannot-measure';
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Read-only elapsed-time census of canonical local run ledgers, including unsuccessful runs. */
|
|
264
|
+
export function measureSpeedClaims(options: SpeedClaimsOptions = {}): SpeedClaimsMeasurement {
|
|
265
|
+
const ledgerDirectory = resolve(options.dir ?? runLedgerDir());
|
|
266
|
+
let parent: ParentSpeedClaimsMeasurement;
|
|
267
|
+
try {
|
|
268
|
+
const events = (options.readParentEvents ?? readParentEventsFromLogs)();
|
|
269
|
+
let queue: unknown;
|
|
270
|
+
try { queue = (options.readParentQueue ?? readParentQueueFromState)(); } catch { /* Queue absence does not erase parent observations or establish a direct launch. */ }
|
|
271
|
+
parent = measureParentSpeedClaims(events, queue);
|
|
272
|
+
} catch {
|
|
273
|
+
parent = { ...measureParentSpeedClaims([]), status: 'cannot-measure' };
|
|
274
|
+
}
|
|
275
|
+
const result: SpeedClaimsMeasurement = {
|
|
276
|
+
scope: 'self-implement-run-ledger', ledgerDirectory, status: 'measured',
|
|
277
|
+
completedCount: 0, inProgressCount: 0, cannotMeasureCount: 0, unknownSeatCount: 0, rows: [], note: NOTE, parent,
|
|
278
|
+
};
|
|
279
|
+
let files: string[];
|
|
280
|
+
try { files = (options.list ?? readdirSync)(ledgerDirectory); }
|
|
281
|
+
catch { return { ...result, status: 'cannot-measure' }; }
|
|
282
|
+
|
|
283
|
+
const groups = new Map<string, { seat: SpeedClaimsSeat; outcome: SpeedClaimsOutcome; values: number[] }>();
|
|
284
|
+
for (const file of files.sort()) {
|
|
285
|
+
const runId = file.endsWith('.jsonl') ? file.slice(0, -'.jsonl'.length) : '';
|
|
286
|
+
if (!CANONICAL_RUN_ID.test(runId)) continue;
|
|
287
|
+
try {
|
|
288
|
+
const entries = loadRunLedger(runId, ledgerDirectory, options.read);
|
|
289
|
+
if (!entries || entries.length === 0) { result.cannotMeasureCount++; continue; }
|
|
290
|
+
const end = endOfRun(entries);
|
|
291
|
+
const first = time(entries[0]);
|
|
292
|
+
const createdAt = checkpointFor(runId, ledgerDirectory)?.createdAt;
|
|
293
|
+
const creation = typeof createdAt === 'number' && Number.isFinite(createdAt) && createdAt >= 0 ? createdAt : null;
|
|
294
|
+
const start = first === null ? creation : first;
|
|
295
|
+
const endTime = end ? time(end.entry) : null;
|
|
296
|
+
if (start === null || (end && (endTime === null || endTime < start))) {
|
|
297
|
+
result.cannotMeasureCount++;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (!end) { result.inProgressCount++; continue; }
|
|
301
|
+
const seat = (options.readSeat ?? seatFromLaunch)(runId, ledgerDirectory, entries);
|
|
302
|
+
const safeSeat = SEATS.includes(seat) ? seat : 'unknown';
|
|
303
|
+
if (safeSeat === 'unknown') result.unknownSeatCount++;
|
|
304
|
+
const key = `${safeSeat}:${end.outcome}`;
|
|
305
|
+
const group = groups.get(key) ?? { seat: safeSeat, outcome: end.outcome, values: [] };
|
|
306
|
+
group.values.push((endTime! - start) / 60_000);
|
|
307
|
+
groups.set(key, group);
|
|
308
|
+
result.completedCount++;
|
|
309
|
+
} catch { result.cannotMeasureCount++; }
|
|
310
|
+
}
|
|
311
|
+
result.rows = [...groups.values()].map(({ seat, outcome, values }) => {
|
|
312
|
+
values.sort((a, b) => a - b);
|
|
313
|
+
return { seat, outcome, count: values.length, underOneMinuteCount: values.filter((minutes) => minutes < 1).length, medianMinutes: percentile(values, 0.5), q1Minutes: percentile(values, 0.25), q3Minutes: percentile(values, 0.75) };
|
|
314
|
+
}).sort((a, b) => SEATS.indexOf(a.seat) - SEATS.indexOf(b.seat) || a.outcome.localeCompare(b.outcome));
|
|
315
|
+
if (result.cannotMeasureCount > 0) result.status = 'cannot-measure';
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export function renderSpeedClaims(result: SpeedClaimsMeasurement): string {
|
|
320
|
+
return [
|
|
321
|
+
'child run-ledger (child start → child end):',
|
|
322
|
+
`ledger directory: ${result.ledgerDirectory}`,
|
|
323
|
+
`status: ${result.status}`,
|
|
324
|
+
`finished (quartile denominator): ${result.completedCount}`,
|
|
325
|
+
`in progress (excluded): ${result.inProgressCount}`,
|
|
326
|
+
`cannot measure (excluded): ${result.cannotMeasureCount}`,
|
|
327
|
+
`unknown seat (finished): ${result.unknownSeatCount}`,
|
|
328
|
+
...result.rows.map((row) => `seat=${row.seat} outcome=${row.outcome} count=${row.count} underOneMinute=${row.underOneMinuteCount} median=${row.medianMinutes}m q1=${row.q1Minutes}m q3=${row.q3Minutes}m`),
|
|
329
|
+
`note: ${result.note}`,
|
|
330
|
+
'parent launch → done (self-dev.orchestrate):',
|
|
331
|
+
`parent status: ${result.parent.status}`,
|
|
332
|
+
`parent finished (quartile denominator): ${result.parent.completedCount}`,
|
|
333
|
+
`parent in progress (excluded): ${result.parent.inProgressCount}`,
|
|
334
|
+
`parent cannot measure (excluded): ${result.parent.cannotMeasureCount}`,
|
|
335
|
+
...result.parent.rows.map((row) => `seat=${row.seat} stage=${row.stage} merged=${row.merged} count=${row.count} median=${row.medianMinutes}m q1=${row.q1Minutes}m q3=${row.q3Minutes}m`),
|
|
336
|
+
`parent note: ${result.parent.note}`,
|
|
337
|
+
].join('\n');
|
|
338
|
+
}
|
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. */
|