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,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave-context helpers — single source of truth for the
|
|
3
|
+
* agent-context env-var contract documented in
|
|
4
|
+
* CLAUDE.md (or AGENTS.md on Codex CLI) "Critical Gotchas"
|
|
5
|
+
* + .claude/rules/parallel-sessions.md.
|
|
6
|
+
*
|
|
7
|
+
* The `SO_WAVE_AGENT` env-var is set to '1' by the wave-executor
|
|
8
|
+
* harness when dispatching subagents. Agents check this guard before
|
|
9
|
+
* calling memory.propose() — coordinator-thread invocations must NOT
|
|
10
|
+
* succeed (use /evolve instead). See #548 A4.
|
|
11
|
+
*
|
|
12
|
+
* Strict equality semantics (=== '1') are intentional: leading
|
|
13
|
+
* whitespace, trailing newline, '01', 'true', '0', undefined, and
|
|
14
|
+
* empty string all return false. This matches the documented contract
|
|
15
|
+
* in scripts/memory-propose.mjs Step 2b and the regression tests in
|
|
16
|
+
* tests/scripts/memory-propose.test.mjs Section E.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export const WAVE_AGENT_ENV_VAR = 'SO_WAVE_AGENT';
|
|
20
|
+
export const WAVE_AGENT_ENV_VALUE = '1';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @returns {boolean} true iff this process is running as a dispatched
|
|
24
|
+
* wave-agent (i.e., SO_WAVE_AGENT === '1', strict equality — leading
|
|
25
|
+
* whitespace, trailing newline, '01', etc. all return false).
|
|
26
|
+
*/
|
|
27
|
+
export function isWaveAgentContext() {
|
|
28
|
+
return process.env[WAVE_AGENT_ENV_VAR] === WAVE_AGENT_ENV_VALUE;
|
|
29
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pool.mjs — Bounded-concurrency worker-pool cursor for wave-executor dispatch.
|
|
3
|
+
*
|
|
4
|
+
* Provides a true cursor-based pull loop for agent dispatch, replacing the
|
|
5
|
+
* existing Promise.all() fan-out model when `worker-pool.enabled: true` in
|
|
6
|
+
* Session Config. Agents pull tasks from a shared cursor so at most
|
|
7
|
+
* `maxParallel` workers are active at any moment — forward-compatible with
|
|
8
|
+
* resource-gate decisions (#193).
|
|
9
|
+
*
|
|
10
|
+
* Issue: #415
|
|
11
|
+
*
|
|
12
|
+
* Exports:
|
|
13
|
+
* runWavePool(opts) → Promise<{results: Array, errors: Array}>
|
|
14
|
+
*
|
|
15
|
+
* AbortSignal behaviour:
|
|
16
|
+
* When the caller's abortSignal fires, each in-flight worker is sent a
|
|
17
|
+
* per-worker AbortController.abort(). No new tasks are pulled. The function
|
|
18
|
+
* drains for up to `drainTimeoutMs` before returning partial results.
|
|
19
|
+
*
|
|
20
|
+
* Error semantics:
|
|
21
|
+
* A single failing task never cascades — remaining workers continue pulling
|
|
22
|
+
* until the cursor is exhausted. Failed tasks accumulate in `errors`.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Types (JSDoc only — no TypeScript at runtime)
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {{ taskId: string, dispatch: () => Promise<*> }} PoolTask
|
|
31
|
+
*
|
|
32
|
+
* @typedef {{
|
|
33
|
+
* tasks: PoolTask[],
|
|
34
|
+
* maxParallel: number,
|
|
35
|
+
* abortSignal?: AbortSignal,
|
|
36
|
+
* drainTimeoutMs?: number,
|
|
37
|
+
* onTaskStart?: (taskId: string) => void,
|
|
38
|
+
* onTaskComplete?: (taskId: string, result: *, error: Error|null) => void,
|
|
39
|
+
* }} PoolOptions
|
|
40
|
+
*
|
|
41
|
+
* @typedef {{
|
|
42
|
+
* results: Array<{ taskId: string, result: * }>,
|
|
43
|
+
* errors: Array<{ taskId: string, error: Error }>,
|
|
44
|
+
* }} PoolResult
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/** Default drain timeout (ms) when an abort signal fires mid-run. */
|
|
48
|
+
export const DEFAULT_DRAIN_TIMEOUT_MS = 10_000;
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// runWavePool
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Execute `tasks` with at most `maxParallel` concurrent workers.
|
|
56
|
+
*
|
|
57
|
+
* Workers pull tasks from a shared cursor — this is a genuine cursor model,
|
|
58
|
+
* not Promise.all(tasks.map(...)). Each worker loop:
|
|
59
|
+
* 1. Picks up the next unstarted task (atomic cursor advance).
|
|
60
|
+
* 2. Awaits task.dispatch().
|
|
61
|
+
* 3. Records result/error.
|
|
62
|
+
* 4. Repeats until cursor is exhausted.
|
|
63
|
+
*
|
|
64
|
+
* When `abortSignal.aborted` is true (or fires during execution), the pool
|
|
65
|
+
* stops pulling new tasks and each in-flight worker receives its own
|
|
66
|
+
* AbortController abort. The pool then waits up to `drainTimeoutMs` for
|
|
67
|
+
* workers to settle before returning partial results.
|
|
68
|
+
*
|
|
69
|
+
* @param {PoolOptions} opts
|
|
70
|
+
* @returns {Promise<PoolResult>}
|
|
71
|
+
*/
|
|
72
|
+
export async function runWavePool({
|
|
73
|
+
tasks,
|
|
74
|
+
maxParallel,
|
|
75
|
+
abortSignal,
|
|
76
|
+
drainTimeoutMs = DEFAULT_DRAIN_TIMEOUT_MS,
|
|
77
|
+
onTaskStart,
|
|
78
|
+
onTaskComplete,
|
|
79
|
+
}) {
|
|
80
|
+
// ── Input validation ──────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
if (!Array.isArray(tasks)) {
|
|
83
|
+
throw new TypeError('runWavePool: tasks must be an array');
|
|
84
|
+
}
|
|
85
|
+
if (typeof maxParallel !== 'number' || !Number.isInteger(maxParallel) || maxParallel < 1) {
|
|
86
|
+
throw new TypeError('runWavePool: maxParallel must be a positive integer');
|
|
87
|
+
}
|
|
88
|
+
if (typeof drainTimeoutMs !== 'number' || drainTimeoutMs < 0) {
|
|
89
|
+
throw new TypeError('runWavePool: drainTimeoutMs must be a non-negative number');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── Duplicate taskId guard ─────────────────────────────────────────────────
|
|
93
|
+
// Two tasks with the same taskId would produce duplicate records in `results`,
|
|
94
|
+
// silently dropping one when a caller builds a Map<taskId, result>.
|
|
95
|
+
const seenIds = new Set();
|
|
96
|
+
for (const t of tasks) {
|
|
97
|
+
if (!t || typeof t.taskId !== 'string') continue; // caught by runtime dispatch
|
|
98
|
+
if (seenIds.has(t.taskId)) {
|
|
99
|
+
throw new TypeError(`runWavePool: duplicate taskId "${t.taskId}"`);
|
|
100
|
+
}
|
|
101
|
+
seenIds.add(t.taskId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ── Fast path: empty tasks ─────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
if (tasks.length === 0) {
|
|
107
|
+
return { results: [], errors: [] };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── Shared state (cursor + result accumulators) ───────────────────────────
|
|
111
|
+
|
|
112
|
+
let nextIndex = 0;
|
|
113
|
+
/** @type {Map<number, AbortController>} taskIndex → per-worker AbortController */
|
|
114
|
+
const inFlight = new Map();
|
|
115
|
+
/** @type {Array<{ taskId: string, result: * }>} */
|
|
116
|
+
const results = [];
|
|
117
|
+
/** @type {Array<{ taskId: string, error: Error }>} */
|
|
118
|
+
const errors = [];
|
|
119
|
+
|
|
120
|
+
// Cap effective concurrency at tasks.length to avoid over-spawn.
|
|
121
|
+
const effective = Math.min(maxParallel, tasks.length);
|
|
122
|
+
|
|
123
|
+
// ── Abort handling ─────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Returns true when the pool should stop pulling new tasks.
|
|
127
|
+
* Checks the caller-supplied AbortSignal (if any).
|
|
128
|
+
*/
|
|
129
|
+
function isAborted() {
|
|
130
|
+
return abortSignal !== null && abortSignal !== undefined && abortSignal.aborted;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Abort all currently in-flight workers by forwarding to their per-worker
|
|
135
|
+
* AbortControllers. Called when the caller's abortSignal fires.
|
|
136
|
+
*
|
|
137
|
+
* Only workers that are already executing `task.dispatch()` at the moment
|
|
138
|
+
* this function is called will receive the abort signal. Tasks that are
|
|
139
|
+
* still queued (not yet pulled by any worker) are simply never started —
|
|
140
|
+
* the `isAborted()` guard in the worker loop stops the cursor on the next
|
|
141
|
+
* pull attempt.
|
|
142
|
+
*/
|
|
143
|
+
function abortInFlight() {
|
|
144
|
+
for (const controller of inFlight.values()) {
|
|
145
|
+
controller.abort();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Register a one-shot listener on the caller's AbortSignal so we react
|
|
150
|
+
// immediately if it fires while workers are running.
|
|
151
|
+
let abortListener = null;
|
|
152
|
+
if (abortSignal !== null && abortSignal !== undefined) {
|
|
153
|
+
abortListener = () => abortInFlight();
|
|
154
|
+
abortSignal.addEventListener('abort', abortListener, { once: true });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── Worker function ───────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* A single worker loop. Pulls tasks from the shared cursor until the
|
|
161
|
+
* cursor is exhausted or the pool is aborted.
|
|
162
|
+
*/
|
|
163
|
+
async function worker() {
|
|
164
|
+
while (true) {
|
|
165
|
+
// Stop pulling when aborted.
|
|
166
|
+
if (isAborted()) break;
|
|
167
|
+
|
|
168
|
+
// Atomically claim the next task index.
|
|
169
|
+
const taskIndex = nextIndex;
|
|
170
|
+
if (taskIndex >= tasks.length) break; // cursor exhausted
|
|
171
|
+
nextIndex += 1;
|
|
172
|
+
|
|
173
|
+
const task = tasks[taskIndex];
|
|
174
|
+
const workerController = new AbortController();
|
|
175
|
+
inFlight.set(taskIndex, workerController);
|
|
176
|
+
|
|
177
|
+
// Notify caller that this task is starting.
|
|
178
|
+
if (typeof onTaskStart === 'function') {
|
|
179
|
+
onTaskStart(task.taskId);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
// Pass the worker's AbortSignal to the dispatch function so callers
|
|
184
|
+
// can propagate cancellation into their child processes.
|
|
185
|
+
const result = await task.dispatch(workerController.signal);
|
|
186
|
+
results.push({ taskId: task.taskId, result });
|
|
187
|
+
if (typeof onTaskComplete === 'function') {
|
|
188
|
+
onTaskComplete(task.taskId, result, null);
|
|
189
|
+
}
|
|
190
|
+
} catch (err) {
|
|
191
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
192
|
+
errors.push({ taskId: task.taskId, error });
|
|
193
|
+
if (typeof onTaskComplete === 'function') {
|
|
194
|
+
onTaskComplete(task.taskId, null, error);
|
|
195
|
+
}
|
|
196
|
+
// Single failure: continue the cursor, do NOT cascade abort.
|
|
197
|
+
} finally {
|
|
198
|
+
inFlight.delete(taskIndex);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── Spawn workers ──────────────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
// Pre-abort: if the signal already fired before workers started, abort now.
|
|
206
|
+
if (isAborted()) {
|
|
207
|
+
abortInFlight();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Worker errors are individual task failures already captured above; we
|
|
211
|
+
// use allSettled so the pool itself never rejects.
|
|
212
|
+
const workerSettle = Promise.allSettled(
|
|
213
|
+
Array.from({ length: effective }, () => worker()),
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// ── Drain with abort-timeout ───────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
// Normal path: await all workers.
|
|
219
|
+
// Abort path: the abortListener forwards abort to each worker's controller,
|
|
220
|
+
// then we race settle vs. drainTimeoutMs. Workers that honour their signal
|
|
221
|
+
// exit quickly; those that ignore it are abandoned after the timeout (the
|
|
222
|
+
// Promise is still in-flight but we return whatever results we have).
|
|
223
|
+
|
|
224
|
+
let drainTimeoutHandle;
|
|
225
|
+
const drainTimeoutPromise = new Promise((resolve) => {
|
|
226
|
+
drainTimeoutHandle = setTimeout(resolve, drainTimeoutMs);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (abortSignal !== null && abortSignal !== undefined) {
|
|
230
|
+
// Race: workers settle OR drain timeout fires after abort.
|
|
231
|
+
// The abortListener (registered earlier) already handles aborting workers
|
|
232
|
+
// when the signal fires. We just need to bound the wait.
|
|
233
|
+
await Promise.race([workerSettle, drainTimeoutPromise]);
|
|
234
|
+
} else {
|
|
235
|
+
// No abort signal — just wait for all workers to finish.
|
|
236
|
+
await workerSettle;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
clearTimeout(drainTimeoutHandle);
|
|
240
|
+
|
|
241
|
+
// ── Cleanup ───────────────────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
if (abortListener !== null && abortSignal !== null && abortSignal !== undefined) {
|
|
244
|
+
abortSignal.removeEventListener('abort', abortListener);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return { results, errors };
|
|
248
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wave-resource-gate.mjs — Pre-dispatch resource gate for parallel agent waves.
|
|
3
|
+
*
|
|
4
|
+
* Evaluates RAM / CPU / concurrent-session pressure before dispatching agents
|
|
5
|
+
* and returns a decision: "proceed", "reduce", or "coordinator-direct".
|
|
6
|
+
*
|
|
7
|
+
* Part of v3.1.0 Epic #157, Sub-Epic resource-gate. Issue #193.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { probe } from './resource-probe.mjs';
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Public API
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extract resource measurements either from a test override or by calling
|
|
18
|
+
* the live resource probe. Returns either a measurements object or a sentinel
|
|
19
|
+
* `{ probeFailed: true }` indicating the caller should short-circuit with a
|
|
20
|
+
* "proceed" decision.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} opts - Same opts shape as evaluateWaveResourceGate
|
|
23
|
+
* @returns {Promise<{ramFreeGb: number, ramAvailableGb: number|null, cpuLoadPct: number, concurrentSessions: number} | {probeFailed: true}>}
|
|
24
|
+
*/
|
|
25
|
+
async function extractMeasurements(opts) {
|
|
26
|
+
const { probeOverride } = opts;
|
|
27
|
+
|
|
28
|
+
if (probeOverride !== undefined && probeOverride !== null) {
|
|
29
|
+
return {
|
|
30
|
+
ramFreeGb: probeOverride.ramFreeGb,
|
|
31
|
+
// Tests may supply ramAvailableGb to exercise the macOS path; absent → null.
|
|
32
|
+
ramAvailableGb: probeOverride.ramAvailableGb ?? null,
|
|
33
|
+
cpuLoadPct: probeOverride.cpuLoadPct,
|
|
34
|
+
concurrentSessions: probeOverride.concurrentSessions,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let snapshot;
|
|
39
|
+
try {
|
|
40
|
+
snapshot = await probe({ skipProcessCounts: false });
|
|
41
|
+
} catch {
|
|
42
|
+
return { probeFailed: true };
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
ramFreeGb: snapshot.ram_free_gb,
|
|
46
|
+
// macOS: free + reclaimable (vm_stat). null on Linux/Windows where
|
|
47
|
+
// os.freemem() is already accurate. (#667)
|
|
48
|
+
ramAvailableGb: snapshot.ram_available_gb ?? null,
|
|
49
|
+
cpuLoadPct: snapshot.cpu_load_pct,
|
|
50
|
+
// concurrent sessions: number of claude processes found by the probe.
|
|
51
|
+
concurrentSessions: snapshot.claude_processes_count ?? 0,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Apply the gate decision rule sequence (rules 3-8) given measurements and
|
|
57
|
+
* config. Returns the full gate result.
|
|
58
|
+
*
|
|
59
|
+
* @param {{ramFreeGb: number, cpuLoadPct: number, concurrentSessions: number}} measurements
|
|
60
|
+
* @param {object} opts - Same opts shape as evaluateWaveResourceGate
|
|
61
|
+
* @returns {{decision: string, agents: number, reasons: string[], measurements: object}}
|
|
62
|
+
*/
|
|
63
|
+
function applyDecisionRules(measurements, opts) {
|
|
64
|
+
const { config, plannedAgents } = opts;
|
|
65
|
+
const { ramFreeGb, ramAvailableGb, cpuLoadPct, concurrentSessions } = measurements;
|
|
66
|
+
const T = config['resource-thresholds'];
|
|
67
|
+
|
|
68
|
+
// macOS fix (#667): os.freemem() reports only `Pages free`, which reads
|
|
69
|
+
// sub-1 GB even on a 128 GB host with 80+ GB reclaimable cache — a false
|
|
70
|
+
// RAM-critical that forced spurious coordinator-direct fallbacks. When the
|
|
71
|
+
// probe supplied a numeric `ramAvailableGb` (free + reclaimable, via vm_stat),
|
|
72
|
+
// judge RAM thresholds on AVAILABLE; otherwise fall back to FREE (Linux/Win,
|
|
73
|
+
// where os.freemem() is already accurate).
|
|
74
|
+
const hasAvailable = ramAvailableGb !== null && ramAvailableGb !== undefined;
|
|
75
|
+
const effectiveRamGb = hasAvailable ? ramAvailableGb : ramFreeGb;
|
|
76
|
+
const ramLabel = hasAvailable ? 'RAM available' : 'RAM free';
|
|
77
|
+
|
|
78
|
+
// Rule 3: resource-thresholds missing → degrade to proceed (defensive).
|
|
79
|
+
// Handles legacy pre-#166 configs and test fixtures that omit the key.
|
|
80
|
+
// The gate is considered "ran" — caller receives measurements but no enforcement.
|
|
81
|
+
if (!T || typeof T !== 'object') {
|
|
82
|
+
return {
|
|
83
|
+
decision: 'proceed',
|
|
84
|
+
agents: plannedAgents,
|
|
85
|
+
reasons: ['resource-thresholds missing from config — gate skipped'],
|
|
86
|
+
measurements,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Rule 4: RAM below critical → coordinator-direct.
|
|
91
|
+
if (effectiveRamGb < T['ram-free-critical-gb']) {
|
|
92
|
+
return {
|
|
93
|
+
decision: 'coordinator-direct',
|
|
94
|
+
agents: 0,
|
|
95
|
+
reasons: [
|
|
96
|
+
`${ramLabel} ${effectiveRamGb}GB < critical ${T['ram-free-critical-gb']}GB — escalating to coordinator-direct`,
|
|
97
|
+
],
|
|
98
|
+
measurements,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Rule 5: RAM below min (but above critical) → reduce.
|
|
103
|
+
if (effectiveRamGb < T['ram-free-min-gb']) {
|
|
104
|
+
return {
|
|
105
|
+
decision: 'reduce',
|
|
106
|
+
agents: Math.max(1, Math.floor(plannedAgents / 2)),
|
|
107
|
+
reasons: [
|
|
108
|
+
`${ramLabel} ${effectiveRamGb}GB < min ${T['ram-free-min-gb']}GB — reducing agent count`,
|
|
109
|
+
],
|
|
110
|
+
measurements,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Rule 6: CPU overloaded → reduce.
|
|
115
|
+
if (cpuLoadPct > T['cpu-load-max-pct']) {
|
|
116
|
+
return {
|
|
117
|
+
decision: 'reduce',
|
|
118
|
+
agents: Math.max(1, Math.floor(plannedAgents / 2)),
|
|
119
|
+
reasons: [
|
|
120
|
+
`CPU load ${cpuLoadPct}% > max ${T['cpu-load-max-pct']}% — reducing agent count`,
|
|
121
|
+
],
|
|
122
|
+
measurements,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Rule 7: concurrent sessions above warn → proceed with warning.
|
|
127
|
+
if (concurrentSessions > T['concurrent-sessions-warn']) {
|
|
128
|
+
return {
|
|
129
|
+
decision: 'proceed',
|
|
130
|
+
agents: plannedAgents,
|
|
131
|
+
reasons: [`warn: ${concurrentSessions} concurrent sessions`],
|
|
132
|
+
measurements,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Rule 8: all within bounds.
|
|
137
|
+
return {
|
|
138
|
+
decision: 'proceed',
|
|
139
|
+
agents: plannedAgents,
|
|
140
|
+
reasons: ['all thresholds within bounds'],
|
|
141
|
+
measurements,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Evaluate whether the wave can be dispatched at the planned agent count.
|
|
147
|
+
* @param {object} opts
|
|
148
|
+
* @param {object} opts.config - Parsed Session Config (from parse-config.sh output)
|
|
149
|
+
* @param {number} opts.plannedAgents - Number of agents the session-plan wants to dispatch
|
|
150
|
+
* @param {string} opts.waveRole - e.g. "Impl-Core", "Quality"
|
|
151
|
+
* @param {object} [opts.probeOverride] - {ramFreeGb, cpuLoadPct, concurrentSessions} for
|
|
152
|
+
* testing; when omitted, calls resource-probe
|
|
153
|
+
* @returns {Promise<{decision: "proceed"|"reduce"|"coordinator-direct", agents: number, reasons: string[], measurements: object}>}
|
|
154
|
+
*/
|
|
155
|
+
export async function evaluateWaveResourceGate(opts) {
|
|
156
|
+
const { config, plannedAgents } = opts;
|
|
157
|
+
|
|
158
|
+
// Rule 1: resource-awareness disabled — skip all probing.
|
|
159
|
+
if (config['resource-awareness'] === false) {
|
|
160
|
+
return {
|
|
161
|
+
decision: 'proceed',
|
|
162
|
+
agents: plannedAgents,
|
|
163
|
+
reasons: ['resource-awareness disabled in Session Config'],
|
|
164
|
+
measurements: {},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Rule 2: probe the system (or use override for tests).
|
|
169
|
+
const measured = await extractMeasurements(opts);
|
|
170
|
+
if ('probeFailed' in measured) {
|
|
171
|
+
return {
|
|
172
|
+
decision: 'proceed',
|
|
173
|
+
agents: plannedAgents,
|
|
174
|
+
reasons: ['probe failed (ignored)'],
|
|
175
|
+
measurements: {},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Rules 3-8: apply decision rule sequence.
|
|
180
|
+
return applyDecisionRules(measured, opts);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Format a gate result into a short multi-line coordinator progress string.
|
|
185
|
+
* @param {{decision: string, agents: number, reasons: string[], measurements: object}} result
|
|
186
|
+
* @returns {string}
|
|
187
|
+
*/
|
|
188
|
+
export function formatGateReport(result) {
|
|
189
|
+
const { decision, agents, reasons, measurements } = result;
|
|
190
|
+
const lines = reasons.map((r) => ` - ${r}`);
|
|
191
|
+
const m = measurements;
|
|
192
|
+
// Prefer the macOS available-RAM figure in the banner when present (#667):
|
|
193
|
+
// "RAM free" alone is misleading on Darwin (Pages-free underreports).
|
|
194
|
+
const hasAvailable = m.ramAvailableGb !== null && m.ramAvailableGb !== undefined;
|
|
195
|
+
const ramStr = hasAvailable
|
|
196
|
+
? `RAM ${m.ramAvailableGb}GB avail`
|
|
197
|
+
: `RAM ${m.ramFreeGb ?? '?'}GB free`;
|
|
198
|
+
const measStr =
|
|
199
|
+
Object.keys(m).length > 0
|
|
200
|
+
? ` (${ramStr}, CPU ${m.cpuLoadPct ?? '?'}%, sessions ${m.concurrentSessions ?? '?'})`
|
|
201
|
+
: '';
|
|
202
|
+
lines.push(`Decision: ${decision} — agents: ${agents}${measStr}`);
|
|
203
|
+
return lines.join('\n');
|
|
204
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wave-sizing.mjs — graduated isolation defaults (issue #194).
|
|
3
|
+
* Learning: coordinator-over-worktree-on-shared-files caused back-to-back regressions.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const VALID_SESSION_TYPES = ['housekeeping', 'feature', 'deep'];
|
|
7
|
+
const VALID_COLLISION_RISKS = ['low', 'medium', 'high'];
|
|
8
|
+
const VALID_CONFIG_ISOLATIONS = ['auto', 'worktree', 'none'];
|
|
9
|
+
const VALID_ENFORCEMENTS = ['strict', 'warn', 'off'];
|
|
10
|
+
const VALID_ISOLATIONS = ['worktree', 'none'];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolve isolation mode for an agent wave dispatch.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} opts
|
|
16
|
+
* @param {number} opts.agentCount positive integer
|
|
17
|
+
* @param {string} opts.sessionType 'housekeeping' | 'feature' | 'deep'
|
|
18
|
+
* @param {string} [opts.collisionRisk] 'low' | 'medium' | 'high' — default 'low'
|
|
19
|
+
* @param {string} [opts.configIsolation] 'auto' | 'worktree' | 'none' — default 'auto'
|
|
20
|
+
* @returns {'worktree'|'none'}
|
|
21
|
+
*/
|
|
22
|
+
export function resolveIsolation({ agentCount, sessionType, collisionRisk = 'low', configIsolation = 'auto' } = {}) {
|
|
23
|
+
if (!Number.isInteger(agentCount) || agentCount < 1) {
|
|
24
|
+
throw new TypeError(`resolveIsolation: agentCount must be a positive integer, got ${agentCount}`);
|
|
25
|
+
}
|
|
26
|
+
if (!VALID_SESSION_TYPES.includes(sessionType)) {
|
|
27
|
+
throw new TypeError(`resolveIsolation: sessionType must be housekeeping|feature|deep, got '${sessionType}'`);
|
|
28
|
+
}
|
|
29
|
+
if (!VALID_COLLISION_RISKS.includes(collisionRisk)) {
|
|
30
|
+
throw new TypeError(`resolveIsolation: collisionRisk must be low|medium|high, got '${collisionRisk}'`);
|
|
31
|
+
}
|
|
32
|
+
if (!VALID_CONFIG_ISOLATIONS.includes(configIsolation)) {
|
|
33
|
+
throw new TypeError(`resolveIsolation: configIsolation must be auto|worktree|none, got '${configIsolation}'`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Hard user overrides — first-match wins
|
|
37
|
+
if (configIsolation === 'worktree') return 'worktree';
|
|
38
|
+
if (configIsolation === 'none') return 'none';
|
|
39
|
+
|
|
40
|
+
// Plan-level collision declaration forces isolation even at ≤2 agents
|
|
41
|
+
if (collisionRisk === 'high') return 'worktree';
|
|
42
|
+
|
|
43
|
+
// Decision table
|
|
44
|
+
if (agentCount <= 2) return 'none';
|
|
45
|
+
if (agentCount >= 5) return 'worktree';
|
|
46
|
+
// agentCount is 3 or 4
|
|
47
|
+
if (sessionType === 'housekeeping') return 'none';
|
|
48
|
+
return 'worktree'; // feature | deep
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolve enforcement mode given isolation.
|
|
53
|
+
*
|
|
54
|
+
* When isolation is 'none', the scope-enforcement hook is the only barrier —
|
|
55
|
+
* auto-promote 'warn' to 'strict'. Explicit 'off' is respected as user opt-out.
|
|
56
|
+
*
|
|
57
|
+
* @param {Object} opts
|
|
58
|
+
* @param {string} opts.isolation 'worktree' | 'none'
|
|
59
|
+
* @param {string} [opts.configEnforcement] 'strict' | 'warn' | 'off' — default 'warn'
|
|
60
|
+
* @returns {'strict'|'warn'|'off'}
|
|
61
|
+
*/
|
|
62
|
+
export function resolveEnforcement({ isolation, configEnforcement = 'warn' } = {}) {
|
|
63
|
+
if (!VALID_ISOLATIONS.includes(isolation)) {
|
|
64
|
+
throw new TypeError(`resolveEnforcement: isolation must be worktree|none, got '${isolation}'`);
|
|
65
|
+
}
|
|
66
|
+
if (!VALID_ENFORCEMENTS.includes(configEnforcement)) {
|
|
67
|
+
throw new TypeError(`resolveEnforcement: configEnforcement must be strict|warn|off, got '${configEnforcement}'`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (isolation === 'worktree') return configEnforcement;
|
|
71
|
+
|
|
72
|
+
// isolation === 'none': auto-promote warn → strict; respect strict and off as-is
|
|
73
|
+
if (configEnforcement === 'warn') return 'strict';
|
|
74
|
+
return configEnforcement;
|
|
75
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* webhook-url.mjs — Centralized webhook URL resolution for session-orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Replaces ad-hoc hardcoded personal-domain URL fallbacks with a single
|
|
5
|
+
* resolution function that reads from environment variables and Session Config.
|
|
6
|
+
* No personal-domain default exists — callers that need a URL must supply one
|
|
7
|
+
* explicitly via env or config.
|
|
8
|
+
*
|
|
9
|
+
* Resolution order (env wins over config):
|
|
10
|
+
* 1. Environment variable: SO_WEBHOOK_<KIND>_URL (e.g. SO_WEBHOOK_SLACK_URL)
|
|
11
|
+
* 2. Session Config field: webhooks.<kind>.url
|
|
12
|
+
* 3. Error: WebhookConfigError is thrown — no silent fallback to a personal domain.
|
|
13
|
+
*
|
|
14
|
+
* Supported kinds: 'slack' | 'discord' | 'generic' | 'gitlab-pipeline-status'
|
|
15
|
+
*
|
|
16
|
+
* Issue #228 — centralize webhook URL resolution + drop personal-domain default.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Named error
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Thrown by `resolveWebhookUrl` when no URL source (env or config) provides a
|
|
25
|
+
* webhook URL for the requested kind. Callers should catch this and surface an
|
|
26
|
+
* actionable message to the user.
|
|
27
|
+
*/
|
|
28
|
+
export class WebhookConfigError extends Error {
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} kind - The webhook kind that was requested.
|
|
31
|
+
* @param {string} [message] - Optional override; defaults to a descriptive message.
|
|
32
|
+
*/
|
|
33
|
+
constructor(kind, message) {
|
|
34
|
+
super(
|
|
35
|
+
message ??
|
|
36
|
+
`No webhook URL configured for kind "${kind}". ` +
|
|
37
|
+
`Set SO_WEBHOOK_${kind.toUpperCase().replace(/-/g, '_')}_URL ` +
|
|
38
|
+
`or add webhooks.${kind}.url to Session Config.`,
|
|
39
|
+
);
|
|
40
|
+
this.name = 'WebhookConfigError';
|
|
41
|
+
this.kind = kind;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Supported kinds
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
/** @type {ReadonlySet<string>} */
|
|
50
|
+
const SUPPORTED_KINDS = new Set([
|
|
51
|
+
'slack',
|
|
52
|
+
'discord',
|
|
53
|
+
'generic',
|
|
54
|
+
'gitlab-pipeline-status',
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Public API
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Resolve a webhook URL for the given kind.
|
|
63
|
+
*
|
|
64
|
+
* Resolution order:
|
|
65
|
+
* 1. `SO_WEBHOOK_<KIND>_URL` environment variable (e.g. `SO_WEBHOOK_SLACK_URL`)
|
|
66
|
+
* — kind is upper-cased and hyphens become underscores.
|
|
67
|
+
* 2. `config.webhooks.<kind>.url` — when a Session Config object is provided.
|
|
68
|
+
* 3. Throws {@link WebhookConfigError} — no silent personal-domain fallback.
|
|
69
|
+
*
|
|
70
|
+
* @param {{ kind: string, config?: object }} options
|
|
71
|
+
* - `kind` — One of the supported webhook kinds (see SUPPORTED_KINDS).
|
|
72
|
+
* - `config` — Parsed Session Config object (optional). Must have a `webhooks`
|
|
73
|
+
* sub-object if a config-based URL is desired.
|
|
74
|
+
* @returns {string} The resolved webhook URL (guaranteed non-empty string).
|
|
75
|
+
* @throws {WebhookConfigError} When kind is unsupported or no URL is found.
|
|
76
|
+
* @throws {TypeError} When `kind` is not a string.
|
|
77
|
+
*/
|
|
78
|
+
export function resolveWebhookUrl({ kind, config } = {}) {
|
|
79
|
+
if (typeof kind !== 'string' || kind.length === 0) {
|
|
80
|
+
throw new TypeError('resolveWebhookUrl: "kind" must be a non-empty string');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!SUPPORTED_KINDS.has(kind)) {
|
|
84
|
+
throw new WebhookConfigError(
|
|
85
|
+
kind,
|
|
86
|
+
`Unsupported webhook kind "${kind}". Supported kinds: ${[...SUPPORTED_KINDS].join(', ')}.`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 1. Environment variable: SO_WEBHOOK_<KIND>_URL
|
|
91
|
+
const envKey = `SO_WEBHOOK_${kind.toUpperCase().replace(/-/g, '_')}_URL`;
|
|
92
|
+
const envUrl = process.env[envKey];
|
|
93
|
+
if (typeof envUrl === 'string' && envUrl.length > 0) {
|
|
94
|
+
return envUrl;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 2. Session Config: webhooks.<kind>.url
|
|
98
|
+
const configUrl = config?.webhooks?.[kind]?.url;
|
|
99
|
+
if (typeof configUrl === 'string' && configUrl.length > 0) {
|
|
100
|
+
return configUrl;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 3. No source provided a URL — throw rather than fall back to a personal domain.
|
|
104
|
+
throw new WebhookConfigError(kind);
|
|
105
|
+
}
|