session-orchestrator 3.24.0 → 4.0.1
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/.agents/skills/architecture/SKILL.md +18 -0
- package/.agents/skills/autopilot/SKILL.md +17 -0
- package/.agents/skills/bootstrap/SKILL.md +20 -0
- package/.agents/skills/brainstorm/SKILL.md +22 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
- package/.agents/skills/debug/SKILL.md +22 -0
- package/.agents/skills/discovery/SKILL.md +20 -0
- package/.agents/skills/dispatcher/SKILL.md +15 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
- package/.agents/skills/ecosystem-health/SKILL.md +20 -0
- package/.agents/skills/eli5/SKILL.md +20 -0
- package/.agents/skills/eval/SKILL.md +21 -0
- package/.agents/skills/evolve/SKILL.md +21 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
- package/.agents/skills/gitlab-ops/SKILL.md +20 -0
- package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
- package/.agents/skills/grill/SKILL.md +22 -0
- package/.agents/skills/hook-development/SKILL.md +15 -0
- package/.agents/skills/mcp-builder/SKILL.md +15 -0
- package/.agents/skills/memory-cleanup/SKILL.md +21 -0
- package/.agents/skills/mode-selector/SKILL.md +17 -0
- package/.agents/skills/npm-publish/SKILL.md +16 -0
- package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
- package/.agents/skills/persona-panel/SKILL.md +17 -0
- package/.agents/skills/plan/SKILL.md +20 -0
- package/.agents/skills/playwright-driver/SKILL.md +20 -0
- package/.agents/skills/quality-gates/SKILL.md +20 -0
- package/.agents/skills/reconcile/SKILL.md +21 -0
- package/.agents/skills/remote-offload/SKILL.md +20 -0
- package/.agents/skills/repo-audit/SKILL.md +16 -0
- package/.agents/skills/session-end/SKILL.md +20 -0
- package/.agents/skills/session-plan/SKILL.md +20 -0
- package/.agents/skills/session-start/SKILL.md +20 -0
- package/.agents/skills/spinout/SKILL.md +16 -0
- package/.agents/skills/sunset-review/SKILL.md +16 -0
- package/.agents/skills/test-runner/SKILL.md +20 -0
- package/.agents/skills/tmux-layout/SKILL.md +21 -0
- package/.agents/skills/using-orchestrator/SKILL.md +17 -0
- package/.agents/skills/vault-mirror/SKILL.md +15 -0
- package/.agents/skills/vault-sync/SKILL.md +15 -0
- package/.agents/skills/wave-executor/SKILL.md +20 -0
- package/.agents/skills/write-executable-plan/SKILL.md +22 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +3 -2
- package/.codex-plugin/skills/architecture/SKILL.md +20 -0
- package/.codex-plugin/skills/autopilot/SKILL.md +21 -0
- package/.codex-plugin/skills/autopilot/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/bootstrap/SKILL.md +22 -0
- package/.codex-plugin/skills/bootstrap/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/brainstorm/SKILL.md +22 -0
- package/.codex-plugin/skills/brainstorm/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/claude-md-drift-check/SKILL.md +17 -0
- package/.codex-plugin/skills/close/SKILL.md +21 -0
- package/.codex-plugin/skills/close/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +24 -0
- package/.codex-plugin/skills/debug/SKILL.md +21 -0
- package/.codex-plugin/skills/debug/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/discovery/SKILL.md +21 -0
- package/.codex-plugin/skills/discovery/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/dispatcher/SKILL.md +21 -0
- package/.codex-plugin/skills/dispatcher/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/docs-orchestrator/SKILL.md +20 -0
- package/.codex-plugin/skills/ecosystem-health/SKILL.md +22 -0
- package/.codex-plugin/skills/eli5/SKILL.md +21 -0
- package/.codex-plugin/skills/eli5/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/eval/SKILL.md +21 -0
- package/.codex-plugin/skills/eval/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/evolve/SKILL.md +21 -0
- package/.codex-plugin/skills/evolve/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/frontmatter-guard/SKILL.md +17 -0
- package/.codex-plugin/skills/gitlab-ops/SKILL.md +22 -0
- package/.codex-plugin/skills/gitlab-portfolio/SKILL.md +17 -0
- package/.codex-plugin/skills/go/SKILL.md +22 -0
- package/.codex-plugin/skills/go/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/grill/SKILL.md +21 -0
- package/.codex-plugin/skills/grill/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/harness-audit/SKILL.md +19 -0
- package/.codex-plugin/skills/harness-audit/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/hook-development/SKILL.md +17 -0
- package/.codex-plugin/skills/mcp-builder/SKILL.md +17 -0
- package/.codex-plugin/skills/memory-cleanup/SKILL.md +21 -0
- package/.codex-plugin/skills/memory-cleanup/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/mode-selector/SKILL.md +19 -0
- package/.codex-plugin/skills/npm-publish/SKILL.md +18 -0
- package/.codex-plugin/skills/peekaboo-driver/SKILL.md +20 -0
- package/.codex-plugin/skills/persona-panel/SKILL.md +22 -0
- package/.codex-plugin/skills/persona-panel/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/plan/SKILL.md +22 -0
- package/.codex-plugin/skills/plan/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/playwright-driver/SKILL.md +22 -0
- package/.codex-plugin/skills/portfolio/SKILL.md +21 -0
- package/.codex-plugin/skills/portfolio/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/quality-gates/SKILL.md +22 -0
- package/.codex-plugin/skills/reconcile/SKILL.md +21 -0
- package/.codex-plugin/skills/reconcile/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/release/SKILL.md +22 -0
- package/.codex-plugin/skills/release/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/remote-offload/SKILL.md +22 -0
- package/.codex-plugin/skills/repo-audit/SKILL.md +19 -0
- package/.codex-plugin/skills/repo-audit/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/session/SKILL.md +21 -0
- package/.codex-plugin/skills/session/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/session-end/SKILL.md +22 -0
- package/.codex-plugin/skills/session-plan/SKILL.md +22 -0
- package/.codex-plugin/skills/session-start/SKILL.md +22 -0
- package/.codex-plugin/skills/spinout/SKILL.md +21 -0
- package/.codex-plugin/skills/spinout/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/sunset-review/SKILL.md +21 -0
- package/.codex-plugin/skills/sunset-review/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/templates-ack/SKILL.md +21 -0
- package/.codex-plugin/skills/templates-ack/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/test/SKILL.md +21 -0
- package/.codex-plugin/skills/test/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/test-runner/SKILL.md +22 -0
- package/.codex-plugin/skills/tmux-layout/SKILL.md +23 -0
- package/.codex-plugin/skills/using-orchestrator/SKILL.md +19 -0
- package/.codex-plugin/skills/vault-mirror/SKILL.md +17 -0
- package/.codex-plugin/skills/vault-sync/SKILL.md +17 -0
- package/.codex-plugin/skills/wave-executor/SKILL.md +22 -0
- package/.codex-plugin/skills/write-executable-plan/SKILL.md +24 -0
- package/.cursor/commands/autopilot.md +2 -2
- package/.cursor/commands/bootstrap.md +1 -1
- package/.cursor/commands/brainstorm.md +1 -1
- package/.cursor/commands/debug.md +1 -1
- package/.cursor/commands/discovery.md +1 -1
- package/.cursor/commands/dispatcher.md +2 -2
- package/.cursor/commands/eli5.md +2 -2
- package/.cursor/commands/eval.md +2 -2
- package/.cursor/commands/evolve.md +1 -1
- package/.cursor/commands/go.md +1 -1
- package/.cursor/commands/grill.md +2 -2
- package/.cursor/commands/memory-cleanup.md +2 -2
- package/.cursor/commands/persona-panel.md +1 -1
- package/.cursor/commands/plan.md +1 -1
- package/.cursor/commands/portfolio.md +1 -1
- package/.cursor/commands/reconcile.md +2 -2
- package/.cursor/commands/release.md +2 -2
- package/.cursor/commands/session.md +2 -2
- package/.cursor/commands/spinout.md +2 -2
- package/.cursor/commands/sunset-review.md +2 -2
- package/.cursor/commands/templates-ack.md +2 -2
- package/.cursor/commands/test.md +2 -2
- package/.cursor/skills/brainstorm/SKILL.md +1 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/quality-gates/SKILL.md +1 -1
- package/.cursor/skills/remote-offload/SKILL.md +1 -1
- package/.cursor-plugin/plugin.json +30 -0
- package/.orchestrator/policy/blocked-commands.json +121 -0
- package/.orchestrator/policy/ecosystem.schema.json +66 -0
- package/.orchestrator/policy/quality-gates.example.json +16 -0
- package/.orchestrator/policy/quality-gates.schema.json +38 -0
- package/.orchestrator/policy/templates-policy.json +27 -0
- package/.orchestrator/policy/test-profiles.json +47 -0
- package/AGENTS.md +225 -0
- package/CHANGELOG.md +1314 -2
- package/NOTICE +11 -6
- package/README.md +135 -94
- package/agents/eval-judge.md +1 -1
- package/agents/skill-applied-judge.md +1 -1
- package/assets/wave-lifecycle.svg +98 -0
- package/commands/release.md +6 -3
- package/commands/session.md +18 -3
- package/docs/README.md +4 -0
- package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
- package/docs/baseline.md +67 -0
- package/docs/ci-setup.md +108 -62
- package/docs/codex-setup.md +107 -29
- package/docs/components.md +38 -16
- package/docs/cursor-setup.md +6 -2
- package/docs/events-schema.md +9 -6
- package/docs/instruction-delivery.md +69 -0
- package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
- package/docs/migration-v4.md +365 -0
- package/docs/pi-setup.md +6 -1
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/rule-authoring.md +85 -19
- package/docs/scope-collision-guard.md +5 -5
- package/docs/session-config-reference.md +57 -56
- package/docs/session-config-template.md +6 -29
- package/docs/telemetry.md +157 -3
- package/docs/vault-docs-architecture.md +50 -11
- package/hooks/_lib/hook-import-set.json +1488 -0
- package/hooks/_lib/subagent-transcript.mjs +562 -0
- package/hooks/config-protection.mjs +2 -2
- package/hooks/cwd-change-restore.mjs +2 -2
- package/hooks/enforce-commands.mjs +69 -0
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +10 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +3 -3
- package/hooks/on-session-end.mjs +2 -2
- package/hooks/on-session-start.mjs +103 -2
- package/hooks/on-stop.mjs +60 -14
- package/hooks/operator-steer.mjs +2 -2
- package/hooks/post-bash-write-verify.mjs +85 -0
- package/hooks/post-edit-import-probe.mjs +344 -0
- package/hooks/post-subagent-discovery-validator.mjs +187 -431
- package/hooks/post-tool-batch-wave-signal.mjs +118 -4
- package/hooks/post-tool-failure-corrective-context.mjs +2 -2
- package/hooks/post-tooluse-frontend-slop.mjs +3 -3
- package/hooks/pre-bash-destructive-guard.mjs +39 -13
- package/hooks/skill-invocation-telemetry.mjs +17 -5
- package/hooks/subagent-telemetry.mjs +13 -4
- package/monitors/monitors.json +3 -3
- package/package.json +9 -1
- package/pi/prompts/session.md +2 -2
- package/scripts/backfill-abandoned-sessions.mjs +50 -4
- package/scripts/backfill-learnings-from-vault.mjs +9 -3
- package/scripts/dialectic-deriver.mjs +73 -8
- package/scripts/export-hw-learnings.mjs +113 -1
- package/scripts/generate-agents-skills.mjs +378 -0
- package/scripts/generate-codex-skills.mjs +246 -0
- package/scripts/generate-cursor-adapter.mjs +45 -8
- package/scripts/generate-hook-import-set.mjs +292 -0
- package/scripts/lib/agent-status.mjs +13 -2
- package/scripts/lib/auto-dream.mjs +38 -36
- package/scripts/lib/autonomy/suitability.mjs +6 -0
- package/scripts/lib/autopilot/loop.mjs +2 -2
- package/scripts/lib/ci-status-banner.mjs +220 -75
- package/scripts/lib/codex/plugin-contract.mjs +88 -6
- package/scripts/lib/config/auto-dream.mjs +2 -1
- package/scripts/lib/config/block-header.mjs +8 -0
- package/scripts/lib/config/block-preprocess.mjs +177 -0
- package/scripts/lib/config/broken-window.mjs +2 -1
- package/scripts/lib/config/cold-start.mjs +2 -1
- package/scripts/lib/config/config-protection.mjs +22 -2
- package/scripts/lib/config/context-coverage.mjs +2 -1
- package/scripts/lib/config/cross-repo.mjs +2 -1
- package/scripts/lib/config/custom-phases.mjs +2 -1
- package/scripts/lib/config/dialectic.mjs +2 -1
- package/scripts/lib/config/discovery-validator.mjs +2 -1
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
- package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
- package/scripts/lib/config/docs-orchestrator.mjs +2 -1
- package/scripts/lib/config/docs-staleness.mjs +2 -1
- package/scripts/lib/config/drift-check.mjs +2 -1
- package/scripts/lib/config/eval.mjs +2 -1
- package/scripts/lib/config/events-rotation.mjs +2 -1
- package/scripts/lib/config/evolve.mjs +8 -2
- package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
- package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
- package/scripts/lib/config/handover-gate.mjs +2 -1
- package/scripts/lib/config/health-endpoints.mjs +7 -2
- package/scripts/lib/config/host-paths.mjs +20 -4
- package/scripts/lib/config/issue-budget.mjs +2 -1
- package/scripts/lib/config/loop-guard.mjs +2 -1
- package/scripts/lib/config/memory.mjs +2 -1
- package/scripts/lib/config/moc-staleness.mjs +2 -1
- package/scripts/lib/config/persona-gate-wave.mjs +2 -1
- package/scripts/lib/config/private-config-dir.mjs +67 -0
- package/scripts/lib/config/reconcile.mjs +2 -1
- package/scripts/lib/config/remote-hosts.mjs +2 -1
- package/scripts/lib/config/section-extractor.mjs +7 -1
- package/scripts/lib/config/skill-evolution.mjs +2 -1
- package/scripts/lib/config/slopcheck.mjs +2 -1
- package/scripts/lib/config/state-md-lock.mjs +2 -1
- package/scripts/lib/config/templates-first.mjs +2 -1
- package/scripts/lib/config/test.mjs +2 -1
- package/scripts/lib/config/vault-integration.mjs +7 -1
- package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
- package/scripts/lib/config/vault-staleness.mjs +2 -1
- package/scripts/lib/config/vault-sync.mjs +2 -1
- package/scripts/lib/config/verification-auto-fix.mjs +2 -1
- package/scripts/lib/config/wave-reviewers.mjs +2 -1
- package/scripts/lib/config/worktree-orphans.mjs +2 -1
- package/scripts/lib/convergence-monitor.mjs +82 -16
- package/scripts/lib/dispatcher/rank.mjs +124 -48
- package/scripts/lib/ecosystem-health.mjs +16 -2
- package/scripts/lib/eval/engine.mjs +9 -1
- package/scripts/lib/eval/session-resolve.mjs +23 -4
- package/scripts/lib/events.mjs +22 -6
- package/scripts/lib/frontmatter-guard.mjs +131 -13
- package/scripts/lib/gates/gate-full.mjs +30 -0
- package/scripts/lib/gates/gate-helpers.mjs +76 -0
- package/scripts/lib/hardware-pattern-detector.mjs +18 -1
- package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
- package/scripts/lib/host-identity.mjs +50 -11
- package/scripts/lib/instruction-budget-guard.mjs +171 -5
- package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
- package/scripts/lib/learnings/io.mjs +60 -6
- package/scripts/lib/memory-proposals/store.mjs +30 -22
- package/scripts/lib/owner-config-banner.mjs +41 -6
- package/scripts/lib/owner-config-loader.mjs +21 -10
- package/scripts/lib/owner-interview.mjs +3 -3
- package/scripts/lib/owner-yaml.mjs +215 -15
- package/scripts/lib/platform.mjs +108 -15
- package/scripts/lib/plugin-update-banner.mjs +414 -0
- package/scripts/lib/project-hygiene.mjs +38 -2
- package/scripts/lib/qg-command-drift-banner.mjs +50 -12
- package/scripts/lib/quality-gate.mjs +133 -44
- package/scripts/lib/reconcile/emitter.mjs +68 -6
- package/scripts/lib/reconcile/engine.mjs +51 -11
- package/scripts/lib/reconcile/idempotency.mjs +37 -4
- package/scripts/lib/reconcile/writer.mjs +40 -18
- package/scripts/lib/session-close-backfill.mjs +67 -9
- package/scripts/lib/session-id.mjs +12 -23
- package/scripts/lib/session-identity/own-session.mjs +125 -10
- package/scripts/lib/session-lock-shape.mjs +43 -0
- package/scripts/lib/session-lock.mjs +5 -10
- package/scripts/lib/session-registry.mjs +25 -9
- package/scripts/lib/session-schema/constants.mjs +64 -3
- package/scripts/lib/session-schema/validator.mjs +38 -4
- package/scripts/lib/session-start-probes.mjs +30 -1
- package/scripts/lib/sessions-staleness-banner.mjs +18 -11
- package/scripts/lib/skill-health/join.mjs +17 -4
- package/scripts/lib/state-md.mjs +78 -0
- package/scripts/lib/sunset/walker.mjs +6 -0
- package/scripts/lib/telemetry/schema.mjs +202 -9
- package/scripts/lib/telemetry/sync.mjs +368 -12
- package/scripts/lib/telemetry-flush-health-banner.mjs +211 -0
- package/scripts/lib/validate/check-agents-skills.mjs +327 -0
- package/scripts/lib/validate/check-agents.mjs +3 -3
- package/scripts/lib/validate/check-codex-skills.mjs +191 -0
- package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
- package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
- package/scripts/lib/validate/check-owner-leakage.mjs +319 -22
- package/scripts/lib/validate/check-skill-links.mjs +193 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +47 -28
- package/scripts/lib/validate/check-test-git-config-target.mjs +192 -12
- package/scripts/lib/validate/check-unwired-features.mjs +163 -15
- package/scripts/lib/validate/check-validator-registration.mjs +10 -4
- package/scripts/lib/validate/confidential-names.mjs +95 -30
- package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
- package/scripts/lib/validate/repo-files.mjs +48 -14
- package/scripts/lib/vault-backfill/template.mjs +63 -6
- package/scripts/lib/vault-mirror/process.mjs +165 -42
- package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
- package/scripts/lib/vault-status/narrative-mirror.mjs +127 -18
- package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
- package/scripts/lib/wave-executor/remote-dispatch.mjs +5 -7
- package/scripts/lib/wave-resource-gate.mjs +8 -2
- package/scripts/lib/wave-sizing.mjs +4 -1
- package/scripts/lib/wave-transcript-tail.mjs +118 -4
- package/scripts/materialize-wave-scope.mjs +12 -5
- package/scripts/memory-propose.mjs +19 -5
- package/scripts/migrate-cold-start-seed.mjs +4 -1
- package/scripts/parse-config.mjs +60 -3
- package/scripts/release.mjs +430 -31
- package/scripts/repair-invalid-sessions.mjs +3 -3
- package/scripts/run-quality-gate.mjs +128 -11
- package/scripts/site-numbers.mjs +344 -8
- package/scripts/sweep-expired-learnings.mjs +90 -0
- package/scripts/sync-vault-schema.mjs +3 -1
- package/scripts/telemetry.mjs +2 -2
- package/scripts/validate-plugin.mjs +164 -0
- package/scripts/validate-wave-scope.mjs +28 -8
- package/scripts/wave-scope-binding.mjs +215 -0
- package/skills/_shared/instruction-file-resolution.md +10 -0
- package/skills/_shared/parallel-aware-preamble.md +1 -0
- package/skills/_shared/platform-tools.md +1 -1
- package/skills/_shared/state-ownership.md +1 -1
- package/skills/architecture/SKILL.md +7 -5
- package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
- package/skills/autopilot/SKILL.md +4 -18
- package/skills/claude-md-drift-check/SKILL.md +5 -1
- package/skills/claude-md-drift-check/checker.mjs +62 -2
- package/skills/convergence-monitoring/SIGNALS.md +55 -0
- package/skills/discovery/probes/vault-staleness.mjs +37 -13
- package/skills/discovery/probes-arch.md +20 -18
- package/skills/dispatcher/SKILL.md +3 -2
- package/skills/evolve/SKILL.md +65 -26
- package/skills/frontmatter-guard/SKILL.md +11 -5
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +33 -0
- package/skills/remote-offload/SKILL.md +1 -1
- package/skills/session-end/SKILL.md +18 -905
- package/skills/session-end/phase-3-6-tail.md +10 -3
- package/skills/session-end/plan-verification.md +221 -155
- package/skills/session-end/references/phase-2-quality-gate.md +93 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
- package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
- package/skills/session-end/references/session-summary-template.md +62 -0
- package/skills/session-plan/SKILL.md +49 -0
- package/skills/session-start/SKILL.md +22 -904
- package/skills/session-start/phase-8-5-express-path.md +1 -1
- package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
- package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +160 -0
- package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
- package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
- package/skills/vault-sync/SKILL.md +10 -0
- package/skills/vault-sync/validator.mjs +21 -27
- package/skills/wave-executor/SKILL.md +15 -1
- package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
- package/skills/wave-executor/references/wave-loop-review.md +570 -0
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
- package/skills/wave-executor/wave-loop.md +14 -1309
- package/templates/_shared/journey-manifest.md +10 -6
- package/.cursor/commands/autopilot-multi.md +0 -14
- package/.cursor/commands/contract-version-bump.md +0 -14
- package/.cursor/commands/journey-audit.md +0 -14
- package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
- package/.cursor/skills/daily/SKILL.md +0 -12
- package/.cursor/skills/domain-model/SKILL.md +0 -13
- package/.cursor/skills/journey-audit/SKILL.md +0 -13
- package/.cursor/skills/skill-creator/SKILL.md +0 -13
- package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
- package/commands/autopilot-multi.md +0 -74
- package/commands/contract-version-bump.md +0 -28
- package/commands/journey-audit.md +0 -43
- package/pi/prompts/autopilot-multi.md +0 -12
- package/pi/prompts/contract-version-bump.md +0 -12
- package/pi/prompts/journey-audit.md +0 -12
- package/scripts/autopilot-multi.mjs +0 -885
- package/scripts/backfill-learnings-expires.mjs +0 -196
- package/scripts/backfill-learnings.mjs +0 -203
- package/scripts/fleet-instruction-scan.mjs +0 -141
- package/scripts/lib/autopilot/dep-graph.mjs +0 -417
- package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
- package/scripts/lib/webhook-url.mjs +0 -105
- package/scripts/lifecycle-sim-v6.mjs +0 -347
- package/scripts/migrate-learnings-jsonl.mjs +0 -189
- package/scripts/migrate-subagents-jsonl.mjs +0 -196
- package/scripts/upload-social-preview.mjs +0 -316
- package/skills/_shared/model-selection.md +0 -64
- package/skills/contract-version-bump/SKILL.md +0 -219
- package/skills/daily/SKILL.md +0 -222
- package/skills/daily/generate.sh +0 -92
- package/skills/daily/templates/daily.md.tpl +0 -36
- package/skills/journey-audit/SKILL.md +0 -270
- package/skills/skill-creator/SKILL.md +0 -168
- package/skills/ubiquitous-language/SKILL.md +0 -97
- package/skills/vault-sync/package-lock.json +0 -40
- /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
- /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dispatch-common.mjs — shared base for the two foreign-dispatch adapters:
|
|
3
|
+
* `foreign-dispatch.mjs` (Cursor channel, `cursor-agent`) and
|
|
4
|
+
* `remote-dispatch.mjs` (remote-host channel, `offload`). Two adapters, one
|
|
5
|
+
* base (#1204).
|
|
6
|
+
*
|
|
7
|
+
* These six symbols moved here because each one is an enforcement point or a
|
|
8
|
+
* budget constant that must NOT diverge between the two channels: the
|
|
9
|
+
* `never_foreign` role lock, the run-id path-segment validator, the
|
|
10
|
+
* SIGTERM→SIGKILL child runner, and the two dispatch-budget constants. Before
|
|
11
|
+
* this module existed, `remote-dispatch.mjs` imported all five from
|
|
12
|
+
* `foreign-dispatch.mjs` directly — a Cursor-specific file acting as a shared
|
|
13
|
+
* base for a channel that has nothing to do with Cursor. Both adapters now
|
|
14
|
+
* import from here instead, so neither exports something the other must reach
|
|
15
|
+
* through.
|
|
16
|
+
*
|
|
17
|
+
* Revisit-Trigger: if a THIRD dispatch channel ever needs a different budget
|
|
18
|
+
* default or a different never-foreign list, split the constant per-channel
|
|
19
|
+
* at that point rather than overriding it at the call site — a call-site
|
|
20
|
+
* override would silently diverge from the one enforcement point this module
|
|
21
|
+
* exists to be.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Roles that may NEVER run on a foreign model. Copied verbatim from
|
|
26
|
+
* account-switch `tools/routing/routing.yaml` § never_foreign (read 2026-08-25).
|
|
27
|
+
* Keep in sync by hand — the YAML lives in a different repo and is parsed there
|
|
28
|
+
* by awk, not by a shared library.
|
|
29
|
+
* @type {readonly string[]}
|
|
30
|
+
*/
|
|
31
|
+
export const NEVER_FOREIGN_ROLES = Object.freeze([
|
|
32
|
+
'impl-core',
|
|
33
|
+
'security-review',
|
|
34
|
+
'migration',
|
|
35
|
+
'release',
|
|
36
|
+
'secrets',
|
|
37
|
+
'incident',
|
|
38
|
+
'refactor-crosscut',
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
/** Default wall-clock budget for one foreign dispatch, in seconds.
|
|
42
|
+
* This is a FLOOR, not a suggestion: measured 2026-08-23, `cursor-grok-4.6-high`
|
|
43
|
+
* ran 2 of 3 hard-test tasks past a 540 s cap (recorded as DNF). Lowering this
|
|
44
|
+
* manufactures timeouts and reads as model failure. */
|
|
45
|
+
export const DEFAULT_TIMEOUT_SEC = 900;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Grace period between SIGTERM and SIGKILL, in ms.
|
|
49
|
+
*
|
|
50
|
+
* `child.kill('SIGTERM')` is a REQUEST: a child that installs a handler and
|
|
51
|
+
* ignores it never emits `close`, and this module's only resolve paths are
|
|
52
|
+
* `close` and `error` — so without escalation a wedged `cursor-agent` hangs
|
|
53
|
+
* `dispatchForeign` forever and takes the whole wave with it. SIGKILL is not
|
|
54
|
+
* catchable, so the escalation always terminates.
|
|
55
|
+
*
|
|
56
|
+
* Named ceiling (BV-004): 10 s is enough for a cooperative child to flush its
|
|
57
|
+
* stream-json tail and exit. Revisit if a well-behaved `cursor-agent` is ever
|
|
58
|
+
* measured needing longer than that to shut down — raise the constant, never
|
|
59
|
+
* drop the escalation.
|
|
60
|
+
*/
|
|
61
|
+
export const DEFAULT_KILL_GRACE_MS = 10_000;
|
|
62
|
+
|
|
63
|
+
/** A runId names a directory and a log file. Anything outside this alphabet —
|
|
64
|
+
* or the two relative-path literals the alphabet happens to admit — can escape
|
|
65
|
+
* the parent it is joined to. */
|
|
66
|
+
const SAFE_RUN_ID = /^[A-Za-z0-9._-]+$/;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {unknown} runId
|
|
70
|
+
* @returns {boolean} true when the id is safe to use as a path segment.
|
|
71
|
+
*/
|
|
72
|
+
export function isSafeRunId(runId) {
|
|
73
|
+
const s = String(runId ?? '');
|
|
74
|
+
if (s === '.' || s === '..') return false; // admitted by SAFE_RUN_ID, still an escape
|
|
75
|
+
return SAFE_RUN_ID.test(s);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {string} role
|
|
80
|
+
* @returns {boolean} true when the role is locked to Claude.
|
|
81
|
+
*/
|
|
82
|
+
export function isNeverForeignRole(role) {
|
|
83
|
+
return NEVER_FOREIGN_ROLES.includes(String(role || '').trim());
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Run the child process, capturing stdout+stderr merged, with a hand-rolled
|
|
88
|
+
* SIGTERM timeout that ESCALATES to SIGKILL. `spawnFn` is the DI seam so tests
|
|
89
|
+
* never touch the real `cursor-agent` binary.
|
|
90
|
+
*
|
|
91
|
+
* The escalation is the difference between a bounded dispatch and a hung wave:
|
|
92
|
+
* this function's only resolve paths are the child's `close` and `error`
|
|
93
|
+
* events, so a child that ignores SIGTERM never lets the promise settle. See
|
|
94
|
+
* {@link DEFAULT_KILL_GRACE_MS}.
|
|
95
|
+
*
|
|
96
|
+
* @returns {Promise<{capture: string, exitCode: number|null, signal: string|null,
|
|
97
|
+
* timerFired: boolean, killSignals: string[], errorCode: string|null}>}
|
|
98
|
+
*/
|
|
99
|
+
export function runChild(cmd, args, options, { spawnFn, timeoutMs, killGraceMs }) {
|
|
100
|
+
return new Promise((resolve) => {
|
|
101
|
+
/** @type {string[]} */
|
|
102
|
+
const killSignals = [];
|
|
103
|
+
let child;
|
|
104
|
+
try {
|
|
105
|
+
child = spawnFn(cmd, args, options);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
resolve({
|
|
108
|
+
capture: `spawn failed: ${err && err.message}`,
|
|
109
|
+
exitCode: null,
|
|
110
|
+
signal: null,
|
|
111
|
+
timerFired: false,
|
|
112
|
+
killSignals,
|
|
113
|
+
errorCode: (err && err.code) || null,
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let capture = '';
|
|
119
|
+
let timerFired = false;
|
|
120
|
+
let settled = false;
|
|
121
|
+
let errorCode = null;
|
|
122
|
+
/** @type {ReturnType<typeof setTimeout>|null} */
|
|
123
|
+
let graceTimer = null;
|
|
124
|
+
|
|
125
|
+
const onChunk = (chunk) => {
|
|
126
|
+
capture += chunk.toString();
|
|
127
|
+
};
|
|
128
|
+
child.stdout?.on('data', onChunk);
|
|
129
|
+
child.stderr?.on('data', onChunk);
|
|
130
|
+
|
|
131
|
+
const send = (signal) => {
|
|
132
|
+
killSignals.push(signal);
|
|
133
|
+
try {
|
|
134
|
+
child.kill(signal);
|
|
135
|
+
} catch {
|
|
136
|
+
/* already gone */
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const timer = setTimeout(() => {
|
|
141
|
+
timerFired = true;
|
|
142
|
+
send('SIGTERM');
|
|
143
|
+
// SIGTERM is a request; SIGKILL is not. A child that installs a handler
|
|
144
|
+
// and declines to exit would otherwise hang this promise forever.
|
|
145
|
+
graceTimer = setTimeout(() => send('SIGKILL'), killGraceMs);
|
|
146
|
+
graceTimer.unref?.();
|
|
147
|
+
}, timeoutMs);
|
|
148
|
+
|
|
149
|
+
const finish = (exitCode, signal) => {
|
|
150
|
+
if (settled) return;
|
|
151
|
+
settled = true;
|
|
152
|
+
clearTimeout(timer);
|
|
153
|
+
if (graceTimer) clearTimeout(graceTimer);
|
|
154
|
+
resolve({ capture, exitCode, signal, timerFired, killSignals, errorCode });
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
child.on('error', (err) => {
|
|
158
|
+
errorCode = (err && err.code) || null;
|
|
159
|
+
capture += `\nchild error: ${err && err.message}`;
|
|
160
|
+
finish(null, null);
|
|
161
|
+
});
|
|
162
|
+
child.on('close', (code, signal) => finish(code, signal ?? null));
|
|
163
|
+
});
|
|
164
|
+
}
|
|
@@ -54,45 +54,13 @@ import path from 'node:path';
|
|
|
54
54
|
|
|
55
55
|
import { emitEvent, sessionAttribution } from '../events.mjs';
|
|
56
56
|
import { isPathInside } from '../path-utils.mjs';
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
*/
|
|
65
|
-
export const NEVER_FOREIGN_ROLES = Object.freeze([
|
|
66
|
-
'impl-core',
|
|
67
|
-
'security-review',
|
|
68
|
-
'migration',
|
|
69
|
-
'release',
|
|
70
|
-
'secrets',
|
|
71
|
-
'incident',
|
|
72
|
-
'refactor-crosscut',
|
|
73
|
-
]);
|
|
74
|
-
|
|
75
|
-
/** Default wall-clock budget for one foreign dispatch, in seconds.
|
|
76
|
-
* This is a FLOOR, not a suggestion: measured 2026-08-23, `cursor-grok-4.6-high`
|
|
77
|
-
* ran 2 of 3 hard-test tasks past a 540 s cap (recorded as DNF). Lowering this
|
|
78
|
-
* manufactures timeouts and reads as model failure. */
|
|
79
|
-
export const DEFAULT_TIMEOUT_SEC = 900;
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Grace period between SIGTERM and SIGKILL, in ms.
|
|
83
|
-
*
|
|
84
|
-
* `child.kill('SIGTERM')` is a REQUEST: a child that installs a handler and
|
|
85
|
-
* ignores it never emits `close`, and this module's only resolve paths are
|
|
86
|
-
* `close` and `error` — so without escalation a wedged `cursor-agent` hangs
|
|
87
|
-
* `dispatchForeign` forever and takes the whole wave with it. SIGKILL is not
|
|
88
|
-
* catchable, so the escalation always terminates.
|
|
89
|
-
*
|
|
90
|
-
* Named ceiling (BV-004): 10 s is enough for a cooperative child to flush its
|
|
91
|
-
* stream-json tail and exit. Revisit if a well-behaved `cursor-agent` is ever
|
|
92
|
-
* measured needing longer than that to shut down — raise the constant, never
|
|
93
|
-
* drop the escalation.
|
|
94
|
-
*/
|
|
95
|
-
export const DEFAULT_KILL_GRACE_MS = 10_000;
|
|
57
|
+
import {
|
|
58
|
+
DEFAULT_KILL_GRACE_MS,
|
|
59
|
+
DEFAULT_TIMEOUT_SEC,
|
|
60
|
+
isNeverForeignRole,
|
|
61
|
+
isSafeRunId,
|
|
62
|
+
runChild,
|
|
63
|
+
} from './dispatch-common.mjs';
|
|
96
64
|
|
|
97
65
|
/**
|
|
98
66
|
* argv prefix for every foreign dispatch, ahead of `--model`/`--output-format`.
|
|
@@ -156,29 +124,6 @@ export const FOREIGN_ENV_ALLOWLIST = Object.freeze([
|
|
|
156
124
|
* directory named this way. */
|
|
157
125
|
export const FOREIGN_TMP_PREFIX = 'so-foreign';
|
|
158
126
|
|
|
159
|
-
/** A runId names a directory and a log file. Anything outside this alphabet —
|
|
160
|
-
* or the two relative-path literals the alphabet happens to admit — can escape
|
|
161
|
-
* the parent it is joined to. */
|
|
162
|
-
const SAFE_RUN_ID = /^[A-Za-z0-9._-]+$/;
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* @param {unknown} runId
|
|
166
|
-
* @returns {boolean} true when the id is safe to use as a path segment.
|
|
167
|
-
*/
|
|
168
|
-
export function isSafeRunId(runId) {
|
|
169
|
-
const s = String(runId ?? '');
|
|
170
|
-
if (s === '.' || s === '..') return false; // admitted by SAFE_RUN_ID, still an escape
|
|
171
|
-
return SAFE_RUN_ID.test(s);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* @param {string} role
|
|
176
|
-
* @returns {boolean} true when the role is locked to Claude.
|
|
177
|
-
*/
|
|
178
|
-
export function isNeverForeignRole(role) {
|
|
179
|
-
return NEVER_FOREIGN_ROLES.includes(String(role || '').trim());
|
|
180
|
-
}
|
|
181
|
-
|
|
182
127
|
/**
|
|
183
128
|
* Build the allowlisted child environment.
|
|
184
129
|
* @param {Record<string, string|undefined>} [source]
|
|
@@ -317,86 +262,6 @@ export function parseCursorStream(raw) {
|
|
|
317
262
|
*/
|
|
318
263
|
export const MEASUREMENT_EXCLUDES = Object.freeze(['node_modules']);
|
|
319
264
|
|
|
320
|
-
/**
|
|
321
|
-
* Run the child process, capturing stdout+stderr merged, with a hand-rolled
|
|
322
|
-
* SIGTERM timeout that ESCALATES to SIGKILL. `spawnFn` is the DI seam so tests
|
|
323
|
-
* never touch the real `cursor-agent` binary.
|
|
324
|
-
*
|
|
325
|
-
* The escalation is the difference between a bounded dispatch and a hung wave:
|
|
326
|
-
* this function's only resolve paths are the child's `close` and `error`
|
|
327
|
-
* events, so a child that ignores SIGTERM never lets the promise settle. See
|
|
328
|
-
* {@link DEFAULT_KILL_GRACE_MS}.
|
|
329
|
-
*
|
|
330
|
-
* @returns {Promise<{capture: string, exitCode: number|null, signal: string|null,
|
|
331
|
-
* timerFired: boolean, killSignals: string[], errorCode: string|null}>}
|
|
332
|
-
*/
|
|
333
|
-
export function runChild(cmd, args, options, { spawnFn, timeoutMs, killGraceMs }) {
|
|
334
|
-
return new Promise((resolve) => {
|
|
335
|
-
/** @type {string[]} */
|
|
336
|
-
const killSignals = [];
|
|
337
|
-
let child;
|
|
338
|
-
try {
|
|
339
|
-
child = spawnFn(cmd, args, options);
|
|
340
|
-
} catch (err) {
|
|
341
|
-
resolve({
|
|
342
|
-
capture: `spawn failed: ${err && err.message}`,
|
|
343
|
-
exitCode: null,
|
|
344
|
-
signal: null,
|
|
345
|
-
timerFired: false,
|
|
346
|
-
killSignals,
|
|
347
|
-
errorCode: (err && err.code) || null,
|
|
348
|
-
});
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
let capture = '';
|
|
353
|
-
let timerFired = false;
|
|
354
|
-
let settled = false;
|
|
355
|
-
let errorCode = null;
|
|
356
|
-
/** @type {ReturnType<typeof setTimeout>|null} */
|
|
357
|
-
let graceTimer = null;
|
|
358
|
-
|
|
359
|
-
const onChunk = (chunk) => {
|
|
360
|
-
capture += chunk.toString();
|
|
361
|
-
};
|
|
362
|
-
child.stdout?.on('data', onChunk);
|
|
363
|
-
child.stderr?.on('data', onChunk);
|
|
364
|
-
|
|
365
|
-
const send = (signal) => {
|
|
366
|
-
killSignals.push(signal);
|
|
367
|
-
try {
|
|
368
|
-
child.kill(signal);
|
|
369
|
-
} catch {
|
|
370
|
-
/* already gone */
|
|
371
|
-
}
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
const timer = setTimeout(() => {
|
|
375
|
-
timerFired = true;
|
|
376
|
-
send('SIGTERM');
|
|
377
|
-
// SIGTERM is a request; SIGKILL is not. A child that installs a handler
|
|
378
|
-
// and declines to exit would otherwise hang this promise forever.
|
|
379
|
-
graceTimer = setTimeout(() => send('SIGKILL'), killGraceMs);
|
|
380
|
-
graceTimer.unref?.();
|
|
381
|
-
}, timeoutMs);
|
|
382
|
-
|
|
383
|
-
const finish = (exitCode, signal) => {
|
|
384
|
-
if (settled) return;
|
|
385
|
-
settled = true;
|
|
386
|
-
clearTimeout(timer);
|
|
387
|
-
if (graceTimer) clearTimeout(graceTimer);
|
|
388
|
-
resolve({ capture, exitCode, signal, timerFired, killSignals, errorCode });
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
child.on('error', (err) => {
|
|
392
|
-
errorCode = (err && err.code) || null;
|
|
393
|
-
capture += `\nchild error: ${err && err.message}`;
|
|
394
|
-
finish(null, null);
|
|
395
|
-
});
|
|
396
|
-
child.on('close', (code, signal) => finish(code, signal ?? null));
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
|
|
400
265
|
/** Read `git` output as text, returning '' on failure (a detached worktree with
|
|
401
266
|
* no commits still answers, but a broken invocation must not throw here). */
|
|
402
267
|
function gitText(args, execFn) {
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* Relationship to `foreign-dispatch.mjs`: same contract, different channel.
|
|
7
7
|
* That module sends a task to a foreign MODEL on this machine; this one sends
|
|
8
8
|
* a task to Claude on ANOTHER machine. Both share the parts that must not
|
|
9
|
-
* diverge and are therefore IMPORTED
|
|
10
|
-
* lock ({@link isNeverForeignRole}),
|
|
11
|
-
* ({@link isSafeRunId}), the SIGTERM→SIGKILL
|
|
12
|
-
* and the two budget constants.
|
|
9
|
+
* diverge and are therefore IMPORTED from the common base (`dispatch-common.mjs`,
|
|
10
|
+
* #1204), never copied: the `never_foreign` role lock ({@link isNeverForeignRole}),
|
|
11
|
+
* the run-id path-segment validator ({@link isSafeRunId}), the SIGTERM→SIGKILL
|
|
12
|
+
* child runner ({@link runChild}), and the two budget constants.
|
|
13
13
|
*
|
|
14
14
|
* Three properties are load-bearing here and each has a test:
|
|
15
15
|
*
|
|
@@ -40,9 +40,7 @@ import {
|
|
|
40
40
|
isNeverForeignRole,
|
|
41
41
|
isSafeRunId,
|
|
42
42
|
runChild,
|
|
43
|
-
} from './
|
|
44
|
-
|
|
45
|
-
export { DEFAULT_KILL_GRACE_MS, DEFAULT_TIMEOUT_SEC };
|
|
43
|
+
} from './dispatch-common.mjs';
|
|
46
44
|
|
|
47
45
|
/** The `offload` binary. Resolved on PATH; a missing binary surfaces as ENOENT
|
|
48
46
|
* and is classified as a CHANNEL failure, never as a model failure. */
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { probe, evaluate } from './resource-probe.mjs';
|
|
11
|
-
import { isNeverForeignRole } from './wave-executor/foreign-dispatch.mjs';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Wave roles that may run on a declared remote host, mapped to the
|
|
@@ -147,7 +146,14 @@ async function applyOffloadDecision(result, opts) {
|
|
|
147
146
|
if (!Array.isArray(hosts) || hosts.length === 0) return result;
|
|
148
147
|
|
|
149
148
|
const role = String(waveRole ?? '').trim().toLowerCase();
|
|
150
|
-
|
|
149
|
+
// No isNeverForeignRole() check here (D8 #1204 LOW-1): the invariant is
|
|
150
|
+
// enforced STRUCTURALLY by this mapping table, not by an extra guard.
|
|
151
|
+
// NEVER_FOREIGN_ROLES {impl-core, security-review, migration, release,
|
|
152
|
+
// secrets, incident, refactor-crosscut} and OFFLOADABLE_WAVE_ROLES keys
|
|
153
|
+
// {quality, test, ui, perf} have ZERO overlap — measured by inspection of
|
|
154
|
+
// both literal lists — so a never-foreign role always falls through to
|
|
155
|
+
// `mappedRole === undefined` below regardless. A redundant call here was
|
|
156
|
+
// dead code that a lookup-table edit could not make live again on its own.
|
|
151
157
|
const mappedRole = OFFLOADABLE_WAVE_ROLES[role];
|
|
152
158
|
if (mappedRole === undefined) return result;
|
|
153
159
|
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
* Learning: coordinator-over-worktree-on-shared-files caused back-to-back regressions.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// 'unknown' is accepted because scripts/lib/session-close-backfill.mjs writes it for records whose
|
|
7
|
+
// type it could not measure (4.0.0). It is NOT a mode anyone selects — isolation resolves as for
|
|
8
|
+
// 'deep' (the conservative end). SSOT for the ledger enum: scripts/lib/session-schema/constants.mjs.
|
|
9
|
+
const VALID_SESSION_TYPES = ['housekeeping', 'feature', 'deep', 'unknown'];
|
|
7
10
|
const VALID_COLLISION_RISKS = ['low', 'medium', 'high'];
|
|
8
11
|
const VALID_CONFIG_ISOLATIONS = ['auto', 'worktree', 'none'];
|
|
9
12
|
const VALID_ENFORCEMENTS = ['strict', 'warn', 'off'];
|
|
@@ -96,9 +96,107 @@ const RE_EMIT_EVERY = 10;
|
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* git subcommands that mutate the shared index, the stash stack, or remote
|
|
99
|
-
* history — the PSA-007 prohibition list, verbatim.
|
|
99
|
+
* history — the PSA-007 prohibition list, verbatim. Matching the subcommand
|
|
100
|
+
* LITERAL is only the first half: the arguments decide whether the invocation
|
|
101
|
+
* writes anything at all (#1215).
|
|
102
|
+
*
|
|
103
|
+
* Group 1 is the subcommand, group 2 the remaining argument string.
|
|
104
|
+
*
|
|
105
|
+
* The global-flag run has TWO alternatives on purpose (W4/F2). A bare
|
|
106
|
+
* `(?:-[^\s]+\s+)*` cannot absorb a VALUE-taking global flag: in
|
|
107
|
+
* `git -C /tmp stash` the run eats `-C ` and then meets `/tmp`, which is not a
|
|
108
|
+
* subcommand, so the whole invocation read as NOT a git write — measured 2026-09-04,
|
|
109
|
+
* `isGitWrite('git -C /tmp stash')` and `isGitWrite('git -c user.name=x commit -m y')`
|
|
110
|
+
* both returned false. `-C`/`-c`/`--git-dir`/`--work-tree`/`--namespace`/
|
|
111
|
+
* `--exec-path`/`--config-env` are the value-taking globals; the space-separated
|
|
112
|
+
* form is matched first, the `--flag=value` form falls through to the generic
|
|
113
|
+
* alternative unchanged.
|
|
114
|
+
*
|
|
115
|
+
* `command git …` and `env VAR=x git …` prefixes are absorbed too — both are
|
|
116
|
+
* ordinary agent shapes that hid a write behind one token.
|
|
117
|
+
*
|
|
118
|
+
* DELIBERATE — `-C <dir>` is NOT treated as fixture context. `FIXTURE_CONTEXT_RE`
|
|
119
|
+
* keys on `cd /tmp` / `mktemp`, the shapes agents actually write around a test
|
|
120
|
+
* fixture; teaching it `-C` would hand back the exact one-token evasion this fix
|
|
121
|
+
* closes (`git -C /tmp/../<repo> commit`). A genuine `git -C "$TMPDIR" commit`
|
|
122
|
+
* therefore reports — a false alarm on an advisory detector, never a missed breach.
|
|
123
|
+
*
|
|
124
|
+
* CEILING (BV-004): the prefix list is enumerated, not general. `sudo git …`,
|
|
125
|
+
* `nice -n 5 git …` and any other wrapper still hide the write; and a git write
|
|
126
|
+
* inside `$(…)` or a quoted string was never detected (see `isGitWrite`).
|
|
127
|
+
* Revisit if a real PSA-007 breach is ever missed through a wrapper.
|
|
100
128
|
*/
|
|
101
|
-
const GIT_WRITE_RE =
|
|
129
|
+
const GIT_WRITE_RE =
|
|
130
|
+
/^\s*(?:command\s+|env\s+(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)+)*git\s+(?:(?:-C|-c|--git-dir|--work-tree|--namespace|--exec-path|--config-env)\s+\S+\s+|-[^\s]+\s+)*(add|commit|stash|push|mv|rm|reset|checkout)\b(.*)$/;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* `git stash` sub-subcommands that actually touch the stash stack. Bare
|
|
134
|
+
* `git stash` (no sub-subcommand) is `stash push`, so it counts too; anything
|
|
135
|
+
* else — `list`, `show`, an unknown word — is a read.
|
|
136
|
+
*/
|
|
137
|
+
const STASH_WRITE_SUBS = new Set([
|
|
138
|
+
'push',
|
|
139
|
+
'save',
|
|
140
|
+
'pop',
|
|
141
|
+
'apply',
|
|
142
|
+
'drop',
|
|
143
|
+
'clear',
|
|
144
|
+
'branch',
|
|
145
|
+
'create',
|
|
146
|
+
'store',
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Argument-aware PSA-007 classification. Decision table (#1215):
|
|
151
|
+
*
|
|
152
|
+
* | invocation | verdict | why |
|
|
153
|
+
* |-----------------------------------|---------|----------------------------------------|
|
|
154
|
+
* | `git stash` / `stash push|save` | write | pushes onto the shared stash stack |
|
|
155
|
+
* | `stash pop|apply|drop|clear` | write | mutates / consumes the stack |
|
|
156
|
+
* | `stash branch|create|store` | write | creates refs from the stack |
|
|
157
|
+
* | `git stash list` / `stash show` | READ | capability/inspection probe |
|
|
158
|
+
* | `--help` / `-h` / `--version` | READ | capability probe, any subcommand |
|
|
159
|
+
* | `--dry-run` (any subcommand) | READ | prints, never writes |
|
|
160
|
+
* | `-n` on add / rm / mv / push | READ | `-n` IS `--dry-run` there |
|
|
161
|
+
* | `-n` on commit | WRITE | `-n` is `--no-verify` — a REAL commit |
|
|
162
|
+
* | `git checkout -- <path>` | write | discards sibling work (destructive) |
|
|
163
|
+
* | `git checkout <branch>` | READ | no index/worktree content destroyed |
|
|
164
|
+
* | `git reset` (any form) | write | destructive, kept unconditional |
|
|
165
|
+
* | `git add|commit|push|mv|rm` else | write | the PSA-007 list, verbatim |
|
|
166
|
+
*
|
|
167
|
+
* The `-n` split is measured, not assumed — `git commit -h` prints
|
|
168
|
+
* `-n, --no-verify` while `git add -h` / `git rm -h` / `git push -h` all print
|
|
169
|
+
* `-n, --[no-]dry-run` (measured 2026-09-04, git in this repo's PATH).
|
|
170
|
+
* Treating `commit -n` as a read would blind the detector to
|
|
171
|
+
* `git commit --no-verify`, which `parallel-sessions.md` § PSA-007 names as an
|
|
172
|
+
* anti-pattern verbatim.
|
|
173
|
+
*
|
|
174
|
+
* Ceiling: flags are matched as whole tokens after quoted spans are stripped,
|
|
175
|
+
* so `git commit -m "fix -h"` stays a write. Bundled short flags (`-nv`) and
|
|
176
|
+
* flags hidden in `$(…)` are not decoded — they read as writes, a false alarm,
|
|
177
|
+
* never a missed breach. Revisit if agents start writing bundled git flags.
|
|
178
|
+
*
|
|
179
|
+
* @param {string} sub — the matched git subcommand
|
|
180
|
+
* @param {string} rawArgs — everything after it in the segment
|
|
181
|
+
* @returns {boolean}
|
|
182
|
+
*/
|
|
183
|
+
function gitSegmentWrites(sub, rawArgs) {
|
|
184
|
+
// Strip quoted spans so a commit message can never supply a read flag.
|
|
185
|
+
const args = String(rawArgs || '').replace(/"[^"]*"|'[^']*'/g, ' ');
|
|
186
|
+
const tokens = args.split(/\s+/).filter(Boolean);
|
|
187
|
+
|
|
188
|
+
for (const t of tokens) {
|
|
189
|
+
if (t === '--help' || t === '-h' || t === '--version' || t === '--dry-run') return false;
|
|
190
|
+
if (t === '-n' && sub !== 'commit') return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (sub === 'checkout') return tokens.includes('--');
|
|
194
|
+
if (sub === 'stash') {
|
|
195
|
+
const first = tokens.find((t) => !t.startsWith('-'));
|
|
196
|
+
return first === undefined || STASH_WRITE_SUBS.has(first);
|
|
197
|
+
}
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
102
200
|
|
|
103
201
|
/**
|
|
104
202
|
* STATUS literals that all report as pattern `status-partial`.
|
|
@@ -283,9 +381,22 @@ export function classifyErrorClass(text) {
|
|
|
283
381
|
* `/private/tmp`, `/var/folders`, `$TMPDIR`). A fixture repo created somewhere
|
|
284
382
|
* else is still reported — a false alarm, never a missed breach. Revisit if
|
|
285
383
|
* agents start seeding fixtures outside these paths.
|
|
384
|
+
*
|
|
385
|
+
* #1172: the temp path must also match BARE — the reported false alarm was
|
|
386
|
+
* `cd /tmp && git init && git commit -m x`, where the old pattern required a
|
|
387
|
+
* trailing `/` after `/tmp` and therefore saw nothing. The terminator class
|
|
388
|
+
* (`/`, quote, whitespace, `;`, `&`, `|`, end of string) is what keeps
|
|
389
|
+
* `cd /tmpfoo` — a real directory that is NOT the temp dir — reported.
|
|
390
|
+
*
|
|
391
|
+
* The destructive guard's own temp allowlist (`hooks/pre-bash-destructive-guard.mjs`
|
|
392
|
+
* `isSafeRmTarget`) is deliberately NOT reused: it exports nothing
|
|
393
|
+
* (`grep -n "^export" hooks/pre-bash-destructive-guard.mjs` → 0 matches,
|
|
394
|
+
* measured 2026-09-04) and resolves paths on the filesystem against a loaded
|
|
395
|
+
* policy, whereas this is a lexical test on a transcript string with no repo
|
|
396
|
+
* context. Same intent, different substrate.
|
|
286
397
|
*/
|
|
287
398
|
const FIXTURE_CONTEXT_RE =
|
|
288
|
-
/\bmktemp\b|\bcd\s+["']?(?:\/private)?\/(?:tmp|var\/folders)
|
|
399
|
+
/\bmktemp\b|\bcd\s+["']?(?:\/private)?\/(?:tmp|var\/folders)(?:["'\s;&|/]|$)|\bcd\s+["']?\$\{?(?:TMPDIR|TMP|SCRATCH)/;
|
|
289
400
|
|
|
290
401
|
/**
|
|
291
402
|
* True when the Bash command contains a git INDEX/HISTORY write in ANY
|
|
@@ -301,7 +412,10 @@ const FIXTURE_CONTEXT_RE =
|
|
|
301
412
|
export function isGitWrite(command) {
|
|
302
413
|
const text = String(command || '');
|
|
303
414
|
if (FIXTURE_CONTEXT_RE.test(text)) return false;
|
|
304
|
-
return text.split(/&&|\|\||;|\||\n/).some((seg) =>
|
|
415
|
+
return text.split(/&&|\|\||;|\||\n/).some((seg) => {
|
|
416
|
+
const m = GIT_WRITE_RE.exec(seg);
|
|
417
|
+
return m ? gitSegmentWrites(m[1], m[2]) : false;
|
|
418
|
+
});
|
|
305
419
|
}
|
|
306
420
|
|
|
307
421
|
/**
|
|
@@ -27,6 +27,10 @@ import { resolve } from 'node:path';
|
|
|
27
27
|
import { fileURLToPath } from 'node:url';
|
|
28
28
|
import { writeJsonAtomicSync } from './lib/io.mjs';
|
|
29
29
|
import { isPeerRecordId } from './lib/scope-gate.mjs';
|
|
30
|
+
import {
|
|
31
|
+
MANIFEST_SESSION_KEYS,
|
|
32
|
+
manifestSessionBinding,
|
|
33
|
+
} from './lib/session-identity/own-session.mjs';
|
|
30
34
|
|
|
31
35
|
const HELP = `Usage: node scripts/materialize-wave-scope.mjs --state-dir <dir> --wave <positive-int> [--json]
|
|
32
36
|
|
|
@@ -220,7 +224,8 @@ export function validateScopeRecords(value) {
|
|
|
220
224
|
/**
|
|
221
225
|
* Read the session ids the sibling manifest `<state-dir>/wave-scope.json`
|
|
222
226
|
* declares for this state directory (#1123 wrote both `session` — the raw
|
|
223
|
-
* `session_id` — and its human-readable twin `
|
|
227
|
+
* `session_id` — and its human-readable twin `semantic_session_id`; the
|
|
228
|
+
* pre-#1153 spellings `session` / `semantic_session` are still read).
|
|
224
229
|
*
|
|
225
230
|
* A caller may legitimately hold either spelling, so BOTH are returned and a
|
|
226
231
|
* match against either proves ownership. Any failure to read or parse the
|
|
@@ -239,10 +244,12 @@ export function manifestSessionIds(stateDir, readFile = readFileSync) {
|
|
|
239
244
|
return [];
|
|
240
245
|
}
|
|
241
246
|
if (!isRecord(manifest)) return [];
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
247
|
+
// #1153 P2 — the key spellings live in own-session.mjs, which also reads the
|
|
248
|
+
// pre-#1153 `session`/`semantic_session` names for one transition release.
|
|
249
|
+
const binding = manifestSessionBinding(manifest);
|
|
250
|
+
return MANIFEST_SESSION_KEYS.current
|
|
251
|
+
.map((key) => binding[key])
|
|
252
|
+
.filter((value) => typeof value === 'string' && value.length > 0);
|
|
246
253
|
}
|
|
247
254
|
|
|
248
255
|
/**
|
|
@@ -46,7 +46,7 @@ import { dirname, join, isAbsolute } from 'node:path';
|
|
|
46
46
|
import { fileURLToPath } from 'node:url';
|
|
47
47
|
|
|
48
48
|
import { isWaveAgentContext, WAVE_AGENT_ENV_VAR, WAVE_AGENT_ENV_VALUE } from './lib/wave-context.mjs';
|
|
49
|
-
import { readProcessLocalSessionIds } from './lib/session-identity/own-session.mjs';
|
|
49
|
+
import { readProcessLocalSessionIds, manifestSessionBinding } from './lib/session-identity/own-session.mjs';
|
|
50
50
|
import { readLockDetailed } from './lib/session-lock.mjs';
|
|
51
51
|
|
|
52
52
|
// ---------------------------------------------------------------------------
|
|
@@ -369,7 +369,7 @@ if (!dryRun && (currentWaveRaw === undefined || currentWaveRaw === null || curre
|
|
|
369
369
|
// trusted (#1177 FX1) — since #1123 both writers stamp the binding, so a
|
|
370
370
|
// binding-less manifest is a peer's or a stale artefact.
|
|
371
371
|
//
|
|
372
|
-
// The session binding compares
|
|
372
|
+
// The session binding compares the manifest's `semantic_session_id` — the SEMANTIC label
|
|
373
373
|
// — against the semantic id looked up in `session.lock`, and that lookup is only
|
|
374
374
|
// honoured when this PROCESS's own raw session id equals the lock's raw
|
|
375
375
|
// `session_id` (#1188). STATE.md's `session` field is NOT a witness here: it is
|
|
@@ -382,14 +382,18 @@ function resolveRunningWaveId() {
|
|
|
382
382
|
const manifestPath = join(stateDir, 'wave-scope.json');
|
|
383
383
|
if (existsSync(manifestPath)) {
|
|
384
384
|
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
385
|
-
|
|
385
|
+
// #1153 P2 — key spellings resolved in ONE place (own-session.mjs),
|
|
386
|
+
// which also reads the pre-#1153 `session` / `semantic_session` names
|
|
387
|
+
// for one transition release. Absent slots come back as `undefined`.
|
|
388
|
+
const binding = manifestSessionBinding(manifest);
|
|
389
|
+
const boundSession = binding.semantic_session_id ?? binding.session_id;
|
|
386
390
|
const unbound =
|
|
387
391
|
boundSession === undefined || boundSession === null || boundSession === '';
|
|
388
392
|
// BOTH sides must be non-empty strings. `undefined === undefined` is
|
|
389
393
|
// true, so a STATE.md WITHOUT a `session` key previously "matched" any
|
|
390
394
|
// manifest whose `semantic_session` was also absent-but-present-in-the
|
|
391
395
|
// -comparison — adopting a foreign coordinator's wave number as its own.
|
|
392
|
-
const mineSide =
|
|
396
|
+
const mineSide = binding.semantic_session_id;
|
|
393
397
|
// #1188 — STATE.md is a working-copy artefact of the LOCK OWNER, not a
|
|
394
398
|
// process-local witness: when a peer holds the lock the peer wrote BOTH
|
|
395
399
|
// STATE.md and the manifest, so the two "independent" sides agree about
|
|
@@ -403,6 +407,16 @@ function resolveRunningWaveId() {
|
|
|
403
407
|
// Cursor) resolves no process-local id -> binding unprovable ->
|
|
404
408
|
// current-wave + 1 fallback with a stderr line. REVISIT if that fallback
|
|
405
409
|
// rate is measured non-trivial in events.jsonl.
|
|
410
|
+
//
|
|
411
|
+
// #1207 — kept hand-rolled rather than calling `attributionForRecord()`
|
|
412
|
+
// (scripts/lib/events.mjs): that function's contract is `{}`-on-any-
|
|
413
|
+
// mismatch with no detail, but the stderr diagnostics below (raw id,
|
|
414
|
+
// lock status, process-local id list) are load-bearing for debugging a
|
|
415
|
+
// wrongly-bucketed proposal, and attributionForRecord() has no lock-
|
|
416
|
+
// status channel to source them from (it swallows `readLock()` errors
|
|
417
|
+
// silently by design). `readLockDetailed()` is used here specifically
|
|
418
|
+
// for that `.status` field `readLock()` (attributionForRecord's own
|
|
419
|
+
// primitive) does not return.
|
|
406
420
|
const lock = readLockDetailed({ repoRoot: process.cwd() });
|
|
407
421
|
const processLocal = readProcessLocalSessionIds();
|
|
408
422
|
const lockRaw =
|
|
@@ -431,7 +445,7 @@ function resolveRunningWaveId() {
|
|
|
431
445
|
// bucket. Unbound → say so once on stderr and fall back.
|
|
432
446
|
if (unbound) {
|
|
433
447
|
process.stderr.write(
|
|
434
|
-
'memory-propose: wave-scope.json unbound (no
|
|
448
|
+
'memory-propose: wave-scope.json unbound (no semantic_session_id) — ignored\n',
|
|
435
449
|
);
|
|
436
450
|
}
|
|
437
451
|
if (mine) {
|
|
@@ -143,7 +143,10 @@ if (applyFlag && dryRunFlag) {
|
|
|
143
143
|
process.exit(1);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
// Default is dry-run
|
|
146
|
+
// Default is dry-run: a migration script writes only when `--apply` is passed, so an
|
|
147
|
+
// invocation with no flags can never mutate a store. Repo-wide convention for every
|
|
148
|
+
// `scripts/migrate-*` / `scripts/backfill-*` CLI; live example:
|
|
149
|
+
// `scripts/migrate-sessions-jsonl.mjs` (`args.apply = false` default, `--apply` opts in).
|
|
147
150
|
const dryRun = !applyFlag;
|
|
148
151
|
|
|
149
152
|
// `--repos` is kept as the raw comma-separated string here (split later, at
|