session-orchestrator 5.1.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/architecture/SKILL.md +3 -1
- package/.agents/skills/autopilot/SKILL.md +6 -1
- package/.agents/skills/autopilot/agents/openai.yaml +5 -0
- package/.agents/skills/bootstrap/SKILL.md +7 -1
- package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
- package/.agents/skills/brainstorm/SKILL.md +8 -1
- package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +3 -1
- package/.agents/skills/close/SKILL.md +21 -0
- package/.agents/skills/close/agents/openai.yaml +5 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
- package/.agents/skills/debug/SKILL.md +7 -1
- package/.agents/skills/debug/agents/openai.yaml +5 -0
- package/.agents/skills/discovery/SKILL.md +7 -2
- package/.agents/skills/discovery/agents/openai.yaml +5 -0
- package/.agents/skills/dispatcher/SKILL.md +7 -1
- package/.agents/skills/dispatcher/agents/openai.yaml +5 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +3 -1
- package/.agents/skills/ecosystem-health/SKILL.md +3 -1
- package/.agents/skills/eli5/SKILL.md +7 -1
- package/.agents/skills/eli5/agents/openai.yaml +5 -0
- package/.agents/skills/eval/SKILL.md +7 -2
- package/.agents/skills/eval/agents/openai.yaml +5 -0
- package/.agents/skills/evolve/SKILL.md +8 -3
- package/.agents/skills/evolve/agents/openai.yaml +5 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +3 -1
- package/.agents/skills/gitlab-ops/SKILL.md +3 -1
- package/.agents/skills/gitlab-portfolio/SKILL.md +3 -1
- package/.agents/skills/go/SKILL.md +22 -0
- package/.agents/skills/go/agents/openai.yaml +5 -0
- package/.agents/skills/grill/SKILL.md +7 -1
- package/.agents/skills/grill/agents/openai.yaml +5 -0
- package/.agents/skills/harness-audit/SKILL.md +20 -0
- package/.agents/skills/harness-audit/agents/openai.yaml +5 -0
- package/.agents/skills/hook-development/SKILL.md +3 -1
- package/.agents/skills/mcp-builder/SKILL.md +3 -1
- package/.agents/skills/memory-cleanup/SKILL.md +6 -1
- package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
- package/.agents/skills/mode-selector/SKILL.md +3 -1
- package/.agents/skills/npm-publish/SKILL.md +4 -2
- package/.agents/skills/peekaboo-driver/SKILL.md +3 -1
- package/.agents/skills/persona-panel/SKILL.md +6 -1
- package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
- package/.agents/skills/plan/SKILL.md +8 -2
- package/.agents/skills/plan/agents/openai.yaml +5 -0
- package/.agents/skills/playwright-driver/SKILL.md +3 -1
- package/.agents/skills/portfolio/SKILL.md +21 -0
- package/.agents/skills/portfolio/agents/openai.yaml +5 -0
- package/.agents/skills/quality-gates/SKILL.md +3 -1
- package/.agents/skills/reconcile/SKILL.md +6 -1
- package/.agents/skills/reconcile/agents/openai.yaml +5 -0
- package/.agents/skills/release/SKILL.md +22 -0
- package/.agents/skills/release/agents/openai.yaml +5 -0
- package/.agents/skills/remote-offload/SKILL.md +3 -1
- package/.agents/skills/repo-audit/SKILL.md +6 -1
- package/.agents/skills/repo-audit/agents/openai.yaml +5 -0
- package/.agents/skills/session/SKILL.md +21 -0
- package/.agents/skills/session/agents/openai.yaml +5 -0
- package/.agents/skills/session-end/SKILL.md +3 -1
- package/.agents/skills/session-plan/SKILL.md +3 -1
- package/.agents/skills/session-start/SKILL.md +3 -1
- package/.agents/skills/spinout/SKILL.md +6 -1
- package/.agents/skills/spinout/agents/openai.yaml +5 -0
- package/.agents/skills/sunset-review/SKILL.md +7 -1
- package/.agents/skills/sunset-review/agents/openai.yaml +5 -0
- package/.agents/skills/templates-ack/SKILL.md +21 -0
- package/.agents/skills/templates-ack/agents/openai.yaml +5 -0
- package/.agents/skills/test/SKILL.md +21 -0
- package/.agents/skills/test/agents/openai.yaml +5 -0
- package/.agents/skills/test-runner/SKILL.md +3 -1
- package/.agents/skills/tmux-layout/SKILL.md +3 -1
- package/.agents/skills/using-orchestrator/SKILL.md +3 -1
- package/.agents/skills/ux-grill/SKILL.md +7 -1
- package/.agents/skills/ux-grill/agents/openai.yaml +5 -0
- package/.agents/skills/vault-mirror/SKILL.md +3 -1
- package/.agents/skills/vault-sync/SKILL.md +3 -1
- package/.agents/skills/wave-executor/SKILL.md +3 -1
- package/.agents/skills/write-executable-plan/SKILL.md +3 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +4 -4
- package/.codex-plugin/skills/autopilot/SKILL.md +5 -4
- package/.codex-plugin/skills/bootstrap/SKILL.md +8 -4
- package/.codex-plugin/skills/brainstorm/SKILL.md +11 -4
- package/.codex-plugin/skills/close/SKILL.md +3 -3
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -1
- package/.codex-plugin/skills/debug/SKILL.md +11 -4
- package/.codex-plugin/skills/discovery/SKILL.md +8 -4
- package/.codex-plugin/skills/dispatcher/SKILL.md +4 -4
- package/.codex-plugin/skills/eli5/SKILL.md +9 -4
- package/.codex-plugin/skills/eval/SKILL.md +9 -4
- package/.codex-plugin/skills/evolve/SKILL.md +9 -4
- package/.codex-plugin/skills/go/SKILL.md +3 -3
- package/.codex-plugin/skills/grill/SKILL.md +11 -4
- package/.codex-plugin/skills/harness-audit/SKILL.md +4 -3
- package/.codex-plugin/skills/memory-cleanup/SKILL.md +9 -4
- package/.codex-plugin/skills/npm-publish/SKILL.md +1 -1
- package/.codex-plugin/skills/persona-panel/SKILL.md +5 -5
- package/.codex-plugin/skills/plan/SKILL.md +8 -4
- package/.codex-plugin/skills/portfolio/SKILL.md +3 -3
- package/.codex-plugin/skills/reconcile/SKILL.md +9 -4
- package/.codex-plugin/skills/release/SKILL.md +3 -3
- package/.codex-plugin/skills/repo-audit/SKILL.md +6 -4
- package/.codex-plugin/skills/session/SKILL.md +1 -1
- package/.codex-plugin/skills/spinout/SKILL.md +4 -4
- package/.codex-plugin/skills/sunset-review/SKILL.md +5 -4
- package/.codex-plugin/skills/test/SKILL.md +3 -3
- package/.codex-plugin/skills/ux-grill/SKILL.md +11 -4
- package/.cursor/commands/autopilot.md +4 -4
- package/.cursor/commands/bootstrap.md +5 -4
- package/.cursor/commands/brainstorm.md +5 -4
- package/.cursor/commands/close.md +4 -3
- package/.cursor/commands/debug.md +4 -4
- package/.cursor/commands/discovery.md +4 -4
- package/.cursor/commands/dispatcher.md +4 -4
- package/.cursor/commands/eli5.md +4 -4
- package/.cursor/commands/eval.md +4 -4
- package/.cursor/commands/evolve.md +4 -4
- package/.cursor/commands/go.md +4 -3
- package/.cursor/commands/grill.md +4 -4
- package/.cursor/commands/harness-audit.md +3 -3
- package/.cursor/commands/memory-cleanup.md +4 -4
- package/.cursor/commands/persona-panel.md +4 -4
- package/.cursor/commands/plan.md +5 -4
- package/.cursor/commands/portfolio.md +3 -3
- package/.cursor/commands/reconcile.md +4 -4
- package/.cursor/commands/release.md +4 -3
- package/.cursor/commands/repo-audit.md +4 -4
- package/.cursor/commands/session.md +1 -1
- package/.cursor/commands/spinout.md +4 -4
- package/.cursor/commands/sunset-review.md +4 -4
- package/.cursor/commands/test.md +3 -3
- package/.cursor/commands/ux-grill.md +4 -4
- package/.cursor/rules/000-session-orchestrator.mdc +0 -2
- package/.cursor/rules/010-session-workflow.mdc +2 -2
- package/.cursor/rules/050-plan.mdc +1 -1
- package/.cursor/skills/bootstrap/SKILL.md +1 -0
- package/.cursor/skills/close/SKILL.md +13 -0
- package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
- package/.cursor/skills/debug/SKILL.md +0 -1
- package/.cursor/skills/discovery/SKILL.md +0 -1
- package/.cursor/skills/dispatcher/SKILL.md +0 -1
- package/.cursor/skills/eli5/SKILL.md +0 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/evolve/SKILL.md +0 -1
- package/.cursor/skills/go/SKILL.md +13 -0
- package/.cursor/skills/grill/SKILL.md +0 -1
- package/.cursor/skills/harness-audit/SKILL.md +12 -0
- package/.cursor/skills/npm-publish/SKILL.md +1 -0
- package/.cursor/skills/portfolio/SKILL.md +12 -0
- package/.cursor/skills/release/SKILL.md +13 -0
- package/.cursor/skills/repo-audit/SKILL.md +0 -1
- package/.cursor/skills/sunset-review/SKILL.md +0 -1
- package/.cursor/skills/test/SKILL.md +12 -0
- package/.cursor/skills/ux-grill/SKILL.md +0 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +13 -4
- package/AGENTS.md +3 -2
- package/CHANGELOG.md +197 -0
- package/README.md +11 -9
- package/SECURITY.md +12 -0
- package/agents/dialectic-deriver.md +13 -10
- package/agents/eval-judge.md +67 -45
- package/agents/skill-applied-judge.md +34 -19
- package/commands/session.md +17 -3
- package/docs/baseline.md +12 -6
- package/docs/ci-setup.md +53 -0
- package/docs/codex-setup.md +15 -3
- package/docs/components.md +13 -6
- package/docs/events-schema.md +59 -9
- package/docs/install.md +16 -0
- package/docs/persona-panel.md +1 -1
- package/docs/pi-setup.md +1 -1
- package/docs/rule-authoring.md +135 -14
- package/docs/scope-collision-guard.md +2 -0
- package/docs/session-config-reference.md +106 -11
- package/docs/session-config-template.md +31 -2
- package/docs/telemetry.md +2 -0
- package/hooks/_lib/hook-import-set.json +125 -8
- package/hooks/_lib/subagent-paths.mjs +15 -0
- package/hooks/_lib/subagent-transcript.mjs +582 -31
- package/hooks/_lib/vcs-create-matcher.mjs +217 -62
- package/hooks/config-protection.mjs +11 -3
- package/hooks/cwd-change-restore.mjs +11 -3
- package/hooks/enforce-commands.mjs +70 -23
- package/hooks/enforce-scope.mjs +143 -33
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/loop-guard.mjs +11 -3
- package/hooks/on-session-end.mjs +72 -25
- package/hooks/on-session-start.mjs +48 -11
- package/hooks/on-stop.mjs +211 -23
- package/hooks/operator-steer.mjs +11 -3
- package/hooks/post-bash-issue-budget-refund.mjs +18 -8
- package/hooks/post-bash-write-verify.mjs +6 -2
- package/hooks/post-edit-import-probe.mjs +17 -9
- package/hooks/post-edit-validate.mjs +13 -5
- package/hooks/post-subagent-discovery-validator.mjs +98 -13
- package/hooks/post-tool-batch-wave-signal.mjs +200 -38
- package/hooks/post-tool-failure-corrective-context.mjs +11 -5
- package/hooks/post-tooluse-frontend-slop.mjs +10 -4
- package/hooks/pre-auq-clarity.mjs +18 -2
- package/hooks/pre-bash-destructive-guard.mjs +80 -9
- package/hooks/pre-bash-issue-budget.mjs +119 -28
- package/hooks/pre-bash-memory-propose-audit.mjs +86 -54
- package/hooks/pre-bash-sessions-ledger-guard.mjs +391 -20
- package/hooks/pre-bash-staging-fence.mjs +335 -31
- package/hooks/pre-bash-templates-first.mjs +19 -14
- package/hooks/pre-task-scope-disjoint.mjs +385 -5
- package/hooks/skill-invocation-telemetry.mjs +2 -1
- package/hooks/subagent-telemetry.mjs +15 -19
- package/hooks/wave-scope-commit-guard.mjs +197 -100
- package/monitors/monitors.json +1 -1
- package/output-styles/wave-summary.md +1 -1
- package/package.json +2 -1
- package/pi/prompts/autopilot.md +3 -3
- package/pi/prompts/bootstrap.md +3 -3
- package/pi/prompts/brainstorm.md +3 -3
- package/pi/prompts/close.md +2 -2
- package/pi/prompts/debug.md +3 -3
- package/pi/prompts/discovery.md +3 -3
- package/pi/prompts/dispatcher.md +3 -3
- package/pi/prompts/eli5.md +3 -3
- package/pi/prompts/eval.md +3 -3
- package/pi/prompts/evolve.md +3 -3
- package/pi/prompts/go.md +2 -2
- package/pi/prompts/grill.md +3 -3
- package/pi/prompts/harness-audit.md +2 -3
- package/pi/prompts/memory-cleanup.md +3 -3
- package/pi/prompts/persona-panel.md +3 -3
- package/pi/prompts/plan.md +3 -3
- package/pi/prompts/portfolio.md +2 -2
- package/pi/prompts/reconcile.md +3 -3
- package/pi/prompts/release.md +3 -3
- package/pi/prompts/repo-audit.md +3 -4
- package/pi/prompts/session.md +2 -2
- package/pi/prompts/spinout.md +3 -3
- package/pi/prompts/sunset-review.md +3 -3
- package/pi/prompts/templates-ack.md +1 -1
- package/pi/prompts/test.md +3 -3
- package/pi/prompts/ux-grill.md +3 -3
- package/rules/README.md +1 -1
- package/rules/opt-in-domain/prompt-caching.md +1 -1
- package/rules/opt-in-stack/backend-data.md +1 -1
- package/rules/opt-in-stack/backend.md +3 -3
- package/rules/opt-in-stack/frontend.md +1 -1
- package/rules/opt-in-stack/security-web.md +3 -3
- package/rules/opt-in-stack/swift.md +1 -1
- package/scripts/archive-closed-prds.mjs +2 -2
- package/scripts/auq-audit.mjs +2 -3
- package/scripts/autopilot.mjs +23 -2
- package/scripts/backfill-abandoned-sessions.mjs +171 -15
- package/scripts/backfill-evidence-digest.mjs +2 -1
- package/scripts/backfill-learnings-from-vault.mjs +2 -2
- package/scripts/check-package-manager.mjs +2 -2
- package/scripts/check-sessions-integrity.mjs +300 -0
- package/scripts/ci/assert-vitest-green.mjs +2 -1
- package/scripts/dialectic-deriver.mjs +50 -13
- package/scripts/emit-session.mjs +77 -32
- package/scripts/eval-session.mjs +65 -3
- package/scripts/export-hw-learnings.mjs +2 -1
- package/scripts/express-path.mjs +1 -1
- package/scripts/gc-stale-worktrees.mjs +2 -1
- package/scripts/generate-agents-skills.mjs +102 -29
- package/scripts/generate-codex-skills.mjs +48 -4
- package/scripts/generate-cursor-adapter.mjs +220 -11
- package/scripts/generate-hook-import-set.mjs +12 -27
- package/scripts/generate-pi-prompts.mjs +183 -13
- package/scripts/github-protection-audit.mjs +2 -3
- package/scripts/lib/agent-frontmatter.mjs +23 -1
- package/scripts/lib/agent-status.mjs +2 -31
- package/scripts/lib/auq/clarity.mjs +10 -2
- package/scripts/lib/auq/parse.mjs +12 -31
- package/scripts/lib/auq/schema.mjs +56 -41
- package/scripts/lib/auto-dialectic.mjs +304 -15
- package/scripts/lib/autopilot/flags.mjs +12 -1
- package/scripts/lib/autopilot/kill-switches.mjs +6 -3
- package/scripts/lib/autopilot/loop.mjs +14 -1
- package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
- package/scripts/lib/ci-status-banner.mjs +376 -16
- package/scripts/lib/claude-md-budget-lint.mjs +2 -5
- package/scripts/lib/command-blocker.mjs +408 -33
- package/scripts/lib/config/dialectic.mjs +12 -3
- package/scripts/lib/config/drift-check.mjs +19 -0
- package/scripts/lib/config/gate.mjs +74 -0
- package/scripts/lib/config/reaper.mjs +162 -0
- package/scripts/lib/config.mjs +14 -0
- package/scripts/lib/convergence-monitor.mjs +76 -13
- package/scripts/lib/cursor-hook-bridge.mjs +2 -2
- package/scripts/lib/description-surface.mjs +2 -5
- package/scripts/lib/dispatcher/cli.mjs +2 -1
- package/scripts/lib/ecosystem-health.mjs +11 -0
- package/scripts/lib/ecosystem-wizard.mjs +2 -1
- package/scripts/lib/eval/engine.mjs +421 -53
- package/scripts/lib/eval/judge.mjs +463 -40
- package/scripts/lib/eval/schema.mjs +10 -1
- package/scripts/lib/events-rotation.mjs +221 -25
- package/scripts/lib/events-schema.mjs +114 -0
- package/scripts/lib/events.mjs +524 -5
- package/scripts/lib/fetch-baseline.mjs +3 -8
- package/scripts/lib/frontmatter-guard.mjs +21 -10
- package/scripts/lib/gates/gate-baseline.mjs +27 -2
- package/scripts/lib/gates/gate-full.mjs +28 -3
- package/scripts/lib/gates/gate-helpers.mjs +243 -21
- package/scripts/lib/gates/gate-incremental.mjs +28 -3
- package/scripts/lib/gates/gate-per-file.mjs +27 -2
- package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +2 -1
- package/scripts/lib/gitlab-portfolio/cli.mjs +2 -1
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
- package/scripts/lib/instruction-budget-guard.mjs +332 -50
- package/scripts/lib/io.mjs +42 -8
- package/scripts/lib/is-main-module.mjs +82 -0
- package/scripts/lib/issue-close-strip-labels.mjs +207 -49
- package/scripts/lib/js-mask.mjs +197 -0
- package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
- package/scripts/lib/locks/index.mjs +32 -25
- package/scripts/lib/maintenance-due-banner.mjs +122 -91
- package/scripts/lib/orphan-reaper.mjs +1588 -0
- package/scripts/lib/peer-cards/merger.mjs +48 -10
- package/scripts/lib/peer-cards/reader.mjs +78 -2
- package/scripts/lib/peer-discovery.mjs +2 -5
- package/scripts/lib/playwright-driver/runner.mjs +2 -1
- package/scripts/lib/process-group.mjs +899 -0
- package/scripts/lib/quality-gate.mjs +107 -28
- package/scripts/lib/reconcile/backlog.mjs +368 -0
- package/scripts/lib/reconcile/engine.mjs +55 -188
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +884 -0
- package/scripts/lib/reconcile/sanitize.mjs +69 -3
- package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
- package/scripts/lib/resource-probe/parsers.mjs +31 -0
- package/scripts/lib/rule-loader.mjs +41 -12
- package/scripts/lib/rules-sync.mjs +2 -5
- package/scripts/lib/scope-echo.mjs +429 -7
- package/scripts/lib/scope-gate.mjs +605 -1
- package/scripts/lib/session-close-backfill.mjs +91 -12
- package/scripts/lib/session-id.mjs +9 -20
- package/scripts/lib/session-invocation.mjs +20 -0
- package/scripts/lib/session-schema/constants.mjs +30 -2
- package/scripts/lib/session-schema/normalizer.mjs +56 -4
- package/scripts/lib/session-schema.mjs +8 -3
- package/scripts/lib/session-start-probes.mjs +95 -10
- package/scripts/lib/sessions-canonical.mjs +23 -0
- package/scripts/lib/sessions-integrity-banner.mjs +7 -1
- package/scripts/lib/sessions-staleness-banner.mjs +193 -51
- package/scripts/lib/skill-evidence-window.mjs +891 -0
- package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
- package/scripts/lib/skill-evolution/engine.mjs +18 -9
- package/scripts/lib/skill-judge.mjs +45 -3
- package/scripts/lib/state-md.mjs +84 -3
- package/scripts/lib/sunset/walker.mjs +31 -4
- package/scripts/lib/tail-window.mjs +56 -0
- package/scripts/lib/telemetry/schema.mjs +30 -0
- package/scripts/lib/telemetry/sync.mjs +61 -6
- package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
- package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
- package/scripts/lib/tests-src-ratio.mjs +2 -6
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +74 -14
- package/scripts/lib/user-invocable-skills.mjs +205 -0
- package/scripts/lib/ux-grill/reconcile.mjs +48 -22
- package/scripts/lib/validate/check-agents-skills.mjs +26 -15
- package/scripts/lib/validate/check-banner-parity.mjs +2 -2
- package/scripts/lib/validate/check-cursor-adapter.mjs +3 -2
- package/scripts/lib/validate/check-dead-bridge.mjs +2 -2
- package/scripts/lib/validate/check-doc-cli-commands.mjs +2 -2
- package/scripts/lib/validate/check-entry-guard.mjs +329 -0
- package/scripts/lib/validate/check-guard-requires-parity.mjs +2 -2
- package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
- package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +2 -2
- package/scripts/lib/validate/check-learning-provenance.mjs +2 -2
- package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
- package/scripts/lib/validate/check-rules.mjs +7 -5
- package/scripts/lib/validate/check-skill-links.mjs +35 -6
- package/scripts/lib/validate/check-skill-script-paths.mjs +241 -29
- package/scripts/lib/validate/check-test-git-config-target.mjs +26 -36
- package/scripts/lib/validate/check-unicode-safety.mjs +2 -2
- package/scripts/lib/validate/check-untracked-test-deps.mjs +9 -104
- package/scripts/lib/validate/check-unwired-features.mjs +220 -33
- package/scripts/lib/validate/check-validator-registration.mjs +36 -12
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +2 -2
- package/scripts/lib/validate/confidential-names.mjs +10 -0
- package/scripts/lib/validate-vendored-rules.mjs +39 -12
- package/scripts/lib/vault-mirror/namespace.mjs +46 -8
- package/scripts/lib/vault-mirror/process.mjs +10 -3
- package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
- package/scripts/lib/vault-status/narrative-mirror.mjs +31 -7
- package/scripts/lib/vault-yaml.mjs +118 -0
- package/scripts/lib/wave-transcript-tail.mjs +2 -2
- package/scripts/lib/worktree/lifecycle.mjs +153 -1
- package/scripts/lock-reaper.mjs +2 -1
- package/scripts/materialize-wave-scope.mjs +87 -4
- package/scripts/migrate-sessions-jsonl.mjs +2 -1
- package/scripts/migrate-vault-paths.mjs +2 -3
- package/scripts/release-session-lock.mjs +305 -0
- package/scripts/release.mjs +109 -39
- package/scripts/relocate-vault-corpus.mjs +2 -3
- package/scripts/repair-invalid-sessions.mjs +2 -2
- package/scripts/resolve-session-invocation.mjs +59 -0
- package/scripts/run-quality-gate.mjs +156 -17
- package/scripts/session-shape.mjs +2 -2
- package/scripts/site-numbers.mjs +35 -11
- package/scripts/sweep-expired-rules.mjs +227 -0
- package/scripts/validate-plugin.mjs +21 -0
- package/scripts/validate-wave-scope.mjs +32 -105
- package/scripts/vault-consolidate.mjs +2 -2
- package/scripts/vault-mirror.mjs +11 -4
- package/scripts/wave-scope-binding.mjs +2 -3
- package/skills/_shared/bootstrap-gate.md +1 -1
- package/skills/_shared/monitor-patterns.md +1 -1
- package/skills/_shared/platform-tools.md +23 -11
- package/skills/_shared/research-evidence.md +53 -0
- package/skills/_shared/state-ownership.md +3 -0
- package/skills/autopilot/SKILL.md +80 -11
- package/skills/bootstrap/SKILL.md +51 -1
- package/skills/brainstorm/SKILL.md +16 -0
- package/skills/claude-md-drift-check/SKILL.md +1 -1
- package/skills/claude-md-drift-check/checker.mjs +49 -11
- package/{commands/close.md → skills/close/SKILL.md} +9 -3
- package/skills/convergence-monitoring/README.md +8 -1
- package/skills/convergence-monitoring/SIGNALS.md +50 -6
- package/skills/convergence-monitoring/SKILL.md +15 -6
- package/skills/debug/SKILL.md +10 -0
- package/skills/discovery/SKILL.md +24 -1
- package/skills/discovery/probes-session.md +2 -2
- package/skills/dispatcher/SKILL.md +38 -7
- package/skills/eli5/SKILL.md +11 -0
- package/skills/eval/SKILL.md +52 -23
- package/skills/eval/rubric-v1.md +1 -0
- package/skills/eval/rubric-v2.md +457 -0
- package/skills/evolve/SKILL.md +9 -2
- package/skills/evolve/references/evolve-dialectic-mode.md +46 -25
- package/skills/gitlab-ops/SKILL.md +3 -2
- package/{commands/go.md → skills/go/SKILL.md} +9 -1
- package/skills/grill/SKILL.md +19 -0
- package/{commands/harness-audit.md → skills/harness-audit/SKILL.md} +7 -2
- package/skills/hook-development/SKILL.md +46 -41
- package/skills/memory-cleanup/SKILL.md +7 -0
- package/skills/npm-publish/SKILL.md +2 -2
- package/skills/persona-panel/SKILL.md +56 -1
- package/skills/persona-panel/persona-format.md +1 -1
- package/skills/plan/SKILL.md +28 -1
- package/{commands/portfolio.md → skills/portfolio/SKILL.md} +8 -2
- package/skills/reconcile/SKILL.md +21 -0
- package/{commands/release.md → skills/release/SKILL.md} +16 -2
- package/skills/repo-audit/SKILL.md +7 -0
- package/skills/session-end/SKILL.md +13 -16
- package/skills/session-end/discovery-scan.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +55 -9
- package/skills/session-end/plan-verification.md +2 -2
- package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
- package/skills/session-end/session-metrics-write.md +10 -0
- package/skills/session-plan/SKILL.md +18 -6
- package/skills/session-plan/references/session-plan-task-classification.md +2 -2
- package/skills/session-start/SKILL.md +5 -4
- package/skills/session-start/phase-8-5-express-path.md +6 -6
- package/skills/session-start/references/phase-1-5-session-continuity.md +1 -1
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +1 -1
- package/skills/session-start/references/phase-4-ssot-environment-check.md +6 -4
- package/skills/spinout/SKILL.md +12 -1
- package/skills/sunset-review/SKILL.md +13 -0
- package/{commands/test.md → skills/test/SKILL.md} +10 -4
- package/skills/ux-grill/SKILL.md +20 -2
- package/skills/wave-executor/SKILL.md +14 -7
- package/skills/wave-executor/circuit-breaker.md +2 -0
- package/skills/wave-executor/references/wave-executor-state-init.md +18 -4
- package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
- package/skills/wave-executor/references/wave-loop-review.md +17 -1
- package/commands/autopilot.md +0 -80
- package/commands/bootstrap.md +0 -56
- package/commands/brainstorm.md +0 -48
- package/commands/debug.md +0 -36
- package/commands/discovery.md +0 -32
- package/commands/dispatcher.md +0 -59
- package/commands/eli5.md +0 -33
- package/commands/eval.md +0 -28
- package/commands/evolve.md +0 -10
- package/commands/grill.md +0 -45
- package/commands/memory-cleanup.md +0 -26
- package/commands/persona-panel.md +0 -121
- package/commands/plan.md +0 -15
- package/commands/reconcile.md +0 -23
- package/commands/repo-audit.md +0 -24
- package/commands/spinout.md +0 -15
- package/commands/sunset-review.md +0 -27
- package/commands/ux-grill.md +0 -51
|
@@ -66,18 +66,21 @@
|
|
|
66
66
|
* hooks.json wiring is managed separately (W3-C4 scope).
|
|
67
67
|
*/
|
|
68
68
|
|
|
69
|
-
import { execFileSync } from 'node:child_process';
|
|
69
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
70
|
+
import { readFileSync } from 'node:fs';
|
|
70
71
|
import { readFile } from 'node:fs/promises';
|
|
71
72
|
import path from 'node:path';
|
|
73
|
+
import { performance } from 'node:perf_hooks';
|
|
74
|
+
import { fileURLToPath } from 'node:url';
|
|
72
75
|
|
|
73
76
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
74
|
-
|
|
75
|
-
if (!shouldRunHook('post-tool-batch-wave-signal')) process.exit(0);
|
|
77
|
+
import { isMainModule } from '../scripts/lib/is-main-module.mjs';
|
|
76
78
|
|
|
77
79
|
import { getProjectDir } from '../scripts/lib/platform.mjs';
|
|
78
80
|
import { emitEvent } from '../scripts/lib/events.mjs';
|
|
79
81
|
import { findScopeFile } from '../scripts/lib/scope-gate.mjs';
|
|
80
82
|
import { atomicMutateJson } from './_lib/atomic-json.mjs';
|
|
83
|
+
import { _parseReaper } from '../scripts/lib/config/reaper.mjs';
|
|
81
84
|
|
|
82
85
|
// ---------------------------------------------------------------------------
|
|
83
86
|
// Helpers
|
|
@@ -205,7 +208,7 @@ async function ownsSessionFile(input, sessionFile) {
|
|
|
205
208
|
* previous hard-coded `.claude/` path made this hook's whole wave-lifecycle
|
|
206
209
|
* fallback structurally dead on Codex CLI, Cursor and pi: the manifest exists,
|
|
207
210
|
* it just is not under `.claude/`, so every batch read 0 and no
|
|
208
|
-
*
|
|
211
|
+
* wave-lifecycle event was ever emitted there.
|
|
209
212
|
*
|
|
210
213
|
* @param {string} projectDir
|
|
211
214
|
* @returns {Promise<number>}
|
|
@@ -305,6 +308,145 @@ function countFilesChangedSince(projectDir, sha) {
|
|
|
305
308
|
}
|
|
306
309
|
}
|
|
307
310
|
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
// orphan-reaper trigger (#1432 B4)
|
|
313
|
+
// ---------------------------------------------------------------------------
|
|
314
|
+
//
|
|
315
|
+
// DUPLICATED VERBATIM between the two trigger hooks —
|
|
316
|
+
// hooks/post-tool-batch-wave-signal.mjs <-> hooks/on-stop.mjs. Deliberate, with
|
|
317
|
+
// a named revisit trigger (BV-004): they are the only two trigger points the
|
|
318
|
+
// PRD declares, and a shared `hooks/_lib/reaper-trigger.mjs` would be a third
|
|
319
|
+
// file in the hook import graph. SIZE, measured 2026-09-22 by diffing the two
|
|
320
|
+
// blocks: 138 identical lines — this note said "~40 lines of glue" until then,
|
|
321
|
+
// which is what made the duplicate look cheaper than it is. Extract it the
|
|
322
|
+
// moment a THIRD hook needs the trigger, or the moment the copies must differ.
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Filesystem path of the scan CLI, spawned as a PLAIN argv call.
|
|
326
|
+
*
|
|
327
|
+
* It used to be a `file://` URL handed to `node --input-type=module -e
|
|
328
|
+
* <program>`, because `scripts/lib/orphan-reaper.mjs` had no entry guard. It
|
|
329
|
+
* has one now (`parseReaperCliArgs` + the `isMainModule` tail), so the child's
|
|
330
|
+
* contract lives in that module instead of as source text duplicated here —
|
|
331
|
+
* and nothing this hook builds is a program any more: every variable part is
|
|
332
|
+
* an argv value, which cannot become code whatever the checkout path contains.
|
|
333
|
+
*
|
|
334
|
+
* `fileURLToPath`, not `new URL(...).pathname`: the latter leaves a
|
|
335
|
+
* percent-encoded path for any checkout directory containing a space.
|
|
336
|
+
*/
|
|
337
|
+
const ORPHAN_REAPER_SCRIPT = fileURLToPath(
|
|
338
|
+
new URL('../scripts/lib/orphan-reaper.mjs', import.meta.url),
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Read the `reaper:` block from the project's Session Config host file.
|
|
343
|
+
* Sync + inline, mirroring `hooks/loop-guard.mjs` `loadConfig()` — a hot hook
|
|
344
|
+
* path must not import the full config orchestrator. A missing or unreadable
|
|
345
|
+
* file yields the parser defaults, i.e. DISABLED.
|
|
346
|
+
*
|
|
347
|
+
* @param {string} projectDir
|
|
348
|
+
* @returns {ReturnType<typeof _parseReaper>}
|
|
349
|
+
*/
|
|
350
|
+
function loadReaperConfig(projectDir) {
|
|
351
|
+
for (const name of ['CLAUDE.md', 'AGENTS.md']) {
|
|
352
|
+
try {
|
|
353
|
+
return _parseReaper(readFileSync(path.join(projectDir, name), 'utf8'));
|
|
354
|
+
} catch {
|
|
355
|
+
// missing or unreadable — try the next candidate
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return _parseReaper('');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Trigger the orphan scan, throttled and NON-BLOCKING (PRD FA4).
|
|
363
|
+
*
|
|
364
|
+
* The hook itself does exactly two pieces of I/O — one config read and one
|
|
365
|
+
* `stat` of the throttle marker — and then hands the work to a DETACHED,
|
|
366
|
+
* unref'd child. The scan is never run inline: one `ps` round-trip out of Node
|
|
367
|
+
* was measured at ~47 ms over 287 KB of output, which alone would blow the
|
|
368
|
+
* 50 ms `reaper.max-hook-latency-ms` budget this hook has to stay inside.
|
|
369
|
+
*
|
|
370
|
+
* The marker is stamped BEFORE the spawn, so a spawn that fails still consumes
|
|
371
|
+
* the throttle window — otherwise a broken spawn would be retried on every
|
|
372
|
+
* single tool batch.
|
|
373
|
+
*
|
|
374
|
+
* `reaper.max-hook-latency-ms` is the ceiling on the work above — measured here
|
|
375
|
+
* with `performance.now()` and reported as ONE stderr WARN line when exceeded.
|
|
376
|
+
* A WARN and not an event: this fires from a `PostToolBatch`-class hook, and a
|
|
377
|
+
* per-fire telemetry record is exactly the always-on signal
|
|
378
|
+
* `.claude/rules/host-resources.md` HR-101 calls a broken instrument. The
|
|
379
|
+
* measurement covers preparation only — the scan itself runs detached, which is
|
|
380
|
+
* the whole reason the budget can be held.
|
|
381
|
+
*
|
|
382
|
+
* Never throws: any failure degrades silently (PRD FA4 "lautlos degradieren").
|
|
383
|
+
*
|
|
384
|
+
* @param {object} [opts]
|
|
385
|
+
* @param {string} [opts.projectDir] Repo root; defaults to `getProjectDir()`.
|
|
386
|
+
* @param {number} [opts.now] Injected clock (ms).
|
|
387
|
+
* @param {Function} [opts.spawnFn] Injected `spawn` (tests).
|
|
388
|
+
* @param {Function} [opts.statFn] Injected `statSync` (tests).
|
|
389
|
+
* @param {Function} [opts.writeFn] Injected marker writer (tests).
|
|
390
|
+
* @param {() => number} [opts.clockFn] Injected monotonic clock for the latency
|
|
391
|
+
* budget (tests); defaults to `performance.now`.
|
|
392
|
+
* @returns {Promise<{spawned: boolean, reason: string}>}
|
|
393
|
+
*/
|
|
394
|
+
export async function maybeTriggerOrphanScan({
|
|
395
|
+
projectDir,
|
|
396
|
+
now,
|
|
397
|
+
spawnFn = spawn,
|
|
398
|
+
statFn,
|
|
399
|
+
writeFn,
|
|
400
|
+
clockFn = () => performance.now(),
|
|
401
|
+
} = {}) {
|
|
402
|
+
const startedAt = clockFn();
|
|
403
|
+
/** One WARN line when the preparation overran `reaper.max-hook-latency-ms`. */
|
|
404
|
+
const checkLatency = (budgetMs) => {
|
|
405
|
+
const elapsed = clockFn() - startedAt;
|
|
406
|
+
if (Number.isFinite(budgetMs) && budgetMs > 0 && elapsed > budgetMs) {
|
|
407
|
+
process.stderr.write(
|
|
408
|
+
`orphan-reaper trigger: hook latency ${elapsed.toFixed(1)} ms exceeded `
|
|
409
|
+
+ `reaper.max-hook-latency-ms (${budgetMs} ms)\n`,
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
try {
|
|
414
|
+
const root = typeof projectDir === 'string' && projectDir ? projectDir : getProjectDir();
|
|
415
|
+
const cfg = loadReaperConfig(root);
|
|
416
|
+
// Cheapest gate first: disabled means no stat, no spawn, no module load.
|
|
417
|
+
if (cfg.enabled !== true) return { spawned: false, reason: 'disabled' };
|
|
418
|
+
|
|
419
|
+
const reaper = await import('../scripts/lib/orphan-reaper.mjs');
|
|
420
|
+
const markerPath = reaper.scanMarkerPath(root);
|
|
421
|
+
const nowMs = typeof now === 'number' ? now : Date.now();
|
|
422
|
+
|
|
423
|
+
if (!reaper.shouldScanNow(markerPath, nowMs, cfg['min-scan-interval-seconds'], { statFn })) {
|
|
424
|
+
checkLatency(cfg['max-hook-latency-ms']);
|
|
425
|
+
return { spawned: false, reason: 'throttled' };
|
|
426
|
+
}
|
|
427
|
+
reaper.touchScanMarker(markerPath, { writeFn });
|
|
428
|
+
|
|
429
|
+
const child = spawnFn(
|
|
430
|
+
process.execPath,
|
|
431
|
+
[
|
|
432
|
+
ORPHAN_REAPER_SCRIPT,
|
|
433
|
+
'--repo-root', root,
|
|
434
|
+
'--mode', cfg.mode,
|
|
435
|
+
'--min-age-seconds', String(cfg['min-age-seconds']),
|
|
436
|
+
'--kill-grace-ms', String(cfg['kill-grace-ms']),
|
|
437
|
+
'--verify-wait-ms', String(cfg['verify-wait-ms']),
|
|
438
|
+
'--false-alarm-window', String(cfg['false-alarm-window']),
|
|
439
|
+
],
|
|
440
|
+
{ detached: true, stdio: 'ignore' },
|
|
441
|
+
);
|
|
442
|
+
if (child && typeof child.unref === 'function') child.unref();
|
|
443
|
+
checkLatency(cfg['max-hook-latency-ms']);
|
|
444
|
+
return { spawned: true, reason: 'spawned' };
|
|
445
|
+
} catch {
|
|
446
|
+
return { spawned: false, reason: 'error' };
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
308
450
|
// ---------------------------------------------------------------------------
|
|
309
451
|
// Main
|
|
310
452
|
// ---------------------------------------------------------------------------
|
|
@@ -378,9 +520,13 @@ async function main() {
|
|
|
378
520
|
}
|
|
379
521
|
} catch { /* best effort — hook must remain non-blocking */ }
|
|
380
522
|
|
|
381
|
-
// Emit wave-
|
|
382
|
-
// populates wave_signal
|
|
383
|
-
//
|
|
523
|
+
// Emit the wave-completion event via the canonical stream when the
|
|
524
|
+
// orchestrator populates wave_signal 'wave-complete'. Mechanical seam — see
|
|
525
|
+
// docs/events-schema.md. Best-effort; never blocks the hook. A 'wave-start'
|
|
526
|
+
// signal is still ACCEPTED — it keeps suppressing the fallback below, as
|
|
527
|
+
// before — but emits nothing: the `started` sibling event was removed
|
|
528
|
+
// 2026-09-19, having no reader once the convergence monitor stopped
|
|
529
|
+
// admitting it.
|
|
384
530
|
//
|
|
385
531
|
// Live path today: the `wave_signal === null` branch below provides the
|
|
386
532
|
// mechanical wave-lifecycle fallback (#612). It fires live by diffing
|
|
@@ -388,19 +534,14 @@ async function main() {
|
|
|
388
534
|
// payload injection required. This explicit-signal branch remains as the
|
|
389
535
|
// preferred path for whenever the harness DOES inject `wave_signal` into the
|
|
390
536
|
// batch payload (it takes precedence over the fallback when present).
|
|
391
|
-
if (waveSignal === 'wave-
|
|
537
|
+
if (waveSignal === 'wave-complete') {
|
|
392
538
|
try {
|
|
393
|
-
await emitEvent(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
{
|
|
398
|
-
|
|
399
|
-
...(nextWaveRole !== null ? { next_wave_role: nextWaveRole } : {}),
|
|
400
|
-
...(batchId !== null ? { batch_id: batchId } : {}),
|
|
401
|
-
...(batchSize !== null ? { batch_size: batchSize } : {}),
|
|
402
|
-
},
|
|
403
|
-
);
|
|
539
|
+
await emitEvent('orchestrator.wave.completed', {
|
|
540
|
+
...(waveNumber !== null ? { wave_number: waveNumber } : {}),
|
|
541
|
+
...(nextWaveRole !== null ? { next_wave_role: nextWaveRole } : {}),
|
|
542
|
+
...(batchId !== null ? { batch_id: batchId } : {}),
|
|
543
|
+
...(batchSize !== null ? { batch_size: batchSize } : {}),
|
|
544
|
+
});
|
|
404
545
|
// #1193 review F2 — this branch is a SECOND emitter of
|
|
405
546
|
// `wave.completed`, so it must feed the same high-water mark the other
|
|
406
547
|
// two read; otherwise SessionEnd (and the fallback below) would close the
|
|
@@ -413,8 +554,7 @@ async function main() {
|
|
|
413
554
|
// and walked the marker BACKWARDS to 4. Both keys go through maxWave().
|
|
414
555
|
// W4c Q1-MED — and neither is written into a PEER's record.
|
|
415
556
|
if (
|
|
416
|
-
|
|
417
|
-
&& typeof waveNumber === 'number'
|
|
557
|
+
typeof waveNumber === 'number'
|
|
418
558
|
&& waveNumber > 0
|
|
419
559
|
&& await ownsSessionFile(input, sessionFile)
|
|
420
560
|
) {
|
|
@@ -498,24 +638,31 @@ async function main() {
|
|
|
498
638
|
...(batchSize !== null ? { batch_size: batchSize } : {}),
|
|
499
639
|
});
|
|
500
640
|
}
|
|
501
|
-
// Open the new wave.
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
});
|
|
508
|
-
// Persist the high-water marks so the next batch does not re-emit,
|
|
509
|
-
// and so SessionEnd can tell whether the prior wave was already
|
|
510
|
-
// closed here (#1193). `last_wave_completed` is only advanced when a
|
|
511
|
-
// `completed` was actually emitted above (lastWave > 0).
|
|
641
|
+
// Open the new wave. There is no event for this (the `started`
|
|
642
|
+
// sibling was removed 2026-09-19); opening IS the high-water marks
|
|
643
|
+
// persisted below, so the next batch does not re-close the prior
|
|
644
|
+
// wave, and SessionEnd can tell whether it was already closed here
|
|
645
|
+
// (#1193). `last_wave_completed` is only advanced when a `completed`
|
|
646
|
+
// was actually emitted above (lastWave > 0).
|
|
512
647
|
// W4c Q1-MED — never stamp these into a PEER session's record. The
|
|
513
|
-
//
|
|
648
|
+
// event above is still emitted (it carries THIS session's
|
|
514
649
|
// attribution via emitEvent); only the shared-file claim is withheld.
|
|
515
650
|
// NAMED CEILING: for a non-owning session the marks therefore never
|
|
516
|
-
// advance, so
|
|
517
|
-
//
|
|
518
|
-
//
|
|
651
|
+
// advance, so each of its batches re-emits completed{lastWave} while
|
|
652
|
+
// the file's `last_wave` is ahead of its `last_wave_completed`, until
|
|
653
|
+
// that session's own SessionStart takes over the file. The revisit
|
|
654
|
+
// trigger (repeated emissions for one wave) HAS FIRED — measured
|
|
655
|
+
// 2026-09-19 @ 8f6ac022 over this repo's
|
|
656
|
+
// `.orchestrator/metrics/events.jsonl{.1,}`, timestamps >= 2026-09-12,
|
|
657
|
+
// grouped by (session_id, wave_number) with jq:
|
|
658
|
+
// started — 1936 emissions / 48 groups, 19 duplicated, max 274
|
|
659
|
+
// completed — 650 emissions / 28 groups, 2 duplicated, max 579
|
|
660
|
+
// The `started` half is GONE (#1202 §11): its only reader,
|
|
661
|
+
// `scripts/lib/convergence-monitor.mjs`, no longer admits it — it
|
|
662
|
+
// carried no measurement and, sharing a tail tick with completed{N},
|
|
663
|
+
// masked the (N-1, N) shrinking_diff pair. The `completed` half is
|
|
664
|
+
// OPEN: that event has readers, so the fix is a mark a non-owning
|
|
665
|
+
// session can keep for itself, not a removal.
|
|
519
666
|
if (await ownsSessionFile(input, sessionFile)) {
|
|
520
667
|
// #980 — the OPEN half: stamp the sha this new wave starts from.
|
|
521
668
|
// Written as null (not omitted) when git is unreadable, so the
|
|
@@ -559,7 +706,22 @@ async function main() {
|
|
|
559
706
|
},
|
|
560
707
|
}));
|
|
561
708
|
}
|
|
709
|
+
|
|
710
|
+
// Orphan-reaper trigger (#1432 B4). LAST, after this hook's own contract is
|
|
711
|
+
// fulfilled — the stdout envelope above must never wait on the watchdog.
|
|
712
|
+
// Awaited (not fired and forgotten) because the entry guard calls
|
|
713
|
+
// `process.exit(0)` in a `finally`: an un-awaited spawn would race the exit.
|
|
714
|
+
// The await is bounded by design — one config read, one stat, one detached
|
|
715
|
+
// spawn, all measured under `reaper.max-hook-latency-ms` (50 ms).
|
|
716
|
+
await maybeTriggerOrphanScan();
|
|
562
717
|
}
|
|
563
718
|
|
|
564
|
-
//
|
|
565
|
-
|
|
719
|
+
// Entry guard (#1393): run only as the node script the harness execs — a bare
|
|
720
|
+
// `import()` must run no handler and must not exit the importing process.
|
|
721
|
+
if (isMainModule(import.meta.url)) {
|
|
722
|
+
// Exit 0 immediately when disabled via SO_HOOK_PROFILE / SO_DISABLED_HOOKS.
|
|
723
|
+
if (!shouldRunHook('post-tool-batch-wave-signal')) process.exit(0);
|
|
724
|
+
|
|
725
|
+
// Exit 0 always — informational hook must never block Claude.
|
|
726
|
+
main().catch(() => {}).finally(() => process.exit(0));
|
|
727
|
+
}
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
import path from 'node:path';
|
|
28
28
|
|
|
29
29
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
30
|
-
|
|
31
|
-
if (!shouldRunHook('post-tool-failure-corrective-context')) process.exit(0);
|
|
30
|
+
import { isMainModule } from '../scripts/lib/is-main-module.mjs';
|
|
32
31
|
|
|
33
32
|
import { getProjectDir } from '../scripts/lib/platform.mjs';
|
|
34
33
|
import { atomicMutateJson } from './_lib/atomic-json.mjs';
|
|
@@ -206,7 +205,7 @@ async function main() {
|
|
|
206
205
|
// Strip control chars (newlines, ANSI escapes) before forwarding to additionalContext —
|
|
207
206
|
// SEC-016 (Log Injection): tool errors may contain attacker-controlled bytes.
|
|
208
207
|
const safeSummary = errorSummary
|
|
209
|
-
? errorSummary.replace(/[\r\n]/g, ' ').split('
|
|
208
|
+
? errorSummary.replace(/[\r\n]/g, ' ').split('\x1b').join(' ').slice(0, 120)
|
|
210
209
|
: '';
|
|
211
210
|
const summaryLabel = safeSummary ? ` Error: ${safeSummary}` : '';
|
|
212
211
|
const additionalContext =
|
|
@@ -221,5 +220,12 @@ async function main() {
|
|
|
221
220
|
}));
|
|
222
221
|
}
|
|
223
222
|
|
|
224
|
-
//
|
|
225
|
-
|
|
223
|
+
// Entry guard (#1393): run only as the node script the harness execs — a bare
|
|
224
|
+
// `import()` must run no handler and must not exit the importing process.
|
|
225
|
+
if (isMainModule(import.meta.url)) {
|
|
226
|
+
// Exit 0 immediately when disabled via SO_HOOK_PROFILE / SO_DISABLED_HOOKS.
|
|
227
|
+
if (!shouldRunHook('post-tool-failure-corrective-context')) process.exit(0);
|
|
228
|
+
|
|
229
|
+
// Exit 0 always — informational hook must never block Claude.
|
|
230
|
+
main().catch(() => {}).finally(() => process.exit(0));
|
|
231
|
+
}
|
|
@@ -24,8 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
27
|
-
|
|
28
|
-
if (!shouldRunHook('frontend-slop-hook')) process.exit(0);
|
|
27
|
+
import { isMainModule } from '../scripts/lib/is-main-module.mjs';
|
|
29
28
|
|
|
30
29
|
import path from 'node:path';
|
|
31
30
|
|
|
@@ -180,5 +179,12 @@ async function main() {
|
|
|
180
179
|
}));
|
|
181
180
|
}
|
|
182
181
|
|
|
183
|
-
//
|
|
184
|
-
|
|
182
|
+
// Entry guard (#1393): run only as the node script the harness execs — a bare
|
|
183
|
+
// `import()` must run no handler and must not exit the importing process.
|
|
184
|
+
if (isMainModule(import.meta.url)) {
|
|
185
|
+
// Exit 0 immediately when disabled via SO_HOOK_PROFILE / SO_DISABLED_HOOKS.
|
|
186
|
+
if (!shouldRunHook('frontend-slop-hook')) process.exit(0);
|
|
187
|
+
|
|
188
|
+
// Exit 0 always — informational hook must never block Claude (#684).
|
|
189
|
+
main().catch(() => {}).finally(() => process.exit(0));
|
|
190
|
+
}
|
|
@@ -659,6 +659,20 @@ export function decide(input, lib) {
|
|
|
659
659
|
}
|
|
660
660
|
const tail = omitted > 0 ? `\n(+${omitted} weitere Befund(e) nicht gezeigt)` : '';
|
|
661
661
|
|
|
662
|
+
// Die Optionsgrenze wird aus der Schwelle GERENDERT, nie danebengeschrieben.
|
|
663
|
+
// Ausgeschriebene Zahlwörter ("zwei bis vier") standen hier bis 2026-09-18
|
|
664
|
+
// und waren für jeden Zahlen-grep unsichtbar: verschiebt sich
|
|
665
|
+
// `THRESHOLDS.K6.optionsMax`, blockt der Hook nach der neuen Zahl und nennt
|
|
666
|
+
// dem Operator die alte. Fehlt die Schwelle (defektes Schema-Modul), entfällt
|
|
667
|
+
// der Satz — eine Lücke ist besser als eine erfundene Zahl.
|
|
668
|
+
const k6Limits = lib?.schema?.THRESHOLDS?.K6;
|
|
669
|
+
const limitSentence =
|
|
670
|
+
Number.isInteger(k6Limits?.optionsMin) && Number.isInteger(k6Limits?.optionsMax)
|
|
671
|
+
? `Das ist keine Stilfrage: das Tool selbst nimmt nur ${k6Limits.optionsMin} bis ` +
|
|
672
|
+
`${k6Limits.optionsMax} Optionen je Frage an, und mehr als ${k6Limits.optionsMax} kann ` +
|
|
673
|
+
'niemand gegeneinander abwägen.\n\n'
|
|
674
|
+
: '';
|
|
675
|
+
|
|
662
676
|
// The FIRST LINE names the concrete break, because `emitDeny` derives the
|
|
663
677
|
// operator-visible headline from it. A general preamble first would put a
|
|
664
678
|
// sentence that is identical for every deny in front of the human, and the
|
|
@@ -666,8 +680,7 @@ export function decide(input, lib) {
|
|
|
666
680
|
const reason =
|
|
667
681
|
`AskUserQuestion blockiert: harte Grenze ${groups.map(([id]) => id).join(' + ')} gerissen — ` +
|
|
668
682
|
'so erreicht die Frage den Operator nicht.\n\n' +
|
|
669
|
-
|
|
670
|
-
'und mehr als vier kann niemand gegeneinander abwägen.\n\n' +
|
|
683
|
+
limitSentence +
|
|
671
684
|
`${sections.join('\n\n')}${tail}`;
|
|
672
685
|
|
|
673
686
|
const suggestion =
|
|
@@ -778,6 +791,9 @@ async function main() {
|
|
|
778
791
|
// under a symlinked plugin install) while `import.meta.url` is realpath-resolved
|
|
779
792
|
// by node's default loader, so BOTH sides are realpath'd.
|
|
780
793
|
// ---------------------------------------------------------------------------
|
|
794
|
+
// BV-004 ceiling: kept inline — hooks avoid importing scripts/lib on the hot
|
|
795
|
+
// path; the canonical predicate is scripts/lib/is-main-module.mjs. Revisit if the
|
|
796
|
+
// hook-import-set (hooks/_lib/hook-import-set.json) ever admits scripts/lib here.
|
|
781
797
|
function invokedAsScript() {
|
|
782
798
|
const entry = process.argv[1];
|
|
783
799
|
if (!entry) return false;
|
|
@@ -29,6 +29,12 @@
|
|
|
29
29
|
* generic pattern path — its `pattern: ">"` would FP-match nearly
|
|
30
30
|
* every redirect. Unresolved targets (variable/substitution) warn
|
|
31
31
|
* on stderr (fail-visible) and never block.
|
|
32
|
+
* rule.type === 'path-delete' (#1401): decided by the DELETE/RENAME
|
|
33
|
+
* operand via firstDeniedDeleteTarget (same target-denylist globs,
|
|
34
|
+
* same compiler), likewise shadowing the generic pattern path. The
|
|
35
|
+
* redirect class answers "what does this command WRITE"; nothing
|
|
36
|
+
* answered "what does it REMOVE", so `rm -f <ledger>` was allowed
|
|
37
|
+
* while `> <ledger>` was denied — 53,896 lines of telemetry lost.
|
|
32
38
|
* G6 no match → exit 0
|
|
33
39
|
*
|
|
34
40
|
* Telemetry (Epic #803 process-safety dimension): best-effort
|
|
@@ -46,8 +52,7 @@ import crypto from 'node:crypto';
|
|
|
46
52
|
import { pathToFileURL } from 'node:url';
|
|
47
53
|
|
|
48
54
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
49
|
-
|
|
50
|
-
if (!shouldRunHook('pre-bash-destructive-guard')) process.exit(0);
|
|
55
|
+
import { isMainModule } from '../scripts/lib/is-main-module.mjs';
|
|
51
56
|
|
|
52
57
|
// ---------------------------------------------------------------------------
|
|
53
58
|
// #992 — late-bound repo dependencies
|
|
@@ -179,6 +184,8 @@ async function bootstrap() {
|
|
|
179
184
|
'redirectSpanEnd',
|
|
180
185
|
'resolveSegmentVerb',
|
|
181
186
|
'splitChainSegments',
|
|
187
|
+
'extractDeleteTargets',
|
|
188
|
+
'firstDeniedDeleteTarget',
|
|
182
189
|
],
|
|
183
190
|
},
|
|
184
191
|
},
|
|
@@ -877,6 +884,61 @@ async function main() {
|
|
|
877
884
|
continue; // unreachable (blockCommand never returns) — kept for clarity
|
|
878
885
|
}
|
|
879
886
|
|
|
887
|
+
// #1401 — path-delete rules are decided by the DELETE/RENAME operand via
|
|
888
|
+
// firstDeniedDeleteTarget, never by the generic pattern path below (same
|
|
889
|
+
// full-shadow contract as redirect-truncate above: this rule's `pattern` is
|
|
890
|
+
// a human-readable label, and routing it through commandMatchesBlocked
|
|
891
|
+
// would match either nothing or every `rm`).
|
|
892
|
+
//
|
|
893
|
+
// Why a second surface at all: the redirect rule answers "what does this
|
|
894
|
+
// command WRITE". Nothing answered "what does it REMOVE" — so `rm -f
|
|
895
|
+
// .orchestrator/metrics/events.jsonl.1` was ALLOWED while `> ` on the same
|
|
896
|
+
// file was DENIED (measured 2026-09-19, #1401: 53,896 ledger lines lost).
|
|
897
|
+
if (rule.type === 'path-delete') {
|
|
898
|
+
const collected = blocker.extractDeleteTargets(command);
|
|
899
|
+
const cutOff = collected.filter(
|
|
900
|
+
(e) => e.unresolved && (e.reason === 'budget-exhausted' || e.reason === 'depth-exceeded')
|
|
901
|
+
);
|
|
902
|
+
if (cutOff.length > 0) {
|
|
903
|
+
// A recursion cap that drops payloads silently is a bypass, not a cap
|
|
904
|
+
// (#988 T2) — surface it on the VISIBLE channel (#995).
|
|
905
|
+
//
|
|
906
|
+
// Deliberately NARROWER than the redirect branch above, which also
|
|
907
|
+
// surfaces `variable/substitution` operands: `rm -rf "$WORK"` is the
|
|
908
|
+
// ORDINARY spelling of a temp cleanup, so notifying on it would fire on
|
|
909
|
+
// a large share of benign commands and train the operator to ignore the
|
|
910
|
+
// channel (host-resources.md § HR-101 — a signal may only warn if it is
|
|
911
|
+
// rare). A cut-off marker, by contrast, needs 32+ payload segments or
|
|
912
|
+
// 3 levels of nesting: pathological by construction. Unresolved
|
|
913
|
+
// operands stay fail-visible on stderr below; neither class ever blocks.
|
|
914
|
+
const reasons = [...new Set(cutOff.map((e) => e.reason))].join(', ');
|
|
915
|
+
const msg = `pre-bash-destructive-guard: delete-target traversal cut off (${reasons}) — not matched (fail-visible)`;
|
|
916
|
+
process.stderr.write(`⚠ ${msg}\n`);
|
|
917
|
+
notices.push(msg);
|
|
918
|
+
}
|
|
919
|
+
const unresolvedOperands = collected.filter(
|
|
920
|
+
(e) => e.unresolved && e.reason === 'variable/substitution'
|
|
921
|
+
);
|
|
922
|
+
if (unresolvedOperands.length > 0) {
|
|
923
|
+
process.stderr.write(
|
|
924
|
+
`ℹ pre-bash-destructive-guard: ${unresolvedOperands.length} delete target(s) carry a variable/substitution — not matched (rule: ${id})\n`
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
const hit = blocker.firstDeniedDeleteTarget(rule, collected, { repoRoot: projectDir });
|
|
928
|
+
if (!hit) continue;
|
|
929
|
+
if (severity !== 'block') {
|
|
930
|
+
const msg = `pre-bash-destructive-guard: protected delete target matched (rule: ${id}) — ${rationale}`;
|
|
931
|
+
process.stderr.write(`⚠ ${msg}\n`);
|
|
932
|
+
notices.push(msg);
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
935
|
+
// Reason stays short (stdout-budget): verb + target, never the command.
|
|
936
|
+
// The policy `rationale` is long by design (it carries the measurement
|
|
937
|
+
// trail); blockCommand puts only its first line in the systemMessage.
|
|
938
|
+
await blockCommand(`${hit.verb} ${hit.target}`, id, rationale, command, sessionId);
|
|
939
|
+
continue; // unreachable (blockCommand never returns) — kept for clarity
|
|
940
|
+
}
|
|
941
|
+
|
|
880
942
|
// The rm-rf-destructive rule also fires for recursive+force rm flag variants
|
|
881
943
|
// the literal "rm -rf" pattern misses (`rm -r -f`, `rm -fr`) — #641 gap closure.
|
|
882
944
|
const matched = id === 'rm-rf-destructive'
|
|
@@ -984,10 +1046,19 @@ try {
|
|
|
984
1046
|
process.exit(0); // fail-open, but no longer fail-silent
|
|
985
1047
|
}
|
|
986
1048
|
|
|
987
|
-
//
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1049
|
+
// Entry guard (#1393): run only as the node script the harness execs — a bare
|
|
1050
|
+
// `import()` must run no handler and must not exit the importing process.
|
|
1051
|
+
// Placed strictly AFTER all decision logic: it gates whether main() is invoked,
|
|
1052
|
+
// never what main() decides, so no deny path can be short-circuited by it.
|
|
1053
|
+
if (isMainModule(import.meta.url)) {
|
|
1054
|
+
// #211: exit 0 immediately (silent allow) when this hook is disabled via profile/env
|
|
1055
|
+
if (!shouldRunHook('pre-bash-destructive-guard')) process.exit(0);
|
|
1056
|
+
|
|
1057
|
+
// Top-level error handler — never let exit 1 leak
|
|
1058
|
+
main().catch((e) => {
|
|
1059
|
+
process.stderr.write(
|
|
1060
|
+
`⚠ pre-bash-destructive-guard: internal error — ${e?.message || e}\n`
|
|
1061
|
+
);
|
|
1062
|
+
process.exit(0); // fail-open on internal errors to avoid blocking legitimate work
|
|
1063
|
+
});
|
|
1064
|
+
}
|