elanous 0.2.14 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/{1307-6e7e0377c1db0bdc.js → 1307-b46d45b705e8c9ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-3cb52e5ea8fc2fa5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-c1e20d7d338713f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-482c5dbde699947c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-8c2d9526164f5d09.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-e6b84ab07d3567d5.js → page-a0e2a6d1bfb52fc5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c53598cfce6156f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-13a63953f53c6a23.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-5d3c8429d04f3831.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-3193bdc0bbab047a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-6107ca8ddd5e3bdd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-dce686e582c23937.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-160089123a40dd91.js → page-1a61c7ca1ceb3ae7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-51f811fa119308b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-4167348049166852.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-de52e8fdf9095f7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-ca3952cd78a83ca2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-e7e10cd3386222e5.js → page-5b042736aa113756.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-1ebfe049c640e171.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-57924393e3e4e539.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-0f5cd57ca928f42b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-abe8e3b74bea1ffc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-d28573d6792b4275.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-58043db78ba519ae.js → page-59f03fda76c03d22.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-357750dfb91bf960.js → page-d751de5f8062ff5e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-fe1759f94003b32b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d64c984e855c8c68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-9afdee66b820c76e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-f8da5dc6f3752276.js → page-1ea64acadd704ace.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-3544f154c87b43ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +1 -0
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
- package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
- package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +15 -15
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +15 -15
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +15 -15
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +15 -15
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +15 -15
- package/apps/pwa/out/ceo/index.html +1 -0
- package/apps/pwa/out/ceo/index.txt +33 -0
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +15 -15
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +15 -15
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +15 -15
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +15 -15
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +15 -15
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +15 -15
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +15 -15
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +15 -15
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +15 -15
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +15 -15
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +15 -15
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +15 -15
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +15 -15
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +15 -15
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +15 -15
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +15 -15
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +15 -15
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +15 -15
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +15 -15
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +15 -15
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +16 -16
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +15 -15
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +15 -15
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +15 -15
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +15 -15
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +15 -15
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +15 -15
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +15 -15
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +15 -15
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +15 -15
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +15 -15
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +15 -15
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +15 -15
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +15 -15
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +15 -15
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +15 -15
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +15 -15
- package/graphs/doc-gen/doc-gen.yaml +24 -0
- package/graphs/doc-gen/recipes.yaml +3 -0
- package/graphs/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/orchestrator/orchestrator.yaml +7 -0
- package/graphs/orchestrator/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -2
- package/graphs/steward/recipes.yaml +2 -1
- package/package.json +1 -1
- package/scripts/brand/check.ts +7 -5
- package/scripts/ci-public-export-import-gate.ts +154 -0
- package/scripts/ci-test-interference-gate.ts +5 -1
- package/scripts/ci-typecheck-changed.ts +82 -31
- package/scripts/docs/doc-rot.ts +164 -0
- package/scripts/docs/generate-graph-anatomy.ts +437 -0
- package/scripts/docs-cli-check.ts +4 -4
- package/scripts/expose-rubric-suggest.ts +18 -12
- package/scripts/goals/goal-census.ts +343 -0
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +30 -3
- package/scripts/marketing/generate-ax-brief.ts +140 -0
- package/scripts/marketing/generate-feature-map.ts +118 -0
- package/scripts/measure/author-section-contribution.ts +84 -0
- package/scripts/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/cut-branch.ts +43 -13
- package/scripts/release-loop/gate-node.ts +82 -14
- package/scripts/release-loop/vault-note-node.ts +257 -8
- package/scripts/release-story/draft.ts +57 -9
- package/scripts/rhythm/daily.ts +263 -76
- package/scripts/rhythm/monthly.ts +97 -0
- package/scripts/rhythm/weekly.ts +30 -9
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/src/acp/client.ts +17 -2
- package/src/agent-env/profile.ts +161 -0
- package/src/agent-mission/driver.ts +51 -1
- package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
- package/src/agent-mission/supervisor-metrics.ts +218 -0
- package/src/agent-substrate/pr-reviewer.ts +2 -0
- package/src/ask-user-question/pending-questions.ts +5 -0
- package/src/auth/github-app-token.ts +112 -13
- package/src/autopilot/build/llm-conflict-merge.ts +32 -6
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/briefing/brief-items.ts +276 -0
- package/src/budget/usage-outcomes.ts +204 -0
- package/src/claims/claims-render.ts +14 -5
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/brief-cli.ts +73 -0
- package/src/cli/decisions-cli.ts +9 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/intake-cli.ts +46 -17
- package/src/cli/know-cli.ts +20 -0
- package/src/cli/logs-cli.ts +43 -1
- package/src/cli/pod-cli.ts +2 -0
- package/src/cli/pr-cli.ts +80 -12
- package/src/cli/release-cli.ts +66 -29
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/usage-cli.ts +26 -0
- package/src/context-bus/context-now-public.ts +16 -6
- package/src/context-bus/context-now.ts +3 -1
- package/src/context-bus/coord-events.ts +6 -4
- package/src/decisions/decision-cards.ts +33 -7
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/discord-self-message.ts +9 -0
- package/src/domains/alert-audit.ts +271 -0
- package/src/domains/core-tools.ts +4 -3
- package/src/domains/ops-facts-tool.ts +31 -1
- package/src/flow-loop/split.ts +24 -0
- package/src/harness/harness-cli-command.ts +147 -17
- package/src/harness/harness-incidents.ts +2 -0
- package/src/harness/harness-pod-dispatch.ts +67 -8
- package/src/harness/harness-queue.ts +380 -50
- package/src/harness/harness-seams.ts +13 -3
- package/src/harness/helper-repair.ts +218 -0
- package/src/harness/helper-scan.ts +27 -4
- package/src/harness/launch-stamp.ts +236 -0
- package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
- package/src/harness/ledger-write-guard.ts +35 -0
- package/src/harness/nested-elanous-policy.ts +114 -0
- package/src/harness/pod-memory-policy.ts +15 -0
- package/src/harness/repository-name.ts +55 -0
- package/src/hq/fence-audit.ts +90 -0
- package/src/hq/hq.ts +70 -6
- package/src/hq/lease.ts +9 -7
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +31 -0
- package/src/hq/transfer.ts +185 -0
- package/src/index.ts +189 -13
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +65 -21
- package/src/intake-plane/front-classifier.ts +6 -0
- package/src/intake-plane/items.ts +3 -2
- package/src/intake-plane/lens.ts +177 -0
- package/src/intake-plane/route.ts +145 -1
- package/src/knowledge/know-find.ts +105 -0
- package/src/llm.ts +75 -19
- package/src/loops/budget.ts +36 -5
- package/src/loops/checker.ts +2 -2
- package/src/loops/orchestrator/author-depth.ts +65 -9
- package/src/loops/orchestrator/author-input-check.ts +8 -3
- package/src/loops/orchestrator/author-ledger.ts +3 -1
- package/src/loops/orchestrator/finish-rate.ts +14 -3
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +107 -29
- package/src/loops/orchestrator/traffic.ts +117 -11
- package/src/loops/registry.ts +142 -61
- package/src/maturity/bot-slash-catalog.ts +11 -2
- package/src/maturity/feature-maturity.ts +4 -3
- package/src/maturity/tui-slash-availability.ts +40 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/http-server.ts +28 -14
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/schedules-read.ts +13 -0
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/oauth/overload-failover-launch.ts +75 -0
- package/src/oauth/overload-failover.ts +137 -0
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +6 -3
- package/src/release-loop/checklist.ts +19 -5
- package/src/release-loop/feature-store.ts +59 -31
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +98 -29
- package/src/seat-dispatch/seat-ask.ts +33 -6
- package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
- package/src/seat-dispatch/seat-request-ledger.ts +133 -0
- package/src/seat-loop/seat-loop.ts +215 -12
- package/src/self-dev/ask-launch-flow.ts +22 -1
- package/src/self-dev/authored-goal-trace.ts +64 -0
- package/src/self-dev/dev-cli.ts +19 -1
- package/src/self-dev/dev-pipeline.ts +14 -9
- package/src/self-dev/draft-sweep.ts +90 -10
- package/src/self-dev/draft-triage-rules.ts +25 -8
- package/src/self-dev/entrance-registry.ts +10 -6
- package/src/self-dev/old-door.ts +73 -0
- package/src/self-dev/orchestrate.ts +15 -1
- package/src/self-dev/review-provider-fallback.ts +6 -1
- package/src/self-dev/run-supervisor.ts +49 -2
- package/src/self-implement/app-api-budget.ts +166 -0
- package/src/self-implement/budget-gate.ts +7 -5
- package/src/self-implement/follow-up-goals.ts +289 -0
- package/src/self-implement/frozen-merges.ts +3 -2
- package/src/self-implement/goal-author.ts +50 -6
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/heal-intake.ts +120 -2
- package/src/self-implement/heal-triage.ts +44 -0
- package/src/self-implement/host-regate.ts +9 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +325 -47
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +42 -7
- package/src/self-implement/run-ledger.ts +6 -0
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +45 -16
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/session-runtime/index.ts +2 -0
- package/src/session-runtime/retry-api.ts +87 -0
- package/src/session-runtime/retry-policy.ts +107 -6
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +20 -2
- package/src/task-cards/card-cli.ts +25 -0
- package/src/task-cards/card-store.ts +5 -3
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
- package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
- package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +70 -9
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +166 -46
- package/src/task-orchestrator/surfaces/self-implement.ts +7 -1
- package/src/telegram-commands.ts +5 -0
- package/src/telegram.ts +72 -4
- package/src/user-config.ts +64 -7
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +0 -1
- package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → rv9cierV0jSOvDBm1g0XB}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmdirSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
3
|
import { dirname, join } from 'node:path';
|
|
3
4
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
4
5
|
import { debug } from '../debug/log.js';
|
|
6
|
+
import { getUserConfig } from '../user-config.js';
|
|
7
|
+
import { psProcessStartMs, START_TOLERANCE_MS } from '../harness/harness-stop.js';
|
|
5
8
|
|
|
6
9
|
export type FailureSource = 'release-run' | 'harness-run' | 'loop-tick' | 'cron';
|
|
7
10
|
|
|
@@ -14,6 +17,114 @@ export interface FailureEvent {
|
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
const FOLD_WINDOW_MS = 60 * 60 * 1_000;
|
|
20
|
+
const HEAL_GRAPH = join(import.meta.dir, '../../graphs/heal/heal-loop.yaml');
|
|
21
|
+
|
|
22
|
+
type HealLoopOptions = { runId: string; input: { failureEvent: FailureEvent }; deps: { root: string } };
|
|
23
|
+
type StartHealLoop = (file: string, options: HealLoopOptions) => Promise<unknown>;
|
|
24
|
+
|
|
25
|
+
function healRunId(event: FailureEvent): string {
|
|
26
|
+
return `heal-${createHash('sha256').update(JSON.stringify([event.source, event.ref, event.at])).digest('hex')}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function maxConcurrentLoops(): number {
|
|
30
|
+
const raw = getUserConfig().raw.heal;
|
|
31
|
+
const cap = raw && typeof raw === 'object' && !Array.isArray(raw)
|
|
32
|
+
? (raw as Record<string, unknown>).maxConcurrentLoops : undefined;
|
|
33
|
+
return typeof cap === 'number' && Number.isSafeInteger(cap) && cap > 0 ? cap : 2;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function ownerAlive(pid: unknown, pidStartedAt: unknown): boolean {
|
|
37
|
+
if (!Number.isSafeInteger(pid) || (pid as number) <= 0 || typeof pidStartedAt !== 'string') return false;
|
|
38
|
+
const started = Date.parse(pidStartedAt);
|
|
39
|
+
if (!Number.isFinite(started)) return false;
|
|
40
|
+
try {
|
|
41
|
+
const actual = psProcessStartMs(pid as number);
|
|
42
|
+
return actual !== null && Number.isFinite(actual) && Math.abs(actual - started) <= START_TOLERANCE_MS;
|
|
43
|
+
} catch { return false; }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function liveHealRuns(root: string): Set<string> {
|
|
47
|
+
const live = new Set<string>();
|
|
48
|
+
const dir = join(root, 'graph-runs', 'heal-loop');
|
|
49
|
+
let files: string[];
|
|
50
|
+
try { files = readdirSync(dir); }
|
|
51
|
+
catch (error) {
|
|
52
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return live;
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
if (!file.endsWith('.json')) continue;
|
|
57
|
+
try {
|
|
58
|
+
const state = JSON.parse(readFileSync(join(dir, file), 'utf8')) as Record<string, unknown>;
|
|
59
|
+
if (state.graphId === 'heal-loop' && state.runId === file.slice(0, -5) &&
|
|
60
|
+
state.status === 'running' && ownerAlive(state.pid, state.pidStartedAt)) live.add(state.runId as string);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT' && !(error instanceof SyntaxError)) throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return live;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** A test process must never start the real heal loop — it spawns external grounding (10-05 load 150 incident). */
|
|
69
|
+
export async function startHealLoop(file: string, options: HealLoopOptions, env: NodeJS.ProcessEnv = process.env,
|
|
70
|
+
run: StartHealLoop = async (graph, args) => (await import('../graph-runner/runner.js')).runGraph(graph, args)): Promise<unknown> {
|
|
71
|
+
if (env.NODE_ENV === 'test' || env.ELANOUS_TEST_HOME) {
|
|
72
|
+
debug.log('heal.intake', 'loop-start-skipped-test', { runId: options.runId });
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const root = options.deps.root;
|
|
76
|
+
const claimDir = join(root, 'heal', 'starts');
|
|
77
|
+
const selected = withInboxLock(inboxPath(root), () => {
|
|
78
|
+
mkdirSync(claimDir, { recursive: true });
|
|
79
|
+
const live = liveHealRuns(root);
|
|
80
|
+
const claimed = new Set<string>();
|
|
81
|
+
for (const name of readdirSync(claimDir)) {
|
|
82
|
+
if (!/^heal-[a-f0-9]{64}\.json$/.test(name)) continue;
|
|
83
|
+
const runId = name.slice(0, -5);
|
|
84
|
+
const path = join(claimDir, name);
|
|
85
|
+
const claim = JSON.parse(readFileSync(path, 'utf8')) as { active?: boolean; started?: boolean; pid?: number; pidStartedAt?: string };
|
|
86
|
+
const owner = claim.active && ownerAlive(claim.pid, claim.pidStartedAt);
|
|
87
|
+
if (!owner && !claim.started && !existsSync(join(root, 'graph-runs', 'heal-loop', `${runId}.json`))) {
|
|
88
|
+
unlinkSync(path);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
claimed.add(runId);
|
|
92
|
+
if (owner && !live.has(runId)) live.add(runId);
|
|
93
|
+
}
|
|
94
|
+
const cap = maxConcurrentLoops();
|
|
95
|
+
if (live.size >= cap) {
|
|
96
|
+
debug.log('heal.intake', 'loop-start-deferred', { running: live.size, cap, runId: options.runId });
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const next = readInbox(inboxPath(root)).find(event => !claimed.has(healRunId(event)) &&
|
|
100
|
+
!existsSync(join(root, 'graph-runs', 'heal-loop', `${healRunId(event)}.json`)));
|
|
101
|
+
if (!next) return undefined;
|
|
102
|
+
const runId = healRunId(next);
|
|
103
|
+
writeFileSync(join(claimDir, `${runId}.json`), JSON.stringify({ active: true, pid: process.pid,
|
|
104
|
+
pidStartedAt: new Date(Date.now() - process.uptime() * 1_000).toISOString() }), { flag: 'wx' });
|
|
105
|
+
return { event: next, runId };
|
|
106
|
+
});
|
|
107
|
+
if (!selected) return undefined;
|
|
108
|
+
let started = false;
|
|
109
|
+
try {
|
|
110
|
+
const result = await run(file, { runId: selected.runId, input: { failureEvent: selected.event }, deps: { root } });
|
|
111
|
+
started = true;
|
|
112
|
+
return result;
|
|
113
|
+
} finally {
|
|
114
|
+
withInboxLock(inboxPath(root), () => {
|
|
115
|
+
const claim = join(claimDir, `${selected.runId}.json`);
|
|
116
|
+
if (started || existsSync(join(root, 'graph-runs', 'heal-loop', `${selected.runId}.json`))) {
|
|
117
|
+
writeFileSync(claim, JSON.stringify({ active: false, started: true }));
|
|
118
|
+
} else {
|
|
119
|
+
unlinkSync(claim);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
// A completed run opens a slot; a pre-ledger failure waits for the next intake rather than retrying in a tight loop.
|
|
123
|
+
if (started) void Promise.resolve().then(() => startHealLoop(file, options, env, run)).catch(error => {
|
|
124
|
+
debug.log('heal.intake', 'loop-start-failed', { runId: selected.runId, error: String(error) }, { level: 'error' });
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
17
128
|
|
|
18
129
|
function inboxPath(root: string): string {
|
|
19
130
|
return join(root, 'heal', 'inbox.jsonl');
|
|
@@ -55,7 +166,7 @@ function withInboxLock<T>(file: string, work: () => T): T {
|
|
|
55
166
|
}
|
|
56
167
|
|
|
57
168
|
/** One durable JSON line per failure; repeat source+ref within an hour is folded. */
|
|
58
|
-
export function recordFailureEvent(event: FailureEvent, root = effectiveInstanceRoot()): { folded: boolean } {
|
|
169
|
+
export function recordFailureEvent(event: FailureEvent, root = effectiveInstanceRoot(), startLoop: StartHealLoop = startHealLoop): { folded: boolean } {
|
|
59
170
|
if (!['release-run', 'harness-run', 'loop-tick', 'cron'].includes(event.source) ||
|
|
60
171
|
!event.kind || !event.ref || !event.summary || !Number.isFinite(Date.parse(event.at))) {
|
|
61
172
|
throw new Error('invalid heal failure event');
|
|
@@ -69,6 +180,13 @@ export function recordFailureEvent(event: FailureEvent, root = effectiveInstance
|
|
|
69
180
|
return false;
|
|
70
181
|
});
|
|
71
182
|
debug.log('heal.intake', 'recorded', { source: event.source, kind: event.kind, ref: event.ref, folded });
|
|
183
|
+
if (!folded) {
|
|
184
|
+
const runId = healRunId(event);
|
|
185
|
+
void Promise.resolve().then(() => startLoop(HEAL_GRAPH, { runId, input: { failureEvent: event }, deps: { root } }))
|
|
186
|
+
.catch(error => {
|
|
187
|
+
debug.log('heal.intake', 'loop-start-failed', { source: event.source, kind: event.kind, ref: event.ref, runId, error: String(error) }, { level: 'error' });
|
|
188
|
+
});
|
|
189
|
+
}
|
|
72
190
|
return { folded };
|
|
73
191
|
}
|
|
74
192
|
|
|
@@ -156,6 +156,50 @@ export interface HealTriageResult {
|
|
|
156
156
|
readonly evidence: readonly string[];
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/** 수리 한도를 다 쓴 런의 결함 셋. 힐 루프가 이 셋 중 하나만 고른다. */
|
|
160
|
+
export type ReworkCapDefectClass = 'test-defect' | 'goal-defect' | 'code-defect';
|
|
161
|
+
|
|
162
|
+
/** 분류 뒤 힐 루프가 고르는 다음 행동. */
|
|
163
|
+
export type ReworkCapNextAction = 'harvest' | 'relaunch' | 'card';
|
|
164
|
+
|
|
165
|
+
export interface ReworkCapHealInput {
|
|
166
|
+
/** 게이트가 이번 diff 가 들여온 실패 수. 없으면 0. */
|
|
167
|
+
readonly introduced?: readonly string[];
|
|
168
|
+
/** 마지막 리뷰 must-fix. 없으면 빈 목록. */
|
|
169
|
+
readonly mustFix?: readonly string[];
|
|
170
|
+
readonly summary?: string;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface ReworkCapHealDecision {
|
|
174
|
+
readonly defectClass: ReworkCapDefectClass;
|
|
175
|
+
readonly nextAction: ReworkCapNextAction;
|
|
176
|
+
readonly evidence: readonly string[];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const GOAL_DEFECT = /골|goal|acceptance|수용 기준|요구가 모순|스펙|경계:/i;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 수리 한도 소진 런을 «시험 결함 · 골 결함 · 코드 결함» 중 하나로 가르고
|
|
183
|
+
* 수확·재발사·카드 중 하나를 고른다. 근거는 게이트 introduced 목록과 마지막 리뷰 지적이다.
|
|
184
|
+
* 도입 실패가 있으면 코드 결함(재발사). 리뷰가 골·수용 기준을 짚으면 골 결함(카드).
|
|
185
|
+
* 그 밖(리뷰만 있거나 근거가 비면)은 시험 결함(수확)이다.
|
|
186
|
+
*/
|
|
187
|
+
export function classifyReworkCapExhaustion(input: ReworkCapHealInput): ReworkCapHealDecision {
|
|
188
|
+
const introduced = (input.introduced ?? []).map((item) => item.trim()).filter(Boolean);
|
|
189
|
+
const mustFix = (input.mustFix ?? []).map((item) => item.trim()).filter(Boolean);
|
|
190
|
+
const evidence = [
|
|
191
|
+
...introduced.map((item) => `gate introduced: ${item}`),
|
|
192
|
+
...mustFix.map((item) => `review must-fix: ${item}`),
|
|
193
|
+
];
|
|
194
|
+
if (introduced.length > 0) {
|
|
195
|
+
return { defectClass: 'code-defect', nextAction: 'relaunch', evidence };
|
|
196
|
+
}
|
|
197
|
+
if (mustFix.some((item) => GOAL_DEFECT.test(item)) || (input.summary ? GOAL_DEFECT.test(input.summary) : false)) {
|
|
198
|
+
return { defectClass: 'goal-defect', nextAction: 'card', evidence };
|
|
199
|
+
}
|
|
200
|
+
return { defectClass: 'test-defect', nextAction: 'harvest', evidence };
|
|
201
|
+
}
|
|
202
|
+
|
|
159
203
|
const UNTESTABLE_EXT = new Set(['.md', '.json', '.yaml', '.yml', '.txt']);
|
|
160
204
|
const ENVIRONMENT_CLAIM = /할당량|자격|기판|quota|credential|substrate/i;
|
|
161
205
|
|
|
@@ -20,7 +20,7 @@ export function needsPwaBuild(files: readonly string[]): boolean {
|
|
|
20
20
|
return files.some((file) => file.startsWith('apps/pwa/') || (file.startsWith('src/') && !/\.test\.tsx?$/.test(file)));
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export interface HostRegateInput { prNumber: number; headCommit: string; repoRoot: string; verifyOnly?: boolean; /** Called by a resume sweep that already holds this PR's claim. */ resumed?: true }
|
|
23
|
+
export interface HostRegateInput { prNumber: number; headCommit: string; repoRoot: string; goalFile?: string; verifyOnly?: boolean; /** Called by a resume sweep that already holds this PR's claim. */ resumed?: true }
|
|
24
24
|
export interface HostRegateResult { passed: boolean; failures: Array<{ step: string; detail: string }>; os: string; status?: 'passed' | 'failed' | 'unmeasured' | 'frozen'; /** verifyOnly: 실제로 얹어 잰 base 끝 */ baseCommit?: string }
|
|
25
25
|
export type HostRegateDeps = {
|
|
26
26
|
command?: (bin: string, args: readonly string[], cwd: string, env?: NodeJS.ProcessEnv) => { status: number | null; stdout: string; stderr: string };
|
|
@@ -31,6 +31,7 @@ export type HostRegateDeps = {
|
|
|
31
31
|
log?: (event: 'passed' | 'failed' | 'unmeasured' | 'base-raced', data: Record<string, unknown>) => void;
|
|
32
32
|
freezeRoot?: string;
|
|
33
33
|
runExposeGate?: typeof runExposeGate;
|
|
34
|
+
syncMergedChecklist?: typeof import('../release-loop/merged-pr-checklist.js').syncMergedPrChecklist;
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
const SPAWN_MAX_BUFFER = 256 * 1024 * 1024;
|
|
@@ -240,7 +241,7 @@ export async function runHostRegate(input: HostRegateInput, deps: HostRegateDeps
|
|
|
240
241
|
// --match-head-commit pins the head. gh has no base pin, so the base was re-read just
|
|
241
242
|
// above; the seconds between that read and the merge are checked after the fact below.
|
|
242
243
|
if (input.verifyOnly) return result('passed');
|
|
243
|
-
const landing = admitLandingMerge({ prNumber: input.prNumber, headCommit: input.headCommit, repoRoot: input.repoRoot }, deps.freezeRoot, {}, input.resumed ? undefined : { prNumber: input.prNumber, repoRoot: input.repoRoot, headCommit: input.headCommit });
|
|
244
|
+
const landing = admitLandingMerge({ prNumber: input.prNumber, headCommit: input.headCommit, repoRoot: input.repoRoot, ...(input.goalFile ? { goalFile: input.goalFile } : {}) }, deps.freezeRoot, {}, input.resumed ? undefined : { prNumber: input.prNumber, repoRoot: input.repoRoot, headCommit: input.headCommit });
|
|
244
245
|
if (landing.kind !== 'merge') {
|
|
245
246
|
debug.log('harness.merge', 'frozen', { pr: input.prNumber, ...(landing.kind === 'held' ? { reason: landing.freeze.reason, until: landing.freeze.until } : { resumedElsewhere: true }) });
|
|
246
247
|
return { passed: true, failures: [], os: process.platform, status: 'frozen' };
|
|
@@ -255,6 +256,12 @@ export async function runHostRegate(input: HostRegateInput, deps: HostRegateDeps
|
|
|
255
256
|
catch (e) { return result('unmeasured', 'merge-confirm', String(e)); }
|
|
256
257
|
if (merged.state !== 'MERGED') return result('unmeasured', 'merge-confirm', `PR state after merge is ${merged.state ?? 'unknown'}`);
|
|
257
258
|
landed = true;
|
|
259
|
+
try {
|
|
260
|
+
const sync = deps.syncMergedChecklist ?? (await import('../release-loop/merged-pr-checklist.js')).syncMergedPrChecklist;
|
|
261
|
+
sync(input.prNumber, input.repoRoot, input.goalFile);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
debug.log('release.checklist', 'merged-pr-sync-failed', { pr: input.prNumber, error: String(error) });
|
|
264
|
+
}
|
|
258
265
|
const mergeCommit = merged.mergeCommit?.oid ?? '';
|
|
259
266
|
let mergedOnto = '';
|
|
260
267
|
try {
|
|
@@ -8,6 +8,20 @@ import { getDefaultLogStore } from '../mss/logging/log-store.js';
|
|
|
8
8
|
type Command = (bin: string, args: readonly string[], cwd: string) => { status: number | null; stdout: string; stderr: string };
|
|
9
9
|
export type L8MergeQueueInput = { number: number; cwd: string; matchHeadCommit: string };
|
|
10
10
|
export type L8MergeQueueResult = { merged: boolean; baseRefName?: string; mergeCommit?: string; detail?: string };
|
|
11
|
+
/** Read-only integration report. Never a landing decision and never a remote write. */
|
|
12
|
+
export type L8ChangedTestResult = { file: string; passed: boolean; pass: number; fail: number; detail?: string };
|
|
13
|
+
export type L8IntegrationEvaluation = {
|
|
14
|
+
number: number;
|
|
15
|
+
head: string;
|
|
16
|
+
base?: string;
|
|
17
|
+
/** True only when the candidate integrates (next.md-only conflicts resolved) and every changed test passed. */
|
|
18
|
+
feasible: boolean;
|
|
19
|
+
conflictingFiles: string[];
|
|
20
|
+
/** True when there is no conflict, or every conflict path is exactly release/next.md. */
|
|
21
|
+
conflictsLimitedToNextMd: boolean;
|
|
22
|
+
changedTests: L8ChangedTestResult[];
|
|
23
|
+
detail?: string;
|
|
24
|
+
};
|
|
11
25
|
export type L8MergeQueueDeps = {
|
|
12
26
|
command?: Command;
|
|
13
27
|
/** Must identify the same shared repository across all harness processes. */
|
|
@@ -195,6 +209,124 @@ export async function acquireL8MergeQueue(cwd: string): Promise<() => void> {
|
|
|
195
209
|
}
|
|
196
210
|
}
|
|
197
211
|
|
|
212
|
+
const TEST_PATH = /(?:^|\/)[^/]+\.(?:test|spec)\.(?:[cm]?[jt]sx?)$/;
|
|
213
|
+
|
|
214
|
+
function parseChangedTestCounts(output: string): { pass: number; fail: number; ran: number; files: number } | null {
|
|
215
|
+
const counts = /\b(\d+) pass\b[\s\S]*?\b(\d+) fail\b[\s\S]*?Ran ([1-9]\d*) tests? across ([1-9]\d*) files?/.exec(output);
|
|
216
|
+
if (!counts) return null;
|
|
217
|
+
return { pass: Number(counts[1]), fail: Number(counts[2]), ran: Number(counts[3]), files: Number(counts[4]) };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Report whether one pinned PR can integrate onto freshly fetched main.
|
|
222
|
+
* Resolves an append-only release/next.md conflict in a disposable worktree only.
|
|
223
|
+
* Never pushes, never calls a merge API, and never moves a remote ref.
|
|
224
|
+
*/
|
|
225
|
+
export async function evaluateL8Integration(input: L8MergeQueueInput, deps: L8MergeQueueDeps = {}): Promise<L8IntegrationEvaluation> {
|
|
226
|
+
const report: L8IntegrationEvaluation = {
|
|
227
|
+
number: input.number,
|
|
228
|
+
head: input.matchHeadCommit,
|
|
229
|
+
feasible: false,
|
|
230
|
+
conflictingFiles: [],
|
|
231
|
+
conflictsLimitedToNextMd: false,
|
|
232
|
+
changedTests: [],
|
|
233
|
+
};
|
|
234
|
+
if (!Number.isSafeInteger(input.number) || input.number <= 0 || !/^[0-9a-f]{40}$/i.test(input.matchHeadCommit)) {
|
|
235
|
+
return { ...report, detail: 'invalid PR number or pinned head' };
|
|
236
|
+
}
|
|
237
|
+
const execute = deps.command ?? command;
|
|
238
|
+
const run = (bin: string, args: readonly string[], cwd: string): string => {
|
|
239
|
+
const result = execute(bin, args, cwd);
|
|
240
|
+
if (result.status !== 0) throw new Error(`${bin} ${args.join(' ')}: ${(result.stderr || result.stdout || `exit ${result.status}`).trim().slice(0, 500)}`);
|
|
241
|
+
return args.includes('-z') ? result.stdout : result.stdout.trim();
|
|
242
|
+
};
|
|
243
|
+
const refuseRemote = (bin: string, args: readonly string[]) => {
|
|
244
|
+
if (bin === 'git' && args.some((arg) => arg === 'push' || arg === 'update-ref')) throw new Error('evaluation must not push');
|
|
245
|
+
if (bin === 'gh' && args.some((arg) => arg === 'merge')) throw new Error('evaluation must not merge');
|
|
246
|
+
};
|
|
247
|
+
const guarded: Command = (bin, args, cwd) => {
|
|
248
|
+
refuseRemote(bin, args);
|
|
249
|
+
return execute(bin, args, cwd);
|
|
250
|
+
};
|
|
251
|
+
let temp: string | undefined;
|
|
252
|
+
let attached = false;
|
|
253
|
+
try {
|
|
254
|
+
type View = { headRefOid?: string; headRefName?: string; baseRefName?: string; state?: string; isDraft?: boolean; isCrossRepository?: boolean };
|
|
255
|
+
const view = JSON.parse(run('gh', ['pr', 'view', String(input.number), '--json', 'headRefOid,headRefName,baseRefName,state,isDraft,isCrossRepository'], input.cwd)) as View;
|
|
256
|
+
if (view.headRefOid !== input.matchHeadCommit || view.baseRefName !== 'main' || view.state !== 'OPEN' || view.isDraft !== false || view.isCrossRepository !== false) {
|
|
257
|
+
throw new Error('PR head, base, or ready/open state changed');
|
|
258
|
+
}
|
|
259
|
+
run('git', ['fetch', 'origin', 'refs/heads/main'], input.cwd);
|
|
260
|
+
const base = run('git', ['rev-parse', 'FETCH_HEAD'], input.cwd);
|
|
261
|
+
if (!/^[0-9a-f]{40}$/i.test(base)) throw new Error('main commit unavailable');
|
|
262
|
+
report.base = base;
|
|
263
|
+
run('git', ['fetch', 'origin', `refs/pull/${input.number}/head`], input.cwd);
|
|
264
|
+
if (run('git', ['rev-parse', 'FETCH_HEAD'], input.cwd) !== input.matchHeadCommit) throw new Error('fetched PR head changed');
|
|
265
|
+
temp = mkdtempSync(join(tmpdir(), 'elanous-l8-eval-'));
|
|
266
|
+
run('git', ['worktree', 'add', '--detach', temp, input.matchHeadCommit], input.cwd);
|
|
267
|
+
attached = true;
|
|
268
|
+
const fork = run('git', ['merge-base', base, input.matchHeadCommit], temp);
|
|
269
|
+
const paths = run('git', ['diff', '--name-only', '-z', fork, input.matchHeadCommit], temp).split('\0').filter(Boolean);
|
|
270
|
+
if (!paths.length) throw new Error('PR changed-file paths unavailable');
|
|
271
|
+
if (paths.some((path) => path.startsWith('-') || path.startsWith('/') || path.split('/').includes('..') || path.includes('\\'))) throw new Error('unsafe PR changed-file path');
|
|
272
|
+
const livePaths = run('git', ['diff', '--name-only', '-z', '--diff-filter=ACMRT', fork, input.matchHeadCommit], temp).split('\0').filter(Boolean);
|
|
273
|
+
const tests = livePaths.filter((path) => TEST_PATH.test(path));
|
|
274
|
+
const merge = guarded('git', ['-c', 'user.name=elanous merge queue', '-c', 'user.email=queue@localhost', 'merge', '--no-ff', '--no-edit', base], temp);
|
|
275
|
+
if (merge.status !== 0) {
|
|
276
|
+
const unresolved = run('git', ['diff', '--name-only', '--diff-filter=U'], temp).split('\n').filter(Boolean);
|
|
277
|
+
if (!unresolved.length) throw new Error(`git merge failed: ${(merge.stderr || `exit ${merge.status}`).trim()}`);
|
|
278
|
+
report.conflictingFiles = unresolved;
|
|
279
|
+
report.conflictsLimitedToNextMd = unresolved.length === 1 && unresolved[0] === NEXT_MD_PATH;
|
|
280
|
+
if (!report.conflictsLimitedToNextMd) {
|
|
281
|
+
report.detail = `merge conflicts: ${unresolved.join(', ')}`;
|
|
282
|
+
return report;
|
|
283
|
+
}
|
|
284
|
+
const stage = (n: number): string => {
|
|
285
|
+
const result = guarded('git', ['show', `:${n}:${NEXT_MD_PATH}`], temp!);
|
|
286
|
+
if (result.status !== 0) throw new Error(`release/next.md stage ${n} unavailable: ${result.stderr}`);
|
|
287
|
+
return result.stdout;
|
|
288
|
+
};
|
|
289
|
+
const resolved = resolveNextMdConflict(stage(1), stage(3), stage(2));
|
|
290
|
+
if (resolved === null) {
|
|
291
|
+
report.feasible = false;
|
|
292
|
+
report.detail = 'release/next.md conflict is not append-only';
|
|
293
|
+
return report;
|
|
294
|
+
}
|
|
295
|
+
writeFileSync(join(temp, NEXT_MD_PATH), resolved);
|
|
296
|
+
run('git', ['add', '--', NEXT_MD_PATH], temp);
|
|
297
|
+
run('git', ['-c', 'user.name=elanous merge queue', '-c', 'user.email=queue@localhost', 'commit', '-m', 'Integrate latest main for L8 evaluation'], temp);
|
|
298
|
+
} else {
|
|
299
|
+
report.conflictsLimitedToNextMd = true;
|
|
300
|
+
}
|
|
301
|
+
if (tests.length) {
|
|
302
|
+
const unavailable = tests.filter((path) => !existsSync(join(temp!, path)));
|
|
303
|
+
if (unavailable.length) throw new Error(`changed test paths unavailable: ${unavailable.join(', ')}`);
|
|
304
|
+
run('bun', ['install', '--frozen-lockfile'], temp);
|
|
305
|
+
for (const file of tests) {
|
|
306
|
+
const result = guarded('bun', ['test', file], temp);
|
|
307
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
308
|
+
const counts = parseChangedTestCounts(output);
|
|
309
|
+
if (!counts || counts.pass + counts.fail !== counts.ran || counts.files !== 1 || (result.status !== 0 && counts.fail === 0)) {
|
|
310
|
+
throw new Error(`changed tests unmeasured: ${output.slice(-400)}`);
|
|
311
|
+
}
|
|
312
|
+
const passed = result.status === 0 && counts.fail === 0 && counts.pass > 0;
|
|
313
|
+
report.changedTests.push({ file, passed, pass: counts.pass, fail: counts.fail, ...(passed ? {} : { detail: output.slice(-400) }) });
|
|
314
|
+
}
|
|
315
|
+
if (report.changedTests.some((entry) => !entry.passed)) {
|
|
316
|
+
report.detail = 'changed tests failed';
|
|
317
|
+
return report;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
report.feasible = true;
|
|
321
|
+
return report;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
return { ...report, feasible: false, detail: String(error) };
|
|
324
|
+
} finally {
|
|
325
|
+
if (attached && temp) execute('git', ['worktree', 'remove', '--force', temp], input.cwd);
|
|
326
|
+
if (temp) rmSync(temp, { recursive: true, force: true });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
198
330
|
/** One PR per shared git directory, integrated on a freshly fetched main in a disposable worktree. */
|
|
199
331
|
export async function runL8MergeQueue(input: L8MergeQueueInput, deps: L8MergeQueueDeps = {}): Promise<L8MergeQueueResult> {
|
|
200
332
|
if (!Number.isSafeInteger(input.number) || input.number <= 0 || !/^[0-9a-f]{40}$/i.test(input.matchHeadCommit)) {
|