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
package/src/cli/release-cli.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// elanous release verify [--version <x.y.z>]
|
|
7
7
|
// elanous release notes --from <ref> [--to <ref>]
|
|
8
8
|
// elanous release run --version <x.y.z> [--cut-commit <sha>] [--dry-run] [--json] [--if-ready]
|
|
9
|
+
// elanous release auto-start [--window-minutes <n>] [--apply] [--json]
|
|
9
10
|
// elanous release cut-branch --version <x.y.z> --base <sha> --pick <sha>... [--dry-run]
|
|
10
11
|
//
|
|
11
12
|
// 🩸 계기(2026-09-25 v0.1.0 첫 공개): 손으로 밟은 절차에서 둘을 빠뜨릴 뻔했다 — 공개본 git 커밋(판 커밋이 공개본 커밋이 된다) ·
|
|
@@ -28,14 +29,18 @@ import { addItem, claimItem, decodeClaimHistoryEntry, devVersion, listChecklist,
|
|
|
28
29
|
import * as features from '../release-loop/feature-store.js';
|
|
29
30
|
import { evidencePlan, landedButYellow, type MergedChecklistPr } from '../release-loop/landed-but-yellow.js';
|
|
30
31
|
import { formatSchedule, getSchedule, listSchedules, setSchedule } from '../release-loop/release-schedule.js';
|
|
32
|
+
import { placeCell, rebalance, seatMove, type PlacementPriority } from '../release-loop/placement.js';
|
|
31
33
|
import { writeStdoutJson } from './stdout-json.js';
|
|
32
34
|
import { CliUserError } from './cli-user-error.js';
|
|
33
35
|
import { hqCliWriteAllowed, type HqDeps } from '../hq/hq.js';
|
|
34
36
|
import { fileLeaseStore } from '../hq/lease.js';
|
|
37
|
+
import { isIsolatedLedgerWriteRoot } from '../hq/ledger-write-target.js';
|
|
35
38
|
import { runUnattendedRelease, type UnattendedReleaseDeps } from '../../scripts/release-loop/unattended-release.js';
|
|
36
39
|
import { cutReleaseBranch } from '../../scripts/release-loop/cut-branch.js';
|
|
37
40
|
import { releaseReadiness } from '../../scripts/release-loop/release-readiness.js';
|
|
38
41
|
import { runReleaseIfReady } from './release-run-if-ready.js';
|
|
42
|
+
import { autoStartScheduledRelease, type AutoStartDeferral } from '../release-loop/auto-start.js';
|
|
43
|
+
import { landingFreezeMessage, LandingFrozenError, readLandingFreeze } from '../release-loop/landing-freeze.js';
|
|
39
44
|
|
|
40
45
|
export const DEFAULT_PUBLIC_REPO = 'ElanvitalAI/elanous';
|
|
41
46
|
const SEMVER = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
|
|
@@ -459,7 +464,8 @@ async function jsonAction<T>(json: boolean | undefined, body: (log: (l: string)
|
|
|
459
464
|
if (json) process.stdout.write(`${JSON.stringify({ ok: ok(r), ...(r as object) })}\n`);
|
|
460
465
|
if (!ok(r) && !process.exitCode) process.exitCode = 1;
|
|
461
466
|
} catch (e) {
|
|
462
|
-
|
|
467
|
+
const frozen = e instanceof LandingFrozenError ? { reason: 'frozen' as const } : {};
|
|
468
|
+
if (json) process.stdout.write(`${JSON.stringify({ ok: false, ...frozen, error: (e as Error).message })}\n`);
|
|
463
469
|
else console.error(`⛔ ${(e as Error).message}`);
|
|
464
470
|
process.exitCode = 1;
|
|
465
471
|
}
|
|
@@ -494,7 +500,7 @@ async function checklistOutput(v: string, codenames: Record<string, string>, mod
|
|
|
494
500
|
console.log(`⚠ 짝 경고 ${parity.length}: ${parity.map(({ id }) => id).join(', ') || '없음'}`);
|
|
495
501
|
console.log(`${v}${result.codename ? ` (${result.codename})` : ''} · 공개 ${data.released || '없음'} · 개발 ${data.dev}`);
|
|
496
502
|
if (mode === 'list') {
|
|
497
|
-
for (const item of data.items) console.log(`${({ green: '🟢', yellow: '🟡', red: '🔴', done: '✅' } as const)[item.status]} ${item.id} ${item.title}${item.owner ? ` · ${item.owner}` : ''}`);
|
|
503
|
+
for (const item of data.items) console.log(`${({ green: '🟢', yellow: '🟡', red: '🔴', done: '✅' } as const)[item.status]} ${item.id} ${item.title}${item.owner ? ` · ${item.owner}` : ''}${item.ceoMinutes !== undefined ? ` · 대표 손 ${item.ceoMinutes}분${item.ceoDate ? ` (${item.ceoDate})` : ''}` : ''}`);
|
|
498
504
|
} else {
|
|
499
505
|
console.log(`🟢 ${summary.green} · 🟡 ${summary.yellow} · 🔴 ${summary.red} · ✅ ${summary.done}`);
|
|
500
506
|
console.log(`🔴 칸: ${summary.blocked.join(', ') || '없음'}`);
|
|
@@ -511,6 +517,46 @@ export interface LandedButYellowDeps {
|
|
|
511
517
|
released?: typeof features.releasedVersion;
|
|
512
518
|
}
|
|
513
519
|
|
|
520
|
+
const MERGED_PR_LIMIT = 400;
|
|
521
|
+
|
|
522
|
+
function mergedChecklistPrs(since: string, now: Date, run: Runner): { prs: MergedChecklistPr[]; truncated: boolean } {
|
|
523
|
+
const cwd = process.cwd();
|
|
524
|
+
const fetch = (lower: string, upper?: string, limit = MERGED_PR_LIMIT): MergedChecklistPr[] => {
|
|
525
|
+
const search = `merged:>=${lower}${upper ? ` merged:<${upper}` : ''}`;
|
|
526
|
+
const raw = must(run('gh', ['pr', 'list', '--state', 'merged', '--search', search, '--limit', String(limit), '--json', 'number,title,body,mergedAt'], cwd), '병합 PR 조회(gh)');
|
|
527
|
+
const page = JSON.parse(raw) as MergedChecklistPr[];
|
|
528
|
+
if (!Array.isArray(page)) throw new Error('병합 PR 조회(gh): 배열이 아니다');
|
|
529
|
+
return page;
|
|
530
|
+
};
|
|
531
|
+
const unique = new Map<number, MergedChecklistPr>();
|
|
532
|
+
let truncated = false;
|
|
533
|
+
const collect = (page: MergedChecklistPr[]) => {
|
|
534
|
+
for (const pr of page) if (!unique.has(pr.number)) unique.set(pr.number, pr);
|
|
535
|
+
};
|
|
536
|
+
const first = fetch(since);
|
|
537
|
+
if (first.length < MERGED_PR_LIMIT) return { prs: first, truncated: false };
|
|
538
|
+
const lower = Date.parse(`${since}T00:00:00Z`);
|
|
539
|
+
const upper = Math.max(lower + 1000, Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));
|
|
540
|
+
const visit = (start: number, end: number): void => {
|
|
541
|
+
const from = new Date(start).toISOString();
|
|
542
|
+
const to = new Date(end).toISOString();
|
|
543
|
+
const page = fetch(from, to);
|
|
544
|
+
if (page.length < MERGED_PR_LIMIT) { collect(page); return; }
|
|
545
|
+
if (end - start <= 1000) {
|
|
546
|
+
// The second-precision search cannot be subdivided; probe one extra row to distinguish exactly 400 from a real truncation.
|
|
547
|
+
const probe = fetch(from, to, MERGED_PR_LIMIT + 1);
|
|
548
|
+
collect(probe.slice(0, MERGED_PR_LIMIT));
|
|
549
|
+
if (probe.length > MERGED_PR_LIMIT) truncated = true;
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const middle = start + Math.floor((end - start) / 2000) * 1000;
|
|
553
|
+
visit(start, middle);
|
|
554
|
+
visit(middle, end);
|
|
555
|
+
};
|
|
556
|
+
visit(lower, upper);
|
|
557
|
+
return { prs: [...unique.values()], truncated };
|
|
558
|
+
}
|
|
559
|
+
|
|
514
560
|
function printLandedRows(rows: ReturnType<typeof landedButYellow>, items: ReturnType<typeof listChecklist>['items']): void {
|
|
515
561
|
const updatedAtById = new Map(items.map((item) => [item.id, item.updatedAt]));
|
|
516
562
|
const date = (iso: string) => {
|
|
@@ -544,6 +590,13 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
|
|
|
544
590
|
const ledgerFenceDeps = (): HqDeps => getElanousConfigDirOverride() && !hqDeps.store && !getUserConfig().hq?.arbiter
|
|
545
591
|
? { ...hqDeps, store: fileLeaseStore(join(effectiveInstanceRoot(), 'hq', 'lease.json')), seenPath: join(effectiveInstanceRoot(), 'hq', 'seen-generation'), localPath: join(effectiveInstanceRoot(), 'hq', 'local.json') }
|
|
546
592
|
: hqDeps;
|
|
593
|
+
const mayWriteLedger = (command: string, override: boolean, ledgerRoot: string) => {
|
|
594
|
+
if (isIsolatedLedgerWriteRoot(ledgerRoot)) {
|
|
595
|
+
try { debug.log('hq.fence', 'skipped-isolated', { root: ledgerRoot, command }); } catch { /* observation is fail-soft */ }
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
return hqCliWriteAllowed(command, override, ledgerFenceDeps());
|
|
599
|
+
};
|
|
547
600
|
schedule.command('list').option('--json', '결과 JSON').action((o: { json?: boolean }) => {
|
|
548
601
|
const rows = listSchedules(scheduleLedgerRoot());
|
|
549
602
|
if (o.json) console.log(JSON.stringify(rows));
|
|
@@ -556,18 +609,50 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
|
|
|
556
609
|
if (o.json) console.log(JSON.stringify(row)); else console.log(formatSchedule(row));
|
|
557
610
|
});
|
|
558
611
|
schedule.command('set').requiredOption('--version <v>', '갱신할 판')
|
|
559
|
-
.option('--cut-at <iso>', '오프셋 포함 컷 시각').option('--land-by <iso>', '오프셋 포함 착지 마감')
|
|
560
|
-
.
|
|
561
|
-
|
|
562
|
-
if (
|
|
563
|
-
|
|
612
|
+
.option('--cut-at <iso>', '오프셋 포함 컷 시각').option('--land-by <iso>', '오프셋 포함 착지 마감')
|
|
613
|
+
.option('--freeze-from <iso>', '동결 시작').option('--freeze-until <iso>', '동결 종료').option('--json', '결과 JSON').option('--hq-override')
|
|
614
|
+
.action((o: { version: string; cutAt?: string; landBy?: string; freezeFrom?: string; freezeUntil?: string; json?: boolean; hqOverride?: boolean }) => {
|
|
615
|
+
if (o.cutAt === undefined && o.landBy === undefined && o.freezeFrom === undefined && o.freezeUntil === undefined) throw new CliUserError('갱신할 시각을 지정하라', '--cut-at <iso> 또는 --land-by <iso>');
|
|
616
|
+
if (!mayWriteLedger('release schedule set', Boolean(o.hqOverride), scheduleLedgerRoot())) return;
|
|
617
|
+
const row = setSchedule(o.version, { cutAt: o.cutAt, landBy: o.landBy, freezeFrom: o.freezeFrom, freezeUntil: o.freezeUntil }, process.env.ELANOUS_TRACK || 'cli', scheduleLedgerRoot());
|
|
564
618
|
if (o.json) console.log(JSON.stringify(row)); else console.log(formatSchedule(row));
|
|
565
619
|
});
|
|
620
|
+
release.command('place <id>').description('칸의 우선순위·마감·용량으로 판 배치')
|
|
621
|
+
.option('--priority <priority>', 'P0|P1|P2').option('--dry-run', '이동 없이 배치 보기')
|
|
622
|
+
.action((id: string, opts: { priority?: string; dryRun?: boolean }) => {
|
|
623
|
+
const rows = listSchedules(scheduleLedgerRoot());
|
|
624
|
+
const found = rows.flatMap(({ version }) => listChecklist(version).items.filter((item) => item.id === id));
|
|
625
|
+
if (found.length !== 1) throw new CliUserError(found.length ? `여러 판의 같은 칸: ${id}` : `없는 칸: ${id}`, 'release checklist add 로 칸을 먼저 만든다');
|
|
626
|
+
const item = found[0]!;
|
|
627
|
+
if (!item.owner) throw new CliUserError(`담당 없는 칸: ${id}`);
|
|
628
|
+
const who = process.env.ELANOUS_TRACK;
|
|
629
|
+
if (who && who !== 'OP' && !ownerMatches(item.owner, parseOwner(who).seat)) throw new CliUserError('남의 칸 당기기 거부 — COO 에 요청');
|
|
630
|
+
if (!opts.dryRun && who !== 'OP') throw new CliUserError('판 배치는 COO 에 요청 — 자리는 이유와 함께 release checklist move 로 다음 판에만 이월');
|
|
631
|
+
if (!opts.dryRun && !mayWriteLedger('release place', false, scheduleLedgerRoot())) return;
|
|
632
|
+
const priority = opts.priority ?? item.priority;
|
|
633
|
+
if (!priority) throw new CliUserError(`우선순위가 없는 칸: ${id} — --priority P0|P1|P2 를 지정하라`);
|
|
634
|
+
const result = placeCell({ id, title: item.title, owner: item.owner, priority: priority as PlacementPriority, predecessors: item.predecessors ?? [], deadlineVersion: item.deadlineVersion, ceoMinutes: item.ceoMinutes, ceoDate: item.ceoDate }, { schedules: rows, dryRun: opts.dryRun, by: who ?? 'cli' });
|
|
635
|
+
console.log(`${opts.dryRun ? '· 드라이런' : '✅'} ${id} ${result.from ?? '-'} → ${result.version} · ${result.reason}${result.displaced.length ? ` · P2 이월 ${result.displaced.map((row) => row.id).join(', ')}` : ''}`);
|
|
636
|
+
});
|
|
637
|
+
release.command('rebalance').description('마감 2시간 전 미시작 칸을 다음 판으로')
|
|
638
|
+
.requiredOption('--version <v>', '대상 판').option('--dry-run', '이동 없이 보기')
|
|
639
|
+
.action((opts: { version: string; dryRun?: boolean }) => {
|
|
640
|
+
if (process.env.ELANOUS_TRACK !== 'OP' && !opts.dryRun) throw new CliUserError('판 이월은 COO 에 요청');
|
|
641
|
+
if (!opts.dryRun && !mayWriteLedger('release rebalance', false, scheduleLedgerRoot())) return;
|
|
642
|
+
const { decisions, blocked } = rebalance(opts.version, { schedules: listSchedules(scheduleLedgerRoot()), dryRun: opts.dryRun });
|
|
643
|
+
for (const row of decisions) console.log(`${opts.dryRun ? '· 드라이런' : '✅'} ${row.id} ${row.from} → ${row.version} · ${row.reason}`);
|
|
644
|
+
for (const row of blocked) console.log(`⛔ ${row.id} ${row.from} → ${row.to} 이월 거부 · ${row.reason}`);
|
|
645
|
+
if (!decisions.length && !blocked.length) console.log('이월할 미시작 칸 없음');
|
|
646
|
+
});
|
|
647
|
+
const ceoMinutes = (value: string): number => {
|
|
648
|
+
if (!/^\d+$/.test(value) || !Number.isSafeInteger(Number(value))) throw new CliUserError('대표 손 분량은 0 이상의 정수 분이어야 한다');
|
|
649
|
+
return Number(value);
|
|
650
|
+
};
|
|
566
651
|
const checklist = release.command('checklist').description('판별 확인표 조회·갱신')
|
|
567
652
|
.option('--version <v>', '판 또는 별칭(기본: package.json 의 개발판에서 -dev.N 제거)')
|
|
568
653
|
.option('--json', '결과 JSON')
|
|
569
654
|
.option('--hq-override', '본부 임대 거부를 관측하며 수동 우회');
|
|
570
|
-
const mayWrite = (cmd: Command) =>
|
|
655
|
+
const mayWrite = (cmd: Command) => mayWriteLedger(`release checklist ${cmd.name()}`, Boolean(cmd.optsWithGlobals().hqOverride), releaseLedgerRoot());
|
|
571
656
|
const context = (cmd: Command) => {
|
|
572
657
|
const opts = { ...(cmd.parent?.parent?.opts() as { version?: string; json?: boolean }), ...(cmd.parent?.opts() as { version?: string; json?: boolean }), ...(cmd.opts() as { version?: string; json?: boolean }) };
|
|
573
658
|
const codenames = checklistCodenames();
|
|
@@ -607,11 +692,14 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
|
|
|
607
692
|
return;
|
|
608
693
|
}
|
|
609
694
|
if (opts.applyEvidence && !opts.dryRun && !mayWrite(cmd)) return;
|
|
610
|
-
const since = new Date((landedDeps.now ?? (() => new Date()))().getTime() - 7 * 86_400_000).toISOString().slice(0, 10);
|
|
611
|
-
const raw = must((landedDeps.run ?? defaultRunner)('gh', ['pr', 'list', '--state', 'merged', '--search', `merged:>=${since}`, '--limit', '400', '--json', 'number,title,body,mergedAt'], process.cwd()), '병합 PR 조회(gh)');
|
|
612
|
-
const prs = JSON.parse(raw) as MergedChecklistPr[];
|
|
613
|
-
if (!Array.isArray(prs)) throw new Error('병합 PR 조회(gh): 배열이 아니다');
|
|
614
695
|
const snapshots = opts.open ? openSnapshots : [(landedDeps.checklist ?? listChecklist)(context(cmd).version)];
|
|
696
|
+
const now = (landedDeps.now ?? (() => new Date()))();
|
|
697
|
+
const updated = snapshots.flatMap(({ items }) => items
|
|
698
|
+
.filter((item) => (item.status === 'yellow' || item.status === 'red') && (opts.owner === undefined || ownerMatches(item.owner, opts.owner)))
|
|
699
|
+
.map((item) => Date.parse(item.updatedAt)).filter((at) => Number.isFinite(at)));
|
|
700
|
+
const since = updated.length ? new Date(updated.reduce((earliest, at) => Math.min(earliest, at))).toISOString().slice(0, 10)
|
|
701
|
+
: new Date(now.getTime() - 7 * 86_400_000).toISOString().slice(0, 10);
|
|
702
|
+
const { prs, truncated } = mergedChecklistPrs(since, now, landedDeps.run ?? defaultRunner);
|
|
615
703
|
const results: Array<{ version: string; rows?: ReturnType<typeof landedButYellow>; plan?: ReturnType<typeof evidencePlan> }> = [];
|
|
616
704
|
let totalAdded = 0, totalSkipped = 0;
|
|
617
705
|
for (const snapshot of snapshots) {
|
|
@@ -643,30 +731,34 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
|
|
|
643
731
|
if (!json) console.log(`판 ${snapshots.length} · 붙임 합 ${totalAdded} · 건너뜀(언급만) 합 ${totalSkipped}`);
|
|
644
732
|
else await writeStdoutJson(`${JSON.stringify(results)}\n`);
|
|
645
733
|
} else if (json) await writeStdoutJson(`${JSON.stringify(opts.applyEvidence || opts.dryRun ? results[0]!.plan : results[0]!.rows)}\n`);
|
|
646
|
-
if (
|
|
734
|
+
if (truncated) console.error('⚠ 병합 PR 400개 상한: 목록이 잘렸을 수 있다');
|
|
647
735
|
} catch (error) {
|
|
648
736
|
console.error(`⛔ ${error instanceof Error ? error.message : String(error)}`);
|
|
649
737
|
process.exitCode = 1;
|
|
650
738
|
}
|
|
651
739
|
});
|
|
652
740
|
withContext(checklist.command('add <id> <title>').description('칸 추가')).option('--owner <owner>', '담당').option('--kind <kind>', 'screen = 다섯 화면 짝 칸 · 근거에 짝: PWA … · 데스크톱 … · 폴드 … · 아이폰 … · 아이패드 … 한 줄')
|
|
741
|
+
.option('--priority <priority>', 'P0|P1|P2').option('--deadline-version <v>', '마감 판').option('--predecessor <id...>', '선행 칸 id')
|
|
742
|
+
.option('--ceo-minutes <minutes>', '대표 손 분량(분)').option('--ceo-date <date>', '대표 손 날짜 YYYY-MM-DD (없으면 판 착지일 KST)')
|
|
653
743
|
.option('--allow-duplicate-id', '다른 판에 같은 id 가 있어도 경고 후 추가')
|
|
654
|
-
.action((id: string, title: string, opts: { owner?: string; kind?: string; allowDuplicateId?: boolean }, cmd: Command) => {
|
|
744
|
+
.action((id: string, title: string, opts: { owner?: string; kind?: string; priority?: string; deadlineVersion?: string; predecessor?: string[]; ceoMinutes?: string; ceoDate?: string; allowDuplicateId?: boolean }, cmd: Command) => {
|
|
655
745
|
const { version, json } = context(cmd);
|
|
656
746
|
if (!mayWrite(cmd)) return;
|
|
657
|
-
const data = addItem(version, { id, title, ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.kind !== undefined ? { kind: opts.kind as ChecklistKind } : {}) }, { allowDuplicateId: opts.allowDuplicateId });
|
|
747
|
+
const data = addItem(version, { id, title, ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.kind !== undefined ? { kind: opts.kind as ChecklistKind } : {}), ...(opts.priority !== undefined ? { priority: opts.priority as PlacementPriority } : {}), ...(opts.deadlineVersion !== undefined ? { deadlineVersion: opts.deadlineVersion } : {}), ...(opts.predecessor !== undefined ? { predecessors: opts.predecessor } : {}), ...(opts.ceoMinutes !== undefined ? { ceoMinutes: ceoMinutes(opts.ceoMinutes) } : {}), ...(opts.ceoDate !== undefined ? { ceoDate: opts.ceoDate } : {}) }, { allowDuplicateId: opts.allowDuplicateId });
|
|
658
748
|
if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 추가`);
|
|
659
749
|
});
|
|
660
750
|
withContext(checklist.command('set <id>').description('칸 상태·근거·담당·처분 갱신'))
|
|
661
751
|
.option('--status <status>', 'green|yellow|red|done').option('--evidence <evidence>', '근거').option('--owner <owner>', '담당')
|
|
662
752
|
.option('--disposition <disposition>', 'move|known-issue|block').option('--kind <kind>', 'screen = 다섯 화면 짝 칸 · 근거에 짝: PWA … · 데스크톱 … · 폴드 … · 아이폰 … · 아이패드 … 한 줄')
|
|
663
|
-
.
|
|
753
|
+
.option('--priority <priority>', 'P0|P1|P2').option('--deadline-version <v>', '마감 판').option('--predecessor <id...>', '선행 칸 id')
|
|
754
|
+
.option('--ceo-minutes <minutes>', '대표 손 분량(분)').option('--ceo-date <date>', '대표 손 날짜 YYYY-MM-DD')
|
|
755
|
+
.action((id: string, opts: { status?: string; evidence?: string; owner?: string; disposition?: string; kind?: string; priority?: string; deadlineVersion?: string; predecessor?: string[]; ceoMinutes?: string; ceoDate?: string }, cmd: Command) => {
|
|
664
756
|
const { version, json } = context(cmd);
|
|
665
757
|
if (opts.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(opts.status)) throw new CliUserError(`잘못된 상태: ${opts.status}`, 'green|yellow|red|done');
|
|
666
758
|
if (opts.disposition !== undefined && !['move', 'known-issue', 'block'].includes(opts.disposition)) throw new CliUserError(`잘못된 처분: ${opts.disposition}`, 'move|known-issue|block');
|
|
667
|
-
if (opts.status === undefined && opts.evidence === undefined && opts.owner === undefined && opts.disposition === undefined && opts.kind === undefined) throw new CliUserError('갱신할 칸을 지정하라', '--status · --evidence · --owner · --disposition · --kind 중 하나');
|
|
759
|
+
if (opts.status === undefined && opts.evidence === undefined && opts.owner === undefined && opts.disposition === undefined && opts.kind === undefined && opts.priority === undefined && opts.deadlineVersion === undefined && opts.predecessor === undefined && opts.ceoMinutes === undefined && opts.ceoDate === undefined) throw new CliUserError('갱신할 칸을 지정하라', '--status · --evidence · --owner · --disposition · --kind · --priority · --deadline-version · --predecessor · --ceo-minutes · --ceo-date 중 하나');
|
|
668
760
|
if (!mayWrite(cmd)) return;
|
|
669
|
-
const data = setItem(version, id, { ...(opts.status !== undefined ? { status: opts.status as ChecklistStatus } : {}), ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}), ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.disposition !== undefined ? { disposition: opts.disposition as ChecklistDisposition } : {}), ...(opts.kind !== undefined ? { kind: opts.kind as ChecklistKind } : {}) }, process.env.ELANOUS_TRACK || 'cli');
|
|
761
|
+
const data = setItem(version, id, { ...(opts.status !== undefined ? { status: opts.status as ChecklistStatus } : {}), ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}), ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.disposition !== undefined ? { disposition: opts.disposition as ChecklistDisposition } : {}), ...(opts.kind !== undefined ? { kind: opts.kind as ChecklistKind } : {}), ...(opts.priority !== undefined ? { priority: opts.priority as PlacementPriority } : {}), ...(opts.deadlineVersion !== undefined ? { deadlineVersion: opts.deadlineVersion } : {}), ...(opts.predecessor !== undefined ? { predecessors: opts.predecessor } : {}), ...(opts.ceoMinutes !== undefined ? { ceoMinutes: ceoMinutes(opts.ceoMinutes) } : {}), ...(opts.ceoDate !== undefined ? { ceoDate: opts.ceoDate } : {}) }, process.env.ELANOUS_TRACK || 'cli');
|
|
670
762
|
const item = data.items.find((entry) => entry.id === id);
|
|
671
763
|
const why = item?.kind === 'screen' && item.status === 'green' ? parityGap(item.evidence) : null;
|
|
672
764
|
if (why) {
|
|
@@ -701,8 +793,15 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
|
|
|
701
793
|
.action((id: string, opts: { from: string; to: string; reason?: string }, cmd: Command) => {
|
|
702
794
|
const { codenames, json } = context(cmd);
|
|
703
795
|
const from = checklistVersion(opts.from, codenames), to = checklistVersion(opts.to, codenames);
|
|
796
|
+
if (!opts.reason?.trim()) throw new CliUserError('이동 이유가 필요하다');
|
|
704
797
|
if (!mayWrite(cmd)) return;
|
|
705
|
-
|
|
798
|
+
if (actor() === 'cli') {
|
|
799
|
+
// No seat identity (ELANOUS_TRACK unset): keep the plain move so seat sessions without the variable still work, but say so.
|
|
800
|
+
console.error('⚠ 자리 신원 없음(ELANOUS_TRACK 미설정) — 자리 규칙(자기 칸 · 다음 판 · 용량) 검사 없이 옮긴다');
|
|
801
|
+
debug.log('release.placement', 'anonymous-move', { id, from, to });
|
|
802
|
+
features.move(id, from, to, actor(), undefined, undefined, opts.reason);
|
|
803
|
+
} else if (actor() !== 'OP') seatMove(id, from, to, actor(), opts.reason, { schedules: listSchedules(scheduleLedgerRoot()) });
|
|
804
|
+
else features.move(id, from, to, actor(), undefined, undefined, opts.reason);
|
|
706
805
|
if (json) console.log(JSON.stringify(listChecklist(to))); else console.log(`✅ ${id} ${from} → ${to}`);
|
|
707
806
|
});
|
|
708
807
|
withContext(checklist.command('retitle <id> <title>').description('칸 제목 수정')).action((id: string, title: string, _opts: unknown, cmd: Command) => {
|
|
@@ -807,42 +906,100 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
|
|
|
807
906
|
.action(async (o: { version: string; base: string; pick: string[]; dryRun?: boolean; json?: boolean }) => {
|
|
808
907
|
await jsonAction(o.json, async (log) => cutReleaseBranch({ ...o, log }), () => true);
|
|
809
908
|
});
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
909
|
+
const runAction = async (o: { version: string; cutCommit?: string; dryRun?: boolean; ifReady?: boolean; forceFreeze?: boolean; json?: boolean }, logRun = console.log): Promise<boolean | AutoStartDeferral | undefined> => {
|
|
910
|
+
if (!o.dryRun) {
|
|
911
|
+
try {
|
|
912
|
+
const frozen = readLandingFreeze(releaseRunDeps.freezeRoot);
|
|
913
|
+
if (frozen) {
|
|
914
|
+
debug.log('release.run', o.forceFreeze ? 'freeze-forced' : 'frozen', { version: o.version, reason: frozen.reason, until: frozen.until });
|
|
915
|
+
if (!o.forceFreeze) {
|
|
916
|
+
// `--if-ready` (the scheduled path) defers quietly; a direct run fails — the release run is run again after `freeze off`.
|
|
917
|
+
if (o.ifReady) {
|
|
918
|
+
if (o.json) await writeStdoutJson(`${JSON.stringify({ ok: true, skipped: true, reason: 'frozen', detail: landingFreezeMessage(frozen) })}\n`);
|
|
919
|
+
else logRun(`· ${landingFreezeMessage(frozen)} · 릴리스 루프 연기`);
|
|
920
|
+
} else {
|
|
921
|
+
if (o.json) await writeStdoutJson(`${JSON.stringify({ ok: false, reason: 'frozen', error: landingFreezeMessage(frozen) })}\n`);
|
|
922
|
+
else console.error(`⛔ ${landingFreezeMessage(frozen)} · 릴리스 루프 거부(--force-freeze 로만 강행)`);
|
|
923
|
+
process.exitCode = 1;
|
|
924
|
+
}
|
|
925
|
+
return o.ifReady ? { deferred: true, reason: 'frozen' } : false;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
} catch (e) {
|
|
929
|
+
if (o.json) await writeStdoutJson(`${JSON.stringify({ ok: false, error: (e as Error).message })}\n`);
|
|
930
|
+
else console.error(`⛔ ${(e as Error).message}`);
|
|
931
|
+
process.exitCode = 1;
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
818
935
|
if (o.ifReady && !o.dryRun) {
|
|
819
936
|
try {
|
|
820
937
|
const outcome = await runReleaseIfReady(o.version, {
|
|
821
938
|
ledgerRoot: releaseRunDeps.ledgerRoot,
|
|
822
939
|
readiness: (version, options) => releaseReadiness(version, { ledgerRoot: releaseRunDeps.ledgerRoot, checklist: releaseRunDeps.checklist, ...options }),
|
|
823
|
-
run: () => runUnattendedRelease({ version: o.version, cutCommit: o.cutCommit }, releaseRunDeps),
|
|
940
|
+
run: () => runUnattendedRelease({ version: o.version, cutCommit: o.cutCommit, forceFreeze: o.forceFreeze }, releaseRunDeps),
|
|
824
941
|
});
|
|
825
942
|
if (outcome.skipped) {
|
|
826
943
|
if (o.json) await writeStdoutJson(`${JSON.stringify(outcome)}\n`);
|
|
827
|
-
else
|
|
828
|
-
return;
|
|
944
|
+
else logRun(`· 준비 안 됨 ${o.version} · ${outcome.reason} · ${outcome.detail}`);
|
|
945
|
+
return { deferred: true, reason: outcome.reason };
|
|
829
946
|
}
|
|
830
947
|
const result = outcome.result;
|
|
831
948
|
const ok = result.dryRun || result.state?.status === 'done' || result.state?.status === 'awaiting-approval';
|
|
832
|
-
(o.json ? console.error :
|
|
949
|
+
(o.json ? console.error : logRun)(`${result.dryRun ? '· 드라이런' : '▶ 릴리스 루프'} ${result.input.version} · 입력 ${JSON.stringify(result.input)}`);
|
|
833
950
|
if (o.json) await writeStdoutJson(`${JSON.stringify({ ok, ...result })}\n`);
|
|
834
951
|
if (!ok && !process.exitCode) process.exitCode = 1;
|
|
952
|
+
return ok;
|
|
835
953
|
} catch (e) {
|
|
954
|
+
if (e instanceof LandingFrozenError) {
|
|
955
|
+
// A freeze switched on after the entry check still defers the scheduled run rather than failing it.
|
|
956
|
+
debug.log('release.run', 'frozen', { version: o.version, reason: e.freeze.reason, until: e.freeze.until, at: 'run-boundary' });
|
|
957
|
+
if (o.json) await writeStdoutJson(`${JSON.stringify({ ok: true, skipped: true, reason: 'frozen', detail: e.message })}\n`);
|
|
958
|
+
else logRun(`· ${e.message} · 릴리스 루프 연기`);
|
|
959
|
+
return { deferred: true, reason: 'frozen' };
|
|
960
|
+
}
|
|
836
961
|
if (o.json) await writeStdoutJson(`${JSON.stringify({ ok: false, error: (e as Error).message })}\n`);
|
|
837
962
|
else console.error(`⛔ ${(e as Error).message}`);
|
|
838
963
|
process.exitCode = 1;
|
|
964
|
+
return false;
|
|
839
965
|
}
|
|
840
|
-
return;
|
|
841
966
|
}
|
|
842
967
|
await jsonAction(o.json, async (log) => {
|
|
843
|
-
const result = await runUnattendedRelease({ version: o.version, dryRun: o.dryRun, cutCommit: o.cutCommit }, releaseRunDeps);
|
|
968
|
+
const result = await runUnattendedRelease({ version: o.version, dryRun: o.dryRun, cutCommit: o.cutCommit, forceFreeze: o.forceFreeze }, releaseRunDeps);
|
|
844
969
|
log(`${result.dryRun ? '· 드라이런' : '▶ 릴리스 루프'} ${result.input.version} · 입력 ${JSON.stringify(result.input)}`);
|
|
845
970
|
return result;
|
|
846
971
|
}, (r) => r.dryRun || r.state?.status === 'done' || r.state?.status === 'awaiting-approval');
|
|
972
|
+
};
|
|
973
|
+
release.command('run')
|
|
974
|
+
.description('릴리스 루프 실행 — 원장 직전 판과 release.loop 설정으로 그래프 입력 구성 · --dry-run 은 입력만 출력')
|
|
975
|
+
.requiredOption('--version <v>', '공개할 판(x.y.z)')
|
|
976
|
+
.option('--cut-commit <sha>', 'origin/main HEAD 대신 컷으로 쓸 커밋')
|
|
977
|
+
.option('--dry-run', '체크리스트·그래프 실행 없이 전체 입력 보기')
|
|
978
|
+
.option('--if-ready', '준비되지 않았으면 사유를 출력하고 성공으로 건너뛴다')
|
|
979
|
+
.option('--force-freeze', '동결 중 게이트·발행 강행(관측 기록)')
|
|
980
|
+
.option('--json', '결과 한 줄 JSON(stdout) · 사람 줄은 stderr')
|
|
981
|
+
.action(async (o: { version: string; cutCommit?: string; dryRun?: boolean; ifReady?: boolean; forceFreeze?: boolean; json?: boolean }) => { await runAction(o); });
|
|
982
|
+
release.command('auto-start')
|
|
983
|
+
.description('판 일정의 컷 창에서 릴리스 시작을 미리 본다 · --apply 로 release run --if-ready 실행')
|
|
984
|
+
.option('--window-minutes <n>', '컷 뒤 선택 창(분)', '15')
|
|
985
|
+
.option('--apply', '선택한 판을 release run --if-ready 로 시작')
|
|
986
|
+
.option('--json', '결과 한 줄 JSON(stdout)')
|
|
987
|
+
.action(async (o: { windowMinutes: string; apply?: boolean; json?: boolean }) => {
|
|
988
|
+
await jsonAction(o.json, async (log) => {
|
|
989
|
+
const result = await autoStartScheduledRelease({
|
|
990
|
+
windowMinutes: Number(o.windowMinutes), apply: o.apply, ledgerRoot: scheduleLedgerRoot(),
|
|
991
|
+
readiness: (version, ledgerRoot, now) => releaseReadiness(version, { ledgerRoot, now: () => now, checklist: releaseRunDeps.checklist }),
|
|
992
|
+
launch: async (version) => {
|
|
993
|
+
const ran = await runAction({ version, ifReady: true, json: false }, log);
|
|
994
|
+
if (ran && typeof ran === 'object' && ran.deferred) return ran;
|
|
995
|
+
if (ran !== true) throw new Error(`release run 미시작 또는 실패: ${version}`);
|
|
996
|
+
},
|
|
997
|
+
});
|
|
998
|
+
if (result.status === 'planned') log(`· 미리 보기 ${result.version} · 컷 ${result.cutAt} · 실행하려면 --apply`);
|
|
999
|
+
else if (result.status === 'started') log(`▶ 릴리스 시작 ${result.version} · 컷 ${result.cutAt}`);
|
|
1000
|
+
else if (result.status === 'skipped') log(`· 릴리스 연기 ${result.version} · ${result.reason}`);
|
|
1001
|
+
else log('· 선택할 컷 없음');
|
|
1002
|
+
return result;
|
|
1003
|
+
}, () => true);
|
|
847
1004
|
});
|
|
848
1005
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join, relative, resolve } from 'node:path';
|
|
4
|
+
import YAML from 'yaml';
|
|
5
|
+
|
|
6
|
+
export const RFC_STATUSES = ['unknown', 'proposed', 'adopted', 'completed', 'superseded', 'rejected'] as const;
|
|
7
|
+
export type RfcStatus = typeof RFC_STATUSES[number];
|
|
8
|
+
export interface RfcRow {
|
|
9
|
+
path: string;
|
|
10
|
+
status: RfcStatus;
|
|
11
|
+
owner: string | null;
|
|
12
|
+
card: string | null;
|
|
13
|
+
supersededBy: string | null;
|
|
14
|
+
handbook: string | null;
|
|
15
|
+
issue: string | null;
|
|
16
|
+
/** Legacy top-level `status:` / `owner:` values, verbatim: evidence for a human migration, never the lifecycle status. */
|
|
17
|
+
legacyStatus: string | null;
|
|
18
|
+
legacyOwner: string | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function rfcPaths(dir: string): string[] {
|
|
22
|
+
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
23
|
+
const path = join(dir, entry.name);
|
|
24
|
+
if (entry.isDirectory()) return rfcPaths(path);
|
|
25
|
+
return entry.isFile() && /^RFC-.*\.md$/.test(entry.name) ? [path] : [];
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function field(value: unknown): string | null {
|
|
30
|
+
return typeof value === 'string' && value.trim() && value.trim().toLowerCase() !== 'unknown' ? value.trim() : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Explicit lifecycle fields only: prose, old `status: rfc`, and file location do not establish adoption. */
|
|
34
|
+
export function readRfcRow(text: string, path: string): RfcRow {
|
|
35
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(text);
|
|
36
|
+
let meta: Record<string, unknown> = {};
|
|
37
|
+
let issue: string | null = null;
|
|
38
|
+
let legacyStatus: string | null = null;
|
|
39
|
+
let legacyOwner: string | null = null;
|
|
40
|
+
if (!match) issue = 'lifecycle frontmatter missing';
|
|
41
|
+
else {
|
|
42
|
+
// Legacy RFC frontmatter can contain non-YAML prose. Parse only the new, top-level rfc_* declarations.
|
|
43
|
+
legacyStatus = /^status:\s*(.+)$/m.exec(match[1]!)?.[1]?.trim().replace(/^["']|["']$/g, '') || null;
|
|
44
|
+
legacyOwner = /^owner:\s*(.+)$/m.exec(match[1]!)?.[1]?.trim().replace(/^["']|["']$/g, '') || null;
|
|
45
|
+
const declarations = [...match[1]!.matchAll(/^(rfc_status|rfc_owner|rfc_card|rfc_superseded_by|rfc_handbook):\s*(.*)$/gm)];
|
|
46
|
+
for (const [, key, raw] of declarations) {
|
|
47
|
+
try { meta[key!] = YAML.parse(raw!); }
|
|
48
|
+
catch { issue = 'invalid lifecycle frontmatter'; }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const rawStatus = meta.rfc_status;
|
|
52
|
+
const status: RfcStatus = typeof rawStatus === 'string' && RFC_STATUSES.includes(rawStatus as RfcStatus)
|
|
53
|
+
? rawStatus as RfcStatus : 'unknown';
|
|
54
|
+
if (rawStatus !== undefined && status === 'unknown' && rawStatus !== 'unknown') issue = 'invalid rfc_status';
|
|
55
|
+
return {
|
|
56
|
+
path, status, owner: field(meta.rfc_owner), card: field(meta.rfc_card),
|
|
57
|
+
supersededBy: field(meta.rfc_superseded_by), handbook: field(meta.rfc_handbook), issue, legacyStatus, legacyOwner,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function scanRfcs(repoRoot: string): RfcRow[] {
|
|
62
|
+
const root = resolve(repoRoot);
|
|
63
|
+
return rfcPaths(join(root, 'docs')).map((file) => readRfcRow(readFileSync(file, 'utf8'), relative(root, file).replaceAll('\\', '/')))
|
|
64
|
+
.sort((a, b) => a.path.localeCompare(b.path, 'en'));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A missing declaration is not proof that a card does not exist; an adopted RFC with an explicit null is. */
|
|
68
|
+
export function adoptedWithoutCard(rows: readonly RfcRow[]): RfcRow[] {
|
|
69
|
+
return rows.filter((row) => row.status === 'adopted' && row.card === null);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function runRfcStatus(options: { json?: boolean; missingCards?: boolean }, root = process.cwd(),
|
|
73
|
+
write: (text: string) => void = (text) => process.stdout.write(text)): void {
|
|
74
|
+
const all = scanRfcs(root);
|
|
75
|
+
const rows = options.missingCards ? adoptedWithoutCard(all) : all;
|
|
76
|
+
if (options.json) {
|
|
77
|
+
write(`${JSON.stringify({ total: all.length, adoptedWithoutCard: adoptedWithoutCard(all).length, rows }, null, 2)}\n`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
write(`RFC ${all.length} · 채택·칸 없음 ${adoptedWithoutCard(all).length} · 표시 ${rows.length}\n`);
|
|
81
|
+
write('상태\t담당\t칸\t대체 문서\t핸드북\t옛 상태\t옛 담당\t경로\t메타 오류\n');
|
|
82
|
+
for (const row of rows) write(`${row.status}\t${row.owner ?? '모름'}\t${row.card ?? '모름'}\t${row.supersededBy ?? '모름'}\t${row.handbook ?? '모름'}\t${row.legacyStatus ?? '-'}\t${row.legacyOwner ?? '-'}\t${row.path}\t${row.issue ?? '-'}\n`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function registerRfcStatusCommand(docs: Command): void {
|
|
86
|
+
docs.command('rfc-status')
|
|
87
|
+
.description('전체 RFC 생애주기 표 (RFC frontmatter만 읽음)')
|
|
88
|
+
.option('--json', '기계가 읽을 JSON')
|
|
89
|
+
.option('--missing-cards', '채택됐으나 칸이 기록되지 않은 RFC만')
|
|
90
|
+
.action((options: { json?: boolean; missingCards?: boolean }) => runRfcStatus(options));
|
|
91
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
3
|
+
import { hqCliWriteAllowed, type HqDeps } from '../hq/hq.js';
|
|
4
|
+
import { isIsolatedLedgerWriteRoot } from '../hq/ledger-write-target.js';
|
|
5
|
+
import { closeSeatRequests, listSeatRequests, type SeatRequestStatus } from '../seat-dispatch/seat-request-ledger.js';
|
|
6
|
+
|
|
7
|
+
function duration(value: string): number {
|
|
8
|
+
const match = /^(\d+)(ms|s|m|h|d)$/.exec(value);
|
|
9
|
+
if (!match) throw new Error(`invalid --older-than: ${value} (use e.g. 24h)`);
|
|
10
|
+
const ms = Number(match[1]) * ({ ms: 1, s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 }[match[2]! as 'ms' | 's' | 'm' | 'h' | 'd']);
|
|
11
|
+
if (!Number.isSafeInteger(ms)) throw new Error(`invalid --older-than: ${value}`);
|
|
12
|
+
return ms;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function registerSeatRequestsCommands(seat: Command, deps: { root?: () => string; hq?: HqDeps; now?: () => Date } = {}): void {
|
|
16
|
+
const requests = seat.command('requests').description('자리 요청 원장 조회·닫기');
|
|
17
|
+
requests.command('list').description('키별 최신 요청 상태 조회')
|
|
18
|
+
.option('--seat <seat>', '자리 ID')
|
|
19
|
+
.option('--status <status>', 'pending|queued|rejected|done')
|
|
20
|
+
.option('--json', 'JSON 출력')
|
|
21
|
+
.action((opts: { seat?: string; status?: string; json?: boolean }) => {
|
|
22
|
+
try {
|
|
23
|
+
if (opts.status && !['pending', 'queued', 'rejected', 'done'].includes(opts.status)) throw new Error(`invalid status: ${opts.status}`);
|
|
24
|
+
const rows = listSeatRequests((deps.root ?? effectiveInstanceRoot)(), { seat: opts.seat, status: opts.status as SeatRequestStatus | undefined });
|
|
25
|
+
if (opts.json) console.log(JSON.stringify(rows));
|
|
26
|
+
else for (const row of rows) console.log(`${row.key} ${row.seat} ${row.status} ${row.queuedAt} ${row.text}`);
|
|
27
|
+
} catch (error) { console.error(`seat requests list: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
|
|
28
|
+
});
|
|
29
|
+
requests.command('close <keys...>').description('열린 요청을 사유와 함께 종료 (원장에 덧붙임)')
|
|
30
|
+
.requiredOption('--reason <reason>', '닫는 이유')
|
|
31
|
+
.option('--status <status>', 'rejected|done', 'rejected')
|
|
32
|
+
.option('--older-than <duration>', '최소 대기 시간 (예: 24h)')
|
|
33
|
+
.option('--dry-run', '쓰기 없이 대상만 미리 보기')
|
|
34
|
+
.action((keys: string[], opts: { reason: string; status: string; olderThan?: string; dryRun?: boolean }) => {
|
|
35
|
+
try {
|
|
36
|
+
if (!opts.reason.trim()) throw new Error('reason must not be empty');
|
|
37
|
+
if (opts.status !== 'rejected' && opts.status !== 'done') throw new Error(`invalid close status: ${opts.status}`);
|
|
38
|
+
const olderThan = opts.olderThan === undefined ? undefined : duration(opts.olderThan);
|
|
39
|
+
const root = (deps.root ?? effectiveInstanceRoot)();
|
|
40
|
+
const known = new Set(listSeatRequests(root).map((row) => row.key));
|
|
41
|
+
const missing = keys.find((key) => !known.has(key));
|
|
42
|
+
if (missing) throw new Error(`unknown key: ${missing}`);
|
|
43
|
+
if (!opts.dryRun && !isIsolatedLedgerWriteRoot(root) && !hqCliWriteAllowed('seat requests close', false, deps.hq)) return;
|
|
44
|
+
const rows = closeSeatRequests(root, keys, {
|
|
45
|
+
reason: opts.reason, status: opts.status,
|
|
46
|
+
...(olderThan === undefined ? {} : { olderThan }),
|
|
47
|
+
...(deps.now ? { now: deps.now() } : {}), dryRun: opts.dryRun,
|
|
48
|
+
});
|
|
49
|
+
for (const row of rows) console.log(`${opts.dryRun ? 'would close' : 'closed'} ${row.key} ${row.status}: ${row.reason}`);
|
|
50
|
+
} catch (error) { console.error(`seat requests close: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
|
|
51
|
+
});
|
|
52
|
+
}
|
package/src/connectors/linear.ts
CHANGED
|
@@ -68,8 +68,8 @@ export function parseLinearWebhook(body: unknown, deliveryId: string, ledger?: E
|
|
|
68
68
|
return isLinearProjectionEcho(event, ledger ?? new EventLedger()) ? null : event;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
export async function fetchLinearIssues({ apiKey, teamKey, labelOrPrefix, since, fetch: fetchFn = fetch, ledger }: {
|
|
72
|
-
apiKey: string; teamKey: string; labelOrPrefix?: string; since?: string; fetch?: typeof fetch; ledger?: EventLedger;
|
|
71
|
+
export async function fetchLinearIssues({ apiKey, teamKey, labelOrPrefix, excludeLabel, since, fetch: fetchFn = fetch, ledger }: {
|
|
72
|
+
apiKey: string; teamKey: string; labelOrPrefix?: string; excludeLabel?: string; since?: string; fetch?: typeof fetch; ledger?: EventLedger;
|
|
73
73
|
}): Promise<ExternalTaskEvent[]> {
|
|
74
74
|
const query = `query ConnectorIssues($teamKey: String!, $after: String) {
|
|
75
75
|
issues(filter: { team: { key: { eq: $teamKey } } }, first: 100, after: $after) {
|
|
@@ -106,8 +106,10 @@ export async function fetchLinearIssues({ apiKey, teamKey, labelOrPrefix, since,
|
|
|
106
106
|
const issue = asIssue(node);
|
|
107
107
|
if (!issue || (since && issue.updatedAt < since) ||
|
|
108
108
|
['completed', 'canceled', 'duplicate'].includes((node.state as { type?: string } | undefined)?.type ?? '')) continue;
|
|
109
|
+
const labels = (node.labels as { nodes?: Array<{ name: string }> } | undefined)?.nodes;
|
|
110
|
+
if (excludeLabel && labels?.some(label => label.name.toLowerCase() === excludeLabel.toLowerCase())) continue;
|
|
109
111
|
if (labelOrPrefix && !issue.title.startsWith(labelOrPrefix) &&
|
|
110
|
-
!
|
|
112
|
+
!labels?.some(label => label.name === labelOrPrefix)) continue;
|
|
111
113
|
const event: ExternalTaskEvent = { provider: 'linear', eventId: `${issue.id}:${issue.updatedAt}`, kind: 'updated',
|
|
112
114
|
ref: issue.id, identifier: issue.identifier, title: issue.title, body: issue.description ?? '',
|
|
113
115
|
url: issue.url ?? '', priority: priorityOf(issue.priority), occurredAt: issue.updatedAt };
|
|
@@ -1,32 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { closeSync, constants, existsSync, fstatSync, lstatSync, mkdirSync, openSync } from 'node:fs';
|
|
1
|
+
import { closeSync, constants, fstatSync, lstatSync, mkdirSync, openSync } from 'node:fs';
|
|
3
2
|
import { dlopen, FFIType } from 'bun:ffi';
|
|
4
3
|
import { join } from 'node:path';
|
|
5
4
|
import { listMemories, memoryRoot, saveMemory, type MemoryEntry } from '../memory.js';
|
|
6
|
-
import { surfaceEventsDbPath } from '../domains/surface-events.js';
|
|
7
5
|
import { DecisionLedger } from '../decisions/decision-ledger.js';
|
|
8
|
-
import {
|
|
9
|
-
import { condenseContextWindowWithReport, type CondenseSkipped, type MemoryDeps, type MemoryItem, type MemorySource, type MemorySummary } from './long-term-memory.js';
|
|
6
|
+
import { condenseContextWindowWithReport, readCondenseEvents, type CondenseFunnel, type CondenseSkipped, type MemoryDeps, type MemoryItem, type MemorySource, type MemorySummary } from './long-term-memory.js';
|
|
10
7
|
|
|
11
8
|
const summarize = async (source: MemorySource): Promise<MemorySummary> =>
|
|
12
9
|
(await import('./nightly-condense.js')).summarize(source);
|
|
13
10
|
|
|
14
|
-
/** Reuse the context-bus reader against a strictly read-only SQLite handle. */
|
|
15
|
-
function readContextEvents(since: string) {
|
|
16
|
-
const path = surfaceEventsDbPath();
|
|
17
|
-
if (!existsSync(path)) return [];
|
|
18
|
-
const db = new Database(path, { readonly: true, strict: true });
|
|
19
|
-
try { return listCoordEvents({ since }, { db }); }
|
|
20
|
-
finally { db.close(); }
|
|
21
|
-
}
|
|
22
|
-
|
|
23
11
|
export interface CondenseReport {
|
|
24
12
|
since: string;
|
|
25
13
|
until: string;
|
|
26
14
|
cards: MemoryItem[];
|
|
27
15
|
conflicts: number;
|
|
28
16
|
skipped: CondenseSkipped;
|
|
17
|
+
funnel: CondenseFunnel;
|
|
29
18
|
folded: number;
|
|
19
|
+
droppedHarnessChild: number;
|
|
30
20
|
markdown: string;
|
|
31
21
|
applied?: { written: number; retired: number; decisions: string[] };
|
|
32
22
|
}
|
|
@@ -35,12 +25,17 @@ function mdCell(value: string): string {
|
|
|
35
25
|
return value.replaceAll('|', '\\|').replaceAll('\n', ' ');
|
|
36
26
|
}
|
|
37
27
|
|
|
38
|
-
export function renderCondenseMarkdown(report: Pick<CondenseReport, 'since' | 'until' | 'cards' | 'conflicts' | 'skipped'>): string {
|
|
28
|
+
export function renderCondenseMarkdown(report: Pick<CondenseReport, 'since' | 'until' | 'cards' | 'conflicts' | 'skipped' | 'droppedHarnessChild' | 'funnel'>): string {
|
|
29
|
+
const seats = (counts: Record<string, number>) => ['OP', 'TC', 'MK', 'UX', ...Object.keys(counts).filter(seat => !['OP', 'TC', 'MK', 'UX'].includes(seat)).sort()]
|
|
30
|
+
.map(seat => `${seat} ${counts[seat] ?? 0}`).join(' · ');
|
|
31
|
+
const stage = (name: string, value: { total: number; bySeat: Record<string, number> }) =>
|
|
32
|
+
`${name} ${value.total} (${seats(value.bySeat)})`;
|
|
39
33
|
const lines = [
|
|
40
34
|
'# Long-term context memory candidates',
|
|
41
35
|
'',
|
|
42
36
|
`Window: ${report.since} → ${report.until} · Cards: ${report.cards.length} · Conflicts: ${report.conflicts}`,
|
|
43
|
-
`
|
|
37
|
+
`Funnel: ${stage('read', report.funnel.read)} → ${stage('sources', report.funnel.sources)} → ${stage('summarized', report.funnel.summarized)} → ${stage('cards', report.funnel.cards)}`,
|
|
38
|
+
`Skipped: ${JSON.stringify(report.skipped)} · Dropped harness-child: ${report.droppedHarnessChild}`,
|
|
44
39
|
'',
|
|
45
40
|
];
|
|
46
41
|
for (const card of report.cards) {
|
|
@@ -180,15 +175,15 @@ export async function runContextCondense(options: {
|
|
|
180
175
|
const now = options.now ?? new Date();
|
|
181
176
|
const until = now.toISOString();
|
|
182
177
|
const since = new Date(now.getTime() - options.hours * 3_600_000).toISOString();
|
|
183
|
-
const { cards, skipped, folded } = await condenseContextWindowWithReport(since, until, [], {
|
|
178
|
+
const { cards, skipped, folded, droppedHarnessChild, funnel } = await condenseContextWindowWithReport(since, until, [], {
|
|
184
179
|
summarize: deps.summarize,
|
|
185
|
-
events: deps.events ??
|
|
180
|
+
events: deps.events ?? readCondenseEvents,
|
|
186
181
|
decisions: deps.decisions ?? (() => new DecisionLedger(options.root ? { stateDir: options.root } : {}).list({ status: 'all' })
|
|
187
182
|
.filter(decision => decision.raisedBy.agent !== 'context-condense')),
|
|
188
183
|
}, now);
|
|
189
184
|
const conflicts = cards.filter(card => card.conflict !== undefined).length;
|
|
190
|
-
const report: CondenseReport = { since, until, cards, conflicts, skipped, folded,
|
|
191
|
-
markdown: renderCondenseMarkdown({ since, until, cards, conflicts, skipped }) };
|
|
185
|
+
const report: CondenseReport = { since, until, cards, conflicts, skipped, folded, droppedHarnessChild, funnel,
|
|
186
|
+
markdown: renderCondenseMarkdown({ since, until, cards, conflicts, skipped, droppedHarnessChild, funnel }) };
|
|
192
187
|
if (options.apply === true) report.applied = applyCondensedCards(cards, store ?? defaultStore(options.root));
|
|
193
188
|
return report;
|
|
194
189
|
}
|