elanous 0.2.16 → 0.2.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/P6YTVgzuZz5oTZhY4Din_/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1307-b46d45b705e8c9ae.js → 1307-30dff22c9e1f3073.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6158.9c0449a9a8d0064b.js → 6158.af2e93b7adff709b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d92f7ee3994e84c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-3cb52e5ea8fc2fa5.js → page-4733c967b56a4277.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d22fd849cc22f104.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c1e20d7d338713f1.js → page-bb742f995c6769e7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-482c5dbde699947c.js → page-02b073a2ac793b83.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-8a9b3594bc23289b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-a0eb02638b1e6f31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-a0e2a6d1bfb52fc5.js → page-308111abadf35670.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-c53598cfce6156f2.js → page-5d97b2901e0d1ea6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-13a63953f53c6a23.js → page-c39554c321576982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-5d3c8429d04f3831.js → page-8c3d8e63ae2e1c98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-3193bdc0bbab047a.js → page-9b72a07d424701c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-6107ca8ddd5e3bdd.js → page-a572f58cd3ca5864.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-be5ea965a07fbc41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c130b63862437814.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-fa244bd146a00ae8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-44d56d6391965a6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-4167348049166852.js → page-83a700d4c754d802.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-de52e8fdf9095f7a.js → not-found-7b3170f374f77b9d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-ca3952cd78a83ca2.js → page-a3c58f5c72a5f3f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e8e1ecaea7cd71d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-1ebfe049c640e171.js → page-1467047104141297.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-2da6f9b8a38c6978.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-102310930791197a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-6ed0b73bae8351d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-abe8e3b74bea1ffc.js → page-44d5f767491692bf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-43336055451f6e37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-d28573d6792b4275.js → page-3cc47dca0922ab9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a77ee355a2304ff0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-d751de5f8062ff5e.js → page-b17cd46cf0e9b8cf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-fe1759f94003b32b.js → page-61553851aa606146.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/{page-d64c984e855c8c68.js → page-a1f585e154e61c59.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-7bf54c9bb9274af8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-9afdee66b820c76e.js → page-5150ef17b5383a57.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-3544f154c87b43ee.js → page-134da468538baf48.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-539cde99554be20f.js → webpack-c2782b330447ffb6.js} +1 -1
- package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/decisions/index.html +1 -0
- package/apps/pwa/out/decisions/index.txt +30 -0
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- package/graphs/intake/intake-daily.yaml +9 -2
- package/graphs/intake/news-sources.yaml +4 -0
- package/graphs/intake/recipes.yaml +6 -1
- package/package.json +2 -1
- package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
- package/samples/workflows/templates/chat-driven-research.yaml +35 -0
- package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
- package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
- package/samples/workflows/templates/manual-quickstart.yaml +21 -0
- package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
- package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
- package/scripts/__fixtures__/intake-news.xml +7 -0
- package/scripts/ci-public-export-import-gate.ts +7 -2
- package/scripts/ci-typecheck-changed.ts +21 -6
- package/scripts/discord-seat-probe.ts +3 -2
- package/scripts/docs/generate-cli-anatomy.ts +162 -0
- package/scripts/goals/authoring-metrics.ts +73 -0
- package/scripts/intake-news.ts +169 -0
- package/scripts/landing-heal/must-fix-match.ts +7 -7
- package/scripts/landing-heal/run-mission.ts +3 -2
- package/scripts/public-export-test-run.ts +37 -2
- package/scripts/release-loop/checklist-gate-node.ts +13 -11
- package/scripts/release-loop/cut-branch.ts +2 -1
- package/scripts/release-loop/cutoff.ts +4 -2
- package/scripts/release-loop/docs-land-node.ts +77 -11
- package/scripts/release-loop/docs-node.ts +1 -1
- package/scripts/release-loop/gate-node.ts +99 -14
- package/scripts/release-loop/node-verdict.ts +2 -1
- package/scripts/release-loop/npm-publish-node.ts +11 -7
- package/scripts/release-loop/ops-upgrade-node.ts +8 -1
- package/scripts/release-loop/preflight.ts +246 -0
- package/scripts/release-loop/publish-node.ts +10 -3
- package/scripts/release-loop/release-docs.ts +134 -0
- package/scripts/release-loop/release-readiness.ts +11 -6
- package/scripts/release-loop/release-version.ts +48 -0
- package/scripts/release-loop/resume-release.ts +115 -0
- package/scripts/release-loop/unattended-release.ts +43 -7
- package/scripts/release-loop/version-node.ts +149 -41
- package/scripts/release-story/draft.ts +42 -14
- package/scripts/rhythm/daily.ts +25 -4
- package/scripts/skills/public-check.ts +62 -12
- package/scripts/test-deterministic.ts +51 -33
- package/scripts/test-diet/effectiveness.ts +62 -1
- package/scripts/test-diet/lib.ts +1 -1
- package/scripts/test-diet/node.ts +8 -1
- package/src/agent-mission/driver.ts +53 -10
- package/src/agent-mission/review-loop.ts +4 -3
- package/src/agent-substrate/pr-comment-meta.ts +20 -0
- package/src/autopilot/build/llm-conflict-merge.ts +28 -8
- package/src/autopilot/track-agent.ts +5 -3
- package/src/briefing/brief-items.ts +86 -17
- package/src/cli/autopilot-cli.ts +86 -1
- package/src/cli/brief-cli.ts +36 -2
- package/src/cli/decisions-cli.ts +4 -1
- package/src/cli/freeze-cli.ts +3 -2
- package/src/cli/intake-cli.ts +74 -2
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/notify-cli.ts +75 -0
- package/src/cli/pr-cli.ts +12 -1
- package/src/cli/provider-cli.ts +24 -0
- package/src/cli/release-cli.ts +101 -17
- package/src/cli/self-update-canary.ts +46 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/tasks-cli.ts +309 -3
- package/src/context-bus/condense.ts +19 -3
- package/src/context-bus/context-day.ts +77 -24
- package/src/context-bus/context-first.ts +33 -0
- package/src/context-card/index.ts +45 -0
- package/src/context-card/task.ts +76 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
- package/src/decisions/decision-ledger.ts +10 -1
- package/src/discord-self-message.ts +20 -2
- package/src/discord-slash-wire.ts +1 -0
- package/src/domains/outbound-alert.ts +200 -27
- package/src/domains/outbound-log-probe.ts +16 -1
- package/src/domains/release-tool.ts +2 -2
- package/src/execution-loop/gates.ts +11 -2
- package/src/flow-loop/split.ts +8 -4
- package/src/git-fs/worktree.ts +23 -3
- package/src/harness/harness-cli-command.ts +151 -11
- package/src/harness/harness-pod-dispatch.ts +11 -0
- package/src/harness/harness-queue.ts +466 -32
- package/src/harness/helper-scan.ts +10 -3
- package/src/harness/nested-elanous-policy.ts +23 -4
- package/src/hq/fence-wrapper.ts +123 -0
- package/src/hq/hq.ts +8 -0
- package/src/hq/seats.ts +369 -0
- package/src/index.ts +190 -23
- package/src/intake-plane/adopt-gate.ts +49 -0
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/collect-github.ts +57 -25
- package/src/intake-plane/digest.ts +39 -4
- package/src/intake-plane/discord-seat-work.ts +8 -2
- package/src/intake-plane/requirement-funnel.ts +316 -0
- package/src/knowledge/know-find.ts +10 -1
- package/src/loops/budget.ts +36 -0
- package/src/loops/orchestrator/finish-rate.ts +73 -1
- package/src/loops/orchestrator/grid-place.ts +11 -0
- package/src/loops/orchestrator/tick.ts +295 -20
- package/src/maturity/feature-maturity.ts +3 -2
- package/src/nexus/api/decisions-route.ts +11 -1
- package/src/nexus/api/grid.ts +61 -0
- package/src/nexus/api/http-server.ts +19 -1
- package/src/nexus/api/loop-edges.ts +69 -13
- package/src/nexus/api/ops-seats-sources.ts +2 -2
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/nexus/api/workflow-templates.ts +3 -0
- package/src/oauth/codex.ts +120 -2
- package/src/onboarding/first-chat-gate.ts +29 -0
- package/src/release-loop/checklist.ts +104 -6
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/landing-freeze.ts +24 -14
- package/src/release-loop/placement.ts +13 -3
- package/src/release-loop/rubric.ts +80 -0
- package/src/seat-dispatch/seat-ask.ts +79 -16
- package/src/seat-loop/seat-loop.ts +107 -37
- package/src/self-dev/ask-launch-flow.ts +50 -10
- package/src/self-dev/ask-path-candidates.ts +68 -0
- package/src/self-dev/dev-pipeline.ts +4 -0
- package/src/self-dev/draft-triage-rules.ts +1 -1
- package/src/self-dev/orchestrate-cli.ts +22 -0
- package/src/self-dev/round-pr-supersede.ts +244 -0
- package/src/self-dev/run-store.ts +5 -1
- package/src/self-dev/run-supervisor.ts +21 -1
- package/src/self-dev/supervisor-verdict-edges.ts +46 -0
- package/src/self-implement/follow-up-goals.ts +2 -2
- package/src/self-implement/frozen-merges.ts +25 -20
- package/src/self-implement/gate-remote.ts +427 -0
- package/src/self-implement/goal-author-clarification.ts +81 -0
- package/src/self-implement/goal-author-cli.ts +33 -5
- package/src/self-implement/goal-author.ts +93 -12
- package/src/self-implement/goal-clarification-escalation.ts +37 -1
- package/src/self-implement/goal-document-write.ts +28 -0
- package/src/self-implement/heal-intake.ts +4 -0
- package/src/self-implement/host-regate.ts +3 -1
- package/src/self-implement/l8-merge-queue.ts +29 -5
- package/src/self-implement/orchestrator.ts +78 -13
- package/src/self-implement/prior-draft-findings.ts +4 -3
- package/src/skills/skill-feedback.ts +76 -0
- package/src/task-agent/actions.ts +151 -0
- package/src/task-agent/judge.ts +122 -0
- package/src/task-agent/mission.ts +423 -0
- package/src/task-agent/shadow.ts +90 -0
- package/src/task-agent/task-hand.ts +235 -0
- package/src/task-cards/card-store.ts +4 -1
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -4
- package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
- package/src/task-orchestrator/surfaces/pod-pool.ts +81 -13
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +225 -35
- package/src/telegram-commands.ts +1 -1
- package/src/telegram-loops-command.ts +22 -15
- package/src/telegram.ts +5 -20
- package/src/user-config.ts +86 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-29d889b809288e65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-8c2d9526164f5d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-dce686e582c23937.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1a61c7ca1ceb3ae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-51f811fa119308b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-5b042736aa113756.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-57924393e3e4e539.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0f5cd57ca928f42b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-59f03fda76c03d22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-1ea64acadd704ace.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +0 -1
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
- package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{rv9cierV0jSOvDBm1g0XB → P6YTVgzuZz5oTZhY4Din_}/_ssgManifest.js +0 -0
package/scripts/rhythm/daily.ts
CHANGED
|
@@ -301,8 +301,9 @@ function recipient(deps: DailyDeps): { chatId: number; botToken: string } | null
|
|
|
301
301
|
function sendReview(file: string, text: string, target: { chatId: number; botToken: string },
|
|
302
302
|
send: (request: DailySendRequest) => DailySendConfirmation | null,
|
|
303
303
|
receipt: (key: string) => DeliveryReceipt,
|
|
304
|
-
recordDelivery?: (text: string, kind: 'ops-report') => void
|
|
305
|
-
|
|
304
|
+
recordDelivery?: (text: string, kind: 'ops-report') => void,
|
|
305
|
+
keySuffix = ''): DeliveryResult {
|
|
306
|
+
const key = file.slice(file.lastIndexOf('/') + 1).replace(/\.md$/, '') + keySuffix;
|
|
306
307
|
const label = `telegram:${target.chatId}`;
|
|
307
308
|
const db = new Database(join(dirname(file), 'delivery.sqlite'));
|
|
308
309
|
try {
|
|
@@ -346,6 +347,21 @@ function sendReview(file: string, text: string, target: { chatId: number; botTok
|
|
|
346
347
|
finally { db.close(); }
|
|
347
348
|
}
|
|
348
349
|
|
|
350
|
+
function newsMessage(markdown: string): string | null {
|
|
351
|
+
const section = markdown.split('## ⑦ 외부 동향\n')[1]?.split('\n\n## ')[0];
|
|
352
|
+
if (!section || section.startsWith('못 읽음 ·')) return null;
|
|
353
|
+
const articles = [...section.matchAll(/^- (.+) — (https?:\/\/\S+)\n 시사점: (.*)$/gm)].slice(0, 3);
|
|
354
|
+
return articles.length ? `## 외부 동향\n${articles.map(article => article[0]).join('\n')}` : null;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function sendNewsAfterReview(delivery: DeliveryResult, file: string, markdown: string,
|
|
358
|
+
target: { chatId: number; botToken: string }, deps: DailyDeps): string | null {
|
|
359
|
+
if (!['sent', 'already-sent', 'receipt-sent'].includes(delivery.deliveryState)) return null;
|
|
360
|
+
const text = newsMessage(markdown);
|
|
361
|
+
if (!text) return null;
|
|
362
|
+
return sendReview(file, text, target, deps.send ?? sendTelegram, deps.receipt ?? (() => 'unknown'), deps.recordDelivery, '-news').deliveryState;
|
|
363
|
+
}
|
|
364
|
+
|
|
349
365
|
export type DailyDeps = { now?: () => Date; root?: string; vaultRoot?: string | null; sendEnabled?: boolean; repoRoot?: string; repoName?: string;
|
|
350
366
|
landings?: (now: Date) => Promise<Landing[]>; release?: () => Promise<Release>; loops?: () => Promise<Loop[]>;
|
|
351
367
|
grid?: () => Promise<Seat[]>; decisions?: () => Promise<Pending[]>; news?: () => Promise<News[]>;
|
|
@@ -402,6 +418,7 @@ export async function runDaily(options: { dryRun?: boolean; noNews?: boolean; st
|
|
|
402
418
|
let target = 'unknown';
|
|
403
419
|
let sendError: string | null = null;
|
|
404
420
|
let recordError: string | null = null;
|
|
421
|
+
let newsState: string | null = null;
|
|
405
422
|
let enabled = deps.sendEnabled === true;
|
|
406
423
|
if (deps.sendEnabled === undefined && options.stage === 'deliver' && !options.dryRun) {
|
|
407
424
|
try { enabled = record(record(record(getUserConfig().raw.loops).rhythm).daily).send === true; }
|
|
@@ -420,12 +437,13 @@ export async function runDaily(options: { dryRun?: boolean; noNews?: boolean; st
|
|
|
420
437
|
sendError = sendError ?? delivery.sendError;
|
|
421
438
|
recordError = delivery.recordError ?? null;
|
|
422
439
|
deliveryState = delivery.deliveryState;
|
|
440
|
+
if (resolved) newsState = sendNewsAfterReview(delivery, file, markdown, resolved, deps);
|
|
423
441
|
}
|
|
424
442
|
tick.sent = sent;
|
|
425
443
|
const status = vault.vaultFatal || Object.values(sections).includes('unreadable') ? 'degraded'
|
|
426
444
|
: deliveryState === 'pending' ? 'pending'
|
|
427
445
|
: ['sent', 'already-sent', 'receipt-sent'].includes(deliveryState) ? 'ok' : 'degraded';
|
|
428
|
-
const deliveryLine = `${options.stage} ${status} · target=${target} · ${deliveryState} · sent=${sent} · chars=${chars} · receipt=${receiptKey}${recordError ? ` · ${recordError}` : ''}`;
|
|
446
|
+
const deliveryLine = `${options.stage} ${status} · target=${target} · ${deliveryState} · sent=${sent} · chars=${chars} · receipt=${receiptKey}${recordError ? ` · ${recordError}` : ''}${newsState ? ` · news=${newsState}` : ''}`;
|
|
429
447
|
if (options.stage === 'deliver') {
|
|
430
448
|
Object.assign(tick, { status, deliveryState, target, chars, receiptKey, deliveryLine, recordError });
|
|
431
449
|
if (!options.dryRun) {
|
|
@@ -526,6 +544,7 @@ export async function runDaily(options: { dryRun?: boolean; noNews?: boolean; st
|
|
|
526
544
|
}
|
|
527
545
|
let sent = false;
|
|
528
546
|
let recordError: string | null = null;
|
|
547
|
+
let newsState: string | null = null;
|
|
529
548
|
let chars = 0;
|
|
530
549
|
let target = 'unknown';
|
|
531
550
|
if (!options.dryRun && options.stage !== 'collect' && enabled) {
|
|
@@ -541,11 +560,13 @@ export async function runDaily(options: { dryRun?: boolean; noNews?: boolean; st
|
|
|
541
560
|
sendError = sendError ?? delivery.sendError;
|
|
542
561
|
recordError = delivery.recordError ?? null;
|
|
543
562
|
deliveryState = delivery.deliveryState;
|
|
563
|
+
if (resolved && parts.news.status === 'ok' && parts.news.value.length > 0)
|
|
564
|
+
newsState = sendNewsAfterReview(delivery, file, review.markdown, resolved, deps);
|
|
544
565
|
}
|
|
545
566
|
const status = vaultFatal || Object.values(review.sections).includes('unreadable') ? 'degraded'
|
|
546
567
|
: deliveryState === 'pending' ? 'pending'
|
|
547
568
|
: ['sent', 'already-sent', 'receipt-sent'].includes(deliveryState) ? 'ok' : 'degraded';
|
|
548
|
-
const deliveryLine = `${options.stage === 'collect' ? 'collect' : 'deliver'} ${status} · target=${target} · ${deliveryState} · sent=${sent} · chars=${chars} · receipt=${receiptKey}${recordError ? ` · ${recordError}` : ''}`;
|
|
569
|
+
const deliveryLine = `${options.stage === 'collect' ? 'collect' : 'deliver'} ${status} · target=${target} · ${deliveryState} · sent=${sent} · chars=${chars} · receipt=${receiptKey}${recordError ? ` · ${recordError}` : ''}${newsState ? ` · news=${newsState}` : ''}`;
|
|
549
570
|
if (options.stage !== 'collect') {
|
|
550
571
|
const tick = { sections: review.sections, risks: review.risks.length, sent, status, deliveryState, target, chars, receiptKey, deliveryLine, recordError };
|
|
551
572
|
if (!options.dryRun) {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
import { existsSync, lstatSync, readdirSync, readFileSync } from 'node:fs';
|
|
4
|
-
import {
|
|
2
|
+
/** Public (core) skill readiness inventory; optional builds never execute a skill. */
|
|
3
|
+
import { cpSync, existsSync, lstatSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { basename, isAbsolute, join, relative, sep, win32 } from 'node:path';
|
|
5
6
|
import { computeSkillBoundaries, type SkillBoundaryReport } from '../skill-boundary.js';
|
|
6
7
|
|
|
7
8
|
export type PublicCheckKey = 'header' | 'entry' | 'free' | 'portable' | 'install';
|
|
8
9
|
export interface PublicSkillRow {
|
|
9
10
|
readonly skill: string;
|
|
10
|
-
readonly checks: Readonly<Record<PublicCheckKey, boolean | 'N/A'
|
|
11
|
+
readonly checks: Readonly<Record<PublicCheckKey, boolean | 'N/A'> & { build?: SkillBuildResult }>;
|
|
11
12
|
readonly violations: readonly string[];
|
|
12
13
|
}
|
|
13
14
|
export interface PublicCheckReport {
|
|
@@ -68,6 +69,44 @@ function executableEntries(files: readonly string[], dir: string, docs: string):
|
|
|
68
69
|
&& (commanded.has(file) || file.startsWith('bin/') || ENTRY_NAME.test(file)));
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
export type SkillBuildResult = 'OK' | 'FAIL' | 'N/A';
|
|
73
|
+
|
|
74
|
+
/** Build only selected TS/JS entries from an isolated skill copy, never from the repository. */
|
|
75
|
+
export function buildSkillEntries(
|
|
76
|
+
dir: string,
|
|
77
|
+
entries: readonly string[],
|
|
78
|
+
run: (args: string[], cwd: string) => boolean = (args, cwd) => Bun.spawnSync(args, {
|
|
79
|
+
cwd, stdout: 'ignore', stderr: 'ignore',
|
|
80
|
+
}).exitCode === 0,
|
|
81
|
+
): SkillBuildResult {
|
|
82
|
+
const buildable = entries.filter((entry) => /\.(?:ts|tsx|js|mjs|cjs)$/iu.test(entry));
|
|
83
|
+
if (buildable.length === 0) return 'N/A';
|
|
84
|
+
if (buildable.some((entry) => isAbsolute(entry) || win32.isAbsolute(entry) || entry.split(/[\\/]/u).includes('..'))) return 'FAIL';
|
|
85
|
+
const temp = mkdtempSync(join(tmpdir(), 'skill-public-build-'));
|
|
86
|
+
try {
|
|
87
|
+
const skill = join(temp, 'skill');
|
|
88
|
+
cpSync(dir, skill, {
|
|
89
|
+
recursive: true,
|
|
90
|
+
filter: (source) => source === dir || !['.git', 'node_modules', '__pycache__'].includes(basename(source)) && !lstatSync(source).isSymbolicLink(),
|
|
91
|
+
});
|
|
92
|
+
const copiedRoot = realpathSync(skill);
|
|
93
|
+
for (const entry of buildable) {
|
|
94
|
+
const target = realpathSync(join(skill, entry));
|
|
95
|
+
const inside = relative(copiedRoot, target);
|
|
96
|
+
if (inside === '..' || inside.startsWith(`..${sep}`) || isAbsolute(inside) || !lstatSync(target).isFile()) return 'FAIL';
|
|
97
|
+
}
|
|
98
|
+
if (existsSync(join(skill, 'package.json')) && !run(['bun', 'install', '--ignore-scripts'], skill)) return 'FAIL';
|
|
99
|
+
for (const [index, entry] of buildable.entries()) {
|
|
100
|
+
if (!run(['bun', 'build', entry, '--target=bun', '--outfile', join(temp, `entry-${index}.js`)], skill)) return 'FAIL';
|
|
101
|
+
}
|
|
102
|
+
return 'OK';
|
|
103
|
+
} catch {
|
|
104
|
+
return 'FAIL';
|
|
105
|
+
} finally {
|
|
106
|
+
rmSync(temp, { recursive: true, force: true });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
71
110
|
function freeValid(doc: string, entries: ReadonlySet<string>): boolean {
|
|
72
111
|
const section = /(?:^|\n)#{1,4}\s*[^\n]*(?:무료|free)[^\n]*\n([\s\S]*?)(?=\n#{1,4}\s|$)/giu;
|
|
73
112
|
for (const match of doc.matchAll(section)) {
|
|
@@ -97,7 +136,7 @@ function installValid(doc: string, dir: string): boolean {
|
|
|
97
136
|
}
|
|
98
137
|
|
|
99
138
|
/** `boundary` is injectable for pure fixture tests; production always derives it from the repository. */
|
|
100
|
-
export function checkPublicSkills(root: string, boundary: SkillBoundaryReport = computeSkillBoundaries(root)): PublicCheckReport {
|
|
139
|
+
export function checkPublicSkills(root: string, boundary: SkillBoundaryReport = computeSkillBoundaries(root), build = false): PublicCheckReport {
|
|
101
140
|
const errors = [...boundary.errors];
|
|
102
141
|
const skills: PublicSkillRow[] = [];
|
|
103
142
|
const skillsDir = join(root, 'skills');
|
|
@@ -133,6 +172,7 @@ export function checkPublicSkills(root: string, boundary: SkillBoundaryReport =
|
|
|
133
172
|
free: freeValid(docs, entries),
|
|
134
173
|
portable: matches.length === 0,
|
|
135
174
|
install: installValid(docs, dir),
|
|
175
|
+
...(build ? { build: buildSkillEntries(dir, [...entries]) } : {}),
|
|
136
176
|
};
|
|
137
177
|
const violations = [
|
|
138
178
|
...(!checks.header ? ['SKILL.md 머리(name·description) 없음/불일치'] : []),
|
|
@@ -140,6 +180,7 @@ export function checkPublicSkills(root: string, boundary: SkillBoundaryReport =
|
|
|
140
180
|
...(!checks.free ? ['키/.env 없이 쓰는 무료 경로가 문서에 없음'] : []),
|
|
141
181
|
...matches.map((file) => `${file}: 개인 경로/내부 표지 문자열`),
|
|
142
182
|
...(!checks.install ? ['의존 manifest와 문서의 한 줄 설치 명령이 없음'] : []),
|
|
183
|
+
...(checks.build === 'FAIL' ? ['스킬 실행 파일 빌드 실패'] : []),
|
|
143
184
|
];
|
|
144
185
|
skills.push({ skill, checks, violations });
|
|
145
186
|
}
|
|
@@ -147,23 +188,32 @@ export function checkPublicSkills(root: string, boundary: SkillBoundaryReport =
|
|
|
147
188
|
}
|
|
148
189
|
|
|
149
190
|
export function formatPublicCheck(report: PublicCheckReport): string {
|
|
150
|
-
const columns: PublicCheckKey[] = ['header', 'entry', 'free', 'portable', 'install'];
|
|
191
|
+
const columns: (PublicCheckKey | 'build')[] = ['header', 'entry', 'free', 'portable', 'install'];
|
|
192
|
+
if (report.skills.some((row) => row.checks.build !== undefined)) columns.push('build');
|
|
151
193
|
return [
|
|
152
194
|
`skill ${columns.map((key) => key.padEnd(9)).join(' ')} result`,
|
|
153
|
-
...report.skills.map((row) => `${row.skill.padEnd(34)} ${columns.map((key) =>
|
|
195
|
+
...report.skills.map((row) => `${row.skill.padEnd(34)} ${columns.map((key) => { const value = row.checks[key]; return (typeof value === 'string' ? value : value ? 'OK' : 'FAIL').padEnd(9); }).join(' ')} ${row.violations.length ? row.violations.join('; ') : 'OK'}`),
|
|
154
196
|
...report.errors.map((error) => `ERROR ${error}`),
|
|
155
197
|
`PUBLIC-CHECK ${report.ok ? 'PASS' : 'FAIL'} (${report.skills.length} public skills)`,
|
|
156
198
|
].join('\n');
|
|
157
199
|
}
|
|
158
200
|
|
|
201
|
+
export function runPublicCheck(root: string, args: readonly string[], boundary?: SkillBoundaryReport): { output: string; exitCode: number } {
|
|
202
|
+
const report = checkPublicSkills(root, boundary, args.includes('--build'));
|
|
203
|
+
return {
|
|
204
|
+
output: args.includes('--json') ? JSON.stringify(report, null, 2) : formatPublicCheck(report),
|
|
205
|
+
exitCode: report.ok ? 0 : 1,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
159
209
|
if (import.meta.main) {
|
|
160
210
|
const args = process.argv.slice(2);
|
|
161
|
-
if (args.some((arg) => arg !== '--json')) {
|
|
162
|
-
console.error('Usage: bun scripts/skills/public-check.ts [--json]');
|
|
211
|
+
if (args.some((arg) => arg !== '--json' && arg !== '--build')) {
|
|
212
|
+
console.error('Usage: bun scripts/skills/public-check.ts [--json] [--build]');
|
|
163
213
|
process.exitCode = 2;
|
|
164
214
|
} else {
|
|
165
|
-
const
|
|
166
|
-
console.log(
|
|
167
|
-
|
|
215
|
+
const result = runPublicCheck(join(import.meta.dir, '..', '..'), args);
|
|
216
|
+
console.log(result.output);
|
|
217
|
+
process.exitCode = result.exitCode;
|
|
168
218
|
}
|
|
169
219
|
}
|
|
@@ -57,7 +57,10 @@ const HARNESS_TEST_ENV_KEYS = [
|
|
|
57
57
|
// Discovery: rg -n "process\.env\.ELANOUS_(HOST|RUN|ORIGIN|SUPERVISOR|PARENT)" src
|
|
58
58
|
// These are ambient execution identities, not inputs to a deterministic test.
|
|
59
59
|
// debug.log also attributes SUBSTRATE and ARM_ID to every plain-object record.
|
|
60
|
-
const GIT_LOCATION_ENV_KEYS = [
|
|
60
|
+
export const GIT_LOCATION_ENV_KEYS = [
|
|
61
|
+
'GIT_DIR', 'GIT_WORK_TREE', 'GIT_COMMON_DIR', 'GIT_INDEX_FILE',
|
|
62
|
+
'GIT_OBJECT_DIRECTORY', 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
|
|
63
|
+
] as const;
|
|
61
64
|
|
|
62
65
|
const EXECUTION_ORIGIN_ENV_KEYS = [
|
|
63
66
|
'ELANOUS_HOST_ID',
|
|
@@ -123,7 +126,7 @@ export type DeterministicRunnerDeps = {
|
|
|
123
126
|
childGraceMs?: number;
|
|
124
127
|
killProcessGroup?: KillProcessGroup;
|
|
125
128
|
getProcessGroupId?: GetProcessGroupId;
|
|
126
|
-
gitConfig?: (cwd: string, args: string[]) => string;
|
|
129
|
+
gitConfig?: (cwd: string, args: string[]) => string | undefined;
|
|
127
130
|
};
|
|
128
131
|
|
|
129
132
|
export function isLivePid(pid: number): boolean {
|
|
@@ -273,14 +276,17 @@ export async function terminateDirectChild(
|
|
|
273
276
|
reportVisible(`failed to terminate child pid=${pid}`, report);
|
|
274
277
|
}
|
|
275
278
|
|
|
276
|
-
function gitConfig(cwd: string, args: string[]): string {
|
|
279
|
+
function gitConfig(cwd: string, args: string[]): string | undefined {
|
|
277
280
|
const env = { ...process.env };
|
|
278
281
|
for (const key of GIT_LOCATION_ENV_KEYS) delete env[key];
|
|
279
282
|
const result = spawnSync('git', args, { cwd, env, encoding: 'utf8' });
|
|
280
283
|
if (result.error || (result.status !== 0 && !(result.status === 1 && args.includes('--get-all') && !result.stderr))) {
|
|
281
284
|
throw new Error(`git ${args.join(' ')} failed: ${result.error?.message ?? result.stderr}`);
|
|
282
285
|
}
|
|
283
|
-
|
|
286
|
+
if (result.status === 1) return undefined;
|
|
287
|
+
// --null --get-all terminates each complete value with NUL, even when
|
|
288
|
+
// a value itself contains newlines or is empty.
|
|
289
|
+
return args.includes('--get-all') ? result.stdout : result.stdout.trim();
|
|
284
290
|
}
|
|
285
291
|
|
|
286
292
|
export function prepareIsolatedTestEnv(sourceEnv: NodeJS.ProcessEnv, testRoot: string): NodeJS.ProcessEnv {
|
|
@@ -406,33 +412,45 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
|
|
|
406
412
|
const cwd = deps.cwd ?? process.cwd();
|
|
407
413
|
const config = deps.gitConfig ?? gitConfig;
|
|
408
414
|
const commonDir = config(cwd, ['rev-parse', '--path-format=absolute', '--git-common-dir']);
|
|
415
|
+
if (commonDir === undefined) throw new Error('git common directory is unset');
|
|
409
416
|
const configFile = join(resolve(cwd, commonDir), 'config');
|
|
410
|
-
const
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
417
|
+
const configArgs = ['config', '--file', configFile];
|
|
418
|
+
const watchedKeys = ['core.bare', 'user.name', 'user.email'] as const;
|
|
419
|
+
// Legacy injected gitConfig fixtures supply newline-delimited results and
|
|
420
|
+
// return '' for an absent key. Real Git uses NUL-delimited complete values.
|
|
421
|
+
const readConfig = (key: string): string[] => {
|
|
422
|
+
const value = config(cwd, [...configArgs, '--null', '--get-all', key]);
|
|
423
|
+
if (value === undefined || (deps.gitConfig && value === '')) return [];
|
|
424
|
+
if (deps.gitConfig && !value.includes('\0')) return value.split('\n');
|
|
425
|
+
if (!value.endsWith('\0')) throw new Error(`unterminated git config value for ${key}`);
|
|
426
|
+
return value.slice(0, -1).split('\0');
|
|
427
|
+
};
|
|
428
|
+
const before = new Map(watchedKeys.map((key) => [key, readConfig(key)]));
|
|
429
|
+
let configChanged = false;
|
|
430
|
+
let configChecked = false;
|
|
431
|
+
const checkConfig = (): void => {
|
|
432
|
+
configChecked = true;
|
|
433
|
+
const visible = (values: string[]) => values.length === 0 ? '<unset>' : values.map((value) => value === '' ? '<empty>' : value.replaceAll('\n', ',')).join(',');
|
|
434
|
+
for (const key of watchedKeys) {
|
|
435
|
+
const original = before.get(key)!;
|
|
436
|
+
let after: string[] = ['<unreadable>'];
|
|
437
|
+
try {
|
|
438
|
+
after = readConfig(key);
|
|
439
|
+
if (JSON.stringify(after) === JSON.stringify(original)) continue;
|
|
440
|
+
configChanged = true;
|
|
441
|
+
if (original.length > 0) {
|
|
442
|
+
config(cwd, [...configArgs, '--replace-all', key, original[0]!]);
|
|
443
|
+
for (const value of original.slice(1)) config(cwd, [...configArgs, '--add', key, value]);
|
|
444
|
+
} else {
|
|
445
|
+
config(cwd, [...configArgs, '--unset-all', key]);
|
|
446
|
+
}
|
|
447
|
+
const restored = readConfig(key);
|
|
448
|
+
if (JSON.stringify(restored) !== JSON.stringify(original)) throw new Error(`expected ${visible(original)}, got ${visible(restored)}`);
|
|
449
|
+
report(`[test-deterministic] ${key} changed ${visible(original)} -> ${visible(after)}; restored ${visible(original)}; failing run`);
|
|
450
|
+
} catch (error) {
|
|
451
|
+
configChanged = true;
|
|
452
|
+
report(`[test-deterministic] ${key} changed ${visible(original)} -> ${visible(after)}; restore failed: ${formatError(error)}`);
|
|
429
453
|
}
|
|
430
|
-
const restored = readBare();
|
|
431
|
-
if (restored !== bareBefore) throw new Error(`expected ${visible(bareBefore)}, got ${visible(restored)}`);
|
|
432
|
-
report(`[test-deterministic] core.bare changed ${visible(bareBefore)} -> ${visible(bareAfter)}; restored ${visible(bareBefore)}; failing run`);
|
|
433
|
-
} catch (error) {
|
|
434
|
-
bareChanged = true;
|
|
435
|
-
report(`[test-deterministic] core.bare changed ${visible(bareBefore)} -> ${visible(bareAfter)}; restore failed: ${formatError(error)}`);
|
|
436
454
|
}
|
|
437
455
|
};
|
|
438
456
|
// ⛔ 사람이 «경로»를 직접 준 창에서는 아무것도 빼지 않는다 — 그 창은 「이것만 돌려라」다.
|
|
@@ -494,7 +512,7 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
|
|
|
494
512
|
});
|
|
495
513
|
cleanupTemporaryRoot(testRoot, { rmSync: deps.rmSync, report });
|
|
496
514
|
rootCleanupAttempted = true;
|
|
497
|
-
|
|
515
|
+
checkConfig();
|
|
498
516
|
gate?.dispose();
|
|
499
517
|
gate = undefined;
|
|
500
518
|
if (deps.killSelf) {
|
|
@@ -509,8 +527,8 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
|
|
|
509
527
|
}
|
|
510
528
|
|
|
511
529
|
cleanupRoot();
|
|
512
|
-
|
|
513
|
-
return
|
|
530
|
+
checkConfig();
|
|
531
|
+
return configChanged ? 1 : (outcome.code ?? 1);
|
|
514
532
|
} catch (error) {
|
|
515
533
|
if (child) {
|
|
516
534
|
try {
|
|
@@ -529,7 +547,7 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
|
|
|
529
547
|
} finally {
|
|
530
548
|
gate?.dispose();
|
|
531
549
|
cleanupRoot();
|
|
532
|
-
if (!
|
|
550
|
+
if (!configChecked) checkConfig();
|
|
533
551
|
}
|
|
534
552
|
}
|
|
535
553
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import { cpSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, existsSync, statSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
5
5
|
import ts from 'typescript';
|
|
6
6
|
import { pickRange } from './lib.js';
|
|
7
7
|
|
|
@@ -119,6 +119,67 @@ export function fixedCountAssertions(testPath: string, root: string): Array<{ li
|
|
|
119
119
|
return assertions.sort((a, b) => a.line - b.line);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// Read module specifiers from syntax, not from comments or strings; re-exports count for non-test importers.
|
|
123
|
+
function directImports(file: string, includeReexports = false): string[] {
|
|
124
|
+
const source = ts.createSourceFile(file, readFileSync(file, 'utf8'), ts.ScriptTarget.Latest, true);
|
|
125
|
+
const imports: string[] = [];
|
|
126
|
+
const visit = (node: ts.Node): void => {
|
|
127
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) imports.push(node.moduleSpecifier.text);
|
|
128
|
+
if (includeReexports && ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier))
|
|
129
|
+
imports.push(node.moduleSpecifier.text);
|
|
130
|
+
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword
|
|
131
|
+
&& node.arguments.length === 1 && ts.isStringLiteral(node.arguments[0]!)) imports.push(node.arguments[0]!.text);
|
|
132
|
+
ts.forEachChild(node, visit);
|
|
133
|
+
};
|
|
134
|
+
visit(source);
|
|
135
|
+
return imports;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Directly imported repository modules with no direct, tracked, non-test importer. */
|
|
139
|
+
export function unreachableImports(testPath: string, root: string, deps: {
|
|
140
|
+
importers?: (modulePath: string, root: string) => string[];
|
|
141
|
+
} = {}): string[] {
|
|
142
|
+
const repo = realpathSync(resolve(root));
|
|
143
|
+
const inside = (path: string) => path !== repo && !relative(repo, path).startsWith(`..${sep}`)
|
|
144
|
+
&& relative(repo, path) !== '..' && !isAbsolute(relative(repo, path));
|
|
145
|
+
const validFile = (path: string) => inside(path) && existsSync(path) && realpathSync(path) === path && statSync(path).isFile();
|
|
146
|
+
const isTest = (path: string) => path.endsWith('.test.ts') || path === 'test' || path.startsWith(`test${sep}`);
|
|
147
|
+
const testFile = resolve(repo, testPath);
|
|
148
|
+
if (!validFile(testFile)) throw new Error('test path outside repository or symlinked');
|
|
149
|
+
const resolveImport = (from: string, specifier: string): string | undefined => {
|
|
150
|
+
if (!/^\.{1,2}\//.test(specifier)) return undefined;
|
|
151
|
+
const base = resolve(dirname(from), specifier);
|
|
152
|
+
const candidates = base.endsWith('.js')
|
|
153
|
+
? [base.replace(/\.js$/, '.ts'), base, `${base}.ts`, join(base, 'index.ts')]
|
|
154
|
+
: [base, `${base}.ts`, join(base, 'index.ts')];
|
|
155
|
+
return candidates.find(validFile);
|
|
156
|
+
};
|
|
157
|
+
const importers = deps.importers ?? ((modulePath: string, rootPath: string): string[] => {
|
|
158
|
+
const stem = basename(modulePath).replace(/\.[^.]+$/, '');
|
|
159
|
+
const needles = stem === 'index' ? [stem, basename(dirname(modulePath))] : [stem];
|
|
160
|
+
const matches = new Set<string>();
|
|
161
|
+
for (const needle of needles) {
|
|
162
|
+
const found = spawnSync('git', ['-C', rootPath, 'grep', '-l', '-F', '--', needle], { encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
|
|
163
|
+
if (found.error || (found.status !== 0 && found.status !== 1)) throw new Error(`importer lookup failed: ${found.error?.message ?? found.stderr}`);
|
|
164
|
+
for (const file of found.stdout.split('\n').filter(Boolean)) matches.add(file);
|
|
165
|
+
}
|
|
166
|
+
return [...matches];
|
|
167
|
+
});
|
|
168
|
+
const sources = new Set(directImports(testFile).map((specifier) => resolveImport(testFile, specifier))
|
|
169
|
+
.filter((path): path is string => path !== undefined && !isTest(relative(repo, path))));
|
|
170
|
+
const unreachable: string[] = [];
|
|
171
|
+
for (const source of sources) {
|
|
172
|
+
const modulePath = relative(repo, source);
|
|
173
|
+
const used = importers(modulePath, repo).some((candidate) => {
|
|
174
|
+
const path = resolve(repo, candidate);
|
|
175
|
+
if (!validFile(path) || isTest(relative(repo, path)) || path === source) return false;
|
|
176
|
+
return directImports(path, true).some((specifier) => resolveImport(path, specifier) === source);
|
|
177
|
+
});
|
|
178
|
+
if (!used) unreachable.push(modulePath.split(sep).join('/'));
|
|
179
|
+
}
|
|
180
|
+
return unreachable.sort();
|
|
181
|
+
}
|
|
182
|
+
|
|
122
183
|
export function mutationProbe(file: string, deps: { root: string; run?: (root: string, file: string) => number | null; baselineStable?: boolean }): Mutation {
|
|
123
184
|
if (deps.baselineStable === false) return 'n/a';
|
|
124
185
|
const root = realpathSync(resolve(deps.root));
|
package/scripts/test-diet/lib.ts
CHANGED
|
@@ -7,7 +7,7 @@ export const DEFAULT_COST_SECS = 10;
|
|
|
7
7
|
export const SLOW_SECS = 60;
|
|
8
8
|
export const HEAVY_MB = 2048;
|
|
9
9
|
|
|
10
|
-
export interface Effectiveness { flake: 'stable' | 'flaky' | 'failing'; mutation: 'caught' | 'survived' | 'n/a'; fixedCounts?: number }
|
|
10
|
+
export interface Effectiveness { flake: 'stable' | 'flaky' | 'failing'; mutation: 'caught' | 'survived' | 'n/a'; fixedCounts?: number; unreachable?: number }
|
|
11
11
|
export interface Measurement { file: string; secs: number; rssMb: number | null; rc: number | null; pass: number | null; fail: number | null; reason?: string; effectiveness?: Effectiveness }
|
|
12
12
|
export type Verdict = 'keep' | 'failing' | 'review';
|
|
13
13
|
export type Proposal = 'delete' | 'shrink' | 'rewrite-cheap' | 'move-out-of-gate' | 'investigate';
|
|
@@ -10,7 +10,7 @@ import { effectiveInstanceRoot } from '../../src/instance/resolve.js';
|
|
|
10
10
|
import { CardStore } from '../../src/task-cards/card-store.js';
|
|
11
11
|
import { GATE_NIGHTLY_AUDITS } from '../release-loop/gate-node.js';
|
|
12
12
|
import { appendLedger, costTable, judge, lastLedgerLine, ledgerPath, nightlyAuditLedgerPath, pickRange, propose, td1Dispositions, writeCardDraft, type LedgerLine, type Measurement, type Td1Disposition } from './lib.js';
|
|
13
|
-
import { fixedCountAssertions, flakeVerdict, sweepSlice, type Mutation, type TestRun } from './effectiveness.js';
|
|
13
|
+
import { fixedCountAssertions, flakeVerdict, sweepSlice, unreachableImports, type Mutation, type TestRun } from './effectiveness.js';
|
|
14
14
|
|
|
15
15
|
type Context = { graphId?: string; input: Record<string, unknown>; outputs: Record<string, Record<string, unknown> | null> };
|
|
16
16
|
function context(): Context {
|
|
@@ -123,6 +123,13 @@ function measure(ctx: Context, audit = false, effectiveness = false): number {
|
|
|
123
123
|
file: measurement.file, reason: error instanceof Error ? error.message : String(error),
|
|
124
124
|
});
|
|
125
125
|
}
|
|
126
|
+
try {
|
|
127
|
+
measurement.effectiveness.unreachable = unreachableImports(measurement.file, process.cwd()).length;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
debug.log('test-diet.effectiveness', 'unreachable-unreadable', {
|
|
130
|
+
file: measurement.file, reason: error instanceof Error ? error.message : String(error),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
126
133
|
debug.log('test-diet.effectiveness', 'judged', { file: measurement.file, ...measurement.effectiveness, secs: effect.secs });
|
|
127
134
|
}
|
|
128
135
|
}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
// - 증거 게이트(doc 존재 / tsc 0 / test pass)로 완료 판정 후 commit
|
|
9
9
|
// 재사용: startPty(Bun 네이티브 PTY)·createWorktree·streamLLM·worktreeHasChanges·commitWorktree.
|
|
10
10
|
import { execFileSync, spawnSync, spawn as spawnChild } from 'node:child_process';
|
|
11
|
-
import { homedir } from 'node:os';
|
|
12
|
-
import { existsSync, writeFileSync, mkdirSync, readdirSync, readFileSync, statSync, readlinkSync, realpathSync, lstatSync, unlinkSync } from 'node:fs';
|
|
11
|
+
import { homedir, tmpdir } from 'node:os';
|
|
12
|
+
import { existsSync, writeFileSync, mkdirSync, mkdtempSync, rmSync, readdirSync, readFileSync, statSync, readlinkSync, realpathSync, lstatSync, unlinkSync } from 'node:fs';
|
|
13
13
|
import { createHash } from 'node:crypto';
|
|
14
14
|
import { join, relative, resolve, isAbsolute, sep } from 'node:path';
|
|
15
15
|
import { startPty, mintPtyId, onPtyEvent, type PtyHandle, type StartOpts } from '../pty-shell/registry.js';
|
|
@@ -18,7 +18,7 @@ import { NESTED_AGENT_ENV_BLOCKLIST } from '../agent/nested-agent-env.js';
|
|
|
18
18
|
import { getNestDepth } from '../agent/nest-depth.js';
|
|
19
19
|
import { gateNestedElanousLaunch, nestedDepthCodexArgs, readNestedElanousDepth } from '../harness/nested-elanous-policy.js';
|
|
20
20
|
import { createWorktree, resolveMainRepoRoot, type CreateWorktreeOpts } from '../git-fs/worktree.js';
|
|
21
|
-
import { configuredWorktreeRoot } from '../user-config.js';
|
|
21
|
+
import { configuredWorktreeRoot, getUserConfig } from '../user-config.js';
|
|
22
22
|
import { worktreeHasChanges, commitWorktree, changedFiles } from '../self-implement/seams.js';
|
|
23
23
|
import { streamLLM, type LLMMessage } from '../llm.js';
|
|
24
24
|
import { debug } from '../debug/log.js';
|
|
@@ -330,6 +330,27 @@ export function resolveBackendSpawn(
|
|
|
330
330
|
return { cmd: backend.cmd, args, env, unsetEnv, nestedEnvRemovedCount, forcedEnv };
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
+
/** Claude's strict MCP override is per-child; never edit the user's Claude settings. */
|
|
334
|
+
export function prepareClaudeChildSpawn(
|
|
335
|
+
spawn: ReturnType<typeof resolveBackendSpawn>,
|
|
336
|
+
options: { readonly backendName: string; readonly depth: number; readonly allowNestedElanous: boolean; readonly tempRoot?: string },
|
|
337
|
+
): { readonly args: string[]; readonly cleanup: () => void; readonly configPath?: string } {
|
|
338
|
+
// Backend identity, not the command string — an absolute-path claude binary is still the Claude backend.
|
|
339
|
+
if (options.backendName !== 'claude' || (options.depth === 0 && options.allowNestedElanous)) {
|
|
340
|
+
return { args: [...spawn.args], cleanup: () => {} };
|
|
341
|
+
}
|
|
342
|
+
const dir = mkdtempSync(join(options.tempRoot ?? tmpdir(), 'elanous-claude-mcp-'));
|
|
343
|
+
try {
|
|
344
|
+
const configPath = join(dir, 'mcp.json');
|
|
345
|
+
writeFileSync(configPath, JSON.stringify({ mcpServers: {} }), { mode: 0o600 });
|
|
346
|
+
return { args: [...spawn.args, '--strict-mcp-config', '--mcp-config', configPath], configPath,
|
|
347
|
+
cleanup: () => rmSync(dir, { recursive: true, force: true }) };
|
|
348
|
+
} catch (error) {
|
|
349
|
+
rmSync(dir, { recursive: true, force: true });
|
|
350
|
+
throw error;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
333
354
|
/**
|
|
334
355
|
* Refuse a requested nested elanous launch when this backend cannot carry the depth marker.
|
|
335
356
|
* Unset depth is 0. A normal mission spawn (`nestedLaunchRequested: false`) is never refused here.
|
|
@@ -528,6 +549,8 @@ export interface AgentMissionSpec {
|
|
|
528
549
|
* Absent means an ordinary mission spawn — never refused for a missing marker.
|
|
529
550
|
*/
|
|
530
551
|
nestedElanousLaunch?: boolean;
|
|
552
|
+
/** Explicit depth-0 allow from the caller (e.g. `--nested-elanous allow`). Ignored at depth >= 1. Config `harness.nestedElanous` also counts at depth 0. */
|
|
553
|
+
nestedElanousAllow?: boolean;
|
|
531
554
|
/** Concurrent missions may share one decision stream alias without sharing a PTY or worktree. */
|
|
532
555
|
decisionMissionId?: string;
|
|
533
556
|
/** ⭐⭐ 그 브랜치를 이미 쥔 «소유» 워크트리가 있으면 지우지 말고 그대로 재사용하라고 요청한다
|
|
@@ -1574,6 +1597,8 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
|
|
|
1574
1597
|
const nestedRefusal = refuseNestedElanousOnBackend({
|
|
1575
1598
|
backendName: backend.name,
|
|
1576
1599
|
nestedLaunchRequested: spec.nestedElanousLaunch === true,
|
|
1600
|
+
...(spec.nestedElanousAllow === true ? { allow: true } : {}),
|
|
1601
|
+
...(getUserConfig().harness?.nestedElanous === 'allow' ? { configAllow: true } : {}),
|
|
1577
1602
|
env: process.env,
|
|
1578
1603
|
});
|
|
1579
1604
|
if (nestedRefusal.refused) {
|
|
@@ -1691,12 +1716,16 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
|
|
|
1691
1716
|
evidencePath: finalEv.path, evidenceSatisfied: true, committed, usedOmniCrawl: false, detail: '완료(증거 충족)' };
|
|
1692
1717
|
}
|
|
1693
1718
|
env.TERM = 'xterm-256color';
|
|
1719
|
+
// Claude's per-child MCP override is held until the PTY exits, not until the control loop returns.
|
|
1720
|
+
const claudeChild = backend.name === 'claude'
|
|
1721
|
+
? prepareClaudeChildSpawn(spawnParams, { backendName: backend.name, depth: readNestedElanousDepth(process.env), allowNestedElanous: spec.nestedElanousAllow === true || getUserConfig().harness?.nestedElanous === 'allow' })
|
|
1722
|
+
: undefined;
|
|
1694
1723
|
// ⭐run-identity — runId was resolved before either headless or PTY spawn.
|
|
1695
|
-
debug.log('agent-mission', 'spawn', { agent: backend.name, cmd: `${spawnParams.cmd} ${spawnParams.args.join(' ')}`, scrubbed: backend.scrubEnv ?? [], nestedEnvRemovedCount: spawnParams.nestedEnvRemovedCount, runId });
|
|
1724
|
+
debug.log('agent-mission', 'spawn', { agent: backend.name, cmd: `${spawnParams.cmd} ${(claudeChild?.args ?? spawnParams.args).join(' ')}`, scrubbed: backend.scrubEnv ?? [], nestedEnvRemovedCount: spawnParams.nestedEnvRemovedCount, runId });
|
|
1696
1725
|
// PTY 정체성 — kind(=backend)·nickname(goto 로 나중 접근)·accessMode='auto'(헤드리스 자율·brain 이 write 소유).
|
|
1697
1726
|
const ptyOpts = buildAgentMissionPtySpawnOptions({
|
|
1698
1727
|
backend,
|
|
1699
|
-
spawn: { cmd: spawnParams.cmd, args: spawnParams.args, env, unsetEnv: spawnParams.unsetEnv },
|
|
1728
|
+
spawn: { cmd: spawnParams.cmd, args: claudeChild?.args ?? spawnParams.args, env, unsetEnv: spawnParams.unsetEnv },
|
|
1700
1729
|
workdir: wt.path,
|
|
1701
1730
|
nickname: spec.nickname ?? spec.branch ?? backend.name,
|
|
1702
1731
|
});
|
|
@@ -1706,6 +1735,11 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
|
|
|
1706
1735
|
let usageReemitted = false;
|
|
1707
1736
|
let childSessionId: string | undefined;
|
|
1708
1737
|
let sessionOutput = '';
|
|
1738
|
+
let claudeCleanupTimer: ReturnType<typeof setInterval> | undefined;
|
|
1739
|
+
const cleanupClaude = (): void => {
|
|
1740
|
+
if (claudeCleanupTimer) clearInterval(claudeCleanupTimer);
|
|
1741
|
+
claudeChild?.cleanup();
|
|
1742
|
+
};
|
|
1709
1743
|
const offLive = onPtyEvent((ev) => {
|
|
1710
1744
|
if (ev.id !== ptyOpts.id) return;
|
|
1711
1745
|
if (ev.type === 'output') {
|
|
@@ -1718,6 +1752,7 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
|
|
|
1718
1752
|
if (ev.type === 'exit') {
|
|
1719
1753
|
offLive();
|
|
1720
1754
|
if (liveTimer) clearInterval(liveTimer);
|
|
1755
|
+
cleanupClaude();
|
|
1721
1756
|
}
|
|
1722
1757
|
if (ev.type === 'exit' && backend.name === 'codex' && !usageReemitted) {
|
|
1723
1758
|
usageReemitted = true;
|
|
@@ -1740,9 +1775,17 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
|
|
|
1740
1775
|
}
|
|
1741
1776
|
});
|
|
1742
1777
|
let h: PtyHandle;
|
|
1743
|
-
if (deps.isMissionAborted?.()) { offLive(); throw new Error('MISSION_ABORTED'); }
|
|
1778
|
+
if (deps.isMissionAborted?.()) { offLive(); cleanupClaude(); throw new Error('MISSION_ABORTED'); }
|
|
1744
1779
|
try { h = spawnPty(ptyOpts); }
|
|
1745
|
-
catch (error) { offLive(); throw error; }
|
|
1780
|
+
catch (error) { offLive(); cleanupClaude(); throw error; }
|
|
1781
|
+
if (!h.isAlive()) cleanupClaude();
|
|
1782
|
+
else if (claudeChild) {
|
|
1783
|
+
// A test-injected PTY may not emit registry events; keep the config until its handle exits.
|
|
1784
|
+
claudeCleanupTimer = setInterval(() => {
|
|
1785
|
+
if (!h.isAlive()) cleanupClaude();
|
|
1786
|
+
}, 1000);
|
|
1787
|
+
claudeCleanupTimer.unref();
|
|
1788
|
+
}
|
|
1746
1789
|
if (backend.name === 'codex') observeExternalEvent({ origin: 'codex-agent-mission', kind: 'started',
|
|
1747
1790
|
summary: 'Codex agent mission started', source: `elanous://agent-mission/${encodeURIComponent(runId)}/${encodeURIComponent(h.id)}` });
|
|
1748
1791
|
let contextFinished = false;
|
|
@@ -1753,8 +1796,8 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
|
|
|
1753
1796
|
summary: `Codex agent mission ${ok ? 'completed' : 'failed'}`,
|
|
1754
1797
|
source: `elanous://agent-mission/${encodeURIComponent(runId)}/${encodeURIComponent(h.id)}` });
|
|
1755
1798
|
};
|
|
1756
|
-
deps.onPtyStarted?.(h, () => { if (liveTimer) clearInterval(liveTimer); offLive(); finishContext(false); });
|
|
1757
|
-
if (deps.isMissionAborted?.()) { finishContext(false); return { ok: false, reason: 'aborted', worktree: wt.path, branch: wt.branch,
|
|
1799
|
+
deps.onPtyStarted?.(h, () => { if (liveTimer) clearInterval(liveTimer); offLive(); finishContext(false); cleanupClaude(); });
|
|
1800
|
+
if (deps.isMissionAborted?.()) { finishContext(false); cleanupClaude(); return { ok: false, reason: 'aborted', worktree: wt.path, branch: wt.branch,
|
|
1758
1801
|
rounds: 0, evidencePath: null, committed: false, usedOmniCrawl: false, detail: 'aborted' }; }
|
|
1759
1802
|
const executorRef = buildExecutorPtyRef({
|
|
1760
1803
|
ptyId: h.id,
|
|
@@ -2275,7 +2318,7 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
|
|
|
2275
2318
|
ptyId: h.id, webUrl: webAddress.webUrl, driveVerdict: driveVerdict.kind,
|
|
2276
2319
|
detail: recoverExhausted ? control.termination.reason : driveVerdict.kind === 'done-but-failed' ? `미완(DRIVE-OK: ${driveVerdict.reason})` : driveVerdict.kind === 'success-unverified' && finalEv.ok ? '완료(증거)' : done ? '완료(증거 충족)' : (finalEv.ok ? '증거 충족(루프 종료)' : '미완(증거 부족)'),
|
|
2277
2320
|
};
|
|
2278
|
-
} finally { stopLive(); finishContext(false); }
|
|
2321
|
+
} finally { stopLive(); finishContext(false); if (!h.isAlive() || deps.isMissionAborted?.()) cleanupClaude(); }
|
|
2279
2322
|
}
|
|
2280
2323
|
|
|
2281
2324
|
/** @deprecated codex 특정 이름 — runAgentMission 을 쓰라(codex 는 기본 backend). */
|
|
@@ -22,7 +22,7 @@ import { budgetReviewerContext } from '../agent-substrate/pr-reviewer.js';
|
|
|
22
22
|
import { DEFAULT_REVIEW_BACKEND } from '../agent-substrate/acp-reviewer.js';
|
|
23
23
|
import { loadReviewerContext } from '../agent-substrate/self-review-cli.js';
|
|
24
24
|
import { createRepositoryReferencedFileReader, type ReferencedFileReader } from '../self-implement/goal-file-reader.js';
|
|
25
|
-
import { parse as parsePrCommentMeta } from '../agent-substrate/pr-comment-meta.js';
|
|
25
|
+
import { expandRecords, parse as parsePrCommentMeta } from '../agent-substrate/pr-comment-meta.js';
|
|
26
26
|
|
|
27
27
|
export type ReviewVerdict = 'ok' | 'reinforce' | 'reject' | 'ambiguous';
|
|
28
28
|
export type ReviewClassificationSource = 'llm' | 'llm-call-failure' | 'empty-review' | 'injected';
|
|
@@ -310,9 +310,10 @@ function analyzeAppliedReviewItems(comments: readonly NonNullable<LatestReviewSo
|
|
|
310
310
|
const items: string[] = [];
|
|
311
311
|
const seen = new Set<string>();
|
|
312
312
|
let headlineComments = 0;
|
|
313
|
-
const newestFirst =
|
|
313
|
+
const newestFirst = comments.flatMap((comment) => expandRecords(comment.body ?? '').map((body) => ({ ...comment, body })))
|
|
314
|
+
.sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? ''));
|
|
314
315
|
for (const comment of newestFirst) {
|
|
315
|
-
const lines =
|
|
316
|
+
const lines = comment.body.split('\n');
|
|
316
317
|
const headlineIndex = parsePrCommentMeta(lines[0] ?? '') ? 1 : 0;
|
|
317
318
|
const headline = lines[headlineIndex]?.trim() ?? '';
|
|
318
319
|
if (!headline.startsWith(REVIEW_REINFORCEMENT_HEADLINE) && !headline.startsWith(ACP_REWORK_HEADLINE)) continue;
|