elanous 0.2.9 → 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/5845-e6c003ce8d5fd36a.js +1 -0
- 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-f972fc1c00a62e88.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-adfbd2ba9598e59a.js → page-35414e0a007b4082.js} +1 -1
- 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 -0
- package/apps/pwa/out/ops/seats/index.txt +30 -0
- 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 -0
- package/apps/pwa/out/today/index.txt +33 -0
- 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/field/field-feed.yaml +2 -0
- package/graphs/implement-loop.yaml +1 -1
- 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/card-followup/graphs/run-step.ts +48 -13
- package/plugins/doc-draft/graphs/doc-draft.yaml +3 -3
- package/plugins/doc-draft/graphs/run-step.ts +13 -6
- 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/ci-pwa-build-gate.ts +160 -0
- 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 +54 -8
- package/scripts/coord-digest.ts +199 -0
- package/scripts/coord-post.sh +60 -3
- 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/install.ps1 +68 -9
- package/scripts/install.sh +39 -12
- package/scripts/lib/pwa-scenarios/scenarios.ts +20 -0
- package/scripts/lib/tui-regress-checks.ts +146 -0
- package/scripts/marketing/measure-site-numbers.ts +169 -0
- package/scripts/measure-gate-shards.ts +309 -0
- package/scripts/memory-lifecycle-cycle.ts +2 -2
- package/scripts/mission-request-judge.ts +31 -1
- package/scripts/morning-report.ts +2 -2
- package/scripts/opportunity-scan.ts +1 -1
- package/scripts/ops-now-probe.ts +327 -0
- package/scripts/publish-internal-dist.ts +11 -1
- package/scripts/release-loop/auto-approve-node.ts +21 -4
- package/scripts/release-loop/cut-branch.ts +84 -0
- package/scripts/release-loop/cutoff.ts +19 -8
- 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/ops-upgrade-node.ts +72 -16
- package/scripts/release-loop/release-readiness.ts +9 -0
- 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 +112 -13
- package/scripts/repo-watch-cycle.ts +1 -1
- package/scripts/retro-cycle.ts +2 -2
- package/scripts/schedule-health-report.ts +1 -1
- package/scripts/signal-digest-cycle.ts +2 -7
- package/scripts/signal-digest-photo.ts +11 -0
- package/scripts/storyboard/storyboard.ts +5 -161
- package/scripts/tech1-public-measure.ts +94 -0
- package/scripts/test-diet/lib.ts +30 -1
- package/scripts/test-diet/node.ts +83 -8
- package/scripts/test-timeout-audit.ts +114 -0
- package/scripts/tree-sync-alert.ts +1 -1
- package/scripts/tui-regress.ts +71 -0
- package/scripts/ux/demo-rehearsal.ts +243 -0
- package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
- package/scripts/waitlist-daily-digest.ts +1 -1
- 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/explainer-video/SKILL.md +4 -0
- package/skills/explainer-video/engine/reel.mjs +18 -13
- package/skills/explainer-video/engine/reel.sh +24 -5
- package/skills/explainer-video/engine/stitch.mjs +145 -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 +36 -0
- package/skills/pitch-deck/template.html +80 -0
- 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/codex-approval-adapter.ts +9 -3
- package/src/acp/core-turn-bridge.ts +117 -18
- package/src/acp/fact-question.ts +11 -0
- package/src/acp/server.ts +53 -9
- package/src/acp/tool-approval.ts +126 -0
- package/src/acp/turn-runner.ts +36 -0
- package/src/agent/monad-agent-turn.ts +8 -0
- 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/ask-user-question/pending-questions.ts +25 -10
- package/src/ask-user-question/types.ts +9 -0
- package/src/auth/setup-link-tokens.ts +16 -5
- package/src/autopilot/build/integrity-gate.ts +12 -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 +8 -0
- package/src/boot/daemon-runtime.ts +9 -3
- package/src/boot/daemon-tools/index.ts +8 -0
- package/src/boot/daemon-tools/types.ts +2 -0
- package/src/boot/daemon-tools/web-search.ts +4 -0
- package/src/card-followup/core.ts +109 -0
- package/src/chat/ctrl-c-in-input.ts +14 -0
- package/src/chat/index.ts +53 -0
- package/src/chat/pickers/state.ts +6 -2
- package/src/cli/agent-cli.ts +7 -0
- package/src/cli/decisions-cli.ts +25 -3
- package/src/cli/doctor-bundle-upload.ts +68 -0
- package/src/cli/doctor-cli.ts +38 -4
- 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/ops-cli.ts +20 -0
- package/src/cli/persona-cli.ts +90 -4
- package/src/cli/plugin-cli.ts +31 -22
- package/src/cli/pod-cli.ts +61 -2
- package/src/cli/pr-cli.ts +82 -17
- package/src/cli/pty-drive-cli.ts +41 -7
- package/src/cli/pty-drive-verdict.ts +40 -0
- package/src/cli/pwa-stop.ts +163 -13
- package/src/cli/pwa-test.ts +3 -0
- package/src/cli/release-cli.ts +80 -24
- package/src/cli/self-update.ts +14 -0
- package/src/cli/setup-cli.ts +43 -1
- package/src/cli/skills-cli.ts +17 -1
- package/src/cli/storyboard-cli.ts +37 -0
- package/src/cli/test-state-dir-flag.ts +11 -4
- package/src/connectors/linear.ts +146 -0
- 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 +115 -0
- package/src/context-bus/external-events.ts +52 -0
- package/src/context-bus/long-term-memory.ts +108 -0
- package/src/dashboard/dashboard-boot-fixture.ts +23 -0
- package/src/dashboard/index.ts +63 -21
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +14 -1
- package/src/dashboard/slash-runtime/help-from-registry.ts +43 -28
- package/src/decisions/decision-cards.ts +58 -3
- package/src/decisions/decision-ledger.ts +85 -6
- package/src/decisions/decision-linear-projection.ts +157 -0
- package/src/decisions/web-push-decision-cards.ts +50 -0
- package/src/directive/interpretation-card.ts +36 -0
- 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 +19 -2
- package/src/domains/coo-admin-tool.ts +62 -0
- package/src/domains/core-tools.ts +13 -4
- package/src/domains/elanous-card.ts +29 -0
- package/src/domains/ops-facts-tool.ts +76 -0
- package/src/domains/outbound-alert.ts +46 -13
- package/src/domains/release-tool.ts +168 -0
- package/src/domains/telegram-kind-route.ts +65 -9
- package/src/exec-requests/default-outputs.ts +25 -0
- package/src/exec-requests/planner.ts +106 -28
- package/src/exec-requests/runner.ts +123 -12
- package/src/exec-requests/store.ts +11 -2
- package/src/execution-loop/launch-gate.ts +100 -0
- package/src/field/field-media.ts +10 -7
- package/src/field/field-reel.ts +19 -5
- package/src/field-note/index.ts +201 -0
- package/src/field-note/transcribe.ts +248 -0
- package/src/git-fs/worktree.ts +45 -6
- package/src/graph-kinds/registry.ts +3 -3
- package/src/graph-runner/graph-approval-card.ts +113 -0
- package/src/graph-runner/graph-cli.ts +189 -4
- package/src/graph-runner/graph-grow-llm.ts +37 -0
- package/src/graph-runner/graph-grow.ts +47 -0
- package/src/graph-runner/graph-notify.ts +25 -3
- package/src/graph-runner/runner.ts +453 -35
- package/src/grok/agent-search.ts +8 -2
- package/src/guardian/secret-exposure-scan.ts +101 -0
- package/src/harness/browser-verify.ts +25 -2
- package/src/harness/harness-cli-command.ts +478 -42
- package/src/harness/harness-incidents-writer.fixture.ts +24 -0
- package/src/harness/harness-incidents.ts +129 -0
- package/src/harness/harness-pod-dispatch.ts +43 -23
- package/src/harness/harness-seams.ts +38 -4
- package/src/harness/harness-stop.ts +75 -6
- package/src/harness/launch-registry.ts +91 -0
- package/src/harness/process-garbage.ts +5 -3
- package/src/hitl/clarification-policy.ts +3 -1
- 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 +206 -49
- package/src/intake-plane/author-goals.ts +37 -4
- package/src/intake-plane/categorize.ts +18 -46
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/discord-seat-work.ts +57 -2
- 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/seat-doc-route.ts +170 -6
- package/src/intake-plane/telegram-seat-work.ts +80 -4
- package/src/intake-plane/telegram-work.ts +1 -0
- 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/cli-maturity.ts +34 -0
- package/src/maturity/feature-maturity.ts +120 -1
- package/src/maturity/tui-slash-maturity.ts +31 -0
- package/src/memory.ts +32 -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/exec-requests.ts +33 -3
- 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 +89 -1
- package/src/nexus/api/inside-events.ts +226 -0
- package/src/nexus/api/meta-api.ts +32 -8
- package/src/nexus/api/missions.ts +6 -1
- package/src/nexus/api/ops-api.ts +10 -1
- package/src/nexus/api/ops-seats-sources.ts +37 -7
- package/src/nexus/api/ops-seats.ts +49 -6
- package/src/nexus/api/outputs-route.ts +44 -0
- package/src/nexus/api/seat-requests.ts +132 -10
- package/src/nexus/api/sessions-store.ts +42 -0
- package/src/nexus/api/setup-codex-login.ts +160 -0
- 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/usage-api.ts +102 -0
- package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
- package/src/nexus/api/workflows.ts +80 -6
- package/src/nexus/index.ts +47 -4
- 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/first-run.ts +89 -0
- package/src/onboarding/lan-address.ts +45 -0
- package/src/onboarding/voice-ai.ts +3 -3
- package/src/onboarding/web-first.ts +119 -24
- package/src/onboarding.ts +4 -0
- package/src/ops-now/ops-now-refresh.ts +100 -0
- 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-store.ts +115 -0
- package/src/project/project-suggestion.ts +16 -0
- package/src/project/session-context.ts +24 -0
- package/src/release-loop/checklist.ts +79 -5
- package/src/release-loop/feature-store.ts +159 -24
- package/src/release-loop/next-md-merge.ts +58 -0
- package/src/release-loop/release-note.ts +59 -4
- package/src/release-loop/release-schedule.ts +53 -0
- package/src/repl/index.ts +7 -0
- package/src/seat-dispatch/ceo-commands.ts +41 -8
- package/src/seat-dispatch/ceo-intent.ts +38 -0
- 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 +750 -0
- package/src/seat-loop/seat-report.ts +99 -0
- package/src/self-dev/decompose.ts +66 -11
- 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/run-store.ts +10 -1
- 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/execution-clarification.ts +115 -0
- package/src/self-implement/gate-baseline.ts +1 -1
- package/src/self-implement/gate-cli.ts +46 -4
- 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 +279 -12
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/route-consumer-test-index.ts +122 -0
- package/src/self-implement/run-status-mapping.ts +5 -3
- 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/chat.ts +2 -0
- package/src/session/index.ts +45 -13
- package/src/shell-env-bootstrap.ts +17 -0
- package/src/skills/gift-install.ts +215 -0
- package/src/skills/tools/web-search.ts +4 -1
- package/src/steward/directive.ts +51 -2
- package/src/steward/launch.ts +266 -0
- package/src/steward/outcome-notify.ts +115 -0
- package/src/steward/steward-cards.ts +25 -11
- package/src/steward/triage-plan.ts +32 -4
- package/src/steward/triage.ts +129 -13
- package/src/steward/working-backwards.ts +64 -0
- package/src/storyboard/storyboard.ts +141 -0
- package/src/task-cards/card-cli.ts +25 -1
- package/src/task-cards/card-store.ts +2 -2
- 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 +397 -0
- 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 +153 -0
- package/src/telegram-commands.ts +10 -1
- package/src/telegram-project-command.ts +71 -0
- package/src/telegram-report.ts +29 -19
- package/src/telegram-run.ts +7 -0
- package/src/telegram.ts +58 -11
- package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
- package/src/tool-runtime/types.ts +4 -0
- package/src/tui-client/dashboard-session.ts +2 -0
- package/src/user-config.ts +216 -27
- package/src/version/packed-revision.json +1 -1
- package/src/web-push/notify-exec-request.ts +32 -0
- 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/bFI5EEwg7T6aG88ibX4Zq/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.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/2929-617f1e66a55be184.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/3441-a1dd4acfeda14488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5080-1613752891fcadb4.js +0 -10
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.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/7032-f4ef698518fba61c.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/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8630-cdfc61e82c653ffc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8894-e263b332208e3957.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-139040d37c79dbc9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.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-fb7c2d458f32fcad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-35e2713597dd24d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-918b656f064577ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-5f30e00693527ebd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-7fbae173b3c103a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-3b4edcce364062ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-c5794f484c251f44.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1130d8eefcd87e76.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-973996d584f008c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-016e2bda04eecb1f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-b4d6d9a3b56f2940.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-ec49a466121233ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-f72db356744ceade.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-a6fd9b50d1b85378.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-8b0402a46a749a3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-037f83e2a1edc8e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-46fedff57d4bec67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-0cf318e9dcee2bbf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-899b327cb8b6845c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6c1e6ba743a29dfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-1a8e4807292f6b17.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-28deba6bcb834916.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-de68e2205794037d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-c71c33a573230672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-0df30e0c7e50148a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4fc56790fe0d2682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-59df2aebb38a7c1e.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-4647be5dbcbd0795.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-e025892bda74b743.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a0955de73b89f078.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-369846d2921af584.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-72cbc1c8cdd0f65d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-77b28bfb0996d5ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-3220c4934f49c6c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-90670589b87abea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-7df9243ed021a4cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-4498c3853f4f688d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-35bedde6d885f08f.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-6870f30ea99f8346.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/f92f63a5df0ba229.css +0 -1
- package/src/dashboard/turn-typeahead-echo.ts +0 -23
- /package/apps/pwa/out/_next/static/{bFI5EEwg7T6aG88ibX4Zq → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { realpathSync, statSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, extname, relative, resolve } from 'node:path';
|
|
3
|
+
import { debug } from '../debug/log.js';
|
|
4
|
+
import { notifyMissionDocument, notifyMissionOrigin } from '../autopilot/mission-notify.js';
|
|
5
|
+
import type { MissionOrigin } from '../autopilot/mission-origin.js';
|
|
6
|
+
import { redactSecrets } from '../task-cards/card-store.js';
|
|
7
|
+
import { loadDirectiveOrigins } from './directive.js';
|
|
8
|
+
import { saveLaunchLedger, type LaunchEntry, type LaunchLedger } from './launch.js';
|
|
9
|
+
|
|
10
|
+
export interface OutcomeNotifyDeps {
|
|
11
|
+
root: string;
|
|
12
|
+
ledger: LaunchLedger;
|
|
13
|
+
shadow?: boolean;
|
|
14
|
+
loadOrigin?: (root: string, issue: string) => MissionOrigin | null;
|
|
15
|
+
loadOrigins?: typeof loadDirectiveOrigins;
|
|
16
|
+
document?: typeof notifyMissionDocument;
|
|
17
|
+
message?: typeof notifyMissionOrigin;
|
|
18
|
+
worktreeCommand?: (args: string[]) => { exitCode: number; stdout: string };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const EXTENSIONS = new Set(['.md', '.pdf', '.png', '.jpg', '.csv', '.txt', '.html', '.json']);
|
|
22
|
+
const MAX_BYTES = 20 * 1024 * 1024;
|
|
23
|
+
|
|
24
|
+
function localArtifact(outcome: LaunchEntry, deps: OutcomeNotifyDeps): string | null {
|
|
25
|
+
const ref = outcome.artifactRef;
|
|
26
|
+
const branch = outcome.artifactBranch;
|
|
27
|
+
if (!ref || !branch || isAbsolute(ref) || !EXTENSIONS.has(extname(ref).toLowerCase())) return null;
|
|
28
|
+
const command = deps.worktreeCommand ?? ((args: string[]) => {
|
|
29
|
+
const result = Bun.spawnSync(args, { cwd: resolve(import.meta.dir, '../..'), stdout: 'pipe', stderr: 'pipe' });
|
|
30
|
+
return { exitCode: result.exitCode, stdout: result.stdout.toString() };
|
|
31
|
+
});
|
|
32
|
+
try {
|
|
33
|
+
const result = command(['git', 'worktree', 'list', '--porcelain']);
|
|
34
|
+
if (result.exitCode !== 0) return null;
|
|
35
|
+
for (const block of result.stdout.split(/\n\s*\n/)) {
|
|
36
|
+
const path = /^worktree (.+)$/m.exec(block)?.[1];
|
|
37
|
+
if (!path || !block.split('\n').includes(`branch refs/heads/${branch}`) ||
|
|
38
|
+
(outcome.artifactWorktree && !path.endsWith(outcome.artifactWorktree))) continue;
|
|
39
|
+
try {
|
|
40
|
+
const base = realpathSync(path);
|
|
41
|
+
const file = realpathSync(resolve(base, ref));
|
|
42
|
+
const inside = relative(base, file);
|
|
43
|
+
if (!inside || inside === '..' || inside.startsWith('../') || isAbsolute(inside)) continue;
|
|
44
|
+
const stat = statSync(file);
|
|
45
|
+
return stat.isFile() && stat.size <= MAX_BYTES ? file : null;
|
|
46
|
+
} catch { /* Worktree or file is gone; try another matching worktree. */ }
|
|
47
|
+
}
|
|
48
|
+
} catch { /* No accessible worktree or artifact: use PR link instead. */ }
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Persist each conversation's delivery independently; only failed recipients retry (up to three times). */
|
|
53
|
+
export function notifyOutcome(outcome: LaunchEntry, deps: OutcomeNotifyDeps): LaunchEntry['notified'] {
|
|
54
|
+
if (outcome.notified === 'shadow') return 'shadow';
|
|
55
|
+
let origins: MissionOrigin[] = [];
|
|
56
|
+
try {
|
|
57
|
+
origins = deps.loadOrigin
|
|
58
|
+
? [deps.loadOrigin(deps.root, outcome.issue)].filter((item): item is MissionOrigin => item !== null)
|
|
59
|
+
: (deps.loadOrigins ?? loadDirectiveOrigins)(deps.root, outcome.issue);
|
|
60
|
+
} catch { /* Damaged or unavailable origin cannot route a result. */ }
|
|
61
|
+
const recipients = new Map<string, MissionOrigin>();
|
|
62
|
+
for (const origin of origins) {
|
|
63
|
+
if (origin.channel !== 'telegram' || !Number.isSafeInteger(origin.chatId)) continue;
|
|
64
|
+
const key = JSON.stringify([origin.chatId, origin.botId ?? null, origin.threadId ?? null]);
|
|
65
|
+
recipients.set(key, origin);
|
|
66
|
+
}
|
|
67
|
+
if (outcome.notified === 'skipped-no-origin' && recipients.size === 0 && !deps.shadow) return outcome.notified;
|
|
68
|
+
if (outcome.notified === 'sent' && !outcome.notifyRecipients) return 'sent';
|
|
69
|
+
const file = outcome.status !== 'failed' && (recipients.size > 0 || deps.shadow) ? localArtifact(outcome, deps) : null;
|
|
70
|
+
const kind = outcome.status === 'failed' ? 'failure' : file ? 'file' : 'link';
|
|
71
|
+
let sent = false;
|
|
72
|
+
if (deps.shadow) {
|
|
73
|
+
outcome.notified = 'shadow';
|
|
74
|
+
outcome.notifyPreview = { hasChatId: recipients.size > 0, kind: file ? 'file' : 'link' };
|
|
75
|
+
} else if (recipients.size === 0) {
|
|
76
|
+
outcome.notified = 'skipped-no-origin';
|
|
77
|
+
} else {
|
|
78
|
+
const title = redactSecrets(outcome.title).replace(/\s+/g, ' ').slice(0, 240);
|
|
79
|
+
// Say what happened in plain words: an open PR is not a merge, and a run that left nothing is not a failure.
|
|
80
|
+
const status = outcome.awaitingMerge ? 'PR 열림(병합 대기)' : outcome.status === 'merged' ? '병합됨'
|
|
81
|
+
: outcome.status === 'completed' ? '완료(병합 없음)' : outcome.status;
|
|
82
|
+
const pr = outcome.prNumber;
|
|
83
|
+
const link = outcome.prUrl && /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/.test(outcome.prUrl) &&
|
|
84
|
+
(pr === undefined || outcome.prUrl.endsWith(`/pull/${pr}`)) ? outcome.prUrl : undefined;
|
|
85
|
+
const noOutput = outcome.status === 'completed' && !outcome.artifactRef && pr === undefined && !outcome.prUrl;
|
|
86
|
+
const undeliverable = outcome.status !== 'failed' && !noOutput && !file && !link;
|
|
87
|
+
const text = outcome.status === 'failed'
|
|
88
|
+
? `${title} · 실패 — ${redactSecrets(outcome.reason ?? '런 실패').split(/\r?\n/)[0]?.slice(0, 240) ?? '런 실패'}`
|
|
89
|
+
: noOutput
|
|
90
|
+
? `${title} · ${status} — 남긴 파일·PR 없음`
|
|
91
|
+
: undeliverable
|
|
92
|
+
? `${title} · ${status} · 산출물 전달 불가 — ${outcome.artifactRef ? '파일을 확인할 수 없습니다' : '검증된 PR 링크가 없습니다'}`
|
|
93
|
+
: `${title} · ${status} · PR ${pr ?? '없음'} · ${link}`;
|
|
94
|
+
const legacyAttempts = !outcome.notifyRecipients && outcome.notified === 'failed' ? outcome.notifyAttempts ?? 0 : 0;
|
|
95
|
+
const progress = outcome.notifyRecipients ??= {};
|
|
96
|
+
for (const [key, origin] of recipients) {
|
|
97
|
+
const previous = progress[key] ?? (legacyAttempts ? { notified: 'failed' as const, attempts: legacyAttempts } : undefined);
|
|
98
|
+
if (previous?.notified === 'sent' || (previous?.attempts ?? 0) >= 3) continue;
|
|
99
|
+
let delivered = false;
|
|
100
|
+
try {
|
|
101
|
+
if (!undeliverable || !previous) {
|
|
102
|
+
delivered = file ? (deps.document ?? notifyMissionDocument)(origin, file, redactSecrets(`${title} · ${status}`))
|
|
103
|
+
: (deps.message ?? notifyMissionOrigin)(origin, redactSecrets(text));
|
|
104
|
+
}
|
|
105
|
+
} catch { /* A failed recipient remains eligible on a later tick. */ }
|
|
106
|
+
progress[key] = { notified: delivered && !undeliverable ? 'sent' : 'failed', attempts: (previous?.attempts ?? 0) + 1 };
|
|
107
|
+
sent ||= delivered && !undeliverable;
|
|
108
|
+
}
|
|
109
|
+
outcome.notifyAttempts = Math.max(0, ...Object.values(progress).map(item => item.attempts));
|
|
110
|
+
outcome.notified = [...recipients.keys()].every(key => progress[key]?.notified === 'sent') ? 'sent' : 'failed';
|
|
111
|
+
}
|
|
112
|
+
saveLaunchLedger(deps.root, deps.ledger);
|
|
113
|
+
debug.log('steward.outcome', 'notify', { cardId: `linear:${outcome.issue}`, issue: outcome.issue, kind, sent });
|
|
114
|
+
return outcome.notified;
|
|
115
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { CardStore, redactSecrets, type AppendSectionInput, type CreateCardInput, type TaskCard } from '../task-cards/card-store.js';
|
|
3
3
|
import type { ScheduledDecision, TriageIssue } from './triage.js';
|
|
4
|
+
import type { HitlEntry } from './launch.js';
|
|
4
5
|
|
|
5
6
|
export interface StewardCardStore {
|
|
6
7
|
createCard(input: CreateCardInput): TaskCard;
|
|
@@ -14,6 +15,22 @@ export interface StewardCardDeps {
|
|
|
14
15
|
now?: () => Date;
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
/** Record the first intake for each polled issue, independently of LLM triage. */
|
|
19
|
+
export function recordIntakeOnCards(store: StewardCardStore, issues: TriageIssue[], now: () => Date = () => new Date()): void {
|
|
20
|
+
for (const issue of issues) {
|
|
21
|
+
const card = store.createCard({ goalId: `linear:${issue.identifier}`, title: redactSecrets(issue.title) });
|
|
22
|
+
const intakeKey = `intake:${issue.identifier}`;
|
|
23
|
+
if (!card.sections.some(section => section.key === intakeKey)) {
|
|
24
|
+
const directiveSource = /^출처: (telegram|pwa|tui|cli)$/m.exec(issue.body)?.[1];
|
|
25
|
+
const directiveTime = /^시각: (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)$/m.exec(issue.body)?.[1];
|
|
26
|
+
store.appendSection(card.id, {
|
|
27
|
+
key: intakeKey, owner: 'steward',
|
|
28
|
+
content: JSON.stringify({ summary: redactSecrets(issue.title), source: directiveSource ?? `linear:${issue.identifier}`, at: directiveTime ?? now().toISOString() }),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
17
34
|
/** Record observe-only intake and the latest scheduled judgment without launching any work. */
|
|
18
35
|
export function recordTriageOnCards(decisions: ScheduledDecision[], issues: TriageIssue[], deps: StewardCardDeps = {}): void {
|
|
19
36
|
const store = deps.store ?? new CardStore(deps.root);
|
|
@@ -22,18 +39,10 @@ export function recordTriageOnCards(decisions: ScheduledDecision[], issues: Tria
|
|
|
22
39
|
for (const decision of decisions) {
|
|
23
40
|
const issue = byIssue.get(decision.issue);
|
|
24
41
|
if (!issue) throw new Error(`Missing issue ${decision.issue}`);
|
|
42
|
+
recordIntakeOnCards(store, [issue], deps.now);
|
|
25
43
|
const card = store.createCard({ goalId: `linear:${issue.identifier}`, title: redactSecrets(issue.title) });
|
|
26
|
-
const intakeKey = `intake:${issue.identifier}`;
|
|
27
|
-
if (!card.sections.some(section => section.key === intakeKey)) {
|
|
28
|
-
const directiveSource = /^출처: (telegram|pwa|tui|cli)$/m.exec(issue.body)?.[1];
|
|
29
|
-
const directiveTime = /^시각: (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)$/m.exec(issue.body)?.[1];
|
|
30
|
-
store.appendSection(card.id, {
|
|
31
|
-
key: intakeKey, owner: 'steward',
|
|
32
|
-
content: JSON.stringify({ summary: redactSecrets(issue.title), source: directiveSource ?? `linear:${issue.identifier}`, at: directiveTime ?? (deps.now ?? (() => new Date()))().toISOString() }),
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
44
|
const { rung, dependsOn, priority, disposition, hitlReason, why, owner } = decision;
|
|
36
|
-
const judgment = { rung, dependsOn, priority, disposition: rung === 'hitl' || hitlReason ? 'hitl' : disposition, hitlReason: hitlReason ?? null, why: redactSecrets(why), ...(owner ? { owner } : {}) };
|
|
45
|
+
const judgment = { rung, dependsOn, priority, disposition: rung === 'hitl' || hitlReason ? 'hitl' : disposition, hitlReason: hitlReason ?? null, why: redactSecrets(why), ...(owner ? { owner } : {}), ...(decision.capability ? { capability: decision.capability } : {}) };
|
|
37
46
|
const key = `triage:${createHash('sha256').update(JSON.stringify(judgment)).digest('hex')}`;
|
|
38
47
|
if (!card.sections.some(section => section.key === key)) {
|
|
39
48
|
store.appendSection(card.id, { key, owner: 'steward', content: JSON.stringify(judgment) });
|
|
@@ -44,7 +53,7 @@ export function recordTriageOnCards(decisions: ScheduledDecision[], issues: Tria
|
|
|
44
53
|
}
|
|
45
54
|
}
|
|
46
55
|
|
|
47
|
-
function recordOnCard(section: 'launch' | 'outcome', issue: Pick<TriageIssue, 'identifier' | 'title'>, value: object, deps: StewardCardDeps): void {
|
|
56
|
+
function recordOnCard(section: 'launch' | 'outcome' | 'hitl', issue: Pick<TriageIssue, 'identifier' | 'title'>, value: object, deps: StewardCardDeps): void {
|
|
48
57
|
const store = deps.store ?? new CardStore(deps.root);
|
|
49
58
|
try {
|
|
50
59
|
const content = redactSecrets(JSON.stringify(value));
|
|
@@ -67,3 +76,8 @@ export function recordLaunchOnCard(issue: Pick<TriageIssue, 'identifier' | 'titl
|
|
|
67
76
|
export function recordOutcomeOnCard(issue: Pick<TriageIssue, 'identifier' | 'title'>, outcome: object, deps: StewardCardDeps = {}): void {
|
|
68
77
|
recordOnCard('outcome', issue, outcome, deps);
|
|
69
78
|
}
|
|
79
|
+
|
|
80
|
+
/** Append a HITL handoff revision (pending → raised/unavailable) to the issue card. */
|
|
81
|
+
export function recordHitlOnCard(issue: Pick<TriageIssue, 'identifier' | 'title'>, entry: HitlEntry, deps: StewardCardDeps = {}): void {
|
|
82
|
+
recordOnCard('hitl', issue, entry, deps);
|
|
83
|
+
}
|
|
@@ -1,8 +1,33 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
1
4
|
import { resolveRoleModel } from '../user-config.js';
|
|
2
5
|
import type { HitlReason, Rung, TriageIssue } from './triage.js';
|
|
3
6
|
|
|
4
7
|
export type StewardAsk = (prompt: string, role: 'classify' | 'planning') => Promise<unknown>;
|
|
5
|
-
export interface ClassifiedIssue { issue: string; rung: Rung; why: string; hitlReason?: HitlReason; duplicateOf?: string }
|
|
8
|
+
export interface ClassifiedIssue { issue: string; rung: Rung; why: string; hitlReason?: HitlReason; duplicateOf?: string; capability?: 'new-capability' | 'existing-capability' }
|
|
9
|
+
export interface CapabilityInventory { graphs: readonly string[]; plugins: readonly string[]; commands: readonly string[] }
|
|
10
|
+
|
|
11
|
+
/** Read installed names, not promises in documentation. The caller can supply a test-instance root. */
|
|
12
|
+
export function installedCapabilities(root: string = resolve(import.meta.dir, '../..')): CapabilityInventory {
|
|
13
|
+
const files = (dir: string): string[] => {
|
|
14
|
+
try { return readdirSync(dir, { withFileTypes: true }).flatMap(entry => entry.isDirectory()
|
|
15
|
+
? files(join(dir, entry.name)) : entry.isFile() && /\.ya?ml$/.test(entry.name) ? [join(dir, entry.name)] : []); }
|
|
16
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []; throw error; }
|
|
17
|
+
};
|
|
18
|
+
const graphs = files(join(root, 'graphs')).flatMap(file => {
|
|
19
|
+
const match = /^graph_id:\s*['"]?([^\s'"#]+)/m.exec(readFileSync(file, 'utf8'));
|
|
20
|
+
return match ? [match[1]!] : [];
|
|
21
|
+
});
|
|
22
|
+
let plugins: string[];
|
|
23
|
+
try { plugins = readdirSync(join(root, 'plugins'), { withFileTypes: true }).filter(entry => entry.isDirectory() && entry.name !== 'examples').map(entry => entry.name); }
|
|
24
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; plugins = []; }
|
|
25
|
+
const help = spawnSync(process.execPath, [join(root, 'bin/elanous.mjs'), '--test', '--help'], { cwd: root, encoding: 'utf8', timeout: 15_000 });
|
|
26
|
+
// An unconfigured test instance cannot start the CLI. Unknown commands must not be treated as installed.
|
|
27
|
+
const commands = help.status === 0
|
|
28
|
+
? [...help.stdout.matchAll(/^\s{2}(?!-)([\w:-]+)(?:\s|$)/gm)].map(match => match[1]!) : [];
|
|
29
|
+
return { graphs, plugins, commands };
|
|
30
|
+
}
|
|
6
31
|
export interface IssuePlan { issue: string; priority: number; dependsOn: string[]; owner?: string }
|
|
7
32
|
|
|
8
33
|
const unsafe: Array<[HitlReason, RegExp]> = [
|
|
@@ -42,14 +67,17 @@ export async function askSteward(prompt: string, role: 'classify' | 'planning'):
|
|
|
42
67
|
return parsed(result);
|
|
43
68
|
}
|
|
44
69
|
|
|
45
|
-
export async function classifyIssue(issue: TriageIssue, ask: StewardAsk = askSteward): Promise<ClassifiedIssue> {
|
|
70
|
+
export async function classifyIssue(issue: TriageIssue, ask: StewardAsk = askSteward, inventory: CapabilityInventory = installedCapabilities()): Promise<ClassifiedIssue> {
|
|
46
71
|
const candidate = unsafeReason(issue);
|
|
47
|
-
const response = parsed(await ask(`스튜어드 분류. JSON 객체만: {"rung":0|1|2|3|4|5|"hitl","hitlReason":null,"why":"한 문장"}. 0=이미됨, 1=셸, 2=CLI, 3=조사, 4=하니스 골, 5=외부 에이전트. 돈·공개·보안·비가역은 hitl; 그 외 사람 확인은 other. 확인되지 않은 완료를 추측하지 마라. ${candidate ? `안전 후보 ${candidate}: 이유를 확인하라.` : ''}\n이슈: ${JSON.stringify(issue)}`, 'classify'));
|
|
72
|
+
const response = parsed(await ask(`스튜어드 분류. JSON 객체만: {"rung":0|1|2|3|4|5|"hitl","hitlReason":null,"why":"한 문장","capability":"new-capability"|"existing-capability"}. 0=이미됨, 1=셸, 2=CLI, 3=조사, 4=하니스 골, 5=외부 에이전트. 돈·공개·보안·비가역은 hitl; 그 외 사람 확인은 other. 새 능력인지 기존 그래프·플러그인·명령으로 되는 일인지 판정하라. 존재만으로 그 기능이 소원을 충족한다고 추측하지 마라. 확인되지 않은 완료를 추측하지 마라. ${candidate ? `안전 후보 ${candidate}: 이유를 확인하라.` : ''}\n설치된 능력: ${JSON.stringify(inventory)}\n이슈: ${JSON.stringify(issue)}`, 'classify'));
|
|
48
73
|
if (![0, 1, 2, 3, 4, 5, 'hitl'].includes(response.rung as Rung) || typeof response.why !== 'string' || !response.why.trim()) throw new Error(`Invalid steward classification: ${issue.identifier}`);
|
|
49
74
|
const reason = candidate ?? (unsafe.some(([name]) => name === response.hitlReason) ? response.hitlReason as HitlReason : undefined)
|
|
50
75
|
?? (response.rung === 'hitl' ? 'other' : undefined);
|
|
76
|
+
// A missing or unknown verdict does not block triage; working-backwards drafting is simply skipped for that issue.
|
|
77
|
+
const capability = response.capability === 'new-capability' || response.capability === 'existing-capability' ? response.capability : undefined;
|
|
51
78
|
return { issue: issue.identifier, rung: reason ? 'hitl' : response.rung as Rung,
|
|
52
|
-
...(reason ? { hitlReason: reason } : {}), why: response.why.trim().replace(/\s+/g, ' ')
|
|
79
|
+
...(reason ? { hitlReason: reason } : {}), why: response.why.trim().replace(/\s+/g, ' '),
|
|
80
|
+
...(capability ? { capability } : {}) };
|
|
53
81
|
}
|
|
54
82
|
|
|
55
83
|
/** One request for the current batch, never one request per issue. */
|
package/src/steward/triage.ts
CHANGED
|
@@ -8,9 +8,14 @@ import { debug, redactSecretText } from '../debug/log.js';
|
|
|
8
8
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
9
9
|
import { redactSecrets } from '../task-cards/card-store.js';
|
|
10
10
|
import { directiveHash } from './directive.js';
|
|
11
|
-
import {
|
|
11
|
+
import { notifyOutcome, type OutcomeNotifyDeps } from './outcome-notify.js';
|
|
12
|
+
import { recordIntakeOnCards, recordTriageOnCards, recordLaunchOnCard, recordOutcomeOnCard, recordHitlOnCard, type StewardCardDeps } from './steward-cards.js';
|
|
13
|
+
import { planLaunches, launch, collectOutcomes, raiseHitl, readLaunchLedger, saveLaunchLedger, type LaunchDeps } from './launch.js';
|
|
14
|
+
import { listFabricExecutionCandidates } from '../self-dev/fabric-plan-core.js';
|
|
15
|
+
import { InvalidWorkingBackwardsDraft, recordWorkingBackwardsOnCards } from './working-backwards.js';
|
|
16
|
+
import { CardStore } from '../task-cards/card-store.js';
|
|
12
17
|
import { triageWithinBudget } from './triage-budget.js';
|
|
13
|
-
import { askSteward, classifyIssue, planIssues, ruleJudgment, unsafeReason, type ClassifiedIssue, type StewardAsk } from './triage-plan.js';
|
|
18
|
+
import { askSteward, classifyIssue, installedCapabilities, planIssues, ruleJudgment, unsafeReason, type ClassifiedIssue, type StewardAsk } from './triage-plan.js';
|
|
14
19
|
import { failureAlertText, markStageStart, markStageEnd, readFailureStreak, shouldAlert, writeFailureStreak, type FailureStreak, type StewardStage } from './failure-streak.js';
|
|
15
20
|
|
|
16
21
|
export type Rung = 0 | 1 | 2 | 3 | 4 | 5 | 'hitl';
|
|
@@ -24,14 +29,18 @@ export interface TriageDecision {
|
|
|
24
29
|
owner?: string;
|
|
25
30
|
/** Carried from a previous triage without a current judgment; scheduling must not route it. */
|
|
26
31
|
deferred?: true;
|
|
32
|
+
capability?: ClassifiedIssue['capability'];
|
|
27
33
|
}
|
|
28
34
|
export interface ScheduledDecision extends TriageDecision { disposition: 'now' | 'wait' | 'hitl' }
|
|
29
35
|
export interface StewardSettings {
|
|
30
|
-
mode?: '
|
|
36
|
+
mode?: 'off' | 'shadow' | 'live'; linearTeam?: string;
|
|
31
37
|
roles?: Record<string, { maxConcurrent?: number }>;
|
|
32
38
|
budget?: number;
|
|
33
39
|
tracks?: Record<string, string>;
|
|
34
40
|
alertAfterFailures?: number;
|
|
41
|
+
launch?: 'off' | 'shadow' | 'live';
|
|
42
|
+
maxParallel?: number;
|
|
43
|
+
podPool?: string;
|
|
35
44
|
}
|
|
36
45
|
export interface StewardDeps {
|
|
37
46
|
fetch?: typeof fetch;
|
|
@@ -44,14 +53,28 @@ export interface StewardDeps {
|
|
|
44
53
|
cardStore?: StewardCardDeps['store'];
|
|
45
54
|
warn?: (message: string) => void;
|
|
46
55
|
ask?: StewardAsk;
|
|
56
|
+
launchSettings?: Pick<StewardSettings, 'mode' | 'launch' | 'maxParallel' | 'podPool'>;
|
|
57
|
+
launchCommand?: LaunchDeps['command'];
|
|
58
|
+
spawnLaunch?: LaunchDeps['spawn'];
|
|
59
|
+
launchGate?: LaunchDeps['gate'];
|
|
60
|
+
outcomeNotify?: Pick<OutcomeNotifyDeps, 'document' | 'message' | 'worktreeCommand' | 'loadOrigin' | 'loadOrigins'>;
|
|
47
61
|
/** Internal command boundary: the current sync has already claimed this pid. */
|
|
48
62
|
stageOwnedByCommand?: boolean;
|
|
49
63
|
}
|
|
50
64
|
|
|
51
65
|
export function stewardSettings(): StewardSettings {
|
|
52
|
-
return getUserConfig().loops?.steward ?? {};
|
|
66
|
+
return getUserConfig().loops?.steward ?? { mode: 'shadow' };
|
|
53
67
|
}
|
|
54
68
|
|
|
69
|
+
function effectiveStewardMode(settings: StewardSettings): 'off' | 'shadow' | 'live' {
|
|
70
|
+
return settings.mode ?? settings.launch ?? 'shadow';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function resolvedStewardSettings(override?: StewardDeps['launchSettings']): StewardSettings {
|
|
74
|
+
const settings = { ...stewardSettings(), ...override };
|
|
75
|
+
if (override?.launch !== undefined && override.mode === undefined) settings.mode = override.launch;
|
|
76
|
+
return { ...settings, mode: effectiveStewardMode(settings) };
|
|
77
|
+
}
|
|
55
78
|
|
|
56
79
|
function parseJudgment(issue: TriageIssue, raw: unknown, tracks?: Record<string, string>): TriageDecision {
|
|
57
80
|
const value = typeof raw === 'string' ? JSON.parse(raw) as unknown : raw;
|
|
@@ -89,6 +112,8 @@ export async function triageIssues(issues: TriageIssue[], judge?: NonNullable<St
|
|
|
89
112
|
const classified: ClassifiedIssue[] = [];
|
|
90
113
|
const classifiedIds = new Set<string>();
|
|
91
114
|
const counts = { rule: 0, classify: 0, planning: 0 };
|
|
115
|
+
// Inventory is shared by every classification in this batch; do not spawn --help per issue.
|
|
116
|
+
let inventory: ReturnType<typeof installedCapabilities> | undefined;
|
|
92
117
|
const signal = (result: TriageDecision): void => {
|
|
93
118
|
decide({ kind: result.rung === 'hitl' ? 'ESCALATE' : 'ROUTE', what: result.issue, reason: result.why,
|
|
94
119
|
purpose: 'steward triage', target: result.owner ? `${result.rung} · ${result.owner}` : String(result.rung), refs: { issue: result.issue } });
|
|
@@ -99,7 +124,7 @@ export async function triageIssues(issues: TriageIssue[], judge?: NonNullable<St
|
|
|
99
124
|
const byRule = ruleJudgment(issue, observedIssues ?? issues);
|
|
100
125
|
if (byRule) { counts.rule++; return { ...byRule, dependsOn: [], priority: 0 }; }
|
|
101
126
|
counts.classify++;
|
|
102
|
-
const row = await classifyIssue(issue, ask);
|
|
127
|
+
const row = await classifyIssue(issue, ask, inventory ??= installedCapabilities());
|
|
103
128
|
return { ...row, dependsOn: [], priority: 0 };
|
|
104
129
|
} catch (error) {
|
|
105
130
|
try { debug.log('steward.stage', 'judgment-invalid', { issue: issue.identifier, reason: redactSecrets(String(error)) }); } catch { /* fail-soft */ }
|
|
@@ -111,6 +136,7 @@ export async function triageIssues(issues: TriageIssue[], judge?: NonNullable<St
|
|
|
111
136
|
result.why !== 'triage judgment timed out — human review' &&
|
|
112
137
|
result.why !== 'triage deferred — deadline') {
|
|
113
138
|
classified.push({ issue: result.issue, rung: result.rung, why: result.why,
|
|
139
|
+
...(result.capability ? { capability: result.capability } : {}),
|
|
114
140
|
...(result.hitlReason ? { hitlReason: result.hitlReason } : {}) });
|
|
115
141
|
classifiedIds.add(result.issue);
|
|
116
142
|
} else signal(result);
|
|
@@ -232,10 +258,10 @@ function readState(path: string): { issues: Record<string, string>; digestDay?:
|
|
|
232
258
|
catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { issues: {} }; throw error; }
|
|
233
259
|
}
|
|
234
260
|
|
|
235
|
-
/** Each command node invokes one stage.
|
|
261
|
+
/** Each command node invokes one stage. Live launch still requires the launch gate; merge and approval remain with the harness and human. */
|
|
236
262
|
export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'report', deps: StewardDeps = {}): Promise<void> {
|
|
237
|
-
const settings =
|
|
238
|
-
if (
|
|
263
|
+
const settings = resolvedStewardSettings(deps.launchSettings);
|
|
264
|
+
if (settings.mode === 'off') return;
|
|
239
265
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
240
266
|
if (stage === 'sync' && !deps.stageOwnedByCommand) {
|
|
241
267
|
return withStageSlot(root, async () => {
|
|
@@ -257,7 +283,25 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
257
283
|
if (!key) throw new Error('connector.linear.apiKey missing');
|
|
258
284
|
if (stage === 'sync') {
|
|
259
285
|
const events = await fetchLinearIssues({ apiKey: key, teamKey: settings.linearTeam ?? 'ELA', fetch: fetchFn });
|
|
260
|
-
|
|
286
|
+
const issues = events.map(e => ({ identifier: e.identifier, ref: e.ref, title: e.title, body: e.body }));
|
|
287
|
+
writeFileSync(snapshot, JSON.stringify(issues));
|
|
288
|
+
const cardIssues = issues.filter((issue): issue is TriageIssue => typeof issue.identifier === 'string');
|
|
289
|
+
if (cardIssues.length) {
|
|
290
|
+
const observeCardFailure = (error: unknown, issue?: string): void => {
|
|
291
|
+
const message = `steward sync: card write failed${issue ? ` (${issue})` : ''}: ${redactSecrets(error instanceof Error ? error.message : String(error))}`;
|
|
292
|
+
try { debug.log('steward.cards', 'write-failed', { error: message }); } catch { /* observation must not fail sync */ }
|
|
293
|
+
try { (deps.warn ?? console.warn)(message); } catch { /* observation must not fail sync */ }
|
|
294
|
+
};
|
|
295
|
+
try {
|
|
296
|
+
const store = deps.cardStore ?? new CardStore(root);
|
|
297
|
+
try {
|
|
298
|
+
for (const issue of cardIssues) {
|
|
299
|
+
try { recordIntakeOnCards(store, [issue], deps.now); }
|
|
300
|
+
catch (error) { observeCardFailure(error, issue.identifier); }
|
|
301
|
+
}
|
|
302
|
+
} finally { if (!deps.cardStore) store.close(); }
|
|
303
|
+
} catch (error) { observeCardFailure(error); }
|
|
304
|
+
}
|
|
261
305
|
} else if (stage === 'triage') {
|
|
262
306
|
const issues = JSON.parse(readFileSync(snapshot, 'utf8')) as TriageIssue[];
|
|
263
307
|
const judge = deps.judge;
|
|
@@ -286,7 +330,8 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
286
330
|
let cursor: string | null = null;
|
|
287
331
|
do {
|
|
288
332
|
const data: { issues: { nodes: Array<{ identifier: string; state?: { type: string } }>; pageInfo: { hasNextPage: boolean; endCursor: string | null } } } = await linear(key,
|
|
289
|
-
|
|
333
|
+
// IssueFilter has no `identifier` field (HTTP 400 GRAPHQL_VALIDATION_FAILED · 10-02 measured); `id` accepts identifiers like ELA-1.
|
|
334
|
+
'query($ids:[ID!]!,$after:String){issues(filter:{id:{in:$ids}},first:250,after:$after){nodes{identifier state{type}} pageInfo{hasNextPage endCursor}}}',
|
|
290
335
|
{ ids: batch, after: cursor }, fetchFn);
|
|
291
336
|
if (!Array.isArray(data.issues?.nodes) || !data.issues.pageInfo ||
|
|
292
337
|
(data.issues.pageInfo.hasNextPage && (!data.issues.pageInfo.endCursor || data.issues.pageInfo.endCursor === cursor))) {
|
|
@@ -297,7 +342,48 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
297
342
|
} while (cursor);
|
|
298
343
|
}
|
|
299
344
|
}
|
|
300
|
-
|
|
345
|
+
let rows = scheduleTriage(decisions, settings, {}, completed);
|
|
346
|
+
const issues = existsSync(snapshot) ? JSON.parse(readFileSync(snapshot, 'utf8')) as TriageIssue[] : [];
|
|
347
|
+
// L2 — a new-capability wish gets its PR/FAQ and manual drafts on the card before any launch is recorded.
|
|
348
|
+
// Only new-capability rows: triage sections are written first so the card reads intake → triage → prfaq → manual → launch;
|
|
349
|
+
// the report stage repeats both calls idempotently. A wish whose draft is invalid goes to hitl on its own —
|
|
350
|
+
// the other rows (and the shadow ledger) still proceed this tick.
|
|
351
|
+
{
|
|
352
|
+
const present = new Set(issues.map(issue => issue.identifier));
|
|
353
|
+
const wishes = rows.filter(row => present.has(row.issue) && row.capability === 'new-capability' && row.rung !== 'hitl');
|
|
354
|
+
const store = wishes.length ? deps.cardStore ?? new CardStore(root) : undefined;
|
|
355
|
+
const invalid = new Set<string>();
|
|
356
|
+
if (store) try {
|
|
357
|
+
recordTriageOnCards(wishes, issues, { root, store, now: deps.now });
|
|
358
|
+
for (const wish of wishes) {
|
|
359
|
+
try { await recordWorkingBackwardsOnCards([wish], issues, store, deps.ask ?? askSteward); }
|
|
360
|
+
catch (error) {
|
|
361
|
+
if (!(error instanceof InvalidWorkingBackwardsDraft)) throw error;
|
|
362
|
+
invalid.add(wish.issue);
|
|
363
|
+
debug.log('steward.stage', 'draft-invalid', { issue: wish.issue });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
} finally { if (!deps.cardStore) store.close(); }
|
|
367
|
+
if (invalid.size) rows = rows.map(row => invalid.has(row.issue)
|
|
368
|
+
? { ...row, rung: 'hitl' as const, disposition: 'hitl' as const, hitlReason: 'other' as const, why: 'working-backwards draft invalid' }
|
|
369
|
+
: row);
|
|
370
|
+
}
|
|
371
|
+
writeFileSync(schedule, JSON.stringify(rows));
|
|
372
|
+
const ledger = readLaunchLedger(root);
|
|
373
|
+
const launchDeps: LaunchDeps = { root, settings, ledger, command: deps.launchCommand, spawn: deps.spawnLaunch, gate: deps.launchGate };
|
|
374
|
+
const cardDeps = { root, store: deps.cardStore, now: deps.now };
|
|
375
|
+
collectOutcomes(ledger, launchDeps);
|
|
376
|
+
const plan = planLaunches(rows, issues, ledger, settings);
|
|
377
|
+
// Keep fabric candidates distinct from Linear launches until a fabric executor is wired.
|
|
378
|
+
writeFileSync(join(dir, 'fabric-candidates.json'), JSON.stringify(listFabricExecutionCandidates(root)));
|
|
379
|
+
for (const { issue, row } of plan.hitl) {
|
|
380
|
+
const entry = raiseHitl(issue, row, launchDeps);
|
|
381
|
+
recordHitlOnCard(issue, entry, cardDeps);
|
|
382
|
+
}
|
|
383
|
+
for (const item of plan.launches) {
|
|
384
|
+
const entry = launch(item, launchDeps);
|
|
385
|
+
recordLaunchOnCard(item.issue, entry, cardDeps);
|
|
386
|
+
}
|
|
301
387
|
} else {
|
|
302
388
|
const rows = JSON.parse(readFileSync(schedule, 'utf8')) as ScheduledDecision[];
|
|
303
389
|
const path = reportPath(root);
|
|
@@ -305,15 +391,23 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
305
391
|
const at = (deps.now ?? (() => new Date()))().toISOString();
|
|
306
392
|
const issues = JSON.parse(readFileSync(snapshot, 'utf8')) as TriageIssue[];
|
|
307
393
|
try {
|
|
308
|
-
|
|
394
|
+
const present = new Set(issues.map(issue => issue.identifier));
|
|
395
|
+
const known = rows.filter(row => present.has(row.issue));
|
|
396
|
+
const store = deps.cardStore ?? new CardStore(root);
|
|
397
|
+
try {
|
|
398
|
+
recordTriageOnCards(known, issues, { root, store, now: deps.now });
|
|
399
|
+
// Rows without a capability verdict (legacy, injected or unjudged) proceed; only drafting is skipped for them.
|
|
400
|
+
await recordWorkingBackwardsOnCards(known, issues, store, deps.ask ?? askSteward);
|
|
401
|
+
} finally { if (!deps.cardStore) store.close(); }
|
|
309
402
|
} catch (error) {
|
|
310
403
|
const message = `steward report: card write failed: ${redactSecrets(error instanceof Error ? error.message : String(error))}`;
|
|
311
404
|
debug.log('steward.cards', 'write-failed', { error: message });
|
|
312
405
|
(deps.warn ?? console.warn)(message);
|
|
406
|
+
if (error instanceof InvalidWorkingBackwardsDraft) throw error;
|
|
313
407
|
}
|
|
314
408
|
for (const row of rows) {
|
|
315
409
|
const issue = issues.find(item => item.identifier === row.issue);
|
|
316
|
-
if (!issue)
|
|
410
|
+
if (!issue) continue;
|
|
317
411
|
const body = `스튜어드 observe · ${row.disposition} · rung ${row.rung} · 우선순위 ${row.priority} · 의존 ${row.dependsOn.join(', ') || '없음'}${row.duplicateOf ? ` · 중복 ${row.duplicateOf}` : ''} · ${row.why}${row.hitlReason ? ` · HITL ${row.hitlReason}` : ''}`;
|
|
318
412
|
const hash = directiveHash(body);
|
|
319
413
|
if (state.issues[row.issue] === hash) continue;
|
|
@@ -322,6 +416,27 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
322
416
|
state.issues[row.issue] = hash;
|
|
323
417
|
writeFileSync(path, JSON.stringify(state));
|
|
324
418
|
}
|
|
419
|
+
const ledger = readLaunchLedger(root);
|
|
420
|
+
const outcomes = collectOutcomes(ledger, { root, settings, ledger, command: deps.launchCommand, spawn: deps.spawnLaunch });
|
|
421
|
+
for (const outcome of outcomes) {
|
|
422
|
+
const issue = issues.find(item => item.identifier === outcome.issue) ??
|
|
423
|
+
{ identifier: outcome.issue, title: outcome.title, ref: '', body: '' };
|
|
424
|
+
recordOutcomeOnCard(issue, outcome, { root, store: deps.cardStore, now: deps.now });
|
|
425
|
+
notifyOutcome(outcome, { root, ledger, shadow: settings.mode !== 'live', ...deps.outcomeNotify });
|
|
426
|
+
const prefix = ['telegram', 'pwa', 'tui', 'cli'].includes(outcome.source) ? `${outcome.source} · ` : '';
|
|
427
|
+
await (deps.sendDigest ?? sendStewardDigest)(`${prefix}${issue.title} · PR ${outcome.prNumber ?? '없음'} · ${outcome.status}`);
|
|
428
|
+
outcome.reported = true;
|
|
429
|
+
saveLaunchLedger(root, ledger);
|
|
430
|
+
}
|
|
431
|
+
const justCollected = new Set(outcomes.map(outcome => outcome.issue));
|
|
432
|
+
for (const outcome of Object.values(ledger.launches)) {
|
|
433
|
+
if ((settings.mode !== 'live' && outcome.status === 'shadow' && !outcome.notified) ||
|
|
434
|
+
(settings.mode === 'live' && !justCollected.has(outcome.issue) && outcome.reported && outcome.notified !== 'shadow') ||
|
|
435
|
+
// A finished run whose PR waits for merge still owes the wish's chat its PR link (once; failures retry).
|
|
436
|
+
(settings.mode === 'live' && outcome.awaitingMerge === true && !outcome.reported && outcome.notified !== 'sent')) {
|
|
437
|
+
notifyOutcome(outcome, { root, ledger, shadow: settings.mode !== 'live', ...deps.outcomeNotify });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
325
440
|
const day = at.slice(0, 10);
|
|
326
441
|
if (state.digestDay !== day) {
|
|
327
442
|
const send = deps.sendDigest ?? sendStewardDigest;
|
|
@@ -371,6 +486,7 @@ async function withStageSlot<T>(root: string, run: () => Promise<T>, onBusy?: ()
|
|
|
371
486
|
|
|
372
487
|
/** The command-node boundary records both normal exits and an unclosed stage from a killed process. */
|
|
373
488
|
export async function runStewardStageCommand(stage: StewardStage, deps: StewardCommandDeps = {}): Promise<0 | 1> {
|
|
489
|
+
if (resolvedStewardSettings(deps.launchSettings).mode === 'off') return 0;
|
|
374
490
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
375
491
|
if (stage !== 'sync' && existsSync(skippedPath(root))) return 0;
|
|
376
492
|
return withStageSlot(root, async () => {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { debug } from '../debug/log.js';
|
|
3
|
+
import { redactSecrets } from '../task-cards/card-store.js';
|
|
4
|
+
import type { ScheduledDecision, TriageIssue } from './triage.js';
|
|
5
|
+
import type { StewardAsk } from './triage-plan.js';
|
|
6
|
+
import type { StewardCardStore } from './steward-cards.js';
|
|
7
|
+
|
|
8
|
+
export interface WorkingBackwardsDraft {
|
|
9
|
+
prfaq: string;
|
|
10
|
+
manual: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class InvalidWorkingBackwardsDraft extends Error {
|
|
14
|
+
constructor() { super('Invalid working-backwards draft'); }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Draft promises before implementation; no document, site, or channel is written here. */
|
|
18
|
+
export async function draftWorkingBackwards(issue: TriageIssue, ask: StewardAsk): Promise<WorkingBackwardsDraft> {
|
|
19
|
+
const raw = await ask(`새 능력 소원의 구현 전 워킹 백워드 초안을 JSON 객체 {"prfaq":"...","manual":"...","signals":["판정 신호: 조건 = ...; 관측 = ...; 기대 = ..."]} 로만 써라. PR/FAQ 는 docs/marketing/TEMPLATE-prfaq-2026-09-29.md 모양의 ① 한 줄, 부제(누가), ② 문제(왜), ③ 해결(무엇), ④ 대표의 말(없는 인용은 확인 대기), ⑤ 시작하기, ⑥ 고객 FAQ(질문·답 셋 이상), ⑦ 내부 FAQ, ⑧ 판정 기준을 포함한다. 매뉴얼은 docs/marketing/TEMPLATE-manual-2026-10-02.md 모양으로 사용자가 칠 명령과 볼 화면을 제안하되 구현 전 가설이라고 명시한다. 아직 구현된 명령·화면이라고 주장하지 마라. 판정 신호 한두 문장은 PR/FAQ ⑧ 에 그대로 포함시켜라.\n소원: ${JSON.stringify(issue)}`, 'planning');
|
|
20
|
+
let value: unknown = raw;
|
|
21
|
+
if (typeof raw === 'string') {
|
|
22
|
+
// A draft that is not JSON is as invalid as one with missing sections — the report must stop on it too.
|
|
23
|
+
try { value = JSON.parse(raw.replace(/^```(?:json)?\s*|\s*```$/g, '').trim()); }
|
|
24
|
+
catch { throw new InvalidWorkingBackwardsDraft(); }
|
|
25
|
+
}
|
|
26
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new InvalidWorkingBackwardsDraft();
|
|
27
|
+
const row = value as Record<string, unknown>;
|
|
28
|
+
const prfaq = row.prfaq;
|
|
29
|
+
const manual = row.manual;
|
|
30
|
+
const faq = typeof prfaq === 'string' ? prfaq.split('⑥ 고객 FAQ')[1]?.split('⑦ 내부 FAQ')[0] ?? '' : '';
|
|
31
|
+
const criteria = typeof prfaq === 'string' ? prfaq.split('⑧ 판정 기준')[1] ?? '' : '';
|
|
32
|
+
const answered = faq.split('\n').filter(line => /^\|\s*[^|?]+\?\s*\|\s*[^|\s][^|]*\|/.test(line));
|
|
33
|
+
if (typeof prfaq !== 'string' || typeof manual !== 'string' ||
|
|
34
|
+
!Array.isArray(row.signals) || row.signals.length < 1 || row.signals.length > 2 ||
|
|
35
|
+
!row.signals.every(signal => typeof signal === 'string' && /^판정 신호: 조건 = .+; 관측 = .+; 기대 = .+\.$/.test(signal) && criteria.includes(signal)) ||
|
|
36
|
+
!['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧'].every(section => prfaq.includes(section)) ||
|
|
37
|
+
answered.length < 3 ||
|
|
38
|
+
!/elanous\s+\S+/.test(manual) || !/화면/.test(manual)) throw new InvalidWorkingBackwardsDraft();
|
|
39
|
+
return { prfaq: redactSecrets(prfaq), manual: redactSecrets(manual) };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Called during the steward report, after triage and before a launch can be recorded. */
|
|
43
|
+
export async function recordWorkingBackwardsOnCards(
|
|
44
|
+
decisions: ScheduledDecision[], issues: TriageIssue[], store: StewardCardStore, ask: StewardAsk,
|
|
45
|
+
): Promise<void> {
|
|
46
|
+
const byId = new Map(issues.map(issue => [issue.identifier, issue]));
|
|
47
|
+
for (const decision of decisions) {
|
|
48
|
+
const issue = byId.get(decision.issue);
|
|
49
|
+
if (!issue) throw new Error(`Missing issue ${decision.issue}`);
|
|
50
|
+
if (decision.capability !== 'new-capability' || decision.deferred || decision.duplicateOf || decision.rung === 'hitl') {
|
|
51
|
+
debug.log('steward.working-backwards', 'skipped-existing', { issueId: issue.identifier, verdict: decision.capability ?? 'unknown' });
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const card = store.createCard({ goalId: `linear:${issue.identifier}`, title: redactSecrets(issue.title) });
|
|
55
|
+
if (card.sections.some(section => section.key.startsWith('prfaq:')) && card.sections.some(section => section.key.startsWith('manual:'))) continue;
|
|
56
|
+
if (card.sections.some(section => section.key.startsWith('launch:'))) throw new Error(`Working-backwards draft follows launch: ${issue.identifier}`);
|
|
57
|
+
const draft = await draftWorkingBackwards(issue, ask);
|
|
58
|
+
for (const [section, content] of [['prfaq', draft.prfaq], ['manual', draft.manual]] as const) {
|
|
59
|
+
const key = `${section}:${createHash('sha256').update(content).digest('hex')}`;
|
|
60
|
+
if (!card.sections.some(item => item.key === key)) store.appendSection(card.id, { key, owner: 'steward', content });
|
|
61
|
+
}
|
|
62
|
+
debug.log('steward.working-backwards', 'drafted', { issueId: issue.identifier, verdict: decision.capability });
|
|
63
|
+
}
|
|
64
|
+
}
|