muonroi-cli 1.8.4 → 1.8.5
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/LICENSE +17 -5
- package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
- package/dist/packages/agent-harness-core/src/driver.js +46 -0
- package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
- package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
- package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
- package/dist/packages/agent-harness-core/src/protocol.js +15 -0
- package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
- package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
- package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
- package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
- package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
- package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
- package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
- package/dist/packages/agent-harness-opentui/src/install.js +10 -0
- package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
- package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
- package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
- package/dist/src/agent-harness/mock-model.d.ts +28 -0
- package/dist/src/agent-harness/mock-model.js +63 -1
- package/dist/src/agent-harness/test-spawn.js +31 -0
- package/dist/src/cli/config/screen-providers.js +1 -1
- package/dist/src/cli/cost-forensics.d.ts +10 -0
- package/dist/src/cli/cost-forensics.js +18 -3
- package/dist/src/cli/keys-bundle.d.ts +1 -1
- package/dist/src/cli/keys-bundle.js +1 -1
- package/dist/src/cli/keys.d.ts +2 -2
- package/dist/src/cli/keys.js +19 -81
- package/dist/src/council/clarifier.d.ts +28 -2
- package/dist/src/council/clarifier.js +81 -15
- package/dist/src/council/context.js +49 -15
- package/dist/src/council/debate-checkpoint.d.ts +129 -0
- package/dist/src/council/debate-checkpoint.js +176 -0
- package/dist/src/council/debate-planner.js +51 -3
- package/dist/src/council/debate-summary.d.ts +25 -0
- package/dist/src/council/debate-summary.js +85 -0
- package/dist/src/council/debate.d.ts +169 -2
- package/dist/src/council/debate.js +1210 -134
- package/dist/src/council/index.d.ts +85 -1
- package/dist/src/council/index.js +634 -196
- package/dist/src/council/leader.d.ts +26 -0
- package/dist/src/council/leader.js +150 -9
- package/dist/src/council/llm.d.ts +32 -0
- package/dist/src/council/llm.js +231 -38
- package/dist/src/council/panel-select.d.ts +30 -0
- package/dist/src/council/panel-select.js +72 -0
- package/dist/src/council/planner.js +23 -0
- package/dist/src/council/preflight.d.ts +7 -0
- package/dist/src/council/preflight.js +14 -2
- package/dist/src/council/prompts.d.ts +30 -3
- package/dist/src/council/prompts.js +234 -64
- package/dist/src/council/stance-recall.d.ts +42 -0
- package/dist/src/council/stance-recall.js +57 -0
- package/dist/src/council/strip-think.d.ts +17 -0
- package/dist/src/council/strip-think.js +33 -0
- package/dist/src/council/types.d.ts +128 -0
- package/dist/src/ee/artifact-cache.d.ts +16 -0
- package/dist/src/ee/artifact-cache.js +32 -0
- package/dist/src/ee/auth.d.ts +1 -0
- package/dist/src/ee/auth.js +15 -2
- package/dist/src/ee/bridge.d.ts +10 -0
- package/dist/src/ee/bridge.js +58 -0
- package/dist/src/ee/client.js +81 -18
- package/dist/src/ee/export-transcripts.d.ts +1 -0
- package/dist/src/ee/export-transcripts.js +8 -10
- package/dist/src/ee/extract-session.js +29 -0
- package/dist/src/ee/extract-style.d.ts +58 -0
- package/dist/src/ee/extract-style.js +270 -0
- package/dist/src/ee/recall-ledger.d.ts +9 -0
- package/dist/src/ee/recall-ledger.js +3 -0
- package/dist/src/ee/scope.d.ts +1 -0
- package/dist/src/ee/scope.js +26 -1
- package/dist/src/ee/search.d.ts +7 -0
- package/dist/src/ee/search.js +24 -0
- package/dist/src/ee/transcript-emit.js +2 -0
- package/dist/src/ee/types.d.ts +22 -0
- package/dist/src/ee/who-am-i-brain.d.ts +35 -0
- package/dist/src/ee/who-am-i-brain.js +220 -0
- package/dist/src/ee/who-am-i.d.ts +10 -3
- package/dist/src/ee/who-am-i.js +12 -0
- package/dist/src/ee/workflow-event.d.ts +48 -0
- package/dist/src/ee/workflow-event.js +81 -0
- package/dist/src/flow/compaction/compress.d.ts +3 -3
- package/dist/src/flow/compaction/compress.js +45 -8
- package/dist/src/flow/compaction/extract.d.ts +4 -7
- package/dist/src/flow/compaction/extract.js +50 -10
- package/dist/src/flow/compaction/index.d.ts +13 -1
- package/dist/src/flow/compaction/index.js +70 -3
- package/dist/src/flow/compaction/input-guard.d.ts +24 -0
- package/dist/src/flow/compaction/input-guard.js +43 -0
- package/dist/src/flow/fold-planning.d.ts +36 -0
- package/dist/src/flow/fold-planning.js +83 -0
- package/dist/src/flow/hierarchy.d.ts +146 -0
- package/dist/src/flow/hierarchy.js +427 -0
- package/dist/src/flow/index.d.ts +1 -0
- package/dist/src/flow/index.js +2 -0
- package/dist/src/flow/run-artifacts.d.ts +102 -0
- package/dist/src/flow/run-artifacts.js +208 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/gsd/assessment-schema.d.ts +44 -0
- package/dist/src/gsd/assessment-schema.js +134 -0
- package/dist/src/gsd/capability-registry.d.ts +45 -0
- package/dist/src/gsd/capability-registry.js +337 -0
- package/dist/src/gsd/complexity-assessor.d.ts +39 -0
- package/dist/src/gsd/complexity-assessor.js +152 -0
- package/dist/src/gsd/config-bridge.d.ts +7 -0
- package/dist/src/gsd/config-bridge.js +114 -0
- package/dist/src/gsd/config-loader.d.ts +27 -0
- package/dist/src/gsd/config-loader.js +50 -0
- package/dist/src/gsd/council-context.d.ts +44 -0
- package/dist/src/gsd/council-context.js +114 -0
- package/dist/src/gsd/ee-closure.d.ts +28 -0
- package/dist/src/gsd/ee-closure.js +49 -0
- package/dist/src/gsd/flags.d.ts +55 -0
- package/dist/src/gsd/flags.js +83 -0
- package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
- package/dist/src/gsd/gsd-dispatch.js +131 -0
- package/dist/src/gsd/gsd-runtime.d.ts +22 -0
- package/dist/src/gsd/gsd-runtime.js +37 -0
- package/dist/src/gsd/host-adapter.d.ts +11 -0
- package/dist/src/gsd/host-adapter.js +29 -0
- package/dist/src/gsd/index.d.ts +24 -1
- package/dist/src/gsd/index.js +27 -0
- package/dist/src/gsd/loop-host-contract.d.ts +21 -0
- package/dist/src/gsd/loop-host-contract.js +39 -0
- package/dist/src/gsd/loop-host.d.ts +69 -0
- package/dist/src/gsd/loop-host.js +245 -0
- package/dist/src/gsd/loop-resolver.d.ts +36 -0
- package/dist/src/gsd/loop-resolver.js +79 -0
- package/dist/src/gsd/model-tier.d.ts +13 -0
- package/dist/src/gsd/model-tier.js +45 -0
- package/dist/src/gsd/mutation-gate.d.ts +16 -0
- package/dist/src/gsd/mutation-gate.js +41 -0
- package/dist/src/gsd/native-roadmap.d.ts +89 -0
- package/dist/src/gsd/native-roadmap.js +343 -0
- package/dist/src/gsd/native-state.d.ts +47 -0
- package/dist/src/gsd/native-state.js +220 -0
- package/dist/src/gsd/paths.d.ts +23 -0
- package/dist/src/gsd/paths.js +66 -0
- package/dist/src/gsd/phase-dag.d.ts +12 -0
- package/dist/src/gsd/phase-dag.js +94 -0
- package/dist/src/gsd/phase-sync.d.ts +42 -0
- package/dist/src/gsd/phase-sync.js +321 -0
- package/dist/src/gsd/pil-gate-context.d.ts +13 -0
- package/dist/src/gsd/pil-gate-context.js +64 -0
- package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
- package/dist/src/gsd/pil-gate-critic.js +74 -0
- package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
- package/dist/src/gsd/plan-council-prompts.js +79 -0
- package/dist/src/gsd/plan-council.d.ts +44 -0
- package/dist/src/gsd/plan-council.js +251 -0
- package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
- package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
- package/dist/src/gsd/product-workspace.d.ts +13 -0
- package/dist/src/gsd/product-workspace.js +124 -0
- package/dist/src/gsd/ship-bridge.d.ts +25 -0
- package/dist/src/gsd/ship-bridge.js +65 -0
- package/dist/src/gsd/state-document.d.ts +40 -0
- package/dist/src/gsd/state-document.js +163 -0
- package/dist/src/gsd/verdict-schema.d.ts +39 -0
- package/dist/src/gsd/verdict-schema.js +144 -0
- package/dist/src/gsd/verify-context.d.ts +22 -0
- package/dist/src/gsd/verify-context.js +27 -0
- package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
- package/dist/src/gsd/verify-council-prompts.js +85 -0
- package/dist/src/gsd/verify-council.d.ts +25 -0
- package/dist/src/gsd/verify-council.js +119 -0
- package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
- package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
- package/dist/src/gsd/workflow-engine.d.ts +60 -0
- package/dist/src/gsd/workflow-engine.js +207 -0
- package/dist/src/gsd/workflow-tools.d.ts +13 -0
- package/dist/src/gsd/workflow-tools.js +277 -0
- package/dist/src/hooks/index.js +1 -1
- package/dist/src/index.js +44 -11
- package/dist/src/maintain/pr-builder.js +23 -13
- package/dist/src/mcp/auto-setup.js +57 -32
- package/dist/src/mcp/client-pool.js +1 -1
- package/dist/src/mcp/ee-tools.js +1 -0
- package/dist/src/mcp/research-onboarding.js +8 -7
- package/dist/src/mcp/runtime.js +34 -2
- package/dist/src/models/catalog-client.d.ts +87 -0
- package/dist/src/models/catalog-client.js +105 -38
- package/dist/src/models/catalog.json +528 -265
- package/dist/src/models/registry.d.ts +22 -7
- package/dist/src/models/registry.js +73 -10
- package/dist/src/ops/doctor.js +1 -1
- package/dist/src/orchestrator/auto-commit.js +1 -1
- package/dist/src/orchestrator/batch-turn-runner.js +2 -2
- package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
- package/dist/src/orchestrator/cache-prefix.js +83 -0
- package/dist/src/orchestrator/compact-request.d.ts +32 -0
- package/dist/src/orchestrator/compact-request.js +41 -0
- package/dist/src/orchestrator/compaction.d.ts +10 -0
- package/dist/src/orchestrator/compaction.js +27 -7
- package/dist/src/orchestrator/council-manager.d.ts +12 -3
- package/dist/src/orchestrator/council-manager.js +65 -24
- package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
- package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
- package/dist/src/orchestrator/error-utils.d.ts +29 -0
- package/dist/src/orchestrator/error-utils.js +132 -24
- package/dist/src/orchestrator/grounding-check.js +39 -1
- package/dist/src/orchestrator/message-processor.js +242 -33
- package/dist/src/orchestrator/orchestrator.d.ts +39 -3
- package/dist/src/orchestrator/orchestrator.js +651 -102
- package/dist/src/orchestrator/preprocessor.js +1 -1
- package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
- package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
- package/dist/src/orchestrator/prompts.js +17 -17
- package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
- package/dist/src/orchestrator/reactive-delegation.js +59 -0
- package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
- package/dist/src/orchestrator/retry-classifier.js +46 -2
- package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
- package/dist/src/orchestrator/safety-intercept.js +55 -0
- package/dist/src/orchestrator/scope-reminder.js +1 -1
- package/dist/src/orchestrator/session-experience.d.ts +2 -1
- package/dist/src/orchestrator/session-experience.js +2 -1
- package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
- package/dist/src/orchestrator/should-run-gate.js +18 -0
- package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
- package/dist/src/orchestrator/stall-watchdog.js +47 -13
- package/dist/src/orchestrator/stream-runner.js +62 -29
- package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
- package/dist/src/orchestrator/sub-agent-cap.js +16 -1
- package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
- package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
- package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
- package/dist/src/orchestrator/subagent-compactor.js +126 -15
- package/dist/src/orchestrator/tool-engine.d.ts +22 -0
- package/dist/src/orchestrator/tool-engine.js +620 -56
- package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
- package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
- package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
- package/dist/src/orchestrator/turn-watchdog.js +55 -0
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +1 -1
- package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
- package/dist/src/pil/cheap-model-playbook.js +5 -1
- package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
- package/dist/src/pil/cheap-model-workbooks.js +1 -1
- package/dist/src/pil/discovery-types.d.ts +1 -0
- package/dist/src/pil/discovery.js +16 -11
- package/dist/src/pil/layer1-intent.d.ts +18 -6
- package/dist/src/pil/layer1-intent.js +66 -757
- package/dist/src/pil/layer15-context-scan.js +15 -1
- package/dist/src/pil/layer3-ee-injection.js +23 -8
- package/dist/src/pil/layer4-gsd.js +69 -16
- package/dist/src/pil/layer5-context.js +7 -3
- package/dist/src/pil/layer6-output.d.ts +23 -0
- package/dist/src/pil/layer6-output.js +5 -1
- package/dist/src/pil/llm-classify.d.ts +33 -2
- package/dist/src/pil/llm-classify.js +123 -131
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +1 -0
- package/dist/src/pil/pipeline.js +34 -2
- package/dist/src/pil/response-tools.js +5 -3
- package/dist/src/pil/schema.d.ts +1 -0
- package/dist/src/pil/schema.js +2 -0
- package/dist/src/pil/types.d.ts +18 -0
- package/dist/src/playbook/directives.d.ts +4 -0
- package/dist/src/playbook/directives.js +17 -5
- package/dist/src/product-loop/backlog-builder.d.ts +14 -1
- package/dist/src/product-loop/backlog-builder.js +30 -6
- package/dist/src/product-loop/discovery-context-format.js +3 -1
- package/dist/src/product-loop/discovery-ecosystem.js +4 -1
- package/dist/src/product-loop/discovery-interview.js +32 -3
- package/dist/src/product-loop/discovery-schema.js +5 -1
- package/dist/src/product-loop/ideal-trace.d.ts +7 -0
- package/dist/src/product-loop/ideal-trace.js +64 -0
- package/dist/src/product-loop/index.d.ts +13 -1
- package/dist/src/product-loop/index.js +333 -52
- package/dist/src/product-loop/loop-driver.d.ts +7 -0
- package/dist/src/product-loop/loop-driver.js +310 -99
- package/dist/src/product-loop/phase-plan.d.ts +5 -0
- package/dist/src/product-loop/phase-plan.js +39 -2
- package/dist/src/product-loop/phase-runner.js +9 -1
- package/dist/src/product-loop/sprint-runner.d.ts +111 -0
- package/dist/src/product-loop/sprint-runner.js +559 -16
- package/dist/src/product-loop/types.d.ts +36 -5
- package/dist/src/providers/adapter.d.ts +1 -1
- package/dist/src/providers/adapter.js +3 -4
- package/dist/src/providers/auth/browser-flow.d.ts +1 -1
- package/dist/src/providers/auth/browser-flow.js +1 -1
- package/dist/src/providers/auth/openai-oauth.js +1 -1
- package/dist/src/providers/auth/registry.js +0 -34
- package/dist/src/providers/auth/token-store.js +4 -1
- package/dist/src/providers/auth/types.d.ts +1 -1
- package/dist/src/providers/auth/types.js +1 -1
- package/dist/src/providers/capabilities.d.ts +24 -5
- package/dist/src/providers/capabilities.js +42 -24
- package/dist/src/providers/endpoints.d.ts +2 -2
- package/dist/src/providers/endpoints.js +11 -10
- package/dist/src/providers/keychain.d.ts +1 -1
- package/dist/src/providers/keychain.js +7 -9
- package/dist/src/providers/mcp-vision-bridge.js +56 -146
- package/dist/src/providers/openai-compatible.js +8 -1
- package/dist/src/providers/pricing.d.ts +2 -2
- package/dist/src/providers/pricing.js +3 -13
- package/dist/src/providers/runtime.d.ts +27 -2
- package/dist/src/providers/runtime.js +78 -15
- package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
- package/dist/src/providers/strategies/base.strategy.js +24 -1
- package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
- package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
- package/dist/src/providers/strategies/registry.js +4 -4
- package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
- package/dist/src/providers/strategies/thinking-mode.js +280 -1
- package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
- package/dist/src/providers/strategies/zai.strategy.js +44 -0
- package/dist/src/providers/types.d.ts +5 -6
- package/dist/src/providers/types.js +2 -2
- package/dist/src/providers/vision-backend.d.ts +47 -0
- package/dist/src/providers/vision-backend.js +258 -0
- package/dist/src/providers/vision-proxy.d.ts +22 -9
- package/dist/src/providers/vision-proxy.js +63 -132
- package/dist/src/providers/wire-debug.js +95 -0
- package/dist/src/router/decide.d.ts +13 -0
- package/dist/src/router/decide.js +138 -36
- package/dist/src/router/peak-hour.d.ts +38 -0
- package/dist/src/router/peak-hour.js +107 -0
- package/dist/src/router/step-router.js +3 -2
- package/dist/src/router/warm.js +4 -5
- package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
- package/dist/src/scaffold/continuation-prompt.js +26 -0
- package/dist/src/scaffold/point-to-existing.d.ts +21 -0
- package/dist/src/scaffold/point-to-existing.js +25 -0
- package/dist/src/self-qa/agentic-loop.js +3 -3
- package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
- package/dist/src/{ui/state → state}/active-run.js +21 -0
- package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
- package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
- package/dist/src/state/turn-trace.d.ts +43 -0
- package/dist/src/state/turn-trace.js +32 -0
- package/dist/src/storage/db.js +2 -1
- package/dist/src/storage/index.d.ts +1 -1
- package/dist/src/storage/index.js +1 -1
- package/dist/src/storage/interaction-log.d.ts +1 -1
- package/dist/src/storage/migrations.js +71 -1
- package/dist/src/storage/sessions.d.ts +28 -10
- package/dist/src/storage/sessions.js +78 -21
- package/dist/src/storage/transcript-view.js +1 -1
- package/dist/src/storage/transcript.d.ts +51 -0
- package/dist/src/storage/transcript.js +284 -13
- package/dist/src/tools/file.d.ts +15 -0
- package/dist/src/tools/file.js +32 -0
- package/dist/src/tools/native-tools.js +5 -0
- package/dist/src/tools/registry.d.ts +3 -0
- package/dist/src/tools/registry.js +460 -22
- package/dist/src/tools/research.d.ts +29 -0
- package/dist/src/tools/research.js +233 -0
- package/dist/src/types/index.d.ts +118 -3
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/cards/product-status-card.js +1 -1
- package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
- package/dist/src/ui/components/bubble-body-guard.js +50 -0
- package/dist/src/ui/components/context-rail.d.ts +26 -0
- package/dist/src/ui/components/context-rail.js +33 -0
- package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
- package/dist/src/ui/components/council-conclusion-card.js +420 -0
- package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
- package/dist/src/ui/components/council-debate-pill.js +34 -0
- package/dist/src/ui/components/council-info-card.js +2 -2
- package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
- package/dist/src/ui/components/council-leader-bubble.js +21 -11
- package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
- package/dist/src/ui/components/council-message-bubble.js +16 -15
- package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
- package/dist/src/ui/components/council-phase-timeline.js +49 -15
- package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
- package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
- package/dist/src/ui/components/council-question-card.js +12 -12
- package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
- package/dist/src/ui/components/council-rail-rounds.js +57 -0
- package/dist/src/ui/components/council-round-group.d.ts +38 -0
- package/dist/src/ui/components/council-round-group.js +88 -0
- package/dist/src/ui/components/council-status-list.d.ts +3 -1
- package/dist/src/ui/components/council-status-list.js +36 -24
- package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
- package/dist/src/ui/components/council-synthesis-banner.js +20 -5
- package/dist/src/ui/components/halt-recovery-card.js +9 -5
- package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
- package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
- package/dist/src/ui/components/prompt-box.js +18 -16
- package/dist/src/ui/components/session-tree-card.d.ts +14 -0
- package/dist/src/ui/components/session-tree-card.js +46 -0
- package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
- package/dist/src/ui/components/slash-inline-menu.js +26 -5
- package/dist/src/ui/components/task-list-panel.d.ts +14 -1
- package/dist/src/ui/components/task-list-panel.js +22 -2
- package/dist/src/ui/containers/modals-layer.d.ts +2 -1
- package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
- package/dist/src/ui/mcp-modal.js +2 -4
- package/dist/src/ui/modals/api-key-modal.js +1 -1
- package/dist/src/ui/modals/connect-modal.js +4 -3
- package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
- package/dist/src/ui/modals/session-picker-modal.js +3 -5
- package/dist/src/ui/picker-providers.d.ts +1 -1
- package/dist/src/ui/picker-providers.js +1 -1
- package/dist/src/ui/primitives/index.d.ts +1 -0
- package/dist/src/ui/primitives/index.js +2 -0
- package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
- package/dist/src/ui/primitives/semantic-primitives.js +81 -0
- package/dist/src/ui/slash/compact.js +5 -7
- package/dist/src/ui/slash/cost.js +1 -1
- package/dist/src/ui/slash/council.js +19 -1
- package/dist/src/ui/slash/debug.d.ts +3 -31
- package/dist/src/ui/slash/debug.js +9 -20
- package/dist/src/ui/slash/ideal.d.ts +6 -2
- package/dist/src/ui/slash/ideal.js +97 -7
- package/dist/src/ui/slash/menu-items.d.ts +7 -0
- package/dist/src/ui/slash/menu-items.js +12 -18
- package/dist/src/ui/slash/registry.d.ts +2 -0
- package/dist/src/ui/slash/registry.js +4 -0
- package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
- package/dist/src/ui/status-bar/cache-hit.js +9 -0
- package/dist/src/ui/status-bar/index.d.ts +1 -1
- package/dist/src/ui/status-bar/index.js +7 -3
- package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
- package/dist/src/ui/status-bar/usd-meter.js +6 -4
- package/dist/src/ui/theme.d.ts +1 -0
- package/dist/src/ui/theme.js +2 -0
- package/dist/src/ui/types.d.ts +7 -0
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/ui/utils/format.d.ts +14 -0
- package/dist/src/ui/utils/format.js +23 -3
- package/dist/src/usage/downgrade.js +2 -2
- package/dist/src/usage/product-ledger.js +2 -2
- package/dist/src/utils/install-manager.js +2 -1
- package/dist/src/utils/logger.js +2 -2
- package/dist/src/utils/permission-mode.js +5 -3
- package/dist/src/utils/redactor.js +1 -1
- package/dist/src/utils/settings.d.ts +153 -5
- package/dist/src/utils/settings.js +233 -29
- package/dist/src/utils/visible-retry.d.ts +11 -0
- package/dist/src/utils/visible-retry.js +10 -1
- package/dist/src/verify/entrypoint.d.ts +1 -1
- package/dist/src/verify/entrypoint.js +1 -1
- package/dist/src/verify/recipes.d.ts +13 -0
- package/dist/src/verify/recipes.js +15 -0
- package/package.json +135 -132
- package/dist/src/providers/auth/gcloud.d.ts +0 -28
- package/dist/src/providers/auth/gcloud.js +0 -102
- package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
- package/dist/src/providers/auth/gemini-oauth.js +0 -472
- package/dist/src/providers/gemini.d.ts +0 -11
- package/dist/src/providers/gemini.js +0 -45
- package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
- package/dist/src/providers/siliconflow-sse-repair.js +0 -177
- package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
- package/dist/src/providers/strategies/google.strategy.js +0 -174
- package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
- package/dist/src/ui/containers/chat-feed.d.ts +0 -40
- package/dist/src/ui/containers/chat-feed.js +0 -66
|
@@ -21,11 +21,17 @@
|
|
|
21
21
|
* projection check, not a retroactive one.
|
|
22
22
|
* - CB-2 (oscillation) is checked AFTER this sprint's score is known.
|
|
23
23
|
*/
|
|
24
|
+
import { existsSync } from "node:fs";
|
|
25
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
24
26
|
import * as path from "node:path";
|
|
25
27
|
import { prependDecisionsLock, readDecisionsLock } from "../council/decisions-lock.js";
|
|
26
28
|
import { runCouncil } from "../council/index.js";
|
|
27
29
|
import { phaseDone, phaseError, phaseStart } from "../council/phase-events.js";
|
|
30
|
+
import { fireAndForgetWorkflowEvent } from "../ee/workflow-event.js";
|
|
28
31
|
import { readArtifact, writeArtifact } from "../flow/artifact-io.js";
|
|
32
|
+
import { renderResumeDigest, writeSprintOutcome, writeSprintVerify } from "../flow/run-artifacts.js";
|
|
33
|
+
import { isContextRailEnabled } from "../gsd/flags.js";
|
|
34
|
+
import { SPRINT_EXECUTION_MARKER } from "../pil/layer6-output.js";
|
|
29
35
|
import { detectProviderForModel } from "../providers/runtime.js";
|
|
30
36
|
import { logUIInteraction } from "../storage/index.js";
|
|
31
37
|
import { commitToProduct, release } from "../usage/ledger.js";
|
|
@@ -40,19 +46,214 @@ import { formatProjectContextForPrompt } from "./discovery-context-format.js";
|
|
|
40
46
|
import { readProjectContext } from "./discovery-persistence.js";
|
|
41
47
|
import { evaluateDoneGate } from "./done-gate.js";
|
|
42
48
|
import { buildContinueFeedback } from "./feedback-routing.js";
|
|
49
|
+
import { idealTrace } from "./ideal-trace.js";
|
|
43
50
|
import { postSprintBoundary } from "./phase-tracker-bridge.js";
|
|
44
51
|
import { computeProgressSnapshot, renderSnapshotMarkdown } from "./progress-snapshot.js";
|
|
45
52
|
import { appendRoleMemory } from "./role-memory.js";
|
|
46
53
|
import { loadVerifyFailureSignatures, recordVerifyFailureAndMaybePush } from "./verify-failure-tracking.js";
|
|
47
|
-
import { parseVerifyResult } from "./verify-result.js";
|
|
54
|
+
import { parseVerifyResult, VERIFY_PASS_MARKER } from "./verify-result.js";
|
|
48
55
|
// P3.7: track one-shot CB-2 retry bonus per run (keyed by runId).
|
|
49
56
|
// The Map is module-scoped so multiple sprints within the same run share state
|
|
50
57
|
// without touching DriverContext / IterationState shapes.
|
|
51
58
|
const _cb2RetryUsed = new Map();
|
|
59
|
+
/** Watchdog ceiling for the verify stage (ms). Override with MUONROI_SPRINT_VERIFY_TIMEOUT_MS. */
|
|
60
|
+
function getVerifyWatchdogTimeoutMs() {
|
|
61
|
+
const raw = process.env.MUONROI_SPRINT_VERIFY_TIMEOUT_MS;
|
|
62
|
+
const n = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
63
|
+
if (Number.isFinite(n) && n > 0)
|
|
64
|
+
return n;
|
|
65
|
+
return 10 * 60 * 1000; // 10 min default
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Bound the verify stage with a watchdog timeout.
|
|
69
|
+
*
|
|
70
|
+
* `runVerifyOrchestration` can hang indefinitely with no visible signal:
|
|
71
|
+
* `prepareVerifyRun` → `ensureVerifyCheckpoint` spawns the `shuru` sandbox
|
|
72
|
+
* (`spawnWithProgress("shuru", …)`) which stalls on hosts where shuru is
|
|
73
|
+
* unavailable/misconfigured (e.g. Windows), and the verify sub-agent itself has
|
|
74
|
+
* no TTFB timeout. Because sprint-runner previously called it as a bare
|
|
75
|
+
* `await runVerifyOrchestration(agent)` with NO abortSignal and NO timeout, a
|
|
76
|
+
* single hung verify BRICKED the whole /ideal run silently — no error, no
|
|
77
|
+
* recovery card — observed live as a 30+ min dead stall right after
|
|
78
|
+
* "Committed: N sprints planned" (the impl turn finished, verify never returned).
|
|
79
|
+
*
|
|
80
|
+
* On timeout we abort the sub-agent, log with context (No-Silent-Catch), and
|
|
81
|
+
* return an ERROR ToolResult so the sprint loop treats it as a failed verify
|
|
82
|
+
* (Step 5 → verifyVerdict FAIL/ERROR → feedback-routing) instead of hanging
|
|
83
|
+
* forever. The hung sandbox op may leak in the background, but the run recovers
|
|
84
|
+
* and the failure is surfaced + resumable. `onProgress` is forwarded to console
|
|
85
|
+
* so a future hang is diagnosable (e.g. "Creating checkpoint: <name>").
|
|
86
|
+
*/
|
|
87
|
+
async function runVerifyWithWatchdog(verifyAgent, runId, sprintN) {
|
|
88
|
+
const timeoutMs = getVerifyWatchdogTimeoutMs();
|
|
89
|
+
const controller = new AbortController();
|
|
90
|
+
let timer;
|
|
91
|
+
const onProgress = (detail) => {
|
|
92
|
+
if (process.env.MUONROI_DEBUG_VERIFY === "1")
|
|
93
|
+
console.error(`[verify:sprint-${sprintN}] ${detail}`);
|
|
94
|
+
};
|
|
95
|
+
const timeout = new Promise((resolve) => {
|
|
96
|
+
timer = setTimeout(() => {
|
|
97
|
+
controller.abort();
|
|
98
|
+
const msg = `verify stage exceeded ${Math.round(timeoutMs / 1000)}s watchdog and was aborted ` +
|
|
99
|
+
`(sprint ${sprintN}, run ${runId}) — likely a hung sandbox checkpoint (shuru) or a ` +
|
|
100
|
+
`verify sub-agent LLM call with no TTFB timeout`;
|
|
101
|
+
console.error(`[sprint-runner] ${msg}`);
|
|
102
|
+
resolve({ success: false, output: "", error: `verify-timeout: ${msg}` });
|
|
103
|
+
}, timeoutMs);
|
|
104
|
+
});
|
|
105
|
+
try {
|
|
106
|
+
return await Promise.race([
|
|
107
|
+
runVerifyOrchestration(verifyAgent, { abortSignal: controller.signal, onProgress }),
|
|
108
|
+
timeout,
|
|
109
|
+
]);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
113
|
+
console.error(`[sprint-runner] verify stage threw (sprint ${sprintN}, run ${runId}): ${message}`);
|
|
114
|
+
return { success: false, output: "", error: `verify-error: ${message}` };
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
if (timer)
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
52
121
|
/** @internal Test-only: reset CB-2 retry state for a given runId. */
|
|
53
122
|
export function _resetCb2RetryUsed(runId) {
|
|
54
123
|
_cb2RetryUsed.delete(runId);
|
|
55
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Idle-chunk ceiling for the implementation stage (ms). Override with
|
|
127
|
+
* MUONROI_SPRINT_IMPL_IDLE_MS. This is a TIME-SINCE-LAST-CHUNK budget, not a
|
|
128
|
+
* total-turn cap — a legitimately long implementation streams progress the
|
|
129
|
+
* whole way, so it may run for many minutes, but it must never go completely
|
|
130
|
+
* silent (no chunk at all) for this long.
|
|
131
|
+
*/
|
|
132
|
+
export function getImplIdleTimeoutMs() {
|
|
133
|
+
const raw = process.env.MUONROI_SPRINT_IMPL_IDLE_MS;
|
|
134
|
+
const n = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
135
|
+
if (Number.isFinite(n) && n > 0)
|
|
136
|
+
return n;
|
|
137
|
+
return 4 * 60 * 1000; // 4 min of total silence → treat the impl turn as stalled
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Hard total-elapsed ceiling for the implementation stage (ms). Override with
|
|
141
|
+
* MUONROI_SPRINT_IMPL_TOTAL_MS. Unlike the idle budget this is armed once and is
|
|
142
|
+
* NOT reset by chunks, so it catches a hang that keeps the idle guard alive with
|
|
143
|
+
* heartbeat/status chunks. Generous by default so a legitimately large sprint is
|
|
144
|
+
* not cut short; a genuine hang still terminates within this ceiling.
|
|
145
|
+
*/
|
|
146
|
+
export function getImplTotalTimeoutMs() {
|
|
147
|
+
const raw = process.env.MUONROI_SPRINT_IMPL_TOTAL_MS;
|
|
148
|
+
const n = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
149
|
+
if (Number.isFinite(n) && n > 0)
|
|
150
|
+
return n;
|
|
151
|
+
return 15 * 60 * 1000; // 15 min hard ceiling on a single impl turn
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Whether the implement stage runs in an ISOLATED bounded sub-agent context
|
|
155
|
+
* (ctx.runIsolatedTask) instead of the shared top-level turn (processMessageFn).
|
|
156
|
+
* Default ON. Disable with MUONROI_SPRINT_ISOLATED_IMPL=0.
|
|
157
|
+
*
|
|
158
|
+
* The isolated path is the fix for the live ctx-overflow wedge: the flat
|
|
159
|
+
* processMessageFn turn inherited the full council-debate history (~5.9M tokens
|
|
160
|
+
* observed), started implementation already at ~94% context, then wedged after a
|
|
161
|
+
* mid-turn compaction. A fresh child context (getSubAgentBudgetChars cap +
|
|
162
|
+
* independent in-loop compaction) never inherits the debate, so it starts near
|
|
163
|
+
* empty and its clutter is absorbed as one compact ToolResult.
|
|
164
|
+
*/
|
|
165
|
+
export function getSprintIsolatedImplEnabled() {
|
|
166
|
+
return process.env.MUONROI_SPRINT_ISOLATED_IMPL !== "0";
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Pure decision: use the isolated sub-agent path for the implement stage?
|
|
170
|
+
* True only when the flag is on AND the driver actually provides the bridge
|
|
171
|
+
* (legacy/test drivers omit runIsolatedTask → fall back to processMessageFn).
|
|
172
|
+
* Extracted for unit testing without spinning up a full runSprint.
|
|
173
|
+
*/
|
|
174
|
+
export function shouldUseIsolatedImpl(hasBridge, enabled = getSprintIsolatedImplEnabled()) {
|
|
175
|
+
return enabled && hasBridge;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Imperative execution directive prepended to the sprint plan before it is
|
|
179
|
+
* handed to the orchestrator. The raw plan synthesis is a declarative design
|
|
180
|
+
* document; without this prefix the impl turn narrates it back instead of
|
|
181
|
+
* applying edits. Exported for test assertion. @internal
|
|
182
|
+
*/
|
|
183
|
+
export const IMPL_EXECUTION_DIRECTIVE = `${SPRINT_EXECUTION_MARKER}\n\n` +
|
|
184
|
+
"You are the sprint IMPLEMENTER. EXECUTE the sprint plan below as an implementation task. Make the " +
|
|
185
|
+
"actual code changes NOW using your file-edit tools — read the target files, then edit/write them to " +
|
|
186
|
+
"apply every action item. Do NOT merely restate, summarize, or re-plan the design; apply the edits to " +
|
|
187
|
+
"the repository. Run the plan's own verification commands where given. Before you finish, self-verify " +
|
|
188
|
+
"as a reviewer would: confirm every target file named in the plan actually exists on disk with the " +
|
|
189
|
+
"intended change — do not stop with action items unaddressed. Stop only when the action items are " +
|
|
190
|
+
"implemented.\n\n" +
|
|
191
|
+
"--- SPRINT PLAN TO IMPLEMENT ---\n\n";
|
|
192
|
+
/**
|
|
193
|
+
* Wrap the implementation `processMessageFn` stream with an idle-chunk watchdog.
|
|
194
|
+
*
|
|
195
|
+
* Root cause it addresses (observed live 2026-07-08, /ideal resume of the
|
|
196
|
+
* gsd-core migration): the implementation stage delegates to the host
|
|
197
|
+
* orchestrator turn via `ctx.processMessageFn(implPrompt)` and consumes it with
|
|
198
|
+
* `for await (const chunk of implGen)`. The orchestrator turn finished its final
|
|
199
|
+
* LLM response cleanly (finishReason "stop", text-only) but the generator then
|
|
200
|
+
* suspended post-finish and never completed — the `for await` blocked for 17+
|
|
201
|
+
* minutes with NO chunk, NO phaseDone, NO advance to Verify, NO error. Because
|
|
202
|
+
* the LLM STREAM had already finished, the orchestrator's mid-stream
|
|
203
|
+
* time-to-next-chunk stall-watchdog does not fire — the hang is on the JS side
|
|
204
|
+
* after the stream terminator.
|
|
205
|
+
*
|
|
206
|
+
* TWO complementary guards (a single idle guard was observed live to be
|
|
207
|
+
* defeated: the impl created 2 files then emitted only non-progress heartbeat
|
|
208
|
+
* chunks for 9+ min, resetting a per-chunk idle timer without ever completing):
|
|
209
|
+
* - `idleMs` — resets on every yielded chunk; catches a TOTALLY silent stall
|
|
210
|
+
* (the post-finish hang above, zero chunks) quickly.
|
|
211
|
+
* - `totalMs` — armed ONCE at entry, NOT reset by chunks; a hard ceiling that
|
|
212
|
+
* fires even when heartbeat/status chunks keep the idle guard alive while no
|
|
213
|
+
* real progress is made.
|
|
214
|
+
* Either firing throws so the caller's existing try/catch converts the wedge
|
|
215
|
+
* into a visible phaseError (the sprint then surfaces + can recover), exactly
|
|
216
|
+
* like `runVerifyWithWatchdog` does for the verify stage. The suspended
|
|
217
|
+
* orchestrator promise may leak in the background, but the run recovers.
|
|
218
|
+
*/
|
|
219
|
+
export async function* withImplIdleWatchdog(gen, idleMs, sprintN, totalMs = getImplTotalTimeoutMs()) {
|
|
220
|
+
const it = gen[Symbol.asyncIterator]();
|
|
221
|
+
let totalTimer;
|
|
222
|
+
const total = new Promise((_, reject) => {
|
|
223
|
+
totalTimer = setTimeout(() => {
|
|
224
|
+
reject(new Error(`implementation stage exceeded ${Math.round(totalMs / 1000)}s total watchdog and was ` +
|
|
225
|
+
`treated as stalled (sprint ${sprintN}) — the orchestrator turn never completed ` +
|
|
226
|
+
`(likely hung after its final response while emitting only heartbeat chunks)`));
|
|
227
|
+
}, totalMs);
|
|
228
|
+
});
|
|
229
|
+
try {
|
|
230
|
+
while (true) {
|
|
231
|
+
let idleTimer;
|
|
232
|
+
const idle = new Promise((_, reject) => {
|
|
233
|
+
idleTimer = setTimeout(() => {
|
|
234
|
+
reject(new Error(`implementation stage produced no output for ${Math.round(idleMs / 1000)}s and was ` +
|
|
235
|
+
`treated as stalled (sprint ${sprintN}) — the orchestrator turn hung post-finish ` +
|
|
236
|
+
`(finished its LLM response but the generator never completed)`));
|
|
237
|
+
}, idleMs);
|
|
238
|
+
});
|
|
239
|
+
let res;
|
|
240
|
+
try {
|
|
241
|
+
res = await Promise.race([it.next(), idle, total]);
|
|
242
|
+
}
|
|
243
|
+
finally {
|
|
244
|
+
if (idleTimer)
|
|
245
|
+
clearTimeout(idleTimer);
|
|
246
|
+
}
|
|
247
|
+
if (res.done)
|
|
248
|
+
return;
|
|
249
|
+
yield res.value;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
if (totalTimer)
|
|
254
|
+
clearTimeout(totalTimer);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
56
257
|
export { computeFailureSignature, loadVerifyFailureSignatures, pushFailureToEE, recordVerifyFailureAndMaybePush, saveVerifyFailureSignatures, } from "./verify-failure-tracking.js";
|
|
57
258
|
/**
|
|
58
259
|
* Run a single sprint. Yields StreamChunk events for the UI and returns the
|
|
@@ -61,6 +262,104 @@ export { computeFailureSignature, loadVerifyFailureSignatures, pushFailureToEE,
|
|
|
61
262
|
* Throws on circuit-breaker halt — caller (loop driver) catches and writes
|
|
62
263
|
* the appropriate halt state to manifest/state.
|
|
63
264
|
*/
|
|
265
|
+
/** Path to the persisted per-sprint plan synthesis (Wave 2). @internal */
|
|
266
|
+
export function sprintPlanPath(runDir, sprintN) {
|
|
267
|
+
return path.join(runDir, `sprint-${sprintN}-plan.md`);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Wave 2: read a persisted sprint plan if present. Returns "" when absent or on
|
|
271
|
+
* read error (caller then runs the planning council). Never throws.
|
|
272
|
+
*
|
|
273
|
+
* The planning council is non-deterministic — re-running it on a resumed/retried
|
|
274
|
+
* sprint produces a different design AND a different target folder, which is why
|
|
275
|
+
* the impl turn was observed re-scaffolding in a new location each run. Reusing
|
|
276
|
+
* the persisted plan makes per-sprint planning idempotent so the same target
|
|
277
|
+
* files are continued across resume.
|
|
278
|
+
*/
|
|
279
|
+
export async function readPersistedSprintPlan(planPath) {
|
|
280
|
+
try {
|
|
281
|
+
if (!existsSync(planPath))
|
|
282
|
+
return "";
|
|
283
|
+
return (await readFile(planPath, "utf8")).trim();
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
console.error(`[sprint-runner] readPersistedSprintPlan failed for ${planPath}: ${err.message}`);
|
|
287
|
+
return "";
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/** Wave 2: persist a sprint plan synthesis for idempotent resume. Never throws. */
|
|
291
|
+
export async function persistSprintPlan(planPath, synthesis) {
|
|
292
|
+
if (!synthesis.trim())
|
|
293
|
+
return;
|
|
294
|
+
try {
|
|
295
|
+
await writeFile(planPath, synthesis, "utf8");
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
console.error(`[sprint-runner] persistSprintPlan failed for ${planPath}: ${err.message}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Extract repo-relative target file paths a sprint plan names (src/…, packages/…,
|
|
303
|
+
* tests/…). Deduped, capped. Used by Wave 3 (existing targets → continue) and 4A
|
|
304
|
+
* (missing targets → completeness re-check). Never throws.
|
|
305
|
+
*/
|
|
306
|
+
export function extractPlanTargetPaths(planSynthesis, cap = 40) {
|
|
307
|
+
try {
|
|
308
|
+
const tokens = new Set();
|
|
309
|
+
const re = /\b((?:src|packages|tests|scripts|lib|app|apps)\/[\w./@-]+\.[a-z]{1,5})\b/gi;
|
|
310
|
+
let m = re.exec(planSynthesis);
|
|
311
|
+
while (m !== null) {
|
|
312
|
+
tokens.add(m[1].replace(/\\/g, "/"));
|
|
313
|
+
if (tokens.size >= cap)
|
|
314
|
+
break;
|
|
315
|
+
m = re.exec(planSynthesis);
|
|
316
|
+
}
|
|
317
|
+
return [...tokens];
|
|
318
|
+
}
|
|
319
|
+
catch (err) {
|
|
320
|
+
console.error(`[sprint-runner] extractPlanTargetPaths failed: ${err.message}`);
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Wave 3: plan-named target file paths that ALREADY EXIST on disk, so the impl
|
|
326
|
+
* turn continues them rather than re-scaffolding in a new location. Empty on a
|
|
327
|
+
* greenfield sprint (files don't exist yet) → no injection.
|
|
328
|
+
*/
|
|
329
|
+
export async function detectExistingPlanTargets(planSynthesis, cwd, cap = 20) {
|
|
330
|
+
const existing = [];
|
|
331
|
+
for (const t of extractPlanTargetPaths(planSynthesis)) {
|
|
332
|
+
if (existsSync(path.resolve(cwd, t)))
|
|
333
|
+
existing.push(t);
|
|
334
|
+
if (existing.length >= cap)
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
return existing;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* 4A: plan-named target file paths that STILL DO NOT EXIST after the impl turn —
|
|
341
|
+
* i.e. action items the implementer left unaddressed. Drives the post-impl
|
|
342
|
+
* completeness re-check (spend an extra turn ONLY when there is proven-incomplete
|
|
343
|
+
* work, unlike an unconditional reviewer pass). Empty ⇒ every named target landed.
|
|
344
|
+
*/
|
|
345
|
+
export async function computeMissingPlanTargets(planSynthesis, cwd, cap = 20) {
|
|
346
|
+
const missing = [];
|
|
347
|
+
for (const t of extractPlanTargetPaths(planSynthesis)) {
|
|
348
|
+
if (!existsSync(path.resolve(cwd, t)))
|
|
349
|
+
missing.push(t);
|
|
350
|
+
if (missing.length >= cap)
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
return missing;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* 4A completeness re-check toggle. Default ON; disable with
|
|
357
|
+
* MUONROI_SPRINT_IMPL_RECHECK=0. When on, and the impl turn left plan-named
|
|
358
|
+
* target files missing, ONE focused follow-up turn is spent to finish them.
|
|
359
|
+
*/
|
|
360
|
+
export function getImplRecheckEnabled() {
|
|
361
|
+
return process.env.MUONROI_SPRINT_IMPL_RECHECK !== "0";
|
|
362
|
+
}
|
|
64
363
|
export async function* runSprint(args) {
|
|
65
364
|
const { sprintN, ctx, productSpec, roleAssignments, history, carryOver, phaseScope } = args;
|
|
66
365
|
const runDir = path.join(ctx.flowDir, "runs", ctx.runId);
|
|
@@ -216,15 +515,52 @@ export async function* runSprint(args) {
|
|
|
216
515
|
const noopProcess = async function* () {
|
|
217
516
|
/* no host orchestrator wired during planning */
|
|
218
517
|
};
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
518
|
+
// Wave 2 (2026-07-08): reuse a persisted per-sprint plan if one exists, making
|
|
519
|
+
// per-sprint planning idempotent across resume/retry. Without this the
|
|
520
|
+
// non-deterministic planning council re-ran on every runSprint call and emitted
|
|
521
|
+
// a different design → a different target folder each time (run1 src/council/,
|
|
522
|
+
// run4 src/engine/), so the impl turn re-scaffolded instead of continuing.
|
|
523
|
+
const planPath = sprintPlanPath(runDir, sprintN);
|
|
524
|
+
let planSynthesis = await readPersistedSprintPlan(planPath);
|
|
525
|
+
if (planSynthesis) {
|
|
526
|
+
idealTrace("sprint.planCouncil.reused", { runId: ctx.runId, sprintN, planSynthesisLen: planSynthesis.length });
|
|
527
|
+
yield {
|
|
528
|
+
type: "content",
|
|
529
|
+
content: `\n> [sprint-plan] Reusing persisted plan for sprint ${sprintN} (${planSynthesis.length} chars) — re-planning skipped so the same target files are continued.\n`,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
idealTrace("sprint.planCouncil.before", { runId: ctx.runId, sprintN });
|
|
534
|
+
const planGen = runCouncil(councilTopic, sessionModelId, [], ctx.runId, productLlm, ctx.respondToQuestion, ctx.respondToPreflight, ctx.processMessageFn ?? noopProcess, {
|
|
535
|
+
skipClarification: true,
|
|
536
|
+
cwd,
|
|
537
|
+
runDir,
|
|
538
|
+
suppressInlineMeta: isContextRailEnabled(),
|
|
539
|
+
// The product plan + spec were already debated (CB-1) and approved at the
|
|
540
|
+
// `/ideal` preflight. Re-gating and re-researching each sprint's internal
|
|
541
|
+
// plan strands the loop before implementation is ever reached (the exact
|
|
542
|
+
// "debate great, never implements" symptom). Auto-approve the per-sprint
|
|
543
|
+
// plan and reuse CB-1 research; the post-sprint customer verdict still lets
|
|
544
|
+
// the user review each sprint's OUTPUT.
|
|
545
|
+
autoApprovePreflight: true,
|
|
546
|
+
skipResearch: true,
|
|
547
|
+
// Automated per-sprint planning: suppress the interactive post-debate menu
|
|
548
|
+
// (it stranded the sprint before implementation — blocker 4/5) and skip the
|
|
549
|
+
// session-scoped persistence that FK-fails on the product-run id. The plan
|
|
550
|
+
// is auto-locked and control returns here for the Implementation stage.
|
|
551
|
+
sprintPlanningMode: true,
|
|
552
|
+
});
|
|
553
|
+
while (true) {
|
|
554
|
+
const step = await planGen.next();
|
|
555
|
+
if (step.done) {
|
|
556
|
+
planSynthesis = step.value ?? "";
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
yield step.value;
|
|
226
560
|
}
|
|
227
|
-
|
|
561
|
+
idealTrace("sprint.planCouncil.after", { runId: ctx.runId, sprintN, planSynthesisLen: planSynthesis.length });
|
|
562
|
+
// Persist so a resumed/retried sprint reuses this exact plan (and target folder).
|
|
563
|
+
await persistSprintPlan(planPath, planSynthesis);
|
|
228
564
|
}
|
|
229
565
|
// P4-C: close the planning phase row before opening implementation.
|
|
230
566
|
yield phaseDone({
|
|
@@ -234,6 +570,7 @@ export async function* runSprint(args) {
|
|
|
234
570
|
startedAt: planStartedAt,
|
|
235
571
|
});
|
|
236
572
|
// ── Step 4: Implement stage — pipe plan through host process loop ─────────
|
|
573
|
+
idealTrace("sprint.implementation.enter", { runId: ctx.runId, sprintN, planSynthesisLen: planSynthesis.length });
|
|
237
574
|
yield { type: "content", content: `\n## Sprint ${sprintN} — Implementation\n` };
|
|
238
575
|
const implPhaseId = `sprint-${sprintN}-implementation`;
|
|
239
576
|
const implStartedAt = Date.now();
|
|
@@ -262,13 +599,28 @@ export async function* runSprint(args) {
|
|
|
262
599
|
subtype: "sprint_stage",
|
|
263
600
|
data: { sprintIndex: sprintN, stage: "implementation", runId: ctx.runId },
|
|
264
601
|
});
|
|
602
|
+
// Defect fix (2026-07-08): the raw plan synthesis is a DECLARATIVE design
|
|
603
|
+
// document ("## Agreed Architecture / Function Signatures / Acceptance
|
|
604
|
+
// Criteria"). Passed verbatim as the orchestrator message it reads as
|
|
605
|
+
// something to discuss, so the impl turn narrated the plan back as markdown
|
|
606
|
+
// (finishReason "stop", zero edits) instead of applying it — observed live on
|
|
607
|
+
// the gsd-core migration. Prepend an explicit execution directive (module-level
|
|
608
|
+
// IMPL_EXECUTION_DIRECTIVE) so the PIL classifier routes it to the
|
|
609
|
+
// implement/edit path, not the respond path.
|
|
265
610
|
// C2: Pre-impl gate — read decisions.lock.md and prepend to implementation prompt.
|
|
266
611
|
// When lock file is missing (greenfield / no council with runDir), pass-through unchanged.
|
|
267
|
-
let implPrompt = planSynthesis;
|
|
612
|
+
let implPrompt = planSynthesis.trim() ? IMPL_EXECUTION_DIRECTIVE + planSynthesis : planSynthesis;
|
|
268
613
|
try {
|
|
269
614
|
const lockContent = await readDecisionsLock(runDir);
|
|
270
615
|
if (lockContent) {
|
|
271
|
-
|
|
616
|
+
// Prepend the lock to the DIRECTIVE-carrying implPrompt, NOT the bare
|
|
617
|
+
// planSynthesis. Passing planSynthesis here (the original 2026-07-08 C2
|
|
618
|
+
// gate bug) silently dropped IMPL_EXECUTION_DIRECTIVE + its
|
|
619
|
+
// SPRINT_EXECUTION_MARKER, so every council-backed sprint (a lock always
|
|
620
|
+
// exists once the council ran) reached the orchestrator as a bare design
|
|
621
|
+
// doc: the impl turn narrated the plan instead of executing it, classified
|
|
622
|
+
// taskType=null (4_096 output cap), then wedged on finishReason:"length".
|
|
623
|
+
implPrompt = prependDecisionsLock(implPrompt, lockContent);
|
|
272
624
|
yield {
|
|
273
625
|
type: "content",
|
|
274
626
|
content: "\n> [decisions.lock.md] Locked decisions prepended to implementation prompt.\n",
|
|
@@ -278,16 +630,66 @@ export async function* runSprint(args) {
|
|
|
278
630
|
catch {
|
|
279
631
|
/* fail-open — lock read failure must not block implementation */
|
|
280
632
|
}
|
|
633
|
+
// Wave 3 (2026-07-08): the impl turn was blind to files a prior sprint/run had
|
|
634
|
+
// already created, so it re-created them from scratch. Tell it which of the
|
|
635
|
+
// plan's OWN named target files already exist on disk so it reads + continues
|
|
636
|
+
// them instead of re-scaffolding. Empty on greenfield (nothing exists yet).
|
|
637
|
+
if (planSynthesis.trim()) {
|
|
638
|
+
const existingTargets = await detectExistingPlanTargets(planSynthesis, cwd);
|
|
639
|
+
if (existingTargets.length > 0) {
|
|
640
|
+
implPrompt = `${implPrompt}\n\n--- FILES ALREADY PRESENT ON DISK (prior-sprint work — READ and CONTINUE these; do NOT recreate them from scratch) ---\n${existingTargets
|
|
641
|
+
.map((f) => `- ${f}`)
|
|
642
|
+
.join("\n")}\n`;
|
|
643
|
+
yield {
|
|
644
|
+
type: "content",
|
|
645
|
+
content: `\n> [continuation] ${existingTargets.length} plan target file(s) already exist — instructed to continue, not recreate.\n`,
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
}
|
|
281
649
|
let implError = null;
|
|
282
650
|
if (ctx.processMessageFn && implPrompt.trim()) {
|
|
651
|
+
const useIsolated = shouldUseIsolatedImpl(!!ctx.runIsolatedTask);
|
|
283
652
|
try {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
653
|
+
if (useIsolated && ctx.runIsolatedTask) {
|
|
654
|
+
// ISOLATED path — run the sprint plan in a fresh, budget-capped child
|
|
655
|
+
// context that does NOT inherit the council-debate history. This is the
|
|
656
|
+
// fix for the ctx-overflow wedge: the sub-agent starts near-empty, has
|
|
657
|
+
// full tool access (edit/bash), compacts independently in-loop, and
|
|
658
|
+
// returns a compact ToolResult (its tool clutter is absorbed, not piped
|
|
659
|
+
// into the parent). No stream to watchdog — the sub-agent has its own
|
|
660
|
+
// stall + no-forward-progress guards (stall-watchdog.ts).
|
|
661
|
+
yield {
|
|
662
|
+
type: "content",
|
|
663
|
+
content: "\n> [isolated impl] Executing the sprint in a fresh sub-agent context " +
|
|
664
|
+
"(anti-overflow: does not inherit the debate history).\n",
|
|
665
|
+
};
|
|
666
|
+
const result = await ctx.runIsolatedTask({
|
|
667
|
+
agent: "general",
|
|
668
|
+
description: `Sprint ${sprintN} implementation`,
|
|
669
|
+
prompt: implPrompt,
|
|
670
|
+
modelId: ctx.sessionModelId,
|
|
671
|
+
});
|
|
672
|
+
if (!result.success) {
|
|
673
|
+
implError = result.error?.trim() || "isolated implementation task failed";
|
|
674
|
+
}
|
|
675
|
+
else if (result.output?.trim()) {
|
|
676
|
+
yield { type: "content", content: `\n${result.output.trim()}\n` };
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
const implGen = ctx.processMessageFn(implPrompt);
|
|
681
|
+
// Guard the impl turn with an idle-chunk watchdog so a post-finish
|
|
682
|
+
// orchestrator hang surfaces as a phaseError instead of a silent wedge.
|
|
683
|
+
for await (const chunk of withImplIdleWatchdog(implGen, getImplIdleTimeoutMs(), sprintN)) {
|
|
684
|
+
yield chunk;
|
|
685
|
+
}
|
|
287
686
|
}
|
|
288
687
|
}
|
|
289
688
|
catch (e) {
|
|
290
689
|
implError = e instanceof Error ? e.message : String(e);
|
|
690
|
+
// No-Silent-Catch: the finally below surfaces a phaseError chunk, but log
|
|
691
|
+
// here too so the hang/failure is diagnosable from stderr / MUONROI logs.
|
|
692
|
+
console.error(`[sprint-runner] implementation stage failed (sprint ${sprintN}, run ${ctx.runId}): ${implError}`);
|
|
291
693
|
}
|
|
292
694
|
finally {
|
|
293
695
|
// A3 FIX: phaseDone for implementation MUST always fire, even when
|
|
@@ -328,6 +730,76 @@ export async function* runSprint(args) {
|
|
|
328
730
|
if (implError) {
|
|
329
731
|
throw new Error(implError);
|
|
330
732
|
}
|
|
733
|
+
// ── Step 4b: 4A completeness re-check ─────────────────────────────────────
|
|
734
|
+
// The impl turn can "finish" (finishReason stop) with plan action items
|
|
735
|
+
// unaddressed — narrated but not applied. Rather than an unconditional
|
|
736
|
+
// (2-3x cost) reviewer pass, spend ONE focused follow-up turn ONLY when
|
|
737
|
+
// plan-named target files are provably still missing on disk. No missing
|
|
738
|
+
// targets ⇒ no extra turn (the resume/migration case where the targets already
|
|
739
|
+
// exist is a no-op). A re-check failure never fails the sprint — the primary
|
|
740
|
+
// impl already succeeded and verify/tests are the real gate.
|
|
741
|
+
if (ctx.processMessageFn && getImplRecheckEnabled() && planSynthesis.trim()) {
|
|
742
|
+
const missing = await computeMissingPlanTargets(planSynthesis, cwd);
|
|
743
|
+
if (missing.length > 0) {
|
|
744
|
+
idealTrace("sprint.implementation.recheck", { runId: ctx.runId, sprintN, missing: missing.length });
|
|
745
|
+
const recheckPhaseId = `sprint-${sprintN}-impl-recheck`;
|
|
746
|
+
const recheckStartedAt = Date.now();
|
|
747
|
+
yield phaseStart({
|
|
748
|
+
phaseId: recheckPhaseId,
|
|
749
|
+
kind: "sprint_stage",
|
|
750
|
+
label: `Sprint ${sprintN} — Completeness re-check`,
|
|
751
|
+
detail: `${missing.length} plan target(s) still missing — finishing`,
|
|
752
|
+
startedAt: recheckStartedAt,
|
|
753
|
+
});
|
|
754
|
+
const recheckPrompt = "The sprint plan named these target files but they DO NOT exist on disk yet — the sprint is NOT " +
|
|
755
|
+
"finished. Create/complete each one NOW using your file-edit tools. Do NOT explain or re-plan; " +
|
|
756
|
+
"make the edits.\n" +
|
|
757
|
+
missing.map((f) => `- ${f}`).join("\n") +
|
|
758
|
+
"\n";
|
|
759
|
+
let recheckErr = null;
|
|
760
|
+
try {
|
|
761
|
+
const recheckGen = ctx.processMessageFn(recheckPrompt);
|
|
762
|
+
for await (const chunk of withImplIdleWatchdog(recheckGen, getImplIdleTimeoutMs(), sprintN)) {
|
|
763
|
+
yield chunk;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
catch (e) {
|
|
767
|
+
recheckErr = e instanceof Error ? e.message : String(e);
|
|
768
|
+
console.error(`[sprint-runner] impl completeness re-check failed (sprint ${sprintN}, run ${ctx.runId}): ${recheckErr}`);
|
|
769
|
+
}
|
|
770
|
+
finally {
|
|
771
|
+
if (recheckErr) {
|
|
772
|
+
yield phaseError({
|
|
773
|
+
phaseId: recheckPhaseId,
|
|
774
|
+
kind: "sprint_stage",
|
|
775
|
+
label: `Sprint ${sprintN} — Completeness re-check`,
|
|
776
|
+
startedAt: recheckStartedAt,
|
|
777
|
+
errorMessage: recheckErr,
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
yield phaseDone({
|
|
782
|
+
phaseId: recheckPhaseId,
|
|
783
|
+
kind: "sprint_stage",
|
|
784
|
+
label: `Sprint ${sprintN} — Completeness re-check`,
|
|
785
|
+
startedAt: recheckStartedAt,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
const stillMissing = await computeMissingPlanTargets(planSynthesis, cwd);
|
|
790
|
+
idealTrace("sprint.implementation.recheck.after", {
|
|
791
|
+
runId: ctx.runId,
|
|
792
|
+
sprintN,
|
|
793
|
+
stillMissing: stillMissing.length,
|
|
794
|
+
});
|
|
795
|
+
if (stillMissing.length > 0) {
|
|
796
|
+
yield {
|
|
797
|
+
type: "content",
|
|
798
|
+
content: `\n> [completeness] ${stillMissing.length} plan target(s) still missing after re-check — deferring to verify.\n`,
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
331
803
|
// ── Step 5: Verify stage ──────────────────────────────────────────────────
|
|
332
804
|
yield { type: "content", content: `\n## Sprint ${sprintN} — Verification\n` };
|
|
333
805
|
const verifyPhaseId = `sprint-${sprintN}-verification`;
|
|
@@ -351,7 +823,30 @@ export async function* runSprint(args) {
|
|
|
351
823
|
subtype: "sprint_stage",
|
|
352
824
|
data: { sprintIndex: sprintN, stage: "verification", runId: ctx.runId },
|
|
353
825
|
});
|
|
354
|
-
|
|
826
|
+
// A — "Skip verify" recovery option: the user chose to bypass a broken verify
|
|
827
|
+
// stage (e.g. shuru sandbox unavailable on Windows that hangs the watchdog
|
|
828
|
+
// every sprint). Treat verify as a PASS with an explicit synthetic output so
|
|
829
|
+
// the done-gate is not blocked, and log loudly so the bypass is auditable.
|
|
830
|
+
// The env var is set by the recovery-card handler and reset on the next fresh
|
|
831
|
+
// `/ideal "<idea>"` start, so a new run re-enables verification.
|
|
832
|
+
const skipVerify = process.env.MUONROI_SPRINT_SKIP_VERIFY === "1";
|
|
833
|
+
let verifyResult;
|
|
834
|
+
if (skipVerify) {
|
|
835
|
+
console.error(`[sprint-runner] MUONROI_SPRINT_SKIP_VERIFY=1 — verify stage bypassed (sprint ${sprintN}, run ${ctx.runId})`);
|
|
836
|
+
verifyResult = {
|
|
837
|
+
success: true,
|
|
838
|
+
// Include the canonical PASS marker so parseVerifyResult → PASS (the user
|
|
839
|
+
// explicitly opted to treat verify as satisfied for this recovery).
|
|
840
|
+
output: `${VERIFY_PASS_MARKER}\nverify skipped by user recovery choice (MUONROI_SPRINT_SKIP_VERIFY=1)`,
|
|
841
|
+
};
|
|
842
|
+
yield {
|
|
843
|
+
type: "content",
|
|
844
|
+
content: `\n> [skip-verify] Verify stage bypassed for sprint ${sprintN} (user recovery choice).\n`,
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
verifyResult = await runVerifyWithWatchdog(verifyAgent, ctx.runId, sprintN);
|
|
849
|
+
}
|
|
355
850
|
yield phaseDone({
|
|
356
851
|
phaseId: verifyPhaseId,
|
|
357
852
|
kind: "sprint_stage",
|
|
@@ -524,8 +1019,39 @@ export async function* runSprint(args) {
|
|
|
524
1019
|
await appendIteration(ctx.flowDir, ctx.runId, iter);
|
|
525
1020
|
// Update Resume Digest in state.md so PIL Layer 5 + future resume can pick it up
|
|
526
1021
|
const stateMap = (await readArtifact(runDir, "state.md")) ?? { preamble: "", sections: new Map() };
|
|
527
|
-
stateMap.sections.set("Resume Digest",
|
|
1022
|
+
stateMap.sections.set("Resume Digest", renderResumeDigest({
|
|
1023
|
+
stage: `sprint-${sprintN}`,
|
|
1024
|
+
lastCompleted: `sprint-${sprintN} ${iter.stage}`,
|
|
1025
|
+
nextAction: verdict.pass
|
|
1026
|
+
? "Definition-of-Done met — advance to the next phase or ship"
|
|
1027
|
+
: `Retry sprint ${sprintN}: ${verdict.failedCondition ?? "continue toward Definition-of-Done"}`,
|
|
1028
|
+
sprintN,
|
|
1029
|
+
score: verdict.score,
|
|
1030
|
+
verify: verifyVerdict,
|
|
1031
|
+
updatedAt: new Date().toISOString(),
|
|
1032
|
+
}));
|
|
528
1033
|
await writeArtifact(runDir, "state.md", stateMap);
|
|
1034
|
+
// Part A — persist a first-class per-sprint outcome record + verify report so
|
|
1035
|
+
// `/ideal review` and cross-run memory render real sprint history (not just
|
|
1036
|
+
// the fire-and-forget EE boundary event, which leaves nothing on disk).
|
|
1037
|
+
try {
|
|
1038
|
+
await writeSprintOutcome(ctx.flowDir, ctx.runId, {
|
|
1039
|
+
sprintN,
|
|
1040
|
+
pass: verdict.pass,
|
|
1041
|
+
score: verdict.score,
|
|
1042
|
+
verify: verifyVerdict,
|
|
1043
|
+
failedCondition: verdict.failedCondition ?? undefined,
|
|
1044
|
+
criteriaMet: iter.criteriaMet,
|
|
1045
|
+
criteriaPartial: iter.criteriaPartial,
|
|
1046
|
+
criteriaUnmet: iter.criteriaUnmet,
|
|
1047
|
+
finishedAt: new Date().toISOString(),
|
|
1048
|
+
});
|
|
1049
|
+
const verifyReport = (verifyResult.error?.trim() ? verifyResult.error : (verifyResult.output ?? "")).trim() || "(no verify output)";
|
|
1050
|
+
await writeSprintVerify(ctx.flowDir, ctx.runId, sprintN, `# Sprint ${sprintN} verify — ${verifyVerdict} (score ${verdict.score.toFixed(2)})\n\n\`\`\`\n${verifyReport.slice(0, 8000)}\n\`\`\`\n`);
|
|
1051
|
+
}
|
|
1052
|
+
catch {
|
|
1053
|
+
/* non-critical — sprint artifacts are a review surface, never derail the loop */
|
|
1054
|
+
}
|
|
529
1055
|
// Emit ProgressSnapshot on sprint boundary so the user sees rolling progress.
|
|
530
1056
|
// Wrapped in try/catch — never crash sprint-runner because the snapshot failed.
|
|
531
1057
|
try {
|
|
@@ -557,6 +1083,23 @@ export async function* runSprint(args) {
|
|
|
557
1083
|
}).catch(() => {
|
|
558
1084
|
/* EE failures must not derail the loop */
|
|
559
1085
|
});
|
|
1086
|
+
// Part C — write-during-execution: persist this sprint's outcome as a NEW
|
|
1087
|
+
// workflow_sprint experience (not just reinforcement) so a later sprint in the
|
|
1088
|
+
// SAME run — or a future run — can recall "how this kind of sprint went".
|
|
1089
|
+
// gate-on-outcome (Kill #4): fired here, AFTER verify+judge produced a verdict.
|
|
1090
|
+
fireAndForgetWorkflowEvent({
|
|
1091
|
+
kind: "sprint-execution",
|
|
1092
|
+
phaseRef: `runs/${ctx.runId}#sprint-${sprintN}`,
|
|
1093
|
+
sessionId: ctx.runId,
|
|
1094
|
+
text: `Sprint ${sprintN} ${verdict.pass ? "passed" : "failed"} (score ${verdict.score.toFixed(2)}, verify ${verifyVerdict})${verdict.failedCondition ? ` — ${verdict.failedCondition}` : ""}`,
|
|
1095
|
+
payload: {
|
|
1096
|
+
sprintN,
|
|
1097
|
+
pass: verdict.pass,
|
|
1098
|
+
score: verdict.score,
|
|
1099
|
+
verify: verifyVerdict,
|
|
1100
|
+
failedCondition: verdict.failedCondition ?? null,
|
|
1101
|
+
},
|
|
1102
|
+
});
|
|
560
1103
|
// ── Step 9: If not done, surface continue-feedback to the user ───────────
|
|
561
1104
|
if (!verdict.pass) {
|
|
562
1105
|
const fb = buildContinueFeedback(verdict, verifyResult, currentCriteria);
|