elanous 0.2.10 → 0.2.11
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/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +1 -0
- package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
- package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
- package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
- package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8605-ea5bee9dfae512c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-b12b6d2f8d2d1aea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-35414e0a007b4082.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-239c17b911b84e72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
- package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.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/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 -0
- package/apps/pwa/out/inside/index.txt +31 -0
- 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/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 -0
- package/apps/pwa/out/outputs/index.txt +30 -0
- 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 -24
- 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 -24
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/drafts/draft-cleanup.yaml +19 -0
- package/graphs/drafts/recipes.yaml +23 -0
- package/graphs/intake/intake-daily.yaml +2 -1
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/ops/secret-exposure.yaml +24 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -1
- package/graphs/video/explainer-line.yaml +2 -2
- package/package.json +2 -1
- package/plugins/elanous-hwp/README.md +25 -0
- package/plugins/elanous-hwp/cli.ts +22 -0
- package/plugins/elanous-hwp/converter.ts +237 -0
- package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
- package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
- package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
- package/plugins/elanous-hwp/plugin.json +16 -0
- package/plugins/elanous-hwp/plugin.ts +40 -0
- package/scripts/ci-public-export-leak-gate.ts +75 -14
- package/scripts/ci-public-leak-gate.ts +44 -9
- package/scripts/context-hooks/claude-stop.sh +4 -0
- package/scripts/context-hooks/emit.ts +24 -0
- package/scripts/context-hooks/post-tool-use.sh +4 -0
- package/scripts/coord-channel-watch.sh +12 -0
- package/scripts/coord-requests-bridge.ts +143 -0
- package/scripts/field-feed/lib.ts +4 -0
- package/scripts/fleet/node-setup.sh +20 -3
- package/scripts/homebrew-formula-bump.ts +69 -0
- package/scripts/marketing/measure-site-numbers.ts +169 -0
- package/scripts/measure-gate-shards.ts +309 -0
- package/scripts/mission-request-judge.ts +31 -1
- package/scripts/morning-report.ts +2 -2
- package/scripts/opportunity-scan.ts +1 -1
- package/scripts/release-loop/auto-approve-node.ts +21 -4
- package/scripts/release-loop/cut-branch.ts +84 -0
- package/scripts/release-loop/gate-node.ts +14 -13
- package/scripts/release-loop/mac-smoke-node.ts +229 -0
- package/scripts/release-loop/node-verdict.ts +3 -3
- package/scripts/release-loop/tui-node-checks.ts +42 -0
- package/scripts/release-loop/tui-sim-node.ts +32 -8
- package/scripts/release-loop/unattended-release.ts +5 -3
- package/scripts/release-loop/version-node.ts +61 -10
- package/scripts/signal-digest-cycle.ts +2 -7
- package/scripts/signal-digest-photo.ts +11 -0
- package/scripts/tech1-public-measure.ts +94 -0
- package/scripts/test-diet/node.ts +1 -1
- package/scripts/tui-regress.ts +2 -2
- package/scripts/ux/demo-rehearsal.ts +243 -0
- package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
- package/skills/diagram-master/SKILL.md +366 -0
- package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
- package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
- package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
- package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
- package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
- package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
- package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
- package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
- package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
- package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
- package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
- package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
- package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
- package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
- package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
- package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
- package/skills/diagram-master/references/examples/README.md +69 -0
- package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
- package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
- package/skills/diagram-master/references/pyproject.toml +16 -0
- package/skills/diagram-master/references/quality-checklist.md +33 -0
- package/skills/diagram-master/references/routing-guide.md +164 -0
- package/skills/diagram-master/references/uv.lock +1356 -0
- package/skills/google-workspace/SKILL.md +33 -0
- package/skills/google-workspace/scripts/gws-safe.sh +177 -0
- package/skills/grill-me/SKILL.md +1 -1
- package/skills/lecture-note-digitizer/SKILL.md +926 -0
- package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
- package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
- package/skills/lecture-note-digitizer/references/html-template.md +167 -0
- package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
- package/skills/omni-crawl/SKILL.md +21 -11
- package/skills/omni-crawl/scripts/main.ts +6 -6
- package/skills/omni-crawl/src/router.ts +3 -3
- package/skills/omni-digest/SKILL.md +17 -9
- package/skills/omni-digest/digest.md +2 -2
- package/skills/omni-digest/src/fetch.ts +30 -3
- package/skills/pitch-deck/SKILL.md +2 -2
- package/skills/pitch-deck/template.html +2 -2
- package/skills/youtube-master/SKILL.md +163 -0
- package/skills/youtube-master/package-lock.json +558 -0
- package/skills/youtube-master/package.json +13 -0
- package/skills/youtube-master/references/study-note-template.md +124 -0
- package/skills/youtube-master/scripts/main.ts +435 -0
- package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
- package/skills/youtube-master/src/audio.ts +63 -0
- package/skills/youtube-master/src/domain-staging.ts +50 -0
- package/skills/youtube-master/src/env.ts +36 -0
- package/skills/youtube-master/src/obsidian.ts +155 -0
- package/skills/youtube-master/src/router.ts +110 -0
- package/skills/youtube-master/src/stt.ts +199 -0
- package/skills/youtube-master/src/study-note.ts +175 -0
- package/skills/youtube-master/src/summarize.ts +358 -0
- package/skills/youtube-master/src/transcript.ts +76 -0
- package/skills/youtube-master/src/types.ts +91 -0
- package/skills/youtube-master/src/util.ts +93 -0
- package/skills/youtube-master/src/youtube.ts +70 -0
- package/skills/youtube-master/tsconfig.json +13 -0
- package/src/acp/server.ts +5 -2
- package/src/agent-mission/driver.ts +80 -10
- package/src/agent-mission/pty-decision.ts +71 -0
- package/src/agent-mission/review-loop.ts +30 -0
- package/src/autopilot/build/llm-conflict-merge.ts +16 -3
- package/src/autopilot/tool-registry.ts +2 -0
- package/src/boot/daemon-prompt-turn.ts +5 -0
- package/src/boot/daemon-runtime.ts +9 -3
- package/src/boot/daemon-tools/web-search.ts +3 -0
- package/src/card-followup/core.ts +109 -0
- package/src/chat/index.ts +1 -0
- package/src/cli/drive-input-jail.ts +101 -0
- package/src/cli/hooks-cli.ts +17 -3
- package/src/cli/make-cli.ts +43 -0
- package/src/cli/persona-cli.ts +90 -4
- package/src/cli/plugin-cli.ts +31 -22
- package/src/cli/pod-cli.ts +19 -9
- package/src/cli/pr-cli.ts +51 -15
- package/src/cli/pty-drive-cli.ts +41 -7
- package/src/cli/pty-drive-verdict.ts +40 -0
- package/src/cli/release-cli.ts +17 -4
- package/src/cli/setup-cli.ts +43 -1
- package/src/cli/skills-cli.ts +17 -1
- package/src/context-bus/claude-hooks-install.ts +10 -0
- package/src/context-bus/context-now-surfaces.ts +48 -0
- package/src/context-bus/context-now.ts +104 -0
- package/src/context-bus/coord-events.ts +2 -2
- package/src/context-bus/external-events.ts +52 -0
- package/src/context-bus/long-term-memory.ts +108 -0
- package/src/dashboard/index.ts +9 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +7 -1
- package/src/decisions/decision-cards.ts +10 -3
- package/src/decisions/decision-ledger.ts +13 -5
- package/src/discord/sprint21-runtime.ts +5 -1
- package/src/discord-self-message.ts +32 -0
- package/src/discord-slash-wire.ts +5 -1
- package/src/discord.ts +17 -1
- package/src/domains/core-tools.ts +3 -2
- package/src/domains/outbound-alert.ts +6 -1
- package/src/domains/telegram-kind-route.ts +51 -13
- package/src/exec-requests/default-outputs.ts +25 -0
- package/src/exec-requests/planner.ts +10 -2
- package/src/exec-requests/runner.ts +20 -6
- package/src/exec-requests/store.ts +3 -0
- package/src/execution-loop/launch-gate.ts +100 -0
- package/src/field/field-reel.ts +5 -1
- package/src/field-note/index.ts +201 -0
- package/src/field-note/transcribe.ts +248 -0
- package/src/graph-kinds/registry.ts +3 -3
- package/src/graph-runner/graph-cli.ts +61 -1
- package/src/graph-runner/graph-grow-llm.ts +37 -0
- package/src/graph-runner/graph-grow.ts +47 -0
- package/src/graph-runner/runner.ts +317 -25
- package/src/grok/agent-search.ts +8 -2
- package/src/guardian/secret-exposure-scan.ts +101 -0
- package/src/harness/harness-cli-command.ts +247 -7
- package/src/harness/harness-pod-dispatch.ts +43 -23
- package/src/harness/harness-seams.ts +38 -4
- package/src/harness/harness-stop.ts +23 -4
- package/src/harness/launch-registry.ts +91 -0
- package/src/harness/process-garbage.ts +5 -3
- package/src/hooks/dispatch.ts +82 -0
- package/src/hooks/expose.ts +254 -0
- package/src/hooks/github-shadow.ts +108 -0
- package/src/hooks/providers.ts +71 -5
- package/src/hooks/queue.ts +1 -1
- package/src/hooks/receiver.ts +21 -20
- package/src/index.ts +52 -14
- package/src/intake-plane/categorize.ts +18 -46
- package/src/intake-plane/discord-seat-work.ts +10 -0
- package/src/intake-plane/front-route-classifier.ts +33 -39
- package/src/intake-plane/persona-answer.ts +30 -0
- package/src/intake-plane/runtime-callables.ts +8 -1
- package/src/intake-plane/telegram-seat-work.ts +36 -2
- package/src/intake-plane/wish-folder.ts +67 -0
- package/src/intent-gate/gate.ts +30 -10
- package/src/live/detail-switch.ts +5 -0
- package/src/llm/difficulty.ts +57 -0
- package/src/llm/judge-layer.ts +101 -0
- package/src/llm/mission-router.ts +33 -19
- package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
- package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
- package/src/loops/contract/examples/steward.loop.yaml +50 -0
- package/src/loops/contract/loop-agent.schema.json +138 -0
- package/src/loops/contract/validate.ts +64 -0
- package/src/loops/registry.ts +20 -6
- package/src/market/signed-index.ts +4 -0
- package/src/maturity/bot-command-maturity.ts +45 -0
- package/src/maturity/feature-maturity.ts +26 -4
- package/src/model-tier/index.ts +2 -2
- package/src/model-tier/tier-resolver.ts +20 -20
- package/src/model-tier/types.ts +6 -6
- package/src/mss/logging/log-store.ts +15 -0
- package/src/nexus/api/config-model-tier.ts +5 -4
- package/src/nexus/api/fabric-plans.ts +53 -0
- package/src/nexus/api/harness-api.ts +5 -1
- package/src/nexus/api/health.ts +2 -0
- package/src/nexus/api/http-server.ts +36 -0
- package/src/nexus/api/inside-events.ts +226 -0
- package/src/nexus/api/meta-api.ts +21 -8
- package/src/nexus/api/missions.ts +6 -1
- package/src/nexus/api/ops-seats.ts +33 -0
- package/src/nexus/api/outputs-route.ts +44 -0
- package/src/nexus/api/seat-requests.ts +44 -2
- package/src/nexus/api/sessions-store.ts +17 -38
- package/src/nexus/api/sub-seat-charters.ts +34 -0
- package/src/nexus/api/tabs-mutations.ts +3 -1
- package/src/nexus/api/trace.ts +18 -6
- package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
- package/src/nexus/api/workflows.ts +80 -6
- package/src/nexus/index.ts +2 -0
- package/src/nexus/kinds/webterm.ts +2 -1
- package/src/nexus/webterm/pty.ts +6 -2
- package/src/onboarding/cli-auth-status.ts +80 -0
- package/src/onboarding/voice-ai.ts +3 -3
- package/src/org/seat-budget.ts +48 -0
- package/src/org/stall-escalation.ts +63 -0
- package/src/outputs/ledger.ts +120 -0
- package/src/persona/persona-todo.ts +79 -0
- package/src/persona/presets.ts +116 -2
- package/src/persona/team-set.ts +155 -0
- package/src/plugins/install/plugin-install.ts +14 -0
- package/src/plugins/maker/market-bundle.ts +109 -0
- package/src/plugins/maker/plugin-maker.ts +254 -11
- package/src/plugins/maker/wizard-generate.ts +103 -0
- package/src/pod-lease/admission.ts +150 -0
- package/src/pod-lease/dependency-state.ts +85 -0
- package/src/pod-lease/host-lease.ts +127 -0
- package/src/project/project-suggestion.ts +16 -0
- package/src/project/session-context.ts +24 -0
- package/src/release-loop/checklist.ts +2 -2
- package/src/release-loop/feature-store.ts +5 -3
- package/src/release-loop/next-md-merge.ts +58 -0
- package/src/release-loop/release-note.ts +59 -4
- package/src/seat-dispatch/seat-ask.ts +130 -0
- package/src/seat-dispatch/seat-questions.ts +100 -0
- package/src/seat-loop/seat-loop.ts +548 -29
- package/src/seat-loop/seat-report.ts +37 -2
- package/src/self-dev/dev-cli.ts +4 -1
- package/src/self-dev/dev-pipeline.ts +37 -1
- package/src/self-dev/draft-sweep.ts +38 -33
- package/src/self-dev/draft-triage-rules.ts +8 -5
- package/src/self-dev/fabric-plan-core.ts +297 -0
- package/src/self-dev/orchestrate.ts +36 -4
- package/src/self-dev/release-path-guard.ts +62 -0
- package/src/self-dev/shard-aggregate.ts +230 -0
- package/src/self-dev/shard-placement.ts +60 -0
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-pod-run.ts +77 -0
- package/src/self-dev/shard-run.ts +105 -0
- package/src/self-implement/gate-baseline.ts +1 -1
- package/src/self-implement/gate-cli.ts +40 -1
- package/src/self-implement/gate-scope.ts +37 -7
- package/src/self-implement/graph-variant.ts +171 -0
- package/src/self-implement/harness-policy-gates.ts +1 -1
- package/src/self-implement/headless-elanous-driver.ts +22 -3
- package/src/self-implement/host-regate.ts +22 -1
- package/src/self-implement/importer-test-index.ts +2 -1
- package/src/self-implement/l8-merge-queue.ts +130 -0
- package/src/self-implement/orchestrator.ts +242 -9
- package/src/self-implement/route-consumer-test-index.ts +122 -0
- package/src/self-implement/seams.ts +72 -11
- package/src/self-implement/test-seams.ts +1 -0
- package/src/self-implement/uncommitted-work.ts +59 -0
- package/src/session/index.ts +36 -13
- package/src/skills/gift-install.ts +215 -0
- package/src/skills/tools/web-search.ts +4 -1
- package/src/steward/launch.ts +42 -26
- package/src/steward/steward-cards.ts +17 -9
- package/src/steward/triage.ts +46 -14
- package/src/task-cards/card-cli.ts +25 -1
- package/src/task-orchestrator/mission.ts +5 -0
- package/src/task-orchestrator/store.ts +12 -3
- package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
- package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
- package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
- package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
- package/src/task-orchestrator/types.ts +2 -0
- package/src/telegram-card-followup.ts +16 -96
- package/src/telegram-commands.ts +10 -1
- package/src/telegram-project-command.ts +71 -0
- package/src/telegram-report.ts +21 -17
- package/src/telegram.ts +29 -3
- package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
- package/src/user-config.ts +102 -20
- package/src/version/packed-revision.json +1 -1
- package/src/web-search/grok.ts +7 -1
- package/src/web-search/index.ts +28 -1
- package/src/web-search/provider.ts +4 -0
- package/src/web-search/x-intent.ts +5 -0
- package/src/workflow-runtime/executor.ts +277 -47
- package/src/workflow-runtime/index.ts +1 -0
- package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
- package/src/workflow-runtime/plugin-kind-node.ts +1 -1
- package/src/workflow-runtime/schema.ts +79 -4
- package/src/workflow-runtime/types.ts +35 -4
- package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
- package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
- /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
// → 유도 결과는 **반드시 존재하는 파일**이어야 한다.
|
|
30
30
|
|
|
31
31
|
import { importerTestsNotInRunSet, type ImporterTestIndex } from './importer-test-index.js';
|
|
32
|
+
import type { RouteConsumerTestIndex } from './route-consumer-test-index.js';
|
|
33
|
+
|
|
34
|
+
export const GATE_CALLER_TEST_LIMIT = 30;
|
|
35
|
+
export type GateCallerReason = 'import' | 'route';
|
|
32
36
|
|
|
33
37
|
/** 테스트 파일 판정(기존 `seams.ts` 정규식과 동일 — 어휘 분기 금지). */
|
|
34
38
|
const TEST_RE = /\.(test|spec)\.[cm]?[tj]sx?$/;
|
|
@@ -258,6 +262,10 @@ interface GateScopeDecision {
|
|
|
258
262
|
readonly truncated: boolean;
|
|
259
263
|
readonly unresolvedRelativeSpecifiers: number;
|
|
260
264
|
} | null;
|
|
265
|
+
/** Selected reverse-dependent tests and why they were pulled into the gate. */
|
|
266
|
+
readonly callerTests: readonly { readonly file: string; readonly reasons: readonly GateCallerReason[] }[];
|
|
267
|
+
/** Caller tests exceeding the cap, retained in full for warning and diagnosis. */
|
|
268
|
+
readonly callerTestsOverflow: readonly { readonly file: string; readonly reasons: readonly GateCallerReason[] }[];
|
|
261
269
|
}
|
|
262
270
|
|
|
263
271
|
/**
|
|
@@ -274,7 +282,7 @@ export function resolveGateScope(
|
|
|
274
282
|
changed: readonly string[],
|
|
275
283
|
exists: (path: string) => boolean,
|
|
276
284
|
importerTestIndex?: ImporterTestIndex,
|
|
277
|
-
opts?: { mode?: GateScopeMode; isDeleted?: (path: string) => boolean },
|
|
285
|
+
opts?: { mode?: GateScopeMode; isDeleted?: (path: string) => boolean; routeConsumerTestIndex?: RouteConsumerTestIndex },
|
|
278
286
|
): GateScopeDecision {
|
|
279
287
|
// ⚠️ **실존 검증(리뷰 must-fix 4R)** — `gitChangedFiles` 는 `git diff --name-only HEAD` 라
|
|
280
288
|
// **삭제·rename 前 경로도 포함**한다. 그걸 그대로 `testArgs` 로 넘기면 필터가 아무것도 매치하지
|
|
@@ -328,15 +336,36 @@ export function resolveGateScope(
|
|
|
328
336
|
const observationFor = (runSet: readonly string[]) => importerTestIndex
|
|
329
337
|
? importerTestsNotInRunSet(importerTestIndex, userChanged, runSet)
|
|
330
338
|
: null;
|
|
331
|
-
const
|
|
339
|
+
const conventional = deriveRelatedTests(userChanged, exists);
|
|
340
|
+
const alreadySelected = new Set([...testFiles, ...conventional]);
|
|
341
|
+
const callers = new Map<string, Set<GateCallerReason>>();
|
|
342
|
+
for (const source of userChanged) {
|
|
343
|
+
for (const [reason, tests] of [
|
|
344
|
+
['import', importerTestIndex?.testsBySource.get(source) ?? []],
|
|
345
|
+
['route', opts?.routeConsumerTestIndex?.testsBySource.get(source) ?? []],
|
|
346
|
+
] as const) {
|
|
347
|
+
for (const test of tests) {
|
|
348
|
+
if (!exists(test) || alreadySelected.has(test)) continue;
|
|
349
|
+
const reasons = callers.get(test) ?? new Set<GateCallerReason>();
|
|
350
|
+
reasons.add(reason);
|
|
351
|
+
callers.set(test, reasons);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const candidates = [...callers].sort(([a], [b]) => a.localeCompare(b, undefined, { numeric: true }))
|
|
356
|
+
.map(([file, reasons]) => ({ file, reasons: [...reasons] }));
|
|
357
|
+
const callerTests = candidates.slice(0, GATE_CALLER_TEST_LIMIT);
|
|
358
|
+
const callerTestsOverflow = candidates.slice(GATE_CALLER_TEST_LIMIT);
|
|
359
|
+
const runWithCallers = (tests: readonly string[]) => [...new Set([...tests, ...callerTests.map(({ file }) => file)])];
|
|
360
|
+
const base = { sourceFiles, documentPaths, ignoredOutsideSrc, missingTestFiles, elanousRuntimeArtifacts, callerTests, callerTestsOverflow,
|
|
332
361
|
unverified: [] as readonly string[], derived: [] as readonly string[], pulledInRelatedTests: [] as readonly string[] };
|
|
333
362
|
const withDocumentNonContribution = (runSet: readonly string[]) => ({
|
|
334
363
|
documentsWithoutDerivedTests: documentsWithoutDerivedTestsFor(runSet),
|
|
335
364
|
});
|
|
336
365
|
|
|
337
366
|
if (testFiles.length > 0) {
|
|
338
|
-
const allRelatedTests =
|
|
339
|
-
const runSet =
|
|
367
|
+
const allRelatedTests = conventional;
|
|
368
|
+
const runSet = runWithCallers([...testFiles, ...allRelatedTests]);
|
|
340
369
|
// 편집된 테스트만 돌렸다면 빠졌을 것들 — 이 목록이 비어있지 않다는 것은 합집합이 실제로 구제했다는 뜻이다.
|
|
341
370
|
const pulledInRelatedTests = allRelatedTests.filter((f) => !testFiles.includes(f));
|
|
342
371
|
return { ...base, ...withDocumentNonContribution(runSet), testArgs: runSet, unverified: unverifiedFor(runSet), pulledInRelatedTests, importerTestsNotRun: observationFor(runSet), skipTestStep: false, reason: 'changed-tests' };
|
|
@@ -348,9 +377,10 @@ export function resolveGateScope(
|
|
|
348
377
|
if (nonDoc.length === 0) {
|
|
349
378
|
return { ...base, ...withDocumentNonContribution([]), importerTestsNotRun: observationFor([]), skipTestStep: true, reason: 'docs-only' };
|
|
350
379
|
}
|
|
351
|
-
const derived =
|
|
352
|
-
|
|
353
|
-
|
|
380
|
+
const derived = conventional;
|
|
381
|
+
const runSet = runWithCallers(derived);
|
|
382
|
+
if (runSet.length > 0) {
|
|
383
|
+
return { ...base, ...withDocumentNonContribution(runSet), derived, testArgs: runSet, unverified: unverifiedFor(runSet), importerTestsNotRun: observationFor(runSet), skipTestStep: false, reason: 'derived' };
|
|
354
384
|
}
|
|
355
385
|
// 실행 집합이 비었으므로 비문서 전부가 미검증이다.
|
|
356
386
|
return { ...base, ...withDocumentNonContribution([]), unverified: unverifiedFor([]), importerTestsNotRun: observationFor([]), skipTestStep: true, reason: 'no-related-tests' };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { applyGraphOverlays, type GraphOverlaySpec, type OverlayPatchOp, type OverlayRejection } from './graph-overlay-yaml.js';
|
|
3
|
+
import { edgeMapOf, graphVersionHash, type GraphEdgeSpec, type GraphNodeSpec, type GraphTemplateSpec } from './graph-yaml.js';
|
|
4
|
+
import { inspectPipelineGraph } from './pipeline-shape.js';
|
|
5
|
+
import { hasNodeKind } from '../graph-kinds/registry.js';
|
|
6
|
+
|
|
7
|
+
/** A proposed change to one existing outcome of a conditional route. Unconditional and fallback edges are protected. */
|
|
8
|
+
export interface GraphVariantRoute {
|
|
9
|
+
readonly from: string;
|
|
10
|
+
readonly outcome: string;
|
|
11
|
+
readonly to: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface GraphVariantGrowth {
|
|
15
|
+
readonly node: GraphNodeSpec;
|
|
16
|
+
readonly from: string;
|
|
17
|
+
readonly outcome: string;
|
|
18
|
+
readonly returnTo?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface GraphVariantPlan {
|
|
22
|
+
readonly maxVisits?: Readonly<Record<string, number>>;
|
|
23
|
+
readonly routes?: readonly GraphVariantRoute[];
|
|
24
|
+
readonly growth?: GraphVariantGrowth;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type GraphVariantRejection =
|
|
28
|
+
| { readonly kind: 'invalid-goal' }
|
|
29
|
+
| { readonly kind: 'unknown-node'; readonly node: string }
|
|
30
|
+
| { readonly kind: 'invalid-budget'; readonly node: string; readonly value: number }
|
|
31
|
+
| { readonly kind: 'protected-edge'; readonly from: string; readonly outcome: string }
|
|
32
|
+
| { readonly kind: 'unknown-outcome'; readonly from: string; readonly outcome: string }
|
|
33
|
+
| { readonly kind: 'duplicate-route'; readonly from: string; readonly outcome: string }
|
|
34
|
+
| { readonly kind: 'ambiguous-outcome'; readonly from: string; readonly outcome: string }
|
|
35
|
+
| { readonly kind: 'unreachable-terminal'; readonly node: string }
|
|
36
|
+
| { readonly kind: 'invalid-graph'; readonly defects: ReturnType<typeof inspectPipelineGraph> }
|
|
37
|
+
| { readonly kind: 'overlay-rejected'; readonly rejections: readonly OverlayRejection[] }
|
|
38
|
+
| { readonly kind: 'invalid-growth'; readonly reason: string };
|
|
39
|
+
|
|
40
|
+
export type GraphVariantResult =
|
|
41
|
+
| { readonly ok: true; readonly overlay: GraphOverlaySpec; readonly template: GraphTemplateSpec; readonly patches: readonly OverlayPatchOp[] }
|
|
42
|
+
| { readonly ok: false; readonly rejections: readonly GraphVariantRejection[] };
|
|
43
|
+
|
|
44
|
+
/** Pure, deterministic variant authoring. Never mutates the supplied template and never writes an overlay. */
|
|
45
|
+
export function createGraphVariant(input: {
|
|
46
|
+
readonly template: GraphTemplateSpec;
|
|
47
|
+
readonly goal: string;
|
|
48
|
+
readonly plan: GraphVariantPlan;
|
|
49
|
+
}): GraphVariantResult {
|
|
50
|
+
const { template, goal, plan } = input;
|
|
51
|
+
const known = new Set(template.nodes.map((node) => node.nodeId));
|
|
52
|
+
const rejections: GraphVariantRejection[] = [];
|
|
53
|
+
if (!/^g_[a-z0-9_-]+$/i.test(goal)) rejections.push({ kind: 'invalid-goal' });
|
|
54
|
+
if (plan.growth) {
|
|
55
|
+
const { node, from, outcome, returnTo } = plan.growth;
|
|
56
|
+
if (!node || typeof node !== 'object') return { ok: false, rejections: [{ kind: 'invalid-growth', reason: 'growth requires one new contracted node' }] };
|
|
57
|
+
const edgeIndex = template.edges.findIndex((edge) => edge.from === from);
|
|
58
|
+
const edge = template.edges[edgeIndex];
|
|
59
|
+
if (!known.has(from) || known.has(node.nodeId) || template.terminalNodes.includes(from) ||
|
|
60
|
+
typeof node.nodeId !== 'string' || !node.nodeId || typeof node.recipe !== 'string' || !node.recipe ||
|
|
61
|
+
typeof node.kind !== 'string' || !hasNodeKind('harness', node.kind) || !Number.isSafeInteger(node.maxVisits) || node.maxVisits < 1 ||
|
|
62
|
+
typeof node.contract?.tools !== 'string' || !node.contract.tools.trim() || !Array.isArray(node.contract.inputs) ||
|
|
63
|
+
!Array.isArray(node.contract.outputs) || typeof outcome !== 'string' || !outcome || !edge || edge.to !== undefined || edge.on !== 'outcome' ||
|
|
64
|
+
!edge.map || Object.hasOwn(edge.map, outcome) ||
|
|
65
|
+
template.edges.filter((candidate) => candidate.from === from).length !== 1 ||
|
|
66
|
+
typeof returnTo !== 'string' || !known.has(returnTo) || plan.routes?.length || Object.keys(plan.maxVisits ?? {}).length) {
|
|
67
|
+
rejections.push({ kind: 'invalid-growth', reason: 'growth requires one new contracted node, one missing conditional outcome and an existing return destination' });
|
|
68
|
+
}
|
|
69
|
+
if (rejections.length) return { ok: false, rejections };
|
|
70
|
+
const addedEdge: GraphEdgeSpec = { from: node.nodeId, to: returnTo as string };
|
|
71
|
+
const patches: OverlayPatchOp[] = [
|
|
72
|
+
{ op: 'add', path: '/nodes/-', value: node },
|
|
73
|
+
{ op: 'add', path: `/edges/${edgeIndex}/map/${outcome.replace(/~/g, '~0').replace(/\//g, '~1')}`, value: node.nodeId },
|
|
74
|
+
{ op: 'add', path: '/edges/-', value: addedEdge },
|
|
75
|
+
];
|
|
76
|
+
const normalized = JSON.stringify({ graph: graphVersionHash(template), goal, patches });
|
|
77
|
+
const overlay: GraphOverlaySpec = { overlayId: `variant-${createHash('sha256').update(normalized).digest('hex').slice(0, 16)}`,
|
|
78
|
+
target: template.graphId, stage: 'runtime', appliesWhen: `goal_key == ${goal}`, patch: patches };
|
|
79
|
+
// Existing YAML runner nodes predate contracts; only the newly proposed node must supply one.
|
|
80
|
+
const validationTemplate: GraphTemplateSpec = { ...template, nodes: template.nodes.map((existing) => ({
|
|
81
|
+
...existing, contract: existing.contract?.tools ? existing.contract : { inputs: [], tools: 'existing', outputs: [] },
|
|
82
|
+
})) };
|
|
83
|
+
const applied = applyGraphOverlays(validationTemplate, [overlay]);
|
|
84
|
+
if (!applied.ok) return { ok: false, rejections: [{ kind: 'overlay-rejected', rejections: applied.rejections }] };
|
|
85
|
+
const projected: GraphTemplateSpec = { ...applied.template, nodes: [...template.nodes, node] };
|
|
86
|
+
const edges = edgeMapOf(projected);
|
|
87
|
+
const reached = new Set<string>();
|
|
88
|
+
const pending = [projected.entryNode];
|
|
89
|
+
while (pending.length) {
|
|
90
|
+
const at = pending.pop()!;
|
|
91
|
+
if (reached.has(at)) continue;
|
|
92
|
+
reached.add(at);
|
|
93
|
+
pending.push(...(edges[at] ?? []));
|
|
94
|
+
}
|
|
95
|
+
const unreachable = projected.terminalNodes.filter((terminal) => !reached.has(terminal) || (edges[terminal]?.length ?? 0) !== 0);
|
|
96
|
+
if (unreachable.length) return { ok: false, rejections: unreachable.map((terminal) => ({ kind: 'unreachable-terminal', node: terminal })) };
|
|
97
|
+
return { ok: true, overlay, template: projected, patches };
|
|
98
|
+
}
|
|
99
|
+
const patches: OverlayPatchOp[] = [];
|
|
100
|
+
|
|
101
|
+
for (const [node, value] of Object.entries(plan.maxVisits ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
102
|
+
if (!known.has(node)) { rejections.push({ kind: 'unknown-node', node }); continue; }
|
|
103
|
+
if (!Number.isSafeInteger(value) || value < 1) { rejections.push({ kind: 'invalid-budget', node, value }); continue; }
|
|
104
|
+
const index = template.nodes.findIndex((candidate) => candidate.nodeId === node);
|
|
105
|
+
if (template.nodes[index]!.maxVisits !== value) patches.push({ op: 'replace', path: `/nodes/${index}/maxVisits`, value });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const seen = new Set<string>();
|
|
109
|
+
for (const route of [...(plan.routes ?? [])].sort((a, b) => {
|
|
110
|
+
const left = JSON.stringify([a.from, a.outcome, a.to]);
|
|
111
|
+
const right = JSON.stringify([b.from, b.outcome, b.to]);
|
|
112
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
113
|
+
})) {
|
|
114
|
+
if (!known.has(route.from)) { rejections.push({ kind: 'unknown-node', node: route.from }); continue; }
|
|
115
|
+
if (!known.has(route.to)) { rejections.push({ kind: 'unknown-node', node: route.to }); continue; }
|
|
116
|
+
const key = JSON.stringify([route.from, route.outcome]);
|
|
117
|
+
if (seen.has(key)) { rejections.push({ kind: 'duplicate-route', from: route.from, outcome: route.outcome }); continue; }
|
|
118
|
+
seen.add(key);
|
|
119
|
+
const matches = template.edges.filter((edge) => edge.from === route.from && edge.map && Object.hasOwn(edge.map, route.outcome));
|
|
120
|
+
// One patch addresses one edge, so an outcome declared on two conditional edges would validate a graph we do not return.
|
|
121
|
+
if (matches.length > 1) { rejections.push({ kind: 'ambiguous-outcome', from: route.from, outcome: route.outcome }); continue; }
|
|
122
|
+
const conditional = matches[0];
|
|
123
|
+
if (!conditional) {
|
|
124
|
+
const protectedEdge = template.edges.some((edge) => edge.from === route.from && (edge.to !== undefined || edge.fallback?.length));
|
|
125
|
+
rejections.push(protectedEdge
|
|
126
|
+
? { kind: 'protected-edge', from: route.from, outcome: route.outcome }
|
|
127
|
+
: { kind: 'unknown-outcome', from: route.from, outcome: route.outcome });
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const index = template.edges.indexOf(conditional);
|
|
131
|
+
if (conditional.map![route.outcome] !== route.to) {
|
|
132
|
+
// JSON Pointer escaping keeps even unusual, already-declared outcome labels addressable.
|
|
133
|
+
const label = route.outcome.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
134
|
+
patches.push({ op: 'replace', path: `/edges/${index}/map/${label}`, value: route.to });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (rejections.length > 0) return { ok: false, rejections };
|
|
138
|
+
|
|
139
|
+
const projected: GraphTemplateSpec = {
|
|
140
|
+
...template,
|
|
141
|
+
nodes: template.nodes.map((node) => ({ ...node, maxVisits: plan.maxVisits?.[node.nodeId] ?? node.maxVisits })),
|
|
142
|
+
edges: template.edges.map((edge) => edge.map === undefined ? edge : {
|
|
143
|
+
...edge,
|
|
144
|
+
map: Object.fromEntries(Object.entries(edge.map).map(([outcome, to]) => [outcome,
|
|
145
|
+
plan.routes?.find((route) => route.from === edge.from && route.outcome === outcome)?.to ?? to])),
|
|
146
|
+
}),
|
|
147
|
+
};
|
|
148
|
+
const edges = edgeMapOf(projected);
|
|
149
|
+
const defects = inspectPipelineGraph(edges, projected.entryNode);
|
|
150
|
+
const reached = new Set<string>();
|
|
151
|
+
const pending = [projected.entryNode];
|
|
152
|
+
while (pending.length) {
|
|
153
|
+
const node = pending.pop()!;
|
|
154
|
+
if (reached.has(node)) continue;
|
|
155
|
+
reached.add(node);
|
|
156
|
+
for (const to of edges[node] ?? []) pending.push(to);
|
|
157
|
+
}
|
|
158
|
+
const unreachable = projected.terminalNodes.filter((node) => !reached.has(node) || (edges[node]?.length ?? 0) !== 0);
|
|
159
|
+
if (unreachable.length > 0) return { ok: false, rejections: unreachable.map((node) => ({ kind: 'unreachable-terminal', node })) };
|
|
160
|
+
if (defects.length > 0) return { ok: false, rejections: [{ kind: 'invalid-graph', defects }] };
|
|
161
|
+
const normalized = JSON.stringify({ graph: graphVersionHash(template), goal, patches });
|
|
162
|
+
const overlayId = `variant-${createHash('sha256').update(normalized).digest('hex').slice(0, 16)}`;
|
|
163
|
+
const overlay: GraphOverlaySpec = { overlayId, target: template.graphId, stage: 'launch', appliesWhen: `goal_key == ${goal}`, patch: patches };
|
|
164
|
+
// Existing runner YAML nodes may predate contracts; the plan only changes routes and visits.
|
|
165
|
+
const validationTemplate: GraphTemplateSpec = { ...template, nodes: template.nodes.map((node) => ({
|
|
166
|
+
...node, contract: node.contract?.tools ? node.contract : { inputs: [], tools: 'existing', outputs: [] },
|
|
167
|
+
})) };
|
|
168
|
+
const applied = applyGraphOverlays(validationTemplate, [overlay]);
|
|
169
|
+
if (!applied.ok) return { ok: false, rejections: [{ kind: 'overlay-rejected', rejections: applied.rejections }] };
|
|
170
|
+
return { ok: true, overlay, template: projected, patches };
|
|
171
|
+
}
|
|
@@ -28,7 +28,7 @@ export function runHarnessPolicyGates(input: {
|
|
|
28
28
|
['mock-module-restore-gate', input.gates?.['mock-module-restore-gate'] ?? runMockModuleRestoreGate],
|
|
29
29
|
['model-hardcode-gate', input.gates?.['model-hardcode-gate'] ?? runModelHardcodeGate],
|
|
30
30
|
['daemon-port-gate', input.gates?.['daemon-port-gate'] ?? ((out) => runDaemonPortGate({ log: out.log, error: out.error, cwd: out.cwd, args: [] }))],
|
|
31
|
-
// LEAK1 — the
|
|
31
|
+
// LEAK1 — the same pre-export check as pr land; an unmeasured result is a nonzero failure, not a pass.
|
|
32
32
|
['public-export-leak', input.gates?.['public-export-leak'] ?? runPublicExportLeakGate],
|
|
33
33
|
];
|
|
34
34
|
const failures: Array<{ gate: GateName; lines: string[] }> = [];
|
|
@@ -58,11 +58,13 @@ import { childRunContextEnv, childProviderKeyEnv, childLlmSelectionEnv, type Chi
|
|
|
58
58
|
import { childToolProfile } from '../agent/tool-profile.js';
|
|
59
59
|
import { escalationAllowedForProvider } from './rework-policy.js';
|
|
60
60
|
import { getUserConfig, resolveActiveProvider, resolveRoleLlm } from '../user-config.js';
|
|
61
|
+
import { goalDifficultySignals, classifyGoalDifficulty, resolveImplementDifficulty } from '../llm/difficulty.js';
|
|
61
62
|
import { childNestEnv } from '../agent/nest-depth.js';
|
|
62
63
|
import { childPtyIdentityEnv } from '../agent/pty-identity.js';
|
|
63
64
|
import { elanousTuiSpawnOptions } from './elanous-tui-spawn.js';
|
|
64
65
|
import { describeGrokCredentialFreshness, readGrokTokenFreshness, type GrokCredentialFreshnessSnapshot } from '../acp/grok-auth.js';
|
|
65
66
|
import { debug } from '../debug/log.js';
|
|
67
|
+
import { observeExternalEvent } from '../context-bus/external-events.js';
|
|
66
68
|
import { sendOutbound } from '../domains/outbound-alert.js';
|
|
67
69
|
import { formatPtyLinkMessage, readReportOrigin } from './report-origin.js';
|
|
68
70
|
import { DETACHED_PROGRESS_FRAME_PREFIX, decodeDetachedProgressFrame } from '../harness/dispatch-detached.js';
|
|
@@ -920,6 +922,8 @@ export interface HeadlessGoalLoopPtyOptions {
|
|
|
920
922
|
cwd: string;
|
|
921
923
|
/** 이미 featurePrompt() 적용된 프롬프트(범위제한·클린빌드 앵커 포함). */
|
|
922
924
|
featurePrompt: string;
|
|
925
|
+
/** Original goal document before the execution prompt is decorated. */
|
|
926
|
+
goalDocument?: string;
|
|
923
927
|
configDir?: string;
|
|
924
928
|
/** ★ K run-identity — 이 실행이 속한 per-run join anchor. 미지정 시 상속(env) → 없으면 canonical mint
|
|
925
929
|
* (`perCallRunId`). 호출자(runSelfImplement)가 지정하면 **리워크 라운드가 하나의 runId 를 공유**해
|
|
@@ -1164,14 +1168,25 @@ export interface HeadlessGoalLoopPtyResult {
|
|
|
1164
1168
|
ptyId: string;
|
|
1165
1169
|
}
|
|
1166
1170
|
|
|
1171
|
+
export function pickImplementRoleForGoal(document: string): ReturnType<typeof resolveRoleLlm> | undefined {
|
|
1172
|
+
try {
|
|
1173
|
+
const role = resolveRoleLlm('implement');
|
|
1174
|
+
const signals = goalDifficultySignals(document);
|
|
1175
|
+
const level = classifyGoalDifficulty(signals);
|
|
1176
|
+
const picked = resolveImplementDifficulty(role, level);
|
|
1177
|
+
try { debug.log('llm.difficulty', 'pick', { level, tier: picked.tier ?? null, signals }); } catch { /* observation is fail-soft */ }
|
|
1178
|
+
return picked;
|
|
1179
|
+
} catch { return undefined; }
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1167
1182
|
export async function runHeadlessGoalLoopPty(opts: HeadlessGoalLoopPtyOptions): Promise<HeadlessGoalLoopPtyResult> {
|
|
1168
1183
|
const childLlm = opts.childLlm;
|
|
1169
1184
|
if (childLlm && (typeof childLlm.provider !== 'string' || typeof childLlm.model !== 'string' || !childLlm.provider.trim() || !childLlm.model.trim())) {
|
|
1170
1185
|
throw new Error('childLlm.provider and childLlm.model must both be non-empty');
|
|
1171
1186
|
}
|
|
1172
|
-
// ⭐ B14 —
|
|
1187
|
+
// ⭐ B14 — 명시 자식 LLM · 런 단위 모델이 없을 때 골 난이도에 따라 역할 `implement` 기본 등급을 고른다.
|
|
1173
1188
|
const implementRole = !childLlm && !process.env.ELANOUS_LLM_MODEL?.trim()
|
|
1174
|
-
? (
|
|
1189
|
+
? pickImplementRoleForGoal(opts.goalDocument ?? opts.featurePrompt)
|
|
1175
1190
|
: undefined;
|
|
1176
1191
|
// ⭐ B9 — 승급 판정에 쓸 «유효 provider»: 명시 자식 → 역할 → 런 env → config(auto 는 실제 결정으로).
|
|
1177
1192
|
const effectiveChildProvider = childLlm?.provider ?? implementRole?.provider ?? process.env.ELANOUS_LLM_PROVIDER?.trim()
|
|
@@ -1202,6 +1217,7 @@ export async function runHeadlessGoalLoopPty(opts: HeadlessGoalLoopPtyOptions):
|
|
|
1202
1217
|
const controlInboxDir = resolveControlInboxDir(space.id);
|
|
1203
1218
|
const { key: screenKey } = resolveHarnessScreenKey(space.id, opts.cwd);
|
|
1204
1219
|
const executionId = mintPtyId('self');
|
|
1220
|
+
const contextSource = `elanous://harness/${encodeURIComponent(runId)}/${encodeURIComponent(executionId)}`;
|
|
1205
1221
|
const boundaryRequestsEnv = harnessBoundaryRequestsEnv(executionId);
|
|
1206
1222
|
const boundaryResponsesEnv = harnessBoundaryResponsesEnv(executionId);
|
|
1207
1223
|
const boundaryRequestsPath = boundaryRequestsEnv.ELANOUS_HARNESS_BOUNDARY_REQUESTS ?? '';
|
|
@@ -1213,6 +1229,8 @@ export async function runHeadlessGoalLoopPty(opts: HeadlessGoalLoopPtyOptions):
|
|
|
1213
1229
|
const emitDone = (data: Record<string, unknown>): void => {
|
|
1214
1230
|
if (doneEmitted) return;
|
|
1215
1231
|
doneEmitted = true;
|
|
1232
|
+
if (spawned) observeExternalEvent({ origin: 'harness-child', kind: 'finished',
|
|
1233
|
+
summary: `Harness child finished: ${String(data.exitReason ?? 'unknown').slice(0, 60)}`, source: contextSource });
|
|
1216
1234
|
debug.log('self-implement', 'headless.done', {
|
|
1217
1235
|
ptyId: executionId,
|
|
1218
1236
|
runId,
|
|
@@ -1326,7 +1344,7 @@ export async function runHeadlessGoalLoopPty(opts: HeadlessGoalLoopPtyOptions):
|
|
|
1326
1344
|
...(space ? harnessSpaceEnv(space.kind, space.id, runId) : {}), // 자식 goal-loop 자기인지(#4948 프리앰블) + ★K runId 명시 전파(captured-env 경계)
|
|
1327
1345
|
...harnessBoundaryEnv(opts.cwd), // ★ #4 명시 쓰기 경계 — worktree(cwd) 전파 → 자식 부팅 시 결정론 경계 활성(정본 오염 봉쇄)
|
|
1328
1346
|
...(opts.documentReferences?.length ? { ELANOUS_DOCUMENT_REFERENCES: JSON.stringify(opts.documentReferences) } : {}),
|
|
1329
|
-
// ⭐
|
|
1347
|
+
// ⭐ B14/E2 — 명시 자식 LLM·런 단위 모델이 없으면 골 난이도를 적용한 구현 역할 모델을 릴레이한다.
|
|
1330
1348
|
...childLlmSelectionEnv(childLlm ?? (implementRole ? { provider: implementRole.provider, model: implementRole.model, source: 'config' } : undefined)),
|
|
1331
1349
|
// 결정 2026-09-23 — 명시 childLlm 은 «같은 provider» 사다리 안에서만 한 칸 승급한다(없으면 승급 안 함).
|
|
1332
1350
|
// 미지정 경로는 기존 티어(codex sol 등)를 그대로 쓴다.
|
|
@@ -1351,6 +1369,7 @@ export async function runHeadlessGoalLoopPty(opts: HeadlessGoalLoopPtyOptions):
|
|
|
1351
1369
|
},
|
|
1352
1370
|
});
|
|
1353
1371
|
spawned = true;
|
|
1372
|
+
observeExternalEvent({ origin: 'harness-child', kind: 'started', summary: 'Harness child started', source: contextSource });
|
|
1354
1373
|
phase = 'initialization';
|
|
1355
1374
|
const ptyId = h.id;
|
|
1356
1375
|
// ⭐⭐ M5 — 그 판정을 «런 슈퍼바이저 레코드»로 올린다. 종전엔 경계 판정이 자기 로그에만 남아
|
|
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync,
|
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
5
|
import { debug } from '../debug/log.js';
|
|
6
|
+
import { releaseGitDiffPaths, releasePathHold, releasePathHoldShouldPost, releasePathHoldComment, releasePathHoldCommentsArgs, RELEASE_PATH_LABEL } from '../self-dev/release-path-guard.js';
|
|
6
7
|
import { detectTestInterference, parseFailureCount, runBunTest } from '../../scripts/detect-test-interference.js';
|
|
7
8
|
import { runTestInterferenceGate } from '../../scripts/ci-test-interference-gate.js';
|
|
8
9
|
|
|
@@ -158,6 +159,26 @@ export async function runHostRegate(input: HostRegateInput, deps: HostRegateDeps
|
|
|
158
159
|
base = run('git', ['merge-base', baseCommit, input.headCommit], worktree);
|
|
159
160
|
files = run('git', ['diff', '--name-only', base, input.headCommit], worktree).split('\n').filter(Boolean);
|
|
160
161
|
if (!files.length) throw new Error('changed files unavailable');
|
|
162
|
+
if (!input.verifyOnly) {
|
|
163
|
+
// --name-only lists only the destination of a rename. Inspect both sides without
|
|
164
|
+
// changing the gate's existing changed-file list or its test selection.
|
|
165
|
+
const guardPaths = releaseGitDiffPaths(run('git', ['diff', '--find-renames', '--name-status', '-z', base, input.headCommit], worktree));
|
|
166
|
+
const heldPath = releasePathHold(guardPaths);
|
|
167
|
+
if (heldPath) {
|
|
168
|
+
debug.log('self-dev.merge', 'release-path-hold', { number: input.prNumber, path: heldPath, label: RELEASE_PATH_LABEL, surface: 'host-regate' });
|
|
169
|
+
try {
|
|
170
|
+
const existing: unknown = JSON.parse(run('gh', ['label', 'list', '--search', RELEASE_PATH_LABEL, '--json', 'name'], input.repoRoot));
|
|
171
|
+
if (!Array.isArray(existing)) throw new Error('invalid label list');
|
|
172
|
+
if (!existing.some((item: unknown) => !!item && typeof item === 'object' && 'name' in item && item.name === RELEASE_PATH_LABEL)) {
|
|
173
|
+
run('gh', ['label', 'create', RELEASE_PATH_LABEL, '--color', 'D93F0B', '--description', 'Release path requires OP approval'], input.repoRoot);
|
|
174
|
+
}
|
|
175
|
+
run('gh', ['pr', 'edit', String(input.prNumber), '--add-label', RELEASE_PATH_LABEL], input.repoRoot);
|
|
176
|
+
} catch (error) { debug.log('self-dev.merge', 'release-path-annotation-failed', { number: input.prNumber, action: 'label', error: String(error) }); }
|
|
177
|
+
try { if (releasePathHoldShouldPost(() => JSON.parse(run('gh', releasePathHoldCommentsArgs(input.prNumber), input.repoRoot)))) run('gh', ['pr', 'comment', String(input.prNumber), '--body', releasePathHoldComment(heldPath)], input.repoRoot); }
|
|
178
|
+
catch (error) { debug.log('self-dev.merge', 'release-path-annotation-failed', { number: input.prNumber, action: 'comment', error: String(error) }); }
|
|
179
|
+
return result('failed', 'release-path-hold', `OP approval required: ${heldPath}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
161
182
|
// Materialize the proposed merge of this exact PR head into the observed base.
|
|
162
183
|
// A clean head checkout cannot expose integration breaks introduced by the base.
|
|
163
184
|
try { run('git', ['merge', '--no-ff', '--no-commit', input.headCommit], worktree); }
|
|
@@ -234,7 +255,7 @@ export async function runHostRegate(input: HostRegateInput, deps: HostRegateDeps
|
|
|
234
255
|
catch (e) { log('unmeasured', { pr: input.prNumber, files, os: process.platform, failedStep: 'cleanup', detail: String(e) }); }
|
|
235
256
|
try { release?.(); }
|
|
236
257
|
catch (e) { log('unmeasured', { pr: input.prNumber, files, os: process.platform, failedStep: 'lock', detail: String(e) }); }
|
|
237
|
-
if (failures.length) {
|
|
258
|
+
if (failures.length && failures[0]?.step !== 'release-path-hold') {
|
|
238
259
|
const { step, detail } = failures[0]!;
|
|
239
260
|
const body = `호스트 재게이트 실패(${process.platform}): ${step} — ${detail.replace(/\s+/g, ' ').slice(0, 180)}`;
|
|
240
261
|
try { run('gh', ['pr', 'comment', String(input.prNumber), '--body', body], input.repoRoot); }
|
|
@@ -6,6 +6,7 @@ const IMPORTER_TEST_DISPLAY_LIMIT = 20;
|
|
|
6
6
|
|
|
7
7
|
export interface ImporterTestIndex {
|
|
8
8
|
readonly testsBySource: ReadonlyMap<string, readonly string[]>;
|
|
9
|
+
readonly testPaths?: readonly string[];
|
|
9
10
|
readonly unresolvedRelativeSpecifiers: number;
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -105,7 +106,7 @@ export function buildImporterTestIndex(cwd: string, testPaths: readonly string[]
|
|
|
105
106
|
testsBySource.set(source, importers);
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
|
-
return { testsBySource, unresolvedRelativeSpecifiers };
|
|
109
|
+
return { testsBySource, testPaths, unresolvedRelativeSpecifiers };
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
export function importerTestsNotInRunSet(
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { NEXT_MD_PATH, resolveNextMdConflict } from '../release-loop/next-md-merge.js';
|
|
6
|
+
|
|
7
|
+
type Command = (bin: string, args: readonly string[], cwd: string) => { status: number | null; stdout: string; stderr: string };
|
|
8
|
+
export type L8MergeQueueInput = { number: number; cwd: string; matchHeadCommit: string };
|
|
9
|
+
export type L8MergeQueueResult = { merged: boolean; baseRefName?: string; mergeCommit?: string; detail?: string };
|
|
10
|
+
export type L8MergeQueueDeps = {
|
|
11
|
+
command?: Command;
|
|
12
|
+
/** Must identify the same shared repository across all harness processes. */
|
|
13
|
+
acquire?: (cwd: string) => Promise<() => void>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const command: Command = (bin, args, cwd) => {
|
|
17
|
+
const r = spawnSync(bin, [...args], { cwd, encoding: 'utf8', timeout: 600_000, maxBuffer: 32 * 1024 * 1024 });
|
|
18
|
+
return { status: r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + (r.error?.message ?? '') };
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Serialize the whole read/integrate/test/merge transaction, not just the final push. */
|
|
22
|
+
export async function acquireL8MergeQueue(cwd: string): Promise<() => void> {
|
|
23
|
+
const git = command('git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], cwd);
|
|
24
|
+
if (git.status !== 0 || !git.stdout.trim()) throw new Error('merge queue common git directory unavailable');
|
|
25
|
+
const slot = join(git.stdout.trim(), 'elanous-l8-merge-queue');
|
|
26
|
+
const deadline = Date.now() + 30 * 60_000;
|
|
27
|
+
for (;;) {
|
|
28
|
+
try {
|
|
29
|
+
mkdirSync(slot);
|
|
30
|
+
try { writeFileSync(join(slot, 'pid'), String(process.pid)); }
|
|
31
|
+
catch (error) { rmSync(slot, { recursive: true, force: true }); throw new Error(`merge queue pid write failed: ${String(error)}`); }
|
|
32
|
+
return () => rmSync(slot, { recursive: true, force: true });
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
35
|
+
// Fail closed on abandoned slots: never steal a slot while a live merge can own it.
|
|
36
|
+
if (Date.now() >= deadline) throw new Error('merge queue occupied for 30 minutes');
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One PR per shared git directory, integrated on a freshly fetched main in a disposable worktree. */
|
|
43
|
+
export async function runL8MergeQueue(input: L8MergeQueueInput, deps: L8MergeQueueDeps = {}): Promise<L8MergeQueueResult> {
|
|
44
|
+
if (!Number.isSafeInteger(input.number) || input.number <= 0 || !/^[0-9a-f]{40}$/i.test(input.matchHeadCommit)) {
|
|
45
|
+
return { merged: false, detail: 'invalid PR number or pinned head' };
|
|
46
|
+
}
|
|
47
|
+
const execute = deps.command ?? command;
|
|
48
|
+
const run = (bin: string, args: readonly string[], cwd: string): string => {
|
|
49
|
+
const result = execute(bin, args, cwd);
|
|
50
|
+
if (result.status !== 0) throw new Error(`${bin} ${args.join(' ')}: ${(result.stderr || result.stdout || `exit ${result.status}`).trim().slice(0, 500)}`);
|
|
51
|
+
return args.includes('-z') ? result.stdout : result.stdout.trim();
|
|
52
|
+
};
|
|
53
|
+
let release: (() => void) | undefined;
|
|
54
|
+
let temp: string | undefined;
|
|
55
|
+
let attached = false;
|
|
56
|
+
try {
|
|
57
|
+
release = await (deps.acquire ?? acquireL8MergeQueue)(input.cwd);
|
|
58
|
+
type View = { headRefOid?: string; headRefName?: string; baseRefName?: string; state?: string; isDraft?: boolean; isCrossRepository?: boolean; mergeCommit?: { oid?: string } | null };
|
|
59
|
+
const view = (): View => JSON.parse(run('gh', ['pr', 'view', String(input.number), '--json', 'headRefOid,headRefName,baseRefName,state,isDraft,isCrossRepository,mergeCommit'], input.cwd));
|
|
60
|
+
const first = view();
|
|
61
|
+
if (first.headRefOid !== input.matchHeadCommit || first.baseRefName !== 'main' || first.state !== 'OPEN' || first.isDraft !== false || first.isCrossRepository !== false || !first.headRefName || !/^[\w./-]+$/.test(first.headRefName) || first.headRefName.startsWith('-') || first.headRefName === 'main') {
|
|
62
|
+
throw new Error('PR head, base, or ready/open state changed');
|
|
63
|
+
}
|
|
64
|
+
run('git', ['fetch', 'origin', 'refs/heads/main'], input.cwd);
|
|
65
|
+
const base = run('git', ['rev-parse', 'FETCH_HEAD'], input.cwd);
|
|
66
|
+
run('git', ['fetch', 'origin', `refs/pull/${input.number}/head`], input.cwd);
|
|
67
|
+
if (run('git', ['rev-parse', 'FETCH_HEAD'], input.cwd) !== input.matchHeadCommit) throw new Error('fetched PR head changed');
|
|
68
|
+
temp = mkdtempSync(join(tmpdir(), 'elanous-l8-'));
|
|
69
|
+
run('git', ['worktree', 'add', '--detach', temp, input.matchHeadCommit], input.cwd);
|
|
70
|
+
attached = true;
|
|
71
|
+
const fork = run('git', ['merge-base', base, input.matchHeadCommit], temp);
|
|
72
|
+
const paths = run('git', ['diff', '--name-only', '-z', fork, input.matchHeadCommit], temp).split('\0').filter(Boolean);
|
|
73
|
+
if (!paths.length) throw new Error('PR changed-file paths unavailable');
|
|
74
|
+
if (paths.some((path) => path.startsWith('-') || path.startsWith('/') || path.split('/').includes('..') || path.includes('\\'))) throw new Error('unsafe PR changed-file path');
|
|
75
|
+
// Deleted tests are not runnable; a successful diff with no runnable paths is not a failed lookup.
|
|
76
|
+
const livePaths = run('git', ['diff', '--name-only', '-z', '--diff-filter=ACMRT', fork, input.matchHeadCommit], temp).split('\0').filter(Boolean);
|
|
77
|
+
const isBunTest = (path: string) => /(?:^|\/)[^/]+\.(?:test|spec)\.(?:[cm]?[jt]sx?)$/.test(path);
|
|
78
|
+
const tests = livePaths.filter(isBunTest);
|
|
79
|
+
const merge = execute('git', ['-c', 'user.name=elanous merge queue', '-c', 'user.email=queue@localhost', 'merge', '--no-ff', '--no-edit', base], temp);
|
|
80
|
+
if (merge.status !== 0) {
|
|
81
|
+
const unresolved = run('git', ['diff', '--name-only', '--diff-filter=U'], temp).split('\n').filter(Boolean);
|
|
82
|
+
if (unresolved.length !== 1 || unresolved[0] !== NEXT_MD_PATH) throw new Error(`merge conflicts: ${unresolved.join(', ') || merge.stderr}`);
|
|
83
|
+
const stage = (n: number): string => {
|
|
84
|
+
const result = execute('git', ['show', `:${n}:${NEXT_MD_PATH}`], temp!);
|
|
85
|
+
if (result.status !== 0) throw new Error(`release/next.md stage ${n} unavailable: ${result.stderr}`);
|
|
86
|
+
return result.stdout;
|
|
87
|
+
};
|
|
88
|
+
// Git stage 2 is the PR and stage 3 is main; the resolver preserves stage 2 and unions main's additions.
|
|
89
|
+
const resolved = resolveNextMdConflict(stage(1), stage(3), stage(2));
|
|
90
|
+
if (resolved === null) throw new Error('release/next.md conflict is not append-only');
|
|
91
|
+
writeFileSync(join(temp, NEXT_MD_PATH), resolved);
|
|
92
|
+
run('git', ['add', '--', NEXT_MD_PATH], temp);
|
|
93
|
+
run('git', ['-c', 'user.name=elanous merge queue', '-c', 'user.email=queue@localhost', 'commit', '-m', 'Integrate latest main for L8 merge queue'], temp);
|
|
94
|
+
}
|
|
95
|
+
if (tests.length) {
|
|
96
|
+
const unavailable = tests.filter((path) => !existsSync(join(temp!, path)));
|
|
97
|
+
if (unavailable.length) throw new Error(`changed test paths unavailable: ${unavailable.join(', ')}`);
|
|
98
|
+
// The disposable checkout must resolve the integrated candidate's lockfile, never the caller's modules.
|
|
99
|
+
run('bun', ['install', '--frozen-lockfile'], temp);
|
|
100
|
+
const result = execute('bun', ['test', ...tests], temp);
|
|
101
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
102
|
+
if (result.status !== 0 || !/\b[1-9]\d* pass\b/.test(output) || !/\b0 fail\b/.test(output) || !/Ran [1-9]\d* tests? across [1-9]\d* files?/.test(output)) throw new Error(`changed tests failed or unmeasured: ${output.slice(-400)}`);
|
|
103
|
+
}
|
|
104
|
+
run('git', ['fetch', 'origin', 'refs/heads/main'], input.cwd);
|
|
105
|
+
if (run('git', ['rev-parse', 'FETCH_HEAD'], input.cwd) !== base) throw new Error('main advanced during queue verification; retry on latest main');
|
|
106
|
+
const current = view();
|
|
107
|
+
if (current.headRefOid !== first.headRefOid || current.headRefName !== first.headRefName || current.baseRefName !== 'main' || current.state !== 'OPEN' || current.isDraft !== false || current.isCrossRepository !== false) throw new Error('PR changed during queue verification');
|
|
108
|
+
const integrated = run('git', ['rev-parse', 'HEAD'], temp);
|
|
109
|
+
const remoteHead = run('git', ['ls-remote', 'origin', `refs/heads/${first.headRefName}`], temp).split(/\s+/)[0];
|
|
110
|
+
if (remoteHead !== input.matchHeadCommit) throw new Error('PR branch advanced before queue push');
|
|
111
|
+
run('git', ['fetch', 'origin', 'refs/heads/main'], input.cwd);
|
|
112
|
+
if (run('git', ['rev-parse', 'FETCH_HEAD'], input.cwd) !== base) throw new Error('main advanced before queue merge; retry on latest main');
|
|
113
|
+
// One atomic push (review must-fix: head check and merge were not atomic): the PR branch moves to the tested commit only
|
|
114
|
+
// while it still equals the pinned head (lease), and main moves to the same commit only as a fast-forward. The server
|
|
115
|
+
// applies both ref updates or neither, so an advanced PR head or main rejects the whole merge — nothing stale lands.
|
|
116
|
+
// main then contains the PR head, so GitHub records the PR as merged.
|
|
117
|
+
run('git', ['push', '--atomic', `--force-with-lease=refs/heads/${first.headRefName}:${input.matchHeadCommit}`, 'origin',
|
|
118
|
+
`HEAD:refs/heads/${first.headRefName}`, 'HEAD:refs/heads/main'], temp);
|
|
119
|
+
// The push itself is the merge decision; confirm main holds exactly the tested commit (a later push to the PR branch is a new change, not this merge).
|
|
120
|
+
run('git', ['fetch', 'origin', 'refs/heads/main'], input.cwd);
|
|
121
|
+
if (run('git', ['rev-parse', 'FETCH_HEAD'], input.cwd) !== integrated) throw new Error('main does not point to tested merge commit');
|
|
122
|
+
return { merged: true, baseRefName: 'main', mergeCommit: integrated };
|
|
123
|
+
} catch (error) {
|
|
124
|
+
return { merged: false, detail: String(error) };
|
|
125
|
+
} finally {
|
|
126
|
+
if (attached && temp) execute('git', ['worktree', 'remove', '--force', temp], input.cwd);
|
|
127
|
+
if (temp) rmSync(temp, { recursive: true, force: true });
|
|
128
|
+
release?.();
|
|
129
|
+
}
|
|
130
|
+
}
|