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
|
@@ -42,6 +42,21 @@
|
|
|
42
42
|
* under B's identity. `last_batch` stays UNGATED — it is a batch-resolution
|
|
43
43
|
* breadcrumb, not a claim about whose wave lifecycle this is.
|
|
44
44
|
*
|
|
45
|
+
* DIFF-SIZE MEASUREMENT (#980). The mechanical fallback below persists
|
|
46
|
+
* `wave_start_sha` (`git rev-parse HEAD`, best-effort) at every wave-OPEN
|
|
47
|
+
* transition and attaches `files_changed` + `files_changed_source:
|
|
48
|
+
* 'worktree-vs-wave-start-sha'` to the `orchestrator.wave.completed` it emits
|
|
49
|
+
* for the wave that sha opened. `files_changed` counts the DEDUPED union of
|
|
50
|
+
* `git diff --name-only <wave_start_sha>` (working tree vs the wave's start
|
|
51
|
+
* commit — the coordinator normally commits only at close, so uncommitted
|
|
52
|
+
* edits must count) and `git ls-files --others --exclude-standard` (untracked).
|
|
53
|
+
* Every git call is best-effort with a 1.5 s timeout; any failure, or a missing
|
|
54
|
+
* `wave_start_sha`, OMITS the key — the convergence-monitor reader treats an
|
|
55
|
+
* absent key as null and the `shrinking_diff` signal simply does not fire.
|
|
56
|
+
* `wave_start_sha` is written under the SAME ownership gate as the wave keys.
|
|
57
|
+
* OUT OF SCOPE: `hooks/on-session-end.mjs`'s final-wave `.completed` carries no
|
|
58
|
+
* `files_changed` — no wave-open transition runs there, so it has no start sha.
|
|
59
|
+
*
|
|
45
60
|
* MONOTONICITY RULE (#1193 W4c Q3-MED-3). Three writers touch
|
|
46
61
|
* `last_wave_completed` (this hook's explicit branch, this hook's fallback,
|
|
47
62
|
* `hooks/on-session-end.mjs`). Every one of them writes through `maxWave()`,
|
|
@@ -51,6 +66,7 @@
|
|
|
51
66
|
* hooks.json wiring is managed separately (W3-C4 scope).
|
|
52
67
|
*/
|
|
53
68
|
|
|
69
|
+
import { execFileSync } from 'node:child_process';
|
|
54
70
|
import { readFile } from 'node:fs/promises';
|
|
55
71
|
import path from 'node:path';
|
|
56
72
|
|
|
@@ -58,7 +74,7 @@ import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
|
58
74
|
// Exit 0 immediately when disabled via SO_HOOK_PROFILE / SO_DISABLED_HOOKS.
|
|
59
75
|
if (!shouldRunHook('post-tool-batch-wave-signal')) process.exit(0);
|
|
60
76
|
|
|
61
|
-
import {
|
|
77
|
+
import { getProjectDir } from '../scripts/lib/platform.mjs';
|
|
62
78
|
import { emitEvent } from '../scripts/lib/events.mjs';
|
|
63
79
|
import { findScopeFile } from '../scripts/lib/scope-gate.mjs';
|
|
64
80
|
import { atomicMutateJson } from './_lib/atomic-json.mjs';
|
|
@@ -208,6 +224,87 @@ async function resolveWaveNumber(projectDir) {
|
|
|
208
224
|
}
|
|
209
225
|
}
|
|
210
226
|
|
|
227
|
+
/**
|
|
228
|
+
* The only environment variables forwarded to the `git` children below.
|
|
229
|
+
* Everything else — `GIT_DIR`/`GIT_WORK_TREE`/`GIT_COMMON_DIR` above all — is
|
|
230
|
+
* dropped, so an ambient redirect cannot make these probes measure a FOREIGN
|
|
231
|
+
* repository while `cwd` points at this one (`GIT_DIR` outranks `cwd`; that is
|
|
232
|
+
* exactly the 2026-08-19 redirect incident class).
|
|
233
|
+
*
|
|
234
|
+
* Same 6-entry allowlist as `scripts/lib/git-config-drift.mjs:170` and
|
|
235
|
+
* `check-banner-parity.mjs:63`, COPIED rather than imported on purpose: this is
|
|
236
|
+
* a live hook, and importing a scripts/lib module for six strings would add an
|
|
237
|
+
* edge to the hook import graph (`hooks/_lib/hook-import-set.json`) for no
|
|
238
|
+
* behavioural gain — the constant is cheaper to copy than a shared module is to
|
|
239
|
+
* maintain (BV-001, the same call git-config-drift.mjs documents).
|
|
240
|
+
*/
|
|
241
|
+
const GIT_ENV_ALLOWLIST = Object.freeze(['PATH', 'HOME', 'LANG', 'LC_ALL', 'TMPDIR', 'TZ']);
|
|
242
|
+
|
|
243
|
+
/** @returns {Record<string,string>} the allowlisted subset of `process.env`. */
|
|
244
|
+
function filteredGitEnv() {
|
|
245
|
+
/** @type {Record<string,string>} */
|
|
246
|
+
const out = {};
|
|
247
|
+
for (const key of GIT_ENV_ALLOWLIST) {
|
|
248
|
+
const value = process.env[key];
|
|
249
|
+
if (typeof value === 'string') out[key] = value;
|
|
250
|
+
}
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Shared spawn options for the best-effort git probes below (#980). */
|
|
255
|
+
const GIT_OPTS = (projectDir) => ({
|
|
256
|
+
cwd: projectDir,
|
|
257
|
+
encoding: 'utf8',
|
|
258
|
+
timeout: 1_500,
|
|
259
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
260
|
+
env: filteredGitEnv(),
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* `git rev-parse HEAD` in `projectDir`, best-effort (#980).
|
|
265
|
+
* Returns null on ANY failure — not a git repo, detached/empty HEAD, timeout,
|
|
266
|
+
* git absent. A null is persisted as null so a STALE sha from the previous wave
|
|
267
|
+
* can never be mistaken for this wave's start point.
|
|
268
|
+
*
|
|
269
|
+
* @param {string} projectDir
|
|
270
|
+
* @returns {string|null}
|
|
271
|
+
*/
|
|
272
|
+
function readHeadSha(projectDir) {
|
|
273
|
+
try {
|
|
274
|
+
const out = execFileSync('git', ['rev-parse', 'HEAD'], GIT_OPTS(projectDir)).trim();
|
|
275
|
+
return /^[0-9a-f]{40}$/i.test(out) ? out : null;
|
|
276
|
+
} catch {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Count the files this wave touched: the DEDUPED union of the working tree's
|
|
283
|
+
* diff against `sha` and the untracked (non-ignored) files (#980).
|
|
284
|
+
*
|
|
285
|
+
* Working-tree-vs-sha rather than `sha..HEAD` on purpose: the coordinator
|
|
286
|
+
* commits at session close, not per wave, so a commit-only diff reads 0 for
|
|
287
|
+
* every wave of a normal session.
|
|
288
|
+
*
|
|
289
|
+
* @param {string} projectDir
|
|
290
|
+
* @param {unknown} sha Persisted `wave_start_sha` (may be null/absent).
|
|
291
|
+
* @returns {number|null} null when unmeasurable — caller OMITS the key.
|
|
292
|
+
*/
|
|
293
|
+
function countFilesChangedSince(projectDir, sha) {
|
|
294
|
+
if (typeof sha !== 'string' || !/^[0-9a-f]{40}$/i.test(sha)) return null;
|
|
295
|
+
try {
|
|
296
|
+
const opts = GIT_OPTS(projectDir);
|
|
297
|
+
const tracked = execFileSync('git', ['diff', '--name-only', sha], opts);
|
|
298
|
+
const untracked = execFileSync('git', ['ls-files', '--others', '--exclude-standard'], opts);
|
|
299
|
+
const paths = new Set(
|
|
300
|
+
`${tracked}\n${untracked}`.split('\n').map((l) => l.trim()).filter((l) => l.length > 0),
|
|
301
|
+
);
|
|
302
|
+
return paths.size;
|
|
303
|
+
} catch {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
211
308
|
// ---------------------------------------------------------------------------
|
|
212
309
|
// Main
|
|
213
310
|
// ---------------------------------------------------------------------------
|
|
@@ -248,7 +345,7 @@ async function main() {
|
|
|
248
345
|
...(waveSignal !== null ? { wave_signal: waveSignal } : {}),
|
|
249
346
|
};
|
|
250
347
|
|
|
251
|
-
const sessionFile = path.join(
|
|
348
|
+
const sessionFile = path.join(getProjectDir(), '.orchestrator', 'current-session.json');
|
|
252
349
|
|
|
253
350
|
const lastBatchResult = await atomicMutateJson(sessionFile, {}, (current) => ({
|
|
254
351
|
...current,
|
|
@@ -277,7 +374,7 @@ async function main() {
|
|
|
277
374
|
const sessionId = await resolveSessionIdForHeartbeat(input, sessionFile);
|
|
278
375
|
if (typeof sessionId === 'string' && sessionId.length > 0) {
|
|
279
376
|
const { updateHeartbeat } = await import('../scripts/lib/session-lock.mjs');
|
|
280
|
-
updateHeartbeat({ repoRoot:
|
|
377
|
+
updateHeartbeat({ repoRoot: getProjectDir(), sessionId });
|
|
281
378
|
}
|
|
282
379
|
} catch { /* best effort — hook must remain non-blocking */ }
|
|
283
380
|
|
|
@@ -357,16 +454,19 @@ async function main() {
|
|
|
357
454
|
// when an N+1 transition already closed wave N, SessionEnd sees
|
|
358
455
|
// last_wave_completed === last_wave and stays silent.
|
|
359
456
|
try {
|
|
360
|
-
const wave = await resolveWaveNumber(
|
|
457
|
+
const wave = await resolveWaveNumber(getProjectDir());
|
|
361
458
|
if (wave > 0) {
|
|
362
459
|
// Read last_wave from the just-written session file (after the
|
|
363
460
|
// last_batch RMW above, so we observe the latest persisted value).
|
|
364
461
|
let lastWave = 0;
|
|
365
462
|
let lastWaveCompleted = 0;
|
|
463
|
+
let waveStartSha = null;
|
|
366
464
|
try {
|
|
367
465
|
const raw = await readFile(sessionFile, 'utf8');
|
|
368
466
|
const parsed = JSON.parse(raw);
|
|
369
467
|
if (typeof parsed.last_wave === 'number') lastWave = parsed.last_wave;
|
|
468
|
+
// #980 — the sha persisted when THIS `last_wave` was opened.
|
|
469
|
+
if (typeof parsed.wave_start_sha === 'string') waveStartSha = parsed.wave_start_sha;
|
|
370
470
|
// #1193 review F2 — read in the SAME read as `last_wave`: a `/clear`
|
|
371
471
|
// mid-wave already emitted `completed(lastWave)` via SessionEnd, and
|
|
372
472
|
// `on-session-start.mjs` preserves that marker across the restart, so
|
|
@@ -383,8 +483,17 @@ async function main() {
|
|
|
383
483
|
// prior wave is already closed too, and a non-integer marker counts
|
|
384
484
|
// as absent rather than as "different".
|
|
385
485
|
if (lastWave > lastWaveCompleted) {
|
|
486
|
+
// #980 — measure the closing wave's diff size BEFORE the new wave's
|
|
487
|
+
// start sha is persisted below. Unmeasurable → both keys omitted.
|
|
488
|
+
const filesChanged = countFilesChangedSince(getProjectDir(), waveStartSha);
|
|
386
489
|
await emitEvent('orchestrator.wave.completed', {
|
|
387
490
|
wave_number: lastWave,
|
|
491
|
+
...(filesChanged !== null
|
|
492
|
+
? {
|
|
493
|
+
files_changed: filesChanged,
|
|
494
|
+
files_changed_source: 'worktree-vs-wave-start-sha',
|
|
495
|
+
}
|
|
496
|
+
: {}),
|
|
388
497
|
...(batchId !== null ? { batch_id: batchId } : {}),
|
|
389
498
|
...(batchSize !== null ? { batch_size: batchSize } : {}),
|
|
390
499
|
});
|
|
@@ -408,8 +517,13 @@ async function main() {
|
|
|
408
517
|
// that session's own SessionStart takes over the file. Revisit if the
|
|
409
518
|
// ledger shows repeated started{N} for one wave in a shared checkout.
|
|
410
519
|
if (await ownsSessionFile(input, sessionFile)) {
|
|
520
|
+
// #980 — the OPEN half: stamp the sha this new wave starts from.
|
|
521
|
+
// Written as null (not omitted) when git is unreadable, so the
|
|
522
|
+
// PREVIOUS wave's sha can never linger and inflate the next count.
|
|
523
|
+
const startSha = readHeadSha(getProjectDir());
|
|
411
524
|
const markResult = await atomicMutateJson(sessionFile, {}, (current) => ({
|
|
412
525
|
...current,
|
|
526
|
+
wave_start_sha: startSha,
|
|
413
527
|
last_wave: maxWave(current?.last_wave, wave),
|
|
414
528
|
...(Math.max(lastWave, lastWaveCompleted) > 0
|
|
415
529
|
? {
|
|
@@ -30,7 +30,7 @@ import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
|
30
30
|
// Exit 0 immediately when disabled via SO_HOOK_PROFILE / SO_DISABLED_HOOKS.
|
|
31
31
|
if (!shouldRunHook('post-tool-failure-corrective-context')) process.exit(0);
|
|
32
32
|
|
|
33
|
-
import {
|
|
33
|
+
import { getProjectDir } from '../scripts/lib/platform.mjs';
|
|
34
34
|
import { atomicMutateJson } from './_lib/atomic-json.mjs';
|
|
35
35
|
|
|
36
36
|
// ---------------------------------------------------------------------------
|
|
@@ -179,7 +179,7 @@ async function main() {
|
|
|
179
179
|
exit_code: exitCode,
|
|
180
180
|
};
|
|
181
181
|
|
|
182
|
-
const sessionFile = path.join(
|
|
182
|
+
const sessionFile = path.join(getProjectDir(), '.orchestrator', 'current-session.json');
|
|
183
183
|
|
|
184
184
|
const result = await atomicMutateJson(sessionFile, {}, (current) => {
|
|
185
185
|
const existing = Array.isArray(current.corrective_context)
|
|
@@ -29,7 +29,7 @@ if (!shouldRunHook('frontend-slop-hook')) process.exit(0);
|
|
|
29
29
|
|
|
30
30
|
import path from 'node:path';
|
|
31
31
|
|
|
32
|
-
import {
|
|
32
|
+
import { getProjectDir } from '../scripts/lib/platform.mjs';
|
|
33
33
|
import { emitEvent } from '../scripts/lib/events.mjs';
|
|
34
34
|
import { detectFiles, SCANNABLE_EXTS } from '../scripts/lib/frontend-detect/detect.mjs';
|
|
35
35
|
import { loadFrontendSlopHookConfig } from '../scripts/lib/config/frontend-slop-hook.mjs';
|
|
@@ -98,7 +98,7 @@ function resolveSessionId(input) {
|
|
|
98
98
|
*/
|
|
99
99
|
function toRelPath(filePath) {
|
|
100
100
|
try {
|
|
101
|
-
const rel = path.relative(
|
|
101
|
+
const rel = path.relative(getProjectDir(), filePath);
|
|
102
102
|
if (rel && !rel.startsWith('..') && !path.isAbsolute(rel)) return rel;
|
|
103
103
|
} catch { /* fall through */ }
|
|
104
104
|
return path.basename(filePath);
|
|
@@ -147,7 +147,7 @@ async function main() {
|
|
|
147
147
|
if (!SCANNABLE_EXTS.has(ext)) return;
|
|
148
148
|
|
|
149
149
|
// Session Config gate — OPT-IN (default off).
|
|
150
|
-
const config = await loadFrontendSlopHookConfig({ repoRoot:
|
|
150
|
+
const config = await loadFrontendSlopHookConfig({ repoRoot: getProjectDir() });
|
|
151
151
|
if (config.enabled !== true) return;
|
|
152
152
|
|
|
153
153
|
// Run the deterministic detector (fail-soft: unreadable files → []).
|
|
@@ -95,6 +95,8 @@ let blocker = null;
|
|
|
95
95
|
let readConfigFile;
|
|
96
96
|
let loadEffectivePolicy;
|
|
97
97
|
let isSessionConfigHeading;
|
|
98
|
+
let preprocessBlockLinesNoDash;
|
|
99
|
+
let findUnterminatedComment;
|
|
98
100
|
let emitEvent;
|
|
99
101
|
|
|
100
102
|
/**
|
|
@@ -164,6 +166,7 @@ async function bootstrap() {
|
|
|
164
166
|
config: { specifier: lib('config.mjs') },
|
|
165
167
|
blockedCommandsPolicy: { specifier: lib('blocked-commands-policy.mjs') },
|
|
166
168
|
sectionExtractor: { specifier: lib('config', 'section-extractor.mjs') },
|
|
169
|
+
blockPreprocess: { specifier: lib('config', 'block-preprocess.mjs') },
|
|
167
170
|
events: { specifier: lib('events.mjs') },
|
|
168
171
|
blocker: {
|
|
169
172
|
specifier: lib('command-blocker.mjs'),
|
|
@@ -192,6 +195,7 @@ async function bootstrap() {
|
|
|
192
195
|
({ readConfigFile } = modules.config);
|
|
193
196
|
({ loadEffectivePolicy } = modules.blockedCommandsPolicy);
|
|
194
197
|
({ isSessionConfigHeading } = modules.sectionExtractor);
|
|
198
|
+
({ findUnterminatedComment, preprocessBlockLinesNoDash } = modules.blockPreprocess);
|
|
195
199
|
({ emitEvent } = modules.events);
|
|
196
200
|
blocker = modules.blocker;
|
|
197
201
|
degradedLabels = degraded;
|
|
@@ -760,19 +764,41 @@ async function main() {
|
|
|
760
764
|
const mdContent = await readConfigFile(projectDir);
|
|
761
765
|
// Look for "allow-destructive-ops: true" in the Session Config block.
|
|
762
766
|
// Simple line-based scan: find lines under ## Session Config, stop at next ##.
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
767
|
+
// W4/F1b — HTML-comment stripping is MANDATORY here (same class as #1162a).
|
|
768
|
+
// With a raw `split(/\r?\n/)` a COMMENTED-OUT
|
|
769
|
+
// `<!--\nallow-destructive-ops: true\n-->` under `## Session Config` DISABLED
|
|
770
|
+
// the whole 14-rule guard for the session: commenting a key out is the most
|
|
771
|
+
// ordinary way to turn it off and must never turn it ON. `NoDash` (not the
|
|
772
|
+
// full `preprocessBlockLines`) on purpose — bold-subkey normalisation is not
|
|
773
|
+
// wanted; the regex below already tolerates `- **key:**` and stays as-is.
|
|
774
|
+
//
|
|
775
|
+
// UNTERMINATED `<!--` — the fail-closed direction INVERTS for a bypass scan.
|
|
776
|
+
// `stripHtmlCommentBlocks` returns the lines UNFILTERED when a comment is
|
|
777
|
+
// never closed (right for a block PARSER, nothing may silently vanish), which
|
|
778
|
+
// here would hand the commented-out bypass line back and re-arm the guard's
|
|
779
|
+
// own off switch. For this scan "I cannot tell where the comments end" must
|
|
780
|
+
// mean NOT ARMED: the bypass is opt-in, so refusing to grant it is the safe
|
|
781
|
+
// reading of an ambiguous document. Said out loud on stderr, because a
|
|
782
|
+
// silently-ignored bypass would look identical to a typo the operator meant.
|
|
783
|
+
if (findUnterminatedComment(mdContent.split(/\r?\n/)) !== null) {
|
|
784
|
+
process.stderr.write(
|
|
785
|
+
'ℹ destructive-guard: unterminated <!-- in CLAUDE.md — bypass ignored\n'
|
|
786
|
+
);
|
|
787
|
+
} else {
|
|
788
|
+
const lines = preprocessBlockLinesNoDash(mdContent);
|
|
789
|
+
let inConfig = false;
|
|
790
|
+
for (const line of lines) {
|
|
791
|
+
// SSOT predicate (#968) — never re-derive this comparison. A local copy
|
|
792
|
+
// that drifts LOOSER than the extractor silently disagrees with the
|
|
793
|
+
// runtime about where the config block starts.
|
|
794
|
+
if (isSessionConfigHeading(line)) { inConfig = true; continue; }
|
|
795
|
+
if (inConfig && /^## /.test(line)) break;
|
|
796
|
+
if (inConfig) {
|
|
797
|
+
const m = line.match(/^\s*(?:-\s+\*\*)?allow-destructive-ops(?::\*\*)?\s*:\s*(\S+)/);
|
|
798
|
+
if (m && m[1].toLowerCase() === 'true') {
|
|
799
|
+
process.stderr.write('ℹ destructive-guard bypassed\n');
|
|
800
|
+
return emitAllow();
|
|
801
|
+
}
|
|
776
802
|
}
|
|
777
803
|
}
|
|
778
804
|
}
|
|
@@ -33,7 +33,7 @@ import { spawn } from 'node:child_process';
|
|
|
33
33
|
|
|
34
34
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
35
35
|
import { appendSkillInvocation } from '../scripts/lib/skill-invocations-schema.mjs';
|
|
36
|
-
import {
|
|
36
|
+
import { getProjectDir } from '../scripts/lib/platform.mjs';
|
|
37
37
|
import { shouldDailyFlush } from '../scripts/lib/telemetry/sync.mjs';
|
|
38
38
|
import { resolveConsent, readTelemetryState } from '../scripts/lib/telemetry/consent.mjs';
|
|
39
39
|
import { loadOwnerConfig } from '../scripts/lib/owner-yaml.mjs';
|
|
@@ -42,9 +42,21 @@ import { loadOwnerConfig } from '../scripts/lib/owner-yaml.mjs';
|
|
|
42
42
|
// Constants
|
|
43
43
|
// ---------------------------------------------------------------------------
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Metrics directory / ledger path. FUNCTIONS, not module-level consts:
|
|
47
|
+
* `getProjectDir()` walks the filesystem on first call, and this hook must not
|
|
48
|
+
* pay that at import time (#1153 P5).
|
|
49
|
+
*
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
function metricsDirPath() {
|
|
53
|
+
return path.join(getProjectDir(), '.orchestrator', 'metrics');
|
|
54
|
+
}
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
/** @returns {string} */
|
|
57
|
+
function jsonlPath() {
|
|
58
|
+
return path.join(metricsDirPath(), 'skill-invocations.jsonl');
|
|
59
|
+
}
|
|
48
60
|
|
|
49
61
|
/** Absolute path to the telemetry CLI (carries the hidden `_flush` subcommand). */
|
|
50
62
|
const TELEMETRY_CLI_PATH = path.resolve(
|
|
@@ -123,7 +135,7 @@ export function maybeSpawnDailyFlush({
|
|
|
123
135
|
now = Date.now(),
|
|
124
136
|
statePath,
|
|
125
137
|
queuePath,
|
|
126
|
-
metricsDir =
|
|
138
|
+
metricsDir = metricsDirPath(),
|
|
127
139
|
} = {}) {
|
|
128
140
|
try {
|
|
129
141
|
// 1. Cheapest gate: env kill-switches, no file I/O.
|
|
@@ -187,7 +199,7 @@ async function main() {
|
|
|
187
199
|
schema_version: 1,
|
|
188
200
|
};
|
|
189
201
|
|
|
190
|
-
await appendSkillInvocation(
|
|
202
|
+
await appendSkillInvocation(jsonlPath(), record);
|
|
191
203
|
|
|
192
204
|
// Daily-fallback telemetry flush — non-blocking, best-effort, never throws.
|
|
193
205
|
maybeSpawnDailyFlush();
|
|
@@ -84,14 +84,23 @@ if (!shouldRunHook('subagent-telemetry')) process.exit(0);
|
|
|
84
84
|
import fs from 'node:fs';
|
|
85
85
|
import path from 'node:path';
|
|
86
86
|
import { appendSubagent } from '../scripts/lib/subagents-schema.mjs';
|
|
87
|
-
import {
|
|
87
|
+
import { getProjectDir } from '../scripts/lib/platform.mjs';
|
|
88
88
|
import { resolveSubagentSidecar } from './_lib/subagent-paths.mjs';
|
|
89
89
|
|
|
90
90
|
// ---------------------------------------------------------------------------
|
|
91
91
|
// Constants
|
|
92
92
|
// ---------------------------------------------------------------------------
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Absolute path to the subagent ledger. A FUNCTION, not a module-level const:
|
|
96
|
+
* `getProjectDir()` walks the filesystem on first call, and this hook must not
|
|
97
|
+
* pay that at import time (#1153 P5).
|
|
98
|
+
*
|
|
99
|
+
* @returns {string}
|
|
100
|
+
*/
|
|
101
|
+
function jsonlPath() {
|
|
102
|
+
return path.join(getProjectDir(), '.orchestrator', 'metrics', 'subagents.jsonl');
|
|
103
|
+
}
|
|
95
104
|
|
|
96
105
|
/**
|
|
97
106
|
* Defense-in-depth byte ceiling for transcript reads (#624). The transcript path
|
|
@@ -669,7 +678,7 @@ async function main() {
|
|
|
669
678
|
// duration measurement and the explicit orphan discriminator below.
|
|
670
679
|
// 'unknown' is the agent-id fallback for a payload with no usable id —
|
|
671
680
|
// joining on it would collide across unrelated agents, so never scan for it.
|
|
672
|
-
const startedAtMs = agentId === 'unknown' ? null : findStartTimestampMs(
|
|
681
|
+
const startedAtMs = agentId === 'unknown' ? null : findStartTimestampMs(jsonlPath(), agentId);
|
|
673
682
|
|
|
674
683
|
// duration_ms (#917): prefer the harness value, else join backwards to this
|
|
675
684
|
// agent's own start record, else null. Never 0 — see resolveDurationMs().
|
|
@@ -717,7 +726,7 @@ async function main() {
|
|
|
717
726
|
record['gen_ai.system'] = 'anthropic';
|
|
718
727
|
}
|
|
719
728
|
|
|
720
|
-
await appendSubagent(
|
|
729
|
+
await appendSubagent(jsonlPath(), record);
|
|
721
730
|
}
|
|
722
731
|
|
|
723
732
|
// Exit 0 always — informational hook must never block Claude.
|
package/monitors/monitors.json
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
{
|
|
3
3
|
"name": "ecosystem-health",
|
|
4
4
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/lib/ecosystem-health.mjs --watch",
|
|
5
|
-
"description": "Ecosystem repo health watcher (CI status, branch drift, stale baselines across vault-registered projects). Emits one NDJSON line per detected state change.",
|
|
6
|
-
"when": "on-skill-invoke:
|
|
5
|
+
"description": "Ecosystem repo health watcher (CI status, branch drift, stale baselines across vault-registered projects). Emits one NDJSON line per detected state change. Hung on session-start because the previous trigger (on-skill-invoke:ecosystem-health) was self-referential: it could only start when the ecosystem-health skill ran, and that skill has 0 recorded invocations fleet-wide, so the watcher never started once.",
|
|
6
|
+
"when": "on-skill-invoke:session-start"
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
"name": "convergence-monitor",
|
|
10
10
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/lib/convergence-monitor.mjs --tail",
|
|
11
|
-
"description": "Wave convergence telemetry monitor — tails .orchestrator/metrics/events.jsonl
|
|
11
|
+
"description": "Wave convergence telemetry monitor — tails .orchestrator/metrics/events.jsonl (that file only; it never opens sessions.jsonl) and surfaces convergence signals (shrinking diff, pass-rate plateau, velocity).",
|
|
12
12
|
"when": "on-skill-invoke:wave-executor"
|
|
13
13
|
},
|
|
14
14
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "session-orchestrator",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "Loop engineering for AI coding agents — turn ad-hoc sessions into a repeatable research → plan → wave-execute → close loop with verification gates. Runs on Claude Code, Codex CLI, Cursor, and Pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://session-orchestrator.com",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
".mcp.json",
|
|
26
|
+
"AGENTS.md",
|
|
26
27
|
"CHANGELOG.md",
|
|
27
28
|
"pi/",
|
|
28
29
|
"skills/",
|
|
@@ -37,11 +38,15 @@
|
|
|
37
38
|
"assets/",
|
|
38
39
|
".claude-plugin/",
|
|
39
40
|
".codex-plugin/",
|
|
41
|
+
".cursor-plugin/",
|
|
42
|
+
".agents/",
|
|
40
43
|
".cursor/",
|
|
44
|
+
".orchestrator/policy/",
|
|
41
45
|
"docs/*.md",
|
|
42
46
|
"docs/telemetry/telemetry-claims.md",
|
|
43
47
|
"NOTICE",
|
|
44
48
|
"SECURITY.md",
|
|
49
|
+
"!skills/vault-sync/package-lock.json",
|
|
45
50
|
"!scripts/tests/**",
|
|
46
51
|
"!skills/vault-sync/tests/**",
|
|
47
52
|
"!**/node_modules/**",
|
|
@@ -66,9 +71,12 @@
|
|
|
66
71
|
"ajv": "^8.17.1",
|
|
67
72
|
"js-yaml": "^4.3.1",
|
|
68
73
|
"mdast-util-to-string": "^4.0.0",
|
|
74
|
+
"picomatch": "^4.0.4",
|
|
69
75
|
"remark": "^15.0.0",
|
|
70
76
|
"remark-parse": "^11.0.0",
|
|
71
77
|
"unified": "^11.0.0",
|
|
78
|
+
"yaml": "^2.9.0",
|
|
79
|
+
"zod": "^3.25.76",
|
|
72
80
|
"zx": "^8.1.0"
|
|
73
81
|
},
|
|
74
82
|
"devDependencies": {
|
package/pi/prompts/session.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Start a development session (housekeeping, feature, deep)
|
|
3
|
-
argument-hint: "[housekeeping|feature|deep]"
|
|
2
|
+
description: Start a development session (housekeeping, feature, deep; ultradeep = deep + profile)
|
|
3
|
+
argument-hint: "[housekeeping|feature|deep|ultradeep]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /session
|
|
@@ -43,7 +43,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
43
43
|
|
|
44
44
|
import { backfillAbandonedSession, isUuid } from './lib/session-close-backfill.mjs';
|
|
45
45
|
import { emitEvent } from './lib/events.mjs';
|
|
46
|
-
import {
|
|
46
|
+
import { getProjectDir } from './lib/platform.mjs';
|
|
47
47
|
|
|
48
48
|
const LOCK_ACQUIRED = 'orchestrator.session.lock.acquired';
|
|
49
49
|
const SESSION_STARTED = 'orchestrator.session.started';
|
|
@@ -382,13 +382,59 @@ export async function backfillOnSessionStart({ repoRoot, limit = SESSION_START_L
|
|
|
382
382
|
if (typeof repoRoot !== 'string' || repoRoot.length === 0) return null;
|
|
383
383
|
// newestFirst is mandatory here — see runMigration's param docs for the
|
|
384
384
|
// measured failure (budget burned on ancient already-recorded candidates).
|
|
385
|
-
|
|
385
|
+
const summary = await runMigration({ repoRoot, apply: true, limit, newestFirst: true });
|
|
386
|
+
// The telemetry flush rides the SAME start-time trigger, for the same reason
|
|
387
|
+
// the backfill does — see flushTelemetryOnSessionStart.
|
|
388
|
+
await flushTelemetryOnSessionStart(repoRoot);
|
|
389
|
+
return summary;
|
|
386
390
|
} catch {
|
|
387
391
|
// Swallowed by contract — see the docblock. The hook is informational-only.
|
|
388
392
|
return null;
|
|
389
393
|
}
|
|
390
394
|
}
|
|
391
395
|
|
|
396
|
+
/** POST budget for the start-time telemetry drain (ms). Deliberately tighter than
|
|
397
|
+
* the close-time budget: a session START must not wait on the network. Expiry is
|
|
398
|
+
* LOSSLESS — flush() routes a timed-out batch into the offline queue. */
|
|
399
|
+
const STARTUP_FLUSH_TIMEOUT_MS = 1500;
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* SECOND FLUSH TRIGGER (GitLab #1234) — drain the PREVIOUS session's telemetry
|
|
403
|
+
* queue at the NEXT session's start.
|
|
404
|
+
*
|
|
405
|
+
* THE BUG THIS FIXES: the mechanical flush (#1138) lives only in
|
|
406
|
+
* `hooks/on-session-end.mjs`, which fires on a REGULAR close. Measured 2026-09-06
|
|
407
|
+
* over 90 fleet days, the real close rate is 21,3 % (429 clean closes / 2.016
|
|
408
|
+
* distinct `session.started` ids) — so roughly four sessions in five never reach
|
|
409
|
+
* the only code path that sends. This is the exact argument
|
|
410
|
+
* `backfillOnSessionStart` already makes for the ledger, applied to the ping:
|
|
411
|
+
* SessionStart is the trigger that survives whatever killed the last session.
|
|
412
|
+
*
|
|
413
|
+
* Three properties make this safe to run on the hot start path:
|
|
414
|
+
* - LAZY IMPORT. `sync.mjs` is pulled in at CALL time, not module load. Loading
|
|
415
|
+
* it statically would put the whole telemetry subtree onto the SessionStart
|
|
416
|
+
* hook's import graph — the graph `tests/hooks/on-stop.test.mjs` guards for
|
|
417
|
+
* bare specifiers, and the cost would be paid on every start whether or not
|
|
418
|
+
* telemetry is even consented to.
|
|
419
|
+
* - GATED, ALWAYS. `flush()` opens with `resolveConsent()`; with no consent it
|
|
420
|
+
* returns before any network, queue write, or anon-ID mint.
|
|
421
|
+
* - NEVER THROWS, NEVER BLOCKS A START. Every failure is swallowed, and the POST
|
|
422
|
+
* is bounded at STARTUP_FLUSH_TIMEOUT_MS; a timeout queues the batch rather
|
|
423
|
+
* than losing it.
|
|
424
|
+
*
|
|
425
|
+
* @param {string} repoRoot
|
|
426
|
+
* @returns {Promise<void>}
|
|
427
|
+
*/
|
|
428
|
+
async function flushTelemetryOnSessionStart(repoRoot) {
|
|
429
|
+
try {
|
|
430
|
+
const { flush } = await import('./lib/telemetry/sync.mjs');
|
|
431
|
+
await flush({
|
|
432
|
+
metricsDir: path.join(repoRoot, '.orchestrator', 'metrics'),
|
|
433
|
+
timeoutMs: STARTUP_FLUSH_TIMEOUT_MS,
|
|
434
|
+
});
|
|
435
|
+
} catch { /* fire-and-forget by contract */ }
|
|
436
|
+
}
|
|
437
|
+
|
|
392
438
|
function renderHuman(summary) {
|
|
393
439
|
const lines = [];
|
|
394
440
|
lines.push(`Backfill abandoned sessions — ${summary.mode}`);
|
|
@@ -441,7 +487,7 @@ async function main() {
|
|
|
441
487
|
' --dry-run preview only (DEFAULT — nothing is written)\n' +
|
|
442
488
|
' --apply synthesize + append abandoned-session stubs\n' +
|
|
443
489
|
' --json emit the summary as JSON to stdout\n' +
|
|
444
|
-
' --repo-root override the project root (default: resolved
|
|
490
|
+
' --repo-root override the project root (default: resolved project dir)\n' +
|
|
445
491
|
' --assume-dead-before ISO-8601 cutoff; a candidate whose last known event\n' +
|
|
446
492
|
' predates it bypasses a live foreign session.lock\n' +
|
|
447
493
|
'Exit codes: 0 ok, 1 arg error, 2 system error\n'
|
|
@@ -451,7 +497,7 @@ async function main() {
|
|
|
451
497
|
|
|
452
498
|
// --apply is an explicit opt-in; absent it (or with --dry-run) we never write.
|
|
453
499
|
const apply = values.apply === true && values['dry-run'] !== true;
|
|
454
|
-
const repoRoot = values['repo-root'] ||
|
|
500
|
+
const repoRoot = values['repo-root'] || getProjectDir();
|
|
455
501
|
|
|
456
502
|
let assumeDeadBeforeMs = null;
|
|
457
503
|
if (typeof values['assume-dead-before'] === 'string' && values['assume-dead-before'].length > 0) {
|
|
@@ -71,7 +71,7 @@ import { parseSessionConfig } from './lib/config.mjs';
|
|
|
71
71
|
import { subjectToSlug, parseFrontmatter } from './lib/vault-mirror/utils.mjs';
|
|
72
72
|
import { kebab } from './lib/learnings/kebab.mjs';
|
|
73
73
|
import { validateLearning } from './lib/learnings/schema.mjs';
|
|
74
|
-
import { appendLearning } from './lib/learnings/io.mjs';
|
|
74
|
+
import { appendLearning, isBackupOf } from './lib/learnings/io.mjs';
|
|
75
75
|
|
|
76
76
|
const DEFAULT_RULES_DIR = '.claude/rules';
|
|
77
77
|
const DEFAULT_STORE = '.orchestrator/metrics/learnings.jsonl';
|
|
@@ -620,7 +620,13 @@ function idsInStore(filePath) {
|
|
|
620
620
|
return ids;
|
|
621
621
|
}
|
|
622
622
|
|
|
623
|
-
/**
|
|
623
|
+
/**
|
|
624
|
+
* List `<store>.bak[-.]*` siblings (a verbatim original beats any
|
|
625
|
+
* reconstruction). Both delimiter forms count — the legacy dot form
|
|
626
|
+
* (`learnings.jsonl.bak.evolve-<ts>`) is still on disk in consumer repos and
|
|
627
|
+
* was silently skipped here, i.e. a backup that exists and is not found in the
|
|
628
|
+
* one case it is for (#1173).
|
|
629
|
+
*/
|
|
624
630
|
function backupPaths(storePath) {
|
|
625
631
|
const dir = dirname(storePath);
|
|
626
632
|
const base = basename(storePath);
|
|
@@ -633,7 +639,7 @@ function backupPaths(storePath) {
|
|
|
633
639
|
return [];
|
|
634
640
|
}
|
|
635
641
|
return names
|
|
636
|
-
.filter((n) =>
|
|
642
|
+
.filter((n) => isBackupOf(base, n))
|
|
637
643
|
.sort()
|
|
638
644
|
.map((n) => join(dir, n));
|
|
639
645
|
}
|