session-orchestrator 5.2.0 → 5.4.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 +5 -1
- package/.agents/skills/autopilot/agents/openai.yaml +5 -0
- package/.agents/skills/bootstrap/SKILL.md +5 -1
- package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
- package/.agents/skills/brainstorm/SKILL.md +5 -1
- package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +4 -2
- package/.agents/skills/close/SKILL.md +5 -1
- package/.agents/skills/close/agents/openai.yaml +5 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
- package/.agents/skills/debug/SKILL.md +5 -1
- package/.agents/skills/debug/agents/openai.yaml +5 -0
- package/.agents/skills/discovery/SKILL.md +5 -1
- package/.agents/skills/discovery/agents/openai.yaml +5 -0
- package/.agents/skills/dispatcher/SKILL.md +5 -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 +5 -1
- package/.agents/skills/eli5/agents/openai.yaml +5 -0
- package/.agents/skills/eval/SKILL.md +6 -2
- package/.agents/skills/eval/agents/openai.yaml +5 -0
- package/.agents/skills/evolve/SKILL.md +6 -2
- 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 +5 -1
- package/.agents/skills/go/agents/openai.yaml +5 -0
- package/.agents/skills/grill/SKILL.md +5 -1
- package/.agents/skills/grill/agents/openai.yaml +5 -0
- package/.agents/skills/harness-audit/SKILL.md +5 -1
- 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 +5 -1
- package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
- package/.agents/skills/mode-selector/SKILL.md +3 -1
- package/.agents/skills/navigator/SKILL.md +22 -0
- package/.agents/skills/navigator/agents/openai.yaml +5 -0
- 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 +5 -1
- package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
- package/.agents/skills/plan/SKILL.md +5 -1
- package/.agents/skills/plan/agents/openai.yaml +5 -0
- package/.agents/skills/playwright-driver/SKILL.md +3 -1
- package/.agents/skills/portfolio/SKILL.md +5 -1
- package/.agents/skills/portfolio/agents/openai.yaml +5 -0
- package/.agents/skills/quality-gates/SKILL.md +3 -1
- package/.agents/skills/reconcile/SKILL.md +5 -1
- package/.agents/skills/reconcile/agents/openai.yaml +5 -0
- package/.agents/skills/release/SKILL.md +5 -1
- 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 +5 -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 +5 -1
- package/.agents/skills/spinout/agents/openai.yaml +5 -0
- package/.agents/skills/sunset-review/SKILL.md +5 -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 +5 -1
- package/.agents/skills/test/agents/openai.yaml +5 -0
- package/.agents/skills/test-audit/SKILL.md +21 -0
- package/.agents/skills/test-audit/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 +5 -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/claude-md-drift-check/SKILL.md +1 -1
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -3
- package/.codex-plugin/skills/eval/SKILL.md +1 -1
- package/.codex-plugin/skills/evolve/SKILL.md +1 -1
- package/.codex-plugin/skills/navigator/SKILL.md +22 -0
- package/.codex-plugin/skills/{convergence-monitoring → navigator}/agents/openai.yaml +2 -2
- package/.codex-plugin/skills/npm-publish/SKILL.md +1 -3
- package/.codex-plugin/skills/session/SKILL.md +1 -1
- package/.codex-plugin/skills/test-audit/SKILL.md +21 -0
- package/.codex-plugin/skills/{npm-publish → test-audit}/agents/openai.yaml +1 -1
- package/.cursor/commands/eval.md +1 -1
- package/.cursor/commands/navigator.md +15 -0
- package/.cursor/commands/session.md +1 -1
- package/.cursor/commands/test-audit.md +14 -0
- package/.cursor/rules/000-session-orchestrator.mdc +0 -2
- package/.cursor/rules/050-plan.mdc +1 -1
- package/.cursor/rules/060-evolve.mdc +27 -12
- package/.cursor/skills/claude-md-drift-check/SKILL.md +1 -1
- package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/navigator/SKILL.md +13 -0
- package/.cursor/skills/npm-publish/SKILL.md +1 -0
- package/.cursor/skills/test-audit/SKILL.md +12 -0
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +12 -3
- package/AGENTS.md +9 -30
- package/CHANGELOG.md +206 -0
- package/README.md +13 -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 +7 -3
- package/docs/agent-authoring.md +2 -2
- package/docs/baseline.md +22 -7
- package/docs/ci-setup.md +57 -4
- package/docs/codex-setup.md +14 -2
- package/docs/components.md +12 -10
- package/docs/events-schema.md +65 -15
- package/docs/persona-panel.md +5 -4
- package/docs/rule-authoring.md +84 -17
- package/docs/scope-collision-guard.md +26 -4
- package/docs/session-config-reference.md +154 -12
- package/docs/session-config-template.md +41 -3
- package/docs/telemetry.md +2 -0
- package/hooks/_lib/hook-import-set.json +87 -6
- package/hooks/_lib/subagent-transcript.mjs +582 -31
- 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-cursor.json +5 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +11 -3
- package/hooks/on-session-end.mjs +58 -23
- package/hooks/on-session-start.mjs +101 -11
- package/hooks/on-stop.mjs +170 -22
- package/hooks/operator-steer.mjs +11 -3
- package/hooks/post-bash-issue-budget-refund.mjs +18 -8
- package/hooks/post-bash-write-verify.mjs +3 -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 +202 -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 +15 -2
- package/hooks/pre-bash-destructive-guard.mjs +80 -9
- package/hooks/pre-bash-issue-budget.mjs +16 -11
- package/hooks/pre-bash-memory-propose-audit.mjs +137 -56
- package/hooks/pre-bash-sessions-ledger-guard.mjs +419 -22
- package/hooks/pre-bash-staging-fence.mjs +335 -31
- package/hooks/pre-bash-templates-first.mjs +19 -14
- package/hooks/pre-edit-sessions-ledger-guard.mjs +162 -0
- package/hooks/pre-task-scope-disjoint.mjs +792 -74
- package/hooks/subagent-telemetry.mjs +111 -38
- 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 +3 -3
- package/pi/prompts/eval.md +1 -1
- package/pi/prompts/navigator.md +12 -0
- package/pi/prompts/session.md +1 -1
- package/pi/prompts/test-audit.md +12 -0
- package/rules/README.md +7 -2
- package/rules/_index.md +3 -3
- package/rules/always-on/bash-harness-pitfalls.md +7 -1
- package/rules/opt-in-domain/prompt-caching.md +6 -2
- package/rules/opt-in-stack/backend-data.md +10 -2
- package/rules/opt-in-stack/backend.md +10 -4
- package/rules/opt-in-stack/frontend.md +7 -2
- package/rules/opt-in-stack/security-web.md +10 -4
- package/rules/opt-in-stack/swift.md +6 -2
- package/scripts/apply-session-learnings.mjs +492 -0
- package/scripts/archive-closed-prds.mjs +2 -1
- package/scripts/autopilot.mjs +70 -53
- package/scripts/backfill-abandoned-sessions.mjs +118 -16
- package/scripts/baseline-archetypes.mjs +1 -1
- package/scripts/check-package-manager.mjs +62 -5
- package/scripts/check-sessions-integrity.mjs +300 -0
- package/scripts/cursor-install.mjs +1 -4
- package/scripts/dialectic-deriver.mjs +50 -13
- package/scripts/emit-session.mjs +371 -31
- package/scripts/eval-session.mjs +65 -3
- package/scripts/export-hw-learnings.mjs +22 -3
- package/scripts/fleet-checkin.mjs +147 -0
- package/scripts/generate-agents-skills.mjs +102 -29
- package/scripts/generate-cursor-adapter.mjs +61 -16
- package/scripts/lib/agent-frontmatter.mjs +3 -3
- 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/auto-dream.mjs +7 -6
- package/scripts/lib/autopilot/flags.mjs +18 -2
- package/scripts/lib/autopilot/kill-switches.mjs +6 -3
- package/scripts/lib/autopilot/loop.mjs +19 -2
- package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
- package/scripts/lib/baseline-archetypes.mjs +41 -22
- package/scripts/lib/ci-status-banner.mjs +376 -16
- package/scripts/lib/command-blocker.mjs +275 -28
- package/scripts/lib/config/dialectic.mjs +12 -3
- package/scripts/lib/config/gate.mjs +74 -0
- package/scripts/lib/config/host-paths.mjs +79 -0
- package/scripts/lib/config/persona-gate-wave.mjs +3 -3
- package/scripts/lib/config/reaper.mjs +163 -0
- package/scripts/lib/config-schema.mjs +13 -0
- package/scripts/lib/config.mjs +85 -10
- package/scripts/lib/convergence-monitor.mjs +74 -11
- package/scripts/lib/ecosystem-health.mjs +11 -0
- 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 +545 -7
- package/scripts/lib/fleet-protocol.mjs +270 -0
- package/scripts/lib/frontmatter-guard.mjs +21 -10
- package/scripts/lib/gates/gate-baseline.mjs +40 -5
- package/scripts/lib/gates/gate-full.mjs +35 -5
- package/scripts/lib/gates/gate-helpers.mjs +336 -23
- package/scripts/lib/gates/gate-incremental.mjs +49 -23
- package/scripts/lib/gates/gate-per-file.mjs +42 -8
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
- package/scripts/lib/instruction-budget-guard.mjs +208 -8
- package/scripts/lib/io.mjs +42 -8
- 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/learnings/expiry-sweep.mjs +34 -3
- package/scripts/lib/learnings/io.mjs +154 -26
- package/scripts/lib/learnings/schema.mjs +163 -15
- package/scripts/lib/learnings/sizing-subject.mjs +2 -2
- package/scripts/lib/maintenance-due-banner.mjs +53 -88
- package/scripts/lib/memory-proposals/schema.mjs +11 -5
- package/scripts/lib/named-baseline-resolver.mjs +53 -28
- package/scripts/lib/named-vault-resolver.mjs +104 -5
- package/scripts/lib/orphan-reaper/defaults.mjs +41 -0
- package/scripts/lib/orphan-reaper/ps-snapshot.mjs +181 -0
- package/scripts/lib/orphan-reaper/reaper-audit.mjs +291 -0
- package/scripts/lib/orphan-reaper/reaper-decide.mjs +395 -0
- package/scripts/lib/orphan-reaper/scan-throttle.mjs +92 -0
- package/scripts/lib/orphan-reaper/scan.mjs +506 -0
- package/scripts/lib/orphan-reaper.mjs +316 -0
- package/scripts/lib/owner-yaml.mjs +104 -6
- package/scripts/lib/peer-cards/merger.mjs +48 -10
- package/scripts/lib/peer-cards/reader.mjs +78 -2
- package/scripts/lib/persona-panel/catalog-loader.mjs +1 -1
- package/scripts/lib/process-group.mjs +950 -0
- package/scripts/lib/project-hygiene.mjs +21 -1
- package/scripts/lib/quality-gate.mjs +152 -33
- package/scripts/lib/reconcile/backlog.mjs +368 -0
- package/scripts/lib/reconcile/engine.mjs +55 -188
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +302 -60
- 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/resource-probe.mjs +1 -1
- package/scripts/lib/rule-loader.mjs +141 -74
- package/scripts/lib/rules-sync.mjs +27 -12
- package/scripts/lib/scope-baseline.mjs +3 -4
- package/scripts/lib/scope-echo.mjs +39 -2
- package/scripts/lib/scope-gate.mjs +605 -1
- package/scripts/lib/session-close-backfill.mjs +33 -6
- package/scripts/lib/session-id.mjs +9 -20
- package/scripts/lib/session-invocation.mjs +20 -0
- package/scripts/lib/session-record-repair.mjs +183 -26
- package/scripts/lib/session-registry.mjs +27 -2
- package/scripts/lib/session-schema/constants.mjs +56 -2
- package/scripts/lib/session-schema/normalizer.mjs +56 -4
- package/scripts/lib/session-schema/validator.mjs +28 -1
- package/scripts/lib/session-schema.mjs +8 -3
- package/scripts/lib/session-start-probes.mjs +95 -10
- package/scripts/lib/session-token-rollup.mjs +245 -52
- package/scripts/lib/sessions-canonical.mjs +118 -4
- package/scripts/lib/sessions-integrity-banner.mjs +7 -1
- package/scripts/lib/sessions-staleness-banner.mjs +193 -51
- package/scripts/lib/sh-quote.mjs +29 -0
- 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/subagents-schema.mjs +30 -3
- package/scripts/lib/tail-window.mjs +56 -0
- package/scripts/lib/telemetry/pricing.mjs +118 -71
- 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 +140 -23
- package/scripts/lib/tmux-layout/layouts.mjs +1 -11
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +72 -13
- package/scripts/lib/tmux-layout/tmux-shell.mjs +2 -11
- package/scripts/lib/user-invocable-skills.mjs +23 -3
- package/scripts/lib/ux-grill/reconcile.mjs +48 -22
- package/scripts/lib/validate/check-agents-skills.mjs +26 -15
- package/scripts/lib/validate/check-agents.mjs +1 -1
- package/scripts/lib/validate/check-cursor-adapter.mjs +1 -0
- package/scripts/lib/validate/check-entry-guard.mjs +13 -50
- package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +5 -0
- package/scripts/lib/validate/check-learnings-shell-writes.mjs +214 -0
- 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 +9 -1
- package/scripts/lib/validate/check-skill-script-paths.mjs +239 -27
- package/scripts/lib/validate/check-test-git-config-target.mjs +24 -34
- package/scripts/lib/validate/check-test-value-bans.mjs +87 -14
- package/scripts/lib/validate/check-unicode-safety.mjs +1 -0
- package/scripts/lib/validate/check-untracked-test-deps.mjs +7 -102
- package/scripts/lib/validate/check-unwired-features.mjs +130 -27
- package/scripts/lib/validate/check-validator-registration.mjs +34 -10
- package/scripts/lib/validate/confidential-names.mjs +10 -0
- package/scripts/lib/validate-vendored-rules.mjs +210 -84
- 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-mirror/telemetry.mjs +8 -1
- package/scripts/lib/vault-status/board-writer.mjs +48 -11
- package/scripts/lib/vault-status/narrative-mirror.mjs +71 -16
- package/scripts/lib/vault-yaml.mjs +118 -0
- package/scripts/lib/wave-executor/dispatch-common.mjs +10 -18
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +1 -1
- package/scripts/lib/wave-executor/remote-dispatch.mjs +1 -1
- package/scripts/lib/wave-resource-gate.mjs +34 -8
- package/scripts/lib/worktree/lifecycle.mjs +153 -1
- package/scripts/memory-propose.mjs +18 -5
- package/scripts/release-session-lock.mjs +306 -0
- package/scripts/release.mjs +40 -8
- package/scripts/repair-invalid-sessions.mjs +37 -4
- package/scripts/resolve-session-invocation.mjs +59 -0
- package/scripts/run-migrate-v2-cross-repo.mjs +158 -30
- package/scripts/run-quality-gate.mjs +357 -197
- package/scripts/site-numbers.mjs +163 -5
- package/scripts/sweep-expired-learnings.mjs +56 -5
- package/scripts/sweep-expired-rules.mjs +16 -4
- package/scripts/validate-plugin.mjs +19 -0
- package/scripts/validate-wave-scope.mjs +32 -105
- package/scripts/vault-integration-watcher.mjs +2 -1
- package/scripts/vault-mirror.mjs +81 -47
- package/scripts/wave-scope-binding.mjs +2 -1
- package/skills/_shared/config-reading.md +3 -3
- package/skills/_shared/fleet-protocol.md +131 -0
- package/skills/_shared/platform-tools.md +23 -11
- package/skills/_shared/state-ownership.md +5 -0
- package/skills/autopilot/SKILL.md +47 -18
- package/skills/bootstrap/SKILL.md +1 -1
- package/skills/bootstrap/_shared-template.md +1 -1
- package/skills/bootstrap/fast-template.md +1 -1
- package/skills/bootstrap/private-contract.md +2 -1
- package/skills/bootstrap/public-fallback.md +6 -3
- package/skills/bootstrap/references/bootstrap-retroactive-flow.md +2 -1
- package/skills/claude-md-drift-check/SKILL.md +10 -10
- package/skills/claude-md-drift-check/checker.mjs +26 -18
- 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/eval/SKILL.md +39 -24
- package/skills/eval/rubric-v1.md +1 -0
- package/skills/eval/rubric-v2.md +457 -0
- package/skills/evolve/SKILL.md +3 -3
- package/skills/evolve/references/evolve-analyze-mode.md +10 -6
- package/skills/evolve/references/evolve-dialectic-mode.md +42 -25
- package/skills/gitlab-ops/SKILL.md +3 -2
- package/skills/memory-cleanup/SKILL.md +1 -1
- package/skills/navigator/SKILL.md +193 -0
- package/skills/navigator/references/account-check.sh +87 -0
- package/skills/navigator/references/fleet-decisions.mjs +231 -0
- package/skills/navigator/references/slot-check.sh +114 -0
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/persona-panel/SKILL.md +2 -2
- package/skills/persona-panel/persona-format.md +2 -2
- package/skills/persona-panel/presets/designer-lens.md +1 -1
- package/skills/persona-panel/presets/engineer-lens.md +1 -1
- package/skills/persona-panel/presets/pm-lens.md +1 -1
- package/skills/plan/mode-retro.md +19 -4
- package/skills/quality-gates/SKILL.md +6 -4
- package/skills/reconcile/SKILL.md +11 -0
- package/skills/remote-offload/SKILL.md +4 -2
- package/skills/session-end/SKILL.md +13 -16
- package/skills/session-end/discovery-scan.md +1 -1
- package/skills/session-end/learning-patterns.md +22 -12
- package/skills/session-end/metrics-collection.md +5 -6
- package/skills/session-end/phase-3-6-tail.md +55 -9
- package/skills/session-end/references/phase-3-documentation-updates.md +2 -2
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +2 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
- package/skills/session-end/references/session-summary-template.md +1 -1
- package/skills/session-end/session-metrics-write.md +32 -60
- package/skills/session-plan/SKILL.md +17 -8
- package/skills/session-plan/references/session-plan-task-classification.md +2 -2
- package/skills/session-start/SKILL.md +6 -0
- package/skills/session-start/references/operations-contract.md +4 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +1 -1
- package/skills/session-start/references/phase-4-ssot-environment-check.md +4 -3
- package/skills/test-audit/SKILL.md +152 -0
- package/skills/test-audit/references/campaign.md +111 -0
- package/skills/test-audit/references/mutate.sh +50 -0
- package/skills/ux-grill/SKILL.md +1 -1
- package/skills/wave-executor/SKILL.md +9 -5
- package/skills/wave-executor/circuit-breaker.md +2 -0
- package/skills/wave-executor/references/wave-executor-state-init.md +8 -3
- package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
- package/skills/wave-executor/references/wave-loop-review.md +3 -5
- package/templates/personas/README.md +5 -5
- package/templates/personas/accounting-compliance.v1.md +1 -1
- package/templates/personas/accounting-tax-advisor.v1.md +1 -1
- package/templates/personas/buyer-p1-cto.v1.md +1 -1
- package/templates/personas/buyer-p2-kanzlei.v1.md +1 -1
- package/templates/personas/buyer-p3-build.v1.md +1 -1
- package/templates/personas/buyer-p4-tech-veto.v1.md +1 -1
- package/templates/personas/buyer-p5-solo.v1.md +1 -1
- package/templates/personas/buyer-p6-ld.v1.md +1 -1
- package/templates/personas/klima-ai-expert.v1.md +1 -1
- package/templates/personas/klima-physicist.v1.md +1 -1
- package/.cursor/commands/convergence-monitoring.md +0 -13
- package/.cursor/commands/npm-publish.md +0 -13
- package/pi/prompts/convergence-monitoring.md +0 -11
- package/pi/prompts/npm-publish.md +0 -11
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { matchBlockHeader } from './block-header.mjs';
|
|
2
|
+
import { preprocessBlockLines } from './block-preprocess.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* reaper.mjs — Parser for the top-level `reaper:` YAML block
|
|
6
|
+
* (PRD `docs/prd/2026-09-20-prozessgruppen-kill-und-waisen-waechter.md` §4,
|
|
7
|
+
* Epic #1425 / issue #1432 B4).
|
|
8
|
+
*
|
|
9
|
+
* Configures the orphan watchdog that `hooks/post-tool-batch-wave-signal.mjs`
|
|
10
|
+
* (PostToolBatch) and `hooks/on-stop.mjs` (Stop + SubagentStop) trigger as a
|
|
11
|
+
* detached fire-and-forget scan. The scan itself lives in
|
|
12
|
+
* `scripts/lib/orphan-reaper.mjs`; this module only reads the numbers.
|
|
13
|
+
*
|
|
14
|
+
* DEFAULT OFF. `enabled` defaults to `false` — unlike `loop-guard`, this signal
|
|
15
|
+
* can SEND SIGNALS to processes, so it ships inert and is armed per repo only
|
|
16
|
+
* after its firing rate has been measured (`.claude/rules/host-resources.md`
|
|
17
|
+
* HR-101: a class above ~10 % is a broken instrument; HR-105: a rule nothing
|
|
18
|
+
* records is unfalsifiable).
|
|
19
|
+
*
|
|
20
|
+
* Tolerant parser: malformed values silently fall back to defaults.
|
|
21
|
+
*
|
|
22
|
+
* Returns `{ enabled, mode, 'min-age-seconds', 'min-scan-interval-seconds',
|
|
23
|
+
* 'kill-grace-ms', 'verify-wait-ms', 'max-hook-latency-ms', 'false-alarm-window' }`.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Startwerte für Stufe 1 — NOT calibrated. Every number carries its provenance
|
|
28
|
+
* from the PRD §4 parameter table ("keine Zahl ohne ihre Population").
|
|
29
|
+
*
|
|
30
|
+
* RUNTIME SSOT is `REAPER_DEFAULTS` in `scripts/lib/orphan-reaper/defaults.mjs`; these
|
|
31
|
+
* literals mirror it rather than importing it. The original reason — the
|
|
32
|
+
* single-file `orphan-reaper.mjs` pulled `process-group.mjs` +
|
|
33
|
+
* `resource-probe/parsers.mjs` into the config import graph — no longer holds
|
|
34
|
+
* since the #1437 split (`defaults.mjs` imports only `node:path`); revisit the
|
|
35
|
+
* mirror there. The parity is held by a test, not by an import
|
|
36
|
+
* (`tests/lib/config/reaper.test.mjs` § defaults mirror REAPER_DEFAULTS).
|
|
37
|
+
*/
|
|
38
|
+
/** 300 s — DevWatchdog's hard limit for `tsgo`; the 2026-09-20 orphans were 7–17 min old. */
|
|
39
|
+
const DEFAULT_MIN_AGE_SECONDS = 300;
|
|
40
|
+
/** 30 s — DevWatchdog's normal scan cadence; keeps a PostToolBatch storm from taxing every tool call. */
|
|
41
|
+
const DEFAULT_MIN_SCAN_INTERVAL_SECONDS = 30;
|
|
42
|
+
/** 10 000 ms — `DEFAULT_KILL_GRACE_MS` from `process-group.mjs`; repo convention, not newly invented. */
|
|
43
|
+
const DEFAULT_KILL_GRACE_MS = 10_000;
|
|
44
|
+
/** 500 ms — wait before reading the effect back; without it the 2026-09-20 check falsely reported "still alive". */
|
|
45
|
+
const DEFAULT_VERIFY_WAIT_MS = 500;
|
|
46
|
+
/** 50 ms — ceiling a scan may delay a hook by; above that the scan counts as too expensive. */
|
|
47
|
+
const DEFAULT_MAX_HOOK_LATENCY_MS = 50;
|
|
48
|
+
/** 50 decisions — rolling window over the JSONL audit (B5), so the rate has a population on quiet hosts too. */
|
|
49
|
+
const DEFAULT_FALSE_ALARM_WINDOW = 50;
|
|
50
|
+
|
|
51
|
+
/** `report` = decide + audit only (dryRun); `kill` = send signals (PRD Stufe 2, after calibration). */
|
|
52
|
+
const MODE_VALUES = new Set(['report', 'kill']);
|
|
53
|
+
const DEFAULT_MODE = 'report';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Coerce a raw string into a finite integer >= `min`, else `fallback`.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} raw
|
|
59
|
+
* @param {number} fallback
|
|
60
|
+
* @param {number} min
|
|
61
|
+
* @returns {number}
|
|
62
|
+
*/
|
|
63
|
+
function parseBoundedInt(raw, fallback, min) {
|
|
64
|
+
if (!/^-?\d+$/.test(raw)) return fallback;
|
|
65
|
+
const n = Number.parseInt(raw, 10);
|
|
66
|
+
if (!Number.isFinite(n) || n < min) return fallback;
|
|
67
|
+
return n;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Parse the top-level `reaper:` YAML block from markdown content.
|
|
72
|
+
* Independent of the `## Session Config` section boundary.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} content — full file contents
|
|
75
|
+
* @returns {{enabled: boolean, mode: string, 'min-age-seconds': number,
|
|
76
|
+
* 'min-scan-interval-seconds': number, 'kill-grace-ms': number,
|
|
77
|
+
* 'verify-wait-ms': number, 'max-hook-latency-ms': number,
|
|
78
|
+
* 'false-alarm-window': number}}
|
|
79
|
+
*/
|
|
80
|
+
export function _parseReaper(content) {
|
|
81
|
+
let enabled = false;
|
|
82
|
+
let mode = DEFAULT_MODE;
|
|
83
|
+
let minAgeSeconds = DEFAULT_MIN_AGE_SECONDS;
|
|
84
|
+
let minScanIntervalSeconds = DEFAULT_MIN_SCAN_INTERVAL_SECONDS;
|
|
85
|
+
let killGraceMs = DEFAULT_KILL_GRACE_MS;
|
|
86
|
+
let verifyWaitMs = DEFAULT_VERIFY_WAIT_MS;
|
|
87
|
+
let maxHookLatencyMs = DEFAULT_MAX_HOOK_LATENCY_MS;
|
|
88
|
+
let falseAlarmWindow = DEFAULT_FALSE_ALARM_WINDOW;
|
|
89
|
+
|
|
90
|
+
const lines = preprocessBlockLines(content);
|
|
91
|
+
let inBlock = false;
|
|
92
|
+
const blockLines = [];
|
|
93
|
+
|
|
94
|
+
for (const rawLine of lines) {
|
|
95
|
+
const line = rawLine.replace(/\r$/, '');
|
|
96
|
+
if (!inBlock) {
|
|
97
|
+
if (matchBlockHeader(line, 'reaper')) inBlock = true;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (line.length > 0 && !/^\s/.test(line)) break;
|
|
101
|
+
blockLines.push(line);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const rawLine of blockLines) {
|
|
105
|
+
const clean = rawLine.replace(/\s*#.*$/, '').replace(/\s+$/, '');
|
|
106
|
+
if (!clean.trim()) continue;
|
|
107
|
+
|
|
108
|
+
const kvMatch = clean.match(/^\s+([a-zA-Z_-]+):\s*(.*)/);
|
|
109
|
+
if (!kvMatch) continue;
|
|
110
|
+
|
|
111
|
+
const k = kvMatch[1];
|
|
112
|
+
let v = kvMatch[2].trim();
|
|
113
|
+
if (v.startsWith('"') && v.endsWith('"') && v.length >= 2) v = v.slice(1, -1);
|
|
114
|
+
else if (v.startsWith("'") && v.endsWith("'") && v.length >= 2) v = v.slice(1, -1);
|
|
115
|
+
|
|
116
|
+
switch (k) {
|
|
117
|
+
case 'enabled':
|
|
118
|
+
// Default is false → only an explicit "true" arms the watchdog.
|
|
119
|
+
enabled = v.toLowerCase() === 'true';
|
|
120
|
+
break;
|
|
121
|
+
case 'mode':
|
|
122
|
+
// An unknown mode falls back to the INERT one — never to `kill`.
|
|
123
|
+
mode = MODE_VALUES.has(v.toLowerCase()) ? v.toLowerCase() : DEFAULT_MODE;
|
|
124
|
+
break;
|
|
125
|
+
case 'min-age-seconds':
|
|
126
|
+
minAgeSeconds = parseBoundedInt(v, DEFAULT_MIN_AGE_SECONDS, 0);
|
|
127
|
+
break;
|
|
128
|
+
case 'min-scan-interval-seconds':
|
|
129
|
+
minScanIntervalSeconds = parseBoundedInt(v, DEFAULT_MIN_SCAN_INTERVAL_SECONDS, 1);
|
|
130
|
+
break;
|
|
131
|
+
case 'kill-grace-ms':
|
|
132
|
+
killGraceMs = parseBoundedInt(v, DEFAULT_KILL_GRACE_MS, 0);
|
|
133
|
+
break;
|
|
134
|
+
case 'verify-wait-ms':
|
|
135
|
+
verifyWaitMs = parseBoundedInt(v, DEFAULT_VERIFY_WAIT_MS, 0);
|
|
136
|
+
break;
|
|
137
|
+
case 'max-hook-latency-ms':
|
|
138
|
+
maxHookLatencyMs = parseBoundedInt(v, DEFAULT_MAX_HOOK_LATENCY_MS, 1);
|
|
139
|
+
break;
|
|
140
|
+
case 'false-alarm-window':
|
|
141
|
+
falseAlarmWindow = parseBoundedInt(v, DEFAULT_FALSE_ALARM_WINDOW, 1);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Self-healing clamp: a `min-age-seconds` below the scan interval is dead
|
|
147
|
+
// config — a process younger than one scan period can be born and reaped
|
|
148
|
+
// between two scans, so the watchdog would fire on processes it has never
|
|
149
|
+
// seen before. Widen the age floor to at least one scan period (the same
|
|
150
|
+
// shape as loop-guard's window >= threshold clamp).
|
|
151
|
+
if (minAgeSeconds < minScanIntervalSeconds) minAgeSeconds = minScanIntervalSeconds;
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
enabled,
|
|
155
|
+
mode,
|
|
156
|
+
'min-age-seconds': minAgeSeconds,
|
|
157
|
+
'min-scan-interval-seconds': minScanIntervalSeconds,
|
|
158
|
+
'kill-grace-ms': killGraceMs,
|
|
159
|
+
'verify-wait-ms': verifyWaitMs,
|
|
160
|
+
'max-hook-latency-ms': maxHookLatencyMs,
|
|
161
|
+
'false-alarm-window': falseAlarmWindow,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -120,6 +120,19 @@ export function validateSessionConfig(config) {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// #1465 cross-field: offload-first with no declared host can never place a
|
|
124
|
+
// wave anywhere — advisory (warn-level), it never flips `ok`.
|
|
125
|
+
if (config['offload-first'] === true) {
|
|
126
|
+
const hosts = config['remote-hosts'];
|
|
127
|
+
if (!Array.isArray(hosts) || hosts.length === 0) {
|
|
128
|
+
warnings.push({
|
|
129
|
+
path: 'remote-hosts',
|
|
130
|
+
rule: 'offload-first-cross-field',
|
|
131
|
+
message: 'offload-first: true but no remote-hosts declared — every wave stays local',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
123
136
|
if (errors.length > 0) {
|
|
124
137
|
return { ok: false, errors, warnings };
|
|
125
138
|
}
|
package/scripts/lib/config.mjs
CHANGED
|
@@ -65,6 +65,8 @@ import { _parseHealthEndpoints, _parseEcosystemHealthBlockEnabled } from './conf
|
|
|
65
65
|
import { _parseBrokenWindow } from './config/broken-window.mjs';
|
|
66
66
|
import { _parseSlopcheck } from './config/slopcheck.mjs';
|
|
67
67
|
import { _parseDiscoveryValidator } from './config/discovery-validator.mjs';
|
|
68
|
+
import { _parseReaper } from './config/reaper.mjs';
|
|
69
|
+
import { _parseGate } from './config/gate.mjs';
|
|
68
70
|
import { _parseTemplatesFirst } from './config/templates-first.mjs';
|
|
69
71
|
import { _parseVerificationAutoFix } from './config/verification-auto-fix.mjs';
|
|
70
72
|
import { _parseDialectic } from './config/dialectic.mjs';
|
|
@@ -76,8 +78,57 @@ import { _parseRemoteHosts } from './config/remote-hosts.mjs';
|
|
|
76
78
|
import { _parseEvolve, _parseEvolveDecay } from './config/evolve.mjs';
|
|
77
79
|
import { _parseSkillEvolution } from './config/skill-evolution.mjs';
|
|
78
80
|
import { _parseDispatcherAutonomy, resolveDispatcherAutonomy } from './config/dispatcher-autonomy.mjs';
|
|
79
|
-
import { loadHostPaths, resolveHostPath } from './config/host-paths.mjs';
|
|
81
|
+
import { loadHostPaths, resolveHostPath, resolveVaultIntegrationHost } from './config/host-paths.mjs';
|
|
80
82
|
import { resolveNamedBaseline } from './named-baseline-resolver.mjs';
|
|
83
|
+
import { expandTilde } from './common.mjs';
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Resolve `vault-dir` and report which tier produced it (agents/vault#319).
|
|
87
|
+
*
|
|
88
|
+
* Precedence (highest first): SO_VAULT_DIR env > owner.yaml `vault-dirs:`
|
|
89
|
+
* path-prefix match against the cwd > owner.yaml `paths.vault-dir` > committed.
|
|
90
|
+
* The env tier stays on top because tests/setup/vault-guard.mjs relies on it to
|
|
91
|
+
* shadow every host-local vault for the whole suite.
|
|
92
|
+
*
|
|
93
|
+
* Lives here, not in config/host-paths.mjs: host-paths.mjs is a direct CP11
|
|
94
|
+
* helper of check-owner-leakage.mjs, whose standalone vendored copy carries only
|
|
95
|
+
* its three direct helpers. The named-baseline-resolver import edge would make
|
|
96
|
+
* CP11 fail closed there (tests/husky/pre-commit-owner-leakage.test.mjs).
|
|
97
|
+
*
|
|
98
|
+
* The two owner.yaml tiers (`match`, `owner`) come back tilde-expanded: owner.yaml
|
|
99
|
+
* is host-local and `~/…` is the natural way to write a path there, but consumers
|
|
100
|
+
* such as vault-mirror.mjs `existsSync(resolve(vaultDir))` would read a literal
|
|
101
|
+
* `~` as `<cwd>/~/…`. The `env` and `committed` tiers keep their pass-through;
|
|
102
|
+
* vault-mirror.mjs expands its `--vault-dir` at entry for every tier anyway.
|
|
103
|
+
*
|
|
104
|
+
* `cwd` in the ctx is a test-only DI seam; production reads `process.cwd()`.
|
|
105
|
+
*
|
|
106
|
+
* @param {string|null|undefined} committed — value the committed Session Config produced
|
|
107
|
+
* @param {{ env?: Record<string, string|undefined>, ownerConfig?: object, cwd?: string }} [ctx] — from loadHostPaths()
|
|
108
|
+
* @returns {{ value: string|null|undefined, source: 'env'|'match'|'owner'|'committed' }}
|
|
109
|
+
*/
|
|
110
|
+
function resolveVaultDir(committed, { env = process.env, ownerConfig, cwd } = {}) {
|
|
111
|
+
const isNonBlank = (v) => typeof v === 'string' && v.trim() !== '';
|
|
112
|
+
|
|
113
|
+
const envVal = env?.SO_VAULT_DIR;
|
|
114
|
+
if (isNonBlank(envVal)) return { value: envVal, source: 'env' };
|
|
115
|
+
|
|
116
|
+
const matched = resolveNamedBaseline({
|
|
117
|
+
cwd: cwd ?? process.cwd(),
|
|
118
|
+
ownerConfig,
|
|
119
|
+
env,
|
|
120
|
+
section: 'vault-dirs',
|
|
121
|
+
envKey: 'SO_VAULT_DIR',
|
|
122
|
+
});
|
|
123
|
+
if (matched.source === 'match' && isNonBlank(matched.path)) {
|
|
124
|
+
return { value: expandTilde(matched.path), source: 'match' };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const ownerVal = ownerConfig?.paths?.['vault-dir'];
|
|
128
|
+
if (isNonBlank(ownerVal)) return { value: expandTilde(ownerVal), source: 'owner' };
|
|
129
|
+
|
|
130
|
+
return { value: committed, source: 'committed' };
|
|
131
|
+
}
|
|
81
132
|
// express-path lives one level UP from config/ (see its module header for why):
|
|
82
133
|
// the parser is a sibling of config/state-md-lock.mjs in every respect except
|
|
83
134
|
// its directory. Only the pure parser is imported here — `evaluateExpressPath`
|
|
@@ -112,8 +163,9 @@ export { readConfigFile } from './config/io.mjs';
|
|
|
112
163
|
* hermetic ctx (e.g. `{ env: {}, ownerConfig: undefined }`) when asserting COMMITTED values:
|
|
113
164
|
* the default reads the real `owner.yaml`, so a host-local `paths:` override would otherwise
|
|
114
165
|
* bleed into fixture assertions (incident: 2026-07-03 Full-Gate red after the operator set
|
|
115
|
-
* `paths.baseline-path` host-locally). `cwd` feeds the `baselines:`
|
|
116
|
-
* test-only DI seam; production
|
|
166
|
+
* `paths.baseline-path` host-locally). `cwd` feeds the `baselines:` (#819) and
|
|
167
|
+
* `vault-dirs:` (agents/vault#319) match tiers — a test-only DI seam; production
|
|
168
|
+
* always resolves it from `process.cwd()`.
|
|
117
169
|
* @returns {object} config object with EXACT same shape as parse-config.sh stdout
|
|
118
170
|
* @throws if any enum value is invalid
|
|
119
171
|
*/
|
|
@@ -228,6 +280,9 @@ export function parseSessionConfig(mdContent, { hostPaths } = {}) {
|
|
|
228
280
|
// heavy-repo / worktree-cleanup: HR-003 preflight fields (templates/shared/.claude/rules/heavy-repo.md),
|
|
229
281
|
// documented but previously unwired — silently dropped by the parser (baseline issue #60).
|
|
230
282
|
const heavyRepo = _coerceBoolean(kv, 'heavy-repo', false);
|
|
283
|
+
// offload-first (#1465): route offloadable wave roles to a ready remote host
|
|
284
|
+
// even on a `proceed` resource verdict (consumed by wave-resource-gate.mjs).
|
|
285
|
+
const offloadFirst = _coerceBoolean(kv, 'offload-first', false);
|
|
231
286
|
|
|
232
287
|
// List fields
|
|
233
288
|
const crossRepos = _coerceList(kv, 'cross-repos', undefined);
|
|
@@ -329,18 +384,25 @@ export function parseSessionConfig(mdContent, { hostPaths } = {}) {
|
|
|
329
384
|
// vault-integration: parsed from full content (block-scoped, avoids the
|
|
330
385
|
// pre-#593 KV-name collision where `enabled:` was shared with 15+ other
|
|
331
386
|
// blocks like docs-orchestrator/vault-staleness/slopcheck).
|
|
332
|
-
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
|
|
387
|
+
// Host-local overrides — applied here, NOT inside _parseVaultIntegration, so
|
|
388
|
+
// claude-md-drift-check (which calls the parser directly) keeps seeing raw values.
|
|
389
|
+
// - the enabled/mode switch (SO#1448): env SO_VAULT_INTEGRATION > owner.yaml
|
|
390
|
+
// `vault-integration:` > committed, LOWER-ONLY; `host-override` names the tier.
|
|
391
|
+
// - vault-dir (#653, agents/vault#319): env > owner.yaml `vault-dirs:` cwd match >
|
|
392
|
+
// owner.yaml paths.vault-dir > committed; `vault-dir-source` names the tier.
|
|
393
|
+
const vaultIntegration = resolveVaultIntegrationHost(_parseVaultIntegration(mdContent), hostCtx);
|
|
394
|
+
const vaultIntegrationDir = resolveVaultDir(vaultIntegration['vault-dir'], hostCtx);
|
|
395
|
+
vaultIntegration['vault-dir'] = vaultIntegrationDir.value;
|
|
396
|
+
vaultIntegration['vault-dir-source'] = vaultIntegrationDir.source;
|
|
336
397
|
|
|
337
398
|
// resource-thresholds sub-keys (v3.1.0 env-aware — issue #166)
|
|
338
399
|
const resourceThresholds = _parseResourceThresholds(kv);
|
|
339
400
|
|
|
340
401
|
// vault-sync: parsed from full content (can live outside Session Config)
|
|
341
402
|
const vaultSync = _parseVaultSync(mdContent);
|
|
342
|
-
// Host-local override (issue #653) — same
|
|
343
|
-
|
|
403
|
+
// Host-local override (issue #653) — same resolver as vault-integration above, so the
|
|
404
|
+
// two blocks cannot disagree about which vault this repo writes to (agents/vault#319).
|
|
405
|
+
vaultSync['vault-dir'] = resolveVaultDir(vaultSync['vault-dir'], hostCtx).value;
|
|
344
406
|
|
|
345
407
|
// drift-check: parsed from full content (standalone top-level block)
|
|
346
408
|
const driftCheck = _parseDriftCheck(mdContent);
|
|
@@ -406,7 +468,7 @@ export function parseSessionConfig(mdContent, { hostPaths } = {}) {
|
|
|
406
468
|
// dispatcher-autonomy: opt-in cross-repo dispatcher autonomy block (Epic #673 / issue #679).
|
|
407
469
|
// Parser stays pure (raw committed value) for claude-md-drift-check raw-value parity;
|
|
408
470
|
// the host-local override (env > owner.yaml > committed > off) is overlaid onto the
|
|
409
|
-
// FINAL object only — mirroring the vault-dir
|
|
471
|
+
// FINAL object only — mirroring the vault-dir host-override pattern above. Reuses the
|
|
410
472
|
// already-loaded hostCtx.ownerConfig so owner.yaml is not read twice.
|
|
411
473
|
const dispatcherAutonomy = _parseDispatcherAutonomy(mdContent);
|
|
412
474
|
dispatcherAutonomy.autonomy = resolveDispatcherAutonomy({
|
|
@@ -417,6 +479,16 @@ export function parseSessionConfig(mdContent, { hostPaths } = {}) {
|
|
|
417
479
|
// discovery-validator: parsed from full content (PSA-006 enforcement / issue #567)
|
|
418
480
|
const discoveryValidator = _parseDiscoveryValidator(mdContent);
|
|
419
481
|
|
|
482
|
+
// reaper: orphan-process watchdog, default OFF (PRD 2026-09-20 Prozessgruppen-Kill
|
|
483
|
+
// und Waisen-Wächter, Epic #1425 / #1432 B4). Consumed by the two hooks that
|
|
484
|
+
// trigger the detached scan — post-tool-batch-wave-signal.mjs and on-stop.mjs.
|
|
485
|
+
const reaper = _parseReaper(mdContent);
|
|
486
|
+
|
|
487
|
+
// gate: wall-clock ceiling for gate path B (run-quality-gate.mjs →
|
|
488
|
+
// gate-*.mjs), which had no timeout at all before #1425 A3. The operator
|
|
489
|
+
// override SO_GATE_TIMEOUT_MS still wins over this committed value.
|
|
490
|
+
const gate = _parseGate(mdContent);
|
|
491
|
+
|
|
420
492
|
// templates-first: parsed from full content (PRD gsd Pattern 3 / issues #517, #519)
|
|
421
493
|
const templatesFirst = _parseTemplatesFirst(mdContent);
|
|
422
494
|
|
|
@@ -540,6 +612,7 @@ export function parseSessionConfig(mdContent, { hostPaths } = {}) {
|
|
|
540
612
|
'resource-awareness': resourceAwareness,
|
|
541
613
|
'enable-host-banner': enableHostBanner,
|
|
542
614
|
'heavy-repo': heavyRepo,
|
|
615
|
+
'offload-first': offloadFirst,
|
|
543
616
|
'worktree-cleanup': worktreeCleanup,
|
|
544
617
|
'resource-thresholds': resourceThresholds,
|
|
545
618
|
'worktree-exclude': worktreeExclude,
|
|
@@ -555,6 +628,8 @@ export function parseSessionConfig(mdContent, { hostPaths } = {}) {
|
|
|
555
628
|
'skill-evolution': skillEvolution,
|
|
556
629
|
'dispatcher-autonomy': dispatcherAutonomy,
|
|
557
630
|
'discovery-validator': discoveryValidator,
|
|
631
|
+
'reaper': reaper,
|
|
632
|
+
'gate': gate,
|
|
558
633
|
'templates-first': templatesFirst,
|
|
559
634
|
'verification-auto-fix': verificationAutoFix,
|
|
560
635
|
'dialectic': dialectic,
|
|
@@ -144,20 +144,62 @@ function tailRead(absPath, prevOffset) {
|
|
|
144
144
|
* Event types this monitor is meant to classify. Anything else is ignored even
|
|
145
145
|
* when it carries a wave number.
|
|
146
146
|
*
|
|
147
|
-
* The allowlist is
|
|
148
|
-
* and the quality-gate envelope — i.e. exactly the records that can
|
|
149
|
-
* three measurements `evaluateSignals` compares (`files_changed`, the
|
|
150
|
-
* pass count, the per-wave agent count). It is deliberately fail-CLOSED: a
|
|
151
|
-
* event type that gains a `wave_number` field must be added here consciously.
|
|
147
|
+
* The allowlist is the wave-COMPLETION record, the agent-dispatch/-stop
|
|
148
|
+
* counters, and the quality-gate envelope — i.e. exactly the records that can
|
|
149
|
+
* carry the three measurements `evaluateSignals` compares (`files_changed`, the
|
|
150
|
+
* test pass count, the per-wave agent count). It is deliberately fail-CLOSED: a
|
|
151
|
+
* new event type that gains a `wave_number` field must be added here consciously.
|
|
152
152
|
*
|
|
153
153
|
* The gate admission is a TYPE-AND-SHAPE gate, never a bare prefix widening —
|
|
154
154
|
* see `isWaveScopedEvent` for why.
|
|
155
|
+
*
|
|
156
|
+
* `orchestrator.wave.completed` is admitted by its EXACT name. The
|
|
157
|
+
* `orchestrator.wave.` prefix that stood here until 2026-09-19 also admitted
|
|
158
|
+
* `wave.started` (and `wave.final_refused`), neither of which carries a
|
|
159
|
+
* measurement — and it cost the one signal the diff keys
|
|
160
|
+
* exist for: the batch hook wrote completed{N} and started{N+1} in the same
|
|
161
|
+
* millisecond, so they shared a tail tick, started{N+1} instantiated an EMPTY
|
|
162
|
+
* summary for N+1, the tick's highest wave became N+1, and the (N-1, N)
|
|
163
|
+
* `shrinking_diff` pair was never compared (architect-reviewer repro,
|
|
164
|
+
* 40 -> 10 -> 5 files: same tick `[]`, split ticks `shrinking_diff:2` and `:3`).
|
|
155
165
|
*/
|
|
156
|
-
const
|
|
166
|
+
const WAVE_COMPLETED_EVENT = 'orchestrator.wave.completed';
|
|
157
167
|
const WAVE_EVENT_NAMES = new Set(['agent.dispatched', 'orchestrator.agent.dispatched']);
|
|
158
168
|
const AGENT_STOPPED_EVENT = 'orchestrator.agent.stopped';
|
|
159
169
|
const GATE_EVENT_PREFIX = 'orchestrator.quality_gate.';
|
|
160
170
|
|
|
171
|
+
/**
|
|
172
|
+
* True when an `orchestrator.agent.stopped` record names the agent that stopped.
|
|
173
|
+
*
|
|
174
|
+
* The shape half of the type-and-shape gate for the agent counter: the event
|
|
175
|
+
* type is BIMODAL (#939/#949, documented at `hooks/on-stop.mjs:554-580`). Only
|
|
176
|
+
* the records carrying a non-empty `agent` describe a real dispatched agent;
|
|
177
|
+
* the rest are the phantom-stop class the coordinator's own `Stop` emits, and
|
|
178
|
+
* counting them inflates every per-wave agent count and the `velocity_drop`
|
|
179
|
+
* signal derived from it.
|
|
180
|
+
*
|
|
181
|
+
* Measured 2026-09-18 over the then-active `.orchestrator/metrics/events.jsonl`
|
|
182
|
+
* plus the legacy `.jsonl.1` backup beside it (destroyed 2026-09-19):
|
|
183
|
+
* → { total_stopped: 16438, with_wave: 6553, with_wave_no_agent: 5930 }
|
|
184
|
+
* i.e. 90.5% of the wave-scoped stops carry no `agent` — a ~10.5x inflation.
|
|
185
|
+
*
|
|
186
|
+
* To re-measure, read the ledger with `readEventsWithRotations(repoRoot)` from
|
|
187
|
+
* `scripts/lib/events.mjs` — it yields the active file plus every archive in
|
|
188
|
+
* time order and reports a rotation whose archive is gone as a `gaps` entry
|
|
189
|
+
* with `complete: false`, which a `cat` over rotation files cannot. Rotation
|
|
190
|
+
* writes `_archive/events-<firstTs>_<lastTs>.jsonl` and an
|
|
191
|
+
* `orchestrator.events.rotated` first line since #1401; the `.1`..`.N` ring is
|
|
192
|
+
* no longer written (see `events-rotation.mjs` § Why `_archive/<name>`).
|
|
193
|
+
* Recipe with the jq filter: `skills/convergence-monitoring/SIGNALS.md`
|
|
194
|
+
* § Live monitor input.
|
|
195
|
+
*
|
|
196
|
+
* @param {Record<string, unknown>} rec
|
|
197
|
+
* @returns {boolean}
|
|
198
|
+
*/
|
|
199
|
+
function namesAnAgent(rec) {
|
|
200
|
+
return typeof rec.agent === 'string' && rec.agent.trim() !== '';
|
|
201
|
+
}
|
|
202
|
+
|
|
161
203
|
/**
|
|
162
204
|
* True when a record carries a `counts` object this monitor can fold, i.e. a
|
|
163
205
|
* plain object with a numeric `passed`. `counts: null`, an array, or a string
|
|
@@ -183,13 +225,18 @@ function hasFoldableCounts(rec) {
|
|
|
183
225
|
* the real wave record arrives. Hence the discriminator: a gate record is
|
|
184
226
|
* wave-scoped only when it has BOTH `wave_number` and a well-formed `counts`.
|
|
185
227
|
*
|
|
228
|
+
* The same discriminator applies to `orchestrator.agent.stopped`: the type is
|
|
229
|
+
* bimodal, so a stop record is admitted only when it NAMES an agent — see
|
|
230
|
+
* `namesAnAgent`.
|
|
231
|
+
*
|
|
186
232
|
* @param {string} evType
|
|
187
233
|
* @param {Record<string, unknown>} rec
|
|
188
234
|
* @returns {boolean}
|
|
189
235
|
*/
|
|
190
236
|
function isWaveScopedEvent(evType, rec = {}) {
|
|
191
|
-
if (evType
|
|
192
|
-
if (WAVE_EVENT_NAMES.has(evType)
|
|
237
|
+
if (evType === WAVE_COMPLETED_EVENT) return true;
|
|
238
|
+
if (WAVE_EVENT_NAMES.has(evType)) return true;
|
|
239
|
+
if (evType === AGENT_STOPPED_EVENT) return namesAnAgent(rec);
|
|
193
240
|
if (evType.startsWith(GATE_EVENT_PREFIX)) {
|
|
194
241
|
return pickInt(rec.wave_number) !== null && hasFoldableCounts(rec);
|
|
195
242
|
}
|
|
@@ -235,9 +282,16 @@ function isWaveScopedEvent(evType, rec = {}) {
|
|
|
235
282
|
*
|
|
236
283
|
* `orchestrator.agent.stopped` is counted per record toward the wave's agent
|
|
237
284
|
* count, the same way `agent.dispatched` is — it is the only per-agent record
|
|
238
|
-
* this repo actually emits with a wave number
|
|
239
|
-
*
|
|
240
|
-
*
|
|
285
|
+
* this repo actually emits with a wave number. But it is counted ONLY when the
|
|
286
|
+
* record NAMES its agent (`namesAnAgent`): the type is bimodal, and the
|
|
287
|
+
* unfiltered count is dominated by the #939/#949 phantom-stop class.
|
|
288
|
+
*
|
|
289
|
+
* Measured 2026-09-18 over `.orchestrator/metrics/events.jsonl{,.1}` (same jq
|
|
290
|
+
* envelope quoted at `namesAnAgent`):
|
|
291
|
+
* agent.stopped, any shape ................. 16438
|
|
292
|
+
* ... carrying a wave number ............... 6553
|
|
293
|
+
* ... of those, WITHOUT an `agent` field ... 5930 (90.5% — not counted)
|
|
294
|
+
* ... of those, WITH a non-empty `agent` ... 623 (what this counter reads)
|
|
241
295
|
*
|
|
242
296
|
* @param {Record<string, unknown>} rec
|
|
243
297
|
* @param {Map<number, WaveSummary>} state
|
|
@@ -431,6 +485,15 @@ async function tailLoop(intervalS) {
|
|
|
431
485
|
continue;
|
|
432
486
|
}
|
|
433
487
|
offset = tick.offset;
|
|
488
|
+
// Only the HIGHEST wave this tick touched is evaluated. NAMED CEILING
|
|
489
|
+
// (BV-004): a wave-(N+1) record that DOES carry a measurement (a named
|
|
490
|
+
// agent.dispatched/-stopped, a gated quality_gate envelope) landing in the
|
|
491
|
+
// same tick as completed{N} still masks the (N-1, N) pair. Evaluating every
|
|
492
|
+
// touched wave would close that, but the first tick reads the whole ledger
|
|
493
|
+
// from offset 0 and wave numbers are not session-scoped, so one stale
|
|
494
|
+
// startup evaluation would become one per wave number in history. Revisit
|
|
495
|
+
// if the ledger shows such a record within one poll interval of a
|
|
496
|
+
// completed{N}.
|
|
434
497
|
let latestWave = -1;
|
|
435
498
|
for (const line of tick.lines) {
|
|
436
499
|
/** @type {Record<string, unknown> | null} */
|
|
@@ -40,6 +40,17 @@
|
|
|
40
40
|
import { existsSync, statSync, readFileSync } from 'node:fs';
|
|
41
41
|
import { join } from 'node:path';
|
|
42
42
|
|
|
43
|
+
// Ceiling (BV-004): one process per session that invoked session-start
|
|
44
|
+
// (monitors/monitors.json `when: on-skill-invoke:session-start`), alive until
|
|
45
|
+
// SIGTERM; one stat + one full readFileSync of ONE local file per 900 s
|
|
46
|
+
// (resolved against CLAUDE_PLUGIN_ROOT, not the repo); no network, no repo
|
|
47
|
+
// enumeration, no Session Config read (runs even with `ecosystem-health: false`).
|
|
48
|
+
// The watched file has NO producer (0 writers, measured 2026-09-18:
|
|
49
|
+
// `rg -l "ecosystem-health\.jsonl"` → this file only) — every run emits
|
|
50
|
+
// `no-state-yet` and idles. Revisit when: (1) code starts writing
|
|
51
|
+
// .orchestrator/metrics/ecosystem-health.jsonl → switch to a tail read and gate
|
|
52
|
+
// on `ecosystem-health` enabled; or (2) peer sessions regularly exceed ~5 (one
|
|
53
|
+
// idle node process per live session).
|
|
43
54
|
const DEFAULT_INTERVAL_S = 15 * 60; // 15min
|
|
44
55
|
const STATE_FILE_REL = '.orchestrator/metrics/ecosystem-health.jsonl';
|
|
45
56
|
|