elanous 0.2.8 → 0.2.10
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/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +1 -0
- package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8067-87ce253ec2f23697.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 8195-6fde3d7a39859206.js} +5 -5
- package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +1 -0
- package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-d4b6df905f185055.js → page-54b32bd27726588d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-186fc6952f027acc.js → page-f8116559c7424da7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-cbbe03c0da7b081a.js → page-54e1f966ab60a4dc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/{page-c0c0d19d51342d3e.js → page-b31610deda76337b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-2e002ad8faa43291.js → page-6cb0e1bbefe5fb3d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-7464c1987dd76e6d.js → page-e168fa1030af0548.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-d8ab4d1489223459.js → page-4fddc90b23feb215.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +1 -0
- package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +30 -29
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +30 -29
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +30 -29
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +27 -26
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +27 -26
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +27 -26
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +30 -29
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +30 -29
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +30 -29
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +30 -29
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +30 -29
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +30 -29
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +27 -26
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +30 -29
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +30 -29
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +27 -26
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +27 -26
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +30 -29
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +30 -29
- package/apps/pwa/out/ops/checklist/index.html +1 -0
- package/apps/pwa/out/ops/checklist/index.txt +30 -0
- package/apps/pwa/out/ops/release/index.html +1 -0
- package/apps/pwa/out/ops/release/index.txt +30 -0
- package/apps/pwa/out/ops/seats/index.html +1 -0
- package/apps/pwa/out/ops/seats/index.txt +30 -0
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +28 -27
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +30 -29
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +27 -26
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +30 -29
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +30 -29
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +30 -29
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +30 -29
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +30 -29
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +27 -26
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +30 -29
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +30 -29
- 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 +30 -29
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +30 -29
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +30 -29
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +30 -29
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +30 -29
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +30 -29
- package/graphs/field/field-feed.yaml +2 -0
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +3 -1
- package/graphs/test-diet/nightly-audit.yaml +18 -0
- package/graphs/test-diet/recipes.yaml +3 -0
- package/package.json +1 -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 +14 -7
- package/scripts/ci-pwa-build-gate.ts +160 -0
- package/scripts/coord-channel-watch.sh +42 -8
- package/scripts/coord-digest.ts +199 -0
- package/scripts/coord-post.sh +60 -3
- 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/memory-lifecycle-cycle.ts +2 -2
- package/scripts/ops-now-probe.ts +327 -0
- package/scripts/publish-internal-dist.ts +11 -1
- package/scripts/release-loop/cutoff.ts +31 -2
- package/scripts/release-loop/export-check-node.ts +85 -0
- package/scripts/release-loop/gate-node.ts +18 -4
- package/scripts/release-loop/node-verdict.ts +8 -1
- package/scripts/release-loop/ops-upgrade-node.ts +72 -16
- package/scripts/release-loop/release-readiness.ts +9 -0
- package/scripts/release-loop/version-node.ts +58 -10
- 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/storyboard/storyboard.ts +5 -161
- package/scripts/test-diet/lib.ts +31 -1
- package/scripts/test-diet/node.ts +134 -17
- 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/waitlist-daily-digest.ts +1 -1
- 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/pitch-deck/SKILL.md +36 -0
- package/skills/pitch-deck/template.html +80 -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 +48 -7
- 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/ask-user-question/pending-questions.ts +25 -10
- package/src/ask-user-question/types.ts +9 -0
- package/src/auth/setup-link-tokens.fixture.ts +19 -0
- package/src/auth/setup-link-tokens.ts +164 -0
- package/src/autopilot/build/integrity-gate.ts +12 -0
- package/src/boot/daemon-prompt-turn.ts +3 -0
- 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 +1 -0
- package/src/budget/unified-usage.ts +75 -0
- package/src/channel-bot-token.ts +37 -10
- package/src/chat/ctrl-c-in-input.ts +14 -0
- package/src/chat/index.ts +61 -9
- 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/op-proxy-sync.ts +83 -0
- package/src/cli/ops-cli.ts +20 -0
- package/src/cli/pod-cli.ts +49 -0
- package/src/cli/pr-cli.ts +37 -4
- package/src/cli/pwa-stop.ts +163 -13
- package/src/cli/pwa-test.ts +3 -0
- package/src/cli/release-cli.ts +98 -15
- package/src/cli/self-update.ts +14 -0
- 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/coord-events.ts +115 -0
- package/src/dashboard/dashboard-boot-fixture.ts +23 -0
- package/src/dashboard/dashboard-status-lines.ts +45 -0
- package/src/dashboard/first-screen-band.ts +35 -0
- package/src/dashboard/index.ts +128 -34
- package/src/dashboard/provider-slash-runtime.ts +28 -11
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +17 -0
- package/src/dashboard/slash-runtime/help-from-registry.ts +43 -28
- package/src/dashboard/turn-stream-formatter.ts +1 -1
- package/src/decisions/decision-cards.ts +50 -2
- package/src/decisions/decision-ledger.ts +75 -4
- 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.ts +2 -1
- package/src/domains/coo-admin-tool.ts +62 -0
- package/src/domains/core-tools.ts +11 -3
- package/src/domains/elanous-card.ts +29 -0
- package/src/domains/ops-facts-tool.ts +76 -0
- package/src/domains/outbound-alert.ts +41 -13
- package/src/domains/release-tool.ts +168 -0
- package/src/domains/telegram-kind-route.ts +19 -1
- package/src/exec-requests/planner.ts +98 -28
- package/src/exec-requests/runner.ts +107 -10
- package/src/exec-requests/store.ts +8 -2
- package/src/field/field-media.ts +10 -7
- package/src/field/field-reel.ts +14 -4
- package/src/git-fs/worktree.ts +45 -6
- package/src/graph-runner/graph-approval-card.ts +113 -0
- package/src/graph-runner/graph-cli.ts +129 -4
- package/src/graph-runner/graph-notify.ts +25 -3
- package/src/graph-runner/runner.ts +142 -16
- package/src/harness/browser-verify.ts +25 -2
- package/src/harness/harness-cli-command.ts +231 -35
- package/src/harness/harness-incidents-writer.fixture.ts +24 -0
- package/src/harness/harness-incidents.ts +129 -0
- package/src/harness/harness-stop.ts +53 -3
- package/src/hitl/clarification-policy.ts +3 -1
- package/src/index.ts +172 -41
- package/src/intake-plane/author-goals.ts +37 -4
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/discord-seat-work.ts +47 -2
- package/src/intake-plane/seat-doc-route.ts +170 -6
- package/src/intake-plane/telegram-seat-work.ts +44 -2
- package/src/intake-plane/telegram-work.ts +1 -0
- package/src/maturity/cli-maturity.ts +34 -0
- package/src/maturity/feature-maturity.ts +100 -1
- package/src/maturity/tui-slash-maturity.ts +31 -0
- package/src/memory.ts +32 -4
- package/src/nexus/api/exec-requests.ts +33 -3
- package/src/nexus/api/http-server.ts +73 -1
- package/src/nexus/api/meta-api.ts +38 -0
- package/src/nexus/api/operator.ts +61 -0
- package/src/nexus/api/ops-api.ts +148 -0
- package/src/nexus/api/ops-seats-sources.ts +95 -0
- package/src/nexus/api/ops-seats.ts +124 -0
- package/src/nexus/api/public-routes.ts +14 -1
- package/src/nexus/api/seat-requests.ts +88 -8
- package/src/nexus/api/sessions-store.ts +63 -0
- package/src/nexus/api/setup-claim.ts +21 -0
- package/src/nexus/api/setup-codex-login.ts +160 -0
- package/src/nexus/api/usage-api.ts +102 -0
- package/src/nexus/config/secrets/file-backend.ts +56 -18
- package/src/nexus/index.ts +45 -4
- package/src/onboarding/first-run-doctor.ts +54 -0
- package/src/onboarding/first-run.ts +89 -0
- package/src/onboarding/lan-address.ts +45 -0
- 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/project/project-store.ts +115 -0
- package/src/release-loop/checklist.ts +76 -61
- package/src/release-loop/feature-store.ts +460 -0
- package/src/release-loop/manifest.ts +59 -5
- package/src/release-loop/release-schedule.ts +53 -0
- package/src/repl/index.ts +7 -0
- package/src/seat-dispatch/ceo-commands.ts +98 -0
- package/src/seat-dispatch/ceo-intent.ts +38 -0
- package/src/seat-loop/seat-loop.ts +231 -0
- package/src/seat-loop/seat-report.ts +64 -0
- package/src/self-dev/decompose.ts +66 -11
- package/src/self-dev/run-store.ts +10 -1
- package/src/self-implement/execution-clarification.ts +115 -0
- package/src/self-implement/gate-cli.ts +6 -3
- package/src/self-implement/orchestrator.ts +37 -3
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/run-status-mapping.ts +5 -3
- package/src/session/chat.ts +2 -0
- package/src/session/index.ts +9 -0
- package/src/shell-env-bootstrap.ts +17 -0
- package/src/skills/tools/usage-slash.ts +8 -7
- package/src/steward/directive.ts +51 -2
- package/src/steward/launch.ts +250 -0
- package/src/steward/outcome-notify.ts +115 -0
- package/src/steward/steward-cards.ts +8 -2
- package/src/steward/triage-plan.ts +32 -4
- package/src/steward/triage.ts +93 -9
- package/src/steward/working-backwards.ts +64 -0
- package/src/storyboard/storyboard.ts +141 -0
- package/src/task-cards/card-store.ts +2 -2
- package/src/task-orchestrator/surfaces/pod-lease.ts +260 -0
- package/src/telegram-card-followup.ts +233 -0
- package/src/telegram-commands.ts +16 -0
- package/src/telegram-report.ts +15 -9
- package/src/telegram-run.ts +90 -13
- package/src/telegram.ts +29 -8
- package/src/thinking-line.ts +28 -20
- package/src/tool-runtime/types.ts +4 -0
- package/src/tui/chat/execution-badge.ts +1 -1
- package/src/tui-client/dashboard-session.ts +2 -0
- package/src/user-config.ts +118 -11
- package/src/ux-sim/turn-states.ts +1 -1
- package/src/version/packed-revision.json +1 -1
- package/src/web-push/notify-exec-request.ts +32 -0
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4836-3084c58c1725a40b.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/6416-505ba707cacafbba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8894-7317998f6cb97d8c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-a3e0953307216c58.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9aa47b0bc6d570ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f4d2ecbbae6088d6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-2053e49930c82748.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a70ba27c33003dd0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-18166f885d831d3e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-1c9d6cfe68b121c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c108ff1fdb665c97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-147a328753d983e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-799395a9df7d95aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-6a8158b71d1fe9d7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-9567560270857531.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-e7ed3d010153d818.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-b672e02e4bec8deb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-1ba4a5d857276334.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-9db2226f38c542fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-70ee65f5f7a151dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-0bb8e43203efb23b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-092e1bcf27d751cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-eb52580c4b75b905.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-e573437b0a1c728f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-294300625e49e4d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-085847b088675978.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-9fe9d38c36999eb4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-6141f67f531d8b6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-295a02b1026b8255.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-1eb9a0e1e054209d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-82efa177a30f3741.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d0be51cec4f62606.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-2e367075ffdbc062.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-647ed2b66c1d49f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ba058c56e38f2792.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +0 -1
- package/apps/pwa/out/_next/static/css/5ef72a618a8994f3.css +0 -1
- package/apps/pwa/out/_next/static/lywc8m_HDzH9MKSDQhyEj/_buildManifest.js +0 -1
- package/src/dashboard/turn-typeahead-echo.ts +0 -23
- /package/apps/pwa/out/_next/static/{lywc8m_HDzH9MKSDQhyEj → 2DsiDfDCks44g1cPUMhiW}/_ssgManifest.js +0 -0
package/src/steward/triage.ts
CHANGED
|
@@ -8,9 +8,13 @@ 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 { 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 { InvalidWorkingBackwardsDraft, recordWorkingBackwardsOnCards } from './working-backwards.js';
|
|
15
|
+
import { CardStore } from '../task-cards/card-store.js';
|
|
12
16
|
import { triageWithinBudget } from './triage-budget.js';
|
|
13
|
-
import { askSteward, classifyIssue, planIssues, ruleJudgment, unsafeReason, type ClassifiedIssue, type StewardAsk } from './triage-plan.js';
|
|
17
|
+
import { askSteward, classifyIssue, installedCapabilities, planIssues, ruleJudgment, unsafeReason, type ClassifiedIssue, type StewardAsk } from './triage-plan.js';
|
|
14
18
|
import { failureAlertText, markStageStart, markStageEnd, readFailureStreak, shouldAlert, writeFailureStreak, type FailureStreak, type StewardStage } from './failure-streak.js';
|
|
15
19
|
|
|
16
20
|
export type Rung = 0 | 1 | 2 | 3 | 4 | 5 | 'hitl';
|
|
@@ -24,6 +28,7 @@ export interface TriageDecision {
|
|
|
24
28
|
owner?: string;
|
|
25
29
|
/** Carried from a previous triage without a current judgment; scheduling must not route it. */
|
|
26
30
|
deferred?: true;
|
|
31
|
+
capability?: ClassifiedIssue['capability'];
|
|
27
32
|
}
|
|
28
33
|
export interface ScheduledDecision extends TriageDecision { disposition: 'now' | 'wait' | 'hitl' }
|
|
29
34
|
export interface StewardSettings {
|
|
@@ -32,6 +37,9 @@ export interface StewardSettings {
|
|
|
32
37
|
budget?: number;
|
|
33
38
|
tracks?: Record<string, string>;
|
|
34
39
|
alertAfterFailures?: number;
|
|
40
|
+
launch?: 'off' | 'shadow' | 'live';
|
|
41
|
+
maxParallel?: number;
|
|
42
|
+
podPool?: string;
|
|
35
43
|
}
|
|
36
44
|
export interface StewardDeps {
|
|
37
45
|
fetch?: typeof fetch;
|
|
@@ -44,6 +52,10 @@ export interface StewardDeps {
|
|
|
44
52
|
cardStore?: StewardCardDeps['store'];
|
|
45
53
|
warn?: (message: string) => void;
|
|
46
54
|
ask?: StewardAsk;
|
|
55
|
+
launchSettings?: Pick<StewardSettings, 'launch' | 'maxParallel' | 'podPool'>;
|
|
56
|
+
launchCommand?: LaunchDeps['command'];
|
|
57
|
+
spawnLaunch?: LaunchDeps['spawn'];
|
|
58
|
+
outcomeNotify?: Pick<OutcomeNotifyDeps, 'document' | 'message' | 'worktreeCommand' | 'loadOrigin' | 'loadOrigins'>;
|
|
47
59
|
/** Internal command boundary: the current sync has already claimed this pid. */
|
|
48
60
|
stageOwnedByCommand?: boolean;
|
|
49
61
|
}
|
|
@@ -89,6 +101,8 @@ export async function triageIssues(issues: TriageIssue[], judge?: NonNullable<St
|
|
|
89
101
|
const classified: ClassifiedIssue[] = [];
|
|
90
102
|
const classifiedIds = new Set<string>();
|
|
91
103
|
const counts = { rule: 0, classify: 0, planning: 0 };
|
|
104
|
+
// Inventory is shared by every classification in this batch; do not spawn --help per issue.
|
|
105
|
+
let inventory: ReturnType<typeof installedCapabilities> | undefined;
|
|
92
106
|
const signal = (result: TriageDecision): void => {
|
|
93
107
|
decide({ kind: result.rung === 'hitl' ? 'ESCALATE' : 'ROUTE', what: result.issue, reason: result.why,
|
|
94
108
|
purpose: 'steward triage', target: result.owner ? `${result.rung} · ${result.owner}` : String(result.rung), refs: { issue: result.issue } });
|
|
@@ -99,7 +113,7 @@ export async function triageIssues(issues: TriageIssue[], judge?: NonNullable<St
|
|
|
99
113
|
const byRule = ruleJudgment(issue, observedIssues ?? issues);
|
|
100
114
|
if (byRule) { counts.rule++; return { ...byRule, dependsOn: [], priority: 0 }; }
|
|
101
115
|
counts.classify++;
|
|
102
|
-
const row = await classifyIssue(issue, ask);
|
|
116
|
+
const row = await classifyIssue(issue, ask, inventory ??= installedCapabilities());
|
|
103
117
|
return { ...row, dependsOn: [], priority: 0 };
|
|
104
118
|
} catch (error) {
|
|
105
119
|
try { debug.log('steward.stage', 'judgment-invalid', { issue: issue.identifier, reason: redactSecrets(String(error)) }); } catch { /* fail-soft */ }
|
|
@@ -111,6 +125,7 @@ export async function triageIssues(issues: TriageIssue[], judge?: NonNullable<St
|
|
|
111
125
|
result.why !== 'triage judgment timed out — human review' &&
|
|
112
126
|
result.why !== 'triage deferred — deadline') {
|
|
113
127
|
classified.push({ issue: result.issue, rung: result.rung, why: result.why,
|
|
128
|
+
...(result.capability ? { capability: result.capability } : {}),
|
|
114
129
|
...(result.hitlReason ? { hitlReason: result.hitlReason } : {}) });
|
|
115
130
|
classifiedIds.add(result.issue);
|
|
116
131
|
} else signal(result);
|
|
@@ -232,9 +247,9 @@ function readState(path: string): { issues: Record<string, string>; digestDay?:
|
|
|
232
247
|
catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { issues: {} }; throw error; }
|
|
233
248
|
}
|
|
234
249
|
|
|
235
|
-
/** Each command node invokes one stage.
|
|
250
|
+
/** Each command node invokes one stage. Only the launch setting permits spawn; merge and approval remain with the harness and human. */
|
|
236
251
|
export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'report', deps: StewardDeps = {}): Promise<void> {
|
|
237
|
-
const settings = stewardSettings();
|
|
252
|
+
const settings = { ...stewardSettings(), ...deps.launchSettings };
|
|
238
253
|
if ((settings.mode ?? 'observe') !== 'observe') throw new Error('steward act mode is out of scope');
|
|
239
254
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
240
255
|
if (stage === 'sync' && !deps.stageOwnedByCommand) {
|
|
@@ -286,7 +301,8 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
286
301
|
let cursor: string | null = null;
|
|
287
302
|
do {
|
|
288
303
|
const data: { issues: { nodes: Array<{ identifier: string; state?: { type: string } }>; pageInfo: { hasNextPage: boolean; endCursor: string | null } } } = await linear(key,
|
|
289
|
-
|
|
304
|
+
// IssueFilter has no `identifier` field (HTTP 400 GRAPHQL_VALIDATION_FAILED · 10-02 measured); `id` accepts identifiers like ELA-1.
|
|
305
|
+
'query($ids:[ID!]!,$after:String){issues(filter:{id:{in:$ids}},first:250,after:$after){nodes{identifier state{type}} pageInfo{hasNextPage endCursor}}}',
|
|
290
306
|
{ ids: batch, after: cursor }, fetchFn);
|
|
291
307
|
if (!Array.isArray(data.issues?.nodes) || !data.issues.pageInfo ||
|
|
292
308
|
(data.issues.pageInfo.hasNextPage && (!data.issues.pageInfo.endCursor || data.issues.pageInfo.endCursor === cursor))) {
|
|
@@ -297,7 +313,46 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
297
313
|
} while (cursor);
|
|
298
314
|
}
|
|
299
315
|
}
|
|
300
|
-
|
|
316
|
+
let rows = scheduleTriage(decisions, settings, {}, completed);
|
|
317
|
+
const issues = existsSync(snapshot) ? JSON.parse(readFileSync(snapshot, 'utf8')) as TriageIssue[] : [];
|
|
318
|
+
// L2 — a new-capability wish gets its PR/FAQ and manual drafts on the card before any launch is recorded.
|
|
319
|
+
// Only new-capability rows: triage sections are written first so the card reads intake → triage → prfaq → manual → launch;
|
|
320
|
+
// the report stage repeats both calls idempotently. A wish whose draft is invalid goes to hitl on its own —
|
|
321
|
+
// the other rows (and the shadow ledger) still proceed this tick.
|
|
322
|
+
{
|
|
323
|
+
const present = new Set(issues.map(issue => issue.identifier));
|
|
324
|
+
const wishes = rows.filter(row => present.has(row.issue) && row.capability === 'new-capability' && row.rung !== 'hitl');
|
|
325
|
+
const store = wishes.length ? deps.cardStore ?? new CardStore(root) : undefined;
|
|
326
|
+
const invalid = new Set<string>();
|
|
327
|
+
if (store) try {
|
|
328
|
+
recordTriageOnCards(wishes, issues, { root, store, now: deps.now });
|
|
329
|
+
for (const wish of wishes) {
|
|
330
|
+
try { await recordWorkingBackwardsOnCards([wish], issues, store, deps.ask ?? askSteward); }
|
|
331
|
+
catch (error) {
|
|
332
|
+
if (!(error instanceof InvalidWorkingBackwardsDraft)) throw error;
|
|
333
|
+
invalid.add(wish.issue);
|
|
334
|
+
debug.log('steward.stage', 'draft-invalid', { issue: wish.issue });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
} finally { if (!deps.cardStore) store.close(); }
|
|
338
|
+
if (invalid.size) rows = rows.map(row => invalid.has(row.issue)
|
|
339
|
+
? { ...row, rung: 'hitl' as const, disposition: 'hitl' as const, hitlReason: 'other' as const, why: 'working-backwards draft invalid' }
|
|
340
|
+
: row);
|
|
341
|
+
}
|
|
342
|
+
writeFileSync(schedule, JSON.stringify(rows));
|
|
343
|
+
const ledger = readLaunchLedger(root);
|
|
344
|
+
const launchDeps: LaunchDeps = { root, settings, ledger, command: deps.launchCommand, spawn: deps.spawnLaunch };
|
|
345
|
+
const cardDeps = { root, store: deps.cardStore, now: deps.now };
|
|
346
|
+
collectOutcomes(ledger, launchDeps);
|
|
347
|
+
const plan = planLaunches(rows, issues, ledger, settings);
|
|
348
|
+
for (const { issue, row } of plan.hitl) {
|
|
349
|
+
const entry = raiseHitl(issue, row, launchDeps);
|
|
350
|
+
recordHitlOnCard(issue, entry, cardDeps);
|
|
351
|
+
}
|
|
352
|
+
for (const item of plan.launches) {
|
|
353
|
+
const entry = launch(item, launchDeps);
|
|
354
|
+
recordLaunchOnCard(item.issue, entry, cardDeps);
|
|
355
|
+
}
|
|
301
356
|
} else {
|
|
302
357
|
const rows = JSON.parse(readFileSync(schedule, 'utf8')) as ScheduledDecision[];
|
|
303
358
|
const path = reportPath(root);
|
|
@@ -305,15 +360,23 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
305
360
|
const at = (deps.now ?? (() => new Date()))().toISOString();
|
|
306
361
|
const issues = JSON.parse(readFileSync(snapshot, 'utf8')) as TriageIssue[];
|
|
307
362
|
try {
|
|
308
|
-
|
|
363
|
+
const present = new Set(issues.map(issue => issue.identifier));
|
|
364
|
+
const known = rows.filter(row => present.has(row.issue));
|
|
365
|
+
const store = deps.cardStore ?? new CardStore(root);
|
|
366
|
+
try {
|
|
367
|
+
recordTriageOnCards(known, issues, { root, store, now: deps.now });
|
|
368
|
+
// Rows without a capability verdict (legacy, injected or unjudged) proceed; only drafting is skipped for them.
|
|
369
|
+
await recordWorkingBackwardsOnCards(known, issues, store, deps.ask ?? askSteward);
|
|
370
|
+
} finally { if (!deps.cardStore) store.close(); }
|
|
309
371
|
} catch (error) {
|
|
310
372
|
const message = `steward report: card write failed: ${redactSecrets(error instanceof Error ? error.message : String(error))}`;
|
|
311
373
|
debug.log('steward.cards', 'write-failed', { error: message });
|
|
312
374
|
(deps.warn ?? console.warn)(message);
|
|
375
|
+
if (error instanceof InvalidWorkingBackwardsDraft) throw error;
|
|
313
376
|
}
|
|
314
377
|
for (const row of rows) {
|
|
315
378
|
const issue = issues.find(item => item.identifier === row.issue);
|
|
316
|
-
if (!issue)
|
|
379
|
+
if (!issue) continue;
|
|
317
380
|
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
381
|
const hash = directiveHash(body);
|
|
319
382
|
if (state.issues[row.issue] === hash) continue;
|
|
@@ -322,6 +385,27 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
|
|
|
322
385
|
state.issues[row.issue] = hash;
|
|
323
386
|
writeFileSync(path, JSON.stringify(state));
|
|
324
387
|
}
|
|
388
|
+
const ledger = readLaunchLedger(root);
|
|
389
|
+
const outcomes = collectOutcomes(ledger, { root, settings, ledger, command: deps.launchCommand, spawn: deps.spawnLaunch });
|
|
390
|
+
for (const outcome of outcomes) {
|
|
391
|
+
const issue = issues.find(item => item.identifier === outcome.issue) ??
|
|
392
|
+
{ identifier: outcome.issue, title: outcome.title, ref: '', body: '' };
|
|
393
|
+
recordOutcomeOnCard(issue, outcome, { root, store: deps.cardStore, now: deps.now });
|
|
394
|
+
notifyOutcome(outcome, { root, ledger, shadow: settings.launch !== 'live', ...deps.outcomeNotify });
|
|
395
|
+
const prefix = ['telegram', 'pwa', 'tui', 'cli'].includes(outcome.source) ? `${outcome.source} · ` : '';
|
|
396
|
+
await (deps.sendDigest ?? sendStewardDigest)(`${prefix}${issue.title} · PR ${outcome.prNumber ?? '없음'} · ${outcome.status}`);
|
|
397
|
+
outcome.reported = true;
|
|
398
|
+
saveLaunchLedger(root, ledger);
|
|
399
|
+
}
|
|
400
|
+
const justCollected = new Set(outcomes.map(outcome => outcome.issue));
|
|
401
|
+
for (const outcome of Object.values(ledger.launches)) {
|
|
402
|
+
if ((settings.launch !== 'live' && outcome.status === 'shadow' && !outcome.notified) ||
|
|
403
|
+
(settings.launch === 'live' && !justCollected.has(outcome.issue) && outcome.reported && outcome.notified !== 'shadow') ||
|
|
404
|
+
// A finished run whose PR waits for merge still owes the wish's chat its PR link (once; failures retry).
|
|
405
|
+
(settings.launch === 'live' && outcome.awaitingMerge === true && !outcome.reported && outcome.notified !== 'sent')) {
|
|
406
|
+
notifyOutcome(outcome, { root, ledger, shadow: settings.launch !== 'live', ...deps.outcomeNotify });
|
|
407
|
+
}
|
|
408
|
+
}
|
|
325
409
|
const day = at.slice(0, 10);
|
|
326
410
|
if (state.digestDay !== day) {
|
|
327
411
|
const send = deps.sendDigest ?? sendStewardDigest;
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const STORYBOARD_ROOT = resolve(import.meta.dir, '../..');
|
|
5
|
+
const DIR = join(STORYBOARD_ROOT, 'storyboards');
|
|
6
|
+
|
|
7
|
+
type Text = { text: string; source: string; ref?: string };
|
|
8
|
+
type Shot = {
|
|
9
|
+
id: string; t: [number, number]; stage: string; layer?: string; persona?: string; purpose: string; subject?: string; action?: string;
|
|
10
|
+
camera?: { move?: string; lens_mm?: number; angle?: string; framing?: string }; lighting?: string; color?: string;
|
|
11
|
+
transition_in?: { type: string; intent: string }; on_screen_text?: Text[]; audio?: { vo?: string; sfx?: string; bgm?: string };
|
|
12
|
+
generation?: { tool: string; model?: string; prompt?: string; asset?: string }; real_or_staged: string; state: string; notes?: string;
|
|
13
|
+
};
|
|
14
|
+
/** kind: site — a site's page structure, menu and copy draft, planned like a storyboard (SITE2 · 10-02). */
|
|
15
|
+
type Section = { id: string; title: string; purpose: string; change: string; copy?: Text[]; notes?: string };
|
|
16
|
+
type Site = { domain: string; role: string; preview?: string; branch?: string; nav: Array<{ label: string; href: string }>; pages: Array<{ path: string; sections: Section[] }> };
|
|
17
|
+
export type Storyboard = {
|
|
18
|
+
id: string; kind: string; title: string; version: number; status: string; approved?: { by: string; at: string; note?: string } | null;
|
|
19
|
+
supersedes?: string; owner: string; directive?: string; format: { aspects: string[]; duration_s: number; fps?: number; loop?: boolean; surface?: string };
|
|
20
|
+
scqa?: { s?: string; c?: string; q?: string; a?: string }; principles?: string[]; layers?: Record<string, string>; shots: Shot[]; sites?: Site[];
|
|
21
|
+
references?: string[]; open_questions?: string[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const KINDS = ['teaser', 'site-hero', 'event-demo', 'persona-scene', 'explainer', 'ad', 'site'];
|
|
25
|
+
const CHANGES = ['keep', 'move', 'new', 'replace', 'remove'];
|
|
26
|
+
const STAGES = ['hook', 'buildup', 'climax', 'transition', 'end', 'loop'];
|
|
27
|
+
const SOURCES = ['real-ui', 'real-cli', 'real-output', 'copy', 'example'];
|
|
28
|
+
|
|
29
|
+
export function lint(sb: Storyboard, file = ''): { errors: string[]; warnings: string[] } {
|
|
30
|
+
const errors: string[] = [], warnings: string[] = [];
|
|
31
|
+
const need = (cond: unknown, msg: string) => { if (!cond) errors.push(msg); };
|
|
32
|
+
need(/^[a-z0-9][a-z0-9-]{2,80}$/.test(sb.id ?? ''), 'id: 소문자·숫자·하이픈 3~80자');
|
|
33
|
+
need(KINDS.includes(sb.kind), `kind: ${KINDS.join('|')}`);
|
|
34
|
+
need(Number.isInteger(sb.version) && sb.version >= 1, 'version: 1 이상 정수');
|
|
35
|
+
need(['draft', 'approved', 'superseded'].includes(sb.status), 'status: draft|approved|superseded');
|
|
36
|
+
need(sb.owner, 'owner 필요');
|
|
37
|
+
if (sb.status === 'approved') need(sb.approved?.by && sb.approved?.at, '승인본은 approved.by·at 필요');
|
|
38
|
+
if (file && sb.kind && !file.includes(`/${sb.kind}/`)) errors.push(`파일 위치가 kind(${sb.kind}) 폴더가 아니다`);
|
|
39
|
+
if (sb.kind === 'site') return lintSite(sb, errors, warnings);
|
|
40
|
+
need(sb.format?.aspects?.length && sb.format.duration_s > 0, 'format.aspects ⊕ duration_s 필요');
|
|
41
|
+
if (!Array.isArray(sb.shots) || !sb.shots.length) { errors.push('shots 가 비었다'); return { errors, warnings }; }
|
|
42
|
+
const ids = new Set<string>();
|
|
43
|
+
sb.shots.forEach((s, i) => {
|
|
44
|
+
const at = `shots[${i}](${s.id ?? '?'})`;
|
|
45
|
+
need(s.id && !ids.has(s.id), `${at}: id 없음·중복`); ids.add(s.id);
|
|
46
|
+
need(Array.isArray(s.t) && s.t.length === 2 && s.t[0] < s.t[1], `${at}: t = [시작, 끝] 이고 시작 < 끝`);
|
|
47
|
+
need(STAGES.includes(s.stage), `${at}: stage ${STAGES.join('|')}`);
|
|
48
|
+
need(s.purpose, `${at}: purpose(이 비트의 한 가지) 필요`);
|
|
49
|
+
need(['real', 'staged', 'composite'].includes(s.real_or_staged), `${at}: real_or_staged 필요`);
|
|
50
|
+
need(['planned', 'shot', 'approved'].includes(s.state), `${at}: state planned|shot|approved`);
|
|
51
|
+
if (s.transition_in && !s.transition_in.intent) errors.push(`${at}: 전환에는 의도 한 줄(«그냥 컷»은 설계가 아니다)`);
|
|
52
|
+
for (const txt of s.on_screen_text ?? []) {
|
|
53
|
+
need(SOURCES.includes(txt.source), `${at}: 화면 문구 «${txt.text}» 의 source 가 ${SOURCES.join('|')} 가 아니다`);
|
|
54
|
+
if (txt.source?.startsWith('real-') && !txt.ref) warnings.push(`${at}: 실제 문면 «${txt.text}» 에 ref(출처) 가 없다`);
|
|
55
|
+
if (/\d/.test(txt.text) && txt.source === 'copy') warnings.push(`${at}: 마케팅 문구에 숫자 «${txt.text}» — 실측 근거나 «example» 로`);
|
|
56
|
+
}
|
|
57
|
+
if (s.generation?.tool === 'higgsfield' && !s.generation.prompt) warnings.push(`${at}: 힉스필드 샷에 prompt 가 없다`);
|
|
58
|
+
if (s.real_or_staged !== 'real' && !/연출/.test(JSON.stringify(sb))) warnings.push(`${at}: 연출 샷이 있는데 문서 어디에도 «연출 장면» 표기가 없다`);
|
|
59
|
+
});
|
|
60
|
+
const end = Math.max(...sb.shots.map((s) => s.t?.[1] ?? 0));
|
|
61
|
+
if (sb.format?.duration_s && Math.abs(end - sb.format.duration_s) > 0.5) warnings.push(`마지막 샷 끝(${end}s) ≠ format.duration_s(${sb.format.duration_s}s)`);
|
|
62
|
+
return { errors, warnings };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** A site plan has pages instead of shots: every section says why it is there and what changes; copy names its source. */
|
|
66
|
+
function lintSite(sb: Storyboard, errors: string[], warnings: string[]): { errors: string[]; warnings: string[] } {
|
|
67
|
+
if (!Array.isArray(sb.sites) || !sb.sites.length) { errors.push('site 종류는 sites 가 필요하다'); return { errors, warnings }; }
|
|
68
|
+
for (const site of sb.sites) {
|
|
69
|
+
const at = `sites(${site.domain ?? '?'})`;
|
|
70
|
+
if (!site.domain || !site.role) errors.push(`${at}: domain ⊕ role(이 사이트가 맡는 것) 필요`);
|
|
71
|
+
if (!site.nav?.length) errors.push(`${at}: nav(메뉴) 필요`);
|
|
72
|
+
if (!site.pages?.length) errors.push(`${at}: pages 필요`);
|
|
73
|
+
if (!site.preview) warnings.push(`${at}: preview(미리보기 주소) 없음`);
|
|
74
|
+
const ids = new Set<string>();
|
|
75
|
+
for (const page of site.pages ?? []) for (const sec of page.sections ?? []) {
|
|
76
|
+
const where = `${at}${page.path} #${sec.id ?? '?'}`;
|
|
77
|
+
if (!sec.id || ids.has(`${page.path}#${sec.id}`)) errors.push(`${where}: id 없음·중복`);
|
|
78
|
+
ids.add(`${page.path}#${sec.id}`);
|
|
79
|
+
if (!sec.purpose) errors.push(`${where}: purpose(이 칸이 하는 한 가지) 필요`);
|
|
80
|
+
if (!CHANGES.includes(sec.change)) errors.push(`${where}: change ${CHANGES.join('|')}`);
|
|
81
|
+
for (const txt of sec.copy ?? []) {
|
|
82
|
+
if (!SOURCES.includes(txt.source)) errors.push(`${where}: 문구 «${txt.text}» 의 source 가 ${SOURCES.join('|')} 가 아니다`);
|
|
83
|
+
if (/\d/.test(txt.text) && txt.source === 'copy' && !txt.ref) warnings.push(`${where}: 마케팅 문구에 숫자 «${txt.text}» — ref(실측 근거) 를 단다`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { errors, warnings };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function renderSites(sb: Storyboard): string[] {
|
|
91
|
+
return (sb.sites ?? []).flatMap((site) => [
|
|
92
|
+
`## ${site.domain} — ${site.role}`, '',
|
|
93
|
+
...(site.preview ? [`- 미리보기: ${site.preview}${site.branch ? ` (가지 \`${site.branch}\`)` : ''}`] : []),
|
|
94
|
+
`- 메뉴: ${site.nav.map((n) => `${n.label}(${n.href})`).join(' · ')}`, '',
|
|
95
|
+
...site.pages.flatMap((page) => [
|
|
96
|
+
`### ${page.path}`, '', '| # | 칸 | 제목 | 목적 | 바뀜 | 문구(출처) | 메모 |', '|---|---|---|---|---|---|---|',
|
|
97
|
+
...page.sections.map((sec, i) => `| ${i + 1} | ${md(sec.id)} | ${md(sec.title)} | ${md(sec.purpose)} | ${sec.change} | ${md((sec.copy ?? []).map((x) => `«${x.text}»(${x.source}${x.ref ? ` · ${x.ref}` : ''})`).join(' / '))} | ${md(sec.notes)} |`),
|
|
98
|
+
'',
|
|
99
|
+
]),
|
|
100
|
+
]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const md = (v: unknown) => String(v ?? '').replace(/\|/g, '\\|').replace(/\n+/g, ' ');
|
|
104
|
+
export function render(sb: Storyboard, file: string): string {
|
|
105
|
+
const head = [
|
|
106
|
+
`# STORYBOARD — ${sb.title} (v${sb.version} · ${sb.status}${sb.approved ? ` · 승인 ${sb.approved.by} ${sb.approved.at}` : ''})`,
|
|
107
|
+
'',
|
|
108
|
+
`> ⛔ 자동 생성 — 손으로 고치지 않는다. 정본 = \`${file}\` · 다시 뽑기 = \`bun scripts/storyboard/storyboard.ts render ${file}\`.`,
|
|
109
|
+
`> 종류 ${sb.kind} · 소유 ${sb.owner}${sb.format ? ` · ${sb.format.aspects.join(' · ')} · ${sb.format.duration_s}초${sb.format.loop ? ' · 반복' : ''}${sb.format.surface ? ` · ${sb.format.surface}` : ''}` : ''}${sb.supersedes ? ` · 앞 판 ${sb.supersedes}` : ''}`,
|
|
110
|
+
...(sb.directive ? [`> 지시: ${sb.directive}`] : []),
|
|
111
|
+
'',
|
|
112
|
+
];
|
|
113
|
+
const scqa = sb.scqa ? ['## SCQA', ...(['s', 'c', 'q', 'a'] as const).filter((k) => sb.scqa?.[k]).map((k) => `- **${k.toUpperCase()}** — ${sb.scqa![k]}`), ''] : [];
|
|
114
|
+
const pr = sb.principles?.length ? ['## 원칙', ...sb.principles.map((p, i) => `${i + 1}. ${p}`), ''] : [];
|
|
115
|
+
const layers = sb.layers ? ['## 층', ...Object.entries(sb.layers).map(([k, v]) => `- **${k}** — ${v}`), ''] : [];
|
|
116
|
+
const table = sb.kind === 'site' ? renderSites(sb) : [
|
|
117
|
+
'## 샷 구조표',
|
|
118
|
+
'| # | 초 | 단계 | 층·인물 | 목적 | 화면(피사체·행동) | 카메라 | 빛·색 | 전환(의도) | 화면 문구(출처) | 소리 | 만들기 | 실물/연출 · 상태 |',
|
|
119
|
+
'|---|---|---|---|---|---|---|---|---|---|---|---|---|',
|
|
120
|
+
...sb.shots.map((s) => `| ${md(s.id)} | ${s.t[0]}–${s.t[1]} | ${s.stage} | ${md([s.layer, s.persona].filter(Boolean).join(' · '))} | ${md(s.purpose)} | ${md([s.subject, s.action].filter(Boolean).join(' — '))} | ${md(s.camera ? [s.camera.move, s.camera.lens_mm ? `${s.camera.lens_mm}mm` : '', s.camera.angle, s.camera.framing].filter(Boolean).join(' · ') : '')} | ${md([s.lighting, s.color].filter(Boolean).join(' · '))} | ${md(s.transition_in ? `${s.transition_in.type} — ${s.transition_in.intent}` : '')} | ${md((s.on_screen_text ?? []).map((x) => `«${x.text}»(${x.source}${x.ref ? ` · ${x.ref}` : ''})`).join(' / '))} | ${md(s.audio ? [s.audio.vo && `VO ${s.audio.vo}`, s.audio.sfx && `SFX ${s.audio.sfx}`, s.audio.bgm && `BGM ${s.audio.bgm}`].filter(Boolean).join(' · ') : '')} | ${md(s.generation ? [s.generation.tool, s.generation.model].filter(Boolean).join(' · ') : '')} | ${s.real_or_staged} · ${s.state} |`),
|
|
121
|
+
'',
|
|
122
|
+
];
|
|
123
|
+
const shots = sb.shots ?? [];
|
|
124
|
+
const prompts = shots.filter((s) => s.generation?.prompt);
|
|
125
|
+
const pblock = prompts.length ? ['## 생성 프롬프트', ...prompts.flatMap((s) => [`### ${s.id} · ${s.generation!.tool}${s.generation!.model ? ` · ${s.generation!.model}` : ''}`, '```', s.generation!.prompt!.trim(), '```', ...(s.generation!.asset ? [`결과: \`${s.generation!.asset}\``] : []), ''])] : [];
|
|
126
|
+
const notes = shots.filter((s) => s.notes).map((s) => `- ${s.id}: ${s.notes}`);
|
|
127
|
+
const tail = [
|
|
128
|
+
...(notes.length ? ['## 샷 메모', ...notes, ''] : []),
|
|
129
|
+
...(sb.open_questions?.length ? ['## 열린 물음', ...sb.open_questions.map((q) => `- ${q}`), ''] : []),
|
|
130
|
+
...(sb.references?.length ? ['## 참고', ...sb.references.map((r) => `- ${r}`), ''] : []),
|
|
131
|
+
];
|
|
132
|
+
return [...head, ...scqa, ...pr, ...layers, ...table, ...pblock, ...tail].join('\n');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function files(args: string[]): string[] {
|
|
136
|
+
if (args.length) return args.map((a) => resolve(a));
|
|
137
|
+
const out: string[] = [];
|
|
138
|
+
const walk = (d: string) => { for (const f of readdirSync(d)) { const p = join(d, f); if (statSync(p).isDirectory()) walk(p); else if (/\.ya?ml$/.test(f) && !f.startsWith('_')) out.push(p); } };
|
|
139
|
+
walk(DIR);
|
|
140
|
+
return out.sort();
|
|
141
|
+
}
|
|
@@ -38,10 +38,10 @@ type CardEvent =
|
|
|
38
38
|
* A key is `<section>` or `<section>:<idempotency key>`: the same section can be appended many times over a
|
|
39
39
|
* task's life (e.g. workspace diff updates), and the same full key is written once. `incidents` is open to any loop. */
|
|
40
40
|
export const SECTION_OWNERS: Readonly<Record<string, string | null>> = {
|
|
41
|
-
intake: 'steward', triage: 'steward', launch: 'steward', outcome: 'steward',
|
|
41
|
+
intake: 'steward', triage: 'steward', prfaq: 'steward', manual: 'steward', launch: 'steward', outcome: 'steward', hitl: 'steward',
|
|
42
42
|
gates: 'execution-loop', relations: 'execution-loop', memory: 'execution-loop',
|
|
43
43
|
workspace: 'executor', run: 'executor',
|
|
44
|
-
landing: 'landing-loop', release: 'release-loop',
|
|
44
|
+
landing: 'landing-loop', release: 'release-loop', 'test-diet': 'test-diet',
|
|
45
45
|
incidents: null,
|
|
46
46
|
};
|
|
47
47
|
|