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
|
@@ -56,6 +56,12 @@ docs-land:
|
|
|
56
56
|
verify:
|
|
57
57
|
command: 'bun scripts/release-loop/verify-node.ts'
|
|
58
58
|
timeout_ms: 1800000
|
|
59
|
+
vault-note:
|
|
60
|
+
command: 'bun scripts/release-loop/vault-note-node.ts'
|
|
61
|
+
timeout_ms: 60000
|
|
62
|
+
release-story:
|
|
63
|
+
command: 'bun scripts/release-story/draft.ts --graph'
|
|
64
|
+
timeout_ms: 600000
|
|
59
65
|
ops-upgrade:
|
|
60
66
|
command: 'bun scripts/release-loop/ops-upgrade-node.ts'
|
|
61
67
|
timeout_ms: 1200000
|
|
@@ -30,6 +30,8 @@ nodes:
|
|
|
30
30
|
- { node_id: npm-publish, kind: git, recipe: 'cmd:npm-publish', max_visits: 1 }
|
|
31
31
|
- { node_id: docs-land, kind: git, recipe: 'cmd:docs-land', max_visits: 1 }
|
|
32
32
|
- { node_id: verify, kind: gate, recipe: 'cmd:verify', max_visits: 1 }
|
|
33
|
+
- { node_id: vault-note, kind: gate, recipe: 'cmd:vault-note', max_visits: 1 }
|
|
34
|
+
- { node_id: release-story, kind: gate, recipe: 'cmd:release-story', max_visits: 1 }
|
|
33
35
|
- { node_id: ops-upgrade, kind: agent, recipe: 'cmd:ops-upgrade', max_visits: 1 }
|
|
34
36
|
- { node_id: version-dev-bump, kind: git, recipe: 'cmd:version-dev-bump', max_visits: 1 }
|
|
35
37
|
- { node_id: done, kind: gate, max_visits: 1 }
|
|
@@ -54,6 +56,9 @@ edges:
|
|
|
54
56
|
- { from: publish, on: outcome, map: { ok: npm-publish, fail: failed, error: failed } }
|
|
55
57
|
- { from: npm-publish, on: outcome, map: { ok: docs-land, fail: docs-land, error: docs-land } }
|
|
56
58
|
- { from: docs-land, on: outcome, map: { ok: verify, fail: failed, error: failed } }
|
|
57
|
-
- { from: verify, on: outcome, map: { ok:
|
|
59
|
+
- { from: verify, on: outcome, map: { ok: vault-note, fail: failed, error: failed } }
|
|
60
|
+
# Warning-only: vault note and release story failures after publication never block operational upgrades.
|
|
61
|
+
- { from: vault-note, on: outcome, map: { ok: release-story, fail: release-story, error: release-story } }
|
|
62
|
+
- { from: release-story, on: outcome, map: { ok: ops-upgrade, fail: ops-upgrade, error: ops-upgrade } }
|
|
58
63
|
- { from: ops-upgrade, on: outcome, map: { ok: version-dev-bump, fail: version-dev-bump, error: version-dev-bump } }
|
|
59
64
|
- { from: version-dev-bump, on: outcome, map: { ok: done, fail: failed, error: failed } }
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
graph_id: rhythm-daily
|
|
2
|
+
loop:
|
|
3
|
+
title: 데일리 리뷰
|
|
4
|
+
description: 어제 착지·판·루프·그리드·결정·위험·외부 동향을 한 장으로 읽는다
|
|
5
|
+
trigger:
|
|
6
|
+
cron: '30 8 * * *'
|
|
7
|
+
version: 1
|
|
8
|
+
entry_node: collect
|
|
9
|
+
terminal_nodes: [done, failed]
|
|
10
|
+
nodes:
|
|
11
|
+
- node_id: collect
|
|
12
|
+
kind: agent
|
|
13
|
+
recipe: cmd:collect
|
|
14
|
+
max_visits: 1
|
|
15
|
+
- node_id: compose
|
|
16
|
+
kind: agent
|
|
17
|
+
recipe: cmd:compose
|
|
18
|
+
max_visits: 1
|
|
19
|
+
- node_id: deliver
|
|
20
|
+
kind: agent
|
|
21
|
+
recipe: cmd:deliver
|
|
22
|
+
max_visits: 1
|
|
23
|
+
- node_id: done
|
|
24
|
+
kind: gate
|
|
25
|
+
max_visits: 1
|
|
26
|
+
- node_id: failed
|
|
27
|
+
kind: gate
|
|
28
|
+
max_visits: 1
|
|
29
|
+
edges:
|
|
30
|
+
- from: collect
|
|
31
|
+
on: outcome
|
|
32
|
+
map: { ok: compose, fail: compose }
|
|
33
|
+
- from: compose
|
|
34
|
+
on: outcome
|
|
35
|
+
map: { ok: deliver, fail: failed }
|
|
36
|
+
- from: deliver
|
|
37
|
+
on: outcome
|
|
38
|
+
map: { ok: done, fail: failed }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
collect:
|
|
2
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/../../scripts/rhythm/daily.ts" collect --json'
|
|
3
|
+
timeout_ms: 400000
|
|
4
|
+
compose:
|
|
5
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/../../scripts/rhythm/daily.ts" compose --json'
|
|
6
|
+
timeout_ms: 30000
|
|
7
|
+
deliver:
|
|
8
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/../../scripts/rhythm/daily.ts" deliver --json'
|
|
9
|
+
timeout_ms: 30000
|
|
10
|
+
weekly-collect:
|
|
11
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/../../scripts/rhythm/weekly.ts" collect --json'
|
|
12
|
+
timeout_ms: 400000
|
|
13
|
+
weekly-compose:
|
|
14
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/../../scripts/rhythm/weekly.ts" compose --json'
|
|
15
|
+
timeout_ms: 30000
|
|
16
|
+
weekly-deliver:
|
|
17
|
+
command: 'bun "$ELANOUS_GRAPH_DIR/../../scripts/rhythm/weekly.ts" deliver --json'
|
|
18
|
+
timeout_ms: 30000
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
graph_id: rhythm-weekly
|
|
2
|
+
loop:
|
|
3
|
+
title: 주간 미팅 자료
|
|
4
|
+
description: 지난주 성과·이번 주 판·현안·이행 대조·결정·외부 동향을 한 장으로 읽는다
|
|
5
|
+
trigger:
|
|
6
|
+
cron: '0 9 * * 1'
|
|
7
|
+
version: 1
|
|
8
|
+
entry_node: collect
|
|
9
|
+
terminal_nodes: [done, failed]
|
|
10
|
+
nodes:
|
|
11
|
+
- node_id: collect
|
|
12
|
+
kind: agent
|
|
13
|
+
recipe: cmd:weekly-collect
|
|
14
|
+
max_visits: 1
|
|
15
|
+
- node_id: compose
|
|
16
|
+
kind: agent
|
|
17
|
+
recipe: cmd:weekly-compose
|
|
18
|
+
max_visits: 1
|
|
19
|
+
- node_id: deliver
|
|
20
|
+
kind: agent
|
|
21
|
+
recipe: cmd:weekly-deliver
|
|
22
|
+
max_visits: 1
|
|
23
|
+
- node_id: done
|
|
24
|
+
kind: gate
|
|
25
|
+
max_visits: 1
|
|
26
|
+
- node_id: failed
|
|
27
|
+
kind: gate
|
|
28
|
+
max_visits: 1
|
|
29
|
+
edges:
|
|
30
|
+
# A failed collect must not let compose deliver an older file of the same week (review round 3).
|
|
31
|
+
- from: collect
|
|
32
|
+
on: outcome
|
|
33
|
+
map: { ok: compose, fail: failed }
|
|
34
|
+
- from: compose
|
|
35
|
+
on: outcome
|
|
36
|
+
map: { ok: deliver, fail: failed }
|
|
37
|
+
- from: deliver
|
|
38
|
+
on: outcome
|
|
39
|
+
map: { ok: done, fail: failed }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
graph_id: youtube-summary
|
|
2
|
+
version: 1
|
|
3
|
+
loop:
|
|
4
|
+
title: Built-in YouTube summary
|
|
5
|
+
description: Turn video captions (or STT) into a local summary or study note; no publishing or external skill.
|
|
6
|
+
exec_request: true
|
|
7
|
+
inputs: [url, format]
|
|
8
|
+
trigger:
|
|
9
|
+
events: [manual]
|
|
10
|
+
entry_node: summary
|
|
11
|
+
terminal_nodes: [done, failed]
|
|
12
|
+
nodes:
|
|
13
|
+
- { node_id: summary, kind: agent, recipe: 'cmd:summary', max_visits: 1 }
|
|
14
|
+
- { node_id: done, kind: gate, max_visits: 1 }
|
|
15
|
+
- { node_id: failed, kind: gate, max_visits: 1 }
|
|
16
|
+
edges:
|
|
17
|
+
- { from: summary, on: outcome, map: { ok: done, no-transcript: done, invalid-video-id: failed, fail: failed } }
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elanous",
|
|
3
3
|
"mcpName": "io.github.ElanvitalAI/elanous",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.15",
|
|
5
5
|
"description": "Self-healing coding harness where eyes, hands, and memory all turn on one sentence — it authors its own goals, implements them in isolated worktrees, and reviews its own work.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"author": "Elanvital AI",
|
|
@@ -34,18 +34,13 @@ const statePattern = /(?:\b(?:homedir|HOME|ELANOUS_(?:STATE|CONFIG|NEXUS)_DIR|EL
|
|
|
34
34
|
export type StaticIsolationSignal = 'ELANOUS_STATE_DIR' | '--config-dir' | 'mkdtemp';
|
|
35
35
|
export type StaticSafetyClassification = 'isolated' | 'manual-review';
|
|
36
36
|
|
|
37
|
-
const inspectedManualReviews: Record<string, string> = {
|
|
38
|
-
'src/autopilot/build/config-isolation.test.ts': 'setElanousConfigDir redirects the tested config into an isolated worktree path; SHA assertions prove the real config is unchanged.',
|
|
39
|
-
'test/artifact-store.test.ts': 'the apparent writes target an in-memory ArtifactFs fake, not the host filesystem.',
|
|
40
|
-
'test/pty-drive-workdir-decision.test.ts': 'setElanousConfigDir receives only /tmp paths and the afterEach reset restores the override.',
|
|
41
|
-
'test/working-dir.test.ts': 'the fixed test root is derived from tmpdir() and removed by afterAll.',
|
|
42
|
-
};
|
|
43
|
-
|
|
44
37
|
export type WriterCall = {
|
|
45
38
|
line: number;
|
|
46
39
|
pattern: string;
|
|
47
40
|
staticIsolationSignals: StaticIsolationSignal[];
|
|
48
41
|
staticSafety: StaticSafetyClassification;
|
|
42
|
+
/** The expanded target names the real home (homedir(), $HOME, ~/) — the only writer calls that fail the audit. */
|
|
43
|
+
realHomeTarget: boolean;
|
|
49
44
|
};
|
|
50
45
|
|
|
51
46
|
type AuditFinding = {
|
|
@@ -123,8 +118,10 @@ export function classifyStaticIsolation(text: string): { signals: StaticIsolatio
|
|
|
123
118
|
return { signals, safety: signals.length ? 'isolated' : 'manual-review' };
|
|
124
119
|
}
|
|
125
120
|
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
const REAL_HOME_TARGET = /\bhomedir\s*\(|process\.env\.HOME\b|process\.env\[\s*['"]HOME['"]\s*\]|['"`]~\//;
|
|
122
|
+
|
|
123
|
+
function writerIsolation(source: ts.SourceFile, writer: ts.CallExpression | ts.NewExpression, name: string): { signals: StaticIsolationSignal[]; safety: StaticSafetyClassification; realHomeTarget: boolean } {
|
|
124
|
+
if (name === 'mkdtemp' || name === 'mkdtempSync') return { signals: ['mkdtemp'], safety: 'isolated', realHomeTarget: false };
|
|
128
125
|
const variables = new Map<string, string>();
|
|
129
126
|
const visit = (node: ts.Node): void => {
|
|
130
127
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) variables.set(node.name.text, node.initializer.getText(source));
|
|
@@ -138,7 +135,40 @@ function writerIsolation(source: ts.SourceFile, writer: ts.CallExpression | ts.N
|
|
|
138
135
|
return `(${expand(variables.get(name)!)})`;
|
|
139
136
|
});
|
|
140
137
|
const argumentsText = writer.arguments?.map((argument) => argument.getText(source)).join(' ') ?? '';
|
|
141
|
-
return classifyStaticIsolation(expand(argumentsText));
|
|
138
|
+
return { ...classifyStaticIsolation(expand(argumentsText)), realHomeTarget: realHomeTarget(source, writer) };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Lexically nearest declaration of `name` visible before `at` (same file-wide name reused across tests must not leak). */
|
|
142
|
+
function scopedInitializer(source: ts.SourceFile, at: ts.Node, name: string): ts.Expression | undefined {
|
|
143
|
+
for (let scope: ts.Node | undefined = at.parent; scope; scope = scope.parent) {
|
|
144
|
+
if (!ts.isBlock(scope) && !ts.isSourceFile(scope) && !ts.isModuleBlock(scope)) continue;
|
|
145
|
+
let found: ts.Expression | undefined;
|
|
146
|
+
for (const statement of scope.statements) {
|
|
147
|
+
if (statement.pos >= at.pos) break;
|
|
148
|
+
if (!ts.isVariableStatement(statement)) continue;
|
|
149
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
150
|
+
if (ts.isIdentifier(declaration.name) && declaration.name.text === name && declaration.initializer) found = declaration.initializer;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (found) return found;
|
|
154
|
+
}
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Only the target path (first argument) counts — written content may mention homedir() as fixture text. */
|
|
159
|
+
function realHomeTarget(source: ts.SourceFile, writer: ts.CallExpression | ts.NewExpression): boolean {
|
|
160
|
+
const target = writer.arguments?.[0];
|
|
161
|
+
if (!target) return false;
|
|
162
|
+
const seen = new Set<string>();
|
|
163
|
+
const expand = (node: ts.Node, text: string): string => text.replace(/\b[A-Za-z_$][\w$]*\b/g, (name) => {
|
|
164
|
+
if (seen.has(name)) return name;
|
|
165
|
+
const initializer = scopedInitializer(source, node, name);
|
|
166
|
+
if (!initializer) return name;
|
|
167
|
+
seen.add(name);
|
|
168
|
+
return `(${expand(initializer, initializer.getText(source))})`;
|
|
169
|
+
});
|
|
170
|
+
const expanded = expand(target, target.getText(source));
|
|
171
|
+
return REAL_HOME_TARGET.test(expanded) && classifyStaticIsolation(expanded).safety !== 'isolated';
|
|
142
172
|
}
|
|
143
173
|
|
|
144
174
|
export type CandidateClassification = {
|
|
@@ -158,18 +188,30 @@ export function addedCandidates(report: ReturnType<typeof auditTestStateWrites>,
|
|
|
158
188
|
return new Set(report.findings.map(({ file }) => file).filter((file) => !previousInventory.has(file)));
|
|
159
189
|
}
|
|
160
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Fails only writer calls whose target reaches the real home without isolation evidence. A static scan cannot
|
|
193
|
+
* follow a temp directory through function returns or parameters, so "no evidence" alone stays visible as
|
|
194
|
+
* manual review in the rendered inventory instead of failing hundreds of mkdtemp-backed writes.
|
|
195
|
+
*/
|
|
196
|
+
export function unclassifiedCandidates(report: ReturnType<typeof auditTestStateWrites>, previousInventory: ReadonlySet<string>): string[] {
|
|
197
|
+
const { isolated, manualReview } = classifyCandidates(report, addedCandidates(report, previousInventory));
|
|
198
|
+
// The file-wide isolation label can be borrowed from a same-named temp variable in another test, so the
|
|
199
|
+
// scoped real-HOME judgement decides on every call, including ones labelled isolated.
|
|
200
|
+
return [...isolated, ...manualReview].sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line)
|
|
201
|
+
.filter(({ realHomeTarget }) => realHomeTarget)
|
|
202
|
+
.map(({ file, line, pattern }) => `${file}:${line} ${pattern} — writes a real HOME path without isolation evidence`);
|
|
203
|
+
}
|
|
204
|
+
|
|
161
205
|
function classificationSummary(classification: CandidateClassification): string {
|
|
162
206
|
const { findings, isolated, manualReview } = classification;
|
|
163
|
-
const isolatedFiles = findings.filter(({ staticSafety }) => staticSafety === 'isolated');
|
|
164
207
|
const manualReviewFiles = findings.filter(({ staticSafety }) => staticSafety === 'manual-review');
|
|
165
208
|
return [
|
|
166
209
|
`- August 8 candidate window: **${findings.length} files** (current inventory minus the preserved August 8 inventory).`,
|
|
167
|
-
`- Candidate classification (files): **${
|
|
168
|
-
`- ㉡ candidate files: ${manualReviewFiles.length ? manualReviewFiles.map(({ file }) => `\`${file}\``).join(', ') : '0건'}.`,
|
|
210
|
+
`- Candidate classification (files): **${findings.length - manualReviewFiles.length} ㉠ isolated** / **${manualReviewFiles.length} ㉡ manual review**.`,
|
|
169
211
|
`- Writer-call evidence in the August 8 window: **${isolated.length} isolated** / **${manualReview.length} manual review**; a file is ㉡ whenever any writer call lacks a direct approved signal.`,
|
|
170
212
|
`- ㉡ writer calls: ${manualReview.length ? manualReview.map(({ file, line, pattern }) => `\`${file}:${line} ${pattern}\``).join(', ') : '0건'}.`,
|
|
171
|
-
`- Manual
|
|
172
|
-
...manualReviewFiles.map(({ file }) => `- Manual
|
|
213
|
+
`- Manual review required: **${manualReviewFiles.length}** candidate files. This static audit does not establish whether an unclassified write targets a real HOME/state path.`,
|
|
214
|
+
...manualReviewFiles.map(({ file }) => `- Manual review pending — \`${file}\`: isolation is unverified.`),
|
|
173
215
|
].join('\n');
|
|
174
216
|
}
|
|
175
217
|
|
|
@@ -198,7 +240,7 @@ export function auditTestStateWrites(root = repo): { total: number; findings: Au
|
|
|
198
240
|
visit(source);
|
|
199
241
|
const writerCalls = writerNodes.map(({ name, node }) => {
|
|
200
242
|
const classification = writerIsolation(source, node, name);
|
|
201
|
-
return { line: lineOf(source, node), pattern: formatWriterPattern(name, node, source), staticIsolationSignals: classification.signals, staticSafety: classification.safety };
|
|
243
|
+
return { line: lineOf(source, node), pattern: formatWriterPattern(name, node, source), staticIsolationSignals: classification.signals, staticSafety: classification.safety, realHomeTarget: classification.realHomeTarget };
|
|
202
244
|
});
|
|
203
245
|
const stateLines = text.split('\n').flatMap((line, index) => statePattern.test(line) ? [index + 1] : []);
|
|
204
246
|
if (writerCalls.length && stateLines.length) {
|
|
@@ -221,8 +263,9 @@ export function auditTestStateWrites(root = repo): { total: number; findings: Au
|
|
|
221
263
|
return { total: files.length, findings };
|
|
222
264
|
}
|
|
223
265
|
|
|
224
|
-
export function renderAudit(report: ReturnType<typeof auditTestStateWrites>, previous = august8Inventory(repo)): string {
|
|
266
|
+
export function renderAudit(report: ReturnType<typeof auditTestStateWrites>, previous = august8Inventory(repo), reviewed = new Set(readFileSync(baselinePath, 'utf8').split('\n').filter((line) => line && !line.startsWith('#')))): string {
|
|
225
267
|
const candidateWindow = classifyCandidates(report, addedCandidates(report, previous));
|
|
268
|
+
const automatic = classifyCandidates(report, addedCandidates(report, reviewed));
|
|
226
269
|
const rows = report.findings.map((finding) =>
|
|
227
270
|
`| \`${finding.file}\` | ${finding.writerLines.join(', ')} | ${finding.writerTargets.map((target) => `\`${target}\``).join('<br>')} | ${finding.stateLines.join(', ')} | ${finding.risk} | ${finding.isolation} | ${finding.staticSafety} (${finding.staticIsolationSignals.join(', ') || 'none'}) |`,
|
|
228
271
|
);
|
|
@@ -230,6 +273,7 @@ export function renderAudit(report: ReturnType<typeof auditTestStateWrites>, pre
|
|
|
230
273
|
'# Test Home and State Write Audit',
|
|
231
274
|
'',
|
|
232
275
|
'This inventory is generated by `scripts/audit-test-state-writes.ts` using TypeScript AST call detection for filesystem/state writer calls and text detection for home/state-root evidence. It covers every `*.test.*` and `*.spec.*` file under `test/` and `src/`.',
|
|
276
|
+
`Rows for files outside the reviewed baseline are a snapshot from generation time; refresh with \`${INVENTORY_REFRESH_COMMAND}\`.`,
|
|
233
277
|
'',
|
|
234
278
|
`- Scanned test files: **${report.total}**`,
|
|
235
279
|
`- Potential home/state writers: **${report.findings.length}**`,
|
|
@@ -238,6 +282,17 @@ export function renderAudit(report: ReturnType<typeof auditTestStateWrites>, pre
|
|
|
238
282
|
'- Static safety classification is approval-gating: only `ELANOUS_STATE_DIR`, `--config-dir`, or `mkdtemp` counts as isolated; all other candidates require manual inspection.',
|
|
239
283
|
classificationSummary(candidateWindow),
|
|
240
284
|
'',
|
|
285
|
+
'## 분류기 통과(자동) — 사람 검토 기준선 밖',
|
|
286
|
+
'',
|
|
287
|
+
`Reviewed baseline: \`test/test-home-state-write-audit-baseline.txt\`. Regenerate with \`${INVENTORY_REFRESH_COMMAND}\`. Only candidates with every writer call statically isolated appear below; manual-review calls fail the audits rather than expanding the baseline.`,
|
|
288
|
+
'',
|
|
289
|
+
'| File | Writer lines | Approved isolation signals |',
|
|
290
|
+
'| --- | --- | --- |',
|
|
291
|
+
...automatic.findings.filter(({ staticSafety }) => staticSafety === 'isolated').map(({ file, writerLines, staticIsolationSignals }) =>
|
|
292
|
+
`| \`${file}\` | ${writerLines.join(', ')} | ${staticIsolationSignals.join(', ')} |`),
|
|
293
|
+
'',
|
|
294
|
+
'## 전체 스캔 (사람 검토 + 자동 분류)',
|
|
295
|
+
'',
|
|
241
296
|
'| File | Writer call lines (AST) | Write target or store argument | Home/state evidence lines (text) | Risk | Current isolation evidence | Static safety |',
|
|
242
297
|
'| --- | --- | --- | --- | --- | --- | --- |',
|
|
243
298
|
...rows,
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
3
|
+
import { extname, join, resolve } from 'node:path';
|
|
4
|
+
import { parse as parseYaml } from 'yaml';
|
|
5
|
+
import { debug } from '../../src/debug/log.js';
|
|
6
|
+
|
|
7
|
+
type Scope = 'public-docs' | 'release-notes' | 'site' | 'deck' | 'skill' | 'any';
|
|
8
|
+
type Kind = 'forbid' | 'require-form' | 'korean-in-english' | 'number-needs-source';
|
|
9
|
+
type Rule = { id: string; kind: Kind; pattern: string; flags?: string; scope: Scope[]; why: string; source: string; allowInCode?: boolean; allowPattern?: string; sourcePattern?: string };
|
|
10
|
+
type Finding = { file: string; line: number; id: string; match: string };
|
|
11
|
+
type GlossaryEntry = { internal: string; public: string; publicForbidden: boolean };
|
|
12
|
+
const scopes: Scope[] = ['public-docs', 'release-notes', 'site', 'deck', 'skill', 'any'];
|
|
13
|
+
const kinds: Kind[] = ['forbid', 'require-form', 'korean-in-english', 'number-needs-source'];
|
|
14
|
+
const defaultRules = join(import.meta.dir, '../../docs/brand/brand-rules.yaml');
|
|
15
|
+
export const BRAND_RULES_UNMEASURED = '규칙 없음 — 측정 불가';
|
|
16
|
+
|
|
17
|
+
function loadRules(path: string): Rule[] {
|
|
18
|
+
const document = parseYaml(readFileSync(path, 'utf8')) as unknown;
|
|
19
|
+
if (!document || typeof document !== 'object' || !('rules' in document) || !Array.isArray(document.rules)) throw new Error(`invalid brand rules: ${path}`);
|
|
20
|
+
const glossary = 'glossary' in document ? document.glossary : [];
|
|
21
|
+
if (!Array.isArray(glossary) || !glossary.every((entry: unknown) => entry && typeof entry === 'object'
|
|
22
|
+
&& typeof (entry as GlossaryEntry).internal === 'string' && typeof (entry as GlossaryEntry).public === 'string'
|
|
23
|
+
&& typeof (entry as GlossaryEntry).publicForbidden === 'boolean')) throw new Error(`invalid brand glossary: ${path}`);
|
|
24
|
+
const ids = new Set<string>();
|
|
25
|
+
const rules: Rule[] = document.rules.map((candidate: unknown) => {
|
|
26
|
+
if (!candidate || typeof candidate !== 'object') throw new Error(`invalid brand rule: ${path}`);
|
|
27
|
+
const rule = candidate as Partial<Rule>;
|
|
28
|
+
if (typeof rule.id !== 'string' || !rule.id || ids.has(rule.id) || !kinds.includes(rule.kind as Kind)
|
|
29
|
+
|| typeof rule.pattern !== 'string' || !rule.pattern || typeof rule.why !== 'string' || !rule.why
|
|
30
|
+
|| typeof rule.source !== 'string' || !rule.source || !Array.isArray(rule.scope) || !rule.scope.length
|
|
31
|
+
|| !rule.scope.every((scope) => scopes.includes(scope)) || (rule.flags !== undefined && typeof rule.flags !== 'string')
|
|
32
|
+
|| (rule.allowInCode !== undefined && typeof rule.allowInCode !== 'boolean')
|
|
33
|
+
|| (rule.allowPattern !== undefined && typeof rule.allowPattern !== 'string')
|
|
34
|
+
|| (rule.kind === 'number-needs-source' && (typeof rule.sourcePattern !== 'string' || !rule.sourcePattern))) throw new Error(`invalid brand rule: ${path}`);
|
|
35
|
+
new RegExp(rule.pattern, rule.flags);
|
|
36
|
+
if (rule.sourcePattern) new RegExp(rule.sourcePattern, rule.flags);
|
|
37
|
+
if (rule.allowPattern) new RegExp(rule.allowPattern, rule.flags);
|
|
38
|
+
ids.add(rule.id);
|
|
39
|
+
return rule as Rule;
|
|
40
|
+
});
|
|
41
|
+
for (const entry of glossary as GlossaryEntry[]) {
|
|
42
|
+
if (!entry.publicForbidden) continue;
|
|
43
|
+
const id = `GLOSSARY-${entry.internal}`;
|
|
44
|
+
if (!entry.internal || ids.has(id)) throw new Error(`invalid brand glossary: ${path}`);
|
|
45
|
+
ids.add(id);
|
|
46
|
+
const literal = entry.internal.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
47
|
+
const pattern = `(?<![가-힣])${literal}(?![가-힣])`;
|
|
48
|
+
rules.push({ id, kind: 'forbid', pattern, scope: ['any'], why: `Use ${entry.public} instead of the internal term.`, source: `${path}#glossary` });
|
|
49
|
+
}
|
|
50
|
+
return rules;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function filesAt(paths: string[]): string[] {
|
|
54
|
+
const files = new Set<string>();
|
|
55
|
+
const visit = (path: string): void => {
|
|
56
|
+
const stat = statSync(path);
|
|
57
|
+
if (stat.isDirectory()) {
|
|
58
|
+
for (const entry of readdirSync(path, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
59
|
+
if (entry.isDirectory() || entry.isFile()) visit(join(path, entry.name));
|
|
60
|
+
}
|
|
61
|
+
} else if (stat.isFile()) files.add(path);
|
|
62
|
+
};
|
|
63
|
+
for (const path of paths) visit(resolve(path));
|
|
64
|
+
return [...files].sort();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function maskUiStrings(line: string): string {
|
|
68
|
+
const ui = /^\s*(?:>\s*`?\s*)?(?:(?:const|let|var)\s+)?(?:label|title|button|placeholder|ariaLabel|aria-label|tooltip|caption|uiText|uiMessage)\s*[:=]\s*(["'`])([^"'`\n]*[가-힣][^"'`\n]*)\1/;
|
|
69
|
+
const chars = line.split('');
|
|
70
|
+
const match = ui.exec(line);
|
|
71
|
+
if (match) {
|
|
72
|
+
const value = match[2]!;
|
|
73
|
+
const start = match.index + match[0].indexOf(value);
|
|
74
|
+
for (let i = start; i < start + value.length; i++) chars[i] = ' ';
|
|
75
|
+
}
|
|
76
|
+
return chars.join('');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function htmlVisible(content: string): string {
|
|
80
|
+
const blank = (text: string) => text.replace(/[^\r\n]/g, ' ');
|
|
81
|
+
return content.replace(/<!--[\s\S]*?-->|<(?:script|style)\b[^>]*>[\s\S]*?<\/\s*(?:script|style)\s*>/gi, blank)
|
|
82
|
+
.replace(/<(?:[^>"']|"[^"]*"|'[^']*')*>/g, (tag) => {
|
|
83
|
+
if (!/^<img\b/i.test(tag)) return blank(tag);
|
|
84
|
+
const alt = /\balt\s*=\s*(["'])(.*?)\1/is.exec(tag);
|
|
85
|
+
if (!alt) return blank(tag);
|
|
86
|
+
const offset = alt.index + alt[0].indexOf(alt[2]!);
|
|
87
|
+
return blank(tag.slice(0, offset)) + alt[2] + blank(tag.slice(offset + alt[2].length));
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isEnglishDocument(content: string, file: string, scope: Scope): boolean {
|
|
92
|
+
const html = ['.html', '.htm'].includes(extname(file).toLowerCase());
|
|
93
|
+
const frontmatter = /^---\s*\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(content)?.[1];
|
|
94
|
+
const language = html
|
|
95
|
+
? /<html\b[^>]*\blang\s*=\s*["']?([a-z]{2,}(?:-[a-z]+)*)/i.exec(content)?.[1]
|
|
96
|
+
?? /<meta\b[^>]*\b(?:http-equiv\s*=\s*["']?content-language|name\s*=\s*["']?language)[^>]*\bcontent\s*=\s*["']?([a-z]{2,})/i.exec(content)?.[1]
|
|
97
|
+
: frontmatter ? /^lang(?:uage)?\s*:\s*["']?([a-z]{2,}(?:-[a-z]+)*)/im.exec(frontmatter)?.[1] : undefined;
|
|
98
|
+
if (language) return /^en(?:-|$)/i.test(language);
|
|
99
|
+
// Language-suffixed filenames are an explicit declaration, even without frontmatter.
|
|
100
|
+
if (/(?:^|[._-])(?:en|ko)(?:-[a-z]+)?\.[^.]+$/i.test(file)) return /(?:^|[._-])en(?:-[a-z]+)?\.[^.]+$/i.test(file);
|
|
101
|
+
if (scope !== 'site') return false;
|
|
102
|
+
const body = (html ? htmlVisible(content) : content.replace(/^---\s*\r?\n[\s\S]*?\r?\n---/, ''))
|
|
103
|
+
.replace(/\bElanous\b/gi, '');
|
|
104
|
+
return /\b[A-Za-z]{2,}(?:\s+[A-Za-z]{2,}){2,}\b/.test(body);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function findInFile(file: string, rules: Rule[], scope: Scope): Finding[] {
|
|
108
|
+
const findings: Finding[] = [];
|
|
109
|
+
const content = readFileSync(file, 'utf8');
|
|
110
|
+
const html = ['.html', '.htm'].includes(extname(file).toLowerCase());
|
|
111
|
+
const englishDocument = isEnglishDocument(content, file, scope);
|
|
112
|
+
const markdown = ['.md', '.mdx'].includes(extname(file).toLowerCase());
|
|
113
|
+
const bodyLines = html ? htmlVisible(content).split(/\r?\n/) : [];
|
|
114
|
+
let fence: { marker: string; length: number } | undefined;
|
|
115
|
+
const allLines = content.split(/\r?\n/);
|
|
116
|
+
for (const [index, line] of allLines.entries()) {
|
|
117
|
+
const lineFindings: Finding[] = [];
|
|
118
|
+
const marker = markdown ? /^ {0,3}(`{3,}|~{3,})/.exec(line)?.[1] : undefined;
|
|
119
|
+
if (marker) {
|
|
120
|
+
if (!fence) fence = { marker: marker[0]!, length: marker.length };
|
|
121
|
+
else if (marker[0] === fence.marker && marker.length >= fence.length) fence = undefined;
|
|
122
|
+
}
|
|
123
|
+
const quoted = markdown && /^\s*>/.test(line);
|
|
124
|
+
const inCode = Boolean(fence || marker || quoted);
|
|
125
|
+
for (const rule of rules) {
|
|
126
|
+
if (rule.kind === 'korean-in-english' && !englishDocument) continue;
|
|
127
|
+
const text = rule.kind === 'korean-in-english' && html ? bodyLines[index]! : line;
|
|
128
|
+
const visible = rule.allowInCode && inCode ? maskUiStrings(text) : text;
|
|
129
|
+
const allowed = rule.allowPattern ? [...line.matchAll(new RegExp(rule.allowPattern, [...new Set(`${rule.flags ?? ''}g`)].join('')))] : [];
|
|
130
|
+
const matches = visible.matchAll(new RegExp(rule.pattern, [...new Set(`${rule.flags ?? ''}g`)].join('')));
|
|
131
|
+
// B4: a measured number must carry its source marker on the same line or a neighbouring one.
|
|
132
|
+
const sourced = rule.kind === 'number-needs-source'
|
|
133
|
+
&& [index - 1, index, index + 1].some((i) => i >= 0 && i < allLines.length && new RegExp(rule.sourcePattern!, rule.flags).test(allLines[i]!));
|
|
134
|
+
for (const match of matches) {
|
|
135
|
+
if (sourced) break;
|
|
136
|
+
if (allowed.some((exception) => exception.index! <= match.index! && match.index! < exception.index! + exception[0].length)) continue;
|
|
137
|
+
lineFindings.push({ file, line: index + 1, id: rule.id, match: match[0] });
|
|
138
|
+
if (rule.kind === 'korean-in-english') break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
findings.push(...lineFindings.filter((finding) => finding.id !== 'B11'
|
|
142
|
+
|| !lineFindings.some((other) => other.id === 'BRAND-AUTONOMY' && other.match.includes(finding.match))));
|
|
143
|
+
}
|
|
144
|
+
return findings;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function checkBrand(scope: Scope, paths: string[], rulesPath = defaultRules): { files: number; findings: Finding[]; rules: number; missing: boolean; message?: string } {
|
|
148
|
+
if (!existsSync(rulesPath)) {
|
|
149
|
+
debug.log('brand.check', 'run', { scope, files: 0, findings: 0, rules: 0, missing: true, message: BRAND_RULES_UNMEASURED });
|
|
150
|
+
return { files: 0, findings: [], rules: 0, missing: true, message: BRAND_RULES_UNMEASURED };
|
|
151
|
+
}
|
|
152
|
+
const applicable = loadRules(rulesPath).filter((rule) => scope === 'any' || rule.scope.includes('any') || rule.scope.includes(scope));
|
|
153
|
+
const files = filesAt(paths);
|
|
154
|
+
const findings = files.flatMap((file) => findInFile(file, applicable, scope));
|
|
155
|
+
debug.log('brand.check', 'run', { scope, files: files.length, findings: findings.length, rules: applicable.length });
|
|
156
|
+
return { files: files.length, findings, rules: applicable.length, missing: false };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (import.meta.main) {
|
|
160
|
+
try {
|
|
161
|
+
const args = process.argv.slice(2);
|
|
162
|
+
let scope: Scope | undefined;
|
|
163
|
+
let rulesPath = defaultRules;
|
|
164
|
+
let json = false;
|
|
165
|
+
const paths: string[] = [];
|
|
166
|
+
for (let i = 0; i < args.length; i++) {
|
|
167
|
+
const arg = args[i]!;
|
|
168
|
+
if (arg === '--scope') scope = args[++i] as Scope;
|
|
169
|
+
else if (arg === '--rules') rulesPath = args[++i] ?? '';
|
|
170
|
+
else if (arg === '--json') json = true;
|
|
171
|
+
else if (arg.startsWith('--')) throw new Error(`unknown option: ${arg}`);
|
|
172
|
+
else paths.push(arg);
|
|
173
|
+
}
|
|
174
|
+
if (!scope || !scopes.includes(scope) || !paths.length || !rulesPath) throw new Error('usage: bun scripts/brand/check.ts --scope <scope> <file|folder...> [--rules <path>] [--json]');
|
|
175
|
+
const result = checkBrand(scope, paths, rulesPath);
|
|
176
|
+
if (result.missing) console.log(json ? JSON.stringify(result) : result.message);
|
|
177
|
+
else if (json) console.log(JSON.stringify({ scope, ...result }));
|
|
178
|
+
else {
|
|
179
|
+
for (const finding of result.findings) console.log(`${finding.file}:${finding.line}:${finding.id}:${finding.match}`);
|
|
180
|
+
console.log(`brand-check ${scope} files=${result.files} findings=${result.findings.length} rules=${result.rules}`);
|
|
181
|
+
}
|
|
182
|
+
if (result.missing) process.exitCode = 2;
|
|
183
|
+
else if (result.findings.length) process.exitCode = 1;
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
186
|
+
process.exitCode = 2;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -53,6 +53,9 @@ export type ChangedTsFiles = {
|
|
|
53
53
|
files: Set<string>;
|
|
54
54
|
base: string;
|
|
55
55
|
baseSource: string;
|
|
56
|
+
head?: string;
|
|
57
|
+
diffFiles?: Set<string>;
|
|
58
|
+
warnings?: string[];
|
|
56
59
|
failures?: { command: string; why: string }[];
|
|
57
60
|
};
|
|
58
61
|
|
|
@@ -78,23 +81,41 @@ export function changedTsFiles(run = sh, env: NodeJS.ProcessEnv = process.env):
|
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
83
|
|
|
81
|
-
const files = new Set<string>();
|
|
82
|
-
const add = (out: string) => {
|
|
83
|
-
for (const line of out.split('\n')) {
|
|
84
|
-
const file = line.trim().replace(/^\.\//, '');
|
|
85
|
-
if (/\.tsx?$/.test(file) && existsSync(file)) files.add(file);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
84
|
const failures: { command: string; why: string }[] = [];
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
// Revision reads are observational: an unreadable HEAD/base must not turn into a collection failure.
|
|
86
|
+
const quietRevision = (command: string): string => { try { return run(command).trim(); } catch { return ''; } };
|
|
87
|
+
const head = quietRevision('git rev-parse HEAD') || undefined;
|
|
88
|
+
const warnings: string[] = [];
|
|
89
|
+
if (head && quietRevision(`git rev-parse ${base}`) === head) {
|
|
90
|
+
const main = quietRevision('git rev-parse origin/main');
|
|
91
|
+
const merged = main && main !== head ? quietRevision('git merge-base HEAD origin/main') : '';
|
|
92
|
+
if (merged && merged !== head) {
|
|
93
|
+
warnings.push(`base=${base} 가 HEAD 와 같은 커밋 — origin/main (${main})과 달라 merge-base 로 재계산`);
|
|
94
|
+
base = merged;
|
|
95
|
+
baseSource = 'recomputed merge-base(origin/main)';
|
|
96
|
+
} else if (main && main !== head) {
|
|
97
|
+
failures.push({ command: `git rev-parse ${base}`, why: `base 가 HEAD(${head}) 와 같아 변경을 셀 수 없다 — TSC_BASE_REF 로 base 를 주라 (못 셌음 ≠ 0)` });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const files = new Set<string>();
|
|
102
|
+
const diffFiles = new Set<string>();
|
|
103
|
+
const commands = [`git diff --name-only ${base}...HEAD`, 'git diff --name-only HEAD', 'git ls-files --others --exclude-standard'];
|
|
104
|
+
for (const command of commands) {
|
|
105
|
+
try {
|
|
106
|
+
for (const line of run(command).split('\n')) {
|
|
107
|
+
const file = line.trim().replace(/^\.\//, '');
|
|
108
|
+
if (!file) continue;
|
|
109
|
+
const ts = /\.tsx?$/.test(file);
|
|
110
|
+
// A deleted .ts is not a compile candidate, so it must not count as «should have been collected».
|
|
111
|
+
if (!ts || existsSync(file)) diffFiles.add(file);
|
|
112
|
+
if (ts && existsSync(file)) files.add(file);
|
|
113
|
+
}
|
|
114
|
+
} catch (cause) {
|
|
91
115
|
failures.push({ command, why: cause instanceof Error ? cause.message : String(cause) });
|
|
92
116
|
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
addCommand('git diff --name-only HEAD');
|
|
96
|
-
addCommand('git ls-files --others --exclude-standard');
|
|
97
|
-
return { files, base, baseSource, failures };
|
|
117
|
+
}
|
|
118
|
+
return { files, base, baseSource, head, diffFiles, warnings, failures };
|
|
98
119
|
}
|
|
99
120
|
|
|
100
121
|
type RequiredExportField = { typeName: string; fieldName: string };
|
|
@@ -850,10 +871,13 @@ export function runGate(io: Partial<GateIo> = {}): void {
|
|
|
850
871
|
? { additions: [] }
|
|
851
872
|
: exportedLiteralUnionMembersInChanges(changed, base));
|
|
852
873
|
const changedResult = getChanged();
|
|
853
|
-
const { files: changed, base, baseSource, failures = [] } = changedResult instanceof Set
|
|
854
|
-
? { files: changedResult, base: 'injected test seam', baseSource: 'injected test seam', failures: [] }
|
|
874
|
+
const { files: changed, base, baseSource, head, diffFiles, warnings = [], failures = [] } = changedResult instanceof Set
|
|
875
|
+
? { files: changedResult, base: 'injected test seam', baseSource: 'injected test seam', head: 'injected test seam', diffFiles: new Set<string>(), warnings: [], failures: [] }
|
|
855
876
|
: changedResult;
|
|
856
|
-
|
|
877
|
+
for (const message of warnings) warn(`[tsc-gate] ⚠ ${message}`);
|
|
878
|
+
const diffCount = diffFiles?.size;
|
|
879
|
+
const diffTsCount = diffFiles && [...diffFiles].filter((file) => /\.tsx?$/.test(file)).length;
|
|
880
|
+
log(`[tsc-gate] base=${base} (${baseSource}); 변경 .ts ${changed.size}개 관측. (head=${head ?? 'unknown'} · 전체 diff 파일 ${diffCount ?? 'unknown'}개 · diff .ts ${diffTsCount ?? 'unknown'}개)`);
|
|
857
881
|
if (failures.length > 0) {
|
|
858
882
|
error('[tsc-gate] ⛔ 변경 파일 수집 실패:');
|
|
859
883
|
for (const { command, why } of failures) error(` ${command}: ${why}`);
|
|
@@ -861,7 +885,15 @@ export function runGate(io: Partial<GateIo> = {}): void {
|
|
|
861
885
|
exit(1);
|
|
862
886
|
return;
|
|
863
887
|
}
|
|
864
|
-
if (changed.size === 0) {
|
|
888
|
+
if (changed.size === 0) {
|
|
889
|
+
if (!(changedResult instanceof Set) && (!head || diffCount === undefined || diffTsCount === undefined || diffTsCount > 0)) {
|
|
890
|
+
error(`[tsc-gate] FAIL — 변경 파일 수집 불일치: 전체 diff 파일 ${diffCount ?? 'unknown'}개 · diff .ts ${diffTsCount ?? 'unknown'}개 · 수집 .ts 0개 (못 셌음 ≠ 0).`);
|
|
891
|
+
exit(1);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
log(`[tsc-gate] PASS — 변경된 .ts 파일 없음 · base=${base} · head=${head} · 전체 diff 파일 ${diffCount}개 · diff .ts ${diffTsCount}개 · 수집 .ts 0개; tsc 실행 없이 통과.`);
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
865
897
|
const fieldCheck = requiredExportFields(changed, base);
|
|
866
898
|
const requiredFields = Array.isArray(fieldCheck) ? fieldCheck : fieldCheck.fields;
|
|
867
899
|
if (!Array.isArray(fieldCheck) && fieldCheck.failure) {
|
package/scripts/coord-post.sh
CHANGED
|
@@ -220,7 +220,17 @@ fi
|
|
|
220
220
|
echo "[coord-post] 발신 성공$VIA · 채널 #$PR · 신원 [$ID] · 시각 $TS" >&2
|
|
221
221
|
# Only a successfully sent post is recorded; never pass the body or later lines to the ledger.
|
|
222
222
|
POST_URL=$(printf '%s\n' "$POST_OUTPUT" | grep -oE 'https://github\.com/[^[:space:]]+/issues/[0-9]+#issuecomment-[0-9]+|https://github\.com/[^[:space:]]+/pull/[0-9]+#issuecomment-[0-9]+' | head -1 || true)
|
|
223
|
-
|
|
223
|
+
# The channel is a production surface, so its record belongs in the production ledger, not this tree's
|
|
224
|
+
# test universe (OP 10-04 18:00). An explicit ELANOUS_STATE_DIR is the caller's chosen universe and wins;
|
|
225
|
+
# otherwise prefer the installed CLI (resolves prod from any tree), else pin the source CLI to prod.
|
|
226
|
+
if [ -n "${ELANOUS_STATE_DIR:-}" ]; then
|
|
227
|
+
RECORD=(bun bin/elanous.mjs coord event record)
|
|
228
|
+
elif command -v eln >/dev/null 2>&1; then
|
|
229
|
+
RECORD=(eln coord event record)
|
|
230
|
+
else
|
|
231
|
+
RECORD=(env ELANOUS_STATE_DIR="$HOME/.elanous" bun bin/elanous.mjs --config-dir "$HOME/.elanous" coord event record)
|
|
232
|
+
fi
|
|
233
|
+
if ! "${RECORD[@]}" --seat "$ID" --header "$(head -1 "$TMP")" --url "$POST_URL" >/dev/null 2>&1; then
|
|
224
234
|
echo '⚠️ 맥락 원장 기록 실패(발신은 됐다)' >&2
|
|
225
235
|
fi
|
|
226
236
|
exit 0
|