elanous 0.2.25 → 0.2.28
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/{1454-37fd16f479f5554a.js → 1454-29d1d773d5996a49.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{599-a914cc0468f56143.js → 599-56f2c0a5cc4a7295.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-b12149c51f7d04b1.js → page-fa46d4588e8442c2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/{page-305cd294e754906e.js → page-256ffd365d32e100.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-dea4b795741126fd.js → page-e0de78ae7b99f01c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-45889e7070d11126.js → page-4c741eb760f2f77c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-16a2e893f83ae4d0.js → page-e9ba98f5f1440a21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-56888bc744e08dac.js → page-2f6f303fa114ec24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-952c6ceec6d9de38.js → page-ced0bcfc6542b3fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-6d0202af568f4c63.js → page-bb1f05cd51c38944.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-4fb6fc34e20c07b7.js → page-f736324afa2117c1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-e9502921f4f01b1f.js → page-6f131b51193a36f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-b6169ea2f09cf287.js → not-found-435f5e2faf02bc01.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-6a14333d1b1b3967.js → page-497e0eedf20e7890.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-8c91a9a66c9ec90a.js → page-a8d28fdf995e16ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{page-3d3fc73dd187ed52.js → page-66a67eb19ebd976b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-9c04dac0831c2bbb.js → page-c71267e7681da982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-04de9ca6d52a3893.js → page-cb249e81ed339318.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-62527677bbc2fdc4.js → page-781cfc5114e097da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-9a2e09c4a311764c.js → page-478157214db6a023.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-bfeb35d69e8b24bb.js → page-86141f12cf090b7f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-89cb33bced824e4a.js → page-7256c73bb1c36751.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/{page-6445ac4cbb8dd869.js → page-979d318a534e92da.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-20162174b77f96cf.js → page-601fb77340e63c9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-05a4b2c9b91034aa.js → page-11bf9e5725a72c22.js} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +13 -13
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +13 -13
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +13 -13
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +13 -13
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +13 -13
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +13 -13
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +13 -13
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +13 -13
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +13 -13
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +13 -13
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +13 -13
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +13 -13
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +13 -13
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +13 -13
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +13 -13
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +13 -13
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +13 -13
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +13 -13
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +13 -13
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +13 -13
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +13 -13
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +13 -13
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +13 -13
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +13 -13
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +13 -13
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +13 -13
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +13 -13
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +13 -13
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +14 -14
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +13 -13
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +13 -13
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +13 -13
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +13 -13
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +13 -13
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +13 -13
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +13 -13
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +13 -13
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +13 -13
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +13 -13
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +13 -13
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +13 -13
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +13 -13
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +13 -13
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +13 -13
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +13 -13
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +13 -13
- package/graphs/implement-loop.yaml +38 -1
- package/graphs/steward/recipes.yaml +6 -0
- package/graphs/steward/release-day.yaml +23 -0
- package/graphs/steward/steward.yaml +9 -1
- package/graphs/work/code-pr.yaml +23 -0
- package/package.json +1 -1
- package/scripts/browser-observe-cycle.ts +76 -2
- package/scripts/ci-typecheck-changed.ts +119 -7
- package/scripts/coord-msg-bridge.ts +209 -0
- package/scripts/coord-post.sh +40 -7
- package/scripts/coord-requests-bridge.ts +10 -5
- package/scripts/detect-test-interference.ts +31 -1
- package/scripts/expose-rubric-suggest.ts +1 -1
- package/scripts/measure-tsc-affected-roots.ts +10 -1
- package/scripts/release-loop/docs-node.ts +51 -22
- package/scripts/release-loop/export-check-node.ts +8 -5
- package/scripts/release-loop/gate-node.ts +37 -4
- package/scripts/release-loop/node-verdict.ts +10 -3
- package/scripts/release-loop/publish-node.ts +3 -0
- package/scripts/release-loop/tui-node-checks.ts +5 -2
- package/scripts/skills/public-check.ts +5 -2
- package/scripts/test-deterministic.ts +520 -37
- package/scripts/ux-sim.ts +7 -1
- package/scripts/verify-kpack6-live.ts +230 -0
- package/src/acp/codex-plugins.ts +17 -0
- package/src/agent/task-notification.ts +32 -2
- package/src/agent-mission/in-agent-plugin.ts +8 -3
- package/src/agent-substrate/review-observation.ts +9 -2
- package/src/agent-substrate/self-review-cli.ts +62 -16
- package/src/boot/daemon-tools/self-implement.ts +72 -4
- package/src/briefing/brief-items.ts +151 -35
- package/src/budget/llm-cost.ts +30 -3
- package/src/budget/usage-outcomes.ts +32 -0
- package/src/claims/claims-ledger.ts +23 -0
- package/src/claims/claims-render.ts +22 -2
- package/src/cli/brief-cli.ts +131 -10
- package/src/cli/claims-cli.ts +24 -4
- package/src/cli/control-cli.ts +29 -0
- package/src/cli/doctor-cli.ts +90 -8
- package/src/cli/msg-cli.ts +177 -11
- package/src/cli/plugin-cli.ts +11 -3
- package/src/cli/pr-cli.ts +133 -18
- package/src/cli/pr-land-overlap-evidence.ts +18 -10
- package/src/cli/pr-land-refusal.ts +20 -0
- package/src/cli/release-cli.ts +34 -5
- package/src/cli/rfc-cli.ts +24 -0
- package/src/cli/seat-cli.ts +121 -0
- package/src/cli/self-utterances-cli.ts +140 -0
- package/src/cli/tailscale-serve.ts +5 -8
- package/src/cli/tasks-cli.ts +528 -31
- package/src/cli/usage-cli.ts +9 -4
- package/src/context-card/cell.ts +40 -0
- package/src/dashboard/boot-timing.ts +35 -0
- package/src/dashboard/first-screen-band.ts +63 -13
- package/src/dashboard/index.ts +94 -27
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
- package/src/dashboard/skill-detection-runtime.ts +24 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +6 -0
- package/src/debug/log.ts +1 -1
- package/src/domains/morning-report.ts +3 -1
- package/src/domains/release-tool.ts +29 -12
- package/src/domains/surface-events.ts +16 -0
- package/src/elanous-config-dir.ts +2 -2
- package/src/fleet/host-doctor.ts +73 -2
- package/src/github/pr-labels.ts +10 -4
- package/src/graph-runner/runner.ts +104 -15
- package/src/harness/harness-clean.ts +21 -3
- package/src/harness/harness-cli-command.ts +68 -10
- package/src/harness/harness-pod-dispatch.ts +11 -4
- package/src/harness/harness-stop.ts +11 -7
- package/src/hq/seats.ts +17 -3
- package/src/index.ts +69 -9
- package/src/instance/current.ts +2 -6
- package/src/instance/resolve.ts +5 -6
- package/src/intelligence-map/model-bench-scores.ts +102 -0
- package/src/llm/openai-sampling-fields.ts +89 -0
- package/src/llm/runtime-model-family.ts +61 -0
- package/src/llm/zai-coding-plan.ts +27 -1
- package/src/llm.ts +13 -89
- package/src/loops/orchestrator/inbox.ts +73 -0
- package/src/loops/orchestrator/resync-shadow.ts +100 -12
- package/src/loops/orchestrator/tick.ts +73 -2
- package/src/machines/machine-ledger.ts +31 -3
- package/src/maturity/cli-maturity.ts +13 -3
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/msg/msg-sse-server.ts +141 -0
- package/src/msg/msg-store.ts +149 -17
- package/src/ops/overview-collect.ts +3 -2
- package/src/ops/overview-owner.ts +29 -0
- package/src/patents/ledger.ts +128 -0
- package/src/plugin-hooks/shell-hook.ts +17 -2
- package/src/plugin-missions/evaluator-runner.ts +18 -2
- package/src/plugins/install/market-fetch.ts +32 -1
- package/src/plugins/install/plugin-install.ts +11 -1
- package/src/pod-lease/admission.ts +116 -41
- package/src/prompt-enhance/enhance.ts +11 -0
- package/src/release-loop/checklist.ts +52 -4
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/merged-pr-checklist.ts +24 -5
- package/src/release-loop/rubric.ts +70 -0
- package/src/rfc-registry/registry.ts +88 -0
- package/src/seat-dispatch/seat-request-ledger.ts +4 -0
- package/src/seat-loop/knowhow-lint.ts +233 -0
- package/src/seat-loop/seat-loop.ts +5 -1
- package/src/seat-loop/seat-pack.ts +16 -4
- package/src/seat-loop/traffic-signal.ts +7 -0
- package/src/self-dev/ask-launch-flow.ts +48 -8
- package/src/self-dev/ask-path-candidates.ts +69 -14
- package/src/self-dev/decompose-proposal.ts +43 -1
- package/src/self-dev/draft-sweep.ts +23 -14
- package/src/self-dev/graph-journey-nodes.ts +2 -0
- package/src/self-dev/launch-preflight-text.ts +5 -1
- package/src/self-dev/launch-preflight.ts +143 -10
- package/src/self-dev/orchestrate.ts +6 -2
- package/src/self-dev/run-supervisor.ts +35 -1
- package/src/self-implement/gate-cli.ts +6 -2
- package/src/self-implement/goal-author.ts +5 -28
- package/src/self-implement/goal-command-execution.ts +26 -0
- package/src/self-implement/goal-new-paths.ts +4 -5
- package/src/self-implement/graph-authority.ts +17 -3
- package/src/self-implement/graph-yaml.ts +15 -7
- package/src/self-implement/headless-elanous-driver.ts +16 -1
- package/src/self-implement/host-regate.ts +44 -9
- package/src/self-implement/orchestrator.ts +5 -11
- package/src/self-implement/pwa-import-graph.ts +194 -7
- package/src/self-implement/rework-policy.ts +80 -2
- package/src/self-implement/self-implement-runtime.ts +126 -21
- package/src/self-implement/speed-claims.ts +145 -6
- package/src/session/index.ts +14 -4
- package/src/session-runtime/index.ts +55 -11
- package/src/skills/tools/persistent-grounding.ts +59 -4
- package/src/skills/tools/shell.ts +79 -1
- package/src/steward/rejudge-tick.ts +12 -1
- package/src/steward/release-day.ts +158 -0
- package/src/steward/resync-tick.ts +123 -0
- package/src/task-agent/actions.ts +1 -0
- package/src/task-agent/adopt-pr.ts +140 -0
- package/src/task-agent/board.ts +11 -1
- package/src/task-agent/card-evidence.ts +37 -5
- package/src/task-agent/cover.ts +105 -19
- package/src/task-agent/failure-notify.ts +61 -0
- package/src/task-agent/green-sweep.ts +170 -0
- package/src/task-agent/land-worktree.ts +52 -5
- package/src/task-agent/live-move-history.ts +1 -0
- package/src/task-agent/live-moves.ts +56 -17
- package/src/task-agent/parent-host.ts +1 -1
- package/src/task-agent/postland-card.ts +139 -0
- package/src/task-agent/rejudge-on-head.ts +321 -25
- package/src/task-agent/remote-rejudge-bind.ts +62 -0
- package/src/task-agent/repair-branch-cas.ts +182 -0
- package/src/task-agent/repair-cycle.ts +275 -0
- package/src/task-agent/repair-next-move.ts +31 -0
- package/src/task-agent/repair-review.ts +24 -0
- package/src/task-agent/repair-worker.ts +87 -0
- package/src/task-agent/review-result-capture.ts +66 -2
- package/src/task-agent/shadow.ts +150 -29
- package/src/task-agent/task-hand.ts +133 -8
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +71 -12
- package/src/telegram.ts +30 -1
- package/src/tool-runtime/tool-search-runtime.ts +25 -0
- package/src/tui.ts +20 -1
- package/src/user-config.ts +11 -60
- package/src/ux-sim/fault-sqlite-busy.ts +50 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4e634c5b98fd4e93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b9cc8a10bc6078b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +0 -1
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_ssgManifest.js +0 -0
|
@@ -348,7 +348,31 @@ export interface DecomposeApplication {
|
|
|
348
348
|
* ⓐ 같은 goal 을 «두 번» 쪼개지 않는다(alreadyDecomposed)
|
|
349
349
|
* ⓑ 조각이 둘 미만인 제안은 애초에 안 온다(pickDecomposeProposals 가 거른다)
|
|
350
350
|
* ⓒ 상한을 넘기면 «쪼개지 않는다» — 조각이 무한히 불어나는 것을 막는다
|
|
351
|
+
*
|
|
352
|
+
* ⭐ **조각은 원 골의 «대상 경로:» 머리줄을 물려받는다** (DECOMPOSE-PIECE-TARGET-LINE · 10-11 부검 run-76941fca):
|
|
353
|
+
* 조각 feature 가 골을 «통째로» 갈아 끼우면 첫 줄 선언이 사라져 Pod 예비 검사가 «대상 경로 0» →
|
|
354
|
+
* 재저작 NOT-GROUNDED 로 조각 전부가 exit 1 했다. 좁히는 규칙 = 원 선언 경로 중 조각 문장에
|
|
355
|
+
* 나오는 것만(없으면 원 선언 전부) · «신설:» 줄도 같은 규칙 · 첫 줄이 라벨이 아니면 지금 그대로.
|
|
351
356
|
*/
|
|
357
|
+
|
|
358
|
+
// ── 머리줄 선언을 읽고 좁히는 순수 조각 (위 규칙의 실물) ──────────────────────────
|
|
359
|
+
const TARGET_PATHS_LINE = /^대상 경로:[ \t]*(.*)$/;
|
|
360
|
+
const NEW_PATHS_LINE = /^신설:[ \t]*(.*)$/;
|
|
361
|
+
|
|
362
|
+
/** 선언 줄의 경로를 나눈다 — « · »·쉼표(`goal-new-paths` 와 같은 분리 규칙). 유효성은 걸러내지 않는다: 선언 그대로다. */
|
|
363
|
+
function splitDeclaredPaths(payload: string): string[] {
|
|
364
|
+
return payload.split(/[·,]/).map((token) => token.trim()).filter((token) => token.length > 0);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** 조각 문장에 «나오는» 선언 경로만 남긴다. 하나도 안 나오면 원 선언 전부 — 선언 밖 경로는 지어내지 않는다. */
|
|
368
|
+
function narrowDeclaredPaths(declared: readonly string[], pieceFeature: string): string[] {
|
|
369
|
+
const mentioned = declared.filter((path) => {
|
|
370
|
+
const escaped = path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
371
|
+
return new RegExp(`(^|[^\\w./-])${escaped}(?=$|[^\\w./-]|\\.(?:\\s|$))`).test(pieceFeature);
|
|
372
|
+
});
|
|
373
|
+
return mentioned.length ? mentioned : [...declared];
|
|
374
|
+
}
|
|
375
|
+
|
|
352
376
|
export function applyDecomposeProposals(
|
|
353
377
|
goals: readonly SelfDevGoal[],
|
|
354
378
|
results: readonly SelfDevJobResult[],
|
|
@@ -374,11 +398,29 @@ export function applyDecomposeProposals(
|
|
|
374
398
|
// ⛔ 상한을 넘기면 «쪼개지 않는다» — 조용히 자르지 않고 원본을 그대로 둔다.
|
|
375
399
|
if (out.length + pieces.length + (goals.length - out.length - 1) > maxGoals) { out.push(goal); continue; }
|
|
376
400
|
decomposed.push(goal.feature);
|
|
401
|
+
// 원 골의 머리줄 선언 — 첫 줄이 «대상 경로:» 가 아니면 조각은 지금 그대로(바이트 불변)다.
|
|
402
|
+
const goalLines = goal.feature.split('\n');
|
|
403
|
+
const targetLine = TARGET_PATHS_LINE.exec(goalLines[0] ?? '');
|
|
404
|
+
// «신설:» 상속은 머리줄 선언이 «있을 때만» — 첫 줄이 라벨이 아니면 조각은 바이트 불변이어야 한다.
|
|
405
|
+
const newLine = targetLine ? goalLines.map((line) => NEW_PATHS_LINE.exec(line)).find((m) => m !== null) : null;
|
|
377
406
|
for (const piece of pieces) {
|
|
407
|
+
const inherited: string[] = [];
|
|
408
|
+
let paths: string[] = [];
|
|
409
|
+
if (targetLine) {
|
|
410
|
+
const declared = splitDeclaredPaths(targetLine[1]!);
|
|
411
|
+
paths = declared.length ? narrowDeclaredPaths(declared, piece.feature) : declared;
|
|
412
|
+
// 선언이 비어 있으면 원 줄을 그대로 실는다 — 라벨만이라도 예비 검사가 읽게 한다.
|
|
413
|
+
inherited.push(declared.length ? `대상 경로: ${paths.join(' · ')}` : goalLines[0]!);
|
|
414
|
+
}
|
|
415
|
+
if (newLine) {
|
|
416
|
+
const declaredNew = splitDeclaredPaths(newLine[1]!);
|
|
417
|
+
const narrowedNew = declaredNew.length ? narrowDeclaredPaths(declaredNew, piece.feature) : declaredNew;
|
|
418
|
+
inherited.push(declaredNew.length ? `신설: ${narrowedNew.join(' · ')}` : newLine[0]!);
|
|
419
|
+
}
|
|
378
420
|
out.push({
|
|
379
421
|
...goal, // 승격 플래그(openPr·autoReview·autoMerge·base)를 물려받는다
|
|
380
422
|
id: piece.id,
|
|
381
|
-
feature: piece.feature,
|
|
423
|
+
feature: inherited.length ? [...inherited, piece.feature].join('\n') : piece.feature,
|
|
382
424
|
...(piece.dependsOn.length ? { dependsOn: [...piece.dependsOn] } : {}),
|
|
383
425
|
// ⛔⭐ `...goal` 이 «부모의» hotPaths 를 그대로 흘린다 — 그러면 조각 전부가 서로 겹쳐
|
|
384
426
|
// `orchestrate.ts` 가 전부를 직렬화한다(위상이 아니라 «한 줄»이 된다).
|
|
@@ -33,7 +33,7 @@ export interface DraftSweepAdapters {
|
|
|
33
33
|
/** Undefined means liveness could not be established. */
|
|
34
34
|
listLiveBranches(repository: string): Promise<ReadonlySet<string> | undefined>;
|
|
35
35
|
setLabels(repository: string, number: number, change: { add: string; remove: readonly string[] }): Promise<void>;
|
|
36
|
-
closeDraft(repository: string, number: number
|
|
36
|
+
closeDraft(repository: string, number: number): Promise<void>;
|
|
37
37
|
/** Resolve the latest claim comment owner; a failed lookup must not authorize a close. */
|
|
38
38
|
getClaimOwner?(repository: string, number: number): Promise<string | undefined>;
|
|
39
39
|
/** Current owner, accounting for a later claim release; only draft metrics need this distinction. */
|
|
@@ -478,6 +478,23 @@ const label = (action: 'mark-stalled' | 'close', name?: string): string => {
|
|
|
478
478
|
const runningLabel = label('mark-stalled');
|
|
479
479
|
const stalledLabel = label('close', 'stalled');
|
|
480
480
|
const supersededLabel = label('close', 'superseded');
|
|
481
|
+
|
|
482
|
+
/** Keep repository labels bounded; the complete reason and PR number live in the closed event. */
|
|
483
|
+
export function sweptReasonLabel(reason: string): string {
|
|
484
|
+
const kinds = ['superseded-by', 'duplicate-of-open', 'claim-expired', 'stale-unobserved',
|
|
485
|
+
'harvest-expired', 'stale-ended-run'] as const;
|
|
486
|
+
const kind = kinds.find((name) => reason === name || reason.startsWith(`${name} `)) ?? 'other';
|
|
487
|
+
return `elanous:swept:${kind}`;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
async function closeSweptDraft(adapters: Pick<DraftSweepAdapters, 'setLabels' | 'closeDraft'>,
|
|
491
|
+
repository: string, number: number, reason: string, onLabelApplied?: () => void): Promise<void> {
|
|
492
|
+
await adapters.setLabels(repository, number, { add: sweptReasonLabel(reason), remove: [] });
|
|
493
|
+
onLabelApplied?.();
|
|
494
|
+
await adapters.closeDraft(repository, number);
|
|
495
|
+
try { debug.log('self-dev.draft-sweep', 'closed', { pr: number, reason }); } catch { /* fail-soft */ }
|
|
496
|
+
}
|
|
497
|
+
|
|
481
498
|
const approvalLabel = PR_LABELS.find((entry) => entry.axis === 'state' && entry.sweep.action === 'none')?.name;
|
|
482
499
|
const keepLabel = PR_LABELS.find((entry) => entry.sweep.action === 'exclude')?.name;
|
|
483
500
|
const benchLabel = PR_LABELS.find((entry) => entry.name === 'elanous:bench')!.name;
|
|
@@ -572,8 +589,7 @@ export async function supersedeDraftsOnMerge(input: {
|
|
|
572
589
|
try {
|
|
573
590
|
// Label first: a failed label write must never strand a closed, unlabelled PR outside open-draft sweeps.
|
|
574
591
|
if (!draft.labels.includes(supersededLabel)) await adapters.setLabels(repository, draft.number, { add: supersededLabel, remove });
|
|
575
|
-
await adapters
|
|
576
|
-
`Draft sweep: superseded-by #${merged.number} (https://github.com/${repository}/pull/${merged.number}). Branch preserved.`);
|
|
592
|
+
await closeSweptDraft(adapters, repository, draft.number, decision.reason);
|
|
577
593
|
closed.push(draft.number);
|
|
578
594
|
} catch (failure) {
|
|
579
595
|
kept.push(draft.number);
|
|
@@ -975,8 +991,7 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
|
|
|
975
991
|
});
|
|
976
992
|
labelsChanged = true;
|
|
977
993
|
}
|
|
978
|
-
await adapters
|
|
979
|
-
`Draft sweep: ${reason} (https://github.com/${repository}/pull/${lineageNewer.number}). Branch preserved.`);
|
|
994
|
+
await closeSweptDraft(adapters, repository, draft.number, reason, () => { labelsChanged = true; });
|
|
980
995
|
entry.applied = true;
|
|
981
996
|
} catch (error) {
|
|
982
997
|
if (labelsChanged) entry.partialApplied = true;
|
|
@@ -1035,11 +1050,10 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
|
|
|
1035
1050
|
if (!apply || action !== 'close') continue;
|
|
1036
1051
|
let labelsChanged = false;
|
|
1037
1052
|
try {
|
|
1038
|
-
|
|
1053
|
+
await adapters.getClaimOwner?.(repository, draft.number);
|
|
1039
1054
|
await adapters.setLabels(repository, draft.number, { add: stalledLabel, remove: [runningLabel] });
|
|
1040
1055
|
labelsChanged = true;
|
|
1041
|
-
await adapters
|
|
1042
|
-
`Draft sweep: 처리 중 표식이 ${CLAIM_IDLE_HOURS}시간 갱신 없음 — ${runStatus === 'self-implement.result final' ? 'self-implement.result final · 남은 워크트리는 live 아님' : '런·워크트리 없음'} · 주인 ${owner}. Closed as stalled. Branch preserved; reopen to restore.`);
|
|
1056
|
+
await closeSweptDraft(adapters, repository, draft.number, entry.reason, () => { labelsChanged = true; });
|
|
1043
1057
|
entry.applied = true;
|
|
1044
1058
|
} catch (error) {
|
|
1045
1059
|
if (labelsChanged) entry.partialApplied = true;
|
|
@@ -1085,12 +1099,7 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
|
|
|
1085
1099
|
labelsChanged = true;
|
|
1086
1100
|
}
|
|
1087
1101
|
if (action === 'close') {
|
|
1088
|
-
await adapters
|
|
1089
|
-
? `Draft sweep: run unobserved — no run record reachable here, no host worktree for this branch, idle ≥${STALLED_DRAFT_HOURS}h. Closed as stalled. Branch preserved; reopen to restore.`
|
|
1090
|
-
: decision.reason === 'harvest-expired'
|
|
1091
|
-
? `Draft sweep: harvest-expired — 종단이 수확 대상으로 표시한 뒤 ${STALLED_DRAFT_HOURS}h 동안 수확 없음. Branch preserved; reopen to restore.`
|
|
1092
|
-
: `Draft sweep: ${decision.reason}${decision.reason.startsWith('superseded-by #')
|
|
1093
|
-
? ` (https://github.com/${repository}/pull/${decision.reason.slice('superseded-by #'.length).match(/^\d+/)![0]})` : ''}. Branch preserved.`);
|
|
1102
|
+
await closeSweptDraft(adapters, repository, draft.number, reason, () => { labelsChanged = true; });
|
|
1094
1103
|
}
|
|
1095
1104
|
entry.applied = labelsChanged || action === 'close';
|
|
1096
1105
|
} catch (error) {
|
|
@@ -17,7 +17,7 @@ export interface LiveRunOverlapPath {
|
|
|
17
17
|
|
|
18
18
|
/** 검사 대상 한 건이 막힌 이유. `name` 은 사람이 바로 열어 볼 수 있는 식별자다. */
|
|
19
19
|
export interface LaunchPreflightBlocker {
|
|
20
|
-
readonly kind: 'open-pr' | 'sibling-pr' | 'live-run' | 'no-target-paths' | 'recent-change' | 'ask-outside-path' | 'ask-marker';
|
|
20
|
+
readonly kind: 'open-pr' | 'sibling-pr' | 'live-run' | 'no-target-paths' | 'missing-traced-path' | 'goal-unreadable' | 'recent-change' | 'ask-outside-path' | 'ask-marker';
|
|
21
21
|
/** `#8100` · `run-6ce5765b…` 처럼 «바로 조회 가능한» 이름. */
|
|
22
22
|
readonly name: string;
|
|
23
23
|
/** 왜 막혔는지 한 줄 — 경로와 수를 포함한다. */
|
|
@@ -490,6 +490,10 @@ export function renderLaunchPreflight(
|
|
|
490
490
|
}
|
|
491
491
|
lines.push(`[preflight] ⛔ 막는 것 ${result.blockers.length}건${forced ? '' : ' — 발사하지 않는다'}`);
|
|
492
492
|
for (const blocker of result.blockers) lines.push(`[preflight] · ${blocker.name} — ${blocker.detail}`);
|
|
493
|
+
if (result.blockers.some((blocker) => blocker.kind === 'missing-traced-path' || blocker.kind === 'goal-unreadable')) {
|
|
494
|
+
lines.push('[preflight] 골 판독 실패 또는 traced path 오류는 --force-preflight 로 우회할 수 없다 — 골 문서와 경로를 바로잡아야 한다');
|
|
495
|
+
return lines.join('\n');
|
|
496
|
+
}
|
|
493
497
|
lines.push(forced
|
|
494
498
|
? '[preflight] ⚠️ --force-preflight — 위 막힘을 «뚫고» 발사한다 (이 우회는 관측에 남는다)'
|
|
495
499
|
: '[preflight] 그래도 가려면 --force-preflight (그 우회는 관측에 남는다) — elanous dev');
|
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
// ⛔ 그래도 관측·산출에는 «그대로» 남긴다 — 「지나갔다」와 「없었다」는 다른 값이다.
|
|
18
18
|
|
|
19
19
|
import { execFileSync } from 'node:child_process';
|
|
20
|
-
import { existsSync } from 'node:fs';
|
|
21
|
-
import { isAbsolute, resolve } from 'node:path';
|
|
20
|
+
import { closeSync, existsSync, lstatSync, openSync, readSync, statSync } from 'node:fs';
|
|
21
|
+
import { dirname, isAbsolute, resolve, sep } from 'node:path';
|
|
22
|
+
import { linesOutsideFencedCode } from '../self-implement/goal-author.js';
|
|
23
|
+
import { declaredNewFilePaths } from '../self-implement/goal-new-paths.js';
|
|
22
24
|
import { resolveLogTargets } from '../cli/logs-cli.js';
|
|
23
25
|
import { debug } from '../debug/log.js';
|
|
24
26
|
import { siblingTestPath, normalizePreexistingFailureObservedAt, normalizePreexistingFailureRecords, emitAskMarkerObservation, emittedAskMarkerObservations, setAskMarkerObserver } from './launch-preflight-text.js';
|
|
@@ -734,8 +736,9 @@ export interface AskPreflightOptions {
|
|
|
734
736
|
readonly openPrsLimit?: number;
|
|
735
737
|
/** 중단 런 원장 조회의 최대 반환 행 수. 상한 도달은 결과의 `truncated` 상태로 남는다. */
|
|
736
738
|
readonly interruptedRunsLimit?: number;
|
|
737
|
-
/** ⛔ 저작 «전» 예비 검사용 —
|
|
738
|
-
*
|
|
739
|
+
/** ⛔ 저작 «전» 예비 검사용 — 충돌 판정에는 이 ask 힌트 경로를 쓴다.
|
|
740
|
+
* 골 파일이 이미 있으면 문서의 TRACED PATHS 는 별도로 읽어 누락을 검사한다.
|
|
741
|
+
* ⚠️ 힌트는 «추정»이다. 정본은 저작된 골의 TRACED PATHS 이고 발사 직전에 다시 판정한다. */
|
|
739
742
|
readonly pathsOverride?: readonly string[];
|
|
740
743
|
/** 원 ask 경로와 최종 대상의 차이를 ask-outside-path 경고에 보인다. */
|
|
741
744
|
readonly askOutsidePathHints?: readonly string[];
|
|
@@ -770,6 +773,78 @@ export interface AskPreflightDecision {
|
|
|
770
773
|
readonly plannedBranchResolution?: PlannedBranchResolution;
|
|
771
774
|
}
|
|
772
775
|
|
|
776
|
+
/** OP #980: fragments kept by cleanup remain inspectable traced targets, including bare names. */
|
|
777
|
+
function isFileShapedTracedPath(path: string): boolean {
|
|
778
|
+
return path.trim().length > 0 && (!/\s/.test(path) || path.includes('/'));
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Keep lexical `..`/`.` segments: resolving them first hides ENOTDIR behind a file parent.
|
|
782
|
+
function tracedFsPath(path: string, root: string): string {
|
|
783
|
+
return isAbsolute(path) ? path : `${root}${root.endsWith(sep) ? '' : sep}${path}`;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function tracedPathExists(path: string, root: string): boolean {
|
|
787
|
+
try {
|
|
788
|
+
const target = tracedFsPath(path, root);
|
|
789
|
+
if (!statSync(target).isFile()) return false;
|
|
790
|
+
const fd = openSync(target, 'r');
|
|
791
|
+
try { readSync(fd, Buffer.alloc(1), 0, 1, 0); }
|
|
792
|
+
finally { closeSync(fd); }
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
catch { return false; }
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function tracedPathIsFile(path: string, root: string): boolean {
|
|
799
|
+
try { return statSync(tracedFsPath(path, root)).isFile(); }
|
|
800
|
+
catch { return false; }
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function tracedPathOccupation(path: string, root: string): 'occupied' | 'missing' | 'inaccessible' {
|
|
804
|
+
const target = tracedFsPath(path, root);
|
|
805
|
+
try { lstatSync(target); return 'occupied'; }
|
|
806
|
+
catch (error) {
|
|
807
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') return 'inaccessible';
|
|
808
|
+
for (let parent = dirname(target); parent !== dirname(parent); parent = dirname(parent)) {
|
|
809
|
+
try {
|
|
810
|
+
if (lstatSync(parent).isSymbolicLink()) {
|
|
811
|
+
try { statSync(parent); } catch { return 'inaccessible'; }
|
|
812
|
+
}
|
|
813
|
+
} catch (parentError) {
|
|
814
|
+
if ((parentError as NodeJS.ErrnoException).code !== 'ENOENT') return 'inaccessible';
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return 'missing';
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/** Remove only malformed TRACED PATHS fragments before the child reads the goal file again. */
|
|
822
|
+
export function discardNonFileTracedPathItems(document: string, _root: string): { document: string; dropped: string[] } {
|
|
823
|
+
const dropped: string[] = [];
|
|
824
|
+
let inTracedPaths = false;
|
|
825
|
+
const outsideStarts = new Set(linesOutsideFencedCode(document).map((line) => line.start));
|
|
826
|
+
let offset = 0;
|
|
827
|
+
const lines = document.split('\n').flatMap((line) => {
|
|
828
|
+
const lineStart = offset;
|
|
829
|
+
offset += line.length + 1;
|
|
830
|
+
if (!outsideStarts.has(lineStart)) return [line];
|
|
831
|
+
const body = line.replace(/\r$/, '');
|
|
832
|
+
if (/^\s*##\s+TRACED PATHS\s*$/.test(body)) { inTracedPaths = true; return [line]; }
|
|
833
|
+
if (inTracedPaths && /^\s*#{1,6}\s/.test(body)) inTracedPaths = false;
|
|
834
|
+
if (!inTracedPaths) return [line];
|
|
835
|
+
const item = /^(\s*(?:[-*+]|\d+[.)])\s+)(.*)$/.exec(body);
|
|
836
|
+
if (!item) return [line];
|
|
837
|
+
// OP #980: a fragment with whitespace and no slash is prose; all other forms stay verbatim.
|
|
838
|
+
const fragments = item[2]!.split(' · ');
|
|
839
|
+
const kept = fragments.filter((fragment) => !(/\s/.test(fragment.trim()) && !fragment.includes('/')));
|
|
840
|
+
if (kept.length === fragments.length) return [line];
|
|
841
|
+
if (kept.length === 0) { dropped.push(line); return []; }
|
|
842
|
+
dropped.push(line);
|
|
843
|
+
return [`${item[1]}${kept.join(' · ')}${line.endsWith('\r') ? '\r' : ''}`];
|
|
844
|
+
});
|
|
845
|
+
return { document: lines.join('\n'), dropped };
|
|
846
|
+
}
|
|
847
|
+
|
|
773
848
|
/** 인자 검증 — ⛔ 잘못된 값을 «조용히» 기본값으로 바꾸지 않는다(리뷰 should-fix). */
|
|
774
849
|
/** ⛔ 기본값은 «내부 상수»다 — 아무도 안 넘기는 인자를 계약 표면으로 두지 않는다(리뷰 must-fix). */
|
|
775
850
|
export const DEFAULT_LIVE_RUN_WINDOW_MINUTES = 30;
|
|
@@ -1001,20 +1076,48 @@ export function decideAskPreflight(
|
|
|
1001
1076
|
): AskPreflightDecision {
|
|
1002
1077
|
const limit = opts.openPrsLimit ?? 200;
|
|
1003
1078
|
const interruptedRunsLimit = resolveInterruptedRunsLimit(opts.interruptedRunsLimit);
|
|
1004
|
-
//
|
|
1005
|
-
// ⚠️ 골 문서를 읽지 «않는다» — 없는 파일을 읽으려 하면 여기서 던져 저작 자체가 막힌다.
|
|
1079
|
+
// pathsOverride is an independent pre-authoring inspection, even if a stale goal file exists.
|
|
1006
1080
|
let goalDocument: string | null = null;
|
|
1007
1081
|
let goalRulersUnknownReason: string | undefined;
|
|
1082
|
+
let goalReadFailed = false;
|
|
1083
|
+
const resolvedGoalFile = opts.declaredPathsRoot?.trim() && !isAbsolute(opts.goalFile)
|
|
1084
|
+
? resolve(opts.declaredPathsRoot, opts.goalFile)
|
|
1085
|
+
: opts.goalFile;
|
|
1008
1086
|
if (!opts.pathsOverride) {
|
|
1009
1087
|
try {
|
|
1010
|
-
goalDocument = deps.readGoalDocument(
|
|
1088
|
+
goalDocument = deps.readGoalDocument(resolvedGoalFile);
|
|
1011
1089
|
} catch (error) {
|
|
1090
|
+
goalReadFailed = true;
|
|
1012
1091
|
goalRulersUnknownReason = `골 문서 판독 불가: ${error instanceof Error ? error.message.split('\n')[0] : String(error)}`;
|
|
1013
1092
|
}
|
|
1014
1093
|
} else {
|
|
1015
1094
|
goalRulersUnknownReason = '저작 전 예비 검사에는 골 문서가 없다';
|
|
1016
1095
|
}
|
|
1017
|
-
|
|
1096
|
+
// Resolve against the caller's root, but keep injected virtual goal documents out of
|
|
1097
|
+
// filesystem checks when no authored file exists there.
|
|
1098
|
+
const hasAuthoredGoal = !opts.pathsOverride && opts.goalFile !== '' && goalDocument !== null
|
|
1099
|
+
&& existsSync(resolvedGoalFile);
|
|
1100
|
+
const authoredTracedPaths = goalDocument === null ? [] : deps.tracedPaths(goalDocument);
|
|
1101
|
+
const tracedPathsRaw = opts.pathsOverride ?? authoredTracedPaths;
|
|
1102
|
+
const inspectionRoot = goalDocument === null || !hasAuthoredGoal
|
|
1103
|
+
? undefined
|
|
1104
|
+
: opts.declaredPathsRoot?.trim() || process.cwd();
|
|
1105
|
+
const tracedPaths = inspectionRoot && !opts.pathsOverride
|
|
1106
|
+
? tracedPathsRaw.filter((path) => tracedPathExists(path, inspectionRoot) || isFileShapedTracedPath(path))
|
|
1107
|
+
: tracedPathsRaw;
|
|
1108
|
+
const declaredNewPaths = goalDocument === null ? new Set<string>() : new Set(declaredNewFilePaths(goalDocument));
|
|
1109
|
+
const invalidTracedPaths: Array<{ path: string; reason: 'occupied-new' | 'not-file' | 'missing' | 'inaccessible' }> = inspectionRoot
|
|
1110
|
+
? authoredTracedPaths.flatMap<{ path: string; reason: 'occupied-new' | 'not-file' | 'missing' | 'inaccessible' }>((path) => {
|
|
1111
|
+
const occupation = tracedPathOccupation(path, inspectionRoot);
|
|
1112
|
+
if (!isFileShapedTracedPath(path) && occupation !== 'occupied') return [];
|
|
1113
|
+
const isDeclaredNew = declaredNewPaths.has(path.replace(/^\.\//, ''));
|
|
1114
|
+
if (isDeclaredNew) return occupation === 'missing' ? [] : [{ path, reason: occupation === 'occupied' ? 'occupied-new' as const : 'inaccessible' as const }];
|
|
1115
|
+
if (tracedPathExists(path, inspectionRoot)) return [];
|
|
1116
|
+
return [{ path, reason: occupation === 'occupied'
|
|
1117
|
+
? (tracedPathIsFile(path, inspectionRoot) ? 'inaccessible' as const : 'not-file' as const)
|
|
1118
|
+
: occupation }];
|
|
1119
|
+
})
|
|
1120
|
+
: [];
|
|
1018
1121
|
let premiseMockedBySignal: number | null = null;
|
|
1019
1122
|
let invariantFunctionExits: number | null = null;
|
|
1020
1123
|
if (goalDocument !== null) {
|
|
@@ -1197,11 +1300,41 @@ export function decideAskPreflight(
|
|
|
1197
1300
|
...(interruptedRunsUnknownReason ? { interruptedRunsUnknownReason } : {}),
|
|
1198
1301
|
...(recentChangesUnknownReason ? { recentChangesUnknownReason } : {}),
|
|
1199
1302
|
});
|
|
1200
|
-
const
|
|
1303
|
+
const missingTracedPathBlockers: LaunchPreflightBlocker[] = [
|
|
1304
|
+
...(hasAuthoredGoal && authoredTracedPaths.length === 0 ? [{
|
|
1305
|
+
kind: 'missing-traced-path' as const,
|
|
1306
|
+
name: '최종 골의 TRACED PATHS 경로 0',
|
|
1307
|
+
detail: '정리 후 보존된 traced 경로가 없다 — 실제 파일 또는 유효한 신설: 파일 경로를 골에 선언해야 발사할 수 있다',
|
|
1308
|
+
}] : []),
|
|
1309
|
+
...invalidTracedPaths.map(({ path, reason }) => ({
|
|
1310
|
+
kind: 'missing-traced-path' as const,
|
|
1311
|
+
name: reason === 'occupied-new' ? `신설 traced path 가 이미 점유됨: ${path}`
|
|
1312
|
+
: reason === 'not-file' ? `traced path 가 파일이 아님: ${path}`
|
|
1313
|
+
: reason === 'inaccessible' ? `traced path 경로 접근 불가: ${path}` : `실재하지 않는 traced path: ${path}`,
|
|
1314
|
+
detail: reason === 'occupied-new'
|
|
1315
|
+
? `골의 신설: 경로 ${path} 가 이미 파일·디렉터리·링크로 점유돼 있다 — 새 파일 경로를 바로잡아야 한다`
|
|
1316
|
+
: reason === 'not-file'
|
|
1317
|
+
? `골의 TRACED PATHS 경로 ${path} 가 존재하지만 읽을 수 있는 파일이 아니다 — 파일 경로를 바로잡아야 한다`
|
|
1318
|
+
: reason === 'inaccessible'
|
|
1319
|
+
? `골의 TRACED PATHS 경로 ${path} 를 검사할 수 없다 — 부모가 파일이거나 접근 권한이 없다. 경로를 바로잡아야 한다`
|
|
1320
|
+
: `골의 TRACED PATHS 에 선언됐지만 ${inspectionRoot} 에 없고 유효한 신설: 목록에도 없다 — 발사 전에 골 경로를 바로잡아야 한다. 신설은 파일 경로로(확장자 포함) 선언한다`,
|
|
1321
|
+
})),
|
|
1322
|
+
];
|
|
1323
|
+
const goalReadBlockers: LaunchPreflightBlocker[] = goalReadFailed ? [{
|
|
1324
|
+
kind: 'goal-unreadable',
|
|
1325
|
+
name: `골 문서 판독 불가: ${opts.goalFile}`,
|
|
1326
|
+
detail: `${goalRulersUnknownReason} — 최종 골의 traced path 를 확인할 수 없어 발사를 멈춘다`,
|
|
1327
|
+
}] : [];
|
|
1328
|
+
const decisionResult = {
|
|
1329
|
+
...result,
|
|
1330
|
+
blockers: [...result.blockers, ...missingTracedPathBlockers, ...goalReadBlockers],
|
|
1331
|
+
warnings: [...result.warnings, ...askMarkerWarnings],
|
|
1332
|
+
runningRunsConfidenceAppendix: runningRunsConfidence.appendix,
|
|
1333
|
+
};
|
|
1201
1334
|
emittedAskMarkerObservations.add(decisionResult);
|
|
1202
1335
|
return {
|
|
1203
1336
|
result: decisionResult,
|
|
1204
|
-
shouldLaunch: result.blockers.length === 0 || force,
|
|
1337
|
+
shouldLaunch: missingTracedPathBlockers.length === 0 && goalReadBlockers.length === 0 && (result.blockers.length === 0 || force),
|
|
1205
1338
|
plannedBranchResolution,
|
|
1206
1339
|
};
|
|
1207
1340
|
}
|
|
@@ -23,6 +23,7 @@ import { parentUnlandedProgressLine } from './run-supervisor.js';
|
|
|
23
23
|
import { mkdirSync, openSync, writeFileSync, closeSync, renameSync, unlinkSync } from 'node:fs';
|
|
24
24
|
import { join, resolve } from 'node:path';
|
|
25
25
|
import { goalPathFromArgv, launchRegistryDir, removeLaunchBinding, writeLaunchBinding } from '../harness/launch-registry.js';
|
|
26
|
+
import { psProcessStartMs } from '../harness/harness-stop.js';
|
|
26
27
|
import { adoptRunCard } from '../task-agent/run-adopt.js';
|
|
27
28
|
import { availableParallelism, cpus } from 'node:os';
|
|
28
29
|
import { selfDevRunsDir } from './run-store.js';
|
|
@@ -912,12 +913,15 @@ async function deleteLabelledJobs(runId: string, selectedTargets: readonly { con
|
|
|
912
913
|
* take minutes before that, and a stop in that window found «no process» (10-01 · three runs). */
|
|
913
914
|
export function writeRunPidRecord(runId: string, runsDir: string = selfDevRunsDir(), registryDir: string | null = launchRegistryDir()): string {
|
|
914
915
|
const pidPath = join(runsDir, runId, 'pid.json');
|
|
916
|
+
// The observer reads `ps -o lstart=`, whose precision is one second. Record that same
|
|
917
|
+
// process birth value when available so a launch near a second boundary still matches.
|
|
918
|
+
const startedAt = psProcessStartMs(process.pid) ?? Date.now() - Math.floor(process.uptime() * 1000);
|
|
915
919
|
mkdirSync(join(runsDir, runId), { recursive: true });
|
|
916
920
|
const temporary = `${pidPath}.${process.pid}.${randomUUID()}.tmp`;
|
|
917
921
|
const fd = openSync(temporary, 'wx', 0o600);
|
|
918
922
|
try {
|
|
919
923
|
try {
|
|
920
|
-
writeFileSync(fd, JSON.stringify({ pid: process.pid, startedAt
|
|
924
|
+
writeFileSync(fd, JSON.stringify({ pid: process.pid, startedAt, argv0: process.argv0 }));
|
|
921
925
|
} finally { closeSync(fd); }
|
|
922
926
|
renameSync(temporary, pidPath);
|
|
923
927
|
} catch (error) { unlinkSync(temporary); throw error; }
|
|
@@ -925,7 +929,7 @@ export function writeRunPidRecord(runId: string, runsDir: string = selfDevRunsDi
|
|
|
925
929
|
if (registryDir !== null) {
|
|
926
930
|
const cwd = process.cwd();
|
|
927
931
|
const goalPath = goalPathFromArgv(process.argv, cwd);
|
|
928
|
-
writeLaunchBinding(runId, { pid: process.pid, startedAt
|
|
932
|
+
writeLaunchBinding(runId, { pid: process.pid, startedAt, argv0: process.argv0,
|
|
929
933
|
cwd, ...(goalPath ? { goalPath } : {}), runsDir: resolve(runsDir),
|
|
930
934
|
...(process.env.ELANOUS_POD_NAME ? { podName: process.env.ELANOUS_POD_NAME } : {}),
|
|
931
935
|
...(process.env.ELANOUS_TASK_CARD_ID ? { taskCardId: process.env.ELANOUS_TASK_CARD_ID } : {}) }, registryDir);
|
|
@@ -9,7 +9,8 @@ import { recordSelfDevRunSupervisorStop, selfDevRunsDir } from './run-store.js';
|
|
|
9
9
|
import { debug } from '../debug/log.js';
|
|
10
10
|
import { sweepFrozenMerges } from '../self-implement/frozen-merges.js';
|
|
11
11
|
import { routeSupervisorVerdict } from './supervisor-verdict-edges.js';
|
|
12
|
-
import { recordTaskAgentShadowMove, type TaskAgentShadowInput } from '../task-agent/shadow.js';
|
|
12
|
+
import { recordTaskAgentShadowMove, runCardFor, type TaskAgentShadowInput } from '../task-agent/shadow.js';
|
|
13
|
+
import { cardPrNumber, taskAgentStatePath } from '../task-agent/task-hand.js';
|
|
13
14
|
|
|
14
15
|
export interface SupervisorRound {
|
|
15
16
|
round: number;
|
|
@@ -641,6 +642,12 @@ export interface SuperviseRunOptions {
|
|
|
641
642
|
* `false` 면 끈다. 던져도 런의 결말은 바뀌지 않는다(fail-soft).
|
|
642
643
|
*/
|
|
643
644
|
taskAgentShadow?: ((input: TaskAgentShadowInput) => unknown) | false;
|
|
645
|
+
/** TA-LAND-AFTER-REVIEW — false disables the hook; function replaces only the final rejudge for isolated tests. */
|
|
646
|
+
taskAgentRejudge?: ((cardId: string, statePath: string) => Promise<unknown>) | false;
|
|
647
|
+
/** Test-only external I/O for the real completion rejudge. The decision and shadow gate remain production code. */
|
|
648
|
+
taskAgentRejudgeExternal?: Parameters<typeof import('../task-agent/rejudge-on-head.js').rejudgeAsyncResultCard>[2];
|
|
649
|
+
/** Isolated state ledger for the terminal rejudge test; production uses the active instance. */
|
|
650
|
+
taskAgentStatePath?: string;
|
|
644
651
|
observeDeliverables?: () => Promise<{
|
|
645
652
|
readonly deployFindings: ReadonlyMap<string, { target: string; findings?: readonly DeployVerifyFinding[] | readonly unknown[] }>;
|
|
646
653
|
readonly unmeasured: readonly unknown[];
|
|
@@ -658,6 +665,23 @@ function resolveSupervisorStopStorage(runStore: SuperviseRunOptions['runStore'])
|
|
|
658
665
|
return { path: selfDevRunsDir(), source: 'process-default' };
|
|
659
666
|
}
|
|
660
667
|
|
|
668
|
+
/** The supervisor's terminal hook, limited to a uniquely run-bound TA land card. */
|
|
669
|
+
export async function rejudgeTerminalTaskCard(runId: string | null, deps: {
|
|
670
|
+
statePath?: string;
|
|
671
|
+
env?: NodeJS.ProcessEnv;
|
|
672
|
+
rejudgeCard?: (cardId: string, statePath: string) => Promise<unknown>;
|
|
673
|
+
external?: Parameters<typeof import('../task-agent/rejudge-on-head.js').rejudgeAsyncResultCard>[2];
|
|
674
|
+
} = {}): Promise<void> {
|
|
675
|
+
const statePath = deps.statePath ?? taskAgentStatePath();
|
|
676
|
+
const card = runCardFor(runId, { statePath, ...(deps.env ? { env: deps.env } : {}) });
|
|
677
|
+
if (!card || card.status !== 'launched' || card.taLand !== true || cardPrNumber(card) === undefined) return;
|
|
678
|
+
const { rejudgeAsyncResultCard } = await import('../task-agent/rejudge-on-head.js');
|
|
679
|
+
const result = await (deps.rejudgeCard ?? ((id, path) => rejudgeAsyncResultCard(id, path, deps.external)))(card.id, statePath);
|
|
680
|
+
const { requireSuccessfulRejudge } = await import('../task-agent/rejudge-on-head.js');
|
|
681
|
+
requireSuccessfulRejudge(result);
|
|
682
|
+
try { debug.log('task-agent', 'terminal-rejudge', { runId, card: card.id }); } catch { /* fail-soft */ }
|
|
683
|
+
}
|
|
684
|
+
|
|
661
685
|
export async function superviseRun(opts: SuperviseRunOptions): Promise<SupervisorJobResult[]> {
|
|
662
686
|
let results = [...opts.initial];
|
|
663
687
|
let history: SupervisorRound[] = [];
|
|
@@ -808,6 +832,16 @@ export async function superviseRun(opts: SuperviseRunOptions): Promise<Superviso
|
|
|
808
832
|
} catch (error) {
|
|
809
833
|
try { debug.log('task-agent', 'shadow-move-failed', { runId, stopReason: decision.stopReason, error: String(error) }); } catch { /* fail-open */ }
|
|
810
834
|
}
|
|
835
|
+
if (opts.taskAgentRejudge !== false) {
|
|
836
|
+
const rejudgeOptions = { ...(opts.taskAgentStatePath ? { statePath: opts.taskAgentStatePath } : {}), ...(opts.taskAgentRejudge ? { rejudgeCard: opts.taskAgentRejudge } : {}), ...(opts.taskAgentRejudgeExternal ? { external: opts.taskAgentRejudgeExternal } : {}) };
|
|
837
|
+
try { await rejudgeTerminalTaskCard(runId, rejudgeOptions); }
|
|
838
|
+
catch (error) {
|
|
839
|
+
try { await rejudgeTerminalTaskCard(runId, rejudgeOptions); }
|
|
840
|
+
catch (retryError) {
|
|
841
|
+
try { debug.log('task-agent', 'terminal-rejudge-failed', { runId, stopReason: decision.stopReason, error: String(retryError), firstError: String(error), attempts: 2 }); } catch { /* fail-open */ }
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
811
845
|
}
|
|
812
846
|
return results;
|
|
813
847
|
}
|
|
@@ -426,6 +426,7 @@ function changedTestCountNote(
|
|
|
426
426
|
|
|
427
427
|
const IMPORTER_GREP_PATHS = ['*.test.ts', '*.test.tsx', '*.test.mts', '*.test.mtsx', '*.test.cts', '*.test.ctsx', '*.test.js', '*.test.jsx', '*.test.mjs', '*.test.mjsx', '*.test.cjs', '*.test.cjsx'];
|
|
428
428
|
const SOURCE_PATH_RE = /^src\/.+\.(?:[cm]?[jt]sx?)$/;
|
|
429
|
+
const SCRIPT_SOURCE_PATH_RE = /^scripts\/.+\.(?:[cm]?[jt]sx?)$/;
|
|
429
430
|
const SOURCE_FILE_RE = /\.(?:[cm]?[jt]sx?)$/;
|
|
430
431
|
|
|
431
432
|
function buildGateImporterTestIndex(
|
|
@@ -433,7 +434,7 @@ function buildGateImporterTestIndex(
|
|
|
433
434
|
cwd: string,
|
|
434
435
|
runCommand: SelfGateCliDeps['runCommand'],
|
|
435
436
|
): ImporterTestIndex | null | { readonly lookupFailed: string } {
|
|
436
|
-
const sources = changed.filter((path) => SOURCE_PATH_RE.test(path) && !isTestPath(path));
|
|
437
|
+
const sources = changed.filter((path) => (SOURCE_PATH_RE.test(path) || SCRIPT_SOURCE_PATH_RE.test(path)) && !isTestPath(path));
|
|
437
438
|
if (sources.length === 0) return null;
|
|
438
439
|
const result = (runCommand ?? defaultRunCommand)('git', ['grep', '-l', '-e', '', '--', ...IMPORTER_GREP_PATHS], cwd);
|
|
439
440
|
if ((result.status !== 0 && result.status !== 1) || result.error) {
|
|
@@ -596,7 +597,10 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
|
|
|
596
597
|
if (options.shards !== undefined && (!Number.isSafeInteger(options.shards) || options.shards < 1)) throw new Error('invalid --shards count');
|
|
597
598
|
const selection = (deps.changedFiles ?? ((dir, input) => defaultChangedFiles(dir, input, deps.runCommand)))(cwd, options);
|
|
598
599
|
const exists = deps.exists ?? ((path) => existsSync(`${cwd}/${path}`));
|
|
599
|
-
|
|
600
|
+
// Caller: src/release-loop/light-rc.ts runLightRc → runSelfGateCli(dir, { ..., includeImporterTests: true }).
|
|
601
|
+
// scripts/ joins the existing importer index only for that opt-in path.
|
|
602
|
+
const importerIndex = buildGateImporterTestIndex(options.includeImporterTests
|
|
603
|
+
? selection.files : selection.files.filter((path) => !SCRIPT_SOURCE_PATH_RE.test(path)), cwd, deps.runCommand);
|
|
600
604
|
const lookupFailed = importerIndex && 'lookupFailed' in importerIndex ? importerIndex.lookupFailed : undefined;
|
|
601
605
|
const availableIndex = importerIndex && !('lookupFailed' in importerIndex) ? importerIndex : undefined;
|
|
602
606
|
const routeIndex = availableIndex?.testPaths
|
|
@@ -51,6 +51,9 @@ import { hasGoalStepCodeName } from './goal-step-code-name.js';
|
|
|
51
51
|
import { targetScopedGoalText } from './goal-text-path-scope.js';
|
|
52
52
|
import { parseReleaseNoteSection } from '../release-loop/release-note.js';
|
|
53
53
|
import { declaredNewFilePaths } from './goal-new-paths.js';
|
|
54
|
+
import { classifyGoalCommandExecution, type GoalCommandExecutionResult } from './goal-command-execution.js';
|
|
55
|
+
export { classifyGoalCommandExecution } from './goal-command-execution.js';
|
|
56
|
+
export type { GoalCommandExecutionKind, GoalCommandExecutionResult, GoalCommandExecutionClassification } from './goal-command-execution.js';
|
|
54
57
|
|
|
55
58
|
// ⭐ `situation`/`complication` 은 **선택**이다 — 안 오면 `scqaNarrative` 가 종전 문면을 쓴다.
|
|
56
59
|
// ⛔ 필수로 만들지 않는다: 그러면 인핸싱이 폴백으로 떨어진 저작(LLM 실패·`disabled`)이 전부 죽는다.
|
|
@@ -439,33 +442,6 @@ export function canonicalStructureFinding(
|
|
|
439
442
|
return finding;
|
|
440
443
|
}
|
|
441
444
|
|
|
442
|
-
export type GoalCommandExecutionKind = 'success' | 'missing-command' | 'argument-problem' | 'execution-failure';
|
|
443
|
-
|
|
444
|
-
export interface GoalCommandExecutionResult {
|
|
445
|
-
readonly status: number | null;
|
|
446
|
-
readonly stderr: string;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
export interface GoalCommandExecutionClassification {
|
|
450
|
-
readonly kind: GoalCommandExecutionKind;
|
|
451
|
-
readonly log: string;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/** Classifies an already-observed Commander result without executing a command. */
|
|
455
|
-
export function classifyGoalCommandExecution(result: GoalCommandExecutionResult): GoalCommandExecutionClassification {
|
|
456
|
-
// A clean exit is authoritative: stderr text from a successful command is not a failure signal.
|
|
457
|
-
if (result.status === 0) return { kind: 'success', log: 'command completed successfully' };
|
|
458
|
-
|
|
459
|
-
const stderr = result.stderr;
|
|
460
|
-
if (stderr.includes('unknown command') || stderr.includes('unknown option')) {
|
|
461
|
-
return { kind: 'missing-command', log: 'command is not recognized; this may be not yet implemented rather than a defect' };
|
|
462
|
-
}
|
|
463
|
-
if (stderr.includes('missing required argument') || stderr.includes('required option')) {
|
|
464
|
-
return { kind: 'argument-problem', log: 'command requires a missing argument or option' };
|
|
465
|
-
}
|
|
466
|
-
return { kind: 'execution-failure', log: `command execution failed with code ${String(result.status)}` };
|
|
467
|
-
}
|
|
468
|
-
|
|
469
445
|
/** Optional repository-bounded source reader. Legacy null means the referenced path is missing. */
|
|
470
446
|
export interface GoalFileLintDeps {
|
|
471
447
|
readReferencedFile?: (repositoryRelativePath: string) => string | null | ReferencedFileReadResult;
|
|
@@ -1725,7 +1701,8 @@ export function lintGoalFile(document: string, branch: string, deps: GoalFileLin
|
|
|
1725
1701
|
}
|
|
1726
1702
|
if (deps.readReferencedFile) {
|
|
1727
1703
|
// AUTHOR-NEW-FILE-PATHS — 문서가 «새로 만들 파일»로 선언한 경로. 실행 쪽 범위 검사와 같은 줄을 읽는다.
|
|
1728
|
-
|
|
1704
|
+
// 저작과 실행은 같은 Original ask 범위만 선언으로 인정한다. 문서의 다른 절은 근거일 뿐이다.
|
|
1705
|
+
const declaredNew = new Set(declaredNewFilePaths(extractVerbatimOriginalAsk(document)?.ask ?? ''));
|
|
1729
1706
|
for (const reference of tracedPathReferences(document)) {
|
|
1730
1707
|
const result = deps.readReferencedFile(reference.path);
|
|
1731
1708
|
const normalized = typeof result === 'string' ? { kind: 'ok' as const, contents: result }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type GoalCommandExecutionKind = 'success' | 'missing-command' | 'argument-problem' | 'execution-failure';
|
|
2
|
+
|
|
3
|
+
export interface GoalCommandExecutionResult {
|
|
4
|
+
readonly status: number | null;
|
|
5
|
+
readonly stderr: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface GoalCommandExecutionClassification {
|
|
9
|
+
readonly kind: GoalCommandExecutionKind;
|
|
10
|
+
readonly log: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Classifies an already-observed Commander result without executing a command. */
|
|
14
|
+
export function classifyGoalCommandExecution(result: GoalCommandExecutionResult): GoalCommandExecutionClassification {
|
|
15
|
+
// A clean exit is authoritative: stderr text from a successful command is not a failure signal.
|
|
16
|
+
if (result.status === 0) return { kind: 'success', log: 'command completed successfully' };
|
|
17
|
+
|
|
18
|
+
const stderr = result.stderr;
|
|
19
|
+
if (stderr.includes('unknown command') || stderr.includes('unknown option')) {
|
|
20
|
+
return { kind: 'missing-command', log: 'command is not recognized; this may be not yet implemented rather than a defect' };
|
|
21
|
+
}
|
|
22
|
+
if (stderr.includes('missing required argument') || stderr.includes('required option')) {
|
|
23
|
+
return { kind: 'argument-problem', log: 'command requires a missing argument or option' };
|
|
24
|
+
}
|
|
25
|
+
return { kind: 'execution-failure', log: `command execution failed with code ${String(result.status)}` };
|
|
26
|
+
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ⭐ AUTHOR-NEW-FILE-PATHS — 저작 골 문서가 «새로 만들 파일»로 선언한 경로.
|
|
3
3
|
*
|
|
4
|
-
* 저작(TRACED PATHS 존재 검사)과 실행(orchestrator 범위 검사)이 **같은
|
|
5
|
-
* 저작 통과·실행 보류가 다시 갈리지 않는다(TC 13:50).
|
|
6
|
-
* src/self-implement/orchestrator.ts 의 DECLARED_NEW_FILES_LINE 와 같은 꼴을 쓴다.
|
|
4
|
+
* 저작(TRACED PATHS 존재 검사)과 실행(orchestrator 범위 검사)이 **같은 함수**로
|
|
5
|
+
* 신설 경로를 읽어야 저작 통과·실행 보류가 다시 갈리지 않는다(TC 13:50).
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
// SCOPE-NEW-FILES-LINE — 칸 문면은 새로 만들 파일을 «신설:» 줄로 따로 적는다(OP D칸 규칙).
|
|
10
|
-
//
|
|
9
|
+
// 저작과 실행이 이 함수를 공유한다(한쪽만 읽으면 저작과 실행이 갈린다).
|
|
11
10
|
export const DECLARED_NEW_FILES_LINE = /^\s*(?:신설|new\s*files?)\s*[::]\s*(.*)$/i;
|
|
12
11
|
|
|
13
12
|
/** 목록 기호 «- »·굵게 «**» 는 장식이다 — 그것만 벗기고 같은 정규식으로 문는다. */
|
|
@@ -32,7 +31,7 @@ export function declaredNewFilePaths(document: string): string[] {
|
|
|
32
31
|
const match = DECLARED_NEW_FILES_LINE.exec(bareDeclarationLine(rawLine));
|
|
33
32
|
if (!match) continue;
|
|
34
33
|
for (const fragment of match[1]!.split(/[·,]/)) {
|
|
35
|
-
//
|
|
34
|
+
// 저작·실행 공통 정규화 — 등 코드, ./ 접두.
|
|
36
35
|
const piece = fragment.trim().replace(/^`+|`+$/g, '').replace(/\\/g, '/').replace(/^\.\//, '');
|
|
37
36
|
if (!isRepositoryFileCandidate(piece)) continue;
|
|
38
37
|
paths.push(piece);
|