elanous 0.2.17 → 0.2.19
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/J5H7BvqgSI8DUjtRRcA_S/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{726.83532d69452ee24b.js → 1470.98a6ab61ccb4131c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9247-269e4464cc4b491c.js → 2375-e061bb24cd2b5cde.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{211-9c9090c59e2c5907.js → 2977-e38442e86289f1d3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → 2ea96958.e8c7e112de50b067.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +2 -0
- package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +5 -0
- package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +1 -0
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 4323.c5dfb6f503304775.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 4847.56f5bc454fbcc827.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 6062-73e68343a9814f6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{935.4ddaa99fc1d1126c.js → 7647.2fcd30d894d5ee30.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{f6231284-45726550aba08fca.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8870-9614a213b990b10b.js +25 -0
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/ae787e4c-e5b1ef00b2dbceca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-5b29d9b06ceadade.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-b70cd005d9169e25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +1 -0
- package/apps/pwa/out/_next/static/css/{e99e3cff94ced78a.css → 599369d853c61df7.css} +1 -1
- package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +1 -0
- package/apps/pwa/out/_next/static/css/d3427996da11e480.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/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 +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +25 -24
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +24 -23
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +28 -27
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/ops/guardian.yaml +26 -0
- package/graphs/ops/recipes.yaml +3 -0
- 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/ci-public-export-import-gate.ts +7 -2
- package/scripts/ci-typecheck-changed.ts +21 -6
- package/scripts/coord-channel-watch.sh +2 -2
- package/scripts/discord-seat-probe.ts +6 -3
- package/scripts/docs/generate-cli-anatomy.ts +162 -0
- package/scripts/goals/authoring-metrics.ts +73 -0
- package/scripts/landing-heal/must-fix-match.ts +7 -7
- package/scripts/landing-heal/run-mission.ts +3 -2
- package/scripts/lib/pch-regress-map.ts +23 -0
- package/scripts/morning-report.ts +18 -4
- package/scripts/pch-regress.ts +88 -0
- 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 +8 -1
- package/scripts/release-loop/docs-node.ts +1 -1
- package/scripts/release-loop/gate-diff.ts +18 -0
- package/scripts/release-loop/gate-node.ts +436 -59
- package/scripts/release-loop/gate-partial.ts +106 -0
- package/scripts/release-loop/gate-timing.ts +120 -0
- 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 +8 -1
- 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 +174 -0
- package/scripts/release-loop/shard-plan.ts +65 -0
- package/scripts/release-loop/unattended-release.ts +45 -7
- package/scripts/release-loop/version-node.ts +145 -40
- 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/scripts/video/scene-table.ts +159 -0
- package/src/acp/core-turn-bridge.ts +39 -2
- 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/away/presence.ts +40 -0
- package/src/away/release-watch.ts +255 -0
- package/src/briefing/brief-items.ts +86 -17
- package/src/chat/index.ts +2 -1
- package/src/chat/short-question.ts +3 -40
- package/src/cli/autopilot-cli.ts +30 -11
- package/src/cli/away-cli.ts +68 -0
- package/src/cli/brief-cli.ts +36 -2
- package/src/cli/freeze-cli.ts +3 -2
- package/src/cli/guardian-cli.ts +49 -0
- package/src/cli/intake-cli.ts +85 -7
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/notify-cli.ts +75 -0
- package/src/cli/pod-cli.ts +5 -1
- package/src/cli/provider-cli.ts +12 -2
- package/src/cli/pty-drive-cli.ts +14 -3
- package/src/cli/release-cli.ts +128 -19
- package/src/cli/setup-cli.ts +31 -1
- package/src/cli/tasks-cli.ts +403 -4
- 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-bus/context-now-public.ts +1 -1
- package/src/context-bus/context-now-surfaces.ts +16 -6
- package/src/context-bus/context-now.ts +97 -3
- package/src/core-turn/short-question.ts +100 -0
- package/src/dashboard/dashboard-status-lines.ts +24 -4
- package/src/dashboard/index.ts +50 -5
- package/src/dashboard/input/slash-executor.ts +6 -2
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +63 -36
- package/src/dashboard/slash-runtime/decide-slash.ts +60 -0
- package/src/dashboard/slash-runtime/help-from-registry.ts +21 -8
- package/src/dashboard/slash-runtime/help-groups.ts +44 -0
- package/src/dashboard/slash-runtime/model-choices.ts +125 -0
- package/src/discord-self-message.ts +20 -2
- package/src/discord-slash-wire.ts +1 -0
- package/src/domains/guardian-tick.ts +138 -0
- package/src/domains/outbound-alert.ts +244 -40
- package/src/domains/outbound-log-probe.ts +16 -1
- package/src/domains/release-tool.ts +2 -2
- package/src/domains/schedule-registry.ts +23 -0
- package/src/domains/telegram-kind-route.ts +39 -0
- package/src/domains/watch-default-scheduler.ts +95 -0
- package/src/esc-abort-gate.ts +44 -0
- 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/graph-runner/runner.ts +25 -5
- package/src/harness/harness-cli-command.ts +12 -2
- package/src/harness/harness-queue.ts +388 -33
- package/src/harness/harness-salvage-cli.ts +78 -0
- package/src/harness/harness-worktree-add.ts +68 -0
- package/src/harness/helper-scan.ts +10 -3
- package/src/harness/ledger-write-guard.ts +6 -16
- package/src/hq/hq.ts +6 -1
- package/src/hq/seats.ts +369 -0
- package/src/index.ts +185 -18
- package/src/instance/test-write-guard-spy-first.fixture.ts +13 -0
- package/src/instance/test-write-guard.ts +200 -0
- 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 -15
- package/src/intake-plane/discord-seat-work.ts +8 -2
- package/src/intake-plane/harness-target.ts +4 -3
- package/src/intake-plane/intake-sources.ts +1 -0
- package/src/intake-plane/items.ts +1 -0
- 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/degradation.ts +57 -0
- package/src/loops/orchestrator/finish-rate.ts +106 -4
- package/src/loops/orchestrator/grid-place.ts +11 -0
- package/src/loops/orchestrator/tick.ts +450 -54
- package/src/maturity/feature-maturity.ts +8 -5
- package/src/maturity/tui-slash-maturity.ts +1 -0
- package/src/mss/logging/log-store.ts +3 -0
- package/src/nexus/api/decisions-route.ts +11 -1
- package/src/nexus/api/http-server.ts +16 -5
- package/src/nexus/api/loop-edges.ts +352 -23
- package/src/nexus/api/ops-api.ts +96 -28
- package/src/nexus/api/ops-seats-sources.ts +2 -2
- package/src/nexus/api/workflow-templates.ts +3 -0
- package/src/nexus/index.ts +7 -0
- package/src/oauth/codex.ts +38 -0
- package/src/onboarding/first-chat-gate.ts +29 -0
- package/src/release-loop/checklist.ts +106 -6
- package/src/release-loop/feature-store.ts +64 -16
- package/src/release-loop/landing-freeze.ts +6 -4
- package/src/release-loop/light-rc.ts +173 -0
- package/src/release-loop/placement.ts +64 -20
- package/src/release-loop/rubric.ts +136 -0
- package/src/seat-dispatch/seat-ask.ts +79 -16
- package/src/seat-loop/seat-loop.ts +12 -6
- package/src/self-dev/ask-launch-flow.ts +24 -3
- package/src/self-dev/ask-path-candidates.ts +68 -0
- package/src/self-dev/dev-cli.ts +36 -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/orchestrate.ts +1 -0
- package/src/self-dev/pr-preview.ts +179 -0
- package/src/self-dev/round-pr-supersede.ts +244 -0
- package/src/self-dev/run-supervisor.ts +14 -0
- package/src/self-dev/shard-run.ts +23 -7
- package/src/self-implement/follow-up-goals.ts +2 -2
- package/src/self-implement/gate-cli.ts +29 -8
- package/src/self-implement/gate-remote.ts +427 -0
- package/src/self-implement/goal-author-clarification.ts +88 -13
- package/src/self-implement/goal-author.ts +8 -3
- package/src/self-implement/goal-clarification-escalation.ts +2 -0
- package/src/self-implement/l8-merge-queue.ts +29 -5
- package/src/self-implement/orchestrator.ts +61 -13
- package/src/self-implement/prior-draft-findings.ts +4 -3
- package/src/self-implement/salvage-branches.ts +254 -0
- package/src/skills/skill-feedback.ts +76 -0
- package/src/task-agent/actions.ts +101 -7
- package/src/task-agent/board.ts +132 -0
- package/src/task-agent/card-evidence.ts +203 -0
- package/src/task-agent/completion-evidence.ts +126 -0
- package/src/task-agent/judge.ts +49 -5
- package/src/task-agent/live-moves.ts +176 -0
- package/src/task-agent/mission.ts +449 -0
- package/src/task-agent/shadow.ts +258 -0
- package/src/task-agent/task-hand.ts +337 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +64 -4
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +10 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +142 -13
- package/src/task-orchestrator/surfaces/pod-pool.ts +13 -5
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +88 -20
- package/src/telegram-away-command.ts +38 -0
- package/src/telegram-commands.ts +11 -0
- package/src/telegram-loops-command.ts +1 -1
- package/src/telegram-report-log-probe.ts +21 -0
- package/src/telegram-report.ts +27 -3
- package/src/telegram.ts +5 -20
- package/src/tui-client/dashboard-session.ts +1 -0
- package/src/user-config.ts +196 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-aac7332576c4d6fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +0 -5
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
- package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.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/4795-f372490b178583fc.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/5360-be1f1d8f9fcc9755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +0 -2
- package/apps/pwa/out/_next/static/chunks/6877-fe94e76a5841ee9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.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/8060-cc008cc6fedfa818.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +0 -25
- package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-7865aba0f4d2ee49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3143b07a817d1da9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-17ba086ef82d339b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-7eb661ff23af7da2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-6e5c3a867557298b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9a41d65355fc25b9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-5e77cd1802567c89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-41afec0b255ed2bb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4187666132fc98c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-09328d499162bae4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-29ff3a8f84f03b6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-80ec7401bc83f511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-fc7d86467291ae38.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-de43a186e5ba5683.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-0e2d6985fb42cd2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-13e4108999f58d13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e8296ee284aa1293.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-043a9111e74f6b24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-23a4601ceb7f452e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-10d958605a77b3bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a04260b8ccc968d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-7a1d85e121065cc8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-12916918988ac97e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d8a3347a9d0c3f4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-5a64cb5208c6a8fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-1407ec83ea64c1f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-1d824510df025d18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-7f49fe645d5132f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-27d71f131acd9320.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-424659bacfb8cdd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-fc2df261f8a2c7df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-5d4fef9ccd7d406b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +0 -1
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
- package/apps/pwa/out/_next/static/yRA5ur6DcPl0YHSRheH2O/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{yRA5ur6DcPl0YHSRheH2O → J5H7BvqgSI8DUjtRRcA_S}/_ssgManifest.js +0 -0
package/src/nexus/api/ops-api.ts
CHANGED
|
@@ -18,13 +18,35 @@ let seatsCache: ReturnType<typeof createSeatsCache> | null = null;
|
|
|
18
18
|
export function setSeatsCacheForTest(cache: ReturnType<typeof createSeatsCache> | null): void { seatsCache = cache; }
|
|
19
19
|
const LOG_PATH = /^\/v1\/ops\/release\/runs\/([^/]+)\/nodes\/([^/]+)\/log$/;
|
|
20
20
|
|
|
21
|
-
interface RunNode { nodeId: string; ok?: unknown; output?: unknown }
|
|
21
|
+
interface RunNode { nodeId: string; ok?: unknown; output?: unknown; startedAt?: unknown; endedAt?: unknown }
|
|
22
22
|
interface RunRecord {
|
|
23
23
|
status: string;
|
|
24
24
|
path: string[];
|
|
25
25
|
startedAt: string;
|
|
26
26
|
input?: unknown;
|
|
27
27
|
nodes: RunNode[];
|
|
28
|
+
currentNode?: { nodeId?: unknown; startedAt?: unknown };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Node times come only from the ledger (GRAPH-NODE-TIMES); older ledgers have none and the screen says so. */
|
|
32
|
+
const ISO_TIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
33
|
+
function isoTime(value: unknown): string | undefined {
|
|
34
|
+
return typeof value === 'string' && ISO_TIME.test(value) && Number.isFinite(Date.parse(value)) ? value : undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function nodeTimes(node: RunNode): { startedAt?: string; endedAt?: string } {
|
|
38
|
+
const startedAt = isoTime(node.startedAt);
|
|
39
|
+
const endedAt = isoTime(node.endedAt);
|
|
40
|
+
return { ...(startedAt ? { startedAt } : {}), ...(endedAt ? { endedAt } : {}) };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The running node has no record yet — show it with its start time when the ledger names it as the path tail. */
|
|
44
|
+
function runningNode(run: RunRecord, recorded: number): Array<{ nodeId: string; ok: null; summary: string; startedAt: string }> {
|
|
45
|
+
const current = run.currentNode;
|
|
46
|
+
const startedAt = isoTime(current?.startedAt);
|
|
47
|
+
if (run.status !== 'running' || !startedAt || typeof current?.nodeId !== 'string' ||
|
|
48
|
+
run.path.length !== recorded + 1 || run.path.at(-1) !== current.nodeId) return [];
|
|
49
|
+
return [{ nodeId: redactSecretText(current.nodeId), ok: null, summary: '', startedAt }];
|
|
28
50
|
}
|
|
29
51
|
|
|
30
52
|
function recordAt(path: string): RunRecord | null {
|
|
@@ -66,6 +88,77 @@ function runVersion(input: unknown): string | null {
|
|
|
66
88
|
return typeof version === 'string' && VERSION.test(version) ? version : null;
|
|
67
89
|
}
|
|
68
90
|
|
|
91
|
+
/** Facts away mode prints for a node — only built when asked, so the HTTP shape stays as it was. */
|
|
92
|
+
export interface ReleaseNodeFacts { verdict?: string; introduced?: number; preexisting?: number; waiver?: string }
|
|
93
|
+
export interface ReleaseRunNodeView {
|
|
94
|
+
nodeId: string; ok: boolean | null; summary: string; startedAt?: string; endedAt?: string; facts?: ReleaseNodeFacts;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function parsedOutput(output: unknown): Record<string, unknown> | null {
|
|
98
|
+
let value = output;
|
|
99
|
+
if (typeof value === 'string') {
|
|
100
|
+
const text = value;
|
|
101
|
+
try { value = JSON.parse(text) as unknown; }
|
|
102
|
+
catch {
|
|
103
|
+
const last = text.split('\n').map((line) => line.trim()).filter(Boolean).at(-1);
|
|
104
|
+
try { value = JSON.parse(last ?? '') as unknown; } catch { return null; }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function releaseNodeFacts(output: unknown): ReleaseNodeFacts {
|
|
111
|
+
const value = parsedOutput(output);
|
|
112
|
+
if (!value) return {};
|
|
113
|
+
const verdict = typeof value.verdict === 'string' ? value.verdict : typeof value.outcome === 'string' ? value.outcome : undefined;
|
|
114
|
+
const introduced = Array.isArray(value.introduced) ? value.introduced.length : typeof value.introduced === 'number' ? value.introduced : undefined;
|
|
115
|
+
const preexisting = typeof value.preexisting === 'number' ? value.preexisting : undefined;
|
|
116
|
+
const waiver = typeof value.waiver === 'string' && value.waiver.trim() ? redactSecretText(value.waiver).slice(0, 200) : undefined;
|
|
117
|
+
return {
|
|
118
|
+
...(verdict ? { verdict: redactSecretText(verdict).slice(0, 40) } : {}),
|
|
119
|
+
...(introduced !== undefined ? { introduced } : {}),
|
|
120
|
+
...(preexisting !== undefined ? { preexisting } : {}),
|
|
121
|
+
...(waiver ? { waiver } : {}),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export interface ReleaseRunView {
|
|
125
|
+
runId: string; status: string; startedAt: string; version: string | null; path: string[]; nodes: ReleaseRunNodeView[];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** The release run ledger as `/v1/ops/release/runs` serves it (newest 20) — shared with Telegram /release and away mode
|
|
129
|
+
* so every surface reads the same verdicts. `'unavailable'` = the directory exists but cannot be read. */
|
|
130
|
+
export function readReleaseRuns(version: string | null = null,
|
|
131
|
+
dir: string = join(effectiveInstanceRoot(), 'graph-runs', 'release-loop'),
|
|
132
|
+
opts: { facts?: boolean } = {}): ReleaseRunView[] | 'unavailable' {
|
|
133
|
+
let filenames: string[];
|
|
134
|
+
try {
|
|
135
|
+
filenames = readdirSync(dir, { withFileTypes: true })
|
|
136
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.json')
|
|
137
|
+
&& IDENTIFIER.test(entry.name.slice(0, -5)))
|
|
138
|
+
.map((entry) => entry.name);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return [];
|
|
142
|
+
return 'unavailable';
|
|
143
|
+
}
|
|
144
|
+
return filenames.flatMap((filename) => {
|
|
145
|
+
const run = recordAt(join(dir, filename));
|
|
146
|
+
if (!run) return [];
|
|
147
|
+
const v = runVersion(run.input);
|
|
148
|
+
if (version !== null && v !== version) return [];
|
|
149
|
+
return [{
|
|
150
|
+
runId: redactSecretText(filename.slice(0, -5)), status: redactSecretText(run.status),
|
|
151
|
+
startedAt: redactSecretText(run.startedAt), version: v,
|
|
152
|
+
path: run.path.filter((step): step is string => typeof step === 'string').map(redactSecretText),
|
|
153
|
+
nodes: [...run.nodes.filter((node) => node && typeof node.nodeId === 'string').map((node) => ({
|
|
154
|
+
nodeId: redactSecretText(node.nodeId), ok: typeof node.ok === 'boolean' ? node.ok : null,
|
|
155
|
+
summary: nodeSummary(node.output), ...nodeTimes(node),
|
|
156
|
+
...(opts.facts ? { facts: releaseNodeFacts(node.output) } : {}),
|
|
157
|
+
})), ...runningNode(run, run.nodes.length)],
|
|
158
|
+
}];
|
|
159
|
+
}).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).slice(0, 20);
|
|
160
|
+
}
|
|
161
|
+
|
|
69
162
|
/** The sole data gate for every read-only /v1/ops endpoint. */
|
|
70
163
|
export function handleOpsApi(req: Request, metaApi: MetaApiOpts | undefined): Response | Promise<Response> {
|
|
71
164
|
const url = new URL(req.url);
|
|
@@ -103,33 +196,8 @@ export function handleOpsApi(req: Request, metaApi: MetaApiOpts | undefined): Re
|
|
|
103
196
|
}
|
|
104
197
|
if (pathname === '/v1/ops/release/runs') {
|
|
105
198
|
if (version !== null && !VERSION.test(version)) return jsonResponse({ error: 'invalid-version' }, 400);
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
filenames = readdirSync(dir, { withFileTypes: true })
|
|
110
|
-
.filter((entry) => entry.isFile() && entry.name.endsWith('.json')
|
|
111
|
-
&& IDENTIFIER.test(entry.name.slice(0, -5)))
|
|
112
|
-
.map((entry) => entry.name);
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return served(jsonResponse([]));
|
|
116
|
-
return jsonResponse({ error: 'runs-unavailable' }, 500);
|
|
117
|
-
}
|
|
118
|
-
const runs = filenames.flatMap((filename) => {
|
|
119
|
-
const run = recordAt(join(dir, filename));
|
|
120
|
-
if (!run) return [];
|
|
121
|
-
const v = runVersion(run.input);
|
|
122
|
-
if (version !== null && v !== version) return [];
|
|
123
|
-
return [{
|
|
124
|
-
runId: redactSecretText(filename.slice(0, -5)), status: redactSecretText(run.status),
|
|
125
|
-
startedAt: redactSecretText(run.startedAt), version: v,
|
|
126
|
-
path: run.path.filter((step): step is string => typeof step === 'string').map(redactSecretText),
|
|
127
|
-
nodes: run.nodes.filter((node) => node && typeof node.nodeId === 'string').map((node) => ({
|
|
128
|
-
nodeId: redactSecretText(node.nodeId), ok: typeof node.ok === 'boolean' ? node.ok : null,
|
|
129
|
-
summary: nodeSummary(node.output),
|
|
130
|
-
})),
|
|
131
|
-
}];
|
|
132
|
-
}).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).slice(0, 20);
|
|
199
|
+
const runs = readReleaseRuns(version);
|
|
200
|
+
if (runs === 'unavailable') return jsonResponse({ error: 'runs-unavailable' }, 500);
|
|
133
201
|
return served(jsonResponse(runs));
|
|
134
202
|
}
|
|
135
203
|
// WHATWG URL normalizes a literal runs/../nodes/... before routing.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// removed — same rule as decision replies), the release checklist, and the decision ledger. Each failure becomes null upstream.
|
|
3
3
|
import { DecisionLedger } from '../../decisions/decision-ledger.js';
|
|
4
4
|
import { listCoordEvents, type CoordEvent } from '../../context-bus/coord-events.js';
|
|
5
|
-
import {
|
|
5
|
+
import { checklistDevVersion, listChecklist, type ChecklistItem } from '../../release-loop/checklist.js';
|
|
6
6
|
import { getUserConfig } from '../../user-config.js';
|
|
7
7
|
import { SEATS, kstDayRange, type ChannelComment, type ChannelSource, type MergedPr, type SeatsSources } from './ops-seats.js';
|
|
8
8
|
|
|
@@ -82,7 +82,7 @@ export function liveSeatsSources(deps: { listEvents?: typeof listCoordEvents; gi
|
|
|
82
82
|
return lines?.map((line) => JSON.parse(line) as MergedPr) ?? null;
|
|
83
83
|
},
|
|
84
84
|
checklist() {
|
|
85
|
-
const dev =
|
|
85
|
+
const dev = checklistDevVersion();
|
|
86
86
|
const next = listChecklist(nextPatch(dev)).items;
|
|
87
87
|
const current = listChecklist(dev).items;
|
|
88
88
|
const all: ChecklistItem[] = [...current, ...next];
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { readdirSync, readFileSync } from 'fs';
|
|
6
6
|
import { join } from 'path';
|
|
7
|
+
import { debug } from '../../debug/log.js';
|
|
7
8
|
import { jsonResponse } from './http-server.js';
|
|
8
9
|
import { checkAuth, type MetaApiOpts } from './meta-api.js';
|
|
9
10
|
|
|
@@ -47,6 +48,8 @@ export function listTemplateCatalog(dir = findTemplateDir()): WorkflowTemplateEn
|
|
|
47
48
|
try {
|
|
48
49
|
files = readdirSync(dir).filter((f) => f.endsWith('.yaml'));
|
|
49
50
|
} catch {
|
|
51
|
+
// An installed package without this folder used to answer «No templates available» silently (10-06 · files list).
|
|
52
|
+
debug.log('nexus.workflow-templates', 'template-dir-missing', { dir });
|
|
50
53
|
return [];
|
|
51
54
|
}
|
|
52
55
|
const out: WorkflowTemplateEntry[] = [];
|
package/src/nexus/index.ts
CHANGED
|
@@ -136,6 +136,7 @@ import {
|
|
|
136
136
|
type DailyReflectionSchedulerHandle,
|
|
137
137
|
} from '../notes/daily-reflection-scheduler.js';
|
|
138
138
|
import { type ScheduleRunnerHandle } from '../domains/schedule-runner.js';
|
|
139
|
+
import { startWatchDefaultScheduler } from '../domains/watch-default-scheduler.js';
|
|
139
140
|
import { resolvePwaStaticDir } from './static-dir-resolve.js';
|
|
140
141
|
import {
|
|
141
142
|
createHitlPendingCallbacks,
|
|
@@ -1310,6 +1311,7 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
|
|
|
1310
1311
|
// the daily-reflection scheduler regardless of which branch booted
|
|
1311
1312
|
// it. Tests with detachForTesting set leave this undefined.
|
|
1312
1313
|
let dailyReflectionScheduler: DailyReflectionSchedulerHandle | undefined;
|
|
1314
|
+
let watchDefaultScheduler: ReturnType<typeof startWatchDefaultScheduler> | undefined;
|
|
1313
1315
|
// RFC #2161 FU A8 (2026-05-11) — discovery cron via NEXUS. Dormant
|
|
1314
1316
|
// by default (opt-in via `ELANOUS_DISCOVERY_CRON_INTERVAL_MS`); when
|
|
1315
1317
|
// configured, fires runDiscovery every interval and pushes the
|
|
@@ -3198,6 +3200,8 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
|
|
|
3198
3200
|
// schedule migrate 로 이관.) 롤백: 아래 한 줄 복원.
|
|
3199
3201
|
// try { scheduleRunner = startScheduleRunner(); } catch { /* fail-soft */ }
|
|
3200
3202
|
void scheduleRunner; // 은퇴(undefined 유지)
|
|
3203
|
+
try { watchDefaultScheduler = startWatchDefaultScheduler(); }
|
|
3204
|
+
catch (error) { debug.log('watch.default', 'failed', { stage: 'boot', reason: error instanceof Error ? error.message : String(error) }); }
|
|
3201
3205
|
}
|
|
3202
3206
|
}
|
|
3203
3207
|
|
|
@@ -3335,6 +3339,9 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
|
|
|
3335
3339
|
}
|
|
3336
3340
|
// S2 (2026-07-07) · stop the schedule runner so its node-cron tasks +
|
|
3337
3341
|
// reconcile interval don't keep firing/holding the loop past shutdown.
|
|
3342
|
+
if (watchDefaultScheduler) {
|
|
3343
|
+
try { watchDefaultScheduler.stop(); } catch { /* swallow */ }
|
|
3344
|
+
}
|
|
3338
3345
|
if (scheduleRunner) {
|
|
3339
3346
|
try { scheduleRunner.stop(); } catch { /* swallow */ }
|
|
3340
3347
|
}
|
package/src/oauth/codex.ts
CHANGED
|
@@ -554,6 +554,44 @@ export function codexAccessHoursLeft(accessToken: unknown, now: number = Date.no
|
|
|
554
554
|
return typeof exp === 'number' ? Math.round(((exp * 1000 - now) / 3_600_000) * 10) / 10 : null;
|
|
555
555
|
}
|
|
556
556
|
|
|
557
|
+
/** PREREFRESH-LOCK (10-06 · post-review of #24449) — the per-account host refresh lock path.
|
|
558
|
+
* 🩸 Two Pod launches on one host picked the same expiring account at once: both refreshed, the server
|
|
559
|
+
* rotated the refresh token under the second, its refresh failed and that run dropped the account.
|
|
560
|
+
* The lock file sits NEXT TO the account's credential state (`<codexHome>/.elanous-prerefresh.lock`) —
|
|
561
|
+
* ⛔ never inside a token file, and it carries only the holder's pid/nonce. null = no named home to lock. */
|
|
562
|
+
export function codexAccountRefreshLockPath(name: string, storePath: string = authStorePath()): string | null {
|
|
563
|
+
if (name === DEFAULT_CODEX_ACCOUNT || !isValidAccountName(name)) return null;
|
|
564
|
+
const codexHome = loadTokens(codexStoreKey(name), storePath)?.codexHome;
|
|
565
|
+
return codexHome ? join(codexHome, '.elanous-prerefresh.lock') : null;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/** A refresh (network + rotate + atomic write + import) can take tens of seconds — wait up to ~90 s,
|
|
569
|
+
* and treat a lock as stale only after 2 min (the async holder heartbeats its mtime meanwhile). */
|
|
570
|
+
export const CODEX_REFRESH_LOCK_OPTS = { staleMs: 120_000, retryBusyMs: 100, maxTries: 900 } as const;
|
|
571
|
+
|
|
572
|
+
/** The refresh lock could not be acquired (timeout · ENOENT · EACCES …) — `fn` never ran, so nothing was refreshed. */
|
|
573
|
+
export class CodexRefreshLockUnavailableError extends Error {
|
|
574
|
+
constructor(readonly reason: 'lock-timeout' | 'lock-error', cause: unknown) {
|
|
575
|
+
super(`codex refresh lock unavailable (${reason}): ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
576
|
+
this.name = 'CodexRefreshLockUnavailableError';
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/** Runs `fn` while holding the account's refresh lock (cross-process · same-process). null path = no lock.
|
|
581
|
+
* Any failure to acquire the lock throws `CodexRefreshLockUnavailableError` (fn not run); fn's own errors pass through. */
|
|
582
|
+
export async function withCodexAccountRefreshLock<T>(lockPath: string | null, fn: () => Promise<T>, opts: { staleMs?: number; retryBusyMs?: number; maxTries?: number } = CODEX_REFRESH_LOCK_OPTS): Promise<T> {
|
|
583
|
+
if (!lockPath) return fn();
|
|
584
|
+
const { acquireLockAsync } = await import('../storage/file-lock.js');
|
|
585
|
+
let handle: Awaited<ReturnType<typeof acquireLockAsync>>;
|
|
586
|
+
try { handle = await acquireLockAsync(lockPath, opts); }
|
|
587
|
+
catch (error) {
|
|
588
|
+
const timedOut = error instanceof Error && error.message.startsWith('acquireLockAsync: timed out');
|
|
589
|
+
throw new CodexRefreshLockUnavailableError(timedOut ? 'lock-timeout' : 'lock-error', error);
|
|
590
|
+
}
|
|
591
|
+
try { return await fn(); }
|
|
592
|
+
finally { handle.release(); }
|
|
593
|
+
}
|
|
594
|
+
|
|
557
595
|
export async function refreshCodexAccountHome(
|
|
558
596
|
name: string,
|
|
559
597
|
opts: {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { debug } from '../debug/log.js';
|
|
2
|
+
import { needsFirstRun, needsOnboarding } from '../onboarding.js';
|
|
3
|
+
import type { UserConfig } from '../user-config.js';
|
|
4
|
+
import type { FirstRunDeps, runFirstRun } from './first-run.js';
|
|
5
|
+
import { UNATTENDED_SETUP_COMMAND } from './entry-hints.js';
|
|
6
|
+
|
|
7
|
+
export interface FirstChatGateDeps {
|
|
8
|
+
isTTY: boolean;
|
|
9
|
+
runFirstRun: (deps: FirstRunDeps) => ReturnType<typeof runFirstRun>;
|
|
10
|
+
runInteractive: () => Promise<boolean>;
|
|
11
|
+
print: (line: string) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function firstChatGate(cfg: UserConfig, deps: FirstChatGateDeps): Promise<'continue' | 'stop'> {
|
|
15
|
+
const decide = (decision: 'continue' | 'stop', outcome: string): 'continue' | 'stop' => {
|
|
16
|
+
debug.log('onboarding.first-chat', decision, { isTTY: deps.isTTY, outcome });
|
|
17
|
+
return decision;
|
|
18
|
+
};
|
|
19
|
+
if (!needsOnboarding(cfg)) return decide('continue', 'completed');
|
|
20
|
+
if (deps.isTTY) {
|
|
21
|
+
return decide(await deps.runInteractive() ? 'continue' : 'stop', 'interactive');
|
|
22
|
+
}
|
|
23
|
+
if (!needsFirstRun(cfg)) return decide('continue', 'already-ready');
|
|
24
|
+
const result = await deps.runFirstRun({ config: cfg, isTTY: false, print: () => {} });
|
|
25
|
+
if (result.outcome === 'ready') return decide('continue', result.outcome);
|
|
26
|
+
deps.print(`다음: elanous setup llm (또는 ${UNATTENDED_SETUP_COMMAND})`);
|
|
27
|
+
process.exitCode = 2;
|
|
28
|
+
return decide('stop', result.outcome);
|
|
29
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, join } from 'node:path';
|
|
3
4
|
import * as store from './feature-store.js';
|
|
4
5
|
import { debug } from '../debug/log.js';
|
|
5
6
|
import { CliUserError } from '../cli/cli-user-error.js';
|
|
@@ -20,6 +21,8 @@ export interface ChecklistItem {
|
|
|
20
21
|
ceoDate?: string;
|
|
21
22
|
/** 다른 칸의 처리량을 올리는 칸(자율성·효율성·동시성). 없으면 가속 등급이 아니다. */
|
|
22
23
|
accelerator?: true;
|
|
24
|
+
/** 이 칸이 근거로 삼는 문서(저장소 상대 경로, 선택적으로 `#절`). 없으면 칸에 실리지 않는다. */
|
|
25
|
+
refs?: string[];
|
|
23
26
|
evidence?: string;
|
|
24
27
|
disposition?: ChecklistDisposition;
|
|
25
28
|
kind?: ChecklistKind;
|
|
@@ -37,6 +40,7 @@ export interface ChecklistHistory {
|
|
|
37
40
|
dev: string;
|
|
38
41
|
force?: true;
|
|
39
42
|
reason?: string;
|
|
43
|
+
generation?: number;
|
|
40
44
|
}
|
|
41
45
|
export interface Checklist {
|
|
42
46
|
version: string;
|
|
@@ -50,6 +54,12 @@ export function devVersion(): string {
|
|
|
50
54
|
return (JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf8')) as { version: string }).version;
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
/** The checklist version of the development tree: package.json says `0.2.18-dev.0` once a release branch is cut,
|
|
58
|
+
* but the checklist only knows `0.2.18` (10-07: every server caller that passed devVersion() raw got null/throw). */
|
|
59
|
+
export function checklistDevVersion(version = devVersion()): string {
|
|
60
|
+
return version.replace(/-dev\.\d+$/, '');
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
export function parseOwner(value: string): { seat: string; sub?: string } {
|
|
54
64
|
const match = /^([A-Z]{2,8})(?:\/([a-z][a-z0-9-]{0,31}))?$/.exec(value);
|
|
55
65
|
if (!match || match[0] !== value) throw new CliUserError(`잘못된 담당: ${value}`, 'TC 또는 TC/rel');
|
|
@@ -92,6 +102,7 @@ export function statusChangesSince(history: readonly ChecklistHistory[], sinceIs
|
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
function mutate(v: string, apply: (data: Checklist, otherItems: (id: string) => store.ChecklistCollision[]) => boolean): Checklist {
|
|
105
|
+
// addItem/setItem/claimItem/removeItem/seedFromRoadmap reach the generation-fenced ledger write here.
|
|
95
106
|
return decodeClaimHistory(store.mutate(v, store.releasedVersion(), devVersion(), apply));
|
|
96
107
|
}
|
|
97
108
|
|
|
@@ -100,12 +111,82 @@ function change(data: Checklist, id: string, field: string, from: unknown, to: u
|
|
|
100
111
|
debug.log('release-loop.checklist', 'change', { version: data.version, id, field, from: from ?? null, to: to ?? null, by });
|
|
101
112
|
}
|
|
102
113
|
|
|
114
|
+
const REPO_ROOT = join(import.meta.dir, '..', '..');
|
|
115
|
+
|
|
116
|
+
/** `--ref` 문서를 찾는 뿌리들 — 이 코드의 트리 ⊕ 지금 작업 디렉토리의 git 뿌리.
|
|
117
|
+
* ⭐ 설치본(`~/.local/share/elanous/current`)에는 `docs/` 가 없다 — 저장소 안에서 부른 설치본도 저장소 문서를 인용할 수 있게. */
|
|
118
|
+
export function refRoots(cwd = process.cwd()): string[] {
|
|
119
|
+
const roots = [REPO_ROOT];
|
|
120
|
+
try {
|
|
121
|
+
const top = spawnSync('git', ['rev-parse', '--show-toplevel'], { cwd, encoding: 'utf8', timeout: 5_000 });
|
|
122
|
+
const path = top.status === 0 ? top.stdout.trim() : '';
|
|
123
|
+
if (path && !roots.includes(path)) roots.push(path);
|
|
124
|
+
} catch { /* git 없음 — 코드 트리만 */ }
|
|
125
|
+
return roots;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** `--ref` 값 검사: 저장소 상대 경로(⊕ `#절`)이고 그 파일이 뿌리들 중 하나에 있어야 한다. 중복은 순서를 지키며 지운다. */
|
|
129
|
+
export function normalizeRefs(refs: readonly string[], root: string | readonly string[] = refRoots()): string[] {
|
|
130
|
+
const roots = typeof root === 'string' ? [root] : root;
|
|
131
|
+
const out: string[] = [];
|
|
132
|
+
for (const raw of refs) {
|
|
133
|
+
const ref = raw.trim();
|
|
134
|
+
const path = ref.split('#')[0] ?? '';
|
|
135
|
+
if (!path || isAbsolute(path) || path.split(/[\\/]/).some((part) => part === '..')) throw new CliUserError(`잘못된 문서 참조: ${raw}`, '저장소 상대 경로[#절] — 예: docs/manual/MANUAL-x.md#§1');
|
|
136
|
+
let isFile = false;
|
|
137
|
+
for (const base of roots) {
|
|
138
|
+
try { if (statSync(join(base, path)).isFile()) { isFile = true; break; } } catch { /* 없음 */ }
|
|
139
|
+
}
|
|
140
|
+
if (!isFile) throw new CliUserError(`없는 문서: ${path}`, '저장소 상대 «파일» 경로로 준다(폴더는 안 된다)');
|
|
141
|
+
if (!out.includes(ref)) out.push(ref);
|
|
142
|
+
}
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** 한 문서를 인용하는 칸 한 줄 — `release checklist refs <doc>` 의 «구현 현황» 행. */
|
|
147
|
+
export interface DocRefRow { version: string; id: string; title: string; status: ChecklistStatus; owner: string | null; section: string | null }
|
|
148
|
+
|
|
149
|
+
function splitRef(ref: string): { path: string; section: string | null } {
|
|
150
|
+
const at = ref.indexOf('#');
|
|
151
|
+
const path = (at < 0 ? ref : ref.slice(0, at)).trim().replace(/^\.\//, '');
|
|
152
|
+
return { path, section: at < 0 ? null : ref.slice(at + 1) };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** 읽기 전용: 칸의 refs 중 경로(`#` 앞)가 doc 의 경로와 같은 것을 행으로 낸다. doc 에 `#절`이 붙으면 그 절과 정확히 같은 ref 만. */
|
|
156
|
+
export function cellsReferencingDoc(doc: string, checklists: readonly Checklist[]): DocRefRow[] {
|
|
157
|
+
const want = splitRef(doc.trim());
|
|
158
|
+
const rows: DocRefRow[] = [];
|
|
159
|
+
for (const data of checklists) {
|
|
160
|
+
for (const item of data.items) {
|
|
161
|
+
// 칸마다 한 행 — 같은 문서의 절을 여럿 인용해도 절을 모아 한 줄로(절 없는 인용이 섞이면 문서 전체로 본다).
|
|
162
|
+
const sections: string[] = [];
|
|
163
|
+
let whole = false;
|
|
164
|
+
for (const ref of item.refs ?? []) {
|
|
165
|
+
const got = splitRef(ref);
|
|
166
|
+
if (got.path !== want.path) continue;
|
|
167
|
+
if (want.section !== null && got.section !== want.section) continue;
|
|
168
|
+
if (got.section === null) whole = true;
|
|
169
|
+
else if (!sections.includes(got.section)) sections.push(got.section);
|
|
170
|
+
}
|
|
171
|
+
if (!whole && sections.length === 0) continue;
|
|
172
|
+
rows.push({ version: data.version, id: item.id, title: item.title, status: item.status, owner: item.owner ?? null, section: whole ? null : sections.join(' · ') });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return rows;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** `| 판 | 칸 | 상태 | 절 | 제목 |` 마크다운 표 — 행이 없으면 머리줄 둘만. */
|
|
179
|
+
export function renderRefsStatus(rows: readonly DocRefRow[]): string {
|
|
180
|
+
const cell = (value: string) => value.replace(/\|/g, '\\|').replace(/\r?\n/g, ' ');
|
|
181
|
+
return ['| 판 | 칸 | 상태 | 절 | 제목 |', '| --- | --- | --- | --- | --- |', ...rows.map((row) => `| ${cell(row.version)} | ${cell(row.id)} | ${row.status} | ${cell(row.section ?? '-')} | ${cell(row.title)} |`)].join('\n');
|
|
182
|
+
}
|
|
183
|
+
|
|
103
184
|
export function validateCeoLoad(input: { ceoMinutes?: number; ceoDate?: string }): void {
|
|
104
185
|
if (input.ceoMinutes !== undefined && (!Number.isSafeInteger(input.ceoMinutes) || input.ceoMinutes < 0)) throw new CliUserError('대표 손 분량은 0 이상의 정수 분이어야 한다');
|
|
105
186
|
if (input.ceoDate !== undefined && (!/^\d{4}-\d{2}-\d{2}$/.test(input.ceoDate) || !Number.isFinite(Date.parse(`${input.ceoDate}T00:00:00Z`)) || new Date(`${input.ceoDate}T00:00:00Z`).toISOString().slice(0, 10) !== input.ceoDate)) throw new CliUserError('대표 손 날짜는 YYYY-MM-DD 이어야 한다');
|
|
106
187
|
}
|
|
107
188
|
|
|
108
|
-
export function addItem(v: string, input: { id: string; title: string; owner?: string; kind?: ChecklistKind; priority?: ChecklistItem['priority']; predecessors?: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean }, options: { allowDuplicateId?: boolean } = {}): Checklist {
|
|
189
|
+
export function addItem(v: string, input: { id: string; title: string; refs?: string[]; owner?: string; kind?: ChecklistKind; priority?: ChecklistItem['priority']; predecessors?: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean }, options: { allowDuplicateId?: boolean } = {}): Checklist {
|
|
109
190
|
if (!input.id.trim()) throw new CliUserError('칸 id 가 비었다');
|
|
110
191
|
if (!input.title.trim()) throw new CliUserError('칸 제목이 비었다');
|
|
111
192
|
if (input.kind !== undefined && input.kind !== 'screen') throw new CliUserError(`잘못된 종류: ${input.kind}`, 'screen');
|
|
@@ -115,6 +196,7 @@ export function addItem(v: string, input: { id: string; title: string; owner?: s
|
|
|
115
196
|
if (input.deadlineVersion !== undefined) store.validateVersion(input.deadlineVersion);
|
|
116
197
|
if (input.accelerator !== undefined && input.accelerator !== true) throw new CliUserError('가속 등급은 true 이거나 생략한다');
|
|
117
198
|
validateCeoLoad(input);
|
|
199
|
+
const refs = input.refs !== undefined && input.refs.length ? normalizeRefs(input.refs) : undefined;
|
|
118
200
|
return mutate(v, (data, otherItems) => {
|
|
119
201
|
if (data.items.some((item) => item.id === input.id)) throw new CliUserError(`이미 있는 칸: ${input.id}`, 'set <id> 로 고친다');
|
|
120
202
|
const collisions = otherItems(input.id);
|
|
@@ -125,14 +207,14 @@ export function addItem(v: string, input: { id: string; title: string; owner?: s
|
|
|
125
207
|
}
|
|
126
208
|
const by = process.env.ELANOUS_TRACK || 'cli';
|
|
127
209
|
const at = new Date().toISOString();
|
|
128
|
-
const item: ChecklistItem = { id: input.id, title: input.title, status: 'yellow', ...(input.owner !== undefined ? { owner: input.owner } : {}), ...(input.kind !== undefined ? { kind: input.kind } : {}), ...(input.priority !== undefined ? { priority: input.priority } : {}), ...(input.predecessors !== undefined ? { predecessors: input.predecessors } : {}), ...(input.deadlineVersion !== undefined ? { deadlineVersion: input.deadlineVersion } : {}), ...(input.ceoMinutes !== undefined ? { ceoMinutes: input.ceoMinutes } : {}), ...(input.ceoDate !== undefined ? { ceoDate: input.ceoDate } : {}), ...(input.accelerator === true ? { accelerator: true as const } : {}), updatedAt: at, updatedBy: by };
|
|
210
|
+
const item: ChecklistItem = { id: input.id, title: input.title, status: 'yellow', ...(input.owner !== undefined ? { owner: input.owner } : {}), ...(input.kind !== undefined ? { kind: input.kind } : {}), ...(input.priority !== undefined ? { priority: input.priority } : {}), ...(input.predecessors !== undefined ? { predecessors: input.predecessors } : {}), ...(input.deadlineVersion !== undefined ? { deadlineVersion: input.deadlineVersion } : {}), ...(input.ceoMinutes !== undefined ? { ceoMinutes: input.ceoMinutes } : {}), ...(input.ceoDate !== undefined ? { ceoDate: input.ceoDate } : {}), ...(input.accelerator === true ? { accelerator: true as const } : {}), ...(refs ? { refs } : {}), updatedAt: at, updatedBy: by };
|
|
129
211
|
data.items.push(item);
|
|
130
212
|
change(data, item.id, 'add', null, item, by, at);
|
|
131
213
|
return true;
|
|
132
214
|
});
|
|
133
215
|
}
|
|
134
216
|
|
|
135
|
-
export function setItem(v: string, id: string, patch: { status?: ChecklistStatus; evidence?: string; owner?: string; disposition?: ChecklistDisposition; kind?: ChecklistKind; priority?: ChecklistItem['priority']; predecessors?: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean | null }, by: string): Checklist {
|
|
217
|
+
export function setItem(v: string, id: string, patch: { status?: ChecklistStatus; evidence?: string; refs?: string[]; owner?: string; disposition?: ChecklistDisposition; kind?: ChecklistKind; priority?: ChecklistItem['priority']; predecessors?: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean | null }, by: string): Checklist {
|
|
136
218
|
return mutate(v, (data) => {
|
|
137
219
|
const item = data.items.find((i) => i.id === id);
|
|
138
220
|
if (!item) throw new CliUserError(`없는 칸: ${id}`, 'list 로 칸 목록을 본다');
|
|
@@ -144,13 +226,15 @@ export function setItem(v: string, id: string, patch: { status?: ChecklistStatus
|
|
|
144
226
|
if (patch.deadlineVersion !== undefined) store.validateVersion(patch.deadlineVersion);
|
|
145
227
|
if (patch.accelerator !== undefined && patch.accelerator !== true && patch.accelerator !== null) throw new CliUserError('가속 등급은 true(부여) 또는 null(해제)이다');
|
|
146
228
|
validateCeoLoad(patch);
|
|
229
|
+
// --ref 는 «더하기»다 — 기존 참조 뒤에 붙이고 중복을 지운다.
|
|
230
|
+
if (patch.refs !== undefined) patch = { ...patch, refs: normalizeRefs([...(item.refs ?? []), ...patch.refs]) };
|
|
147
231
|
if (patch.owner !== undefined) {
|
|
148
232
|
parseOwner(patch.owner);
|
|
149
233
|
if (item.owner && patch.owner !== item.owner) {
|
|
150
234
|
throw new CliUserError(`지금 주인: ${item.owner} — --force 로만 바꾼다`, 'release checklist claim <id> --by <자리> --force');
|
|
151
235
|
}
|
|
152
236
|
}
|
|
153
|
-
const fields = (['evidence', 'owner', 'status', 'disposition', 'kind', 'priority', 'predecessors', 'deadlineVersion', 'ceoMinutes', 'ceoDate', 'accelerator'] as const).filter((field) => patch[field] !== undefined && JSON.stringify(patch[field]) !== JSON.stringify(item[field] ?? null));
|
|
237
|
+
const fields = (['evidence', 'owner', 'status', 'disposition', 'kind', 'priority', 'predecessors', 'deadlineVersion', 'ceoMinutes', 'ceoDate', 'accelerator', 'refs'] as const).filter((field) => patch[field] !== undefined && JSON.stringify(patch[field]) !== JSON.stringify(item[field] ?? null));
|
|
154
238
|
if (fields.length === 0) return false;
|
|
155
239
|
const at = new Date().toISOString();
|
|
156
240
|
for (const field of fields) {
|
|
@@ -269,6 +353,22 @@ export function checklistGate(v: string): ChecklistGate {
|
|
|
269
353
|
return result;
|
|
270
354
|
}
|
|
271
355
|
|
|
356
|
+
/** GATE-ENTRY-ALIGN: the cut-time judgement shared by `release run`'s entry check and the checklist-gate node —
|
|
357
|
+
* past the landing deadline a non-P0 yellow without a disposition is carried (moved), and a P0 yellow always blocks. */
|
|
358
|
+
export function cutChecklistGate(v: string, landBy: string | null | undefined, now: Date = new Date()): ChecklistGate & { autoMoved: string[] } {
|
|
359
|
+
const items = listChecklist(v).items;
|
|
360
|
+
const overdue = landBy !== null && landBy !== undefined && now.getTime() > Date.parse(landBy);
|
|
361
|
+
const autoMoved = overdue ? items.filter((item) => item.status === 'yellow' && item.priority !== 'P0' && item.disposition === undefined).map((item) => item.id) : [];
|
|
362
|
+
const p0 = items.filter((item) => item.status === 'yellow' && item.priority === 'P0').map((item) => item.id);
|
|
363
|
+
const gate = checklistGate(v);
|
|
364
|
+
gate.undecided = gate.undecided.filter((id) => !autoMoved.includes(id) && !p0.includes(id));
|
|
365
|
+
gate.moved = gate.moved.filter((id) => !p0.includes(id)).concat(autoMoved);
|
|
366
|
+
gate.knownIssues = gate.knownIssues.filter((item) => !p0.includes(item.id));
|
|
367
|
+
gate.blocked.push(...p0.filter((id) => !gate.blocked.includes(id)));
|
|
368
|
+
gate.ok = gate.red.length === 0 && gate.undecided.length === 0 && gate.blocked.length === 0;
|
|
369
|
+
return { ...gate, autoMoved };
|
|
370
|
+
}
|
|
371
|
+
|
|
272
372
|
export function seedFromRoadmap(v: string, markdown: string): Checklist {
|
|
273
373
|
return mutate(v, (data) => {
|
|
274
374
|
let added = false;
|