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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
5
5
|
import { join, resolve } from 'node:path';
|
|
6
6
|
import { spawnSyncText } from '../src/util/spawn-sync-output.js';
|
|
7
7
|
|
|
@@ -74,11 +74,21 @@ export function publishInternalDist(checkout: string, out: string, keep = 3): {
|
|
|
74
74
|
} else {
|
|
75
75
|
renameSync(staging, target);
|
|
76
76
|
}
|
|
77
|
+
const alias = join(download, `v${pkg.version}`);
|
|
78
|
+
try {
|
|
79
|
+
if (!lstatSync(alias).isSymbolicLink()) throw new Error(`version alias is not a symlink: ${alias}`);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
82
|
+
}
|
|
77
83
|
mkdirSync(join(out, 'latest'), { recursive: true });
|
|
78
84
|
link = join(out, 'latest', `.download-${process.pid}-${Date.now()}`);
|
|
79
85
|
symlinkSync(`../download/${name}`, link, 'dir');
|
|
80
86
|
renameSync(link, join(out, 'latest', 'download'));
|
|
81
87
|
link = undefined;
|
|
88
|
+
link = join(download, `.alias-${process.pid}-${Date.now()}`);
|
|
89
|
+
symlinkSync(name, link, 'dir');
|
|
90
|
+
renameSync(link, alias);
|
|
91
|
+
link = undefined;
|
|
82
92
|
const versions = readdirSync(download, { withFileTypes: true })
|
|
83
93
|
.filter((entry) => entry.isDirectory() && /^v[0-9A-Za-z][0-9A-Za-z._-]*-[0-9a-f]{12}$/.test(entry.name))
|
|
84
94
|
.map((entry) => ({ name: entry.name, mtime: statSync(join(download, entry.name)).mtimeMs }))
|
|
@@ -4,13 +4,14 @@ import { mkdirSync, realpathSync, writeFileSync } from 'node:fs';
|
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
5
5
|
import { debug } from '../../src/debug/log.js';
|
|
6
6
|
import { effectiveInstanceRoot } from '../../src/instance/resolve.js';
|
|
7
|
-
import { buildReleaseManifest, type ReleaseLanding } from '../../src/release-loop/manifest.js';
|
|
7
|
+
import { buildReleaseManifest, parseNextMdNotes, type NextMdNote, type ReleaseLanding } from '../../src/release-loop/manifest.js';
|
|
8
8
|
import { readReleaseNotes, releaseNotesDir } from '../../src/release-loop/release-note.js';
|
|
9
9
|
import { queryRunningRuns } from '../../src/self-implement/running-runs.js';
|
|
10
10
|
import { loadFederatedRunLedger } from '../../src/self-implement/run-ledger.js';
|
|
11
11
|
|
|
12
12
|
const SEED = 'c1cc37478';
|
|
13
13
|
const CLI_DOC = 'release/public/docs/cli.md';
|
|
14
|
+
const NEXT_MD = 'release/next.md';
|
|
14
15
|
|
|
15
16
|
function git(args: string[], optional = false): string | null {
|
|
16
17
|
const result = spawnSync('git', args, { cwd: process.cwd(), encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
|
|
@@ -61,6 +62,32 @@ function landingAt(sha: string, title: string): ReleaseLanding {
|
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* next.md lines added between the baseline and the cutoff, each tied to the last landing in range whose diff added that
|
|
67
|
+
* exact line. Lines already in the file at the baseline shipped with an earlier release and are left out (REL7b).
|
|
68
|
+
*/
|
|
69
|
+
function nextMdNotes(baseline: string, cutoff: string, landings: ReleaseLanding[]): { notes: NextMdNote[]; carried: number } {
|
|
70
|
+
const text = git(['show', `${cutoff}:${NEXT_MD}`], true);
|
|
71
|
+
if (!text) return { notes: [], carried: 0 };
|
|
72
|
+
const inRange = new Set(addedLines(requiredGit(['diff', '-U0', baseline, cutoff, '--', NEXT_MD])));
|
|
73
|
+
const addedBy = new Map<string, string>();
|
|
74
|
+
for (const landing of landings) {
|
|
75
|
+
if (!landing.changedFiles.includes(NEXT_MD)) continue;
|
|
76
|
+
const diff = requiredGit(['show', '-m', '--first-parent', '--format=', '-U0', landing.sha, '--', NEXT_MD]);
|
|
77
|
+
for (const line of addedLines(diff)) addedBy.set(line, landing.sha);
|
|
78
|
+
}
|
|
79
|
+
const all = parseNextMdNotes(text);
|
|
80
|
+
const notes = all.filter((note) => note.raw !== undefined && inRange.has(note.raw)).map((note) => {
|
|
81
|
+
const sha = addedBy.get(note.raw!);
|
|
82
|
+
return { kind: note.kind, line: note.line, ...(sha ? { sha } : {}) };
|
|
83
|
+
});
|
|
84
|
+
return { notes, carried: all.length - notes.length };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function addedLines(diff: string): string[] {
|
|
88
|
+
return diff.split('\n').filter((line) => line.startsWith('+') && !line.startsWith('+++')).map((line) => line.slice(1).trim());
|
|
89
|
+
}
|
|
90
|
+
|
|
64
91
|
function runningGoalPaths(): string[] {
|
|
65
92
|
const repo = process.cwd();
|
|
66
93
|
const paths: string[] = [];
|
|
@@ -118,11 +145,13 @@ export function main(args: string[] = process.argv.slice(2)): void {
|
|
|
118
145
|
const instanceRoot = effectiveInstanceRoot();
|
|
119
146
|
const notes = readReleaseNotes(releaseNotesDir(instanceRoot));
|
|
120
147
|
debug.log('release.notes', 'read', { fragments: notes.size, problems: notes.problems });
|
|
148
|
+
const nextMd = nextMdNotes(baselineSha, cutoff, landings);
|
|
121
149
|
const manifest = buildReleaseManifest({
|
|
122
|
-
version, baseline: { ref: baseline.ref, sha: baselineSha }, cutoff: { sha: cutoff }, landings, notes,
|
|
150
|
+
version, baseline: { ref: baseline.ref, sha: baselineSha }, cutoff: { sha: cutoff }, landings, notes, nextMd: nextMd.notes,
|
|
123
151
|
runningGoalPaths: runningGoalPaths(), publicCommandsBefore: commandsAt(baselineSha), publicCommandsAfter: commandsAt(cutoff),
|
|
124
152
|
});
|
|
125
153
|
const output = { ...manifest, baselineSource: baseline.source };
|
|
154
|
+
debug.log('release-loop.notes', 'fragments', { version, ...manifest.fragments, carriedFromEarlier: nextMd.carried });
|
|
126
155
|
const directory = join(instanceRoot, 'release', version);
|
|
127
156
|
mkdirSync(directory, { recursive: true });
|
|
128
157
|
writeFileSync(join(directory, 'manifest.json'), JSON.stringify(output, null, 2) + '\n');
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { existsSync, mkdtempSync, readFileSync, realpathSync, rmSync, symlinkSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { finishNode, nodeOutput, readGraphContext, runCommand, type CommandResult, type CommandRunner } from './node-verdict.js';
|
|
6
|
+
|
|
7
|
+
export interface ExportCheckResult extends Record<string, unknown> {
|
|
8
|
+
outcome: 'ok' | 'fail';
|
|
9
|
+
verdict: 'pass' | 'fail';
|
|
10
|
+
summary: string;
|
|
11
|
+
step: 'export' | 'build';
|
|
12
|
+
tail: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function failed(step: ExportCheckResult['step'], message: string, result?: CommandResult): ExportCheckResult {
|
|
16
|
+
const tail = (result ? result.stderr || result.stdout : message).trim().slice(-300);
|
|
17
|
+
return { outcome: 'fail', verdict: 'fail', summary: `${step} failed: ${message}${result ? ` (rc=${result.status})` : ''}`, step, tail };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Check the same public export and PWA build as release prepare, without cloning or writing to a public remote. */
|
|
21
|
+
export function runExportCheck(run: CommandRunner = runCommand, repoRoot = process.cwd()): ExportCheckResult {
|
|
22
|
+
const context = readGraphContext();
|
|
23
|
+
const commit = nodeOutput(context, 'version-release', 'commit');
|
|
24
|
+
if (context.outputs.gate?.outcome !== 'ok') return failed('export', 'gate did not pass');
|
|
25
|
+
const root = resolve(repoRoot);
|
|
26
|
+
const out = mkdtempSync(join(tmpdir(), 'elanous-export-check-'));
|
|
27
|
+
const sourceDir = join(out, 'source');
|
|
28
|
+
let added = false;
|
|
29
|
+
let step: ExportCheckResult['step'] = 'export';
|
|
30
|
+
let result: ExportCheckResult = failed(step, 'check incomplete');
|
|
31
|
+
try {
|
|
32
|
+
const modulesRoot = dirname(realpathSync(join(root, 'node_modules')));
|
|
33
|
+
const add = run('git', ['worktree', 'add', '-q', '--detach', sourceDir, commit], root);
|
|
34
|
+
if (add.status !== 0) result = failed('export', 'git worktree add', add);
|
|
35
|
+
else {
|
|
36
|
+
added = true;
|
|
37
|
+
const head = run('git', ['rev-parse', 'HEAD'], sourceDir);
|
|
38
|
+
if (head.status !== 0 || head.stdout.trim() !== commit) result = failed('export', 'cut commit mismatch', head);
|
|
39
|
+
else {
|
|
40
|
+
const version = (JSON.parse(readFileSync(join(sourceDir, 'package.json'), 'utf8')) as { version?: string }).version;
|
|
41
|
+
if (version !== context.input.version) result = failed('export', `package.json version ${version} != ${context.input.version}`);
|
|
42
|
+
else {
|
|
43
|
+
symlinkSync(join(modulesRoot, 'node_modules'), join(sourceDir, 'node_modules'), 'dir');
|
|
44
|
+
const exported = run('bun', ['scripts/public-export.ts', '--out', join(out, 'export')], sourceDir);
|
|
45
|
+
if (exported.status !== 0) result = failed('export', 'public export', exported);
|
|
46
|
+
else {
|
|
47
|
+
step = 'build';
|
|
48
|
+
const publicDir = join(out, 'export');
|
|
49
|
+
symlinkSync(join(modulesRoot, 'node_modules'), join(publicDir, 'node_modules'), 'dir');
|
|
50
|
+
const pwaModules = join(modulesRoot, 'apps', 'pwa', 'node_modules');
|
|
51
|
+
if (existsSync(pwaModules) && existsSync(join(publicDir, 'apps', 'pwa'))) symlinkSync(pwaModules, join(publicDir, 'apps', 'pwa', 'node_modules'), 'dir');
|
|
52
|
+
const built = run('bun', ['bin/elanous.mjs', 'nexus', 'build'], publicDir);
|
|
53
|
+
result = built.status === 0 && existsSync(join(publicDir, 'apps', 'pwa', 'out', 'index.html'))
|
|
54
|
+
? { outcome: 'ok', verdict: 'pass', summary: `public export and PWA build ${commit.slice(0, 12)}`, step: 'build', tail: '' }
|
|
55
|
+
: failed('build', 'PWA build or out/index.html missing', built);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
result = failed(step, error instanceof Error ? error.message : String(error));
|
|
62
|
+
} finally {
|
|
63
|
+
try {
|
|
64
|
+
if (added) {
|
|
65
|
+
const removed = run('git', ['worktree', 'remove', '--force', sourceDir], root);
|
|
66
|
+
if (removed.status !== 0) result = failed(step, 'git worktree remove', removed);
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
result = failed(step, `git worktree remove: ${error instanceof Error ? error.message : String(error)}`);
|
|
70
|
+
} finally {
|
|
71
|
+
rmSync(out, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (import.meta.main) {
|
|
78
|
+
let version = '';
|
|
79
|
+
try {
|
|
80
|
+
version = readGraphContext().input.version;
|
|
81
|
+
process.exitCode = finishNode('export-check', version, runExportCheck());
|
|
82
|
+
} catch (error) {
|
|
83
|
+
process.exitCode = finishNode('export-check', version, failed('export', error instanceof Error ? error.message : String(error)));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -125,6 +125,14 @@ export const POD_MEMORY_SOURCE = 'docs/measurements/td1-whole-gate-mechanical-20
|
|
|
125
125
|
export const POD_SHARD_FILE_CAP = 27;
|
|
126
126
|
|
|
127
127
|
export const POD_SWEEP_INTEGRATION_ONLY: readonly string[] = ['scripts/install.test.ts'];
|
|
128
|
+
/** Whole-repository audits run alone in the nightly test-diet graph, not in release-gate shards. */
|
|
129
|
+
export const GATE_NIGHTLY_AUDITS: readonly string[] = [
|
|
130
|
+
'test/f12-sweep.test.ts', // Sweeps the whole repository; 7.8 GB even after TD1a.
|
|
131
|
+
'scripts/unwired-exports.test.ts', // Compiles and scans all exports; 9.9 GB.
|
|
132
|
+
'test/guardian/dispatch-surface-contract.test.ts', // Compiles repository-wide dispatch contracts; 8.3 GB.
|
|
133
|
+
'test/pwa-build-typecheck.test.ts', // Builds and typechecks the whole PWA; 4.2 GB.
|
|
134
|
+
'test/user-config-mcp.test.ts', // 2.1 GB peak; keep the gate below the 2 GB audit budget.
|
|
135
|
+
];
|
|
128
136
|
const tail40 = (output: string) => output.trimEnd().split(/\r?\n/).slice(-40).join('\n');
|
|
129
137
|
const lastStartedTestFile = (output: string, paths: string[]): string | undefined => {
|
|
130
138
|
let last: string | undefined;
|
|
@@ -211,9 +219,11 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
211
219
|
// CDP 시험은 `test:deterministic` 이 스스로 뺀다 — 조각에 그것만 남으면 bun «시험 없음» exit 1 을 «불완전»으로 읽었다(09-30 G1d 실측 둘).
|
|
212
220
|
// bun 의 탐색은 숨은 디렉터리(`.x/`)를 안 연다 — 사람의 `bun test` 가 한 번도 안 돌리는 빈 픽스처가 조각에 혼자 남아 «Ran 0 tests» 를 «불완전»으로 읽었다(09-30 G1e 5번 조각).
|
|
213
221
|
const assignable = files.filter((file) => !cdpPatterns.includes(file) && !POD_SWEEP_INTEGRATION_ONLY.includes(file)
|
|
214
|
-
&& !file.split('/').some((part) => part.startsWith('.')));
|
|
222
|
+
&& !GATE_NIGHTLY_AUDITS.includes(file) && !file.split('/').some((part) => part.startsWith('.')));
|
|
215
223
|
const integrationOnly = files.filter((file) => POD_SWEEP_INTEGRATION_ONLY.includes(file));
|
|
216
224
|
if (integrationOnly.length) debug.log('release-loop.gate', 'pod-sweep-integration-only', { files: integrationOnly });
|
|
225
|
+
const nightlyAudits = files.filter((file) => GATE_NIGHTLY_AUDITS.includes(file));
|
|
226
|
+
if (nightlyAudits.length) debug.log('release-loop.gate', 'pod-sweep-nightly-audit', { files: nightlyAudits });
|
|
217
227
|
if (!assignable.length) throw new Error('sweep incomplete: no tests ran');
|
|
218
228
|
const durations = pod.durationSource ? readFileDurations(pod.durationSource) : new Map<string, number>();
|
|
219
229
|
const memory = readFileMemory(join(tree, POD_MEMORY_SOURCE));
|
|
@@ -427,9 +437,10 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
427
437
|
const outputs: string[] = [];
|
|
428
438
|
let total = { pass: 0, fail: 0, errors: 0, ran: 0, files: 0 };
|
|
429
439
|
for (const group of groups) {
|
|
430
|
-
if (!group.paths.length || !files.some((f) => group.paths.some((p) => f === p || f.startsWith(`${p}/`)))) continue;
|
|
440
|
+
if (!group.paths.length || !files.some((f) => !GATE_NIGHTLY_AUDITS.includes(f) && group.paths.some((p) => f === p || f.startsWith(`${p}/`)))) continue;
|
|
431
441
|
const start = Date.now();
|
|
432
|
-
const groupIgnores =
|
|
442
|
+
const groupIgnores = [...new Set([...cdpPatterns, ...GATE_NIGHTLY_AUDITS])]
|
|
443
|
+
.filter((pattern) => files.includes(pattern) && group.paths.some((p) => pattern === p || pattern.startsWith(`${p}/`)))
|
|
433
444
|
.flatMap((pattern) => ['--path-ignore-patterns', pattern]);
|
|
434
445
|
const run = await command('bun', ['run', 'test:deterministic', ...groupIgnores, ...group.paths.map(asPath)], tree);
|
|
435
446
|
if (logDir) {
|
|
@@ -608,7 +619,10 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
608
619
|
};
|
|
609
620
|
let baseline: SweepFailures | undefined = trusted ? { failures: trusted.failures, errors: trusted.errors ?? [] } : undefined;
|
|
610
621
|
if (!baseline) {
|
|
611
|
-
|
|
622
|
+
// K9b — with no reusable ledger the baseline is swept on the same Pod pool as the cut, not on this host (0.2.6: an
|
|
623
|
+
// hour of local baseline after a 23-minute Pod cut).
|
|
624
|
+
const baseRun = await runner.sweep(await getBaseTree(), join(root, 'release', opts.version, 'gate-logs', 'baseline'),
|
|
625
|
+
opts.pod ? { ...opts.pod, durationSource: join(ledger, 'release', opts.baselineVersion, 'gate-logs', 'cut') } : undefined);
|
|
612
626
|
baseline = failuresOf(baseRun, 'baseline sweep');
|
|
613
627
|
}
|
|
614
628
|
for (const id of [...baseline.failures, ...baseline.errors, ...cut.failures, ...cut.errors]) fileOf(id);
|
|
@@ -57,6 +57,11 @@ export function errorResult(error: unknown): { outcome: 'error'; verdict: 'fail'
|
|
|
57
57
|
return { outcome: 'error', verdict: 'fail', summary: error instanceof Error ? error.message : String(error) };
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/** A cut whose notes would show users nothing fails the cutoff instead of shipping a «Plus N internal changes.» page. */
|
|
61
|
+
export function cutoffNotesEmpty(data: Record<string, unknown> | undefined): boolean {
|
|
62
|
+
return Array.isArray(data?.escalate) && (data.escalate as Array<{ kind?: string }>).some((item) => item?.kind === 'notes-empty');
|
|
63
|
+
}
|
|
64
|
+
|
|
60
65
|
// Existing cutoff/PWA/TUI programs have different output conventions; normalize them at the graph boundary.
|
|
61
66
|
if (import.meta.main) {
|
|
62
67
|
const node = process.argv[2];
|
|
@@ -78,7 +83,9 @@ if (import.meta.main) {
|
|
|
78
83
|
const baseline = data?.baseline as { sha?: string } | undefined;
|
|
79
84
|
const outcome: NodeOutcome = node === 'cutoff' ? run.status === 0 && data?.version === version && (data?.cutoff as { sha?: string } | undefined)?.sha === commit && typeof baseline?.sha === 'string' ? 'ok' : 'error'
|
|
80
85
|
: run.status === 0 && (verdict === 'pass' || verdict === 'flaky') ? 'ok' : run.status === 1 ? 'fail' : 'error';
|
|
81
|
-
|
|
86
|
+
if (node === 'cutoff' && outcome === 'ok' && cutoffNotesEmpty(data)) {
|
|
87
|
+
result = { ...data, outcome: 'fail', verdict: 'fail', summary: 'cutoff notes-empty — 사용자에게 보일 변경 줄이 0이다(release/next.md 와 PR 노트 조각을 확인하라)' };
|
|
88
|
+
} else result = { ...data, outcome, verdict: outcome === 'ok' ? verdict === 'flaky' ? 'flaky' : 'pass' : 'fail',
|
|
82
89
|
summary: outcome === 'ok' ? `${node} ${verdict === 'flaky' ? 'flaky' : 'pass'}` : `${node} ${data?.error || run.stderr.trim() || data?.verdict || 'failed'} (rc=${run.status})` };
|
|
83
90
|
} catch (error) { result = errorResult(error); }
|
|
84
91
|
process.exitCode = finishNode(node ?? 'unknown', version, result);
|
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
3
|
import { homedir } from 'node:os';
|
|
4
|
+
import { join, isAbsolute } from 'node:path';
|
|
5
|
+
import { debug } from '../../src/debug/log.js';
|
|
3
6
|
import { errorResult, finishNode, lastResult, nodeOutput, readGraphContext, runCommand, type CommandResult, type CommandRunner } from './node-verdict.js';
|
|
4
7
|
|
|
5
|
-
interface HostResult { host: string; ok: boolean; before: string; after: string; error?: string; skipped?: string; hooks?: string }
|
|
8
|
+
interface HostResult { host: string; ok: boolean; before: string; after: string; error?: string; skipped?: string; hooks?: string; restart?: 'no-service' }
|
|
9
|
+
interface OpsUpgradeDeps { exists?: (path: string) => boolean; installSource?: () => string | null }
|
|
10
|
+
|
|
11
|
+
function localInstallSource(): string | null {
|
|
12
|
+
try {
|
|
13
|
+
const data: unknown = JSON.parse(readFileSync(join(homedir(), '.local/share/elanous/install.json'), 'utf8'));
|
|
14
|
+
return data && typeof data === 'object' && 'source' in data && typeof data.source === 'string' ? data.source : null;
|
|
15
|
+
} catch { return null; }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function installedMatches(installed: unknown, version: string, checkout: boolean): boolean {
|
|
19
|
+
return installed === version || checkout && typeof installed === 'string'
|
|
20
|
+
&& installed.startsWith(`${version}-`) && /^[0-9a-f]{12}$/.test(installed.slice(version.length + 1));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function missingService(data: Record<string, unknown> | undefined): boolean {
|
|
24
|
+
return typeof data?.reason === 'string'
|
|
25
|
+
&& /^재시작 실패:\s*(?:Could not find service\s+"?com\.elanous\.nexus\b|(?:Failed to restart elanous-nexus\.service:\s*)?Unit elanous-nexus(?:\.service)? not found\b)/i.test(data.reason);
|
|
26
|
+
}
|
|
6
27
|
|
|
7
28
|
function firstLine(text: string): string { return text.trim().split(/\r?\n/)[0]?.trim() ?? ''; }
|
|
8
29
|
|
|
@@ -15,7 +36,7 @@ function reason(result: CommandResult): string {
|
|
|
15
36
|
|
|
16
37
|
function quote(value: string): string { return `'${value.replaceAll("'", "'\\''")}'`; }
|
|
17
38
|
|
|
18
|
-
export function runOpsUpgrade(run: CommandRunner = runCommand) {
|
|
39
|
+
export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeDeps = {}) {
|
|
19
40
|
const context = readGraphContext();
|
|
20
41
|
const hosts = context.input.opsHosts;
|
|
21
42
|
if (hosts === undefined || (Array.isArray(hosts) && hosts.length === 0)) {
|
|
@@ -27,9 +48,12 @@ export function runOpsUpgrade(run: CommandRunner = runCommand) {
|
|
|
27
48
|
if (nodeOutput(context, 'publish', 'tag') !== `v${version}`) throw new Error('published tag/version mismatch');
|
|
28
49
|
const internalDist = context.input.internalDist;
|
|
29
50
|
if (internalDist !== undefined && (typeof internalDist !== 'string' || !internalDist.trim())) throw new Error('input.internalDist must be a path string');
|
|
30
|
-
const
|
|
51
|
+
const defaultFeed = join(homedir(), '.local/share/elanous-ops/internal-dist');
|
|
52
|
+
const feedPath = typeof internalDist === 'string'
|
|
53
|
+
? internalDist.startsWith('~/') ? `${homedir()}/${internalDist.slice(2)}` : internalDist
|
|
54
|
+
: (deps.exists ?? existsSync)(defaultFeed) ? defaultFeed : undefined;
|
|
31
55
|
let feedError = '';
|
|
32
|
-
if (
|
|
56
|
+
if (feedPath !== undefined) {
|
|
33
57
|
try {
|
|
34
58
|
const feed = run('bun', ['scripts/publish-internal-dist.ts', '--checkout', process.cwd(), '--out', feedPath]);
|
|
35
59
|
const data = lastResult(feed);
|
|
@@ -38,8 +62,15 @@ export function runOpsUpgrade(run: CommandRunner = runCommand) {
|
|
|
38
62
|
}
|
|
39
63
|
const results: HostResult[] = [];
|
|
40
64
|
for (const host of hosts as string[]) {
|
|
65
|
+
const source = host === 'local' ? (deps.installSource ?? localInstallSource)() : null;
|
|
66
|
+
const checkout = host === 'local' && source !== null && isAbsolute(source);
|
|
67
|
+
const path = checkout ? 'checkout' as const : 'release' as const;
|
|
68
|
+
const record = (result: HostResult) => {
|
|
69
|
+
results.push(result);
|
|
70
|
+
debug.log('release-loop.ops-upgrade', 'host', { host, path, restart: result.restart ?? (context.input.opsRestart === true ? result.ok ? 'ok' : 'failed' : 'skipped'), ok: result.ok });
|
|
71
|
+
};
|
|
41
72
|
if (host !== 'local' && feedError) {
|
|
42
|
-
|
|
73
|
+
record({ host, ok: false, before: '', after: '', skipped: '내부 피드 발행 실패', error: feedError });
|
|
43
74
|
continue;
|
|
44
75
|
}
|
|
45
76
|
const command = (args: string[]): CommandResult => host === 'local'
|
|
@@ -51,22 +82,46 @@ export function runOpsUpgrade(run: CommandRunner = runCommand) {
|
|
|
51
82
|
try {
|
|
52
83
|
const previous = command(['--version']);
|
|
53
84
|
if (previous.status === 0) before = firstLine(previous.stdout);
|
|
54
|
-
|
|
85
|
+
if (checkout) {
|
|
86
|
+
const git = (args: string[]) => run('git', args, source!);
|
|
87
|
+
const dirty = git(['status', '--porcelain', '--untracked-files=all']);
|
|
88
|
+
if (dirty.status !== 0 || dirty.stdout.trim()) {
|
|
89
|
+
record({ host, ok: false, before, after, error: dirty.status === 0 ? '체크아웃 변경 있음' : `체크아웃 상태 확인 실패: ${reason(dirty)}` });
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const fetched = git(['fetch', '--tags']);
|
|
93
|
+
if (fetched.status !== 0) {
|
|
94
|
+
record({ host, ok: false, before, after, error: `태그 가져오기 실패: ${reason(fetched)}` });
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const tag = git(['rev-parse', '--verify', `refs/tags/v${version}^{commit}`]);
|
|
98
|
+
if (tag.status !== 0 || !tag.stdout.trim()) {
|
|
99
|
+
record({ host, ok: false, before, after, error: `태그 없음: v${version}` });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const moved = git(['checkout', '--detach', `v${version}`]);
|
|
103
|
+
if (moved.status !== 0) {
|
|
104
|
+
record({ host, ok: false, before, after, error: `체크아웃 전환 실패: ${reason(moved)}` });
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const updated = command(['update', ...(checkout || host !== 'local' && feedPath !== undefined ? [] : ['--version', version]), '--json', ...(context.input.opsRestart === true ? ['--restart'] : [])]);
|
|
55
109
|
const updateResult = lastResult(updated);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
110
|
+
const noService = host !== 'local' && context.input.opsRestart === true && updateResult?.exitCode === 1 && missingService(updateResult);
|
|
111
|
+
if ((!noService && (updated.status !== 0 || updateResult?.exitCode !== 0)) || !installedMatches(updateResult?.installedVersion, version, checkout)) {
|
|
112
|
+
const error = (updated.status === 0 && updateResult?.exitCode === 0 || noService)
|
|
113
|
+
? `설치판 불일치 (기대 ${version}, 실제 ${String(updateResult?.installedVersion)})` : reason(updated);
|
|
114
|
+
record({ host, ok: false, before, after, error });
|
|
60
115
|
continue;
|
|
61
116
|
}
|
|
62
117
|
const current = command(['--version']);
|
|
63
118
|
if (current.status !== 0) {
|
|
64
|
-
|
|
119
|
+
record({ host, ok: false, before, after, error: reason(current) });
|
|
65
120
|
continue;
|
|
66
121
|
}
|
|
67
122
|
after = firstLine(current.stdout);
|
|
68
|
-
if (after.split(/\s+/)[0]
|
|
69
|
-
|
|
123
|
+
if (!installedMatches(after.split(/\s+/)[0], version, checkout)) {
|
|
124
|
+
record({ host, ok: false, before, after, error: `올렸는데 판이 그대로 (기대 ${version}, 실제 ${after || '없음'})` });
|
|
70
125
|
continue;
|
|
71
126
|
}
|
|
72
127
|
// The webhook receiver (`elanous hooks serve`, a user systemd unit on the bot VM) keeps the old code until it
|
|
@@ -76,16 +131,17 @@ export function runOpsUpgrade(run: CommandRunner = runCommand) {
|
|
|
76
131
|
const restarted = run('ssh', [host, 'systemctl --user try-restart elanous-hooks.service 2>/dev/null && systemctl --user is-active elanous-hooks.service 2>/dev/null || echo absent']);
|
|
77
132
|
hooks = firstLine(restarted.stdout) || 'absent';
|
|
78
133
|
}
|
|
79
|
-
|
|
134
|
+
record({ host, ok: true, before, after, ...(hooks ? { hooks } : {}), ...(noService ? { restart: 'no-service' as const } : {}) });
|
|
80
135
|
} catch (error) {
|
|
81
|
-
|
|
136
|
+
record({ host, ok: false, before, after, error: firstLine(String(error)) });
|
|
82
137
|
}
|
|
83
138
|
}
|
|
84
139
|
const failures = results.filter((result) => !result.ok);
|
|
85
140
|
return { outcome: failures.length || feedError ? 'fail' as const : 'ok' as const,
|
|
86
141
|
verdict: failures.length || feedError ? 'fail' as const : 'pass' as const,
|
|
87
142
|
summary: failures.length ? `운영 판올림 실패: ${failures.map(({ host, error }) => `${host}: ${error}`).join('; ')}${feedError && !failures.some((item) => item.skipped) ? `; ${feedError}` : ''}`
|
|
88
|
-
: feedError || `운영 판올림 ${version} · ${results.length}대 확인`,
|
|
143
|
+
: feedError || `운영 판올림 ${version} · ${results.length}대 확인`,
|
|
144
|
+
feed: feedError ? 'failed' as const : feedPath === undefined ? 'skipped-no-path' as const : 'published' as const, hosts: results };
|
|
89
145
|
}
|
|
90
146
|
|
|
91
147
|
if (import.meta.main) {
|
|
@@ -2,10 +2,13 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { releaseLedgerRoot } from '../../src/instance/resolve.js';
|
|
4
4
|
import { checklistGate, type ChecklistGate } from '../../src/release-loop/checklist.js';
|
|
5
|
+
import { getSchedule, formatKst } from '../../src/release-loop/release-schedule.js';
|
|
6
|
+
import { debug } from '../../src/debug/log.js';
|
|
5
7
|
|
|
6
8
|
export type ReleaseReadiness =
|
|
7
9
|
| { ready: false; reason: 'already-published'; details: string }
|
|
8
10
|
| { ready: false; reason: 'already-running'; details: string }
|
|
11
|
+
| { ready: false; reason: 'before-cut'; details: string }
|
|
9
12
|
| { ready: false; reason: 'checklist-blocked'; details: ChecklistGate }
|
|
10
13
|
| { ready: true; reason: 'ready'; details: ChecklistGate };
|
|
11
14
|
|
|
@@ -13,6 +16,7 @@ export interface ReleaseReadinessDeps {
|
|
|
13
16
|
ledgerRoot?: string;
|
|
14
17
|
checklist?: typeof checklistGate;
|
|
15
18
|
isPidAlive?: (pid: number) => boolean;
|
|
19
|
+
now?: () => Date;
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
function readIfPresent(path: string): string | undefined {
|
|
@@ -52,6 +56,11 @@ export function releaseReadiness(version: string, deps: ReleaseReadinessDeps = {
|
|
|
52
56
|
return { ready: false, reason: 'already-running', details: String(pid) };
|
|
53
57
|
}
|
|
54
58
|
}
|
|
59
|
+
const schedule = getSchedule(version, deps.ledgerRoot);
|
|
60
|
+
if (schedule && (deps.now ?? (() => new Date()))().getTime() < Date.parse(schedule.cutAt)) {
|
|
61
|
+
debug.log('release.schedule', 'before-cut', { version, cutAt: schedule.cutAt, landBy: schedule.landBy });
|
|
62
|
+
return { ready: false, reason: 'before-cut', details: `before-cut (${formatKst(schedule.cutAt)})` };
|
|
63
|
+
}
|
|
55
64
|
const gate = (deps.checklist ?? checklistGate)(version);
|
|
56
65
|
if (!gate.ok) return { ready: false, reason: 'checklist-blocked', details: gate };
|
|
57
66
|
return { ready: true, reason: 'ready', details: gate };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { join, resolve } from 'node:path';
|
|
6
6
|
import { emitNodeResult } from './node-verdict.js';
|
|
@@ -55,7 +55,16 @@ function versionAt(repo: string): string {
|
|
|
55
55
|
return pkg.version;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
/** next.md list lines (trimmed) present at the cut commit, or null when the cut or its next.md cannot be read. */
|
|
59
|
+
function shippedNextMdLines(repo: string, cut: string | undefined): { lines: Set<string> } | { reason: string } {
|
|
60
|
+
if (!cut) return { reason: 'cut-unknown' };
|
|
61
|
+
// git-spawn-allow: reads the cut commit's next.md to learn which lines that release already carried.
|
|
62
|
+
const shown = spawnSync('git', ['show', `${cut}:release/next.md`], { cwd: repo, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
|
|
63
|
+
if (shown.status !== 0 || shown.error) return { reason: 'cut-next-md-unreadable' };
|
|
64
|
+
return { lines: new Set(shown.stdout.split('\n').map((line) => line.trim()).filter((line) => line.startsWith('- '))) };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function landing(repo: string, kind: Kind, releaseVersion: string, cut?: string): Output {
|
|
59
68
|
const target = kind === 'release' ? releaseVersion : nextDevVersion(releaseVersion);
|
|
60
69
|
const source = kind === 'release' ? `${releaseVersion}-dev.N` : releaseVersion;
|
|
61
70
|
fetchMain(repo);
|
|
@@ -85,6 +94,41 @@ function landing(repo: string, kind: Kind, releaseVersion: string): Output {
|
|
|
85
94
|
writeFileSync(lockPath, lock.replace(root[0], root[0].replace(lockedVersion[0], `"version": "${target}"`)));
|
|
86
95
|
}
|
|
87
96
|
run('bun', ['install', '--frozen-lockfile'], worktree);
|
|
97
|
+
if (kind === 'dev-bump') {
|
|
98
|
+
const nextPath = join(worktree, 'release', 'next.md');
|
|
99
|
+
if (existsSync(nextPath)) {
|
|
100
|
+
const original = readFileSync(nextPath, 'utf8');
|
|
101
|
+
// OP 10-02: dev-bump runs on the then-current main, not the cut. Lines landed after the cut are not in this
|
|
102
|
+
// release and #22745 carries only lines added by the next cut range — so remove only lines the cut carried.
|
|
103
|
+
const shipped = shippedNextMdLines(repo, cut);
|
|
104
|
+
if ('reason' in shipped) {
|
|
105
|
+
console.error(`⚠ next.md 비우기 건너뜀(${shipped.reason}) — 컷 커밋의 next.md 를 못 읽어 «나간 줄»을 증명할 수 없다`);
|
|
106
|
+
debug.log('release-loop.notes', 'next-md-reset-skipped', { reason: shipped.reason }, { level: 'warn' });
|
|
107
|
+
}
|
|
108
|
+
const shippedLines = 'lines' in shipped ? shipped.lines : new Set<string>();
|
|
109
|
+
let keptAfterCut = 0;
|
|
110
|
+
let removed = 0;
|
|
111
|
+
let keptLater = 0;
|
|
112
|
+
let inSection = false;
|
|
113
|
+
let droppingItem = false;
|
|
114
|
+
// Only shipped list items (and their indented continuation lines) go; headings, prose, `###` subheadings and
|
|
115
|
+
// `Target: later` items stay — the reviewer's «preserve everything but the released lines».
|
|
116
|
+
const reset = original.split(/(?<=\n)/).filter((line) => {
|
|
117
|
+
if (/^## /.test(line)) { inSection = true; droppingItem = false; return true; }
|
|
118
|
+
if (!inSection) return true;
|
|
119
|
+
if (/^- /.test(line)) {
|
|
120
|
+
if (/\bTarget:\s*later(?:\.|\s|$)/i.test(line)) { keptLater++; droppingItem = false; return true; }
|
|
121
|
+
if (!shippedLines.has(line.trim())) { keptAfterCut++; droppingItem = false; return true; }
|
|
122
|
+
removed++; droppingItem = true; return false;
|
|
123
|
+
}
|
|
124
|
+
if (droppingItem && /^[ \t]+\S/.test(line)) return false;
|
|
125
|
+
droppingItem = false;
|
|
126
|
+
return true;
|
|
127
|
+
}).join('');
|
|
128
|
+
if (reset !== original) writeFileSync(nextPath, reset);
|
|
129
|
+
debug.log('release-loop.notes', 'next-md-reset', { removed, keptLater, keptAfterCut });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
88
132
|
const title = kind === 'release' ? `release: ${target}` : `version: ${target}`;
|
|
89
133
|
const land = spawnSync('bun', ['bin/elanous.mjs', 'pr', 'land', '--commit-message', title, '--title', title, '--body', `Set package.json version to ${target}.`], {
|
|
90
134
|
cwd: worktree, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024,
|
|
@@ -111,24 +155,28 @@ function landing(repo: string, kind: Kind, releaseVersion: string): Output {
|
|
|
111
155
|
function main(args: string[] = process.argv.slice(2), repo = process.cwd()): Output {
|
|
112
156
|
let kind: Kind | null = null;
|
|
113
157
|
let version: string | null = null;
|
|
158
|
+
let cut: string | undefined;
|
|
114
159
|
try {
|
|
115
|
-
if (args[0] !== 'release' && args[0] !== 'dev-bump') throw new Error('usage: version-node.ts <release|dev-bump> [--version <v>] --json');
|
|
160
|
+
if (args[0] !== 'release' && args[0] !== 'dev-bump') throw new Error('usage: version-node.ts <release|dev-bump> [--version <v>] [--cut <sha>] --json');
|
|
116
161
|
kind = args[0];
|
|
117
162
|
for (let i = 1; i < args.length; i++) {
|
|
118
163
|
if (args[i] === '--json') continue;
|
|
119
164
|
if (args[i] === '--version' && args[i + 1]) { version = args[++i]!; continue; }
|
|
165
|
+
if (args[i] === '--cut' && args[i + 1]) { cut = args[++i]!; continue; }
|
|
120
166
|
throw new Error(`unknown or incomplete argument: ${args[i]}`);
|
|
121
167
|
}
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
168
|
+
if (process.env.ELANOUS_GRAPH_CONTEXT) {
|
|
169
|
+
const location = process.env.ELANOUS_GRAPH_CONTEXT;
|
|
170
|
+
const context = JSON.parse(location.trimStart().startsWith('{') ? location : readFileSync(location, 'utf8')) as {
|
|
171
|
+
input?: { version?: unknown }; outputs?: Record<string, { commit?: unknown } | undefined> };
|
|
172
|
+
if (!version && typeof context.input?.version === 'string') version = context.input.version;
|
|
173
|
+
// The cut = the commit version-release landed (what cutoff and the gate measured).
|
|
174
|
+
const cutCommit = context.outputs?.['version-release']?.commit;
|
|
175
|
+
if (!cut && typeof cutCommit === 'string' && cutCommit) cut = cutCommit;
|
|
128
176
|
}
|
|
129
177
|
if (!version) throw new Error('version required (--version or ELANOUS_GRAPH_CONTEXT.input.version)');
|
|
130
178
|
nextDevVersion(version);
|
|
131
|
-
return landing(resolve(repo), kind, version);
|
|
179
|
+
return landing(resolve(repo), kind, version, cut);
|
|
132
180
|
} catch (error) {
|
|
133
181
|
const message = error instanceof Error ? error.message : String(error);
|
|
134
182
|
const worktree = /\[worktree: ([^\]]+)\]$/.exec(message)?.[1];
|
|
@@ -25,7 +25,7 @@ function log(s: string): void {
|
|
|
25
25
|
|
|
26
26
|
const results = runRepoWatchCycle({
|
|
27
27
|
// 흡수 제안 리포트 발송(report 채널 · 야간무음 게이트 · fail-soft).
|
|
28
|
-
notify: (report) => { try { log(`발송 ${sendOutbound(report, 'report') ? 'OK' : '실패'}`); } catch (e) { log(`발송 오류: ${e instanceof Error ? e.message : String(e)}`); } },
|
|
28
|
+
notify: (report) => { try { log(`발송 ${sendOutbound(report, 'ops-report') ? 'OK' : '실패'}`); } catch (e) { log(`발송 오류: ${e instanceof Error ? e.message : String(e)}`); } },
|
|
29
29
|
// 자율행동 회상 기록(loop=autopilot).
|
|
30
30
|
record: (input) => { recordAutonomousActionSafe({ loop: 'autopilot', ...input }); },
|
|
31
31
|
});
|
package/scripts/retro-cycle.ts
CHANGED
|
@@ -120,11 +120,11 @@ const report = await runRetroCycle({
|
|
|
120
120
|
const p = join(REPORT_DIR, filename); writeFileSync(p, md);
|
|
121
121
|
// ★ B5: --collect-only 면 발송 억제(회고 리포트 영속·resolution/발굴 입력·발송만 skip).
|
|
122
122
|
if (process.argv.includes('--collect-only')) log(`collect-only — 회고 리포트 영속·발송 skip`);
|
|
123
|
-
else try { log(`회고 리포트 발송 ${sendOutbound(md, 'report') ? 'OK' : '실패'}`); } catch (e) { log(`발송 오류: ${e instanceof Error ? e.message : String(e)}`); }
|
|
123
|
+
else try { log(`회고 리포트 발송 ${sendOutbound(md, 'ops-report') ? 'OK' : '실패'}`); } catch (e) { log(`발송 오류: ${e instanceof Error ? e.message : String(e)}`); }
|
|
124
124
|
return p;
|
|
125
125
|
},
|
|
126
126
|
// HITL 승인 요청 — 텔레그램 alert(collect-only 면 로그만).
|
|
127
|
-
notify: (t) => { log(`[HITL] ${t.replace(/\n/g, ' ')}`); if (!process.argv.includes('--collect-only')) try { sendOutbound(t, 'alert'); } catch { /* fail-soft */ } },
|
|
127
|
+
notify: (t) => { log(`[HITL] ${t.replace(/\n/g, ' ')}`); if (!process.argv.includes('--collect-only')) try { sendOutbound(t, 'ops-alert'); } catch { /* fail-soft */ } },
|
|
128
128
|
}, period);
|
|
129
129
|
|
|
130
130
|
log(`retro ${period}: report=${report.reportPath} · 제안=${report.hasProposal ? report.proposalId : '없음'}`);
|
|
@@ -96,7 +96,7 @@ function main(): void {
|
|
|
96
96
|
console.log(msg);
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
|
-
const ok = sendOutbound(msg, 'alert');
|
|
99
|
+
const ok = sendOutbound(msg, 'ops-alert');
|
|
100
100
|
console.log(`[schedule-health] ${mode}: ${ok ? '발송' : '발송 실패'} — 밀림 ${health.stale.length}·실패 ${health.errored.length}`);
|
|
101
101
|
} finally {
|
|
102
102
|
db.close();
|