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,520 @@
|
|
|
1
|
+
// scripts/lib/autopilot/mr-draft.mjs
|
|
2
|
+
//
|
|
3
|
+
// Draft MR/PR creation module for autopilot --multi-story per OPEN-6 PRD.
|
|
4
|
+
// Implements the security pattern from ADR-364 C5: execFile (NOT shell) +
|
|
5
|
+
// binary allowlist + shell-metacharacter rejection on title/description.
|
|
6
|
+
//
|
|
7
|
+
// Policy values: 'off' (default), 'on-loop-start', 'on-green' (deferred).
|
|
8
|
+
//
|
|
9
|
+
// References:
|
|
10
|
+
// - "Autopilot Phase D — Per-Story Worktree Pipelines" (#341; archived in the private Meta-Vault) (OPEN-6 [DECIDED])
|
|
11
|
+
// - docs/adr/2026-05-10-364-remote-agent-substrate.md (C5 security finding)
|
|
12
|
+
|
|
13
|
+
import { execFile as execFileCb } from 'node:child_process';
|
|
14
|
+
import { promisify } from 'node:util';
|
|
15
|
+
|
|
16
|
+
const realExecFile = promisify(execFileCb);
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Binary allowlist (ADR-364 C5 + SEC-014)
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Bare binary names only — resolved at runtime via PATH by Node's execve.
|
|
22
|
+
// Reject any string containing '/' or '..' to prevent path traversal.
|
|
23
|
+
const MR_ALLOWLISTED_BINS = Object.freeze({
|
|
24
|
+
glab: 'glab', // GitLab CLI
|
|
25
|
+
gh: 'gh', // GitHub CLI
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// CLI-argument-boundary rejection regex (ADR-364 C5 / OPEN-6 / SEC-PD-MED-1 W4 audit)
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Reject only characters that corrupt CLI arg semantics when passed to execFile
|
|
32
|
+
// with shell: false. The shell metacharacter check (formerly /[;&|`$(){}[\]<>!]/)
|
|
33
|
+
// was over-broad — `shell: false` already prevents shell interpretation, so the
|
|
34
|
+
// regex only served to suppress legitimate GitLab issue titles containing `()`
|
|
35
|
+
// or `[]` (e.g. "Fix nav bug (closes #123)"). Narrowed to newline + null byte
|
|
36
|
+
// per W4 Q6 security-reviewer finding SEC-PD-MED-1 (confidence 0.95).
|
|
37
|
+
const ARG_BOUNDARY_DANGEROUS = /[\n\r\0]/;
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// MrDraftError
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Typed error class for MR-draft operations.
|
|
45
|
+
* Codes: 'COLLISION' | 'VALIDATION' | 'EXEC_FAILURE' | 'POLICY_OFF' | 'POLICY_DEFER' | 'UNSUPPORTED_VCS'
|
|
46
|
+
*/
|
|
47
|
+
export class MrDraftError extends Error {
|
|
48
|
+
constructor(message, code) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = 'MrDraftError';
|
|
51
|
+
this.code = code;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Input validation
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Validate title and description for shell-metacharacter safety.
|
|
61
|
+
* Throws MrDraftError(VALIDATION) if rejected.
|
|
62
|
+
*
|
|
63
|
+
* Rules:
|
|
64
|
+
* - title: non-empty string, ≤200 chars, no newlines, no shell-dangerous chars
|
|
65
|
+
* - description: string, ≤10000 chars, no shell-dangerous chars
|
|
66
|
+
*
|
|
67
|
+
* Note: this function is designed for validating user-supplied input fields
|
|
68
|
+
* (e.g. raw issueTitle from the issue tracker). The assembled MR body produced
|
|
69
|
+
* by buildMrBody() intentionally contains template-controlled characters such
|
|
70
|
+
* as `()` and `[]` which come from trusted code, not user input. Call this
|
|
71
|
+
* function with the raw user-supplied strings before assembling the body.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} title
|
|
74
|
+
* @param {string} description
|
|
75
|
+
*/
|
|
76
|
+
export function validateMrInputs(title, description) {
|
|
77
|
+
if (typeof title !== 'string' || title.length === 0) {
|
|
78
|
+
throw new MrDraftError('MR title must be a non-empty string', 'VALIDATION');
|
|
79
|
+
}
|
|
80
|
+
if (title.length > 200) {
|
|
81
|
+
throw new MrDraftError(`MR title exceeds 200 chars (got ${title.length})`, 'VALIDATION');
|
|
82
|
+
}
|
|
83
|
+
if (ARG_BOUNDARY_DANGEROUS.test(title)) {
|
|
84
|
+
throw new MrDraftError(
|
|
85
|
+
'MR title must not contain newline or null-byte characters',
|
|
86
|
+
'VALIDATION',
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (typeof description !== 'string') {
|
|
91
|
+
throw new MrDraftError('MR description must be a string', 'VALIDATION');
|
|
92
|
+
}
|
|
93
|
+
if (description.length > 10_000) {
|
|
94
|
+
throw new MrDraftError(
|
|
95
|
+
`MR description exceeds 10000 chars (got ${description.length})`,
|
|
96
|
+
'VALIDATION',
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
if (/\0/.test(description)) {
|
|
100
|
+
throw new MrDraftError('MR description must not contain null bytes', 'VALIDATION');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Check for an existing open MR/PR
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check if an MR/PR already exists for the source branch.
|
|
110
|
+
*
|
|
111
|
+
* @param {object} opts
|
|
112
|
+
* @param {'glab'|'gh'} opts.vcs
|
|
113
|
+
* @param {string} opts.branchName
|
|
114
|
+
* @param {Function} [opts.execFile] - defaults to promisified execFile from node:child_process
|
|
115
|
+
* @returns {Promise<{hasMR: boolean, mrIid: number|null, mrUrl: string|null}>}
|
|
116
|
+
*/
|
|
117
|
+
export async function checkExistingMR(opts) {
|
|
118
|
+
const { vcs, branchName } = opts;
|
|
119
|
+
const execFile = typeof opts.execFile === 'function' ? opts.execFile : realExecFile;
|
|
120
|
+
|
|
121
|
+
if (vcs === 'glab') {
|
|
122
|
+
const args = [
|
|
123
|
+
'mr',
|
|
124
|
+
'list',
|
|
125
|
+
'--source-branch',
|
|
126
|
+
branchName,
|
|
127
|
+
'--state',
|
|
128
|
+
'opened',
|
|
129
|
+
'--output',
|
|
130
|
+
'json',
|
|
131
|
+
];
|
|
132
|
+
const { stdout } = await execFile('glab', args, { shell: false, timeout: 5_000 });
|
|
133
|
+
let mrs;
|
|
134
|
+
try {
|
|
135
|
+
mrs = JSON.parse(stdout);
|
|
136
|
+
} catch {
|
|
137
|
+
throw new MrDraftError(
|
|
138
|
+
`glab output could not be parsed as JSON (stdout: ${stdout.slice(0, 120)})`,
|
|
139
|
+
'EXEC_FAILURE',
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
const existing = mrs[0];
|
|
143
|
+
return {
|
|
144
|
+
hasMR: !!existing,
|
|
145
|
+
mrIid: existing?.iid ?? null,
|
|
146
|
+
mrUrl: existing?.web_url ?? null,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (vcs === 'gh') {
|
|
151
|
+
const args = ['pr', 'list', '--head', branchName, '--state', 'open', '--json', 'number,url'];
|
|
152
|
+
const { stdout } = await execFile('gh', args, { shell: false, timeout: 5_000 });
|
|
153
|
+
let prs;
|
|
154
|
+
try {
|
|
155
|
+
prs = JSON.parse(stdout);
|
|
156
|
+
} catch {
|
|
157
|
+
throw new MrDraftError(
|
|
158
|
+
`gh output could not be parsed as JSON (stdout: ${stdout.slice(0, 120)})`,
|
|
159
|
+
'EXEC_FAILURE',
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
const existing = prs[0];
|
|
163
|
+
return {
|
|
164
|
+
hasMR: !!existing,
|
|
165
|
+
mrIid: existing?.number ?? null,
|
|
166
|
+
mrUrl: existing?.url ?? null,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
throw new MrDraftError(`Unsupported VCS for checkExistingMR: '${vcs}'`, 'UNSUPPORTED_VCS');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Evidence block (issue #669 — downstream-human-review legibility)
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// arXiv 2604.16754: reviewing an agent PR takes +441% longer because the
|
|
177
|
+
// reasoning is discarded at diff-time. Attaching a structured evidence block —
|
|
178
|
+
// decision-trace, fresh gate exit-codes, changed-files, carryover — makes the
|
|
179
|
+
// agent's work legible so a human reviews faster.
|
|
180
|
+
//
|
|
181
|
+
// Default-ON; opt-OUT via the SO_MR_EVIDENCE env-var (mirrors the
|
|
182
|
+
// SO_VAULT_DIR / SO_BASELINE_PATH host-local env-override idiom). When
|
|
183
|
+
// SO_MR_EVIDENCE === 'off' the block is omitted entirely.
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Render a single markdown line, falling back to a literal "n/a" when the
|
|
187
|
+
* value is missing/blank. Keeps the block markdown-safe and graceful.
|
|
188
|
+
* @param {unknown} value
|
|
189
|
+
* @returns {string}
|
|
190
|
+
*/
|
|
191
|
+
function evidenceValueOr(value) {
|
|
192
|
+
if (value === null || value === undefined) return 'n/a';
|
|
193
|
+
const s = String(value).trim();
|
|
194
|
+
return s.length === 0 ? 'n/a' : s;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Render the quality-gate sub-section: one row per gate with its exit code and
|
|
199
|
+
* (optional) count summary. A missing gate renders as "n/a".
|
|
200
|
+
* @param {object} [gates] - { test?, typecheck?, lint? } each { exitCode?, summary? }
|
|
201
|
+
* @returns {string[]}
|
|
202
|
+
*/
|
|
203
|
+
function renderGateRows(gates) {
|
|
204
|
+
const g = gates && typeof gates === 'object' ? gates : {};
|
|
205
|
+
const row = (label, gate) => {
|
|
206
|
+
if (!gate || typeof gate !== 'object') return `| ${label} | n/a | n/a |`;
|
|
207
|
+
const exit =
|
|
208
|
+
gate.exitCode === null || gate.exitCode === undefined
|
|
209
|
+
? 'n/a'
|
|
210
|
+
: String(gate.exitCode);
|
|
211
|
+
const summary = evidenceValueOr(gate.summary);
|
|
212
|
+
return `| ${label} | ${exit} | ${summary} |`;
|
|
213
|
+
};
|
|
214
|
+
return [
|
|
215
|
+
'| Gate | Exit code | Summary |',
|
|
216
|
+
'| --- | --- | --- |',
|
|
217
|
+
row('test', g.test),
|
|
218
|
+
row('typecheck', g.typecheck),
|
|
219
|
+
row('lint', g.lint),
|
|
220
|
+
];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Build the structured `## Evidence` block (collapsible <details>) from the
|
|
225
|
+
* OPTIONAL evidence fields on the draft context. Every sub-section degrades to
|
|
226
|
+
* "n/a" when its source field is absent — so existing callers
|
|
227
|
+
* (worktree-pipeline.mjs, skill-evolution/mr-opener.mjs) that pass none of
|
|
228
|
+
* these fields keep working and simply render an all-"n/a" block.
|
|
229
|
+
*
|
|
230
|
+
* @param {object} ctx
|
|
231
|
+
* @param {Array<{wave?: string|number, summary?: string}>|string} [ctx.waveSummary]
|
|
232
|
+
* Per-wave decision trace. Array of {wave, summary} rows, or a pre-rendered string.
|
|
233
|
+
* @param {{test?: object, typecheck?: object, lint?: object}} [ctx.gateResults]
|
|
234
|
+
* Quality-gate outcomes; each gate is {exitCode, summary}.
|
|
235
|
+
* @param {string[]|string} [ctx.changedFiles]
|
|
236
|
+
* Changed-files list (array of paths or a pre-rendered string).
|
|
237
|
+
* @param {Array<{ref?: string, note?: string}>|string} [ctx.carryover]
|
|
238
|
+
* Carryover items (array of {ref, note} or a pre-rendered string).
|
|
239
|
+
* @returns {string[]} markdown lines (empty array when no block should render)
|
|
240
|
+
*/
|
|
241
|
+
export function buildEvidenceBlock(ctx = {}) {
|
|
242
|
+
const { waveSummary, gateResults, changedFiles, carryover } = ctx;
|
|
243
|
+
|
|
244
|
+
// --- Decision-trace / per-wave summary ---
|
|
245
|
+
const waveLines = [];
|
|
246
|
+
if (typeof waveSummary === 'string' && waveSummary.trim().length > 0) {
|
|
247
|
+
waveLines.push(waveSummary.trim());
|
|
248
|
+
} else if (Array.isArray(waveSummary) && waveSummary.length > 0) {
|
|
249
|
+
for (const w of waveSummary) {
|
|
250
|
+
const label = evidenceValueOr(w?.wave);
|
|
251
|
+
const summary = evidenceValueOr(w?.summary);
|
|
252
|
+
waveLines.push(`- **${label}:** ${summary}`);
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
waveLines.push('n/a');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// --- Changed-files summary ---
|
|
259
|
+
const fileLines = [];
|
|
260
|
+
if (typeof changedFiles === 'string' && changedFiles.trim().length > 0) {
|
|
261
|
+
fileLines.push(changedFiles.trim());
|
|
262
|
+
} else if (Array.isArray(changedFiles) && changedFiles.length > 0) {
|
|
263
|
+
fileLines.push(`${changedFiles.length} file(s) changed:`);
|
|
264
|
+
for (const f of changedFiles) fileLines.push(`- \`${evidenceValueOr(f)}\``);
|
|
265
|
+
} else {
|
|
266
|
+
fileLines.push('n/a');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// --- Carryover ---
|
|
270
|
+
const carryLines = [];
|
|
271
|
+
if (typeof carryover === 'string' && carryover.trim().length > 0) {
|
|
272
|
+
carryLines.push(carryover.trim());
|
|
273
|
+
} else if (Array.isArray(carryover) && carryover.length > 0) {
|
|
274
|
+
for (const c of carryover) {
|
|
275
|
+
const ref = evidenceValueOr(c?.ref);
|
|
276
|
+
const note = evidenceValueOr(c?.note);
|
|
277
|
+
carryLines.push(`- ${ref} — ${note}`);
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
carryLines.push('n/a');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return [
|
|
284
|
+
'## Evidence',
|
|
285
|
+
'<details><summary>Decision-trace + verification evidence (for downstream human review)</summary>',
|
|
286
|
+
'',
|
|
287
|
+
'### Decision trace (per wave)',
|
|
288
|
+
...waveLines,
|
|
289
|
+
'',
|
|
290
|
+
'### Quality gates',
|
|
291
|
+
...renderGateRows(gateResults),
|
|
292
|
+
'',
|
|
293
|
+
'### Changed files',
|
|
294
|
+
...fileLines,
|
|
295
|
+
'',
|
|
296
|
+
'### Carryover',
|
|
297
|
+
...carryLines,
|
|
298
|
+
'',
|
|
299
|
+
'</details>',
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
// Build MR body
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Build the MR title and description from issue + autopilot context.
|
|
309
|
+
* Title is trimmed to ≤200 chars with '…' suffix if truncated.
|
|
310
|
+
* Description is capped at 10000 chars.
|
|
311
|
+
*
|
|
312
|
+
* An additive `## Evidence` block (decision-trace, gate exit-codes,
|
|
313
|
+
* changed-files, carryover) is appended by default for downstream-human-review
|
|
314
|
+
* legibility (issue #669). Opt out by setting the SO_MR_EVIDENCE env-var to
|
|
315
|
+
* 'off'. The evidence fields on `ctx` are all OPTIONAL — absent fields render
|
|
316
|
+
* as "n/a", so existing callers that pass none of them keep working.
|
|
317
|
+
*
|
|
318
|
+
* @param {object} ctx
|
|
319
|
+
* @param {string} ctx.issueTitle
|
|
320
|
+
* @param {number} ctx.issueIid
|
|
321
|
+
* @param {string} ctx.parentRunId
|
|
322
|
+
* @param {string} ctx.worktreePath
|
|
323
|
+
* @param {Array|string} [ctx.waveSummary] - optional per-wave decision trace
|
|
324
|
+
* @param {object} [ctx.gateResults] - optional { test, typecheck, lint } gate outcomes
|
|
325
|
+
* @param {string[]|string} [ctx.changedFiles] - optional changed-files summary
|
|
326
|
+
* @param {Array|string} [ctx.carryover] - optional carryover items
|
|
327
|
+
* @returns {{title: string, description: string}}
|
|
328
|
+
*/
|
|
329
|
+
export function buildMrBody(ctx) {
|
|
330
|
+
const { issueTitle, issueIid, parentRunId, worktreePath } = ctx;
|
|
331
|
+
|
|
332
|
+
const rawTitle = `[WIP] ${issueTitle} (Autopilot Loop #${parentRunId})`;
|
|
333
|
+
const title =
|
|
334
|
+
rawTitle.length > 200 ? rawTitle.slice(0, 199) + '…' : rawTitle;
|
|
335
|
+
|
|
336
|
+
const lines = [
|
|
337
|
+
'## Autopilot Draft',
|
|
338
|
+
'',
|
|
339
|
+
`**Issue:** #${issueIid}`,
|
|
340
|
+
`**Loop:** ${parentRunId}`,
|
|
341
|
+
`**Worktree:** ${worktreePath}`,
|
|
342
|
+
'',
|
|
343
|
+
'### Tests (TODO)',
|
|
344
|
+
'- [ ] Unit tests passing',
|
|
345
|
+
'- [ ] Integration tests passing',
|
|
346
|
+
'- [ ] CI pipeline green',
|
|
347
|
+
'',
|
|
348
|
+
'### Code Review',
|
|
349
|
+
'- [ ] Architecture review',
|
|
350
|
+
'- [ ] Security review',
|
|
351
|
+
];
|
|
352
|
+
|
|
353
|
+
// Evidence block — default ON, opt OUT via SO_MR_EVIDENCE=off (issue #669).
|
|
354
|
+
if (process.env.SO_MR_EVIDENCE !== 'off') {
|
|
355
|
+
lines.push('', ...buildEvidenceBlock(ctx));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
lines.push(
|
|
359
|
+
'',
|
|
360
|
+
'---',
|
|
361
|
+
`*Generated by session-orchestrator autopilot on ${new Date().toISOString()}*`,
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
const description = lines.join('\n');
|
|
365
|
+
|
|
366
|
+
// Cap description at 10000 chars (issue titles fit easily; guard for adversarial input)
|
|
367
|
+
const cappedDescription =
|
|
368
|
+
description.length > 10_000 ? description.slice(0, 10_000) : description;
|
|
369
|
+
|
|
370
|
+
return { title, description: cappedDescription };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ---------------------------------------------------------------------------
|
|
374
|
+
// maybeCreateDraftMR — primary entry point
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Create a draft MR/PR per the policy.
|
|
379
|
+
* Returns immediately if policy is 'off' or 'on-green' (the latter signals
|
|
380
|
+
* the caller to defer; caller is responsible for re-invoking with a different
|
|
381
|
+
* policy or after first green test).
|
|
382
|
+
*
|
|
383
|
+
* @param {object} loop
|
|
384
|
+
* @param {'gitlab'|'github'} loop.vcs
|
|
385
|
+
* @param {number} loop.issueIid
|
|
386
|
+
* @param {string} loop.issueTitle
|
|
387
|
+
* @param {string} loop.branchName
|
|
388
|
+
* @param {string} loop.parentRunId
|
|
389
|
+
* @param {string} loop.worktreePath
|
|
390
|
+
* @param {'off'|'on-loop-start'|'on-green'} loop.draftMrPolicy
|
|
391
|
+
* @param {object} [opts]
|
|
392
|
+
* @param {Function} [opts.execFile]
|
|
393
|
+
* @param {(level: string, msg: string) => void} [opts.log]
|
|
394
|
+
* @returns {Promise<{created: boolean, deferred?: boolean, existing?: boolean, mrUrl?: string|null, error?: string}>}
|
|
395
|
+
*/
|
|
396
|
+
export async function maybeCreateDraftMR(loop, opts = {}) {
|
|
397
|
+
const log =
|
|
398
|
+
typeof opts.log === 'function'
|
|
399
|
+
? opts.log
|
|
400
|
+
: (_level, _msg) => {}; // no-op default
|
|
401
|
+
const execFile = typeof opts.execFile === 'function' ? opts.execFile : realExecFile;
|
|
402
|
+
|
|
403
|
+
const { draftMrPolicy, vcs, issueIid, issueTitle, branchName, parentRunId, worktreePath } = loop;
|
|
404
|
+
|
|
405
|
+
// --- Policy: off ---
|
|
406
|
+
if (draftMrPolicy === 'off') {
|
|
407
|
+
log('info', 'mr-draft: policy=off, skipping MR creation');
|
|
408
|
+
return { created: false };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// --- Policy: on-green (deferred) ---
|
|
412
|
+
if (draftMrPolicy === 'on-green') {
|
|
413
|
+
log('info', 'mr-draft: policy=on-green, deferring MR creation until first green test');
|
|
414
|
+
return { created: false, deferred: true };
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// --- Policy: on-loop-start ---
|
|
418
|
+
if (draftMrPolicy === 'on-loop-start') {
|
|
419
|
+
// Map loop.vcs ('gitlab'|'github') to CLI binary ('glab'|'gh')
|
|
420
|
+
let vcsBin;
|
|
421
|
+
if (vcs === 'gitlab') {
|
|
422
|
+
vcsBin = MR_ALLOWLISTED_BINS.glab;
|
|
423
|
+
} else if (vcs === 'github') {
|
|
424
|
+
vcsBin = MR_ALLOWLISTED_BINS.gh;
|
|
425
|
+
} else {
|
|
426
|
+
throw new MrDraftError(`Unsupported vcs value: '${vcs}'`, 'UNSUPPORTED_VCS');
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Validate user-supplied issueTitle before building.
|
|
430
|
+
// validateMrInputs guards against shell-dangerous chars from the issue
|
|
431
|
+
// tracker. The description arg is '' here because the description is
|
|
432
|
+
// entirely template-controlled (checkboxes, headers) — only issueTitle
|
|
433
|
+
// is user-supplied and needs the injection guard. The assembled body
|
|
434
|
+
// intentionally contains '()' and '[ ]' from the trusted template.
|
|
435
|
+
validateMrInputs(issueTitle, '');
|
|
436
|
+
|
|
437
|
+
// Build title/description from validated inputs
|
|
438
|
+
const { title, description } = buildMrBody({
|
|
439
|
+
issueTitle,
|
|
440
|
+
issueIid,
|
|
441
|
+
parentRunId,
|
|
442
|
+
worktreePath,
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// Collision check — skip if MR already exists
|
|
446
|
+
let existingCheck;
|
|
447
|
+
try {
|
|
448
|
+
existingCheck = await checkExistingMR({
|
|
449
|
+
vcs: vcsBin, // 'glab' or 'gh'
|
|
450
|
+
branchName,
|
|
451
|
+
execFile,
|
|
452
|
+
});
|
|
453
|
+
} catch (err) {
|
|
454
|
+
log('error', `mr-draft: collision check failed — ${err.message}`);
|
|
455
|
+
return { created: false, error: err.message };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (existingCheck.hasMR) {
|
|
459
|
+
log(
|
|
460
|
+
'info',
|
|
461
|
+
`mr-draft: existing MR found (iid=${existingCheck.mrIid}), skipping creation`,
|
|
462
|
+
);
|
|
463
|
+
return { created: false, existing: true, mrUrl: existingCheck.mrUrl };
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Build arg vector — no template-string interpolation in command args
|
|
467
|
+
let createArgs;
|
|
468
|
+
if (vcs === 'gitlab') {
|
|
469
|
+
createArgs = [
|
|
470
|
+
'mr',
|
|
471
|
+
'create',
|
|
472
|
+
'--draft',
|
|
473
|
+
'--title',
|
|
474
|
+
title,
|
|
475
|
+
'--description',
|
|
476
|
+
description,
|
|
477
|
+
'--source-branch',
|
|
478
|
+
branchName,
|
|
479
|
+
];
|
|
480
|
+
} else {
|
|
481
|
+
// github
|
|
482
|
+
createArgs = [
|
|
483
|
+
'pr',
|
|
484
|
+
'create',
|
|
485
|
+
'--draft',
|
|
486
|
+
'--title',
|
|
487
|
+
title,
|
|
488
|
+
'--body',
|
|
489
|
+
description,
|
|
490
|
+
'--head',
|
|
491
|
+
branchName,
|
|
492
|
+
];
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Execute MR/PR creation
|
|
496
|
+
try {
|
|
497
|
+
const { stdout } = await execFile(vcsBin, createArgs, {
|
|
498
|
+
shell: false,
|
|
499
|
+
timeout: 10_000,
|
|
500
|
+
maxBuffer: 1024 * 1024,
|
|
501
|
+
});
|
|
502
|
+
const mrUrl = stdout.trim() || null;
|
|
503
|
+
log('info', `mr-draft: created draft MR/PR — ${mrUrl}`);
|
|
504
|
+
return { created: true, mrUrl };
|
|
505
|
+
} catch (err) {
|
|
506
|
+
if (err.code === 'ENOENT') {
|
|
507
|
+
log('error', `mr-draft: binary '${vcsBin}' not found in PATH`);
|
|
508
|
+
return { created: false, error: 'binary not found' };
|
|
509
|
+
}
|
|
510
|
+
log('error', `mr-draft: execFile failed — ${err.message}`);
|
|
511
|
+
return { created: false, error: err.message };
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// --- Unknown policy ---
|
|
516
|
+
throw new MrDraftError(
|
|
517
|
+
`Unknown draftMrPolicy: '${draftMrPolicy}'. Expected 'off', 'on-loop-start', or 'on-green'`,
|
|
518
|
+
'VALIDATION',
|
|
519
|
+
);
|
|
520
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// scripts/lib/autopilot/multi-killswitch.mjs
|
|
2
|
+
//
|
|
3
|
+
// 11th kill-switch (STALE_SUBAGENT_MIN) + cohort policy + concurrency cap for
|
|
4
|
+
// autopilot --multi-story. Pure decision logic; no I/O, no shell.
|
|
5
|
+
//
|
|
6
|
+
// References:
|
|
7
|
+
// - "Autopilot Phase D — Per-Story Worktree Pipelines" (#341; archived in the private Meta-Vault) (OPEN-2, OPEN-3, OPEN-5 decisions)
|
|
8
|
+
// - scripts/lib/autopilot/kill-switches.mjs (existing 10 switches; STALL_TIMEOUT precedent)
|
|
9
|
+
// - scripts/lib/resource-probe.mjs (snapshot shape)
|
|
10
|
+
|
|
11
|
+
import { KILL_SWITCHES } from './kill-switches.mjs';
|
|
12
|
+
|
|
13
|
+
// 11th kill-switch constant — additive to the existing 10 in KILL_SWITCHES
|
|
14
|
+
/** @type {string} */
|
|
15
|
+
export const STALE_SUBAGENT_MIN = 'stale-subagent-min';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Calculate concurrency cap per OPEN-2 decision from PRD §4.
|
|
19
|
+
* Formula: min(staticFloor, max(1, floor(ram_free_gb / ramPerLoopGb) - reserveSlots))
|
|
20
|
+
*
|
|
21
|
+
* Defensive: null/undefined ram_free_gb → 1; memory_pressure_pct_free < 15 → cap 1.
|
|
22
|
+
*
|
|
23
|
+
* @param {{ ram_free_gb: number, memory_pressure_pct_free?: number, claude_processes_count?: number }} snapshot
|
|
24
|
+
* @param {object} [opts]
|
|
25
|
+
* @param {number} [opts.staticFloor=3] - hard cap regardless of resources
|
|
26
|
+
* @param {number} [opts.ramPerLoopGb=4] - per-looper RAM budget
|
|
27
|
+
* @param {number} [opts.reserveSlots=1] - sessions to reserve for system + coordinator
|
|
28
|
+
* @returns {number} integer in [1, staticFloor]
|
|
29
|
+
*/
|
|
30
|
+
export function calculateConcurrencyCap(snapshot, opts = {}) {
|
|
31
|
+
const staticFloor = opts.staticFloor ?? 3;
|
|
32
|
+
const ramPerLoopGb = opts.ramPerLoopGb ?? 4;
|
|
33
|
+
const reserveSlots = opts.reserveSlots ?? 1;
|
|
34
|
+
|
|
35
|
+
// macOS memory pressure degraded guard
|
|
36
|
+
if (
|
|
37
|
+
typeof snapshot?.memory_pressure_pct_free === 'number' &&
|
|
38
|
+
snapshot.memory_pressure_pct_free < 15
|
|
39
|
+
) {
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const ramFreeGb = snapshot?.ram_free_gb;
|
|
44
|
+
if (ramFreeGb === null || ramFreeGb === undefined || typeof ramFreeGb !== 'number' || !Number.isFinite(ramFreeGb)) {
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const computed = Math.floor(ramFreeGb / ramPerLoopGb) - reserveSlots;
|
|
49
|
+
return Math.min(staticFloor, Math.max(1, computed));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @typedef {object} LoopRegistration
|
|
54
|
+
* @property {string} loopId
|
|
55
|
+
* @property {number} pid
|
|
56
|
+
* @property {string} parentRunId
|
|
57
|
+
* @property {number} issueIid
|
|
58
|
+
* @property {'queued'|'running'|'complete'|'failed'} status
|
|
59
|
+
* @property {string|null} killSwitch
|
|
60
|
+
* @property {number} spiralRecoveryCount
|
|
61
|
+
* @property {number} startedAt - epoch ms
|
|
62
|
+
* @property {number} lastActivityAt - epoch ms
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Evaluate cross-loop kill-switches (STALE_SUBAGENT_MIN only — per-loop
|
|
67
|
+
* switches like SPIRAL are handled by postSessionKillSwitch upstream).
|
|
68
|
+
* Returns the FIRST kill condition that fires, or null.
|
|
69
|
+
*
|
|
70
|
+
* @param {LoopRegistration[]} loops
|
|
71
|
+
* @param {object} [opts]
|
|
72
|
+
* @param {number} [opts.staleSubagentMinSeconds=600]
|
|
73
|
+
* @param {() => number} [opts.nowMs=Date.now]
|
|
74
|
+
* @returns {{ kill: string, detail: string, loopId?: string }|null}
|
|
75
|
+
*/
|
|
76
|
+
export function evaluateMultiKillSwitches(loops, opts = {}) {
|
|
77
|
+
if (!Array.isArray(loops) || loops.length === 0) return null;
|
|
78
|
+
|
|
79
|
+
const staleThresholdSeconds = opts.staleSubagentMinSeconds ?? 600;
|
|
80
|
+
const now = (opts.nowMs ?? Date.now)();
|
|
81
|
+
|
|
82
|
+
for (const loop of loops) {
|
|
83
|
+
if (!loop || loop.status !== 'running') continue;
|
|
84
|
+
|
|
85
|
+
const lastActivity = loop.lastActivityAt;
|
|
86
|
+
if (typeof lastActivity !== 'number' || !Number.isFinite(lastActivity)) continue;
|
|
87
|
+
|
|
88
|
+
const idleMs = now - lastActivity;
|
|
89
|
+
const idleSeconds = Math.floor(idleMs / 1000);
|
|
90
|
+
|
|
91
|
+
if (idleMs > staleThresholdSeconds * 1000) {
|
|
92
|
+
return {
|
|
93
|
+
kill: STALE_SUBAGENT_MIN,
|
|
94
|
+
detail: `loop ${loop.loopId} stale > ${idleSeconds}s (threshold ${staleThresholdSeconds}s)`,
|
|
95
|
+
loopId: loop.loopId,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Decide whether a sibling loop's failure should abort the cohort.
|
|
105
|
+
* Per OPEN-5 hybrid policy:
|
|
106
|
+
* - First SPIRAL AND target loop spiralRecoveryCount < 1 → retry
|
|
107
|
+
* - Second SPIRAL OR target already retried → cohort-abort
|
|
108
|
+
*
|
|
109
|
+
* @param {LoopRegistration[]} loops
|
|
110
|
+
* @param {string} failedLoopId
|
|
111
|
+
* @returns {{ action: 'retry'|'cohort-abort', reason: string }}
|
|
112
|
+
*/
|
|
113
|
+
export function decideCohortAction(loops, failedLoopId) {
|
|
114
|
+
if (!Array.isArray(loops)) {
|
|
115
|
+
return { action: 'cohort-abort', reason: 'no loop registry provided' };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const spiralCount = loops.filter(
|
|
119
|
+
(l) => l && l.killSwitch === KILL_SWITCHES.SPIRAL,
|
|
120
|
+
).length;
|
|
121
|
+
|
|
122
|
+
const targetLoop = loops.find((l) => l && l.loopId === failedLoopId);
|
|
123
|
+
const targetSpiralRecoveries = targetLoop?.spiralRecoveryCount ?? 0;
|
|
124
|
+
|
|
125
|
+
if (spiralCount <= 1 && targetSpiralRecoveries < 1) {
|
|
126
|
+
return {
|
|
127
|
+
action: 'retry',
|
|
128
|
+
reason: 'first-strike spiral, recovering loop ' + failedLoopId,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
action: 'cohort-abort',
|
|
134
|
+
reason: 'second-strike spiral or cohort threshold',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check if the orchestrator stop condition is met (OPEN-3 layered policy).
|
|
140
|
+
* Priority: (1) cohort-abort → (2) backlog-empty → (3) inactivity-timeout
|
|
141
|
+
*
|
|
142
|
+
* @param {object} state
|
|
143
|
+
* @param {LoopRegistration[]} state.activeLoops
|
|
144
|
+
* @param {object[]} state.readyBacklog
|
|
145
|
+
* @param {number} state.lastCompletionAt
|
|
146
|
+
* @param {object} [opts]
|
|
147
|
+
* @param {number} [opts.inactivityTimeoutMs=300000]
|
|
148
|
+
* @param {() => number} [opts.nowMs=Date.now]
|
|
149
|
+
* @returns {{ stop: boolean, reason?: 'first-kill-switch'|'backlog-empty'|'inactivity-timeout' }}
|
|
150
|
+
*/
|
|
151
|
+
export function shouldStopOrchestrator(state, opts = {}) {
|
|
152
|
+
const inactivityTimeoutMs = opts.inactivityTimeoutMs ?? 300_000;
|
|
153
|
+
const now = (opts.nowMs ?? Date.now)();
|
|
154
|
+
|
|
155
|
+
const activeLoops = Array.isArray(state?.activeLoops) ? state.activeLoops : [];
|
|
156
|
+
const readyBacklog = Array.isArray(state?.readyBacklog) ? state.readyBacklog : [];
|
|
157
|
+
|
|
158
|
+
// Priority 1: cohort-abort triggered by spiral on an active loop
|
|
159
|
+
for (const loop of activeLoops) {
|
|
160
|
+
if (!loop || loop.killSwitch !== KILL_SWITCHES.SPIRAL) continue;
|
|
161
|
+
|
|
162
|
+
const decision = decideCohortAction(activeLoops, loop.loopId);
|
|
163
|
+
if (decision.action === 'cohort-abort') {
|
|
164
|
+
return { stop: true, reason: 'first-kill-switch' };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Priority 2: all work drained
|
|
169
|
+
if (activeLoops.length === 0 && readyBacklog.length === 0) {
|
|
170
|
+
return { stop: true, reason: 'backlog-empty' };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Priority 3: orchestrator inactivity timeout (only meaningful while loops are running)
|
|
174
|
+
if (
|
|
175
|
+
activeLoops.length > 0 &&
|
|
176
|
+
typeof state?.lastCompletionAt === 'number' &&
|
|
177
|
+
Number.isFinite(state.lastCompletionAt) &&
|
|
178
|
+
now - state.lastCompletionAt > inactivityTimeoutMs
|
|
179
|
+
) {
|
|
180
|
+
return { stop: true, reason: 'inactivity-timeout' };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return { stop: false };
|
|
184
|
+
}
|