elanous 0.2.16 → 0.2.18
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/P6YTVgzuZz5oTZhY4Din_/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1307-b46d45b705e8c9ae.js → 1307-30dff22c9e1f3073.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6158.9c0449a9a8d0064b.js → 6158.af2e93b7adff709b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d92f7ee3994e84c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-3cb52e5ea8fc2fa5.js → page-4733c967b56a4277.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d22fd849cc22f104.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c1e20d7d338713f1.js → page-bb742f995c6769e7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-482c5dbde699947c.js → page-02b073a2ac793b83.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-8a9b3594bc23289b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-a0eb02638b1e6f31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-a0e2a6d1bfb52fc5.js → page-308111abadf35670.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-c53598cfce6156f2.js → page-5d97b2901e0d1ea6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-13a63953f53c6a23.js → page-c39554c321576982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-5d3c8429d04f3831.js → page-8c3d8e63ae2e1c98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-3193bdc0bbab047a.js → page-9b72a07d424701c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-6107ca8ddd5e3bdd.js → page-a572f58cd3ca5864.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-be5ea965a07fbc41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c130b63862437814.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-fa244bd146a00ae8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-44d56d6391965a6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-4167348049166852.js → page-83a700d4c754d802.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-de52e8fdf9095f7a.js → not-found-7b3170f374f77b9d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-ca3952cd78a83ca2.js → page-a3c58f5c72a5f3f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e8e1ecaea7cd71d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-1ebfe049c640e171.js → page-1467047104141297.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-2da6f9b8a38c6978.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-102310930791197a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-6ed0b73bae8351d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-abe8e3b74bea1ffc.js → page-44d5f767491692bf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-43336055451f6e37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-d28573d6792b4275.js → page-3cc47dca0922ab9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a77ee355a2304ff0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-d751de5f8062ff5e.js → page-b17cd46cf0e9b8cf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-fe1759f94003b32b.js → page-61553851aa606146.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/{page-d64c984e855c8c68.js → page-a1f585e154e61c59.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-7bf54c9bb9274af8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-9afdee66b820c76e.js → page-5150ef17b5383a57.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-3544f154c87b43ee.js → page-134da468538baf48.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-539cde99554be20f.js → webpack-c2782b330447ffb6.js} +1 -1
- package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/decisions/index.html +1 -0
- package/apps/pwa/out/decisions/index.txt +30 -0
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/graphs/intake/intake-daily.yaml +9 -2
- package/graphs/intake/news-sources.yaml +4 -0
- package/graphs/intake/recipes.yaml +6 -1
- package/package.json +2 -1
- package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
- package/samples/workflows/templates/chat-driven-research.yaml +35 -0
- package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
- package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
- package/samples/workflows/templates/manual-quickstart.yaml +21 -0
- package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
- package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
- package/scripts/__fixtures__/intake-news.xml +7 -0
- package/scripts/ci-public-export-import-gate.ts +7 -2
- package/scripts/ci-typecheck-changed.ts +21 -6
- package/scripts/discord-seat-probe.ts +3 -2
- package/scripts/docs/generate-cli-anatomy.ts +162 -0
- package/scripts/goals/authoring-metrics.ts +73 -0
- package/scripts/intake-news.ts +169 -0
- package/scripts/landing-heal/must-fix-match.ts +7 -7
- package/scripts/landing-heal/run-mission.ts +3 -2
- package/scripts/public-export-test-run.ts +37 -2
- package/scripts/release-loop/checklist-gate-node.ts +13 -11
- package/scripts/release-loop/cut-branch.ts +2 -1
- package/scripts/release-loop/cutoff.ts +4 -2
- package/scripts/release-loop/docs-land-node.ts +77 -11
- package/scripts/release-loop/docs-node.ts +1 -1
- package/scripts/release-loop/gate-node.ts +99 -14
- package/scripts/release-loop/node-verdict.ts +2 -1
- package/scripts/release-loop/npm-publish-node.ts +11 -7
- package/scripts/release-loop/ops-upgrade-node.ts +8 -1
- package/scripts/release-loop/preflight.ts +246 -0
- package/scripts/release-loop/publish-node.ts +10 -3
- package/scripts/release-loop/release-docs.ts +134 -0
- package/scripts/release-loop/release-readiness.ts +11 -6
- package/scripts/release-loop/release-version.ts +48 -0
- package/scripts/release-loop/resume-release.ts +115 -0
- package/scripts/release-loop/unattended-release.ts +43 -7
- package/scripts/release-loop/version-node.ts +149 -41
- package/scripts/release-story/draft.ts +42 -14
- package/scripts/rhythm/daily.ts +25 -4
- package/scripts/skills/public-check.ts +62 -12
- package/scripts/test-deterministic.ts +51 -33
- package/scripts/test-diet/effectiveness.ts +62 -1
- package/scripts/test-diet/lib.ts +1 -1
- package/scripts/test-diet/node.ts +8 -1
- package/src/agent-mission/driver.ts +53 -10
- package/src/agent-mission/review-loop.ts +4 -3
- package/src/agent-substrate/pr-comment-meta.ts +20 -0
- package/src/autopilot/build/llm-conflict-merge.ts +28 -8
- package/src/autopilot/track-agent.ts +5 -3
- package/src/briefing/brief-items.ts +86 -17
- package/src/cli/autopilot-cli.ts +86 -1
- package/src/cli/brief-cli.ts +36 -2
- package/src/cli/decisions-cli.ts +4 -1
- package/src/cli/freeze-cli.ts +3 -2
- package/src/cli/intake-cli.ts +74 -2
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/notify-cli.ts +75 -0
- package/src/cli/pr-cli.ts +12 -1
- package/src/cli/provider-cli.ts +24 -0
- package/src/cli/release-cli.ts +101 -17
- package/src/cli/self-update-canary.ts +46 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/tasks-cli.ts +309 -3
- package/src/context-bus/condense.ts +19 -3
- package/src/context-bus/context-day.ts +77 -24
- package/src/context-bus/context-first.ts +33 -0
- package/src/context-card/index.ts +45 -0
- package/src/context-card/task.ts +76 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
- package/src/decisions/decision-ledger.ts +10 -1
- package/src/discord-self-message.ts +20 -2
- package/src/discord-slash-wire.ts +1 -0
- package/src/domains/outbound-alert.ts +200 -27
- package/src/domains/outbound-log-probe.ts +16 -1
- package/src/domains/release-tool.ts +2 -2
- package/src/execution-loop/gates.ts +11 -2
- package/src/flow-loop/split.ts +8 -4
- package/src/git-fs/worktree.ts +23 -3
- package/src/harness/harness-cli-command.ts +151 -11
- package/src/harness/harness-pod-dispatch.ts +11 -0
- package/src/harness/harness-queue.ts +466 -32
- package/src/harness/helper-scan.ts +10 -3
- package/src/harness/nested-elanous-policy.ts +23 -4
- package/src/hq/fence-wrapper.ts +123 -0
- package/src/hq/hq.ts +8 -0
- package/src/hq/seats.ts +369 -0
- package/src/index.ts +190 -23
- package/src/intake-plane/adopt-gate.ts +49 -0
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/collect-github.ts +57 -25
- package/src/intake-plane/digest.ts +39 -4
- package/src/intake-plane/discord-seat-work.ts +8 -2
- package/src/intake-plane/requirement-funnel.ts +316 -0
- package/src/knowledge/know-find.ts +10 -1
- package/src/loops/budget.ts +36 -0
- package/src/loops/orchestrator/finish-rate.ts +73 -1
- package/src/loops/orchestrator/grid-place.ts +11 -0
- package/src/loops/orchestrator/tick.ts +295 -20
- package/src/maturity/feature-maturity.ts +3 -2
- package/src/nexus/api/decisions-route.ts +11 -1
- package/src/nexus/api/grid.ts +61 -0
- package/src/nexus/api/http-server.ts +19 -1
- package/src/nexus/api/loop-edges.ts +69 -13
- package/src/nexus/api/ops-seats-sources.ts +2 -2
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/nexus/api/workflow-templates.ts +3 -0
- package/src/oauth/codex.ts +120 -2
- package/src/onboarding/first-chat-gate.ts +29 -0
- package/src/release-loop/checklist.ts +104 -6
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/landing-freeze.ts +24 -14
- package/src/release-loop/placement.ts +13 -3
- package/src/release-loop/rubric.ts +80 -0
- package/src/seat-dispatch/seat-ask.ts +79 -16
- package/src/seat-loop/seat-loop.ts +107 -37
- package/src/self-dev/ask-launch-flow.ts +50 -10
- package/src/self-dev/ask-path-candidates.ts +68 -0
- package/src/self-dev/dev-pipeline.ts +4 -0
- package/src/self-dev/draft-triage-rules.ts +1 -1
- package/src/self-dev/orchestrate-cli.ts +22 -0
- package/src/self-dev/round-pr-supersede.ts +244 -0
- package/src/self-dev/run-store.ts +5 -1
- package/src/self-dev/run-supervisor.ts +21 -1
- package/src/self-dev/supervisor-verdict-edges.ts +46 -0
- package/src/self-implement/follow-up-goals.ts +2 -2
- package/src/self-implement/frozen-merges.ts +25 -20
- package/src/self-implement/gate-remote.ts +427 -0
- package/src/self-implement/goal-author-clarification.ts +81 -0
- package/src/self-implement/goal-author-cli.ts +33 -5
- package/src/self-implement/goal-author.ts +93 -12
- package/src/self-implement/goal-clarification-escalation.ts +37 -1
- package/src/self-implement/goal-document-write.ts +28 -0
- package/src/self-implement/heal-intake.ts +4 -0
- package/src/self-implement/host-regate.ts +3 -1
- package/src/self-implement/l8-merge-queue.ts +29 -5
- package/src/self-implement/orchestrator.ts +78 -13
- package/src/self-implement/prior-draft-findings.ts +4 -3
- package/src/skills/skill-feedback.ts +76 -0
- package/src/task-agent/actions.ts +151 -0
- package/src/task-agent/judge.ts +122 -0
- package/src/task-agent/mission.ts +423 -0
- package/src/task-agent/shadow.ts +90 -0
- package/src/task-agent/task-hand.ts +235 -0
- package/src/task-cards/card-store.ts +4 -1
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -4
- package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
- package/src/task-orchestrator/surfaces/pod-pool.ts +81 -13
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +225 -35
- package/src/telegram-commands.ts +1 -1
- package/src/telegram-loops-command.ts +22 -15
- package/src/telegram.ts +5 -20
- package/src/user-config.ts +86 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-29d889b809288e65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-8c2d9526164f5d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-dce686e582c23937.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1a61c7ca1ceb3ae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-51f811fa119308b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-5b042736aa113756.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-57924393e3e4e539.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0f5cd57ca928f42b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-59f03fda76c03d22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-1ea64acadd704ace.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +0 -1
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
- package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{rv9cierV0jSOvDBm1g0XB → P6YTVgzuZz5oTZhY4Din_}/_ssgManifest.js +0 -0
|
@@ -5,7 +5,7 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
5
5
|
import { githubAutomationToken } from '../../src/auth/github-app-token.js';
|
|
6
6
|
import { debug } from '../../src/debug/log.js';
|
|
7
7
|
import { emitDecision } from '../../src/live/detail-switch.js';
|
|
8
|
-
import { parse } from '../../src/agent-substrate/pr-comment-meta.js';
|
|
8
|
+
import { expandRecords, parse } from '../../src/agent-substrate/pr-comment-meta.js';
|
|
9
9
|
import { classifyReviewRounds, matchMustFix, type MustFixVerdict } from './must-fix-match.js';
|
|
10
10
|
import { verifyCleanCheckout, type CheckoutResult } from './clean-checkout.js';
|
|
11
11
|
|
|
@@ -116,7 +116,8 @@ export function runMission(mission: string, ctx: GraphContext, run: RunCommand =
|
|
|
116
116
|
const pages = JSON.parse(run(['api', `repos/{owner}/{repo}/pulls/${pr.number}/comments`, '--paginate', '--slurp'])) as Array<Array<{ body?: string }>>;
|
|
117
117
|
if (!Array.isArray(pages) || !pages.every(Array.isArray)) throw new Error(`invalid review comments for PR #${pr.number}`);
|
|
118
118
|
const inline = pages.flat();
|
|
119
|
-
const comments = [...(details.reviews ?? []), ...(details.comments ?? []), ...inline]
|
|
119
|
+
const comments = [...(details.reviews ?? []), ...(details.comments ?? []), ...inline]
|
|
120
|
+
.flatMap((comment) => comment.body ? expandRecords(comment.body).map((body) => ({ ...comment, body })) : [comment]);
|
|
120
121
|
const reviewRounds = comments.filter(({ body }) => {
|
|
121
122
|
const meta = body ? parse(body) : null;
|
|
122
123
|
return meta?.role === 'reviewer' && !!meta.run && meta.round !== undefined;
|
|
@@ -4,7 +4,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
4
4
|
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, symlinkSync } from 'node:fs';
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
6
|
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
7
|
-
import { run as exportRun } from './public-export.js';
|
|
7
|
+
import { privateRedactionsPath, run as exportRun } from './public-export.js';
|
|
8
8
|
|
|
9
9
|
export interface Comparison {
|
|
10
10
|
exportOnly: string[];
|
|
@@ -118,4 +118,39 @@ export function run(argv: readonly string[], root = resolve(import.meta.dir, '..
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
// GATE-REMOTE (10-06): `--remote [host]` runs the same commit on that host and prints its result (JSON gains host · remote:true);
|
|
122
|
+
// `--local` forces this machine. Without either, a busy machine (1-min load > gateRemote.loadThreshold) dispatches on its own.
|
|
123
|
+
if (import.meta.main) void (async () => {
|
|
124
|
+
const { extractGateRemoteFlags, dispatchHeavyCheck, unreproducibleArgs, relativizeArgs } = await import('../src/self-implement/gate-remote.js');
|
|
125
|
+
const flags = extractGateRemoteFlags(process.argv.slice(2));
|
|
126
|
+
if (flags.error) { console.error(flags.error); process.exit(2); }
|
|
127
|
+
const repo = resolve(import.meta.dir, '..');
|
|
128
|
+
// The private redaction table changes exported file contents — the remote export must use the same one or the comparison differs.
|
|
129
|
+
const table = privateRedactionsPath();
|
|
130
|
+
const payloadFiles = existsSync(table) ? [{ env: 'ELANOUS_EXPORT_REDACTIONS', content: readFileSync(table, 'utf8') }] : [];
|
|
131
|
+
// File arguments, the --files list and its entries resolve against the repository root (as run() does).
|
|
132
|
+
const paths = flags.rest.filter((arg) => !arg.startsWith('--'));
|
|
133
|
+
const listIndex = flags.rest.indexOf('--files');
|
|
134
|
+
const list = listIndex >= 0 ? flags.rest[listIndex + 1] : undefined;
|
|
135
|
+
// List entries reach the host verbatim (only positional arguments are rewritten), so an absolute entry is not portable.
|
|
136
|
+
const absoluteEntries: string[] = [];
|
|
137
|
+
if (list) {
|
|
138
|
+
try {
|
|
139
|
+
const entries = readFileSync(resolve(repo, list), 'utf8').split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
|
|
140
|
+
absoluteEntries.push(...entries.filter((entry) => isAbsolute(entry)));
|
|
141
|
+
paths.push(...entries);
|
|
142
|
+
} catch { /* run() reports it */ }
|
|
143
|
+
}
|
|
144
|
+
process.exitCode = await dispatchHeavyCheck({
|
|
145
|
+
payloadFiles,
|
|
146
|
+
localOnlyReason: (() => {
|
|
147
|
+
// Each must be a tracked file inside the repository, or the remote checkout would measure something else.
|
|
148
|
+
const bad = [...new Set([...absoluteEntries, ...unreproducibleArgs(paths, repo)])];
|
|
149
|
+
return bad.length ? `file argument not reproducible on the host (outside the repository or not tracked at HEAD): ${bad.join(' ')}` : undefined;
|
|
150
|
+
})(),
|
|
151
|
+
tool: 'public-export-test-run', repo, flags, json: flags.rest.includes('--json'),
|
|
152
|
+
installPwa: paths.some((arg) => arg.includes('apps/pwa')),
|
|
153
|
+
remoteArgv: ['bun', 'scripts/public-export-test-run.ts', '--local', ...relativizeArgs(flags.rest, repo)],
|
|
154
|
+
runLocal: () => run(flags.rest),
|
|
155
|
+
});
|
|
156
|
+
})();
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import {
|
|
2
|
+
import { cutChecklistGate, listChecklist, summarizeChecklist } from '../../src/release-loop/checklist.js';
|
|
3
3
|
import * as store from '../../src/release-loop/feature-store.js';
|
|
4
4
|
import { getSchedule } from '../../src/release-loop/release-schedule.js';
|
|
5
5
|
import { debug } from '../../src/debug/log.js';
|
|
6
|
+
import { prereleaseKind } from './release-version.js';
|
|
6
7
|
import { emitNodeResult, errorResult, readGraphContext, type GraphContext } from './node-verdict.js';
|
|
7
8
|
|
|
9
|
+
/** RELEASE-REHEARSAL-RC: a prerelease rehearses the pipeline; it must not judge or carry the stable version's cells. */
|
|
10
|
+
export function prereleaseChecklistSkip(context: GraphContext) {
|
|
11
|
+
const version = context.input.version;
|
|
12
|
+
if (prereleaseKind(version) === null) return null;
|
|
13
|
+
debug.log('release-loop.checklist-gate', 'skipped', { version, reason: 'prerelease' });
|
|
14
|
+
return { outcome: 'ok' as const, verdict: 'pass' as const, summary: `checklist gate skipped — ${version} is a prerelease rehearsal`, skipped: 'prerelease' };
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
export function runChecklistGate(context: GraphContext = readGraphContext(), now: Date = new Date()) {
|
|
9
18
|
const version = context.input.version;
|
|
10
19
|
const data = listChecklist(version);
|
|
11
20
|
const schedule = getSchedule(version);
|
|
12
21
|
const deadline = schedule?.landBy;
|
|
13
|
-
const
|
|
14
|
-
const autoMoved = overdue ? data.items.filter((item) => item.status === 'yellow' && item.priority !== 'P0' && item.disposition === undefined).map((item) => item.id) : [];
|
|
15
|
-
const gate = checklistGate(version);
|
|
16
|
-
const p0 = data.items.filter((item) => item.status === 'yellow' && item.priority === 'P0').map((item) => item.id);
|
|
17
|
-
gate.undecided = gate.undecided.filter((id) => !autoMoved.includes(id) && !p0.includes(id));
|
|
18
|
-
gate.moved = gate.moved.filter((id) => !p0.includes(id)).concat(autoMoved);
|
|
19
|
-
gate.knownIssues = gate.knownIssues.filter((item) => !p0.includes(item.id));
|
|
20
|
-
gate.blocked.push(...p0.filter((id) => !gate.blocked.includes(id)));
|
|
21
|
-
gate.ok = gate.red.length === 0 && gate.undecided.length === 0 && gate.blocked.length === 0;
|
|
22
|
+
const { autoMoved, ...gate } = cutChecklistGate(version, deadline, now);
|
|
22
23
|
const [major, minor, patch] = version.split('.').map(Number);
|
|
23
24
|
const next = `${major}.${minor}.${patch! + 1}`;
|
|
24
25
|
const carried: string[] = [];
|
|
@@ -65,7 +66,8 @@ export function runChecklistGate(context: GraphContext = readGraphContext(), now
|
|
|
65
66
|
|
|
66
67
|
if (import.meta.main) {
|
|
67
68
|
try {
|
|
68
|
-
const
|
|
69
|
+
const context = readGraphContext();
|
|
70
|
+
const result = prereleaseChecklistSkip(context) ?? runChecklistGate(context);
|
|
69
71
|
emitNodeResult(result);
|
|
70
72
|
process.exitCode = result.outcome === 'ok' ? 0 : 1;
|
|
71
73
|
} catch (error) {
|
|
@@ -3,6 +3,7 @@ import { mkdtempSync, rmSync } from 'node:fs';
|
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
5
|
import { debug } from '../../src/debug/log.js';
|
|
6
|
+
import { isReleaseVersion } from './release-version.js';
|
|
6
7
|
|
|
7
8
|
export interface CutBranchOptions { version: string; base: string; pick: string[]; append?: boolean; dryRun?: boolean; repoRoot?: string; log?: (line: string) => void }
|
|
8
9
|
|
|
@@ -26,7 +27,7 @@ export function cutReleaseBranch(opts: CutBranchOptions): { branch: string; comm
|
|
|
26
27
|
const report = (event: 'created' | 'refused' | 'conflict' | 'appended', reason?: string, commit?: string, extra?: Record<string, string | string[]>) =>
|
|
27
28
|
debug.log('release-loop.cut-branch', event, { version: opts.version, branch, ...(commit ? { commit } : {}), ...(reason ? { reason } : {}), ...extra });
|
|
28
29
|
try {
|
|
29
|
-
if (
|
|
30
|
+
if (!isReleaseVersion(opts.version)) throw new Error(`invalid release version: ${opts.version}`);
|
|
30
31
|
if (!opts.pick.length) throw new Error('at least one --pick commit is required');
|
|
31
32
|
for (const sha of [opts.base, ...opts.pick]) if (!/^[0-9a-f]{7,40}$/i.test(sha)) throw new Error(`invalid commit SHA: ${sha}`);
|
|
32
33
|
git(repo, ['fetch', 'origin', 'main']);
|
|
@@ -25,8 +25,10 @@ function requiredGit(args: string[]): string {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function versionParts(value: string): number[] | null {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// A prerelease (0.2.18-rc.0) is placed by its base version — earlier stable tags are its baseline.
|
|
29
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-(?:rc|alpha|beta)\.\d+)?$/.exec(value);
|
|
30
|
+
if (!match) return null;
|
|
31
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
function compareVersions(a: number[], b: number[]): number {
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { existsSync, mkdtempSync, rmSync } from 'node:fs';
|
|
2
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
3
|
+
import { effectiveInstanceRoot, releaseLedgerRoot } from '../../src/instance/resolve.js';
|
|
4
|
+
import { listChecklist } from '../../src/release-loop/checklist.js';
|
|
5
|
+
import type { ReleaseManifest } from '../../src/release-loop/manifest.js';
|
|
6
|
+
import { recordDocsFollow, type DocsFollowItem } from './release-docs.js';
|
|
3
7
|
import { dirname, join } from 'node:path';
|
|
4
8
|
import { tmpdir } from 'node:os';
|
|
5
9
|
import { errorResult, finishNode, nodeOutput, readGraphContext, runCommand, type CommandRunner } from './node-verdict.js';
|
|
10
|
+
import { prereleaseKind } from './release-version.js';
|
|
6
11
|
|
|
7
|
-
export function runDocsLand(run: CommandRunner = runCommand) {
|
|
12
|
+
export function runDocsLand(run: CommandRunner = runCommand, stateRoot = effectiveInstanceRoot(), ledgerRoot = releaseLedgerRoot()) {
|
|
8
13
|
const context = readGraphContext();
|
|
9
14
|
const version = context.input.version;
|
|
10
15
|
if (context.outputs.publish?.outcome !== 'ok') throw new Error('publish must succeed before docs land');
|
|
@@ -14,6 +19,37 @@ export function runDocsLand(run: CommandRunner = runCommand) {
|
|
|
14
19
|
const worktree = nodeOutput(context, 'docs', 'worktree');
|
|
15
20
|
const scratch = dirname(worktree);
|
|
16
21
|
if (dirname(scratch) !== tmpdir() || !/^release-docs-[A-Za-z0-9]+$/.test(scratch.split('/').at(-1) ?? '') || worktree !== join(scratch, 'tree')) throw new Error('invalid docs worktree');
|
|
22
|
+
const recordFollow = (deployed: boolean) => {
|
|
23
|
+
const releaseDir = join(stateRoot, 'release', version);
|
|
24
|
+
const manifest: ReleaseManifest = JSON.parse(readFileSync(join(releaseDir, 'manifest.json'), 'utf8'));
|
|
25
|
+
if (manifest.version !== version || !Array.isArray(manifest.in)) throw new Error('docs-follow manifest/version mismatch');
|
|
26
|
+
// MK attestations are independent of generated notes; missing proof is unmeasured.
|
|
27
|
+
const evidencePath = join(releaseDir, 'docs-follow-evidence.json');
|
|
28
|
+
// 증거 파일이 깨졌으면 «증거 없음»으로 센다 — 보고서·MK 대기열은 그래도 남긴다(미측정으로).
|
|
29
|
+
let raw: unknown = { items: [] };
|
|
30
|
+
try { if (existsSync(evidencePath)) raw = JSON.parse(readFileSync(evidencePath, 'utf8')); }
|
|
31
|
+
catch (error) { process.stderr.write(`docs-follow evidence unreadable — counted as no evidence: ${String(error)}\n`); }
|
|
32
|
+
const valid = !!raw && typeof raw === 'object' && 'items' in raw && Array.isArray((raw as { items: unknown }).items);
|
|
33
|
+
if (!valid) process.stderr.write('docs-follow evidence malformed — counted as no evidence\n');
|
|
34
|
+
// 항목도 하나씩 본다 — 객체이고 문자열 id 가 있는 것만 증거로 센다(나머지는 «증거 없음»).
|
|
35
|
+
const evidence: { items: unknown[] } = { items: valid ? (raw as { items: unknown[] }).items
|
|
36
|
+
.filter((item) => !!item && typeof item === 'object' && typeof (item as { id?: unknown }).id === 'string') : [] };
|
|
37
|
+
const checklist = listChecklist(version, ledgerRoot);
|
|
38
|
+
const attestations = new Map((evidence.items as DocsFollowItem[]).map((item) => [item.id, item]));
|
|
39
|
+
const items = checklist.items.map((item) => ({ ...attestations.get(item.id), id: item.id, status: item.status,
|
|
40
|
+
...(deployed ? {} : { docs: undefined }) }));
|
|
41
|
+
const publicationFile = join(ledgerRoot, 'release', version, 'release.json');
|
|
42
|
+
const publication: unknown = existsSync(publicationFile) ? JSON.parse(readFileSync(publicationFile, 'utf8')) : null;
|
|
43
|
+
const publishedAt = publication && typeof publication === 'object' && 'publishedAt' in publication
|
|
44
|
+
&& typeof publication.publishedAt === 'string' ? publication.publishedAt : undefined;
|
|
45
|
+
const follow = recordDocsFollow(stateRoot, manifest, items, new Date(), publishedAt);
|
|
46
|
+
return { pending: follow.pending.length, unassessed: follow.unassessed.length, ratio: follow.ratio, verdict: follow.verdict };
|
|
47
|
+
};
|
|
48
|
+
// The deployment error is authoritative even if the follow-up ledger cannot be written.
|
|
49
|
+
const recordFailure = () => {
|
|
50
|
+
try { return recordFollow(false); }
|
|
51
|
+
catch (error) { process.stderr.write(`docs-follow recording failed: ${String(error)}\n`); return undefined; }
|
|
52
|
+
};
|
|
17
53
|
// 재시도 멱등: 노트가 이미 main 에 있으면(앞 판에서 머지됐고 배포만 실패) 착지를 건너뛰고 배포부터.
|
|
18
54
|
const before = run('git', ['fetch', 'origin', 'main']);
|
|
19
55
|
if (before.status !== 0) throw new Error(`docs landing fetch failed: ${before.stderr}`);
|
|
@@ -22,16 +58,20 @@ export function runDocsLand(run: CommandRunner = runCommand) {
|
|
|
22
58
|
if (!existsSync(worktree)) throw new Error(`docs worktree missing and notes not on main: ${worktree}`);
|
|
23
59
|
const land = run('bun', ['bin/elanous.mjs', 'pr', 'land', '--cwd', worktree]);
|
|
24
60
|
if (land.stderr) process.stderr.write(land.stderr);
|
|
25
|
-
if (land.status === 1) return { outcome: 'fail' as const, verdict: 'fail' as const, summary: `docs land failed: ${land.stderr || land.stdout}
|
|
61
|
+
if (land.status === 1) return { outcome: 'fail' as const, verdict: 'fail' as const, summary: `docs land failed: ${land.stderr || land.stdout}`, docsFollow: recordFailure() };
|
|
26
62
|
if (land.status !== 0 || !land.stdout.includes('✓ merge:')) throw new Error(`docs land incomplete (rc=${land.status}): ${land.stderr || land.stdout}`);
|
|
27
63
|
}
|
|
64
|
+
// 착지(병합)는 이미 끝났다 — 이 뒤의 모든 실패는 후속 기록을 시도하고 원래 오류를 그대로 던진다.
|
|
28
65
|
const fetch = run('git', ['fetch', 'origin', 'main']);
|
|
29
|
-
if (fetch.status !== 0) throw new Error(`docs landing fetch failed: ${fetch.stderr}`);
|
|
66
|
+
if (fetch.status !== 0) { recordFailure(); throw new Error(`docs landing fetch failed: ${fetch.stderr}`); }
|
|
30
67
|
const merged = run('git', ['rev-parse', 'origin/main']);
|
|
31
|
-
if (merged.status !== 0 || !/^[0-9a-f]{40}\s*$/i.test(merged.stdout)) throw new Error(`merged main commit unavailable: ${merged.stderr}`);
|
|
32
|
-
|
|
68
|
+
if (merged.status !== 0 || !/^[0-9a-f]{40}\s*$/i.test(merged.stdout)) { recordFailure(); throw new Error(`merged main commit unavailable: ${merged.stderr}`); }
|
|
69
|
+
let deployDir: string;
|
|
70
|
+
try { deployDir = mkdtempSync(join(tmpdir(), 'release-docs-deploy-')); }
|
|
71
|
+
catch (error) { recordFailure(); throw error; }
|
|
33
72
|
const deployTree = join(deployDir, 'tree');
|
|
34
73
|
let added = false;
|
|
74
|
+
let deployed = false;
|
|
35
75
|
try {
|
|
36
76
|
const add = run('git', ['worktree', 'add', '--detach', deployTree, merged.stdout.trim()]);
|
|
37
77
|
if (add.status !== 0) throw new Error(`docs deploy worktree failed: ${add.stderr}`);
|
|
@@ -40,26 +80,52 @@ export function runDocsLand(run: CommandRunner = runCommand) {
|
|
|
40
80
|
if (install.status !== 0) throw new Error(`docs deploy install failed: ${install.stderr}`);
|
|
41
81
|
const deploy = run('bun', ['website/scripts/deploy-pages.ts', '--remote', 'node-b', '--yes'], deployTree);
|
|
42
82
|
if (deploy.stderr) process.stderr.write(deploy.stderr);
|
|
43
|
-
if (deploy.status === 1) return { outcome: 'fail' as const, verdict: 'fail' as const,
|
|
83
|
+
if (deploy.status === 1) return { outcome: 'fail' as const, verdict: 'fail' as const,
|
|
84
|
+
summary: `docs deploy failed: ${deploy.stderr || deploy.stdout}`, docsFollow: recordFailure() };
|
|
44
85
|
if (deploy.status !== 0) throw new Error(`docs deploy incomplete (rc=${deploy.status}): ${deploy.stderr || deploy.stdout}`);
|
|
86
|
+
deployed = true;
|
|
45
87
|
if (existsSync(worktree)) {
|
|
46
88
|
const remove = run('git', ['worktree', 'remove', '--force', worktree]);
|
|
47
89
|
if (remove.status !== 0) throw new Error(`docs worktree cleanup failed: ${remove.stderr}`);
|
|
48
90
|
}
|
|
49
91
|
rmSync(scratch, { recursive: true, force: true });
|
|
50
|
-
|
|
92
|
+
// 배포 성공 — 후속 기록 실패(manifest 없음 등)는 결과를 «실패»로 바꾸지 않는다. 알리고 넘어간다.
|
|
93
|
+
let follow: ReturnType<typeof recordFollow> | undefined;
|
|
94
|
+
try { follow = recordFollow(true); }
|
|
95
|
+
catch (error) { process.stderr.write(`docs-follow recording failed after deploy: ${String(error)}\n`); }
|
|
96
|
+
return { outcome: 'ok' as const, verdict: 'pass' as const,
|
|
97
|
+
summary: `docs landed and deployed v${version}${follow ? ` · docs-follow pending=${follow.pending} unassessed=${follow.unassessed}` : ' · docs-follow not recorded'}`,
|
|
98
|
+
docsFollow: follow };
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (!deployed) recordFailure();
|
|
101
|
+
else {
|
|
102
|
+
// 배포는 됐는데 정리에서 실패 — 보고서·MK 대기열은 «배포됨»으로 남기고 원래 오류를 보존한다.
|
|
103
|
+
try { recordFollow(true); }
|
|
104
|
+
catch (recordError) { process.stderr.write(`docs-follow recording failed: ${String(recordError)}\n`); }
|
|
105
|
+
}
|
|
106
|
+
throw error;
|
|
51
107
|
} finally {
|
|
52
108
|
try {
|
|
53
109
|
if (added) {
|
|
54
110
|
const remove = run('git', ['worktree', 'remove', '--force', deployTree]);
|
|
55
|
-
|
|
111
|
+
// 배포 임시 트리 정리 실패는 앞선 결과(반환·원래 오류)를 덮지 않는다 — 알리고 넘어간다.
|
|
112
|
+
if (remove.status !== 0) process.stderr.write(`docs deploy cleanup failed (kept the original result): ${remove.stderr}\n`);
|
|
56
113
|
}
|
|
57
|
-
} finally {
|
|
114
|
+
} finally {
|
|
115
|
+
try { rmSync(deployDir, { recursive: true, force: true }); }
|
|
116
|
+
catch (error) { process.stderr.write(`docs deploy temp cleanup failed (kept the original result): ${String(error)}\n`); }
|
|
117
|
+
}
|
|
58
118
|
}
|
|
59
119
|
}
|
|
60
120
|
|
|
61
121
|
if (import.meta.main) {
|
|
62
122
|
let version = '';
|
|
63
|
-
try {
|
|
123
|
+
try {
|
|
124
|
+
version = readGraphContext().input.version;
|
|
125
|
+
// RELEASE-REHEARSAL-RC: a prerelease never lands docs on main or upgrades the operating hosts.
|
|
126
|
+
process.exitCode = finishNode('docs-land', version, prereleaseKind(version) !== null
|
|
127
|
+
? { outcome: 'ok', verdict: 'pass', summary: `docs-land skipped — ${version} is a prerelease`, skipped: 'prerelease' }
|
|
128
|
+
: runDocsLand());
|
|
129
|
+
}
|
|
64
130
|
catch (error) { process.exitCode = finishNode('docs-land', version, errorResult(error)); }
|
|
65
131
|
}
|
|
@@ -72,7 +72,7 @@ export function runDocs(
|
|
|
72
72
|
}
|
|
73
73
|
throw new Error(`unknown argument: ${arg}`);
|
|
74
74
|
}
|
|
75
|
-
if (!version || !/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)
|
|
75
|
+
if (!version || !/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:rc|alpha|beta)\.(?:0|[1-9]\d*))?$/.test(version)) {
|
|
76
76
|
throw new Error('version required (--version or ELANOUS_GRAPH_CONTEXT.input.version): x.y.z');
|
|
77
77
|
}
|
|
78
78
|
root = resolve(root);
|
|
@@ -54,6 +54,8 @@ export interface GateResult {
|
|
|
54
54
|
knownEnvCleared: string[];
|
|
55
55
|
stalledEnv: Array<{ file: string; reason: 'no-output' | 'isolated-timeout'; local: 'passed' | 'failed' | 'timeout' }>;
|
|
56
56
|
baselineSource?: 'ledger' | 'instance' | 'cut-logs' | 'swept';
|
|
57
|
+
/** GATE-BASELINE-CACHE — isolated baseline files answered from the ledger (hits) or run now (misses). */
|
|
58
|
+
baselineCache?: { hits: number; misses: number };
|
|
57
59
|
durationMs: number;
|
|
58
60
|
error?: string;
|
|
59
61
|
stalledShards?: Array<{ shard: number; files: string[]; reason: 'incomplete' | 'no-output' | 'job-failed' | 'unattributed'; lastFile?: string; summaryFailures?: number; namedFailures?: number; detail?: string }>;
|
|
@@ -583,6 +585,61 @@ function fileOf(id: string): string {
|
|
|
583
585
|
return file;
|
|
584
586
|
}
|
|
585
587
|
|
|
588
|
+
/**
|
|
589
|
+
* GATE-BASELINE-CACHE (0.2.18) — the isolated `bun run test:deterministic <file>` result of one test file at one commit,
|
|
590
|
+
* kept in the machine ledger so a later gate does not run it again (0.2.17: the failing files were re-run on the previous
|
|
591
|
+
* release commit although that release had already measured them). Cache format harvested from draft #24500.
|
|
592
|
+
* `host` is where the result was measured (`local` or the ssh host) — a result from another host is a miss.
|
|
593
|
+
* Timeouts are never stored: an environment stall is measured again, not remembered.
|
|
594
|
+
*/
|
|
595
|
+
export interface BaselineFileResult { commit: string; file: string; host: string; failures: string[]; errors: string[]; missing: boolean }
|
|
596
|
+
export const baselineCachePath = (ledger: string, commit: string) => join(ledger, 'gate-baseline-cache', `${commit}.jsonl`);
|
|
597
|
+
function validFileResult(value: unknown, commit: string): value is BaselineFileResult {
|
|
598
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
599
|
+
const row = value as Partial<BaselineFileResult>;
|
|
600
|
+
if (row.commit !== commit || typeof row.file !== 'string' || typeof row.host !== 'string' || !row.host.trim()
|
|
601
|
+
|| typeof row.missing !== 'boolean' || !Array.isArray(row.failures) || !Array.isArray(row.errors)
|
|
602
|
+
|| (row.missing && (row.failures.length || row.errors.length))) return false;
|
|
603
|
+
try {
|
|
604
|
+
fileOf(`${row.file} > case`);
|
|
605
|
+
return [...row.failures, ...row.errors].every((id) => typeof id === 'string' && fileOf(id) === row.file);
|
|
606
|
+
} catch { return false; }
|
|
607
|
+
}
|
|
608
|
+
/** A missing cache file is empty; any unreadable or invalid row discards the whole file — it is never read as a pass. */
|
|
609
|
+
export function readBaselineFileCache(ledger: string, commit: string): Map<string, BaselineFileResult> {
|
|
610
|
+
const entries = new Map<string, BaselineFileResult>();
|
|
611
|
+
if (!sha.test(commit)) return entries;
|
|
612
|
+
const path = baselineCachePath(ledger, commit);
|
|
613
|
+
if (!existsSync(path)) return entries;
|
|
614
|
+
try {
|
|
615
|
+
// The writer always ends with one newline and never writes an empty line — anything else is a damaged file.
|
|
616
|
+
const text = readFileSync(path, 'utf8');
|
|
617
|
+
if (!text.endsWith('\n')) throw new Error('missing final newline');
|
|
618
|
+
for (const line of text.slice(0, -1).split('\n')) {
|
|
619
|
+
if (!line.trim()) throw new Error('empty line');
|
|
620
|
+
const row: unknown = JSON.parse(line);
|
|
621
|
+
if (!validFileResult(row, commit)) throw new Error('invalid row');
|
|
622
|
+
entries.set(`${row.file}\0${row.host}`, row);
|
|
623
|
+
}
|
|
624
|
+
} catch (error) {
|
|
625
|
+
debug.log('release.gate', 'baseline-cache-corrupt', { path, error: String(error) });
|
|
626
|
+
entries.clear();
|
|
627
|
+
}
|
|
628
|
+
return entries;
|
|
629
|
+
}
|
|
630
|
+
export function saveBaselineFileCache(ledger: string, commit: string, rows: BaselineFileResult[]): void {
|
|
631
|
+
if (!rows.length || !sha.test(commit)) return;
|
|
632
|
+
const entries = readBaselineFileCache(ledger, commit);
|
|
633
|
+
for (const row of rows) entries.set(`${row.file}\0${row.host}`, row);
|
|
634
|
+
const path = baselineCachePath(ledger, commit);
|
|
635
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
636
|
+
const temp = join(dirname(path), `.gate-baseline-${process.pid}-${randomUUID()}.tmp`);
|
|
637
|
+
try {
|
|
638
|
+
writeFileSync(temp, [...entries.values()].map((row) => JSON.stringify(row)).join('\n') + '\n', { mode: 0o600 });
|
|
639
|
+
renameSync(temp, path);
|
|
640
|
+
} finally { if (existsSync(temp)) rmSync(temp); }
|
|
641
|
+
}
|
|
642
|
+
|
|
586
643
|
export async function judgeGate(opts: GateOptions, runner: GateRunner = createGateRunner(opts.repo ?? process.cwd(), opts.remote)): Promise<GateResult> {
|
|
587
644
|
const start = Date.now();
|
|
588
645
|
const explicitConfig = getElanousConfigDirOverride();
|
|
@@ -743,7 +800,23 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
743
800
|
const diff = diffFailures(countedCut.counted, countedBaseline.counted);
|
|
744
801
|
result.fixed = diff.fixed.length;
|
|
745
802
|
result.preexisting = diff.common.length;
|
|
746
|
-
|
|
803
|
+
// GATE-BASELINE-CACHE — only the files whose new failures reproduce in isolation reach the baseline, and each
|
|
804
|
+
// (commit, file, host) is run at most once across gates: this gate's cut results also seed the next gate's baseline.
|
|
805
|
+
const cacheHost = opts.remote ?? 'local';
|
|
806
|
+
const newFailureFiles = new Set(diff.newFailures.map(fileOf));
|
|
807
|
+
const baseCache = newFailureFiles.size ? readBaselineFileCache(ledger, baseSha) : new Map<string, BaselineFileResult>();
|
|
808
|
+
const cacheStats = { hits: 0, misses: 0 };
|
|
809
|
+
const remember = (row: BaselineFileResult) => {
|
|
810
|
+
// Only a row the reader would accept is stored; an unstorable result skips the cache and leaves the verdict alone.
|
|
811
|
+
const { commit, file } = row;
|
|
812
|
+
if (!validFileResult(row, commit)) {
|
|
813
|
+
debug.log('release.gate', 'baseline-cache-skip', { commit, file });
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
try { saveBaselineFileCache(ledger, row.commit, [row]); }
|
|
817
|
+
catch (error) { debug.log('release.gate', 'baseline-cache-write-failed', { commit: row.commit, file: row.file, error: String(error) }); }
|
|
818
|
+
};
|
|
819
|
+
for (const file of newFailureFiles) {
|
|
747
820
|
// 0.2.15: the cut re-check of a new-failure file (InsidePage.test.tsx · headless Chrome) hung for good — limit it like the host re-run.
|
|
748
821
|
const cutCheck = cutHostFailed.has(file) ? undefined : await runner.command('bun', ['run', 'test:deterministic', asPath(file)], cutTree, GATE_ISOLATED_TIMEOUT_MS);
|
|
749
822
|
if (cutCheck?.timedOut) {
|
|
@@ -754,30 +827,42 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
754
827
|
const isolatedCut = cutCheck === undefined
|
|
755
828
|
? { failures: cut.failures.filter((id) => fileOf(id) === file), errors: cut.errors.filter((id) => fileOf(id) === file) }
|
|
756
829
|
: failuresOf(cutCheck, `cut isolated ${file}`);
|
|
830
|
+
if (cutCheck !== undefined) remember({ commit: opts.commit, file, host: cacheHost, failures: isolatedCut.failures, errors: isolatedCut.errors, missing: false });
|
|
757
831
|
const reproduced = new Set([...isolatedCut.failures, ...isolatedCut.errors]);
|
|
758
832
|
const candidates = diff.newFailures.filter((id) => fileOf(id) === file && reproduced.has(id));
|
|
759
833
|
if (candidates.length === 0) continue;
|
|
760
|
-
const baselineTree = await getBaseTree();
|
|
761
|
-
const previous = await runner.command('bun', ['run', 'test:deterministic', asPath(file)], baselineTree, GATE_ISOLATED_TIMEOUT_MS);
|
|
762
|
-
if (previous.timedOut) {
|
|
763
|
-
debug.log('release-loop.gate', 'isolated-timeout', { file, label: 'baseline isolated', limitMs: GATE_ISOLATED_TIMEOUT_MS });
|
|
764
|
-
result.stalledEnv.push({ file, reason: 'isolated-timeout', local: 'timeout' });
|
|
765
|
-
continue;
|
|
766
|
-
}
|
|
767
834
|
let oldFailures: Set<string>;
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
oldFailures = new Set();
|
|
835
|
+
const hit = baseCache.get(`${file}\0${cacheHost}`);
|
|
836
|
+
if (hit) {
|
|
837
|
+
cacheStats.hits++;
|
|
838
|
+
oldFailures = new Set([...hit.failures, ...hit.errors]);
|
|
772
839
|
} else {
|
|
773
|
-
|
|
774
|
-
|
|
840
|
+
cacheStats.misses++;
|
|
841
|
+
const baselineTree = await getBaseTree();
|
|
842
|
+
const previous = await runner.command('bun', ['run', 'test:deterministic', asPath(file)], baselineTree, GATE_ISOLATED_TIMEOUT_MS);
|
|
843
|
+
if (previous.timedOut) {
|
|
844
|
+
debug.log('release-loop.gate', 'isolated-timeout', { file, label: 'baseline isolated', limitMs: GATE_ISOLATED_TIMEOUT_MS });
|
|
845
|
+
result.stalledEnv.push({ file, reason: 'isolated-timeout', local: 'timeout' });
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
if (/No tests found|had no matches/i.test(previous.output) && previous.rc === 1) {
|
|
849
|
+
const lookup = await runner.command('git', ['ls-tree', '--name-only', baseSha, '--', file], opts.remote ? baselineTree : repo);
|
|
850
|
+
if (lookup.rc !== 0 || lookup.output.trim()) throw new Error(`baseline isolated run incomplete: ${file}`);
|
|
851
|
+
oldFailures = new Set();
|
|
852
|
+
remember({ commit: baseSha, file, host: cacheHost, failures: [], errors: [], missing: true });
|
|
853
|
+
} else {
|
|
854
|
+
const prior = failuresOf(previous, `baseline isolated ${file}`);
|
|
855
|
+
oldFailures = new Set([...prior.failures, ...prior.errors]);
|
|
856
|
+
remember({ commit: baseSha, file, host: cacheHost, failures: prior.failures, errors: prior.errors, missing: false });
|
|
857
|
+
}
|
|
775
858
|
}
|
|
776
859
|
for (const id of candidates) {
|
|
777
860
|
if (oldFailures.has(id)) result.preexisting++;
|
|
778
861
|
else result.introduced.push(id);
|
|
779
862
|
}
|
|
780
863
|
}
|
|
864
|
+
result.baselineCache = cacheStats;
|
|
865
|
+
debug.log('release.gate', 'baseline-cache', { hits: cacheStats.hits, misses: cacheStats.misses, commit: baseSha });
|
|
781
866
|
result.outcome = result.introduced.length ? 'regression' : 'ok';
|
|
782
867
|
} catch (error) {
|
|
783
868
|
result.error = error instanceof Error ? error.message : String(error);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { isReleaseVersion } from './release-version.js';
|
|
3
4
|
import { readFileSync } from 'node:fs';
|
|
4
5
|
import { debug } from '../../src/debug/log.js';
|
|
5
6
|
|
|
@@ -17,7 +18,7 @@ export function readGraphContext(env: NodeJS.ProcessEnv = process.env): GraphCon
|
|
|
17
18
|
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('invalid graph context');
|
|
18
19
|
const context = value as Record<string, unknown>;
|
|
19
20
|
const input = context.input as Record<string, unknown> | undefined;
|
|
20
|
-
if (!input || typeof input.version !== 'string' ||
|
|
21
|
+
if (!input || typeof input.version !== 'string' || !isReleaseVersion(input.version)
|
|
21
22
|
|| typeof input.previousVersion !== 'string' || !/^\d+\.\d+\.\d+$/.test(input.previousVersion)) throw new Error('version and previousVersion required');
|
|
22
23
|
return { input: input as GraphContext['input'], outputs: context.outputs && typeof context.outputs === 'object' ? context.outputs as GraphContext['outputs'] : {} };
|
|
23
24
|
}
|
|
@@ -7,6 +7,7 @@ import { debug } from '../../src/debug/log.js';
|
|
|
7
7
|
import { sendOutbound } from '../../src/domains/outbound-alert.js';
|
|
8
8
|
import { effectiveInstanceRoot, prodInstanceRoot } from '../../src/instance/resolve.js';
|
|
9
9
|
import { emitNodeResult, readGraphContext, type GraphContext } from './node-verdict.js';
|
|
10
|
+
import { prereleaseKind } from './release-version.js';
|
|
10
11
|
|
|
11
12
|
type Result = { outcome: 'ok' | 'fail'; verdict: 'pass' | 'fail'; summary: string; npm?: 'published' | 'already-published' | 'staged-not-visible'; humanAction?: 'npm-support' };
|
|
12
13
|
type Exec = (command: string, args: string[], env?: NodeJS.ProcessEnv) => { status: number | null; stdout: string; stderr: string };
|
|
@@ -47,8 +48,11 @@ function metadata(value: unknown): { name?: string; version?: string } {
|
|
|
47
48
|
export async function runNpmPublish(context: GraphContext = readGraphContext(), deps: NpmPublishDeps = {}): Promise<Result> {
|
|
48
49
|
const version = context.input.version;
|
|
49
50
|
let token = '';
|
|
51
|
+
// RELEASE-REHEARSAL-RC: a prerelease goes to `next` so `npm i elanous` never resolves to a rehearsal build.
|
|
52
|
+
let distTag = 'latest';
|
|
50
53
|
try {
|
|
51
54
|
if (context.outputs.publish?.outcome !== 'ok') throw new Error('publish must succeed before npm publish');
|
|
55
|
+
distTag = prereleaseKind(version) === null ? 'latest' : 'next';
|
|
52
56
|
const stateDir = deps.stateDir ?? effectiveInstanceRoot();
|
|
53
57
|
// REL3 — the archive prepare actually built (its output) wins over this process's universe: a resumed run can
|
|
54
58
|
// resolve another universe (10-01 0.2.7: prepared in the pilot test universe, resumed in production).
|
|
@@ -67,14 +71,14 @@ export async function runNpmPublish(context: GraphContext = readGraphContext(),
|
|
|
67
71
|
const [manifest, tags] = await Promise.all([readRegistry(`${PACKAGE_URL}/${version}`), readRegistry(PACKAGE_URL)]);
|
|
68
72
|
if (manifest.status !== 200 && manifest.status !== 404) throw new Error(`npm registry version lookup failed (${manifest.status})`);
|
|
69
73
|
if (tags.status !== 200) throw new Error(`npm registry tags lookup failed (${tags.status})`);
|
|
70
|
-
const
|
|
71
|
-
? (tags.body as { 'dist-tags'?:
|
|
74
|
+
const tagged = tags.body && typeof tags.body === 'object' && 'dist-tags' in tags.body
|
|
75
|
+
? (tags.body as { 'dist-tags'?: Record<string, unknown> })['dist-tags']?.[distTag] : undefined;
|
|
72
76
|
if (manifest.status === 200 && (metadata(manifest.body).name !== 'elanous' || metadata(manifest.body).version !== version))
|
|
73
77
|
throw new Error('npm registry version metadata mismatch');
|
|
74
|
-
return { visible: manifest.status === 200, tagged: manifest.status === 200 &&
|
|
78
|
+
return { visible: manifest.status === 200, tagged: manifest.status === 200 && tagged === version };
|
|
75
79
|
};
|
|
76
80
|
const before = await inspect();
|
|
77
|
-
if (before.tagged) return { outcome: 'ok', verdict: 'pass', npm: 'already-published', summary: `npm ${version} already published with
|
|
81
|
+
if (before.tagged) return { outcome: 'ok', verdict: 'pass', npm: 'already-published', summary: `npm ${version} already published with ${distTag} tag` };
|
|
78
82
|
if (!before.visible) {
|
|
79
83
|
// The npm token is a machine secret (like the GitHub App key): input → this universe → the production root.
|
|
80
84
|
const tokenFile = context.input.npmTokenFile ?? [join(stateDir, 'secrets', 'npm-token'), join(deps.productionRoot ?? prodInstanceRoot(), 'secrets', 'npm-token')].find((p) => existsSync(p)) ?? join(stateDir, 'secrets', 'npm-token');
|
|
@@ -86,7 +90,7 @@ export async function runNpmPublish(context: GraphContext = readGraphContext(),
|
|
|
86
90
|
try {
|
|
87
91
|
const npmrc = join(dir, 'npmrc');
|
|
88
92
|
writeFileSync(npmrc, `//registry.npmjs.org/:_authToken=${token}\n`, { mode: 0o600, flag: 'wx' });
|
|
89
|
-
published = run('npm', ['publish', candidate, '--ignore-scripts', '--tag',
|
|
93
|
+
published = run('npm', ['publish', candidate, '--ignore-scripts', '--tag', distTag, '--access', 'public'], { ...process.env, NPM_CONFIG_USERCONFIG: npmrc });
|
|
90
94
|
} finally { rmSync(dir, { recursive: true, force: true }); }
|
|
91
95
|
const output = `${published.stdout}\n${published.stderr}`;
|
|
92
96
|
const staged = published.status !== 0 && /\bE409\b/i.test(output) && /previously staged/i.test(output);
|
|
@@ -96,10 +100,10 @@ export async function runNpmPublish(context: GraphContext = readGraphContext(),
|
|
|
96
100
|
let last = before;
|
|
97
101
|
for (let i = 0; i <= polls; i++) {
|
|
98
102
|
last = await inspect();
|
|
99
|
-
if (last.tagged) return { outcome: 'ok', verdict: 'pass', npm: before.visible ? 'already-published' : 'published', summary: `npm ${version} visible with
|
|
103
|
+
if (last.tagged) return { outcome: 'ok', verdict: 'pass', npm: before.visible ? 'already-published' : 'published', summary: `npm ${version} visible with ${distTag} tag` };
|
|
100
104
|
if (i < polls) await wait(Math.min(30_000, waitMinutes * 60_000 - i * 30_000));
|
|
101
105
|
}
|
|
102
|
-
if (last.visible) throw new Error(`npm ${version} visible but dist-tags
|
|
106
|
+
if (last.visible) throw new Error(`npm ${version} visible but dist-tags.${distTag} did not reach ${version} within ${waitMinutes} minutes`);
|
|
103
107
|
try { (deps.log ?? ((v, minutes) => debug.log('release-loop.npm', 'staged-not-visible', { version: v, waitedMinutes: minutes })))(version, waitMinutes); } catch { /* observation must not mask npm's staged state */ }
|
|
104
108
|
try { (deps.alert ?? sendOutbound)(`npm ${version} 가 스테이징에 멈춤 — 계정 소유자 확인 필요`, 'alert'); } catch { /* alert failure must not block docs */ }
|
|
105
109
|
return { outcome: 'ok', verdict: 'pass', npm: 'staged-not-visible', humanAction: 'npm-support', summary: 'npm 스테이징 — 레지스트리에 아직 없음' };
|
|
@@ -4,6 +4,7 @@ import { homedir, tmpdir } from 'node:os';
|
|
|
4
4
|
import { join, isAbsolute } from 'node:path';
|
|
5
5
|
import { debug } from '../../src/debug/log.js';
|
|
6
6
|
import { errorResult, finishNode, lastResult, nodeOutput, readGraphContext, runCommand, type CommandResult, type CommandRunner } from './node-verdict.js';
|
|
7
|
+
import { prereleaseKind } from './release-version.js';
|
|
7
8
|
|
|
8
9
|
interface HostResult { host: string; ok: boolean; before: string; after: string; error?: string; skipped?: string; hooks?: string; restart?: 'no-service' }
|
|
9
10
|
/** Where the internal feed is packed from: a checkout of the release tag, never the run's working tree. */
|
|
@@ -199,6 +200,12 @@ export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeD
|
|
|
199
200
|
|
|
200
201
|
if (import.meta.main) {
|
|
201
202
|
let version = '';
|
|
202
|
-
try {
|
|
203
|
+
try {
|
|
204
|
+
version = readGraphContext().input.version;
|
|
205
|
+
// RELEASE-REHEARSAL-RC: a prerelease never lands docs on main or upgrades the operating hosts.
|
|
206
|
+
process.exitCode = finishNode('ops-upgrade', version, prereleaseKind(version) !== null
|
|
207
|
+
? { outcome: 'ok', verdict: 'pass', summary: `ops-upgrade skipped — ${version} is a prerelease`, skipped: 'prerelease' }
|
|
208
|
+
: runOpsUpgrade());
|
|
209
|
+
}
|
|
203
210
|
catch (error) { process.exitCode = finishNode('ops-upgrade', version, errorResult(error)); }
|
|
204
211
|
}
|