elanous 0.2.14 → 0.2.16
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/chunks/{1307-6e7e0377c1db0bdc.js → 1307-b46d45b705e8c9ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-3cb52e5ea8fc2fa5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-c1e20d7d338713f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-482c5dbde699947c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-8c2d9526164f5d09.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-e6b84ab07d3567d5.js → page-a0e2a6d1bfb52fc5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c53598cfce6156f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-13a63953f53c6a23.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-5d3c8429d04f3831.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-3193bdc0bbab047a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-6107ca8ddd5e3bdd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-dce686e582c23937.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-160089123a40dd91.js → page-1a61c7ca1ceb3ae7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-51f811fa119308b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-4167348049166852.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-de52e8fdf9095f7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-ca3952cd78a83ca2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-e7e10cd3386222e5.js → page-5b042736aa113756.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-1ebfe049c640e171.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-57924393e3e4e539.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-0f5cd57ca928f42b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-abe8e3b74bea1ffc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-d28573d6792b4275.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-58043db78ba519ae.js → page-59f03fda76c03d22.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-357750dfb91bf960.js → page-d751de5f8062ff5e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-fe1759f94003b32b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d64c984e855c8c68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-9afdee66b820c76e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-f8da5dc6f3752276.js → page-1ea64acadd704ace.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-3544f154c87b43ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +1 -0
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
- package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
- package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +15 -15
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +15 -15
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +15 -15
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +15 -15
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +15 -15
- package/apps/pwa/out/ceo/index.html +1 -0
- package/apps/pwa/out/ceo/index.txt +33 -0
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +15 -15
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +15 -15
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +15 -15
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +15 -15
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +15 -15
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +15 -15
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +15 -15
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +15 -15
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +15 -15
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +15 -15
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +15 -15
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +15 -15
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +15 -15
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +15 -15
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +15 -15
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +15 -15
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +15 -15
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +15 -15
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +15 -15
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +15 -15
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +16 -16
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +15 -15
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +15 -15
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +15 -15
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +15 -15
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +15 -15
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +15 -15
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +15 -15
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +15 -15
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +15 -15
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +15 -15
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +15 -15
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +15 -15
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +15 -15
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +15 -15
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +15 -15
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +15 -15
- package/graphs/doc-gen/doc-gen.yaml +24 -0
- package/graphs/doc-gen/recipes.yaml +3 -0
- package/graphs/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/orchestrator/orchestrator.yaml +7 -0
- package/graphs/orchestrator/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -2
- package/graphs/steward/recipes.yaml +2 -1
- package/package.json +1 -1
- package/scripts/brand/check.ts +7 -5
- package/scripts/ci-public-export-import-gate.ts +154 -0
- package/scripts/ci-test-interference-gate.ts +5 -1
- package/scripts/ci-typecheck-changed.ts +82 -31
- package/scripts/docs/doc-rot.ts +164 -0
- package/scripts/docs/generate-graph-anatomy.ts +437 -0
- package/scripts/docs-cli-check.ts +4 -4
- package/scripts/expose-rubric-suggest.ts +18 -12
- package/scripts/goals/goal-census.ts +343 -0
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +30 -3
- package/scripts/marketing/generate-ax-brief.ts +140 -0
- package/scripts/marketing/generate-feature-map.ts +118 -0
- package/scripts/measure/author-section-contribution.ts +84 -0
- package/scripts/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/cut-branch.ts +43 -13
- package/scripts/release-loop/gate-node.ts +82 -14
- package/scripts/release-loop/vault-note-node.ts +257 -8
- package/scripts/release-story/draft.ts +57 -9
- package/scripts/rhythm/daily.ts +263 -76
- package/scripts/rhythm/monthly.ts +97 -0
- package/scripts/rhythm/weekly.ts +30 -9
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/src/acp/client.ts +17 -2
- package/src/agent-env/profile.ts +161 -0
- package/src/agent-mission/driver.ts +51 -1
- package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
- package/src/agent-mission/supervisor-metrics.ts +218 -0
- package/src/agent-substrate/pr-reviewer.ts +2 -0
- package/src/ask-user-question/pending-questions.ts +5 -0
- package/src/auth/github-app-token.ts +112 -13
- package/src/autopilot/build/llm-conflict-merge.ts +32 -6
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/briefing/brief-items.ts +276 -0
- package/src/budget/usage-outcomes.ts +204 -0
- package/src/claims/claims-render.ts +14 -5
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/brief-cli.ts +73 -0
- package/src/cli/decisions-cli.ts +9 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/intake-cli.ts +46 -17
- package/src/cli/know-cli.ts +20 -0
- package/src/cli/logs-cli.ts +43 -1
- package/src/cli/pod-cli.ts +2 -0
- package/src/cli/pr-cli.ts +80 -12
- package/src/cli/release-cli.ts +66 -29
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/usage-cli.ts +26 -0
- package/src/context-bus/context-now-public.ts +16 -6
- package/src/context-bus/context-now.ts +3 -1
- package/src/context-bus/coord-events.ts +6 -4
- package/src/decisions/decision-cards.ts +33 -7
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/discord-self-message.ts +9 -0
- package/src/domains/alert-audit.ts +271 -0
- package/src/domains/core-tools.ts +4 -3
- package/src/domains/ops-facts-tool.ts +31 -1
- package/src/flow-loop/split.ts +24 -0
- package/src/harness/harness-cli-command.ts +147 -17
- package/src/harness/harness-incidents.ts +2 -0
- package/src/harness/harness-pod-dispatch.ts +67 -8
- package/src/harness/harness-queue.ts +380 -50
- package/src/harness/harness-seams.ts +13 -3
- package/src/harness/helper-repair.ts +218 -0
- package/src/harness/helper-scan.ts +27 -4
- package/src/harness/launch-stamp.ts +236 -0
- package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
- package/src/harness/ledger-write-guard.ts +35 -0
- package/src/harness/nested-elanous-policy.ts +114 -0
- package/src/harness/pod-memory-policy.ts +15 -0
- package/src/harness/repository-name.ts +55 -0
- package/src/hq/fence-audit.ts +90 -0
- package/src/hq/hq.ts +70 -6
- package/src/hq/lease.ts +9 -7
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +31 -0
- package/src/hq/transfer.ts +185 -0
- package/src/index.ts +189 -13
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +65 -21
- package/src/intake-plane/front-classifier.ts +6 -0
- package/src/intake-plane/items.ts +3 -2
- package/src/intake-plane/lens.ts +177 -0
- package/src/intake-plane/route.ts +145 -1
- package/src/knowledge/know-find.ts +105 -0
- package/src/llm.ts +75 -19
- package/src/loops/budget.ts +36 -5
- package/src/loops/checker.ts +2 -2
- package/src/loops/orchestrator/author-depth.ts +65 -9
- package/src/loops/orchestrator/author-input-check.ts +8 -3
- package/src/loops/orchestrator/author-ledger.ts +3 -1
- package/src/loops/orchestrator/finish-rate.ts +14 -3
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +107 -29
- package/src/loops/orchestrator/traffic.ts +117 -11
- package/src/loops/registry.ts +142 -61
- package/src/maturity/bot-slash-catalog.ts +11 -2
- package/src/maturity/feature-maturity.ts +4 -3
- package/src/maturity/tui-slash-availability.ts +40 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/http-server.ts +28 -14
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/schedules-read.ts +13 -0
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/oauth/overload-failover-launch.ts +75 -0
- package/src/oauth/overload-failover.ts +137 -0
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +6 -3
- package/src/release-loop/checklist.ts +19 -5
- package/src/release-loop/feature-store.ts +59 -31
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +98 -29
- package/src/seat-dispatch/seat-ask.ts +33 -6
- package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
- package/src/seat-dispatch/seat-request-ledger.ts +133 -0
- package/src/seat-loop/seat-loop.ts +215 -12
- package/src/self-dev/ask-launch-flow.ts +22 -1
- package/src/self-dev/authored-goal-trace.ts +64 -0
- package/src/self-dev/dev-cli.ts +19 -1
- package/src/self-dev/dev-pipeline.ts +14 -9
- package/src/self-dev/draft-sweep.ts +90 -10
- package/src/self-dev/draft-triage-rules.ts +25 -8
- package/src/self-dev/entrance-registry.ts +10 -6
- package/src/self-dev/old-door.ts +73 -0
- package/src/self-dev/orchestrate.ts +15 -1
- package/src/self-dev/review-provider-fallback.ts +6 -1
- package/src/self-dev/run-supervisor.ts +49 -2
- package/src/self-implement/app-api-budget.ts +166 -0
- package/src/self-implement/budget-gate.ts +7 -5
- package/src/self-implement/follow-up-goals.ts +289 -0
- package/src/self-implement/frozen-merges.ts +3 -2
- package/src/self-implement/goal-author.ts +50 -6
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/heal-intake.ts +120 -2
- package/src/self-implement/heal-triage.ts +44 -0
- package/src/self-implement/host-regate.ts +9 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +325 -47
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +42 -7
- package/src/self-implement/run-ledger.ts +6 -0
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +45 -16
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/session-runtime/index.ts +2 -0
- package/src/session-runtime/retry-api.ts +87 -0
- package/src/session-runtime/retry-policy.ts +107 -6
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +20 -2
- package/src/task-cards/card-cli.ts +25 -0
- package/src/task-cards/card-store.ts +5 -3
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
- package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
- package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +70 -9
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +166 -46
- package/src/task-orchestrator/surfaces/self-implement.ts +7 -1
- package/src/telegram-commands.ts +5 -0
- package/src/telegram.ts +72 -4
- package/src/user-config.ts +64 -7
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +0 -1
- package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → rv9cierV0jSOvDBm1g0XB}/_ssgManifest.js +0 -0
|
@@ -19,8 +19,38 @@ function publishedTime(value: string): { date: string; short: string; full: stri
|
|
|
19
19
|
return { date, short: `${parts.month}-${parts.day} ${parts.hour}:${parts.minute}`, full: `${date} ${parts.hour}:${parts.minute} KST` };
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// 구분자(` — ` · ` · ` · 줄바꿈 · 。)는 짝이 맞는 «…» 밖에서만 자른다.
|
|
22
23
|
function firstClause(title: string): string {
|
|
23
|
-
|
|
24
|
+
let depth = 0;
|
|
25
|
+
let end = title.length;
|
|
26
|
+
// Balanced «…», (…), […], {…}, “…” and "…" spans are kept whole — a separator inside them never ends the clause.
|
|
27
|
+
const closers: Record<string, string> = { '«': '»', '(': ')', '[': ']', '{': '}', '“': '”', '‘': '’' };
|
|
28
|
+
const stack: string[] = [];
|
|
29
|
+
let quoted = false;
|
|
30
|
+
let single = false;
|
|
31
|
+
for (let i = 0; i < title.length; i++) {
|
|
32
|
+
const char = title[i]!;
|
|
33
|
+
if (char === '"' && (quoted || title.indexOf('"', i + 1) > i)) { quoted = !quoted; continue; }
|
|
34
|
+
// ASCII ' quotes like ", except a word-internal apostrophe (doesn't).
|
|
35
|
+
if (char === "'" && !(/\p{L}/u.test(title[i - 1] ?? '') && /\p{L}/u.test(title[i + 1] ?? ''))
|
|
36
|
+
&& (single || title.indexOf("'", i + 1) > i)) { single = !single; continue; }
|
|
37
|
+
if (single) continue;
|
|
38
|
+
if (closers[char] && title.indexOf(closers[char]!, i) > i) { stack.push(closers[char]!); continue; }
|
|
39
|
+
if (stack.length && char === stack.at(-1)) { stack.pop(); continue; }
|
|
40
|
+
if (quoted || stack.length) continue;
|
|
41
|
+
if (char === '«' && title.indexOf('»', i) > i) depth++;
|
|
42
|
+
else if (char === '»' && depth > 0) depth--;
|
|
43
|
+
else if (depth === 0 && (char === '\n' || char === '。' || (/[—–·]/.test(char) && /\s/.test(title[i - 1] ?? '') && /\s/.test(title[i + 1] ?? '')))) { end = i; break; }
|
|
44
|
+
}
|
|
45
|
+
const clause = title.slice(0, end);
|
|
46
|
+
const unmatched = new Set<number>();
|
|
47
|
+
const open: number[] = [];
|
|
48
|
+
for (let i = 0; i < clause.length; i++) {
|
|
49
|
+
if (clause[i] === '«') open.push(i);
|
|
50
|
+
else if (clause[i] === '»') { if (open.length) open.pop(); else unmatched.add(i); }
|
|
51
|
+
}
|
|
52
|
+
for (const i of open) unmatched.add(i);
|
|
53
|
+
return clause.split('').filter((_, i) => !unmatched.has(i)).join('').trim().replace(/\s+/g, ' ');
|
|
24
54
|
}
|
|
25
55
|
|
|
26
56
|
function changeCount(draft: string): number {
|
|
@@ -68,7 +98,207 @@ function releaseHistory(context: GraphContext, root: string): string[] {
|
|
|
68
98
|
return history;
|
|
69
99
|
}
|
|
70
100
|
|
|
101
|
+
type LedgerNode = { nodeId: string; ok: boolean; output?: string };
|
|
102
|
+
type LedgerRun = { graphId: string; runId: string; startedAt?: string; input?: { version?: string }; nodes?: LedgerNode[] };
|
|
103
|
+
|
|
104
|
+
function publishedPreviousRun(dir: string, root: string, previousVersion: string, current: LedgerRun): LedgerRun | undefined {
|
|
105
|
+
const record: unknown = JSON.parse(readFileSync(join(root, 'release', previousVersion, 'release.json'), 'utf8'));
|
|
106
|
+
if (!record || typeof record !== 'object') return undefined;
|
|
107
|
+
const published = record as Record<string, unknown>;
|
|
108
|
+
if (published.version !== previousVersion || published.tag !== `v${previousVersion}`
|
|
109
|
+
|| typeof published.sourceCommit !== 'string' || !/^[0-9a-f]{8,40}$/i.test(published.sourceCommit)
|
|
110
|
+
|| typeof published.publishedAt !== 'string' || !Number.isFinite(Date.parse(published.publishedAt))) return undefined;
|
|
111
|
+
const publishedAt = published.publishedAt as string;
|
|
112
|
+
const candidates = readdirSync(dir).filter((file) => /^[\w.-]+\.json$/.test(file) && !file.endsWith('.decision.json')).flatMap((file) => {
|
|
113
|
+
try {
|
|
114
|
+
const value = JSON.parse(readFileSync(join(dir, file), 'utf8')) as LedgerRun;
|
|
115
|
+
if (value.graphId !== 'release-loop' || value.input?.version !== previousVersion || !Array.isArray(value.nodes)
|
|
116
|
+
|| !value.nodes.some((node) => node.nodeId === 'publish' && node.ok)
|
|
117
|
+
|| ledgerOutput(value, 'publish')?.tag !== published.tag
|
|
118
|
+
|| ledgerOutput(value, 'version-release')?.commit !== published.sourceCommit
|
|
119
|
+
|| (typeof value.startedAt === 'string' && value.startedAt > publishedAt)
|
|
120
|
+
|| (typeof current.startedAt === 'string' && typeof value.startedAt === 'string' && value.startedAt > current.startedAt)) return [];
|
|
121
|
+
return [value];
|
|
122
|
+
} catch { return []; }
|
|
123
|
+
});
|
|
124
|
+
// Repeated publications of the same tag/commit cannot identify which run supplied the published count.
|
|
125
|
+
if (new Set(candidates.map((entry) => ledgerOutput(entry, 'known-issues')?.count)).size > 1) return undefined;
|
|
126
|
+
return candidates[0];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function ledgerOutput(run: LedgerRun, nodeId: string): Record<string, unknown> | undefined {
|
|
130
|
+
const node = run.nodes?.filter((entry) => entry.nodeId === nodeId && entry.ok).at(-1);
|
|
131
|
+
try {
|
|
132
|
+
const raw = node?.output?.trim().split(/\r?\n/).at(-1);
|
|
133
|
+
const value: unknown = JSON.parse(raw ?? '');
|
|
134
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined;
|
|
135
|
+
} catch { return undefined; }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function stabilityLine(context: GraphContext, root: string): string {
|
|
139
|
+
const identity = context as GraphContext & { graphId?: string; runId?: string };
|
|
140
|
+
const fields: Record<string, string | number> = { introduced: '?', preexisting: '?', recut: '?', hosts: '?', knownIssuesDelta: '?' };
|
|
141
|
+
const unreadable: string[] = [];
|
|
142
|
+
let line: string;
|
|
143
|
+
try {
|
|
144
|
+
if (identity.graphId !== 'release-loop' || !identity.runId || !/^[\w.-]+$/.test(identity.runId)) throw new Error('런 식별자 없음');
|
|
145
|
+
const dir = join(root, 'graph-runs', 'release-loop');
|
|
146
|
+
const ledgerPath = join(dir, `${identity.runId}.json`);
|
|
147
|
+
if (!existsSync(ledgerPath)) throw new Error(`런 원장 없음(${identity.runId}.json)`);
|
|
148
|
+
const run = JSON.parse(readFileSync(ledgerPath, 'utf8')) as LedgerRun;
|
|
149
|
+
if (run.graphId !== 'release-loop' || run.runId !== identity.runId || run.input?.version !== context.input.version || !Array.isArray(run.nodes)) throw new Error('런 원장 식별 불일치');
|
|
150
|
+
const gate = ledgerOutput(run, 'gate');
|
|
151
|
+
if (Array.isArray(gate?.introduced)) fields.introduced = gate.introduced.length;
|
|
152
|
+
else unreadable.push('gate.introduced');
|
|
153
|
+
if (typeof gate?.preexisting === 'number' && Number.isSafeInteger(gate.preexisting) && gate.preexisting >= 0) fields.preexisting = gate.preexisting;
|
|
154
|
+
else unreadable.push('gate.preexisting');
|
|
155
|
+
// The cutoff manifest contains baseline/cutoff SHAs and included landings, not the cut branch's pick count.
|
|
156
|
+
// Neither a different SHA nor the number of included landings establishes whether there was a recut.
|
|
157
|
+
unreadable.push('cutoff.pick count not recorded');
|
|
158
|
+
// ops-upgrade is downstream of vault-note in the release graph; its host results do not exist yet.
|
|
159
|
+
unreadable.push('ops-upgrade.hosts not yet available');
|
|
160
|
+
const current = ledgerOutput(run, 'known-issues');
|
|
161
|
+
try {
|
|
162
|
+
// Same baseline as the landing section: the newest earlier version that was actually published (an unpublished, folded version is skipped).
|
|
163
|
+
const previousVersion = previousPublishedCut(root, context.input.version)?.version;
|
|
164
|
+
const previousRun = previousVersion ? publishedPreviousRun(dir, root, previousVersion, run) : undefined;
|
|
165
|
+
const previous = previousRun ? ledgerOutput(previousRun, 'known-issues')?.count : undefined;
|
|
166
|
+
if (typeof current?.count === 'number' && Number.isSafeInteger(current.count) && current.count >= 0
|
|
167
|
+
&& typeof previous === 'number' && Number.isSafeInteger(previous) && previous >= 0) {
|
|
168
|
+
const delta = current.count - previous;
|
|
169
|
+
fields.knownIssuesDelta = `${delta >= 0 ? '+' : ''}${delta}`;
|
|
170
|
+
} else unreadable.push('known-issues.count(previous/current)');
|
|
171
|
+
} catch { unreadable.push('known-issues.count(previous/current)'); }
|
|
172
|
+
line = `안정: 게이트 introduced ${fields.introduced} · preexisting ${fields.preexisting} · 재컷 ${fields.recut} · 호스트 판올림 성공 ${fields.hosts} · known-issues 직전 판 대비 ${fields.knownIssuesDelta}`;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
175
|
+
unreadable.push(reason);
|
|
176
|
+
line = `안정: 측정 불가: ${reason.replace(/[\r\n]+/g, ' ')}`;
|
|
177
|
+
}
|
|
178
|
+
try { debug.log('release-loop.vault-note', 'stability-line', { fields, unreadable }); } catch { /* Observation cannot block publication. */ }
|
|
179
|
+
return line;
|
|
180
|
+
}
|
|
181
|
+
|
|
71
182
|
interface ReleaseRecord { version: string; tag: string; sourceCommit: string; publishedAt: string; publicRepo?: string }
|
|
183
|
+
|
|
184
|
+
const landingTopics = [
|
|
185
|
+
{ name: '조직·루프', match: /조직|좌석|스튜어드|본부|운영|자율|루프|미션|cron|schedule|seat|steward|loop|mission|hq\b|ops\b/i },
|
|
186
|
+
{ name: '하니스', match: /하니스|골|게이트|검증|테스트|리뷰|실행기|워크트리|harness|goal|gate|review|test|worktree|self-implement|self-dev|graph/i },
|
|
187
|
+
{ name: '채널·PWA', match: /채널|화면|텔레그램|디스코드|메신저|발송|알림|모바일|음성|channel|pwa|telegram|discord|outbound|tui|web|ios|android/i },
|
|
188
|
+
{ name: '지식·교훈', match: /지식|기억|교훈|관측|로그|리서치|검색|문서|knowledge|memory|lesson|research|observation|logs|docs|manual/i },
|
|
189
|
+
{ name: '공개·브랜드', match: /공개|브랜드|발행|릴리스|홍보|광고|마케팅|public|brand|release|publish|marketing|npm/i },
|
|
190
|
+
{ name: '그 밖', match: /[\s\S]*/ },
|
|
191
|
+
] as const;
|
|
192
|
+
|
|
193
|
+
type LandingLine = { title: string; line?: string; sha: string; prNumber?: number };
|
|
194
|
+
|
|
195
|
+
function balanced(text: string): boolean {
|
|
196
|
+
const pairs: Record<string, string> = { '»': '«', ')': '(', ']': '[', '}': '{', '”': '“', '’': '‘' };
|
|
197
|
+
const stack: string[] = [];
|
|
198
|
+
let quote = false;
|
|
199
|
+
let single = false;
|
|
200
|
+
const chars = [...text];
|
|
201
|
+
for (const [i, char] of chars.entries()) {
|
|
202
|
+
if (char === '"') { quote = !quote; continue; }
|
|
203
|
+
// ASCII ' pairs like ", except a word-internal apostrophe (doesn't) which never opens a quote.
|
|
204
|
+
if (char === "'") {
|
|
205
|
+
if (!(/\p{L}/u.test(chars[i - 1] ?? '') && /\p{L}/u.test(chars[i + 1] ?? ''))) single = !single;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (char in pairs) { if (stack.pop() !== pairs[char]) return false; }
|
|
209
|
+
else if ('«([{“‘'.includes(char)) stack.push(char);
|
|
210
|
+
}
|
|
211
|
+
return !quote && !single && stack.length === 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function landingClause(text: string): string {
|
|
215
|
+
// A candidate with broken punctuation is not repaired by deleting its unmatched character.
|
|
216
|
+
if (!balanced(text)) return '';
|
|
217
|
+
// release/next.md lines lead with their kind («feat — …»); the headline is the change, not the kind.
|
|
218
|
+
const trimmed = text.replace(/^-\s+/, '');
|
|
219
|
+
// Release bookkeeping (version bumps, «release 0.2.14» notes) is never this release's headline (0.2.15 real note).
|
|
220
|
+
if (/^(?:version|release)\s*:/i.test(trimmed) || /^(?:docs|chore)(?:\([^)]*\))?\s*:\s*release\s+v?\d/i.test(trimmed)) return '';
|
|
221
|
+
// Commit subjects lead with a conventional prefix («docs(marketing): …»); the headline is the change itself.
|
|
222
|
+
const body = trimmed.replace(/^(feat|fix|internal|docs|test|perf|refactor|chore|ops|security|breaking)\s+[—–-]\s+/i, '')
|
|
223
|
+
.replace(/^[a-z]+(?:\([^)]*\))?!?:\s+/i, '');
|
|
224
|
+
const clause = shortClause(firstClause(body));
|
|
225
|
+
return balanced(clause) ? clause : '';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** A headline clause longer than 60 characters is cut at its first comma/semicolon, else at a word boundary with «…». */
|
|
229
|
+
function shortClause(clause: string): string {
|
|
230
|
+
const chars = [...clause];
|
|
231
|
+
if (chars.length <= 60) return clause;
|
|
232
|
+
const head = chars.slice(0, 60).join('');
|
|
233
|
+
// The first comma/semicolon after a minimal 10-character lead ends the clause.
|
|
234
|
+
const stop = head.slice(10).search(/[,;,;]/);
|
|
235
|
+
if (stop >= 0) return head.slice(0, 10 + stop);
|
|
236
|
+
const space = head.lastIndexOf(' ');
|
|
237
|
+
return `${(space >= 20 ? head.slice(0, space) : head).trimEnd()}…`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function versionKey(value: string): number[] | null {
|
|
241
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(value);
|
|
242
|
+
return match ? match.slice(1).map(Number) : null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Newest release below `version` with a readable release.json; null when there is none to compare against. */
|
|
246
|
+
function previousPublishedCut(root: string, version: string): { version: string; sourceCommit: string } | null {
|
|
247
|
+
const current = versionKey(version);
|
|
248
|
+
if (!current) return null;
|
|
249
|
+
const compare = (a: number[], b: number[]) => a[0]! - b[0]! || a[1]! - b[1]! || a[2]! - b[2]!;
|
|
250
|
+
const earlier = readdirSync(join(root, 'release'))
|
|
251
|
+
.map((name) => ({ name, key: versionKey(name) }))
|
|
252
|
+
.filter((row): row is { name: string; key: number[] } => !!row.key && compare(row.key, current) < 0)
|
|
253
|
+
.sort((a, b) => compare(b.key, a.key));
|
|
254
|
+
for (const { name } of earlier) {
|
|
255
|
+
const path = join(root, 'release', name, 'release.json');
|
|
256
|
+
if (!existsSync(path)) continue;
|
|
257
|
+
const record = JSON.parse(readFileSync(path, 'utf8')) as { sourceCommit?: unknown };
|
|
258
|
+
if (typeof record.sourceCommit !== 'string' || !/^[0-9a-f]{40}$/i.test(record.sourceCommit)) throw new Error(`v${name} release.json sourceCommit invalid`);
|
|
259
|
+
return { version: name, sourceCommit: record.sourceCommit };
|
|
260
|
+
}
|
|
261
|
+
// Earlier versions exist but none was published: the «직전 판 컷» claim cannot be checked, so it is not presented as settled.
|
|
262
|
+
if (earlier.length) throw new Error(`직전 판 컷 확인 못 함 — 이전 판 ${earlier.length}개에 release.json 없음`);
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function landingsAtCut(root: string, version: string, cut: string): { groups: Array<{ name: string; lines: LandingLine[] }>; error?: string } {
|
|
267
|
+
try {
|
|
268
|
+
const manifest = JSON.parse(readFileSync(join(root, 'release', version, 'manifest.json'), 'utf8')) as {
|
|
269
|
+
version?: unknown; baseline?: { sha?: unknown }; cutoff?: { sha?: unknown }; in?: unknown;
|
|
270
|
+
};
|
|
271
|
+
if (manifest.version !== version || !/^[0-9a-f]{8,40}$/i.test(String(manifest.baseline?.sha ?? ''))
|
|
272
|
+
|| manifest.cutoff?.sha !== cut || !Array.isArray(manifest.in)
|
|
273
|
+
|| !manifest.in.every((entry: unknown) => {
|
|
274
|
+
const item = entry as Partial<LandingLine> | null;
|
|
275
|
+
return item && typeof item.title === 'string' && typeof item.sha === 'string'
|
|
276
|
+
&& (item.line === undefined || typeof item.line === 'string')
|
|
277
|
+
&& (item.prNumber === undefined || Number.isInteger(item.prNumber));
|
|
278
|
+
})) throw new Error('manifest.json 판·직전 컷·이번 컷 또는 착지 줄 불일치');
|
|
279
|
+
// The «직전 판 컷» claim is checked against the newest earlier published release, when one exists.
|
|
280
|
+
const previous = previousPublishedCut(root, version);
|
|
281
|
+
if (previous && previous.sourceCommit.toLowerCase() !== String(manifest.baseline!.sha).toLowerCase())
|
|
282
|
+
throw new Error(`직전 판 컷 불일치 — manifest baseline ${String(manifest.baseline!.sha).slice(0, 8)} ≠ v${previous.version} 컷 ${previous.sourceCommit.slice(0, 8)}`);
|
|
283
|
+
const groups = landingTopics.map(({ name }) => ({ name, lines: [] as LandingLine[] }));
|
|
284
|
+
for (const entry of manifest.in as LandingLine[]) {
|
|
285
|
+
const text = entry.line?.trim() || entry.title.trim();
|
|
286
|
+
const topic = landingTopics.findIndex(({ match }) => match.test(text));
|
|
287
|
+
groups[topic]!.lines.push(entry);
|
|
288
|
+
}
|
|
289
|
+
return { groups: groups.filter((group) => group.lines.length) };
|
|
290
|
+
} catch (error) {
|
|
291
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
292
|
+
return { groups: [], error: `manifest.json: ${reason.replace(/^manifest\.json\s*/, '').replace(/\s+/g, ' ')}` };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function representative(groups: Array<{ name: string; lines: LandingLine[] }>, fallback: string): string {
|
|
297
|
+
const candidates = [...groups].sort((a, b) => b.lines.length - a.lines.length);
|
|
298
|
+
const lines = candidates.map(({ lines }) => lines.map((entry) => landingClause(entry.line?.trim() || entry.title)).find(Boolean))
|
|
299
|
+
.filter((line): line is string => !!line).slice(0, 3);
|
|
300
|
+
return lines.join(' · ') || fallback;
|
|
301
|
+
}
|
|
72
302
|
export interface VaultNoteDeps {
|
|
73
303
|
instanceRoot?: string;
|
|
74
304
|
productionRoot?: string;
|
|
@@ -104,12 +334,22 @@ export function runVaultNote(context: GraphContext = readGraphContext(), deps: V
|
|
|
104
334
|
const published = publishedTime(record.publishedAt);
|
|
105
335
|
const draft = readFileSync(join(root, 'release', version, 'prepared', 'notes-draft.md'), 'utf8');
|
|
106
336
|
const items = (deps.checklist ?? ((v) => listChecklist(v).items))(version).filter((item) => item.status === 'green');
|
|
107
|
-
const
|
|
337
|
+
const landings = landingsAtCut(root, version, record.sourceCommit);
|
|
338
|
+
const oneLine = representative(landings.groups, items.map((item) => landingClause(item.title)).filter(Boolean).slice(0, 3).join(' · ') || '착지 내용 확인 필요');
|
|
108
339
|
const location = process.env.ELANOUS_GRAPH_CONTEXT;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
:
|
|
112
|
-
|
|
340
|
+
let identity: { graphId?: string; runId?: string } = {};
|
|
341
|
+
if (location && !('runId' in context)) {
|
|
342
|
+
try { identity = JSON.parse(location.trimStart().startsWith('{') ? location : readFileSync(location, 'utf8')) as typeof identity; }
|
|
343
|
+
catch { /* Missing or malformed graph identity is reported by the stability line, not by publication. */ }
|
|
344
|
+
}
|
|
345
|
+
const runContext = { ...identity, ...context };
|
|
346
|
+
const stability = stabilityLine(runContext, root);
|
|
347
|
+
let history: string[];
|
|
348
|
+
try { history = releaseHistory(runContext, root); }
|
|
349
|
+
catch (error) {
|
|
350
|
+
history = [];
|
|
351
|
+
try { debug.log('release.vault-note', 'history-unreadable', { reason: error instanceof Error ? error.message : String(error) }); } catch { /* Observation cannot block publication. */ }
|
|
352
|
+
}
|
|
113
353
|
// OP 10-04 19:10 ①: group by feature family (cell id family: HQ-FENCE2 → HQ · LOOP-OBS1 → LOOP · K10i → K), not by seat.
|
|
114
354
|
const groups = new Map<string, typeof items>();
|
|
115
355
|
for (const item of items) {
|
|
@@ -125,6 +365,14 @@ export function runVaultNote(context: GraphContext = readGraphContext(), deps: V
|
|
|
125
365
|
'',
|
|
126
366
|
]),
|
|
127
367
|
]);
|
|
368
|
+
const landingSection = landings.error
|
|
369
|
+
? [`- 못 읽음 · ${landings.error}`]
|
|
370
|
+
: landings.groups.length
|
|
371
|
+
? landings.groups.flatMap(({ name, lines }) => [
|
|
372
|
+
`### ${name} (${lines.length}건)`, '',
|
|
373
|
+
...lines.map((entry) => `- ${entry.line?.trim() || entry.title.trim()}${entry.prNumber ? ` (#${entry.prNumber})` : ''}`), '',
|
|
374
|
+
])
|
|
375
|
+
: ['- 착지 0건'];
|
|
128
376
|
const target = join(vault, directory);
|
|
129
377
|
const name = `엘라누스 v${version} (${published.date})`;
|
|
130
378
|
// 대표 10-04 19:25: notes live under <major>.x/<major>.<minor>/ · the index stays at the top.
|
|
@@ -139,14 +387,15 @@ export function runVaultNote(context: GraphContext = readGraphContext(), deps: V
|
|
|
139
387
|
if (!rowExists && !header.test(current)) throw new Error(`index table header missing: ${indexPath}`);
|
|
140
388
|
mkdirSync(noteDir, { recursive: true });
|
|
141
389
|
const content = [
|
|
142
|
-
'---', `title:
|
|
390
|
+
'---', `title: ${JSON.stringify(`엘라누스 v${version} — ${oneLine}`)}`,
|
|
143
391
|
`version: ${version}`, `published: ${published.full}`, `cut: ${record.sourceCommit.slice(0, 8)}`,
|
|
144
392
|
'tags: [elanous, release]', 'links:', ` github: https://github.com/${publicRepo}/releases/tag/${record.tag}`,
|
|
145
393
|
` npm: elanous@${version}`, '---', '', `# 엘라누스 v${version} — ${oneLine}`, '',
|
|
146
394
|
'## 한 줄', '', oneLine, '',
|
|
147
395
|
'## 발행 경과', '', `- ${published.full} 발행`, ...history, '',
|
|
396
|
+
'## 이번 판 착지 (직전 판 컷 ~ 이번 판 컷)', '', ...landingSection,
|
|
148
397
|
'## 이제 할 수 있는 것', '', ...abilities, '## 숫자', '',
|
|
149
|
-
`- 변경 ${changeCount(draft)}건`, `- green 칸 ${items.length}개`, '',
|
|
398
|
+
`- 변경 ${changeCount(draft)}건`, `- green 칸 ${items.length}개`, stability, '',
|
|
150
399
|
].join('\n');
|
|
151
400
|
// A handwritten note at the old flat location (e.g. OP's v0.2.13 sample) counts as existing — never duplicate it.
|
|
152
401
|
let existed = existsSync(join(target, `${name}.md`));
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { join, resolve } from 'node:path';
|
|
4
|
-
import { checkBrand } from '../brand/check.js';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { BRAND_RULES_UNMEASURED, checkBrand } from '../brand/check.js';
|
|
5
5
|
import { ClaimsLedger } from '../../src/claims/claims-ledger.js';
|
|
6
6
|
import { debug } from '../../src/debug/log.js';
|
|
7
7
|
import { effectiveInstanceRoot } from '../../src/instance/resolve.js';
|
|
8
8
|
import { listChecklist, type ChecklistItem } from '../../src/release-loop/checklist.js';
|
|
9
|
+
import { appendSeatRequestRows, listSeatRequests, withSeatRequestLedgerLock } from '../../src/seat-dispatch/seat-request-ledger.js';
|
|
10
|
+
import { finishNode, readGraphContext, type GraphContext } from '../release-loop/node-verdict.js';
|
|
9
11
|
|
|
10
12
|
const repoRoot = resolve(import.meta.dir, '../..');
|
|
11
13
|
|
|
@@ -16,6 +18,7 @@ type DraftOptions = {
|
|
|
16
18
|
checklistRoot?: string;
|
|
17
19
|
stateDir?: string;
|
|
18
20
|
manualRoot?: string;
|
|
21
|
+
rulesPath?: string;
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
type DraftResult = {
|
|
@@ -27,6 +30,7 @@ type DraftResult = {
|
|
|
27
30
|
claims: number;
|
|
28
31
|
manualCandidates: number;
|
|
29
32
|
brandFindings: number;
|
|
33
|
+
brandUnmeasured?: string;
|
|
30
34
|
files: string[];
|
|
31
35
|
};
|
|
32
36
|
|
|
@@ -126,6 +130,7 @@ export function draftReleaseStory(options: DraftOptions): DraftResult {
|
|
|
126
130
|
const outDir = options.outDir ?? join(effectiveInstanceRoot(), 'release', version, 'story');
|
|
127
131
|
mkdirSync(outDir, { recursive: true });
|
|
128
132
|
let brandFindings = 0;
|
|
133
|
+
let brandUnmeasured: string | undefined;
|
|
129
134
|
const files: string[] = [];
|
|
130
135
|
for (const [name, body, scope] of [
|
|
131
136
|
['announcement.md', announcement, 'release-notes'],
|
|
@@ -134,20 +139,63 @@ export function draftReleaseStory(options: DraftOptions): DraftResult {
|
|
|
134
139
|
] as const) {
|
|
135
140
|
const file = join(outDir, name);
|
|
136
141
|
writeFileSync(file, body);
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
const checked = checkBrand(scope, [file], options.rulesPath);
|
|
143
|
+
if (checked.missing) {
|
|
144
|
+
brandUnmeasured = checked.message ?? BRAND_RULES_UNMEASURED;
|
|
145
|
+
writeFileSync(file, `> ⚠ ${brandUnmeasured}\n\n${body}`);
|
|
146
|
+
} else {
|
|
147
|
+
brandFindings += checked.findings.length;
|
|
148
|
+
if (checked.findings.length) writeFileSync(file, checked.findings.map(({ id, match }) => `> ⚠ 브랜드 규칙: ${id} «${match}»`).join('\n') + '\n\n' + body);
|
|
149
|
+
}
|
|
140
150
|
files.push(file);
|
|
141
151
|
}
|
|
142
152
|
const result: DraftResult = { version, status: 'drafted', userLines: userLines.length, internalDropped, greenCells: green.length,
|
|
143
|
-
claims: evidence.sentences.length, manualCandidates: candidates.length, brandFindings, files };
|
|
153
|
+
claims: evidence.sentences.length, manualCandidates: candidates.length, brandFindings, ...(brandUnmeasured ? { brandUnmeasured } : {}), files };
|
|
144
154
|
debug.log('release.story', 'drafted', { version, userLines: result.userLines, internalDropped, greenCells: result.greenCells,
|
|
145
|
-
claims: result.claims, manualCandidates: result.manualCandidates, brandFindings });
|
|
155
|
+
claims: result.claims, manualCandidates: result.manualCandidates, brandFindings, ...(brandUnmeasured ? { brandUnmeasured } : {}) });
|
|
146
156
|
return result;
|
|
147
157
|
}
|
|
148
158
|
|
|
149
|
-
|
|
159
|
+
export function runReleaseStory(context: GraphContext = readGraphContext(), draft: typeof draftReleaseStory = draftReleaseStory) {
|
|
160
|
+
const version = context.input.version;
|
|
150
161
|
try {
|
|
162
|
+
if (context.outputs.publish?.outcome !== 'ok' || context.outputs.verify?.outcome !== 'ok')
|
|
163
|
+
throw new Error('published release and verification required');
|
|
164
|
+
const result = draft({ version });
|
|
165
|
+
if (result.status === 'drafted') {
|
|
166
|
+
const root = effectiveInstanceRoot();
|
|
167
|
+
const requestsPath = join(root, 'seat-requests', 'requests.jsonl');
|
|
168
|
+
const key = `release-story:${version}`;
|
|
169
|
+
const storyDir = dirname(result.files[0]!);
|
|
170
|
+
withSeatRequestLedgerLock(requestsPath, () => {
|
|
171
|
+
if (listSeatRequests(root).some((row) => row.key === key)) return;
|
|
172
|
+
appendSeatRequestRows(requestsPath, [{ key, seat: 'MK', source: 'release-story', version, status: 'pending',
|
|
173
|
+
text: `${version} 공지 초안 준비됨: ${storyDir}`, queuedAt: new Date().toISOString() }]);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return { outcome: 'ok' as const, verdict: 'pass' as const, summary: result.status,
|
|
177
|
+
files: result.files, status: result.status };
|
|
178
|
+
} catch (error) {
|
|
179
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
180
|
+
debug.log('release.story', 'draft-failed', { version, reason });
|
|
181
|
+
return { outcome: 'fail' as const, verdict: 'fail' as const, summary: `release story failed: ${reason}`, reason };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (import.meta.main) {
|
|
186
|
+
if (process.argv.includes('--graph')) {
|
|
187
|
+
let version = '';
|
|
188
|
+
try {
|
|
189
|
+
if (process.argv.length !== 3) throw new Error('usage: bun scripts/release-story/draft.ts --graph');
|
|
190
|
+
const context = readGraphContext();
|
|
191
|
+
version = context.input.version;
|
|
192
|
+
process.exitCode = finishNode('release-story', version, runReleaseStory(context));
|
|
193
|
+
} catch (error) {
|
|
194
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
195
|
+
debug.log('release.story', 'draft-failed', { version, reason });
|
|
196
|
+
process.exitCode = finishNode('release-story', version, { outcome: 'fail', verdict: 'fail', summary: `release story failed: ${reason}`, reason });
|
|
197
|
+
}
|
|
198
|
+
} else try {
|
|
151
199
|
const args = process.argv.slice(2);
|
|
152
200
|
let version: string | undefined;
|
|
153
201
|
let nextPath: string | undefined;
|
|
@@ -164,7 +212,7 @@ if (import.meta.main) {
|
|
|
164
212
|
if (!version || (args.includes('--next') && !nextPath) || (args.includes('--out') && !outDir))
|
|
165
213
|
throw new Error('usage: bun scripts/release-story/draft.ts --version <v> [--next <path>] [--out <dir>] [--json]');
|
|
166
214
|
const result = draftReleaseStory({ version, ...(nextPath ? { nextPath } : {}), ...(outDir ? { outDir } : {}) });
|
|
167
|
-
console.log(json ? JSON.stringify(result) : result.status === 'drafted' ? `drafted: ${result.files.join(', ')}` : result.status);
|
|
215
|
+
console.log(json ? JSON.stringify(result) : result.status === 'drafted' ? `drafted: ${result.files.join(', ')}${result.brandUnmeasured ? ` · ${result.brandUnmeasured}` : ''}` : result.status);
|
|
168
216
|
} catch (error) {
|
|
169
217
|
console.error(error instanceof Error ? error.message : String(error));
|
|
170
218
|
process.exitCode = 1;
|