elanous 0.2.17 → 0.2.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/P6YTVgzuZz5oTZhY4Din_/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1307-aac7332576c4d6fc.js → 1307-30dff22c9e1f3073.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6158.9c0449a9a8d0064b.js → 6158.af2e93b7adff709b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/{page-3143b07a817d1da9.js → page-d92f7ee3994e84c7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-17ba086ef82d339b.js → page-4733c967b56a4277.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/{page-7eb661ff23af7da2.js → page-d22fd849cc22f104.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-bb742f995c6769e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-6e5c3a867557298b.js → page-02b073a2ac793b83.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-8a9b3594bc23289b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-5e77cd1802567c89.js → page-a0eb02638b1e6f31.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-308111abadf35670.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-4187666132fc98c4.js → page-5d97b2901e0d1ea6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-09328d499162bae4.js → page-c39554c321576982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-29ff3a8f84f03b6a.js → page-8c3d8e63ae2e1c98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-80ec7401bc83f511.js → page-9b72a07d424701c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-fc7d86467291ae38.js → page-a572f58cd3ca5864.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-be5ea965a07fbc41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-0e2d6985fb42cd2f.js → page-c130b63862437814.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-13e4108999f58d13.js → page-fa244bd146a00ae8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-e8296ee284aa1293.js → page-44d56d6391965a6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-043a9111e74f6b24.js → page-83a700d4c754d802.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-23a4601ceb7f452e.js → not-found-7b3170f374f77b9d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-10d958605a77b3bd.js → page-a3c58f5c72a5f3f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-7a1d85e121065cc8.js → page-e8e1ecaea7cd71d2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-12916918988ac97e.js → page-1467047104141297.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-d8a3347a9d0c3f4a.js → page-2da6f9b8a38c6978.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-102310930791197a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-6ed0b73bae8351d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-1d824510df025d18.js → page-44d5f767491692bf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-43336055451f6e37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-7f49fe645d5132f7.js → page-3cc47dca0922ab9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a77ee355a2304ff0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-27d71f131acd9320.js → page-b17cd46cf0e9b8cf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-61553851aa606146.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-a1f585e154e61c59.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-424659bacfb8cdd7.js → page-7bf54c9bb9274af8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-5150ef17b5383a57.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-5d4fef9ccd7d406b.js → page-134da468538baf48.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-539cde99554be20f.js → webpack-c2782b330447ffb6.js} +1 -1
- package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/decisions/index.html +1 -0
- package/apps/pwa/out/decisions/index.txt +30 -0
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/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/discord-seat-probe.ts +3 -2
- package/scripts/docs/generate-cli-anatomy.ts +162 -0
- package/scripts/goals/authoring-metrics.ts +73 -0
- package/scripts/landing-heal/must-fix-match.ts +7 -7
- package/scripts/landing-heal/run-mission.ts +3 -2
- package/scripts/public-export-test-run.ts +37 -2
- package/scripts/release-loop/checklist-gate-node.ts +13 -11
- package/scripts/release-loop/cut-branch.ts +2 -1
- package/scripts/release-loop/cutoff.ts +4 -2
- package/scripts/release-loop/docs-land-node.ts +8 -1
- package/scripts/release-loop/docs-node.ts +1 -1
- package/scripts/release-loop/gate-node.ts +99 -14
- package/scripts/release-loop/node-verdict.ts +2 -1
- package/scripts/release-loop/npm-publish-node.ts +11 -7
- package/scripts/release-loop/ops-upgrade-node.ts +8 -1
- package/scripts/release-loop/preflight.ts +246 -0
- package/scripts/release-loop/publish-node.ts +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 +115 -0
- package/scripts/release-loop/unattended-release.ts +43 -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/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/briefing/brief-items.ts +86 -17
- package/src/cli/autopilot-cli.ts +30 -11
- package/src/cli/brief-cli.ts +36 -2
- package/src/cli/freeze-cli.ts +3 -2
- package/src/cli/intake-cli.ts +73 -1
- package/src/cli/notify-cli.ts +75 -0
- package/src/cli/provider-cli.ts +12 -2
- package/src/cli/release-cli.ts +101 -17
- package/src/cli/tasks-cli.ts +309 -3
- package/src/context-bus/condense.ts +19 -3
- package/src/context-bus/context-day.ts +77 -24
- package/src/context-bus/context-first.ts +33 -0
- package/src/discord-self-message.ts +20 -2
- package/src/discord-slash-wire.ts +1 -0
- package/src/domains/outbound-alert.ts +200 -27
- package/src/domains/outbound-log-probe.ts +16 -1
- package/src/domains/release-tool.ts +2 -2
- package/src/execution-loop/gates.ts +11 -2
- package/src/flow-loop/split.ts +8 -4
- package/src/git-fs/worktree.ts +23 -3
- package/src/harness/harness-cli-command.ts +10 -2
- package/src/harness/harness-queue.ts +388 -33
- package/src/harness/helper-scan.ts +10 -3
- package/src/hq/seats.ts +369 -0
- package/src/index.ts +133 -15
- 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/discord-seat-work.ts +8 -2
- package/src/intake-plane/requirement-funnel.ts +316 -0
- package/src/knowledge/know-find.ts +10 -1
- package/src/loops/budget.ts +36 -0
- package/src/loops/orchestrator/finish-rate.ts +73 -1
- package/src/loops/orchestrator/grid-place.ts +11 -0
- package/src/loops/orchestrator/tick.ts +295 -20
- package/src/maturity/feature-maturity.ts +3 -2
- package/src/nexus/api/decisions-route.ts +11 -1
- package/src/nexus/api/loop-edges.ts +69 -13
- package/src/nexus/api/ops-seats-sources.ts +2 -2
- package/src/nexus/api/workflow-templates.ts +3 -0
- package/src/oauth/codex.ts +38 -0
- package/src/onboarding/first-chat-gate.ts +29 -0
- package/src/release-loop/checklist.ts +104 -6
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/landing-freeze.ts +6 -4
- package/src/release-loop/placement.ts +13 -3
- package/src/release-loop/rubric.ts +80 -0
- package/src/seat-dispatch/seat-ask.ts +79 -16
- package/src/seat-loop/seat-loop.ts +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-pipeline.ts +4 -0
- package/src/self-dev/draft-triage-rules.ts +1 -1
- package/src/self-dev/orchestrate-cli.ts +22 -0
- package/src/self-dev/round-pr-supersede.ts +244 -0
- package/src/self-dev/run-supervisor.ts +14 -0
- package/src/self-implement/follow-up-goals.ts +2 -2
- package/src/self-implement/gate-remote.ts +427 -0
- package/src/self-implement/goal-author.ts +8 -3
- package/src/self-implement/l8-merge-queue.ts +29 -5
- package/src/self-implement/orchestrator.ts +59 -12
- package/src/self-implement/prior-draft-findings.ts +4 -3
- package/src/skills/skill-feedback.ts +76 -0
- package/src/task-agent/actions.ts +39 -6
- package/src/task-agent/judge.ts +31 -5
- package/src/task-agent/mission.ts +423 -0
- package/src/task-agent/shadow.ts +90 -0
- package/src/task-agent/task-hand.ts +235 -0
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +10 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +86 -18
- package/src/telegram-loops-command.ts +1 -1
- package/src/telegram.ts +5 -20
- package/src/user-config.ts +63 -3
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6877-fe94e76a5841ee9b.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/8401-4ce1c42c8d89903f.js +0 -1
- 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/app/botlab/page-6b8444914da74ca6.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/control/page-41afec0b255ed2bb.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/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/observatory/page-1ed1324b1dc36754.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/page-1f50e3d4e13435f4.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/page-6e878ebbe1737d00.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/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/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/worktrees/page-ea90a8ac73ce2749.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 → P6YTVgzuZz5oTZhY4Din_}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { debug } from '../debug/log.js';
|
|
2
|
+
|
|
3
|
+
export interface AdoptionCandidate {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly grade: 'P1' | 'P2' | 'P3' | 'P4';
|
|
6
|
+
readonly risk: number;
|
|
7
|
+
readonly money: number;
|
|
8
|
+
readonly security: number;
|
|
9
|
+
readonly license: string | null;
|
|
10
|
+
readonly touchesPatentCandidate: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type AdoptShadowReason = 'grade' | 'risk' | 'money' | 'security' | 'license' | 'patent' | 'daily-cap';
|
|
14
|
+
|
|
15
|
+
export interface AdoptShadowVerdict {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly adopt: boolean;
|
|
18
|
+
readonly reasons: readonly AdoptShadowReason[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ALLOWED_LICENSES: ReadonlySet<string> = new Set([
|
|
22
|
+
'MIT', 'Apache-2.0', 'BSD-2-Clause', 'BSD-3-Clause', 'ISC', 'MPL-2.0',
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
export function judgeAdoption(candidate: AdoptionCandidate, adoptedToday: number): AdoptShadowVerdict {
|
|
26
|
+
const reasons: AdoptShadowReason[] = [];
|
|
27
|
+
if (candidate.grade !== 'P1' && candidate.grade !== 'P2') reasons.push('grade');
|
|
28
|
+
if (!(candidate.risk <= 1)) reasons.push('risk');
|
|
29
|
+
if (candidate.money !== 0) reasons.push('money');
|
|
30
|
+
if (candidate.security !== 0) reasons.push('security');
|
|
31
|
+
if (candidate.license === null || !ALLOWED_LICENSES.has(candidate.license)) reasons.push('license');
|
|
32
|
+
if (candidate.touchesPatentCandidate !== false) reasons.push('patent');
|
|
33
|
+
if (!(adoptedToday < 5)) reasons.push('daily-cap');
|
|
34
|
+
return { id: candidate.id, adopt: reasons.length === 0, reasons };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function shadowAdopt(candidates: readonly AdoptionCandidate[]): AdoptShadowVerdict[] {
|
|
38
|
+
const verdicts: AdoptShadowVerdict[] = [];
|
|
39
|
+
let adoptedToday = 0;
|
|
40
|
+
for (const candidate of candidates) {
|
|
41
|
+
const verdict = judgeAdoption(candidate, adoptedToday);
|
|
42
|
+
if (verdict.adopt) adoptedToday++;
|
|
43
|
+
verdicts.push(verdict);
|
|
44
|
+
debug.log('intake.adopt', 'shadow-verdict', {
|
|
45
|
+
id: verdict.id, adopt: verdict.adopt, reasons: verdict.reasons,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return verdicts;
|
|
49
|
+
}
|
|
@@ -21,6 +21,7 @@ import { parse as parseYaml } from 'yaml';
|
|
|
21
21
|
import { debug } from '../debug/log.js';
|
|
22
22
|
import { openSurfaceEventsDb, recallEvents } from '../domains/surface-events.js';
|
|
23
23
|
import { extractJsonBlock, skeletonFallback } from './decompose.js';
|
|
24
|
+
import type { AdoptShadowVerdict } from './adopt-gate.js';
|
|
24
25
|
|
|
25
26
|
export const INTAKE_CHECK_MODE = 'check' as const;
|
|
26
27
|
|
|
@@ -110,6 +111,7 @@ export interface IntakeCheckReport {
|
|
|
110
111
|
/** 비교·시너지 제안 수(근거 없음 포함). */
|
|
111
112
|
readonly proposalCount?: number;
|
|
112
113
|
readonly proposals?: readonly IntakeCompareProposal[];
|
|
114
|
+
readonly adoptShadow?: readonly AdoptShadowVerdict[];
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
/** 선가공 렌즈 여섯. 🅞 원장 · PR #20009. */
|
|
@@ -1826,6 +1828,7 @@ export function intakeCheckReportJson(report: IntakeCheckReport): Record<string,
|
|
|
1826
1828
|
proposalCount: report.proposalCount,
|
|
1827
1829
|
proposals: report.proposals,
|
|
1828
1830
|
} : {}),
|
|
1831
|
+
...(report.adoptShadow !== undefined ? { adoptShadow: report.adoptShadow } : {}),
|
|
1829
1832
|
};
|
|
1830
1833
|
}
|
|
1831
1834
|
|
|
@@ -6,14 +6,18 @@ import { execFileSync } from 'node:child_process';
|
|
|
6
6
|
import { debug } from '../debug/log.js';
|
|
7
7
|
import { ingestIntakeItems, intakeLedgerDir, type IngestResult, type RawIntakeItem } from './items.js';
|
|
8
8
|
|
|
9
|
-
/** 한 판에 열 번
|
|
9
|
+
/** 한 판에 열 번 이하 — 검색 API 비율 제한(인증 시 분당 30회)을 시험 밖에서 상수로 묶는다. */
|
|
10
10
|
export const GITHUB_STAR_QUERIES = [
|
|
11
11
|
'topic:ai-agents',
|
|
12
|
-
'topic:llm-agent',
|
|
13
12
|
'topic:coding-agent',
|
|
14
13
|
'topic:mcp',
|
|
15
14
|
'topic:video-generation',
|
|
16
|
-
'
|
|
15
|
+
'topic:agent-framework',
|
|
16
|
+
'topic:computer-use',
|
|
17
|
+
'topic:local-llm',
|
|
18
|
+
'topic:claude-code',
|
|
19
|
+
'topic:codex',
|
|
20
|
+
'topic:agent-memory',
|
|
17
21
|
] as const;
|
|
18
22
|
|
|
19
23
|
/** `--days` 기본. 최근 이 일수 안에 만들어졌거나 푸시된 저장소만 받는다. */
|
|
@@ -32,6 +36,7 @@ export interface GithubStarRepo {
|
|
|
32
36
|
stars: number;
|
|
33
37
|
createdAt: string;
|
|
34
38
|
pushedAt: string;
|
|
39
|
+
license?: string;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
/** 질의 하나 → 저장소 목록. 실제 구현은 `gh api` · 시험은 가짜. */
|
|
@@ -43,6 +48,7 @@ export interface GithubStarSignals {
|
|
|
43
48
|
stars: number;
|
|
44
49
|
starsDelta?: number;
|
|
45
50
|
starsPerDay?: number;
|
|
51
|
+
baselineDays?: number;
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
export interface CollectGithubQueryCount { query: string; received: number }
|
|
@@ -55,6 +61,8 @@ export interface CollectGithubRepoRow {
|
|
|
55
61
|
stars: number;
|
|
56
62
|
starsDelta?: number;
|
|
57
63
|
starsPerDay?: number;
|
|
64
|
+
baselineDays?: number;
|
|
65
|
+
license?: string;
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
export interface CollectGithubResult {
|
|
@@ -99,6 +107,17 @@ export function precedingSnapshot(snaps: readonly StarSnapshot[], repo: string,
|
|
|
99
107
|
return best;
|
|
100
108
|
}
|
|
101
109
|
|
|
110
|
+
/** 7일 전 또는 그 이전 중 가장 가까운 스냅숏; 없으면 직전 스냅숏. */
|
|
111
|
+
export function surgeBaseline(snaps: readonly StarSnapshot[], repo: string, day: string): StarSnapshot | undefined {
|
|
112
|
+
const cutoff = new Date(Date.parse(`${day}T00:00:00.000Z`) - 7 * 86_400_000).toISOString().slice(0, 10);
|
|
113
|
+
let best: StarSnapshot | undefined;
|
|
114
|
+
for (const s of snaps) {
|
|
115
|
+
if (s.repo !== repo || s.day > cutoff) continue;
|
|
116
|
+
if (!best || s.day > best.day) best = s;
|
|
117
|
+
}
|
|
118
|
+
return best ?? precedingSnapshot(snaps, repo, day);
|
|
119
|
+
}
|
|
120
|
+
|
|
102
121
|
function utcDay(iso: string): string {
|
|
103
122
|
const t = Date.parse(iso);
|
|
104
123
|
if (!Number.isFinite(t)) return '';
|
|
@@ -128,7 +147,7 @@ export function signalsFromSnapshot(stars: number, prev: StarSnapshot | undefine
|
|
|
128
147
|
if (!prev) return { stars };
|
|
129
148
|
const span = Math.max(1, daysBetween(prev.day, day));
|
|
130
149
|
const starsDelta = stars - prev.stars;
|
|
131
|
-
return { stars, starsDelta, starsPerDay: starsDelta / span };
|
|
150
|
+
return { stars, starsDelta, starsPerDay: starsDelta / span, baselineDays: span };
|
|
132
151
|
}
|
|
133
152
|
|
|
134
153
|
export function appendGithubStarSnapshots(root: string, rows: StarSnapshot[]): void {
|
|
@@ -154,13 +173,13 @@ export interface CollectGithubOpts {
|
|
|
154
173
|
/** 스냅숏·비교의 «오늘». 생략하면 UTC 날짜. */
|
|
155
174
|
day?: string;
|
|
156
175
|
now?: () => Date;
|
|
157
|
-
/** 등록 원천이 질의 하나만 수집할 때; 생략 시
|
|
176
|
+
/** 등록 원천이 질의 하나만 수집할 때; 생략 시 관심 질의 전부 (최대 10개). */
|
|
158
177
|
queries?: readonly string[];
|
|
159
178
|
}
|
|
160
179
|
|
|
161
180
|
/**
|
|
162
181
|
* 관심 주제 질의마다 별 순 검색을 부르고, 최근 창 안의 저장소만 남긴 뒤 질의 사이에서 중복을 없앤다.
|
|
163
|
-
* 저장소마다 오늘 별 수 스냅숏을 덧붙이고, 직전 스냅숏과 비교한 신호를 원장에 넣는다.
|
|
182
|
+
* 저장소마다 오늘 별 수 스냅숏을 덧붙이고, 7일 기준(없으면 직전) 스냅숏과 비교한 신호를 원장에 넣는다.
|
|
164
183
|
* dryRun 이면 스냅숏·원장을 바꾸지 않는다.
|
|
165
184
|
*/
|
|
166
185
|
export async function collectGithubStars(
|
|
@@ -175,7 +194,7 @@ export async function collectGithubStars(
|
|
|
175
194
|
const found: GithubStarRepo[] = [];
|
|
176
195
|
// «급상승»은 최근에 생긴 저장소 중 별 순이다 — 전체 기간 별 순이면 활발한 거대 저장소가 매일 같은 목록으로 나온다(2026-09-26 검토).
|
|
177
196
|
const windowStart = new Date(Date.parse(`${day}T00:00:00.000Z`) - days * 86_400_000).toISOString().slice(0, 10);
|
|
178
|
-
for (const query of opts.queries ?? GITHUB_STAR_QUERIES) {
|
|
197
|
+
for (const query of (opts.queries ?? GITHUB_STAR_QUERIES).slice(0, 10)) {
|
|
179
198
|
const rows = await search(`${query} created:>=${windowStart}`, perQuery);
|
|
180
199
|
const recent = rows.filter((r) => isRecentRepo(r, day, days));
|
|
181
200
|
queries.push({ query, received: recent.length });
|
|
@@ -186,20 +205,24 @@ export async function collectGithubStars(
|
|
|
186
205
|
const repos: CollectGithubRepoRow[] = unique.map((repo) => {
|
|
187
206
|
const prev = precedingSnapshot(prior, repo.fullName, day);
|
|
188
207
|
const signals = signalsFromSnapshot(repo.stars, prev, day);
|
|
208
|
+
const surge = signalsFromSnapshot(repo.stars, surgeBaseline(prior, repo.fullName, day), day);
|
|
189
209
|
return {
|
|
190
210
|
repo: repo.fullName,
|
|
191
211
|
url: repo.url,
|
|
192
212
|
title: repo.fullName,
|
|
193
213
|
text: repo.description,
|
|
194
214
|
stars: signals.stars,
|
|
215
|
+
...(repo.license !== undefined ? { license: repo.license } : {}),
|
|
195
216
|
...(signals.starsDelta !== undefined ? { starsDelta: signals.starsDelta } : {}),
|
|
196
|
-
...(
|
|
217
|
+
...(surge.starsPerDay !== undefined ? { starsPerDay: surge.starsPerDay } : {}),
|
|
218
|
+
...(surge.baselineDays !== undefined ? { baselineDays: surge.baselineDays } : {}),
|
|
197
219
|
};
|
|
198
|
-
});
|
|
220
|
+
}).sort((a, b) => (b.starsPerDay ?? -Infinity) - (a.starsPerDay ?? -Infinity) || b.stars - a.stars);
|
|
199
221
|
const raws: RawIntakeItem[] = repos.map((row) => ({
|
|
200
222
|
url: row.url,
|
|
201
223
|
title: row.title,
|
|
202
|
-
|
|
224
|
+
// 모든 행 첫 줄에 라이선스: SPDX · 확인된 부재는 «없음» · 응답이 알려 주지 않으면 «미상»(없음으로 읽지 않는다).
|
|
225
|
+
text: `라이선스: ${row.license ?? '미상'}\n${row.text}`,
|
|
203
226
|
signals: {
|
|
204
227
|
stars: row.stars,
|
|
205
228
|
...(row.starsDelta !== undefined ? { starsDelta: row.starsDelta } : {}),
|
|
@@ -226,23 +249,10 @@ interface GhSearchItem {
|
|
|
226
249
|
stargazers_count?: number;
|
|
227
250
|
created_at?: string;
|
|
228
251
|
pushed_at?: string;
|
|
252
|
+
license?: { spdx_id?: string | null } | null;
|
|
229
253
|
}
|
|
230
254
|
|
|
231
|
-
|
|
232
|
-
export const ghApiSearchRepos: SearchGithubRepos = async (query, perQuery) => {
|
|
233
|
-
const perPage = Math.min(100, Math.max(1, perQuery));
|
|
234
|
-
const q = `${query} sort:stars`;
|
|
235
|
-
const out = execFileSync('gh', [
|
|
236
|
-
'api',
|
|
237
|
-
'-X', 'GET',
|
|
238
|
-
'search/repositories',
|
|
239
|
-
'-f', `q=${q}`,
|
|
240
|
-
'-f', 'sort=stars',
|
|
241
|
-
'-f', 'order=desc',
|
|
242
|
-
'-f', `per_page=${perPage}`,
|
|
243
|
-
'--jq', '.items',
|
|
244
|
-
], { encoding: 'utf8', timeout: 30_000 });
|
|
245
|
-
const items = JSON.parse(out) as GhSearchItem[];
|
|
255
|
+
export function reposFromGhSearchItems(items: GhSearchItem[], perQuery: number): GithubStarRepo[] {
|
|
246
256
|
if (!Array.isArray(items)) return [];
|
|
247
257
|
const repos: GithubStarRepo[] = [];
|
|
248
258
|
for (const item of items) {
|
|
@@ -254,7 +264,29 @@ export const ghApiSearchRepos: SearchGithubRepos = async (query, perQuery) => {
|
|
|
254
264
|
stars: Number(item.stargazers_count ?? 0),
|
|
255
265
|
createdAt: item.created_at ?? '',
|
|
256
266
|
pushedAt: item.pushed_at ?? '',
|
|
267
|
+
...(item.license === null
|
|
268
|
+
? { license: '없음' }
|
|
269
|
+
: typeof item.license?.spdx_id === 'string' && item.license.spdx_id
|
|
270
|
+
? { license: item.license.spdx_id }
|
|
271
|
+
: {}),
|
|
257
272
|
});
|
|
258
273
|
}
|
|
259
274
|
return repos.slice(0, perQuery);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** `gh api search/repositories` 를 `sort=stars` 로 한 번 부른다. */
|
|
278
|
+
export const ghApiSearchRepos: SearchGithubRepos = async (query, perQuery) => {
|
|
279
|
+
const perPage = Math.min(100, Math.max(1, perQuery));
|
|
280
|
+
const q = `${query} sort:stars`;
|
|
281
|
+
const out = execFileSync('gh', [
|
|
282
|
+
'api',
|
|
283
|
+
'-X', 'GET',
|
|
284
|
+
'search/repositories',
|
|
285
|
+
'-f', `q=${q}`,
|
|
286
|
+
'-f', 'sort=stars',
|
|
287
|
+
'-f', 'order=desc',
|
|
288
|
+
'-f', `per_page=${perPage}`,
|
|
289
|
+
'--jq', '.items',
|
|
290
|
+
], { encoding: 'utf8', timeout: 30_000 });
|
|
291
|
+
return reposFromGhSearchItems(JSON.parse(out) as GhSearchItem[], perQuery);
|
|
260
292
|
};
|
|
@@ -5,7 +5,7 @@ import { awaitGlobalPersonaLoad, getGlobalPersonaRegistry } from '../persona/glo
|
|
|
5
5
|
import type { PersonaSource } from '../persona/mention-parser.js';
|
|
6
6
|
import { dispatchCeoTask, type CeoCommandDeps } from '../seat-dispatch/ceo-commands.js';
|
|
7
7
|
import { ceoTaskDeps, classifyCeoIntent } from '../seat-dispatch/ceo-intent.js';
|
|
8
|
-
import { askSeat, parseSeatAsk, type SeatAskDeps } from '../seat-dispatch/seat-ask.js';
|
|
8
|
+
import { askSeat, askSeatAs, parseSeatAsk, type SeatAskDeps } from '../seat-dispatch/seat-ask.js';
|
|
9
9
|
import { parseSeatAddress, resolveSeat } from '../seat-address/seat-address.js';
|
|
10
10
|
import { getUserConfig, type UserConfig } from '../user-config.js';
|
|
11
11
|
import { answerAsSeat } from './seat-answer.js';
|
|
@@ -110,7 +110,13 @@ export async function handleDiscordSeatWork(
|
|
|
110
110
|
const seatId = canonicalSeatId(seats[0].seat.id);
|
|
111
111
|
if (auth && ['OP', 'TC', 'MK', 'UX'].includes(seatId)) {
|
|
112
112
|
const intent = classifyCeoIntent(body);
|
|
113
|
-
if (intent === 'task')
|
|
113
|
+
if (intent === 'task') {
|
|
114
|
+
if (deps.askDeps) return askSeatAs(seatId as 'OP' | 'TC' | 'MK' | 'UX', body,
|
|
115
|
+
{ channel: 'discord', channelId: msg.channelId, messageId: msg.messageId,
|
|
116
|
+
...(msg.threadId ? { threadId: msg.threadId } : {}) },
|
|
117
|
+
deps.commandDeps ?? ceoTaskDeps(auth.cfg, auth.id), deps.askDeps);
|
|
118
|
+
return sendTask(seatId, body, auth.cfg, auth.id);
|
|
119
|
+
}
|
|
114
120
|
const answer = await (deps.answer ?? answerAsSeat)(seats[0].name, body);
|
|
115
121
|
debug.log('seat.dispatch', 'intent', { seat: seatId, intent, via: 'discord', outcome: answer ? 'answered' : 'intake' });
|
|
116
122
|
if (answer) return answer.text;
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
5
|
+
import { normalizeClaim, type AddBriefItem } from '../briefing/brief-items.js';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
import { debug } from '../debug/log.js';
|
|
8
|
+
import { listChecklist, setItem, type ChecklistItem } from '../release-loop/checklist.js';
|
|
9
|
+
import { placeCell, type PlacementDeps } from '../release-loop/placement.js';
|
|
10
|
+
import { listSchedules } from '../release-loop/release-schedule.js';
|
|
11
|
+
import { getUserConfig } from '../user-config.js';
|
|
12
|
+
import { dateKey } from '../time/format.js';
|
|
13
|
+
import { cardIndexPath, CardStore, type TaskCard } from '../task-cards/card-store.js';
|
|
14
|
+
import { loadIntakeLedger, type IntakeItem } from './items.js';
|
|
15
|
+
import { parseRubric, rubricGrade, rubricScore } from '../release-loop/rubric.js';
|
|
16
|
+
|
|
17
|
+
export type RequirementSource = 'directive' | 'intake' | 'grounding' | 'coordination' | 'linear';
|
|
18
|
+
export interface RequirementCandidate {
|
|
19
|
+
id: string;
|
|
20
|
+
source: RequirementSource;
|
|
21
|
+
text: string;
|
|
22
|
+
link: string;
|
|
23
|
+
receivedAt: string;
|
|
24
|
+
priority?: 'P0' | 'P1' | 'P2';
|
|
25
|
+
risk?: number;
|
|
26
|
+
cost?: number;
|
|
27
|
+
security?: number;
|
|
28
|
+
license?: string;
|
|
29
|
+
patentPrivatePath?: boolean;
|
|
30
|
+
owner?: string;
|
|
31
|
+
deadlineVersion?: string;
|
|
32
|
+
}
|
|
33
|
+
export type IncomingRequirement = Omit<RequirementCandidate, 'source'>;
|
|
34
|
+
export interface RequirementSources {
|
|
35
|
+
directives?: readonly DirectiveRow[];
|
|
36
|
+
intake?: readonly IntakeItem[];
|
|
37
|
+
grounding?: readonly IncomingRequirement[];
|
|
38
|
+
coordination?: readonly IncomingRequirement[];
|
|
39
|
+
linear?: readonly IncomingRequirement[];
|
|
40
|
+
}
|
|
41
|
+
export interface RequirementDecision {
|
|
42
|
+
candidate: RequirementCandidate;
|
|
43
|
+
sources: RequirementCandidate[];
|
|
44
|
+
status: 'adopted' | 'would-adopt' | 'proposal';
|
|
45
|
+
cellId?: string;
|
|
46
|
+
version?: string;
|
|
47
|
+
rubric: string;
|
|
48
|
+
reason?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface RequirementFunnelResult {
|
|
51
|
+
counts: Record<RequirementSource, number>;
|
|
52
|
+
candidates: number;
|
|
53
|
+
decisions: RequirementDecision[];
|
|
54
|
+
unlinkedDirectives: RequirementCandidate[];
|
|
55
|
+
briefItems: AddBriefItem[];
|
|
56
|
+
}
|
|
57
|
+
export interface FunnelOptions {
|
|
58
|
+
sources: RequirementSources;
|
|
59
|
+
/** Older directives are only checked for unlinked P0 backlog, not counted as today's arrivals. */
|
|
60
|
+
historicalDirectives?: readonly DirectiveRow[];
|
|
61
|
+
cells: readonly { version: string; item: ChecklistItem }[];
|
|
62
|
+
now: Date;
|
|
63
|
+
mode?: 'shadow' | 'live';
|
|
64
|
+
/** Required for live external adoption; the unresolved daily limit has no implicit value. */
|
|
65
|
+
dailyCap?: number;
|
|
66
|
+
adoptedToday?: number;
|
|
67
|
+
allowedLicenses?: readonly string[];
|
|
68
|
+
placement?: PlacementDeps;
|
|
69
|
+
adopt?: (candidate: RequirementCandidate, version: string, cellId: string, evidence: string) => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** How many of the last 24 hours' unlinked directives the single P0 brief line names (the full backlog is one count). */
|
|
73
|
+
export const UNLINKED_DIRECTIVES_SHOWN = 5;
|
|
74
|
+
/** ABSORB-ADOPT daily limit when config sets none (OP 10-06 22:59 · the cell's own number). */
|
|
75
|
+
export const DEFAULT_DAILY_CAP = 5;
|
|
76
|
+
/** Directive row fields the funnel reads — kept local because `src/directives/directive-index` is excluded from the public export. */
|
|
77
|
+
export interface DirectiveRow { ts: string; track: string | null; text: string; source_file: string; line_no: number }
|
|
78
|
+
|
|
79
|
+
/** Loads the private directive index at run time only (same pattern as decisions/proact-meter), so public builds bundle cleanly. */
|
|
80
|
+
function openDirectiveIndex(stateDir: string): { dailyRequirements(since: string, until: string): DirectiveRow[]; close(): void } {
|
|
81
|
+
const spec = ['..', 'directives', 'directive-index.js'].join('/');
|
|
82
|
+
const mod = createRequire(import.meta.url)(spec) as { DirectiveIndex: new (o: { stateDir?: string }) => ReturnType<typeof openDirectiveIndex> };
|
|
83
|
+
return new mod.DirectiveIndex({ stateDir });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const oneLine = (value: string) => value.replace(/\s+/g, ' ').trim();
|
|
87
|
+
const idFor = (source: RequirementSource, key: string) => createHash('sha256').update(`${source}:${key}`).digest('hex').slice(0, 16);
|
|
88
|
+
|
|
89
|
+
export function directiveCandidates(rows: readonly DirectiveRow[]): RequirementCandidate[] {
|
|
90
|
+
return rows.map(row => ({ id: idFor('directive', `${row.source_file}:${row.line_no}`), source: 'directive',
|
|
91
|
+
text: oneLine(row.text), link: `${row.source_file}#L${row.line_no}`, receivedAt: row.ts,
|
|
92
|
+
priority: 'P2', owner: row.track && /^[A-Z]{2,8}$/.test(row.track) ? row.track : 'OP' }));
|
|
93
|
+
}
|
|
94
|
+
export function intakeCandidates(rows: readonly IntakeItem[]): RequirementCandidate[] {
|
|
95
|
+
return rows.map(row => ({ id: row.id, source: 'intake', text: oneLine(row.text || row.title || ''),
|
|
96
|
+
link: row.url || `intake:${row.id}`, receivedAt: row.observedAt }));
|
|
97
|
+
}
|
|
98
|
+
export function suppliedCandidates(source: 'grounding' | 'coordination' | 'linear', rows: readonly IncomingRequirement[]): RequirementCandidate[] {
|
|
99
|
+
return rows.map(row => ({ ...row, source, text: oneLine(row.text) }));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function linkFor(candidate: RequirementCandidate, cells: FunnelOptions['cells']): FunnelOptions['cells'][number] | undefined {
|
|
103
|
+
return cells.find(({ item }) => !!candidate.link && !!item.evidence && item.evidence.includes(candidate.link));
|
|
104
|
+
}
|
|
105
|
+
function gate(candidate: RequirementCandidate, options: FunnelOptions, used: number): string | null {
|
|
106
|
+
if (candidate.priority !== 'P1' && candidate.priority !== 'P2') return 'P1/P2 판정 없음';
|
|
107
|
+
if (candidate.priority === 'P1' && !candidate.deadlineVersion) return 'P1 마감 판 미확인';
|
|
108
|
+
if (candidate.risk === undefined || !Number.isFinite(candidate.risk) || candidate.risk > 1 || candidate.risk < 0) return '위험≤1 미확인';
|
|
109
|
+
if (candidate.cost !== 0) return '돈 0 미확인';
|
|
110
|
+
if (candidate.security !== 0) return '보안 0 미확인';
|
|
111
|
+
if (!candidate.license || !options.allowedLicenses?.includes(candidate.license)) return '허용 라이선스 미확인';
|
|
112
|
+
if (candidate.patentPrivatePath !== false) return '특허 비공개 경로 무접촉 미확인';
|
|
113
|
+
if (options.dailyCap === undefined || !Number.isSafeInteger(options.dailyCap) || options.dailyCap < 0) return '하루 상한 미설정';
|
|
114
|
+
if (used >= options.dailyCap) return '하루 상한 도달';
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** RUBRIC-PLACE: use the release placement engine's priority, deadline, capacity and predecessor checks, not an invented score. */
|
|
119
|
+
export function runRequirementFunnel(options: FunnelOptions): RequirementFunnelResult {
|
|
120
|
+
const { sources, cells, now } = options;
|
|
121
|
+
const arrivals = [
|
|
122
|
+
...directiveCandidates(sources.directives ?? []), ...intakeCandidates(sources.intake ?? []),
|
|
123
|
+
...suppliedCandidates('grounding', sources.grounding ?? []),
|
|
124
|
+
...suppliedCandidates('coordination', sources.coordination ?? []),
|
|
125
|
+
...suppliedCandidates('linear', sources.linear ?? []),
|
|
126
|
+
].filter(candidate => candidate.text && Number.isFinite(Date.parse(candidate.receivedAt)) && Date.parse(candidate.receivedAt) <= now.getTime());
|
|
127
|
+
const counts: RequirementFunnelResult['counts'] = { directive: 0, intake: 0, grounding: 0, coordination: 0, linear: 0 };
|
|
128
|
+
for (const candidate of arrivals) counts[candidate.source]++;
|
|
129
|
+
const groups = new Map<string, RequirementCandidate[]>();
|
|
130
|
+
for (const candidate of arrivals) {
|
|
131
|
+
const key = normalizeClaim(candidate.text);
|
|
132
|
+
groups.set(key, [...(groups.get(key) ?? []), candidate]);
|
|
133
|
+
}
|
|
134
|
+
const decisions: RequirementDecision[] = [];
|
|
135
|
+
let used = options.adoptedToday ?? 0;
|
|
136
|
+
for (const members of groups.values()) {
|
|
137
|
+
members.sort((a, b) => a.receivedAt.localeCompare(b.receivedAt));
|
|
138
|
+
const candidate = members.find(member => member.source === 'directive') ?? members[0]!;
|
|
139
|
+
const linked = members.map(member => linkFor(member, cells)).find(Boolean);
|
|
140
|
+
const evidence = members.map(member => `${member.link} (${member.receivedAt})`).join(' · ');
|
|
141
|
+
if (linked) {
|
|
142
|
+
decisions.push({ candidate, sources: members, status: 'adopted', cellId: linked.item.id, version: linked.version,
|
|
143
|
+
rubric: `RUBRIC-PLACE ${linked.item.priority ?? '미지정'} · ${linked.version}` });
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const isDirective = members.some(member => member.source === 'directive');
|
|
147
|
+
const reason = isDirective ? null : gate(candidate, options, used);
|
|
148
|
+
if (reason) {
|
|
149
|
+
decisions.push({ candidate, sources: members, status: 'proposal', reason, rubric: `ABSORB-ADOPT · ${reason}` });
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const cellId = candidate.id;
|
|
153
|
+
const priority = candidate.priority ?? 'P1';
|
|
154
|
+
const rubricInput = { id: cellId, title: candidate.text, owner: candidate.owner ?? 'OP', priority,
|
|
155
|
+
predecessors: [], ...(priority === 'P1' && candidate.deadlineVersion ? { deadlineVersion: candidate.deadlineVersion } : {}) };
|
|
156
|
+
let placement: ReturnType<typeof placeCell>;
|
|
157
|
+
try { placement = placeCell(rubricInput, { ...options.placement, now, dryRun: true }); }
|
|
158
|
+
catch (error) {
|
|
159
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
160
|
+
decisions.push({ candidate, sources: members, status: isDirective ? 'would-adopt' : 'proposal', reason,
|
|
161
|
+
rubric: `RUBRIC-PLACE · 판 배치 보류: ${reason}` });
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const rubric = `RUBRIC-PLACE ${priority} · ${placement.version}: ${placement.reason}`;
|
|
165
|
+
if (options.mode === 'live') {
|
|
166
|
+
if (!options.adopt) throw new Error('편입 기록자가 설정되지 않았다');
|
|
167
|
+
options.adopt(candidate, placement.version, cellId, evidence);
|
|
168
|
+
}
|
|
169
|
+
decisions.push({ candidate, sources: members, status: options.mode === 'live' ? 'adopted' : 'would-adopt',
|
|
170
|
+
cellId, version: placement.version, rubric });
|
|
171
|
+
if (!isDirective) used++;
|
|
172
|
+
}
|
|
173
|
+
const unlinkedDirectives = [...directiveCandidates(options.historicalDirectives ?? []).filter(candidate =>
|
|
174
|
+
candidate.text && Number.isFinite(Date.parse(candidate.receivedAt)) && Date.parse(candidate.receivedAt) <= now.getTime()
|
|
175
|
+
&& !linkFor(candidate, cells) && !arrivals.some(arrival => arrival.id === candidate.id)),
|
|
176
|
+
...decisions.filter(decision => decision.status !== 'adopted')
|
|
177
|
+
.flatMap(decision => decision.sources.filter(source => source.source === 'directive'))]
|
|
178
|
+
.sort((a, b) => a.receivedAt.localeCompare(b.receivedAt));
|
|
179
|
+
const briefItems: AddBriefItem[] = [];
|
|
180
|
+
const summary = `들어온 요구 ${groups.size}(문별 지시 ${counts.directive} · 흡수 ${counts.intake} · 그라운딩 ${counts.grounding} · 조율 ${counts.coordination} · Linear ${counts.linear}) · 편입 ${decisions.filter(d => d.status === 'adopted').length} · 했을 것 ${decisions.filter(d => d.status === 'would-adopt').length} · 제안 ${decisions.filter(d => d.status === 'proposal').length}`;
|
|
181
|
+
if (arrivals.length || unlinkedDirectives.length) briefItems.push({ text: summary, domain: '흡수', priority: 'P1', source: 'requirement-funnel' });
|
|
182
|
+
for (const decision of decisions) {
|
|
183
|
+
const { candidate } = decision;
|
|
184
|
+
const status = decision.status === 'adopted' ? `편입 ${decision.cellId} · 판 ${decision.version}`
|
|
185
|
+
: decision.status === 'would-adopt' ? decision.version ? `했을 것 ${decision.cellId} · 판 ${decision.version}` : '편입 대상 · 판 배치 대기'
|
|
186
|
+
: `제안: ${decision.reason}`;
|
|
187
|
+
briefItems.push({ text: `${status} · ${candidate.text} · ${decision.rubric}`, domain: decision.status === 'proposal' ? '흡수' : '판',
|
|
188
|
+
priority: 'P1', source: 'requirement-funnel', evidence: decision.sources.map(member => `${member.link} (${member.receivedAt})`).join(' · ') });
|
|
189
|
+
}
|
|
190
|
+
// OP 10-06 22:59: the backlog can hold the whole directive history, so only the last 24 hours get named (one P0 line);
|
|
191
|
+
// the full backlog is a single count. Nothing recent → the count alone, at P1, so history cannot flood the brief.
|
|
192
|
+
if (unlinkedDirectives.length) {
|
|
193
|
+
const recent = unlinkedDirectives.filter(directive => now.getTime() - Date.parse(directive.receivedAt) <= 24 * 3_600_000);
|
|
194
|
+
const named = recent.slice(0, UNLINKED_DIRECTIVES_SHOWN);
|
|
195
|
+
const rest = recent.length - named.length;
|
|
196
|
+
const total = `전체 누계 ${unlinkedDirectives.length}`;
|
|
197
|
+
briefItems.push(recent.length
|
|
198
|
+
? { text: `대표 지시 중 아직 칸 없는 것 — 최근 24시간 ${recent.length}: ${named.map(directive => `${directive.text} (${directive.receivedAt})`).join(' · ')}${rest > 0 ? ` · 외 ${rest}` : ''} · ${total}`,
|
|
199
|
+
domain: '판', priority: 'P0', source: 'requirement-funnel', evidence: named.map(directive => directive.link).join(' · ') }
|
|
200
|
+
: { text: `대표 지시 중 아직 칸 없는 것 — 최근 24시간 0 · ${total}`, domain: '판', priority: 'P1', source: 'requirement-funnel' });
|
|
201
|
+
}
|
|
202
|
+
return { counts, candidates: groups.size, decisions, unlinkedDirectives, briefItems };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Existing ledgers only; no source collector or sender is started here. */
|
|
206
|
+
export function readRequirementFunnel(options: { stateDir?: string; now?: Date; mode?: 'shadow' | 'live'; dailyCap?: number;
|
|
207
|
+
allowedLicenses?: readonly string[]; grounding?: readonly IncomingRequirement[]; coordination?: readonly IncomingRequirement[];
|
|
208
|
+
linear?: readonly IncomingRequirement[]; placement?: PlacementDeps } = {}): RequirementFunnelResult {
|
|
209
|
+
const stateDir = options.stateDir ?? elanousStateRoot();
|
|
210
|
+
const now = options.now ?? new Date();
|
|
211
|
+
const config = (getUserConfig().raw.intake as { requirementFunnel?: { mode?: 'shadow' | 'live'; dailyCap?: number; allowedLicenses?: string[] } } | undefined)?.requirementFunnel;
|
|
212
|
+
const mode = options.mode ?? config?.mode ?? 'shadow';
|
|
213
|
+
if (mode !== 'shadow' && mode !== 'live') throw new Error(`알 수 없는 요구 깔때기 모드: ${mode}`);
|
|
214
|
+
const dailyCap = options.dailyCap ?? config?.dailyCap ?? DEFAULT_DAILY_CAP;
|
|
215
|
+
const allowedLicenses = options.allowedLicenses ?? config?.allowedLicenses;
|
|
216
|
+
let directives: DirectiveRow[] = [];
|
|
217
|
+
let historicalDirectives: DirectiveRow[] = [];
|
|
218
|
+
const since = new Date(Date.parse(`${dateKey(now, { timeZone: 'Asia/Seoul' })}T00:00:00+09:00`)).toISOString();
|
|
219
|
+
try {
|
|
220
|
+
const index = openDirectiveIndex(stateDir);
|
|
221
|
+
try {
|
|
222
|
+
directives = index.dailyRequirements(since, now.toISOString());
|
|
223
|
+
historicalDirectives = index.dailyRequirements('1970-01-01T00:00:00.000Z', since);
|
|
224
|
+
} finally { index.close(); }
|
|
225
|
+
} catch (error) {
|
|
226
|
+
// Public builds have no directive index; the other doors still report.
|
|
227
|
+
debug.log('intake.requirement-funnel', 'directives-unavailable', { error: error instanceof Error ? error.message : String(error) });
|
|
228
|
+
}
|
|
229
|
+
const schedules = listSchedules();
|
|
230
|
+
const cells = schedules.flatMap(schedule => listChecklist(schedule.version).items.map(item => ({ version: schedule.version, item })));
|
|
231
|
+
const ledger = loadIntakeLedger(stateDir);
|
|
232
|
+
const day = dateKey(now, { timeZone: 'Asia/Seoul' });
|
|
233
|
+
const todayCount = cells.filter(({ item }) => {
|
|
234
|
+
const adoptedAt = item.evidence?.match(/ABSORB-ADOPT@(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)/)?.[1];
|
|
235
|
+
return adoptedAt && dateKey(adoptedAt, { timeZone: 'Asia/Seoul' }) === day;
|
|
236
|
+
}).length;
|
|
237
|
+
const cardRequests: Record<'coordination' | 'linear', IncomingRequirement[]> = { coordination: [], linear: [] };
|
|
238
|
+
if (existsSync(cardIndexPath(stateDir))) {
|
|
239
|
+
const cards = new CardStore(stateDir, true);
|
|
240
|
+
try {
|
|
241
|
+
for (const card of cards.listCards()) {
|
|
242
|
+
const section = card.sections.find(entry => entry.key === 'intake');
|
|
243
|
+
if (!section) continue;
|
|
244
|
+
let raw: unknown;
|
|
245
|
+
try { raw = JSON.parse(section.content); } catch { continue; }
|
|
246
|
+
if (!raw || typeof raw !== 'object') continue;
|
|
247
|
+
const input = raw as { source?: string; text?: string; ref?: string; replyTo?: { surface?: string } };
|
|
248
|
+
const source = input.source === 'linear' ? 'linear' : input.source === 'coordination' ? 'coordination' : null;
|
|
249
|
+
if (!source || !input.text) continue;
|
|
250
|
+
cardRequests[source].push({ id: card.goalId, text: oneLine(input.text.split('\n')[0] ?? ''),
|
|
251
|
+
link: input.ref || card.goalId, receivedAt: card.createdAt });
|
|
252
|
+
}
|
|
253
|
+
} finally { cards.close(); }
|
|
254
|
+
}
|
|
255
|
+
const fromQueue = (name: string): IncomingRequirement[] => {
|
|
256
|
+
const path = join(stateDir, 'intake', name);
|
|
257
|
+
if (!existsSync(path)) return [];
|
|
258
|
+
return readFileSync(path, 'utf8').split('\n').flatMap(line => {
|
|
259
|
+
if (!line.trim()) return [];
|
|
260
|
+
try {
|
|
261
|
+
const value: unknown = JSON.parse(line);
|
|
262
|
+
if (!value || typeof value !== 'object') return [];
|
|
263
|
+
const row = value as Partial<IncomingRequirement> & { path?: string };
|
|
264
|
+
const item = row.id ? ledger.items.get(row.id) : undefined;
|
|
265
|
+
const text = row.text ?? item?.text ?? item?.title;
|
|
266
|
+
const link = row.link ?? item?.url ?? row.path;
|
|
267
|
+
const receivedAt = row.receivedAt ?? (row as { at?: string }).at ?? item?.observedAt;
|
|
268
|
+
return typeof row.id === 'string' && typeof text === 'string' && typeof link === 'string' && typeof receivedAt === 'string'
|
|
269
|
+
? [{ ...row, id: row.id, text, link, receivedAt }] : [];
|
|
270
|
+
} catch { return []; }
|
|
271
|
+
});
|
|
272
|
+
};
|
|
273
|
+
// The grounding queue and the card store are cumulative; only today's rows are today's arrivals.
|
|
274
|
+
const today = (rows: readonly IncomingRequirement[]) => rows.filter(row => Date.parse(row.receivedAt) >= Date.parse(since));
|
|
275
|
+
return runRequirementFunnel({ sources: { directives, intake: [...ledger.items.values()].filter(item => Date.parse(item.observedAt) >= Date.parse(since)),
|
|
276
|
+
grounding: today(options.grounding ?? fromQueue('outbox/grounding.jsonl')),
|
|
277
|
+
coordination: today(options.coordination ?? cardRequests.coordination), linear: today(options.linear ?? cardRequests.linear) }, historicalDirectives, cells, now, mode,
|
|
278
|
+
dailyCap, adoptedToday: todayCount, allowedLicenses, placement: { ...options.placement, schedules },
|
|
279
|
+
adopt: (candidate, version, cellId, evidence) => {
|
|
280
|
+
placeCell({ id: cellId, title: candidate.text, owner: candidate.owner ?? 'OP', priority: candidate.priority ?? 'P1',
|
|
281
|
+
predecessors: [], ...(candidate.deadlineVersion ? { deadlineVersion: candidate.deadlineVersion } : {}) }, { ...options.placement, now, schedules });
|
|
282
|
+
setItem(version, cellId, { evidence: `${evidence} · ABSORB-ADOPT@${now.toISOString()}` }, 'OP');
|
|
283
|
+
} });
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ── `intake candidates` — 읽기 전용 요구 후보 목록(지시·흡수·소원 · 명시 루브릭만 점수) ──
|
|
287
|
+
export interface FunnelCandidate {
|
|
288
|
+
source: 'directive' | 'intake' | 'wish';
|
|
289
|
+
id: string;
|
|
290
|
+
title: string;
|
|
291
|
+
score: number | null;
|
|
292
|
+
grade: 'P1' | 'P2' | 'P3' | 'P4' | null;
|
|
293
|
+
}
|
|
294
|
+
/** The candidate list keys directives by session — the funnel's local `DirectiveRow` plus the index's `session_id`. */
|
|
295
|
+
export type FunnelDirectiveRow = DirectiveRow & { session_id: string };
|
|
296
|
+
|
|
297
|
+
function funnelCandidate(source: FunnelCandidate['source'], id: string, title: string, original: string): FunnelCandidate {
|
|
298
|
+
const rubric = parseRubric(original);
|
|
299
|
+
const score = rubric === null ? null : rubricScore(rubric);
|
|
300
|
+
return { source, id, title: title.split(/\r?\n/, 1)[0]!.slice(0, 80), score, grade: score === null ? null : rubricGrade(score) };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function collectFunnelCandidates({ directives, intakeItems, cards }: {
|
|
304
|
+
directives: readonly FunnelDirectiveRow[];
|
|
305
|
+
intakeItems: readonly IntakeItem[];
|
|
306
|
+
cards: readonly TaskCard[];
|
|
307
|
+
}): FunnelCandidate[] {
|
|
308
|
+
return [
|
|
309
|
+
...directives.map((row) => funnelCandidate('directive', `${row.session_id}:${row.line_no}`, row.text, row.text)),
|
|
310
|
+
...intakeItems.filter((item) => item.status !== 'discarded')
|
|
311
|
+
.map((item) => funnelCandidate('intake', item.id, item.title ?? item.text ?? item.url ?? '', [item.title, item.text, item.url].filter(Boolean).join('\n'))),
|
|
312
|
+
...cards.filter((card) => card.status === 'open' && card.goalId.startsWith('wish:'))
|
|
313
|
+
.map((card) => funnelCandidate('wish', card.goalId, card.title, card.title)),
|
|
314
|
+
].sort((a, b) => a.score === null && b.score === null ? 0
|
|
315
|
+
: a.score === null ? 1 : b.score === null ? -1 : b.score - a.score);
|
|
316
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { LessonLedger, type LessonRow } from '../lessons/lesson-ledger.js';
|
|
3
|
-
import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
|
|
3
|
+
import { DecisionLedger, type DecisionEntry, type SeatDecisionRecord } from '../decisions/decision-ledger.js';
|
|
4
4
|
import { devVersion, listChecklist, type Checklist } from '../release-loop/checklist.js';
|
|
5
5
|
import { debug } from '../debug/log.js';
|
|
6
6
|
|
|
@@ -31,6 +31,7 @@ export interface KnowResult {
|
|
|
31
31
|
export interface KnowFindDeps {
|
|
32
32
|
directives: (query: string) => DirectiveRow[];
|
|
33
33
|
decisions: () => DecisionEntry[];
|
|
34
|
+
seatDecisions: () => SeatDecisionRecord[];
|
|
34
35
|
checklist: (version: string) => Checklist;
|
|
35
36
|
lessons: (query: string) => LessonRow[];
|
|
36
37
|
versions: () => string[];
|
|
@@ -49,6 +50,7 @@ const defaults: KnowFindDeps = {
|
|
|
49
50
|
try { return index.search(query); } finally { index.close(); }
|
|
50
51
|
},
|
|
51
52
|
decisions: () => new DecisionLedger().list({ status: 'all' }),
|
|
53
|
+
seatDecisions: () => new DecisionLedger().seatReport(),
|
|
52
54
|
checklist: (version) => listChecklist(version),
|
|
53
55
|
lessons: (query) => new LessonLedger().find(query),
|
|
54
56
|
versions,
|
|
@@ -84,6 +86,13 @@ export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}): KnowR
|
|
|
84
86
|
ref: row.refs?.[0] ?? `decisions show ${row.id}` });
|
|
85
87
|
}
|
|
86
88
|
});
|
|
89
|
+
attempt('decision', () => {
|
|
90
|
+
for (const row of read.seatDecisions()) {
|
|
91
|
+
if (!matches([row.title, row.decision, row.delegation].join(' '), words)) continue;
|
|
92
|
+
rows.push({ source: 'decision', id: row.id, title: row.title, status: 'seat-posthoc',
|
|
93
|
+
at: row.decidedAt ?? row.recordedAt, current: false, ref: `decisions seat-report --seat ${row.seat}` });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
87
96
|
attempt('checklist', () => {
|
|
88
97
|
for (const version of new Set(read.versions())) {
|
|
89
98
|
for (const row of read.checklist(version).items) {
|