session-orchestrator 3.16.0
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/.claude-plugin/marketplace.json +29 -0
- package/.claude-plugin/plugin.json +18 -0
- package/.codex-plugin/agents/explorer.toml +14 -0
- package/.codex-plugin/agents/session-reviewer.toml +23 -0
- package/.codex-plugin/agents/wave-worker.toml +15 -0
- package/.codex-plugin/config.toml +20 -0
- package/.codex-plugin/plugin.json +37 -0
- package/.cursor/rules/000-session-orchestrator.mdc +73 -0
- package/.cursor/rules/010-session-workflow.mdc +170 -0
- package/.cursor/rules/020-quality-gates.mdc +128 -0
- package/.cursor/rules/030-wave-execution.mdc +216 -0
- package/.cursor/rules/040-discovery.mdc +242 -0
- package/.cursor/rules/050-plan.mdc +235 -0
- package/.cursor/rules/060-evolve.mdc +232 -0
- package/.cursor/rules/070-gitlab-ops.mdc +246 -0
- package/.cursor/rules/080-ecosystem-health.mdc +145 -0
- package/.mcp.json +8 -0
- package/CHANGELOG.md +1544 -0
- package/LICENSE +21 -0
- package/NOTICE +64 -0
- package/README.md +242 -0
- package/SECURITY.md +90 -0
- package/agents/AGENTS.md +136 -0
- package/agents/analyst.md +99 -0
- package/agents/architect-reviewer.md +93 -0
- package/agents/code-implementer.md +106 -0
- package/agents/db-specialist.md +104 -0
- package/agents/dialectic-deriver.md +139 -0
- package/agents/docs-writer.md +113 -0
- package/agents/eval-judge.md +146 -0
- package/agents/memory-proposal-collector.md +297 -0
- package/agents/qa-strategist.md +102 -0
- package/agents/schemas/analyst.schema.json +46 -0
- package/agents/schemas/architect-reviewer.schema.json +50 -0
- package/agents/schemas/code-implementer.schema.json +61 -0
- package/agents/schemas/db-specialist.schema.json +80 -0
- package/agents/schemas/docs-writer.schema.json +56 -0
- package/agents/schemas/persona-panel-sidecar.schema.json +245 -0
- package/agents/schemas/qa-strategist.schema.json +46 -0
- package/agents/schemas/security-reviewer.schema.json +86 -0
- package/agents/schemas/session-reviewer.schema.json +69 -0
- package/agents/schemas/test-writer.schema.json +69 -0
- package/agents/schemas/ui-developer.schema.json +90 -0
- package/agents/schemas/ux-evaluator.schema.json +51 -0
- package/agents/security-reviewer.md +236 -0
- package/agents/session-reviewer.md +201 -0
- package/agents/skill-applied-judge.md +122 -0
- package/agents/test-writer.md +123 -0
- package/agents/ui-developer.md +109 -0
- package/agents/ux-evaluator.md +161 -0
- package/assets/icon.svg +11 -0
- package/assets/og-card.png +0 -0
- package/assets/og-card.svg +47 -0
- package/commands/autopilot-multi.md +74 -0
- package/commands/autopilot.md +80 -0
- package/commands/bootstrap.md +56 -0
- package/commands/brainstorm.md +48 -0
- package/commands/close.md +24 -0
- package/commands/debug.md +36 -0
- package/commands/discovery.md +32 -0
- package/commands/dispatcher.md +59 -0
- package/commands/eval.md +28 -0
- package/commands/evolve.md +10 -0
- package/commands/go.md +41 -0
- package/commands/grill.md +45 -0
- package/commands/harness-audit.md +26 -0
- package/commands/memory-cleanup.md +25 -0
- package/commands/persona-panel.md +121 -0
- package/commands/plan.md +15 -0
- package/commands/portfolio.md +97 -0
- package/commands/reconcile.md +23 -0
- package/commands/repo-audit.md +24 -0
- package/commands/session.md +30 -0
- package/commands/spinout.md +15 -0
- package/commands/sunset-review.md +27 -0
- package/commands/templates-ack.md +96 -0
- package/commands/test.md +97 -0
- package/docs/README.md +105 -0
- package/docs/USER-GUIDE.md +1403 -0
- package/docs/ci-setup.md +81 -0
- package/docs/codex-setup.md +142 -0
- package/docs/components.md +74 -0
- package/docs/cursor-setup.md +104 -0
- package/docs/events-schema.md +81 -0
- package/docs/migration-v3.md +148 -0
- package/docs/owner-config-schema.md +154 -0
- package/docs/persona-panel.md +433 -0
- package/docs/pi-setup.md +115 -0
- package/docs/plugin-architecture-v3.md +296 -0
- package/docs/pm-skills-marketplace.md +114 -0
- package/docs/policy-cache-validation-2026-04-28.md +118 -0
- package/docs/rule-authoring.md +316 -0
- package/docs/session-config-reference.md +1439 -0
- package/docs/session-config-template.md +961 -0
- package/docs/vault-docs-architecture.md +297 -0
- package/hooks/_lib/lock-bootstrap.mjs +272 -0
- package/hooks/_lib/lock-reconcile.mjs +93 -0
- package/hooks/_lib/profile-gate.mjs +95 -0
- package/hooks/_lib/transcript-history.mjs +211 -0
- package/hooks/agent-teams-h3-test.sh +362 -0
- package/hooks/config-protection.mjs +0 -0
- package/hooks/cwd-change-restore.mjs +131 -0
- package/hooks/enforce-commands.mjs +179 -0
- package/hooks/enforce-scope.mjs +273 -0
- package/hooks/hooks-codex.json +60 -0
- package/hooks/hooks-cursor.json +15 -0
- package/hooks/hooks-pi.json +115 -0
- package/hooks/hooks.json +215 -0
- package/hooks/loop-guard.mjs +260 -0
- package/hooks/on-session-end.mjs +217 -0
- package/hooks/on-session-start.mjs +660 -0
- package/hooks/on-stop.mjs +294 -0
- package/hooks/operator-steer.mjs +64 -0
- package/hooks/post-edit-validate.mjs +225 -0
- package/hooks/post-subagent-discovery-validator.mjs +398 -0
- package/hooks/post-tool-batch-wave-signal.mjs +328 -0
- package/hooks/post-tool-failure-corrective-context.mjs +248 -0
- package/hooks/post-tooluse-frontend-slop.mjs +184 -0
- package/hooks/pre-bash-destructive-guard.mjs +515 -0
- package/hooks/pre-bash-memory-propose-audit.mjs +206 -0
- package/hooks/pre-bash-staging-fence.mjs +223 -0
- package/hooks/pre-bash-templates-first.mjs +404 -0
- package/hooks/run-node.sh +72 -0
- package/hooks/skill-invocation-telemetry.mjs +99 -0
- package/hooks/subagent-telemetry.mjs +249 -0
- package/hooks/wave-scope-commit-guard.mjs +191 -0
- package/monitors/monitors.json +14 -0
- package/output-styles/finding-report.md +48 -0
- package/output-styles/session-report.md +53 -0
- package/output-styles/wave-summary.md +38 -0
- package/package.json +94 -0
- package/pi/extensions/session-orchestrator.ts +25 -0
- package/pi/prompts/autopilot-multi.md +12 -0
- package/pi/prompts/autopilot.md +12 -0
- package/pi/prompts/bootstrap.md +12 -0
- package/pi/prompts/brainstorm.md +12 -0
- package/pi/prompts/close.md +11 -0
- package/pi/prompts/debug.md +12 -0
- package/pi/prompts/discovery.md +12 -0
- package/pi/prompts/dispatcher.md +12 -0
- package/pi/prompts/eval.md +12 -0
- package/pi/prompts/evolve.md +12 -0
- package/pi/prompts/go.md +12 -0
- package/pi/prompts/grill.md +12 -0
- package/pi/prompts/harness-audit.md +12 -0
- package/pi/prompts/memory-cleanup.md +12 -0
- package/pi/prompts/persona-panel.md +12 -0
- package/pi/prompts/plan.md +12 -0
- package/pi/prompts/portfolio.md +12 -0
- package/pi/prompts/reconcile.md +12 -0
- package/pi/prompts/repo-audit.md +12 -0
- package/pi/prompts/session.md +12 -0
- package/pi/prompts/spinout.md +12 -0
- package/pi/prompts/sunset-review.md +12 -0
- package/pi/prompts/templates-ack.md +12 -0
- package/pi/prompts/test.md +12 -0
- package/rules/_index.md +51 -0
- package/rules/always-on/commit-discipline.md +26 -0
- package/rules/always-on/npm-quality-gates.md +26 -0
- package/rules/always-on/parallel-sessions.md +43 -0
- package/rules/opt-in-domain/prompt-caching.md +270 -0
- package/rules/opt-in-stack/backend-data.md +188 -0
- package/rules/opt-in-stack/backend.md +390 -0
- package/rules/opt-in-stack/frontend.md +98 -0
- package/rules/opt-in-stack/security-web.md +194 -0
- package/rules/opt-in-stack/swift.md +65 -0
- package/scripts/archive-closed-prds.mjs +416 -0
- package/scripts/autopilot-multi.mjs +802 -0
- package/scripts/autopilot.mjs +383 -0
- package/scripts/backfill-abandoned-sessions.mjs +265 -0
- package/scripts/backfill-learnings-expires.mjs +196 -0
- package/scripts/backfill-learnings.mjs +203 -0
- package/scripts/backfill-sessions.mjs +282 -0
- package/scripts/check-doc-consistency.sh +279 -0
- package/scripts/check-package-manager.mjs +445 -0
- package/scripts/ci/assert-vitest-green.mjs +267 -0
- package/scripts/codex-install.mjs +435 -0
- package/scripts/compute-grounding-injection.sh +186 -0
- package/scripts/cursor-install.mjs +113 -0
- package/scripts/dialectic-deriver.mjs +573 -0
- package/scripts/emit-event.mjs +160 -0
- package/scripts/emit-session.mjs +212 -0
- package/scripts/eval-session.mjs +262 -0
- package/scripts/export-hw-learnings.mjs +437 -0
- package/scripts/gc-stale-worktrees.mjs +666 -0
- package/scripts/generate-pi-prompts.mjs +127 -0
- package/scripts/harness-audit.mjs +287 -0
- package/scripts/lib/agent-frontmatter.mjs +266 -0
- package/scripts/lib/agent-output-schema.mjs +166 -0
- package/scripts/lib/agent-status.mjs +303 -0
- package/scripts/lib/ajv-loader.mjs +34 -0
- package/scripts/lib/auto-dialectic.mjs +382 -0
- package/scripts/lib/auto-dream.mjs +471 -0
- package/scripts/lib/autonomy/suitability.mjs +212 -0
- package/scripts/lib/autopilot/dep-graph.mjs +417 -0
- package/scripts/lib/autopilot/durable-telemetry.mjs +121 -0
- package/scripts/lib/autopilot/flags.mjs +104 -0
- package/scripts/lib/autopilot/kill-switches.mjs +174 -0
- package/scripts/lib/autopilot/loop.mjs +320 -0
- package/scripts/lib/autopilot/mr-draft.mjs +520 -0
- package/scripts/lib/autopilot/multi-killswitch.mjs +184 -0
- package/scripts/lib/autopilot/recent-runs.mjs +106 -0
- package/scripts/lib/autopilot/stall-sampler.mjs +97 -0
- package/scripts/lib/autopilot/telemetry.mjs +224 -0
- package/scripts/lib/autopilot/worktree-pipeline.mjs +605 -0
- package/scripts/lib/autopilot-telemetry.mjs +11 -0
- package/scripts/lib/autopilot.mjs +39 -0
- package/scripts/lib/backlog-scan.mjs +179 -0
- package/scripts/lib/bootstrap-lock-freshness.mjs +260 -0
- package/scripts/lib/bootstrap-lock-refresh.mjs +186 -0
- package/scripts/lib/build-live-signals.mjs +150 -0
- package/scripts/lib/ci-status-banner.mjs +425 -0
- package/scripts/lib/claude-md-budget-lint.mjs +246 -0
- package/scripts/lib/cli-flags.mjs +158 -0
- package/scripts/lib/codex/plugin-contract.mjs +610 -0
- package/scripts/lib/cold-start-detector.mjs +240 -0
- package/scripts/lib/command-blocker.mjs +458 -0
- package/scripts/lib/common.mjs +333 -0
- package/scripts/lib/config/auto-dream.mjs +77 -0
- package/scripts/lib/config/block-header.mjs +94 -0
- package/scripts/lib/config/broken-window.mjs +114 -0
- package/scripts/lib/config/coercers.mjs +248 -0
- package/scripts/lib/config/cold-start.mjs +92 -0
- package/scripts/lib/config/config-protection.mjs +120 -0
- package/scripts/lib/config/cross-repo.mjs +104 -0
- package/scripts/lib/config/custom-phases.mjs +213 -0
- package/scripts/lib/config/dialectic.mjs +92 -0
- package/scripts/lib/config/discovery-validator.mjs +75 -0
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +240 -0
- package/scripts/lib/config/dispatcher-autonomy.mjs +152 -0
- package/scripts/lib/config/docs-orchestrator.mjs +90 -0
- package/scripts/lib/config/docs-staleness.mjs +96 -0
- package/scripts/lib/config/drift-check.mjs +155 -0
- package/scripts/lib/config/eval.mjs +130 -0
- package/scripts/lib/config/events-rotation.mjs +74 -0
- package/scripts/lib/config/evolve.mjs +308 -0
- package/scripts/lib/config/frontend-slop-hook.mjs +104 -0
- package/scripts/lib/config/gitlab-portfolio.mjs +150 -0
- package/scripts/lib/config/handover-gate.mjs +106 -0
- package/scripts/lib/config/host-paths.mjs +76 -0
- package/scripts/lib/config/io.mjs +54 -0
- package/scripts/lib/config/loop-guard.mjs +117 -0
- package/scripts/lib/config/memory.mjs +150 -0
- package/scripts/lib/config/persona-gate-wave.mjs +258 -0
- package/scripts/lib/config/reconcile.mjs +205 -0
- package/scripts/lib/config/section-extractor.mjs +100 -0
- package/scripts/lib/config/skill-evolution.mjs +112 -0
- package/scripts/lib/config/slopcheck.mjs +99 -0
- package/scripts/lib/config/state-md-lock.mjs +83 -0
- package/scripts/lib/config/templates-first.mjs +94 -0
- package/scripts/lib/config/test.mjs +113 -0
- package/scripts/lib/config/vault-integration.mjs +201 -0
- package/scripts/lib/config/vault-mirror-quality.mjs +99 -0
- package/scripts/lib/config/vault-staleness.mjs +84 -0
- package/scripts/lib/config/vault-sync.mjs +96 -0
- package/scripts/lib/config/verification-auto-fix.mjs +84 -0
- package/scripts/lib/config/wave-reviewers.mjs +133 -0
- package/scripts/lib/config-schema.mjs +345 -0
- package/scripts/lib/config.mjs +474 -0
- package/scripts/lib/convergence-monitor.mjs +389 -0
- package/scripts/lib/coordinator-snapshot.mjs +371 -0
- package/scripts/lib/crypto-digest-utils.mjs +91 -0
- package/scripts/lib/discovery/helpers.mjs +127 -0
- package/scripts/lib/discovery/triage-state.mjs +279 -0
- package/scripts/lib/dispatcher/cli.mjs +257 -0
- package/scripts/lib/dispatcher/enumerate.mjs +243 -0
- package/scripts/lib/dispatcher/rank.mjs +363 -0
- package/scripts/lib/ecosystem-health.mjs +224 -0
- package/scripts/lib/ecosystem-wizard/ci-detector.mjs +18 -0
- package/scripts/lib/ecosystem-wizard/config-parser.mjs +54 -0
- package/scripts/lib/ecosystem-wizard/config-writer.mjs +287 -0
- package/scripts/lib/ecosystem-wizard/package-manager-detector.mjs +42 -0
- package/scripts/lib/ecosystem-wizard/wizard-prompt.mjs +246 -0
- package/scripts/lib/ecosystem-wizard.mjs +48 -0
- package/scripts/lib/env-check.mjs +89 -0
- package/scripts/lib/eval/engine.mjs +605 -0
- package/scripts/lib/eval/judge.mjs +433 -0
- package/scripts/lib/eval/report.mjs +367 -0
- package/scripts/lib/eval/schema.mjs +618 -0
- package/scripts/lib/eval/session-resolve.mjs +137 -0
- package/scripts/lib/eval/sink.mjs +77 -0
- package/scripts/lib/events-rotation.mjs +86 -0
- package/scripts/lib/events-schema.mjs +81 -0
- package/scripts/lib/events.mjs +80 -0
- package/scripts/lib/evolve/autonomy-verdict.mjs +461 -0
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +293 -0
- package/scripts/lib/exclusivity-matrix.mjs +68 -0
- package/scripts/lib/fetch-baseline.mjs +311 -0
- package/scripts/lib/file-lock.mjs +512 -0
- package/scripts/lib/frontend-detect/detect.mjs +138 -0
- package/scripts/lib/frontend-detect/rules.mjs +295 -0
- package/scripts/lib/frontmatter-guard.mjs +241 -0
- package/scripts/lib/gates/echo-stub-detect.mjs +39 -0
- package/scripts/lib/gates/gate-baseline.mjs +42 -0
- package/scripts/lib/gates/gate-full.mjs +85 -0
- package/scripts/lib/gates/gate-helpers.mjs +231 -0
- package/scripts/lib/gates/gate-incremental.mjs +76 -0
- package/scripts/lib/gates/gate-per-file.mjs +55 -0
- package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +447 -0
- package/scripts/lib/gitlab-portfolio/aggregator.mjs +383 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +428 -0
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +289 -0
- package/scripts/lib/gitlab-portfolio/vcs-detect.mjs +182 -0
- package/scripts/lib/handover-gate.mjs +222 -0
- package/scripts/lib/hardening.mjs +43 -0
- package/scripts/lib/hardware-pattern-detector.mjs +238 -0
- package/scripts/lib/harness-audit/categories/category1.mjs +123 -0
- package/scripts/lib/harness-audit/categories/category2.mjs +145 -0
- package/scripts/lib/harness-audit/categories/category3.mjs +143 -0
- package/scripts/lib/harness-audit/categories/category4.mjs +202 -0
- package/scripts/lib/harness-audit/categories/category5.mjs +152 -0
- package/scripts/lib/harness-audit/categories/category6.mjs +211 -0
- package/scripts/lib/harness-audit/categories/category7.mjs +125 -0
- package/scripts/lib/harness-audit/categories/category8.mjs +328 -0
- package/scripts/lib/harness-audit/categories/category9.mjs +294 -0
- package/scripts/lib/harness-audit/categories/helpers.mjs +165 -0
- package/scripts/lib/harness-audit/categories.mjs +19 -0
- package/scripts/lib/historical-guard.mjs +15 -0
- package/scripts/lib/host-identity.mjs +262 -0
- package/scripts/lib/instruction-budget-guard.mjs +332 -0
- package/scripts/lib/io.mjs +304 -0
- package/scripts/lib/issue-close-strip-labels.mjs +161 -0
- package/scripts/lib/language-mappers/README.md +57 -0
- package/scripts/lib/language-mappers/index.mjs +165 -0
- package/scripts/lib/language-mappers/markdown.mjs +149 -0
- package/scripts/lib/language-mappers/python.mjs +249 -0
- package/scripts/lib/language-mappers/swift.mjs +201 -0
- package/scripts/lib/language-mappers/typescript.mjs +433 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +164 -0
- package/scripts/lib/learnings/filters.mjs +43 -0
- package/scripts/lib/learnings/io.mjs +255 -0
- package/scripts/lib/learnings/schema.mjs +518 -0
- package/scripts/lib/learnings/surface.mjs +207 -0
- package/scripts/lib/learnings.mjs +42 -0
- package/scripts/lib/lock-reaper.mjs +648 -0
- package/scripts/lib/locks/index.mjs +31 -0
- package/scripts/lib/locks/lock-body.mjs +62 -0
- package/scripts/lib/locks/staging-fence-lock.mjs +267 -0
- package/scripts/lib/locks/state-md-lock.mjs +351 -0
- package/scripts/lib/loop-readiness-banner.mjs +144 -0
- package/scripts/lib/memory-banner.mjs +478 -0
- package/scripts/lib/memory-cleanup/worktree-sweep.mjs +108 -0
- package/scripts/lib/memory-cleanup-stamp.mjs +56 -0
- package/scripts/lib/memory-paths.mjs +31 -0
- package/scripts/lib/memory-proposals/collector.mjs +334 -0
- package/scripts/lib/memory-proposals/schema.mjs +289 -0
- package/scripts/lib/memory-proposals/sink.mjs +507 -0
- package/scripts/lib/memory-proposals/store.mjs +441 -0
- package/scripts/lib/mission-status-schema.mjs +114 -0
- package/scripts/lib/mode-selector/alternatives.mjs +64 -0
- package/scripts/lib/mode-selector/constants.mjs +29 -0
- package/scripts/lib/mode-selector/context-pressure.mjs +157 -0
- package/scripts/lib/mode-selector/rationale.mjs +55 -0
- package/scripts/lib/mode-selector/scoring.mjs +221 -0
- package/scripts/lib/mode-selector-accuracy.mjs +121 -0
- package/scripts/lib/mode-selector.mjs +160 -0
- package/scripts/lib/multi-provider-build/providers.mjs +64 -0
- package/scripts/lib/multi-provider-build/templating.mjs +130 -0
- package/scripts/lib/named-baseline-resolver.mjs +233 -0
- package/scripts/lib/named-vault-resolver.mjs +433 -0
- package/scripts/lib/owner-config/coerce.mjs +29 -0
- package/scripts/lib/owner-config/constants.mjs +21 -0
- package/scripts/lib/owner-config/defaults.mjs +50 -0
- package/scripts/lib/owner-config/error.mjs +19 -0
- package/scripts/lib/owner-config/index.mjs +13 -0
- package/scripts/lib/owner-config/merge.mjs +52 -0
- package/scripts/lib/owner-config/validate.mjs +259 -0
- package/scripts/lib/owner-config-banner.mjs +126 -0
- package/scripts/lib/owner-config-loader.mjs +159 -0
- package/scripts/lib/owner-config.example.yaml +72 -0
- package/scripts/lib/owner-config.mjs +28 -0
- package/scripts/lib/owner-interview.mjs +243 -0
- package/scripts/lib/owner-yaml.mjs +571 -0
- package/scripts/lib/package-manager.mjs +160 -0
- package/scripts/lib/path-utils.mjs +217 -0
- package/scripts/lib/peer-cards/merger.mjs +310 -0
- package/scripts/lib/peer-cards/reader.mjs +125 -0
- package/scripts/lib/peer-cards/schema.mjs +230 -0
- package/scripts/lib/peer-cards/staleness-banner.mjs +86 -0
- package/scripts/lib/peer-cards/writer.mjs +138 -0
- package/scripts/lib/peer-discovery.mjs +200 -0
- package/scripts/lib/persona-panel/catalog-loader.mjs +577 -0
- package/scripts/lib/persona-panel/consolidator.mjs +370 -0
- package/scripts/lib/persona-panel/persona-runner.mjs +375 -0
- package/scripts/lib/persona-panel/threshold.mjs +130 -0
- package/scripts/lib/pi-hook-bridge.mjs +328 -0
- package/scripts/lib/platform.mjs +266 -0
- package/scripts/lib/playwright-driver/runner.mjs +297 -0
- package/scripts/lib/plugin-root.mjs +210 -0
- package/scripts/lib/pre-dispatch-check.mjs +126 -0
- package/scripts/lib/product-repo-detect.mjs +121 -0
- package/scripts/lib/profiles/registry.mjs +176 -0
- package/scripts/lib/profiles/schema.mjs +209 -0
- package/scripts/lib/qg-command-drift-banner.mjs +88 -0
- package/scripts/lib/quality-gate/diagnostics.mjs +92 -0
- package/scripts/lib/quality-gate.mjs +536 -0
- package/scripts/lib/quality-gates-cache.mjs +228 -0
- package/scripts/lib/quality-gates-policy.mjs +95 -0
- package/scripts/lib/recommendations-v0.mjs +156 -0
- package/scripts/lib/reconcile/eligibility.mjs +203 -0
- package/scripts/lib/reconcile/emitter.mjs +244 -0
- package/scripts/lib/reconcile/engine.mjs +412 -0
- package/scripts/lib/reconcile/idempotency.mjs +239 -0
- package/scripts/lib/reconcile/renderer.mjs +211 -0
- package/scripts/lib/reconcile/writer.mjs +293 -0
- package/scripts/lib/reconcile-nudge-banner.mjs +284 -0
- package/scripts/lib/resource-probe/evaluate.mjs +190 -0
- package/scripts/lib/resource-probe/parsers.mjs +181 -0
- package/scripts/lib/resource-probe/probe-platform.mjs +300 -0
- package/scripts/lib/resource-probe.mjs +95 -0
- package/scripts/lib/rule-loader.mjs +552 -0
- package/scripts/lib/rules-sync.mjs +439 -0
- package/scripts/lib/scope-gate.mjs +496 -0
- package/scripts/lib/session-close-backfill.mjs +539 -0
- package/scripts/lib/session-discovery.mjs +256 -0
- package/scripts/lib/session-end/phase-skip.mjs +357 -0
- package/scripts/lib/session-end/worktree-cleanup.mjs +112 -0
- package/scripts/lib/session-id.mjs +362 -0
- package/scripts/lib/session-lock.mjs +703 -0
- package/scripts/lib/session-registry.mjs +355 -0
- package/scripts/lib/session-schema/aliases.mjs +71 -0
- package/scripts/lib/session-schema/constants.mjs +115 -0
- package/scripts/lib/session-schema/normalizer.mjs +66 -0
- package/scripts/lib/session-schema/timestamps.mjs +64 -0
- package/scripts/lib/session-schema/validator.mjs +453 -0
- package/scripts/lib/session-schema.mjs +71 -0
- package/scripts/lib/session-token-rollup.mjs +137 -0
- package/scripts/lib/sessions-staleness-banner.mjs +247 -0
- package/scripts/lib/skill-evolution/blast-radius-classifier.mjs +114 -0
- package/scripts/lib/skill-evolution/candidate-intake.mjs +270 -0
- package/scripts/lib/skill-evolution/config-validation-gate.mjs +279 -0
- package/scripts/lib/skill-evolution/engine.mjs +719 -0
- package/scripts/lib/skill-evolution/idempotency.mjs +279 -0
- package/scripts/lib/skill-evolution/mr-opener.mjs +507 -0
- package/scripts/lib/skill-health/join.mjs +181 -0
- package/scripts/lib/skill-health/score.mjs +123 -0
- package/scripts/lib/skill-invocations-schema.mjs +214 -0
- package/scripts/lib/skill-judge.mjs +348 -0
- package/scripts/lib/skill-judgments-schema.mjs +264 -0
- package/scripts/lib/slopcheck.mjs +501 -0
- package/scripts/lib/soul-resolve.mjs +118 -0
- package/scripts/lib/spiral-carryover.mjs +495 -0
- package/scripts/lib/state-md/body-sections.mjs +851 -0
- package/scripts/lib/state-md/frontmatter-mutators.mjs +453 -0
- package/scripts/lib/state-md/mission-status.mjs +247 -0
- package/scripts/lib/state-md/recommendations.mjs +57 -0
- package/scripts/lib/state-md/yaml-parser.mjs +234 -0
- package/scripts/lib/state-md-peer-guard.mjs +232 -0
- package/scripts/lib/state-md.mjs +53 -0
- package/scripts/lib/subagents-schema.mjs +309 -0
- package/scripts/lib/sunset/walker.mjs +1192 -0
- package/scripts/lib/test-runner/artifact-paths.mjs +94 -0
- package/scripts/lib/test-runner/fingerprint.mjs +33 -0
- package/scripts/lib/test-runner/issue-reconcile.mjs +770 -0
- package/scripts/lib/tmux-layout/layouts.mjs +224 -0
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +100 -0
- package/scripts/lib/tmux-layout/telemetry.mjs +88 -0
- package/scripts/lib/tmux-layout/tmux-shell.mjs +82 -0
- package/scripts/lib/tmux-layout/vcs-detector.mjs +88 -0
- package/scripts/lib/validate/check-agents.mjs +457 -0
- package/scripts/lib/validate/check-codex-plugin.mjs +37 -0
- package/scripts/lib/validate/check-commands.mjs +148 -0
- package/scripts/lib/validate/check-component-paths.mjs +112 -0
- package/scripts/lib/validate/check-dead-bridge.mjs +180 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +258 -0
- package/scripts/lib/validate/check-json-files.mjs +116 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +1011 -0
- package/scripts/lib/validate/check-path-utils-canary.mjs +175 -0
- package/scripts/lib/validate/check-peekaboo-driver-canary.mjs +201 -0
- package/scripts/lib/validate/check-pi-package.mjs +110 -0
- package/scripts/lib/validate/check-pi-prompts.mjs +43 -0
- package/scripts/lib/validate/check-playwright-mcp-canary.mjs +154 -0
- package/scripts/lib/validate/check-plugin-json.mjs +96 -0
- package/scripts/lib/validate/check-plugin-monitors.mjs +206 -0
- package/scripts/lib/validate/check-plugin-schema.mjs +137 -0
- package/scripts/lib/validate/check-rules.mjs +143 -0
- package/scripts/lib/validate/check-session-plan-routing.mjs +154 -0
- package/scripts/lib/validate/check-test-fixture-shapes.mjs +280 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +533 -0
- package/scripts/lib/validate/confidential-names.mjs +169 -0
- package/scripts/lib/validate/dead-bridge-corpus.mjs +141 -0
- package/scripts/lib/validate/dead-bridge-detectors.mjs +568 -0
- package/scripts/lib/validate/tier-inference.mjs +100 -0
- package/scripts/lib/validate-vendored-rules.mjs +519 -0
- package/scripts/lib/vault-archive.mjs +404 -0
- package/scripts/lib/vault-backfill/glab.mjs +164 -0
- package/scripts/lib/vault-backfill/manifest.mjs +75 -0
- package/scripts/lib/vault-backfill/template.mjs +130 -0
- package/scripts/lib/vault-consolidate-fs.mjs +331 -0
- package/scripts/lib/vault-migration-rules.mjs +155 -0
- package/scripts/lib/vault-mirror/auto-commit.mjs +203 -0
- package/scripts/lib/vault-mirror/namespace.mjs +152 -0
- package/scripts/lib/vault-mirror/process.mjs +567 -0
- package/scripts/lib/vault-mirror/pseudonym-map.mjs +164 -0
- package/scripts/lib/vault-mirror/render-learnings.mjs +201 -0
- package/scripts/lib/vault-mirror/render-sessions.mjs +367 -0
- package/scripts/lib/vault-mirror/render.mjs +8 -0
- package/scripts/lib/vault-mirror/utils.mjs +217 -0
- package/scripts/lib/vault-relocation-rules.mjs +555 -0
- package/scripts/lib/vault-repo-backfill.mjs +235 -0
- package/scripts/lib/vault-staleness-banner.mjs +142 -0
- package/scripts/lib/vault-status/board-writer.mjs +769 -0
- package/scripts/lib/vault-status/narrative-mirror.mjs +544 -0
- package/scripts/lib/vault-sync-baseline.mjs +152 -0
- package/scripts/lib/wave-context.mjs +29 -0
- package/scripts/lib/wave-executor/pool.mjs +248 -0
- package/scripts/lib/wave-resource-gate.mjs +204 -0
- package/scripts/lib/wave-sizing.mjs +75 -0
- package/scripts/lib/webhook-url.mjs +105 -0
- package/scripts/lib/workspace.mjs +198 -0
- package/scripts/lib/worktree/constants.mjs +35 -0
- package/scripts/lib/worktree/index.mjs +17 -0
- package/scripts/lib/worktree/lifecycle.mjs +287 -0
- package/scripts/lib/worktree/listing.mjs +118 -0
- package/scripts/lib/worktree/meta.mjs +64 -0
- package/scripts/lib/worktree-freshness.mjs +313 -0
- package/scripts/lib/worktree.mjs +15 -0
- package/scripts/lifecycle-sim-v6.mjs +347 -0
- package/scripts/lock-reaper.mjs +185 -0
- package/scripts/mcp-server.sh +241 -0
- package/scripts/measure-policy-cache-effectiveness.mjs +427 -0
- package/scripts/memory-propose.mjs +464 -0
- package/scripts/migrate-cold-start-seed.mjs +404 -0
- package/scripts/migrate-learnings-jsonl.mjs +189 -0
- package/scripts/migrate-legacy-learnings.sh +61 -0
- package/scripts/migrate-sessions-jsonl.mjs +448 -0
- package/scripts/migrate-subagents-jsonl.mjs +196 -0
- package/scripts/migrate-vault-paths.mjs +796 -0
- package/scripts/parse-config.mjs +149 -0
- package/scripts/pi-install.mjs +117 -0
- package/scripts/print-applicable-rules.mjs +247 -0
- package/scripts/promote-vault-strict.mjs +496 -0
- package/scripts/relocate-vault-corpus.mjs +1178 -0
- package/scripts/run-migrate-v2-cross-repo.mjs +385 -0
- package/scripts/run-quality-gate.mjs +216 -0
- package/scripts/spikes/h3-agent-teams/preflight.sh +53 -0
- package/scripts/spikes/h3-agent-teams/run-h3.sh +112 -0
- package/scripts/spikes/h3-agent-teams/setup.sh +137 -0
- package/scripts/spikes/h3-agent-teams/toggle.sh +38 -0
- package/scripts/sweep-expired-learnings.mjs +135 -0
- package/scripts/sync-vault-schema.mjs +376 -0
- package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +8 -0
- package/scripts/tmux-layout.mjs +245 -0
- package/scripts/token-audit.sh +191 -0
- package/scripts/typecheck.mjs +42 -0
- package/scripts/upload-social-preview.mjs +316 -0
- package/scripts/validate-config.mjs +46 -0
- package/scripts/validate-plugin-manifests.mjs +163 -0
- package/scripts/validate-plugin.mjs +264 -0
- package/scripts/validate-wave-scope.mjs +289 -0
- package/scripts/vault-backfill.mjs +404 -0
- package/scripts/vault-consolidate.mjs +596 -0
- package/scripts/vault-integration-watcher.mjs +394 -0
- package/scripts/vault-mirror.mjs +430 -0
- package/skills/_shared/bootstrap-gate.md +111 -0
- package/skills/_shared/config-reading.md +226 -0
- package/skills/_shared/instruction-file-resolution.md +79 -0
- package/skills/_shared/model-selection.md +64 -0
- package/skills/_shared/monitor-patterns.md +300 -0
- package/skills/_shared/parallel-aware-auq.md +121 -0
- package/skills/_shared/parallel-aware-preamble.md +185 -0
- package/skills/_shared/platform-tools.md +96 -0
- package/skills/_shared/state-ownership.md +221 -0
- package/skills/architecture/DEEPENING.md +37 -0
- package/skills/architecture/INTERFACE-DESIGN.md +44 -0
- package/skills/architecture/LANGUAGE.md +53 -0
- package/skills/architecture/SKILL.md +92 -0
- package/skills/autopilot/SKILL.md +419 -0
- package/skills/bootstrap/SKILL.md +592 -0
- package/skills/bootstrap/STATE.md.template +24 -0
- package/skills/bootstrap/_shared-template.md +243 -0
- package/skills/bootstrap/deep-template.md +659 -0
- package/skills/bootstrap/fast-template.md +251 -0
- package/skills/bootstrap/intensity-heuristic.md +80 -0
- package/skills/bootstrap/public-fallback.md +342 -0
- package/skills/bootstrap/standard-template.md +736 -0
- package/skills/bootstrap/templates/agents/project-code-review.md +18 -0
- package/skills/bootstrap/templates/agents/project-discovery.md +18 -0
- package/skills/bootstrap/templates/agents/project-quality-gate.md +18 -0
- package/skills/brainstorm/SKILL.md +268 -0
- package/skills/brainstorm/soul.md +49 -0
- package/skills/claude-md-drift-check/SKILL.md +186 -0
- package/skills/claude-md-drift-check/checker.mjs +1380 -0
- package/skills/claude-md-drift-check/checker.sh +37 -0
- package/skills/claude-md-drift-check/package.json +16 -0
- package/skills/convergence-monitoring/README.md +39 -0
- package/skills/convergence-monitoring/SIGNALS.md +246 -0
- package/skills/convergence-monitoring/SKILL.md +285 -0
- package/skills/daily/SKILL.md +222 -0
- package/skills/daily/generate.sh +92 -0
- package/skills/daily/templates/daily.md.tpl +36 -0
- package/skills/debug/SKILL.md +188 -0
- package/skills/debug/soul.md +35 -0
- package/skills/discovery/SKILL.md +567 -0
- package/skills/discovery/issue-templates.md +237 -0
- package/skills/discovery/probes/docs-staleness.mjs +195 -0
- package/skills/discovery/probes/frontend-slop.mjs +186 -0
- package/skills/discovery/probes/ssot-code-diff.mjs +310 -0
- package/skills/discovery/probes/supply-chain-slopcheck.mjs +440 -0
- package/skills/discovery/probes/vault-narrative-staleness.mjs +355 -0
- package/skills/discovery/probes/vault-staleness.mjs +272 -0
- package/skills/discovery/probes-arch.md +252 -0
- package/skills/discovery/probes-audit.md +95 -0
- package/skills/discovery/probes-code.md +329 -0
- package/skills/discovery/probes-docs.md +76 -0
- package/skills/discovery/probes-feature.md +150 -0
- package/skills/discovery/probes-infra.md +138 -0
- package/skills/discovery/probes-intro.md +25 -0
- package/skills/discovery/probes-session.md +495 -0
- package/skills/discovery/probes-supply-chain.md +94 -0
- package/skills/discovery/probes-ui.md +147 -0
- package/skills/discovery/probes-vault.md +64 -0
- package/skills/discovery/slop-patterns.md +115 -0
- package/skills/dispatcher/SKILL.md +173 -0
- package/skills/docs-orchestrator/SKILL.md +362 -0
- package/skills/docs-orchestrator/audience-mapping.md +140 -0
- package/skills/domain-model/ADR-FORMAT.md +47 -0
- package/skills/domain-model/CONTEXT-FORMAT.md +77 -0
- package/skills/domain-model/SKILL.md +85 -0
- package/skills/ecosystem-health/SKILL.md +119 -0
- package/skills/ecosystem-health/wizard.md +193 -0
- package/skills/eval/SKILL.md +293 -0
- package/skills/eval/rubric-v1.md +218 -0
- package/skills/evolve/SKILL.md +546 -0
- package/skills/frontmatter-guard/SKILL.md +126 -0
- package/skills/gitlab-ops/SKILL.md +368 -0
- package/skills/gitlab-portfolio/SKILL.md +196 -0
- package/skills/grill/SKILL.md +185 -0
- package/skills/grill/soul.md +55 -0
- package/skills/hook-development/SKILL.md +413 -0
- package/skills/mcp-builder/SKILL.md +260 -0
- package/skills/memory-cleanup/SKILL.md +310 -0
- package/skills/mode-selector/SKILL.md +226 -0
- package/skills/peekaboo-driver/SKILL.md +237 -0
- package/skills/peekaboo-driver/soul.md +32 -0
- package/skills/persona-panel/SKILL.md +365 -0
- package/skills/persona-panel/persona-format.md +205 -0
- package/skills/persona-panel/presets/designer-lens.md +87 -0
- package/skills/persona-panel/presets/engineer-lens.md +88 -0
- package/skills/persona-panel/presets/pm-lens.md +86 -0
- package/skills/plan/SKILL.md +496 -0
- package/skills/plan/mode-feature.md +141 -0
- package/skills/plan/mode-new.md +297 -0
- package/skills/plan/mode-retro.md +271 -0
- package/skills/plan/prd-feature-template.md +132 -0
- package/skills/plan/prd-full-template.md +151 -0
- package/skills/plan/prd-reviewer-prompt.md +103 -0
- package/skills/plan/retro-template.md +75 -0
- package/skills/plan/soul.md +62 -0
- package/skills/playwright-driver/SKILL.md +226 -0
- package/skills/playwright-driver/soul.md +30 -0
- package/skills/quality-gates/SKILL.md +212 -0
- package/skills/reconcile/SKILL.md +324 -0
- package/skills/repo-audit/SKILL.md +272 -0
- package/skills/session-end/SKILL.md +1044 -0
- package/skills/session-end/discovery-scan.md +37 -0
- package/skills/session-end/drift-operations.md +97 -0
- package/skills/session-end/learning-patterns.md +78 -0
- package/skills/session-end/metrics-collection.md +175 -0
- package/skills/session-end/phase-3-2-docs-verification.md +148 -0
- package/skills/session-end/phase-3-6-tail.md +344 -0
- package/skills/session-end/phase-3-7a-recommendations.md +86 -0
- package/skills/session-end/plan-verification.md +288 -0
- package/skills/session-end/session-metrics-write.md +223 -0
- package/skills/session-end/vault-operations.md +50 -0
- package/skills/session-end/verification-checklist.md +20 -0
- package/skills/session-plan/SKILL.md +554 -0
- package/skills/session-plan/wave-template.md +37 -0
- package/skills/session-start/SKILL.md +1043 -0
- package/skills/session-start/phase-2-5-docs-planning.md +119 -0
- package/skills/session-start/phase-4-5-resource-health.md +49 -0
- package/skills/session-start/phase-7-1-premise-check.md +47 -0
- package/skills/session-start/phase-7-5-mode-selector.md +237 -0
- package/skills/session-start/phase-8-5-express-path.md +61 -0
- package/skills/session-start/presentation-format.md +81 -0
- package/skills/session-start/soul.md +57 -0
- package/skills/skill-creator/SKILL.md +168 -0
- package/skills/spinout/SKILL.md +76 -0
- package/skills/sunset-review/SKILL.md +96 -0
- package/skills/test-runner/SKILL.md +362 -0
- package/skills/test-runner/rubric-v1.md +388 -0
- package/skills/test-runner/soul.md +46 -0
- package/skills/tmux-layout/SKILL.md +104 -0
- package/skills/ubiquitous-language/SKILL.md +97 -0
- package/skills/using-orchestrator/SKILL.md +144 -0
- package/skills/vault-mirror/SKILL.md +234 -0
- package/skills/vault-sync/SKILL.md +319 -0
- package/skills/vault-sync/package-lock.json +40 -0
- package/skills/vault-sync/package.json +11 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +8 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +8 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +8 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +10 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/README.md +3 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +11 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +9 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +8 -0
- package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +7 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +9 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +11 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +3 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +3 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +11 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +11 -0
- package/skills/vault-sync/tests/schema-drift.test.mjs +133 -0
- package/skills/vault-sync/validator.mjs +658 -0
- package/skills/vault-sync/validator.sh +55 -0
- package/skills/wave-executor/SKILL.md +496 -0
- package/skills/wave-executor/circuit-breaker.md +169 -0
- package/skills/wave-executor/wave-loop.md +1043 -0
- package/skills/write-executable-plan/SKILL.md +237 -0
- package/skills/write-executable-plan/plan-template.md +154 -0
- package/templates/_minimal/CLAUDE.md.tmpl +41 -0
- package/templates/_minimal/README.md.tmpl +15 -0
- package/templates/_minimal/gitignore.tmpl +47 -0
- package/templates/_shared/harte-regeln.md +16 -0
- package/templates/_shared/loop.md +90 -0
- package/templates/_shared/rules/parallel-sessions.md +77 -0
- package/templates/nextjs-minimal/README.md +30 -0
- package/templates/nextjs-minimal/app/layout.tsx +18 -0
- package/templates/nextjs-minimal/app/page.tsx +7 -0
- package/templates/nextjs-minimal/eslint.config.mjs +16 -0
- package/templates/nextjs-minimal/next.config.mjs +4 -0
- package/templates/nextjs-minimal/package.json +27 -0
- package/templates/nextjs-minimal/tsconfig.json +23 -0
- package/templates/node-minimal/README.md +33 -0
- package/templates/node-minimal/eslint.config.mjs +10 -0
- package/templates/node-minimal/package.json +21 -0
- package/templates/node-minimal/src/index.ts +1 -0
- package/templates/node-minimal/tests/sanity.test.ts +5 -0
- package/templates/node-minimal/tsconfig.json +17 -0
- package/templates/personas/README.md +150 -0
- package/templates/personas/accounting-compliance.v1.md +120 -0
- package/templates/personas/accounting-tax-advisor.v1.md +116 -0
- package/templates/personas/buyer-p1-cto.v1.md +125 -0
- package/templates/personas/buyer-p2-kanzlei.v1.md +134 -0
- package/templates/personas/buyer-p3-build.v1.md +130 -0
- package/templates/personas/buyer-p4-tech-veto.v1.md +130 -0
- package/templates/personas/buyer-p5-solo.v1.md +132 -0
- package/templates/personas/buyer-p6-ld.v1.md +130 -0
- package/templates/personas/klima-ai-expert.v1.md +114 -0
- package/templates/personas/klima-physicist.v1.md +117 -0
- package/templates/python-uv/README.md +28 -0
- package/templates/python-uv/pyproject.toml +32 -0
- package/templates/python-uv/src/__PROJECT_NAME__/__init__.py +0 -0
- package/templates/python-uv/src/__PROJECT_NAME__/main.py +6 -0
- package/templates/python-uv/tests/test_sanity.py +2 -0
- package/templates/static-html/README.md +19 -0
- package/templates/static-html/index.html +15 -0
- package/templates/static-html/script.js +1 -0
- package/templates/static-html/styles.css +26 -0
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
// scripts/autopilot-multi.mjs
|
|
2
|
+
//
|
|
3
|
+
// `autopilot --multi-story` CLI entrypoint. Orchestrates N parallel issue
|
|
4
|
+
// pipelines using worktree isolation. v1 thin-slice: apply mode runtime loop
|
|
5
|
+
// wired in by W3 P5-Orchestration-Glue agent.
|
|
6
|
+
//
|
|
7
|
+
// References:
|
|
8
|
+
// - "Autopilot Phase D — Per-Story Worktree Pipelines" (#341; archived in the private Meta-Vault) (PRD, status: shaped)
|
|
9
|
+
// - scripts/lib/autopilot/dep-graph.mjs (NEW, parallel-built by C2 agent)
|
|
10
|
+
// - scripts/lib/autopilot/worktree-pipeline.mjs (NEW, parallel-built by C3 agent)
|
|
11
|
+
// - scripts/lib/autopilot/multi-killswitch.mjs (NEW, parallel-built by C4 agent)
|
|
12
|
+
//
|
|
13
|
+
// CLI guard at bottom — top-level main() never runs on import (deep-2 #368).
|
|
14
|
+
|
|
15
|
+
import { parseArgs } from 'node:util';
|
|
16
|
+
import { execFile as execFileCb, spawnSync } from 'node:child_process';
|
|
17
|
+
import { promisify } from 'node:util';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import fs from 'node:fs';
|
|
20
|
+
|
|
21
|
+
const execFile = promisify(execFileCb);
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Constants
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
const HELP_TEXT = `autopilot-multi — Phase D --multi-story orchestrator (v3.6 scaffold)
|
|
28
|
+
|
|
29
|
+
USAGE:
|
|
30
|
+
autopilot-multi [OPTIONS]
|
|
31
|
+
|
|
32
|
+
OPTIONS:
|
|
33
|
+
--max-stories <N> Max parallel story loops (1..10, default: 3)
|
|
34
|
+
--max-hours <H> Wall-clock budget in hours (0.5..24, default: 8)
|
|
35
|
+
--inactivity-timeout <S> Seconds without completion → stop (60..3600, default: 300)
|
|
36
|
+
--draft-mr <policy> off | on-loop-start | on-green (default: off)
|
|
37
|
+
--stall-seconds <S> Per-loop STALL_TIMEOUT threshold (60..3600, default: 600)
|
|
38
|
+
--dry-run Emit plan only, do not execute (default: true)
|
|
39
|
+
--apply Execute the plan (mutex with --dry-run)
|
|
40
|
+
--json Machine-readable output to stdout
|
|
41
|
+
--verbose Diagnostic output to stderr
|
|
42
|
+
--deconflict-paths <glob> Required when bg-isolation=none AND max-stories>1.
|
|
43
|
+
Confirms per-story file ownership is planned.
|
|
44
|
+
See .claude/rules/parallel-sessions.md PSA-001/002/003.
|
|
45
|
+
-h, --help Show this help and exit
|
|
46
|
+
--version Print version and exit
|
|
47
|
+
|
|
48
|
+
EXIT CODES:
|
|
49
|
+
0 Success
|
|
50
|
+
1 User error (bad flags)
|
|
51
|
+
2 System error (libs/binaries/probe failed)
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
const FLAGS = {
|
|
55
|
+
'max-stories': { type: 'string', default: '3' },
|
|
56
|
+
'max-hours': { type: 'string', default: '8' },
|
|
57
|
+
'inactivity-timeout': { type: 'string', default: '300' },
|
|
58
|
+
'draft-mr': { type: 'string', default: 'off' },
|
|
59
|
+
'stall-seconds': { type: 'string', default: '600' },
|
|
60
|
+
'dry-run': { type: 'boolean', default: true },
|
|
61
|
+
'apply': { type: 'boolean', default: false },
|
|
62
|
+
'json': { type: 'boolean', default: false },
|
|
63
|
+
'verbose': { type: 'boolean', default: false },
|
|
64
|
+
'help': { type: 'boolean', default: false, short: 'h' },
|
|
65
|
+
'version': { type: 'boolean', default: false },
|
|
66
|
+
// bg-isolation: none requires explicit file-scope deconfliction acknowledgement
|
|
67
|
+
// when max-stories > 1. Pass a glob to confirm per-story file ownership is
|
|
68
|
+
// planned. See .claude/rules/parallel-sessions.md PSA-001/002/003 and #431.
|
|
69
|
+
'deconflict-paths': { type: 'string', default: '' },
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const VALID_DRAFT_MR_POLICIES = new Set(['off', 'on-loop-start', 'on-green']);
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// parseFlags — parses + validates argv, returns typed flags object
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Parse and validate CLI flags.
|
|
80
|
+
*
|
|
81
|
+
* @param {string[]} argv
|
|
82
|
+
* @returns {{
|
|
83
|
+
* maxStories: number,
|
|
84
|
+
* maxHours: number,
|
|
85
|
+
* inactivityTimeoutMs: number,
|
|
86
|
+
* draftMrPolicy: string,
|
|
87
|
+
* stallTimeoutSeconds: number,
|
|
88
|
+
* dryRun: boolean,
|
|
89
|
+
* apply: boolean,
|
|
90
|
+
* json: boolean,
|
|
91
|
+
* verbose: boolean,
|
|
92
|
+
* help: boolean,
|
|
93
|
+
* version: boolean,
|
|
94
|
+
* deconflictPaths: string,
|
|
95
|
+
* }}
|
|
96
|
+
*/
|
|
97
|
+
export function parseFlags(argv) {
|
|
98
|
+
const { values } = parseArgs({ args: argv, options: FLAGS, allowPositionals: false });
|
|
99
|
+
|
|
100
|
+
// Mutex: --dry-run and --apply cannot both be explicitly set to true.
|
|
101
|
+
// Since --dry-run defaults to true, treat apply=true + the user explicitly
|
|
102
|
+
// also passing --dry-run as a mutex violation.
|
|
103
|
+
if (values['apply'] && values['dry-run'] && argv.includes('--dry-run')) {
|
|
104
|
+
throw new UserError('--dry-run and --apply are mutually exclusive; pick one.');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Resolve safe default: if neither --apply nor explicit --dry-run, dry-run wins.
|
|
108
|
+
const apply = /** @type {boolean} */ (values['apply']);
|
|
109
|
+
// When apply is true and --dry-run was not explicitly passed, disable dry-run.
|
|
110
|
+
const dryRun = apply ? false : /** @type {boolean} */ (values['dry-run']);
|
|
111
|
+
|
|
112
|
+
// max-stories: int, clamp [1, 10]
|
|
113
|
+
const rawMaxStories = parseInt(/** @type {string} */ (values['max-stories']), 10);
|
|
114
|
+
if (isNaN(rawMaxStories)) {
|
|
115
|
+
throw new UserError(`--max-stories must be an integer, got: ${values['max-stories']}`);
|
|
116
|
+
}
|
|
117
|
+
const maxStories = Math.max(1, Math.min(10, rawMaxStories));
|
|
118
|
+
|
|
119
|
+
// max-hours: float, clamp [0.5, 24]
|
|
120
|
+
const rawMaxHours = parseFloat(/** @type {string} */ (values['max-hours']));
|
|
121
|
+
if (isNaN(rawMaxHours)) {
|
|
122
|
+
throw new UserError(`--max-hours must be a number, got: ${values['max-hours']}`);
|
|
123
|
+
}
|
|
124
|
+
const maxHours = Math.max(0.5, Math.min(24, rawMaxHours));
|
|
125
|
+
|
|
126
|
+
// inactivity-timeout: int seconds, clamp [60, 3600]
|
|
127
|
+
const rawInactivity = parseInt(/** @type {string} */ (values['inactivity-timeout']), 10);
|
|
128
|
+
if (isNaN(rawInactivity)) {
|
|
129
|
+
throw new UserError(`--inactivity-timeout must be an integer, got: ${values['inactivity-timeout']}`);
|
|
130
|
+
}
|
|
131
|
+
const inactivityTimeoutMs = Math.max(60, Math.min(3600, rawInactivity)) * 1000;
|
|
132
|
+
|
|
133
|
+
// draft-mr: validate against allowed set
|
|
134
|
+
const draftMrPolicy = /** @type {string} */ (values['draft-mr']);
|
|
135
|
+
if (!VALID_DRAFT_MR_POLICIES.has(draftMrPolicy)) {
|
|
136
|
+
throw new UserError(
|
|
137
|
+
`--draft-mr must be one of: off, on-loop-start, on-green. Got: "${draftMrPolicy}"`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// stall-seconds: int, clamp [60, 3600]
|
|
142
|
+
const rawStall = parseInt(/** @type {string} */ (values['stall-seconds']), 10);
|
|
143
|
+
if (isNaN(rawStall)) {
|
|
144
|
+
throw new UserError(`--stall-seconds must be an integer, got: ${values['stall-seconds']}`);
|
|
145
|
+
}
|
|
146
|
+
const stallTimeoutSeconds = Math.max(60, Math.min(3600, rawStall));
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
maxStories,
|
|
150
|
+
maxHours,
|
|
151
|
+
inactivityTimeoutMs,
|
|
152
|
+
draftMrPolicy,
|
|
153
|
+
stallTimeoutSeconds,
|
|
154
|
+
dryRun,
|
|
155
|
+
apply,
|
|
156
|
+
json: /** @type {boolean} */ (values['json']),
|
|
157
|
+
verbose: /** @type {boolean} */ (values['verbose']),
|
|
158
|
+
help: /** @type {boolean} */ (values['help']),
|
|
159
|
+
version: /** @type {boolean} */ (values['version']),
|
|
160
|
+
deconflictPaths: /** @type {string} */ (values['deconflict-paths'] ?? ''),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// buildOrchestratorState — compose plan snapshot from flags + probe + issues
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Build a serialisable orchestrator state / plan object.
|
|
170
|
+
*
|
|
171
|
+
* @param {{
|
|
172
|
+
* maxStories: number,
|
|
173
|
+
* maxHours: number,
|
|
174
|
+
* inactivityTimeoutMs: number,
|
|
175
|
+
* draftMrPolicy: string,
|
|
176
|
+
* stallTimeoutSeconds: number,
|
|
177
|
+
* dryRun: boolean,
|
|
178
|
+
* apply: boolean,
|
|
179
|
+
* json: boolean,
|
|
180
|
+
* verbose: boolean,
|
|
181
|
+
* }} flags
|
|
182
|
+
* @param {object|null} snapshot Resource-probe snapshot (null in dry-run without probe)
|
|
183
|
+
* @param {Array<{iid: number, title: string, blocks: number[], blockedBy: number[], labels: string[]}>} issues
|
|
184
|
+
* @param {string} [parentRunId] Unique run-id for this orchestrator session
|
|
185
|
+
* @returns {{
|
|
186
|
+
* concurrencyCap: number,
|
|
187
|
+
* totalIssues: number,
|
|
188
|
+
* readyIssues: number,
|
|
189
|
+
* executionPlan: Array<{iid: number, title: string}>,
|
|
190
|
+
* flags: object,
|
|
191
|
+
* issues: Array<{iid: number, title: string, blocks: number[], blockedBy: number[], labels: string[]}>,
|
|
192
|
+
* parentRunId: string,
|
|
193
|
+
* }}
|
|
194
|
+
*/
|
|
195
|
+
export function buildOrchestratorState(flags, snapshot, issues, parentRunId = '') {
|
|
196
|
+
// If a snapshot is available and has a cap, use it; otherwise fall back to maxStories.
|
|
197
|
+
const snapshotCap = snapshot && typeof snapshot.cpuCores === 'number'
|
|
198
|
+
? snapshot.cpuCores
|
|
199
|
+
: flags.maxStories;
|
|
200
|
+
const concurrencyCap = Math.min(flags.maxStories, snapshotCap);
|
|
201
|
+
|
|
202
|
+
const executionPlan = issues
|
|
203
|
+
.slice(0, concurrencyCap)
|
|
204
|
+
.map(({ iid, title }) => ({ iid, title }));
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
concurrencyCap,
|
|
208
|
+
totalIssues: issues.length,
|
|
209
|
+
readyIssues: issues.length,
|
|
210
|
+
executionPlan,
|
|
211
|
+
issues,
|
|
212
|
+
parentRunId,
|
|
213
|
+
flags: {
|
|
214
|
+
maxStories: flags.maxStories,
|
|
215
|
+
maxHours: flags.maxHours,
|
|
216
|
+
inactivityTimeoutMs: flags.inactivityTimeoutMs,
|
|
217
|
+
draftMrPolicy: flags.draftMrPolicy,
|
|
218
|
+
stallTimeoutSeconds: flags.stallTimeoutSeconds,
|
|
219
|
+
dryRun: flags.dryRun,
|
|
220
|
+
apply: flags.apply,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
// Session Config helpers
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Load Session Config via `node scripts/parse-config.mjs` (same subprocess
|
|
231
|
+
* pattern used by scripts/autopilot.mjs). Returns null on failure — callers
|
|
232
|
+
* apply their own defaults.
|
|
233
|
+
*
|
|
234
|
+
* @returns {object|null}
|
|
235
|
+
*/
|
|
236
|
+
function loadSessionConfig() {
|
|
237
|
+
try {
|
|
238
|
+
const scriptPath = path.resolve(
|
|
239
|
+
new URL('.', import.meta.url).pathname,
|
|
240
|
+
'parse-config.mjs',
|
|
241
|
+
);
|
|
242
|
+
const result = spawnSync(
|
|
243
|
+
process.execPath,
|
|
244
|
+
[scriptPath],
|
|
245
|
+
{ encoding: 'utf8', timeout: 10_000 },
|
|
246
|
+
);
|
|
247
|
+
if (result.status !== 0 || !result.stdout) return null;
|
|
248
|
+
return JSON.parse(result.stdout);
|
|
249
|
+
} catch {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ---------------------------------------------------------------------------
|
|
255
|
+
// Apply-mode helpers
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Extract issue relation refs (blocks / blocked_by) from a raw glab issue
|
|
260
|
+
* object. The glab issue list JSON does not embed these fields by default —
|
|
261
|
+
* they are left as [] for v1.
|
|
262
|
+
*
|
|
263
|
+
* @param {object} _issue Raw glab issue object
|
|
264
|
+
* @param {string} _field Relation field name ('blocks' | 'blocked_by')
|
|
265
|
+
* @returns {number[]}
|
|
266
|
+
*/
|
|
267
|
+
// TODO Phase D.2 (#341): query relations via glab api (e.g. glab api
|
|
268
|
+
// projects/:id/issues/:iid/links) to populate blocks/blocked_by.
|
|
269
|
+
function extractRefs(_issue, _field) {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Fetch the ready-backlog from GitLab via `glab issue list`.
|
|
275
|
+
* Uses execFile (shell: false) per SEC-014 / cli-design rules.
|
|
276
|
+
*
|
|
277
|
+
* @param {Function} execFileFn Promisified execFile
|
|
278
|
+
* @returns {Promise<Array<{iid: number, title: string, blocks: number[], blockedBy: number[], labels: string[]}>>}
|
|
279
|
+
*/
|
|
280
|
+
async function fetchReadyBacklog(execFileFn) {
|
|
281
|
+
const { stdout } = await execFileFn(
|
|
282
|
+
'glab',
|
|
283
|
+
['issue', 'list', '--label', 'status:ready', '--per-page', '50', '--output', 'json'],
|
|
284
|
+
{ shell: false, timeout: 30_000 },
|
|
285
|
+
);
|
|
286
|
+
const issues = JSON.parse(stdout);
|
|
287
|
+
return issues.map((i) => ({
|
|
288
|
+
iid: i.iid ?? i.id,
|
|
289
|
+
title: i.title ?? '',
|
|
290
|
+
blocks: extractRefs(i, 'blocks'), // best-effort; default to []
|
|
291
|
+
blockedBy: extractRefs(i, 'blocked_by'), // best-effort; default to []
|
|
292
|
+
labels: i.labels?.map((l) => l.name ?? l) ?? [],
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Summarise a completed apply run into a canonical result envelope.
|
|
298
|
+
*
|
|
299
|
+
* @param {import('./lib/autopilot/multi-killswitch.mjs').LoopRegistration[]} allLoops
|
|
300
|
+
* @param {string} reason
|
|
301
|
+
* @param {number} startMs
|
|
302
|
+
* @param {{ kill: string, detail: string }|null} [killDetail]
|
|
303
|
+
* @returns {{ success: boolean, data: object }}
|
|
304
|
+
*/
|
|
305
|
+
function finalize(allLoops, reason, startMs, killDetail = null) {
|
|
306
|
+
const terminalReasons = new Set([
|
|
307
|
+
'first-kill-switch',
|
|
308
|
+
'spiral',
|
|
309
|
+
'cohort-abort',
|
|
310
|
+
'spawn-error',
|
|
311
|
+
STALE_SUBAGENT_MIN,
|
|
312
|
+
]);
|
|
313
|
+
return {
|
|
314
|
+
success: !terminalReasons.has(reason),
|
|
315
|
+
data: {
|
|
316
|
+
reason,
|
|
317
|
+
loopCount: allLoops.length,
|
|
318
|
+
completed: allLoops.filter((l) => l.status === 'complete').length,
|
|
319
|
+
failed: allLoops.filter((l) => l.status === 'failed').length,
|
|
320
|
+
elapsedMs: Date.now() - startMs,
|
|
321
|
+
killDetail: killDetail ?? null,
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Sentinel import — resolved lazily in runApplyLoop. Kept at module scope to
|
|
327
|
+
* allow finalize() to reference STALE_SUBAGENT_MIN without a forward-ref issue. */
|
|
328
|
+
let STALE_SUBAGENT_MIN = 'stale-subagent-min';
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Core apply-mode orchestration loop.
|
|
332
|
+
*
|
|
333
|
+
* Iteratively spawns up to N concurrent worktree-pipelines, polls completions,
|
|
334
|
+
* evaluates cross-loop kill-switches, and returns a canonical result envelope
|
|
335
|
+
* when work is exhausted or a stop condition fires.
|
|
336
|
+
*
|
|
337
|
+
* @param {{ issues: Array<object>, parentRunId: string, flags: object }} state
|
|
338
|
+
* @param {{ depGraph: object, wtPipeline: object, mkLib: object, probe: object }} libs
|
|
339
|
+
* @param {{ bgIsolation?: string, [key: string]: unknown }} [opts]
|
|
340
|
+
* @returns {Promise<{ success: boolean, data: object }>}
|
|
341
|
+
*/
|
|
342
|
+
async function runApplyLoop(state, libs, opts) {
|
|
343
|
+
// bg-isolation routing: "none" → skip worktree creation and spawn sub-sessions
|
|
344
|
+
// in the main tree. "worktree" (default) → each story pipeline creates its
|
|
345
|
+
// own worktree via worktree-pipeline.mjs (existing path).
|
|
346
|
+
const bgIsolation = opts?.bgIsolation ?? 'worktree';
|
|
347
|
+
const { depGraph, wtPipeline, mkLib, probe } = libs;
|
|
348
|
+
const { flags } = state;
|
|
349
|
+
|
|
350
|
+
// Patch module-level sentinel with the actual export from the loaded lib.
|
|
351
|
+
STALE_SUBAGENT_MIN = mkLib.STALE_SUBAGENT_MIN ?? 'stale-subagent-min';
|
|
352
|
+
|
|
353
|
+
const startMs = Date.now();
|
|
354
|
+
const maxMs = flags.maxHours * 3600 * 1000;
|
|
355
|
+
|
|
356
|
+
// Early exit when there is nothing to process.
|
|
357
|
+
if (!state.issues || state.issues.length === 0) {
|
|
358
|
+
return finalize([], 'backlog-empty', startMs);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Build graph and tracking state.
|
|
362
|
+
const graph = depGraph.buildGraph(state.issues);
|
|
363
|
+
/** @type {Map<string, { promise: Promise<object>, registration: object }>} */
|
|
364
|
+
const inFlight = new Map(); // loopId → { promise, registration }
|
|
365
|
+
/** @type {Set<number>} */
|
|
366
|
+
const completed = new Set();
|
|
367
|
+
/** @type {object[]} */
|
|
368
|
+
const allLoops = []; // LoopRegistration[]
|
|
369
|
+
let lastCompletionAt = startMs;
|
|
370
|
+
|
|
371
|
+
// TODO Phase D.2 (#341): implement real cross-loop dependency commit-waiting.
|
|
372
|
+
// Currently all issues are treated as independent (blocks/blockedBy are []
|
|
373
|
+
// for v1). When commit-based deps land, nextReady() will need to check
|
|
374
|
+
// whether the blocker's commit is present on the target branch before
|
|
375
|
+
// yielding the blocked issue.
|
|
376
|
+
|
|
377
|
+
while (true) {
|
|
378
|
+
// 1. Stop condition check.
|
|
379
|
+
const activeLoopRegs = Array.from(inFlight.values()).map((v) => v.registration);
|
|
380
|
+
const readyBacklog = depGraph.nextReady(graph, new Set(inFlight.keys().map((id) => {
|
|
381
|
+
const reg = inFlight.get(id)?.registration;
|
|
382
|
+
return reg?.issueIid;
|
|
383
|
+
}).filter((x) => x !== null && x !== undefined)), completed);
|
|
384
|
+
|
|
385
|
+
const orchState = {
|
|
386
|
+
activeLoops: activeLoopRegs,
|
|
387
|
+
readyBacklog,
|
|
388
|
+
lastCompletionAt,
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const stopDecision = mkLib.shouldStopOrchestrator(orchState, {
|
|
392
|
+
inactivityTimeoutMs: flags.inactivityTimeoutMs,
|
|
393
|
+
});
|
|
394
|
+
if (stopDecision.stop) {
|
|
395
|
+
return finalize(allLoops, stopDecision.reason, startMs);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (Date.now() - startMs > maxMs) {
|
|
399
|
+
return finalize(allLoops, 'max-hours', startMs);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// 2. Per-iteration resource probe + cohort kill-switch check.
|
|
403
|
+
let snapshot = null;
|
|
404
|
+
try {
|
|
405
|
+
snapshot = await probe.probe();
|
|
406
|
+
} catch {
|
|
407
|
+
// Resource probe failure is non-fatal — continue with maxStories cap.
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const recommendedCap = snapshot ? mkLib.calculateConcurrencyCap(snapshot) : flags.maxStories;
|
|
411
|
+
const effectiveCap = Math.min(flags.maxStories, recommendedCap);
|
|
412
|
+
|
|
413
|
+
const crossKill = mkLib.evaluateMultiKillSwitches(allLoops, {
|
|
414
|
+
staleSubagentMinSeconds: flags.stallTimeoutSeconds,
|
|
415
|
+
});
|
|
416
|
+
if (crossKill !== null) {
|
|
417
|
+
return finalize(allLoops, crossKill.kill, startMs, crossKill);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// 3. Spawn loops up to cap.
|
|
421
|
+
const spawnableBacklog = [...readyBacklog];
|
|
422
|
+
while (inFlight.size < effectiveCap && spawnableBacklog.length > 0) {
|
|
423
|
+
const issue = spawnableBacklog.shift();
|
|
424
|
+
const loopId = `loop-${issue.iid}-${Date.now()}`;
|
|
425
|
+
/** @type {object} */
|
|
426
|
+
const registration = {
|
|
427
|
+
loopId,
|
|
428
|
+
pid: process.pid, // best-effort; loops are in-process for v1
|
|
429
|
+
parentRunId: state.parentRunId,
|
|
430
|
+
issueIid: issue.iid,
|
|
431
|
+
status: 'running',
|
|
432
|
+
killSwitch: null,
|
|
433
|
+
spiralRecoveryCount: 0,
|
|
434
|
+
startedAt: Date.now(),
|
|
435
|
+
lastActivityAt: Date.now(),
|
|
436
|
+
};
|
|
437
|
+
allLoops.push(registration);
|
|
438
|
+
|
|
439
|
+
// bg-isolation: "none" → main-tree spawn (no worktree, faster for
|
|
440
|
+
// monorepos with heavy build state but requires file-scope discipline).
|
|
441
|
+
// bg-isolation: "worktree" (default) → per-story worktree isolation.
|
|
442
|
+
// worktree-pipeline.mjs respects the bgIsolation option and skips
|
|
443
|
+
// EnterWorktree when set to "none".
|
|
444
|
+
const promise = wtPipeline.runStoryPipeline({
|
|
445
|
+
issueIid: issue.iid,
|
|
446
|
+
issueTitle: issue.title,
|
|
447
|
+
branchName: `issue-${issue.iid}`,
|
|
448
|
+
parentRunId: state.parentRunId,
|
|
449
|
+
repoRoot: process.cwd(),
|
|
450
|
+
draftMrPolicy: flags.draftMrPolicy,
|
|
451
|
+
bgIsolation,
|
|
452
|
+
// TODO Phase D.2 (#341): on-green MR-draft trigger — loop result does not
|
|
453
|
+
// yet signal "first green test"; wire once runStoryPipeline
|
|
454
|
+
// propagates a firstGreenAt timestamp in StoryResult.
|
|
455
|
+
}).then((result) => {
|
|
456
|
+
registration.status = result.killSwitch ? 'failed' : 'complete';
|
|
457
|
+
registration.killSwitch = result.killSwitch ?? null;
|
|
458
|
+
completed.add(issue.iid);
|
|
459
|
+
lastCompletionAt = Date.now();
|
|
460
|
+
return result;
|
|
461
|
+
}).catch((err) => {
|
|
462
|
+
registration.status = 'failed';
|
|
463
|
+
registration.killSwitch = 'spawn-error';
|
|
464
|
+
registration.killSwitchDetail = err.message;
|
|
465
|
+
completed.add(issue.iid);
|
|
466
|
+
lastCompletionAt = Date.now();
|
|
467
|
+
return { error: err.message };
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
inFlight.set(loopId, { promise, registration });
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// 4. Cohort abort decision for newly-failed loops.
|
|
474
|
+
for (const [, { registration }] of inFlight.entries()) {
|
|
475
|
+
if (registration.status === 'failed' && registration.killSwitch) {
|
|
476
|
+
const cohortDecision = mkLib.decideCohortAction(allLoops, registration.loopId);
|
|
477
|
+
if (cohortDecision.action === 'cohort-abort') {
|
|
478
|
+
// TODO Phase D.2 (#341): send real SIGTERM to in-process promises.
|
|
479
|
+
// In v1 loops are in-process Promises; enforcement is logged only.
|
|
480
|
+
// Full SIGTERM requires spawning each pipeline in a child process
|
|
481
|
+
// with a dedicated pid, tracked on LoopRegistration.pid.
|
|
482
|
+
process.stderr.write(
|
|
483
|
+
`[autopilot-multi] cohort-abort triggered by loop ${registration.loopId}: ${cohortDecision.reason}\n`,
|
|
484
|
+
);
|
|
485
|
+
return finalize(allLoops, 'cohort-abort', startMs, { kill: 'cohort-abort', detail: cohortDecision.reason });
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// 5. Wait for next completion (Promise.race) OR settle after 5 s tick.
|
|
491
|
+
if (inFlight.size > 0) {
|
|
492
|
+
await Promise.race([
|
|
493
|
+
Promise.race(Array.from(inFlight.values()).map((v) => v.promise.then(() => null))),
|
|
494
|
+
new Promise((r) => setTimeout(r, 5000)),
|
|
495
|
+
]);
|
|
496
|
+
// Sweep completed / failed entries.
|
|
497
|
+
for (const [id, { registration }] of inFlight.entries()) {
|
|
498
|
+
if (registration.status === 'complete' || registration.status === 'failed') {
|
|
499
|
+
inFlight.delete(id);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
} else if (readyBacklog.length === 0) {
|
|
503
|
+
// No active, no ready — backlog-empty stop condition fires on the next tick.
|
|
504
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// ---------------------------------------------------------------------------
|
|
510
|
+
// Internal helpers
|
|
511
|
+
// ---------------------------------------------------------------------------
|
|
512
|
+
|
|
513
|
+
/** Typed user-error (exit 1). */
|
|
514
|
+
class UserError extends Error {
|
|
515
|
+
constructor(message) {
|
|
516
|
+
super(message);
|
|
517
|
+
this.name = 'UserError';
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Write a canonical JSON envelope to stdout.
|
|
523
|
+
*
|
|
524
|
+
* @param {boolean} success
|
|
525
|
+
* @param {object|null} data
|
|
526
|
+
* @param {{code: string, message: string}|null} error
|
|
527
|
+
*/
|
|
528
|
+
function writeJsonEnvelope(success, data, error) {
|
|
529
|
+
const envelope = success
|
|
530
|
+
? { success: true, data }
|
|
531
|
+
: { success: false, error };
|
|
532
|
+
process.stdout.write(JSON.stringify(envelope) + '\n');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Emit a diagnostic line to stderr (only when verbose or always for warnings).
|
|
537
|
+
*
|
|
538
|
+
* @param {string} message
|
|
539
|
+
* @param {boolean} [verbose]
|
|
540
|
+
* @param {boolean} [force] If true, always emit regardless of verbose flag.
|
|
541
|
+
*/
|
|
542
|
+
function diag(message, verbose, force = false) {
|
|
543
|
+
if (verbose || force) {
|
|
544
|
+
process.stderr.write(`[autopilot-multi] ${message}\n`);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// ---------------------------------------------------------------------------
|
|
549
|
+
// main — primary entrypoint
|
|
550
|
+
// ---------------------------------------------------------------------------
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* CLI main function. Does NOT execute on import — see CLI guard below.
|
|
554
|
+
*
|
|
555
|
+
* @param {string[]} argv
|
|
556
|
+
* @param {object} [opts] Dependency-injection seam for tests.
|
|
557
|
+
* @param {Function} [opts.exit] Override process.exit (for testing).
|
|
558
|
+
* @returns {Promise<void>}
|
|
559
|
+
*/
|
|
560
|
+
export async function main(argv = process.argv.slice(2), opts = {}) {
|
|
561
|
+
const exitFn = opts.exit ?? process.exit;
|
|
562
|
+
|
|
563
|
+
// Unique run-id for this orchestrator session — passed to each child loop.
|
|
564
|
+
const parentRunId = `multi-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
565
|
+
|
|
566
|
+
// -------------------------------------------------------------------------
|
|
567
|
+
// 1. Parse flags
|
|
568
|
+
// -------------------------------------------------------------------------
|
|
569
|
+
let flags;
|
|
570
|
+
try {
|
|
571
|
+
flags = parseFlags(argv);
|
|
572
|
+
} catch (err) {
|
|
573
|
+
if (err instanceof UserError) {
|
|
574
|
+
process.stderr.write(`autopilot-multi: ${err.message}\n`);
|
|
575
|
+
exitFn(1);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
process.stderr.write(`autopilot-multi: flag parse error: ${err.message}\n`);
|
|
579
|
+
exitFn(1);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// -------------------------------------------------------------------------
|
|
584
|
+
// 2. --help
|
|
585
|
+
// -------------------------------------------------------------------------
|
|
586
|
+
if (flags.help) {
|
|
587
|
+
process.stdout.write(HELP_TEXT);
|
|
588
|
+
exitFn(0);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// -------------------------------------------------------------------------
|
|
593
|
+
// 3. --version
|
|
594
|
+
// -------------------------------------------------------------------------
|
|
595
|
+
if (flags.version) {
|
|
596
|
+
try {
|
|
597
|
+
const pkgPath = path.join(import.meta.dirname, '..', 'package.json');
|
|
598
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
599
|
+
process.stdout.write(`${pkg.version}\n`);
|
|
600
|
+
} catch {
|
|
601
|
+
process.stderr.write('autopilot-multi: could not read package.json version\n');
|
|
602
|
+
exitFn(2);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
exitFn(0);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// -------------------------------------------------------------------------
|
|
610
|
+
// 4. Read Session Config and enforce bg-isolation policy (#431)
|
|
611
|
+
//
|
|
612
|
+
// bg-isolation: "none" — skip worktree creation; spawn sub-sessions in the
|
|
613
|
+
// main tree. Requires file-scope discipline: with >1 concurrent story,
|
|
614
|
+
// agents WILL edit the same working copy simultaneously. A hard-error fires
|
|
615
|
+
// unless --deconflict-paths=<glob> is provided to signal that the operator
|
|
616
|
+
// has planned per-story file ownership.
|
|
617
|
+
//
|
|
618
|
+
// bg-isolation: "worktree" (default) — each story gets its own git worktree;
|
|
619
|
+
// safe for parallel writes but costs disk space and EnterWorktree latency.
|
|
620
|
+
//
|
|
621
|
+
// Caution: CC 2.1.133 silently flipped worktree.baseRef default from "head"
|
|
622
|
+
// to "origin/<default>", breaking users relying on unpushed commits. Apply
|
|
623
|
+
// the same operator-awareness here: any future CC change to worktree defaults
|
|
624
|
+
// may silently affect this path. See #431 and SKILL.md § bg-isolation.
|
|
625
|
+
// -------------------------------------------------------------------------
|
|
626
|
+
const sessionConfig = loadSessionConfig();
|
|
627
|
+
const bgIsolation = /** @type {string} */ (
|
|
628
|
+
sessionConfig?.autopilot?.['bg-isolation'] ?? 'worktree'
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
if (bgIsolation === 'none' && flags.maxStories > 1 && !flags.deconflictPaths) {
|
|
632
|
+
// Hard-error per PSA-001/002/003 — see .claude/rules/parallel-sessions.md
|
|
633
|
+
const msg =
|
|
634
|
+
`autopilot-multi: bg-isolation: "none" with --max-stories=${flags.maxStories} ` +
|
|
635
|
+
`requires explicit file-scope deconfliction. ` +
|
|
636
|
+
`Set max-stories=1 OR provide --deconflict-paths=<glob> to confirm you have ` +
|
|
637
|
+
`planned per-story file ownership. ` +
|
|
638
|
+
`See .claude/rules/parallel-sessions.md PSA-001/002/003.`;
|
|
639
|
+
process.stderr.write(`${msg}\n`);
|
|
640
|
+
if (flags.json) {
|
|
641
|
+
writeJsonEnvelope(false, null, { code: 'BG_ISOLATION_CONFLICT', message: msg });
|
|
642
|
+
}
|
|
643
|
+
exitFn(1);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
diag(
|
|
648
|
+
`bg-isolation=${bgIsolation}` +
|
|
649
|
+
(bgIsolation === 'none' ? ' (main-tree mode; worktree creation skipped)' : ''),
|
|
650
|
+
flags.verbose,
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
// -------------------------------------------------------------------------
|
|
654
|
+
// 5. Dynamic-import sibling libs (graceful degradation)
|
|
655
|
+
// -------------------------------------------------------------------------
|
|
656
|
+
let buildGraph, nextReady, calculateConcurrencyCap, probe;
|
|
657
|
+
let depGraphMod, mkLibMod, wtPipelineMod, resourceProbeMod;
|
|
658
|
+
|
|
659
|
+
try {
|
|
660
|
+
depGraphMod = await import('./lib/autopilot/dep-graph.mjs');
|
|
661
|
+
mkLibMod = await import('./lib/autopilot/multi-killswitch.mjs');
|
|
662
|
+
wtPipelineMod = await import('./lib/autopilot/worktree-pipeline.mjs');
|
|
663
|
+
resourceProbeMod = await import('./lib/resource-probe.mjs');
|
|
664
|
+
|
|
665
|
+
buildGraph = depGraphMod.buildGraph;
|
|
666
|
+
nextReady = depGraphMod.nextReady;
|
|
667
|
+
calculateConcurrencyCap = mkLibMod.calculateConcurrencyCap;
|
|
668
|
+
probe = resourceProbeMod.probe;
|
|
669
|
+
} catch {
|
|
670
|
+
const msg = 'multi-story libs not yet shipped; this is the v3.6 CLI scaffold';
|
|
671
|
+
process.stderr.write(`autopilot-multi: ${msg}\n`);
|
|
672
|
+
if (flags.json) {
|
|
673
|
+
writeJsonEnvelope(false, null, { code: 'LIBS_NOT_AVAILABLE', message: msg });
|
|
674
|
+
}
|
|
675
|
+
exitFn(2);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// -------------------------------------------------------------------------
|
|
680
|
+
// 6. Resource probe + concurrency cap advisory
|
|
681
|
+
// -------------------------------------------------------------------------
|
|
682
|
+
let snapshot = null;
|
|
683
|
+
try {
|
|
684
|
+
snapshot = await probe();
|
|
685
|
+
} catch (err) {
|
|
686
|
+
process.stderr.write(
|
|
687
|
+
`autopilot-multi: resource-probe failed: ${err.message}; continuing without snapshot\n`
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const recommendedCap = snapshot ? calculateConcurrencyCap(snapshot) : flags.maxStories;
|
|
692
|
+
|
|
693
|
+
if (flags.maxStories > recommendedCap && !flags.apply) {
|
|
694
|
+
process.stderr.write(
|
|
695
|
+
`autopilot-multi: WARN requested N=${flags.maxStories} but resource-probe recommends` +
|
|
696
|
+
` N=${recommendedCap}; pass --apply to override\n`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
diag(
|
|
701
|
+
`resource-probe snapshot received; recommendedCap=${recommendedCap}`,
|
|
702
|
+
flags.verbose
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
// -------------------------------------------------------------------------
|
|
706
|
+
// 7. Build issue backlog
|
|
707
|
+
// -------------------------------------------------------------------------
|
|
708
|
+
let issues;
|
|
709
|
+
|
|
710
|
+
if (flags.dryRun) {
|
|
711
|
+
// Dry-run: synthesize a stub backlog so plan output is always available.
|
|
712
|
+
issues = [{ iid: 999, title: 'dry-run-stub', blocks: [], blockedBy: [], labels: ['status:ready'] }];
|
|
713
|
+
diag('dry-run mode — using synthetic issue stub instead of calling glab', flags.verbose, true);
|
|
714
|
+
} else {
|
|
715
|
+
// Apply path: fetch real issues via glab (execFile, shell: false — SEC-014).
|
|
716
|
+
try {
|
|
717
|
+
issues = await fetchReadyBacklog(execFile);
|
|
718
|
+
} catch (err) {
|
|
719
|
+
const msg = `glab issue list failed: ${err.message}`;
|
|
720
|
+
process.stderr.write(`autopilot-multi: ${msg}\n`);
|
|
721
|
+
if (flags.json) {
|
|
722
|
+
writeJsonEnvelope(false, null, { code: 'GLAB_FAILED', message: msg });
|
|
723
|
+
}
|
|
724
|
+
exitFn(2);
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// -------------------------------------------------------------------------
|
|
730
|
+
// 8. Build dep-graph + initial schedulable set
|
|
731
|
+
// -------------------------------------------------------------------------
|
|
732
|
+
const graph = buildGraph(issues);
|
|
733
|
+
const initialSet = nextReady(graph, new Set(), new Set());
|
|
734
|
+
|
|
735
|
+
diag(
|
|
736
|
+
`dep-graph built; total=${issues.length} ready=${initialSet.length ?? initialSet.size ?? 0}`,
|
|
737
|
+
flags.verbose
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
// -------------------------------------------------------------------------
|
|
741
|
+
// 9. Dry-run path (default): emit orchestration plan
|
|
742
|
+
// -------------------------------------------------------------------------
|
|
743
|
+
if (flags.dryRun) {
|
|
744
|
+
const plan = buildOrchestratorState(flags, snapshot, issues, parentRunId);
|
|
745
|
+
|
|
746
|
+
if (flags.json) {
|
|
747
|
+
writeJsonEnvelope(true, { plan });
|
|
748
|
+
} else {
|
|
749
|
+
process.stdout.write('autopilot-multi — dry-run orchestration plan\n');
|
|
750
|
+
process.stdout.write(` concurrencyCap : ${plan.concurrencyCap}\n`);
|
|
751
|
+
process.stdout.write(` totalIssues : ${plan.totalIssues}\n`);
|
|
752
|
+
process.stdout.write(` readyIssues : ${plan.readyIssues}\n`);
|
|
753
|
+
process.stdout.write(` executionPlan :\n`);
|
|
754
|
+
for (const item of plan.executionPlan) {
|
|
755
|
+
process.stdout.write(` #${item.iid} ${item.title}\n`);
|
|
756
|
+
}
|
|
757
|
+
process.stdout.write('\nPass --apply to execute.\n');
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
exitFn(0);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// -------------------------------------------------------------------------
|
|
765
|
+
// 10. Apply path: run the multi-story orchestration loop
|
|
766
|
+
// -------------------------------------------------------------------------
|
|
767
|
+
const state = buildOrchestratorState(flags, snapshot, issues, parentRunId);
|
|
768
|
+
const libs = {
|
|
769
|
+
depGraph: depGraphMod,
|
|
770
|
+
wtPipeline: wtPipelineMod,
|
|
771
|
+
mkLib: mkLibMod,
|
|
772
|
+
probe: resourceProbeMod,
|
|
773
|
+
};
|
|
774
|
+
// Pass bgIsolation into opts so runApplyLoop can route the dispatch path.
|
|
775
|
+
// bg-isolation: "none" → spawn sub-sessions in the main tree (no worktree creation).
|
|
776
|
+
// bg-isolation: "worktree" (default) → each story pipeline creates its own worktree.
|
|
777
|
+
const runOpts = { ...opts, bgIsolation };
|
|
778
|
+
|
|
779
|
+
const result = await runApplyLoop(state, libs, runOpts);
|
|
780
|
+
|
|
781
|
+
if (flags.json) {
|
|
782
|
+
console.log(JSON.stringify(result));
|
|
783
|
+
} else {
|
|
784
|
+
console.log(
|
|
785
|
+
`Multi-story apply complete: ${result.data.reason}` +
|
|
786
|
+
` — ${result.data.completed}/${result.data.loopCount} loops succeeded`,
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
exitFn(result.success ? 0 : 2);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// ---------------------------------------------------------------------------
|
|
794
|
+
// CLI guard — prevents top-level execution on import (deep-2 #368)
|
|
795
|
+
// ---------------------------------------------------------------------------
|
|
796
|
+
|
|
797
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
798
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
799
|
+
console.error('autopilot-multi failed:', err.message);
|
|
800
|
+
process.exit(2);
|
|
801
|
+
});
|
|
802
|
+
}
|