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,507 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mr-opener.mjs — Open a merge/pull request for an MR-tier repair candidate
|
|
3
|
+
* (Epic #643 Skill Self-Evolution Foundation / issue #647 — C2 auto-repair engine).
|
|
4
|
+
*
|
|
5
|
+
* MR-tier candidates are `plugin-skill` / `local-skill` targets. Per the
|
|
6
|
+
* blast-radius classifier (`blast-radius-classifier.mjs`), these are ALWAYS
|
|
7
|
+
* routed through a merge request — never applied autonomously. This module
|
|
8
|
+
* carries the candidate's diff onto a real branch and opens a draft MR/PR.
|
|
9
|
+
*
|
|
10
|
+
* DESIGN: heavy REUSE of existing modules. The ONE net-new primitive is the
|
|
11
|
+
* git add/commit/push triple. Everything else delegates:
|
|
12
|
+
* - `validateMrInputs` / `buildMrBody` / `maybeCreateDraftMR` ← mr-draft.mjs
|
|
13
|
+
* - VCS auto-detect (`bin: 'glab'|'gh'|null`) ← vcs-detector.mjs
|
|
14
|
+
* - owner-leakage gate (CLI script, scans TRACKED files) ← check-owner-leakage.mjs
|
|
15
|
+
* - conditional slopcheck gate (`classifyPackages`) ← slopcheck.mjs
|
|
16
|
+
*
|
|
17
|
+
* CONTRACT: NEVER throws. Every failure path degrades to an advisory or blocked
|
|
18
|
+
* result. glab/gh absence or dry-run → `advisory`. Gate failure → `blocked`.
|
|
19
|
+
*
|
|
20
|
+
* SECURITY: all subprocess invocations use execFile arg-vectors (shell:false) —
|
|
21
|
+
* never shell-string interpolation (RCE surface). Branch/title/description are
|
|
22
|
+
* validated via `validateMrInputs` (newline/null-byte guard) before use.
|
|
23
|
+
*
|
|
24
|
+
* DEPENDENCY-INJECTION: every external seam is injectable via `opts` so the
|
|
25
|
+
* whole flow is testable without real git/glab/gh:
|
|
26
|
+
* - opts.execFile — promisified execFile for glab/gh mr-create (mr-draft seam)
|
|
27
|
+
* - opts.git — async ({ args, cwd }) => { stdout } git runner
|
|
28
|
+
* - opts.leakageScan— async ({ repoRoot }) => { ok, exitCode } owner-leakage gate
|
|
29
|
+
* - opts.slopcheck — async (pkgs, { repoRoot }) => Array<{classification}>
|
|
30
|
+
* - opts.vcsDetect — ({ config, projectRoot }) => { bin } VCS detector
|
|
31
|
+
* - opts.createMr — async (loop, draftOpts) => { created, mrUrl } MR opener
|
|
32
|
+
* - opts.log — (level, msg) => void diagnostic logger
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { execFile as execFileCb, spawnSync } from 'node:child_process';
|
|
36
|
+
import { promisify } from 'node:util';
|
|
37
|
+
import path from 'node:path';
|
|
38
|
+
import { fileURLToPath } from 'node:url';
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
validateMrInputs,
|
|
42
|
+
buildMrBody,
|
|
43
|
+
maybeCreateDraftMR,
|
|
44
|
+
MrDraftError,
|
|
45
|
+
} from '../autopilot/mr-draft.mjs';
|
|
46
|
+
import { detectVcsCommand } from '../tmux-layout/vcs-detector.mjs';
|
|
47
|
+
import { classifyPackages } from '../slopcheck.mjs';
|
|
48
|
+
|
|
49
|
+
const realExecFile = promisify(execFileCb);
|
|
50
|
+
|
|
51
|
+
/** Absolute path to the owner-leakage CLI scanner (a script, not an importable fn). */
|
|
52
|
+
const OWNER_LEAKAGE_SCRIPT = fileURLToPath(
|
|
53
|
+
new URL('../validate/check-owner-leakage.mjs', import.meta.url),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {Object} RepairCandidate
|
|
58
|
+
* @property {string} id Deterministic short hash (idempotency key).
|
|
59
|
+
* @property {string} target_path Repo-relative path the repair targets.
|
|
60
|
+
* @property {string} [proposed_change] Human-readable description of the fix.
|
|
61
|
+
* @property {string} [rationale] Why this candidate exists.
|
|
62
|
+
* @property {string} [source_ref] Back-reference into the source feeder.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @typedef {Object} RepairDiff
|
|
67
|
+
* @property {string} [content] Full new file content to write at target_path.
|
|
68
|
+
* @property {string} [raw] Raw unified-diff text (used for the pre-commit
|
|
69
|
+
* owner-leakage preview + package-add detection).
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @typedef {Object} OpenRepairMrResult
|
|
74
|
+
* @property {boolean} ok
|
|
75
|
+
* @property {'mr-opened'|'advisory'|'blocked'} action
|
|
76
|
+
* @property {string} [mrUrl]
|
|
77
|
+
* @property {string} [reason]
|
|
78
|
+
* @property {{ ownerLeakage: 'pass'|'fail', slopcheck?: 'pass'|'fail'|'skipped' }} [gate]
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Net-new primitive: git runner (injectable)
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Default git runner — execFile arg-vector, shell:false. Never interpolates a
|
|
87
|
+
* shell string. Resolves `{ stdout }`; rejects on non-zero exit (the caller
|
|
88
|
+
* wraps this in try/catch and degrades to advisory).
|
|
89
|
+
*
|
|
90
|
+
* @param {{ args: string[], cwd: string }} arg0
|
|
91
|
+
* @returns {Promise<{ stdout: string }>}
|
|
92
|
+
*/
|
|
93
|
+
async function defaultGit({ args, cwd }) {
|
|
94
|
+
const { stdout } = await realExecFile('git', args, {
|
|
95
|
+
cwd,
|
|
96
|
+
shell: false,
|
|
97
|
+
timeout: 30_000,
|
|
98
|
+
maxBuffer: 1024 * 1024,
|
|
99
|
+
});
|
|
100
|
+
return { stdout: stdout ?? '' };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Default owner-leakage gate — spawns the CLI scanner against the committed
|
|
105
|
+
* worktree. Exit 0 = clean, non-zero = leak. Never throws; on spawn error
|
|
106
|
+
* (e.g. ENOENT) returns `{ ok: false }` so the caller can decide. We treat a
|
|
107
|
+
* spawn FAILURE distinctly from a leak DETECTION via the `spawnError` flag.
|
|
108
|
+
*
|
|
109
|
+
* @param {{ repoRoot: string }} arg0
|
|
110
|
+
* @returns {Promise<{ ok: boolean, exitCode: number, spawnError?: boolean }>}
|
|
111
|
+
*/
|
|
112
|
+
async function defaultLeakageScan({ repoRoot }) {
|
|
113
|
+
const result = spawnSync('node', [OWNER_LEAKAGE_SCRIPT, repoRoot], {
|
|
114
|
+
encoding: 'utf8',
|
|
115
|
+
timeout: 30_000,
|
|
116
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
117
|
+
});
|
|
118
|
+
if (result.error) {
|
|
119
|
+
// Scanner could not run at all — surface as a spawn error, not a leak.
|
|
120
|
+
return { ok: false, exitCode: -1, spawnError: true };
|
|
121
|
+
}
|
|
122
|
+
return { ok: result.status === 0, exitCode: result.status ?? -1 };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// Package-add detection (slopcheck gate trigger)
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Detect newly-ADDED package dependencies in a raw unified diff. Only lines that
|
|
131
|
+
* START with a single `+` (added) within a manifest hunk are considered. Returns
|
|
132
|
+
* a `{ name, registry }[]` for slopcheck. Pure prose/config diffs → [].
|
|
133
|
+
*
|
|
134
|
+
* Supported manifests: package.json (npm), requirements.txt (pip),
|
|
135
|
+
* Cargo.toml (cargo). Detection is intentionally conservative — when in doubt
|
|
136
|
+
* it emits nothing (slopcheck then reports 'skipped').
|
|
137
|
+
*
|
|
138
|
+
* @param {string|undefined} rawDiff
|
|
139
|
+
* @returns {Array<{ name: string, registry: 'npm'|'pip'|'cargo' }>}
|
|
140
|
+
*/
|
|
141
|
+
export function detectAddedPackages(rawDiff) {
|
|
142
|
+
if (typeof rawDiff !== 'string' || rawDiff.length === 0) return [];
|
|
143
|
+
|
|
144
|
+
const pkgs = [];
|
|
145
|
+
const seen = new Set();
|
|
146
|
+
let registry = null; // active manifest registry, set by the +++ file header
|
|
147
|
+
|
|
148
|
+
const add = (name, reg) => {
|
|
149
|
+
const key = `${reg}:${name}`;
|
|
150
|
+
if (name && !seen.has(key)) {
|
|
151
|
+
seen.add(key);
|
|
152
|
+
pkgs.push({ name, registry: reg });
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
for (const line of rawDiff.split('\n')) {
|
|
157
|
+
// File header sets the active registry for subsequent + lines.
|
|
158
|
+
if (line.startsWith('+++ ') || line.startsWith('diff --git')) {
|
|
159
|
+
if (/package\.json\b/.test(line)) registry = 'npm';
|
|
160
|
+
else if (/requirements\.txt\b/.test(line)) registry = 'pip';
|
|
161
|
+
else if (/Cargo\.toml\b/.test(line)) registry = 'cargo';
|
|
162
|
+
else registry = null;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
// Only consider added lines (single leading '+', not the '+++' header).
|
|
166
|
+
if (!line.startsWith('+') || line.startsWith('+++')) continue;
|
|
167
|
+
if (registry === null) continue;
|
|
168
|
+
|
|
169
|
+
const added = line.slice(1).trim();
|
|
170
|
+
if (added.length === 0) continue;
|
|
171
|
+
|
|
172
|
+
if (registry === 'npm') {
|
|
173
|
+
// npm dependency line: "name": "^1.2.3" (scope-aware).
|
|
174
|
+
const m = added.match(/^"(@?[a-z0-9~][\w.~/-]*)"\s*:/i);
|
|
175
|
+
if (m) add(m[1], 'npm');
|
|
176
|
+
} else if (registry === 'pip') {
|
|
177
|
+
// requirements line: name==1.2.3 / name>=1.0 / name (skip comments).
|
|
178
|
+
if (added.startsWith('#')) continue;
|
|
179
|
+
const m = added.match(/^([A-Za-z0-9][\w.-]*)/);
|
|
180
|
+
if (m) add(m[1], 'pip');
|
|
181
|
+
} else if (registry === 'cargo') {
|
|
182
|
+
// Cargo.toml dependency line: name = "1.2.3" (skip table headers).
|
|
183
|
+
if (added.startsWith('[')) continue;
|
|
184
|
+
const m = added.match(/^([A-Za-z0-9][\w-]*)\s*=/);
|
|
185
|
+
if (m) add(m[1], 'cargo');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return pkgs;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// Internal helpers
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Map a `vcsDetect().bin` value ('glab'|'gh'|null) to the loop.vcs token
|
|
198
|
+
* ('gitlab'|'github') that maybeCreateDraftMR expects.
|
|
199
|
+
* @param {string|null|undefined} bin
|
|
200
|
+
* @returns {'gitlab'|'github'|null}
|
|
201
|
+
*/
|
|
202
|
+
function binToVcs(bin) {
|
|
203
|
+
if (bin === 'glab') return 'gitlab';
|
|
204
|
+
if (bin === 'gh') return 'github';
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Normalise the caller-supplied `vcs` argument OR detector result into a
|
|
210
|
+
* `{ bin, vcs }` pair. The explicit `vcs` arg wins; otherwise we auto-detect.
|
|
211
|
+
* @param {*} vcsArg Caller arg: 'gitlab'|'github'|'glab'|'gh'|undefined.
|
|
212
|
+
* @param {Function} vcsDetect detectVcsCommand seam.
|
|
213
|
+
* @param {string} repoRoot
|
|
214
|
+
* @returns {{ bin: 'glab'|'gh'|null, vcs: 'gitlab'|'github'|null }}
|
|
215
|
+
*/
|
|
216
|
+
function resolveVcs(vcsArg, vcsDetect, repoRoot) {
|
|
217
|
+
if (vcsArg === 'gitlab' || vcsArg === 'glab') return { bin: 'glab', vcs: 'gitlab' };
|
|
218
|
+
if (vcsArg === 'github' || vcsArg === 'gh') return { bin: 'gh', vcs: 'github' };
|
|
219
|
+
|
|
220
|
+
// Auto-detect via the seam. Pass vcs through Session-Config-shaped `config`.
|
|
221
|
+
let detected;
|
|
222
|
+
try {
|
|
223
|
+
detected = vcsDetect({ config: {}, projectRoot: repoRoot }) ?? {};
|
|
224
|
+
} catch {
|
|
225
|
+
detected = {};
|
|
226
|
+
}
|
|
227
|
+
const bin = detected.bin === 'glab' || detected.bin === 'gh' ? detected.bin : null;
|
|
228
|
+
return { bin, vcs: binToVcs(bin) };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Build a safe MR title + body from the candidate. Title is validated for
|
|
233
|
+
* newline/null-byte safety via validateMrInputs before any subprocess use.
|
|
234
|
+
* @param {RepairCandidate} candidate
|
|
235
|
+
* @returns {{ title: string, description: string }}
|
|
236
|
+
*/
|
|
237
|
+
function buildCandidateMrBody(candidate) {
|
|
238
|
+
const proposed = candidate.proposed_change || 'skill repair';
|
|
239
|
+
const built = buildMrBody({
|
|
240
|
+
issueTitle: `Auto-repair: ${proposed}`,
|
|
241
|
+
issueIid: candidate.source_ref ?? candidate.id ?? 'unknown',
|
|
242
|
+
parentRunId: `repair/${candidate.id ?? 'unknown'}`,
|
|
243
|
+
worktreePath: candidate.target_path ?? '(unknown)',
|
|
244
|
+
});
|
|
245
|
+
// Validate the user/feeder-derived title before it reaches any execFile.
|
|
246
|
+
validateMrInputs(built.title, '');
|
|
247
|
+
return built;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
// Public API
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Open a draft merge/pull request for an MR-tier repair candidate.
|
|
256
|
+
*
|
|
257
|
+
* Flow (see issue #647 spec):
|
|
258
|
+
* 1. dryRun → owner-leakage preview (best-effort) + MR-body build → advisory.
|
|
259
|
+
* 2. Resolve VCS; no glab/gh → advisory.
|
|
260
|
+
* 3. Create branch + write change + git add/commit/push (net-new primitive).
|
|
261
|
+
* 4. GATE owner-leakage (always) → fail = blocked.
|
|
262
|
+
* 5. GATE slopcheck (only when the diff ADDS a package) → SLOP = blocked.
|
|
263
|
+
* 6. Open the MR → mr-opened. ENOENT/exec failure → advisory.
|
|
264
|
+
*
|
|
265
|
+
* NEVER throws — every error path degrades to advisory or blocked.
|
|
266
|
+
*
|
|
267
|
+
* @param {{ candidate: RepairCandidate, diff: RepairDiff, repoRoot: string, dryRun?: boolean, vcs?: string }} arg0
|
|
268
|
+
* @param {object} [opts] dependency-injection seams (see module JSDoc).
|
|
269
|
+
* @returns {Promise<OpenRepairMrResult>}
|
|
270
|
+
*/
|
|
271
|
+
export async function openRepairMr(
|
|
272
|
+
{ candidate, diff, repoRoot, dryRun = false, vcs } = {},
|
|
273
|
+
opts = {},
|
|
274
|
+
) {
|
|
275
|
+
const log = typeof opts.log === 'function' ? opts.log : () => {};
|
|
276
|
+
|
|
277
|
+
// Seam resolution — real implementations are the defaults; opts override.
|
|
278
|
+
const git = typeof opts.git === 'function' ? opts.git : defaultGit;
|
|
279
|
+
const leakageScan =
|
|
280
|
+
typeof opts.leakageScan === 'function' ? opts.leakageScan : defaultLeakageScan;
|
|
281
|
+
const slopcheck = typeof opts.slopcheck === 'function' ? opts.slopcheck : classifyPackages;
|
|
282
|
+
const vcsDetect = typeof opts.vcsDetect === 'function' ? opts.vcsDetect : detectVcsCommand;
|
|
283
|
+
const createMr = typeof opts.createMr === 'function' ? opts.createMr : maybeCreateDraftMR;
|
|
284
|
+
|
|
285
|
+
// --- Input guard (never throw) ---
|
|
286
|
+
if (!candidate || typeof candidate !== 'object' || typeof candidate.id !== 'string') {
|
|
287
|
+
log('error', 'mr-opener: missing/invalid candidate');
|
|
288
|
+
return { ok: false, action: 'advisory', reason: 'invalid candidate input' };
|
|
289
|
+
}
|
|
290
|
+
if (typeof repoRoot !== 'string' || repoRoot.length === 0) {
|
|
291
|
+
log('error', 'mr-opener: missing repoRoot');
|
|
292
|
+
return { ok: false, action: 'advisory', reason: 'invalid repoRoot input' };
|
|
293
|
+
}
|
|
294
|
+
const safeDiff = diff && typeof diff === 'object' ? diff : {};
|
|
295
|
+
|
|
296
|
+
// Branch name derived from the candidate id (validated for arg safety).
|
|
297
|
+
const branch = `repair/${candidate.id}`;
|
|
298
|
+
try {
|
|
299
|
+
validateMrInputs(branch, '');
|
|
300
|
+
} catch (err) {
|
|
301
|
+
log('error', `mr-opener: unsafe branch name — ${err.message}`);
|
|
302
|
+
return { ok: false, action: 'advisory', reason: 'unsafe branch name derived from candidate id' };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Validate the candidate-derived MR title up front (newline/null-byte guard)
|
|
306
|
+
// so a malformed candidate fails fast as advisory — BEFORE any git work. The
|
|
307
|
+
// authoritative title/body is built later by the createMr seam.
|
|
308
|
+
try {
|
|
309
|
+
buildCandidateMrBody(candidate);
|
|
310
|
+
} catch (err) {
|
|
311
|
+
const msg = err instanceof MrDraftError ? err.message : String(err?.message ?? err);
|
|
312
|
+
log('error', `mr-opener: MR body build failed — ${msg}`);
|
|
313
|
+
return { ok: false, action: 'advisory', reason: `MR body validation failed: ${msg}` };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// -----------------------------------------------------------------------
|
|
317
|
+
// Step 1 — dry-run preview. No push, no MR. Best-effort leakage pre-check.
|
|
318
|
+
// -----------------------------------------------------------------------
|
|
319
|
+
if (dryRun === true) {
|
|
320
|
+
// The leakage scanner only inspects TRACKED files, so a true pre-commit
|
|
321
|
+
// scan is not feasible without a commit. We mark the gate 'pass' optimistically
|
|
322
|
+
// for the preview and let the real path run the authoritative scan.
|
|
323
|
+
log('info', `mr-opener: dry-run preview for ${branch} — no MR opened`);
|
|
324
|
+
return {
|
|
325
|
+
ok: true,
|
|
326
|
+
action: 'advisory',
|
|
327
|
+
reason: 'dry-run preview — no MR opened',
|
|
328
|
+
gate: { ownerLeakage: 'pass' },
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// -----------------------------------------------------------------------
|
|
333
|
+
// Step 2 — resolve VCS. No glab/gh → advisory.
|
|
334
|
+
// -----------------------------------------------------------------------
|
|
335
|
+
const { bin, vcs: resolvedVcs } = resolveVcs(vcs, vcsDetect, repoRoot);
|
|
336
|
+
if (bin === null || resolvedVcs === null) {
|
|
337
|
+
log('info', 'mr-opener: no glab/gh available — advisory only');
|
|
338
|
+
return {
|
|
339
|
+
ok: true,
|
|
340
|
+
action: 'advisory',
|
|
341
|
+
reason: 'glab/gh not installed — advisory only',
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// -----------------------------------------------------------------------
|
|
346
|
+
// Step 3 — create branch + write change + git add/commit/push (net-new).
|
|
347
|
+
// -----------------------------------------------------------------------
|
|
348
|
+
try {
|
|
349
|
+
await git({ args: ['checkout', '-B', branch], cwd: repoRoot });
|
|
350
|
+
|
|
351
|
+
// Write the candidate's change into the target file (when content provided).
|
|
352
|
+
if (typeof safeDiff.content === 'string' && typeof candidate.target_path === 'string') {
|
|
353
|
+
const abs = path.resolve(repoRoot, candidate.target_path);
|
|
354
|
+
// R5 defense-in-depth: never write outside the repo even if a future differ
|
|
355
|
+
// populates `content` from a candidate-influenced target_path (mirrors the
|
|
356
|
+
// fail-closed escape check in blast-radius-classifier.mjs).
|
|
357
|
+
const rel = path.relative(repoRoot, abs);
|
|
358
|
+
if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
359
|
+
log('error', `mr-opener: target_path escapes repo (${candidate.target_path}) — blocked`);
|
|
360
|
+
return { ok: false, action: 'blocked', reason: 'target_path escapes repo' };
|
|
361
|
+
}
|
|
362
|
+
const writeFile =
|
|
363
|
+
typeof opts.writeFile === 'function'
|
|
364
|
+
? opts.writeFile
|
|
365
|
+
: (await import('node:fs/promises')).writeFile;
|
|
366
|
+
await writeFile(abs, safeDiff.content, 'utf8');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
await git({ args: ['add', '--', candidate.target_path ?? '.'], cwd: repoRoot });
|
|
370
|
+
const commitMsg = `fix(skill-evolution): auto-repair ${candidate.target_path ?? candidate.id}`;
|
|
371
|
+
await git({ args: ['commit', '-m', commitMsg], cwd: repoRoot });
|
|
372
|
+
} catch (err) {
|
|
373
|
+
if (err && err.code === 'ENOENT') {
|
|
374
|
+
log('error', 'mr-opener: git binary not found — advisory only');
|
|
375
|
+
return { ok: true, action: 'advisory', reason: 'git not installed — advisory only' };
|
|
376
|
+
}
|
|
377
|
+
log('error', `mr-opener: branch/commit failed — ${err?.message ?? err}`);
|
|
378
|
+
return {
|
|
379
|
+
ok: true,
|
|
380
|
+
action: 'advisory',
|
|
381
|
+
reason: `git branch/commit failed: ${err?.message ?? 'unknown error'}`,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// -----------------------------------------------------------------------
|
|
386
|
+
// Step 4 — GATE owner-leakage (always). Runs AFTER commit, BEFORE push.
|
|
387
|
+
// -----------------------------------------------------------------------
|
|
388
|
+
let leak;
|
|
389
|
+
try {
|
|
390
|
+
leak = await leakageScan({ repoRoot });
|
|
391
|
+
} catch (err) {
|
|
392
|
+
log('error', `mr-opener: leakage scan threw — ${err?.message ?? err}`);
|
|
393
|
+
// Fail-closed on scan error: we cannot prove the diff is clean → block.
|
|
394
|
+
return {
|
|
395
|
+
ok: false,
|
|
396
|
+
action: 'blocked',
|
|
397
|
+
reason: 'owner-leakage scan failed to run',
|
|
398
|
+
gate: { ownerLeakage: 'fail' },
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
if (!leak || leak.ok !== true) {
|
|
402
|
+
const reason = leak?.spawnError
|
|
403
|
+
? 'owner-leakage scan failed to run'
|
|
404
|
+
: 'owner-leakage detected';
|
|
405
|
+
log('warn', `mr-opener: owner-leakage gate failed (${reason})`);
|
|
406
|
+
return {
|
|
407
|
+
ok: false,
|
|
408
|
+
action: 'blocked',
|
|
409
|
+
reason,
|
|
410
|
+
gate: { ownerLeakage: 'fail' },
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// -----------------------------------------------------------------------
|
|
415
|
+
// Step 5 — GATE slopcheck (CONDITIONAL — only when the diff ADDS a package).
|
|
416
|
+
// -----------------------------------------------------------------------
|
|
417
|
+
let slopStatus = 'skipped';
|
|
418
|
+
const addedPkgs = detectAddedPackages(safeDiff.raw);
|
|
419
|
+
if (addedPkgs.length > 0) {
|
|
420
|
+
let classified;
|
|
421
|
+
try {
|
|
422
|
+
classified = await slopcheck(addedPkgs, { repoRoot });
|
|
423
|
+
} catch (err) {
|
|
424
|
+
log('error', `mr-opener: slopcheck threw — ${err?.message ?? err}`);
|
|
425
|
+
// slopcheck itself never throws, but the seam might be mocked to. Fail-closed.
|
|
426
|
+
return {
|
|
427
|
+
ok: false,
|
|
428
|
+
action: 'blocked',
|
|
429
|
+
reason: 'slopcheck failed to run',
|
|
430
|
+
gate: { ownerLeakage: 'pass', slopcheck: 'fail' },
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
const hasSlop =
|
|
434
|
+
Array.isArray(classified) && classified.some((c) => c?.classification === 'SLOP');
|
|
435
|
+
if (hasSlop) {
|
|
436
|
+
const slopName =
|
|
437
|
+
classified.find((c) => c?.classification === 'SLOP')?.name ?? 'unknown';
|
|
438
|
+
log('warn', `mr-opener: slopcheck gate failed — SLOP package '${slopName}'`);
|
|
439
|
+
return {
|
|
440
|
+
ok: false,
|
|
441
|
+
action: 'blocked',
|
|
442
|
+
reason: 'slop package',
|
|
443
|
+
gate: { ownerLeakage: 'pass', slopcheck: 'fail' },
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
slopStatus = 'pass';
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const gate = { ownerLeakage: 'pass', slopcheck: slopStatus };
|
|
450
|
+
|
|
451
|
+
// -----------------------------------------------------------------------
|
|
452
|
+
// Step 6 — push + open the draft MR/PR. ENOENT/exec failure → advisory.
|
|
453
|
+
// -----------------------------------------------------------------------
|
|
454
|
+
try {
|
|
455
|
+
await git({ args: ['push', '--set-upstream', 'origin', branch], cwd: repoRoot });
|
|
456
|
+
} catch (err) {
|
|
457
|
+
if (err && err.code === 'ENOENT') {
|
|
458
|
+
log('error', 'mr-opener: git push — binary not found — advisory only');
|
|
459
|
+
return { ok: true, action: 'advisory', reason: 'git not installed — advisory only', gate };
|
|
460
|
+
}
|
|
461
|
+
log('error', `mr-opener: git push failed — ${err?.message ?? err}`);
|
|
462
|
+
return {
|
|
463
|
+
ok: true,
|
|
464
|
+
action: 'advisory',
|
|
465
|
+
reason: `git push failed: ${err?.message ?? 'unknown error'}`,
|
|
466
|
+
gate,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Delegate MR creation to the mr-draft seam (collision check + draft create).
|
|
471
|
+
let mrResult;
|
|
472
|
+
try {
|
|
473
|
+
mrResult = await createMr(
|
|
474
|
+
{
|
|
475
|
+
draftMrPolicy: 'on-loop-start',
|
|
476
|
+
vcs: resolvedVcs,
|
|
477
|
+
issueIid: candidate.source_ref ?? candidate.id,
|
|
478
|
+
issueTitle: `Auto-repair: ${candidate.proposed_change || 'skill repair'}`,
|
|
479
|
+
branchName: branch,
|
|
480
|
+
parentRunId: `repair/${candidate.id}`,
|
|
481
|
+
worktreePath: candidate.target_path ?? '(unknown)',
|
|
482
|
+
},
|
|
483
|
+
{ execFile: opts.execFile, log },
|
|
484
|
+
);
|
|
485
|
+
} catch (err) {
|
|
486
|
+
const msg = err instanceof MrDraftError ? err.message : String(err?.message ?? err);
|
|
487
|
+
log('error', `mr-opener: MR create threw — ${msg}`);
|
|
488
|
+
return { ok: true, action: 'advisory', reason: `MR create failed: ${msg}`, gate };
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// maybeCreateDraftMR returns { created, mrUrl?, existing?, error? } — never throws.
|
|
492
|
+
if (mrResult && mrResult.created === true) {
|
|
493
|
+
log('info', `mr-opener: draft MR opened — ${mrResult.mrUrl ?? '(no url)'}`);
|
|
494
|
+
return { ok: true, action: 'mr-opened', mrUrl: mrResult.mrUrl ?? undefined, gate };
|
|
495
|
+
}
|
|
496
|
+
if (mrResult && mrResult.existing === true) {
|
|
497
|
+
log('info', `mr-opener: existing MR found — ${mrResult.mrUrl ?? '(no url)'}`);
|
|
498
|
+
return { ok: true, action: 'mr-opened', mrUrl: mrResult.mrUrl ?? undefined, gate };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Not created (binary missing, collision-check error, or other) → advisory.
|
|
502
|
+
const reason = mrResult?.error
|
|
503
|
+
? `MR not opened: ${mrResult.error}`
|
|
504
|
+
: 'MR not opened — advisory only';
|
|
505
|
+
log('info', `mr-opener: ${reason}`);
|
|
506
|
+
return { ok: true, action: 'advisory', reason, gate };
|
|
507
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* join.mjs — Layer 2 (L2) skill-health: join skill-selection events to session outcomes.
|
|
3
|
+
*
|
|
4
|
+
* COARSE-GRANULARITY LIMITATION (read before using):
|
|
5
|
+
* The existing data model only records outcome signals at SESSION granularity via
|
|
6
|
+
* `agent_summary: { complete, partial, failed, spiral }` in sessions.jsonl. There is NO
|
|
7
|
+
* per-skill, per-wave, or per-invocation outcome signal in the current schema. Therefore
|
|
8
|
+
* this join is inherently coarse: for each skill we can tell WHICH sessions selected it
|
|
9
|
+
* and what those sessions' AGGREGATE outcomes were — but NOT whether the skill itself
|
|
10
|
+
* succeeded or failed within a session. Callers must not interpret the outcome buckets
|
|
11
|
+
* as per-skill success/failure rates; they are session-level aggregate outcomes for sessions
|
|
12
|
+
* that happened to select the skill.
|
|
13
|
+
*
|
|
14
|
+
* Where a session_id from skill-invocations.jsonl is not found in sessions.jsonl (e.g.
|
|
15
|
+
* the session is still in progress, or the record was never written), the session
|
|
16
|
+
* contributes `unknown` to the outcome tally — it is never silently dropped.
|
|
17
|
+
*
|
|
18
|
+
* Part of Epic #645 — Skill Self-Evolution Foundation, Layer 2.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { promises as fs } from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
|
+
|
|
25
|
+
const DEFAULT_INVOCATIONS_PATH = path.resolve(
|
|
26
|
+
fileURLToPath(import.meta.url),
|
|
27
|
+
'../../../../.orchestrator/metrics/skill-invocations.jsonl',
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const DEFAULT_SESSIONS_PATH = path.resolve(
|
|
31
|
+
fileURLToPath(import.meta.url),
|
|
32
|
+
'../../../../.orchestrator/metrics/sessions.jsonl',
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Reads a JSONL file and returns an array of successfully parsed records.
|
|
37
|
+
* Silently skips malformed lines. Returns [] when the file is absent.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} filePath
|
|
40
|
+
* @returns {Promise<object[]>}
|
|
41
|
+
*/
|
|
42
|
+
async function readJsonl(filePath) {
|
|
43
|
+
let raw;
|
|
44
|
+
try {
|
|
45
|
+
raw = await fs.readFile(filePath, 'utf8');
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (err.code === 'ENOENT') return [];
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
const records = [];
|
|
51
|
+
for (const line of raw.split('\n')) {
|
|
52
|
+
const trimmed = line.trim();
|
|
53
|
+
if (!trimmed) continue;
|
|
54
|
+
try {
|
|
55
|
+
records.push(JSON.parse(trimmed));
|
|
56
|
+
} catch {
|
|
57
|
+
// skip malformed lines per spec
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return records;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Builds a Map<session_id, agent_summary> from sessions.jsonl records.
|
|
65
|
+
* Records without a session_id or with a non-object agent_summary are skipped.
|
|
66
|
+
*
|
|
67
|
+
* @param {object[]} sessionRecords
|
|
68
|
+
* @returns {Map<string, { complete: number, partial: number, failed: number, spiral: number }>}
|
|
69
|
+
*/
|
|
70
|
+
function buildSessionMap(sessionRecords) {
|
|
71
|
+
const map = new Map();
|
|
72
|
+
for (const rec of sessionRecords) {
|
|
73
|
+
if (typeof rec.session_id !== 'string' || !rec.session_id) continue;
|
|
74
|
+
if (rec.agent_summary && typeof rec.agent_summary === 'object') {
|
|
75
|
+
map.set(rec.session_id, rec.agent_summary);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return map;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Joins skill-selection events to existing session outcomes.
|
|
83
|
+
*
|
|
84
|
+
* @param {object} [opts]
|
|
85
|
+
* @param {string} [opts.invocationsPath] - Path to skill-invocations.jsonl (L1 writer output).
|
|
86
|
+
* @param {string} [opts.sessionsPath] - Path to sessions.jsonl.
|
|
87
|
+
*
|
|
88
|
+
* @returns {Promise<{
|
|
89
|
+
* bySkill: {
|
|
90
|
+
* [skill: string]: {
|
|
91
|
+
* skill: string,
|
|
92
|
+
* selections: number,
|
|
93
|
+
* sessions: string[],
|
|
94
|
+
* outcomes: { complete: number, partial: number, failed: number, spiral: number, unknown: number }
|
|
95
|
+
* }
|
|
96
|
+
* },
|
|
97
|
+
* totalSelections: number,
|
|
98
|
+
* sessionsJoined: number,
|
|
99
|
+
* sessionsUnknown: number
|
|
100
|
+
* }>}
|
|
101
|
+
*/
|
|
102
|
+
export async function joinSkillOutcomes({
|
|
103
|
+
invocationsPath = DEFAULT_INVOCATIONS_PATH,
|
|
104
|
+
sessionsPath = DEFAULT_SESSIONS_PATH,
|
|
105
|
+
} = {}) {
|
|
106
|
+
const [invocations, sessionRecords] = await Promise.all([
|
|
107
|
+
readJsonl(invocationsPath),
|
|
108
|
+
readJsonl(sessionsPath),
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
const sessionMap = buildSessionMap(sessionRecords);
|
|
112
|
+
|
|
113
|
+
/** @type {Map<string, { skill: string, selectionCount: number, sessions: Set<string>, outcomes: object }>} */
|
|
114
|
+
const bySkill = new Map();
|
|
115
|
+
|
|
116
|
+
let totalSelections = 0;
|
|
117
|
+
let sessionsJoined = 0;
|
|
118
|
+
let sessionsUnknown = 0;
|
|
119
|
+
|
|
120
|
+
for (const inv of invocations) {
|
|
121
|
+
// Only process skill-selection events with a valid skill field
|
|
122
|
+
if (typeof inv.skill !== 'string' || !inv.skill) continue;
|
|
123
|
+
if (inv.event !== 'selected') continue;
|
|
124
|
+
|
|
125
|
+
const skill = inv.skill;
|
|
126
|
+
const sessionId = typeof inv.session_id === 'string' ? inv.session_id : null;
|
|
127
|
+
|
|
128
|
+
totalSelections += 1;
|
|
129
|
+
|
|
130
|
+
if (!bySkill.has(skill)) {
|
|
131
|
+
bySkill.set(skill, {
|
|
132
|
+
skill,
|
|
133
|
+
selectionCount: 0,
|
|
134
|
+
sessions: new Set(),
|
|
135
|
+
outcomes: { complete: 0, partial: 0, failed: 0, spiral: 0, unknown: 0 },
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const record = bySkill.get(skill);
|
|
140
|
+
record.selectionCount += 1;
|
|
141
|
+
|
|
142
|
+
if (sessionId) {
|
|
143
|
+
const isNew = !record.sessions.has(sessionId);
|
|
144
|
+
record.sessions.add(sessionId);
|
|
145
|
+
|
|
146
|
+
if (isNew) {
|
|
147
|
+
const summary = sessionMap.get(sessionId);
|
|
148
|
+
if (summary) {
|
|
149
|
+
// Sum session-level aggregate outcomes into this skill's buckets
|
|
150
|
+
record.outcomes.complete += typeof summary.complete === 'number' ? summary.complete : 0;
|
|
151
|
+
record.outcomes.partial += typeof summary.partial === 'number' ? summary.partial : 0;
|
|
152
|
+
record.outcomes.failed += typeof summary.failed === 'number' ? summary.failed : 0;
|
|
153
|
+
record.outcomes.spiral += typeof summary.spiral === 'number' ? summary.spiral : 0;
|
|
154
|
+
sessionsJoined += 1;
|
|
155
|
+
} else {
|
|
156
|
+
// Session id not found in sessions.jsonl — count as unknown, never drop
|
|
157
|
+
record.outcomes.unknown += 1;
|
|
158
|
+
sessionsUnknown += 1;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
// No session_id on the invocation record — count as unknown
|
|
163
|
+
record.outcomes.unknown += 1;
|
|
164
|
+
sessionsUnknown += 1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Serialize: convert Set<string> → sorted string[] for stable output
|
|
169
|
+
/** @type {Record<string, object>} */
|
|
170
|
+
const bySkillObj = {};
|
|
171
|
+
for (const [skill, rec] of bySkill.entries()) {
|
|
172
|
+
bySkillObj[skill] = {
|
|
173
|
+
skill: rec.skill,
|
|
174
|
+
selections: rec.selectionCount,
|
|
175
|
+
sessions: [...rec.sessions].sort(),
|
|
176
|
+
outcomes: rec.outcomes,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return { bySkill: bySkillObj, totalSelections, sessionsJoined, sessionsUnknown };
|
|
181
|
+
}
|