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
|
@@ -21,6 +21,13 @@
|
|
|
21
21
|
* when the platform exposes a stable prompt-assembly boundary; at that point the
|
|
22
22
|
* digest can be computed against the real assembled prompt instead of echoed.
|
|
23
23
|
*
|
|
24
|
+
* CALLERS (#1298): the CLI modes run only from prose — `--instruction` in
|
|
25
|
+
* `skills/wave-executor/references/wave-loop-dispatch.md`, `--emit` and `--verify`
|
|
26
|
+
* in `wave-loop-review.md`. Ceiling: `check-unwired-features` S4 cannot notice both
|
|
27
|
+
* dropping that citation — it judges modules, not CLI modes, and reads this one wired
|
|
28
|
+
* as a CLI entrypoint AND via its hook importer (`scopeDigest`). Revisit on a third
|
|
29
|
+
* prose caller, or when the census learns to track prose-invoked CLI entrypoints.
|
|
30
|
+
*
|
|
24
31
|
* Pure + stdlib only. Nothing here throws on malformed input — a broken echo
|
|
25
32
|
* check must never change a wave's outcome.
|
|
26
33
|
*
|
|
@@ -34,14 +41,16 @@
|
|
|
34
41
|
* node scripts/lib/scope-echo.mjs --scope-file <path> --instruction
|
|
35
42
|
* node scripts/lib/scope-echo.mjs --scope-file <path> --report-file <path> \
|
|
36
43
|
* [--wave N --agent-id ID --emit]
|
|
44
|
+
* node scripts/lib/scope-echo.mjs --verify --wave <N> --state-dir <dir> \
|
|
45
|
+
* [--session <id> --events <path> --json --emit]
|
|
37
46
|
* node scripts/lib/scope-echo.mjs --help
|
|
38
47
|
*/
|
|
39
48
|
|
|
40
|
-
import { readFileSync } from 'node:fs';
|
|
41
|
-
import { resolve } from 'node:path';
|
|
42
|
-
import { fileURLToPath } from 'node:url';
|
|
49
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
50
|
+
import { join, resolve } from 'node:path';
|
|
43
51
|
|
|
44
52
|
import { digestSha256Short } from './crypto-digest-utils.mjs';
|
|
53
|
+
import { isMainModule } from './is-main-module.mjs';
|
|
45
54
|
|
|
46
55
|
/** Marker the agent must emit. Case-sensitive by design — a lowercase lookalike is not an echo. */
|
|
47
56
|
export const SCOPE_ECHO_MARKER = 'SCOPE-DIGEST:';
|
|
@@ -49,6 +58,15 @@ export const SCOPE_ECHO_MARKER = 'SCOPE-DIGEST:';
|
|
|
49
58
|
/** Event name for the post-wave verdict. */
|
|
50
59
|
export const SCOPE_ECHO_EVENT = 'orchestrator.wave_dispatch.scope_echo_checked';
|
|
51
60
|
|
|
61
|
+
/** The SEND-side record `hooks/pre-task-scope-disjoint.mjs` writes per dispatch. */
|
|
62
|
+
export const SCOPE_CHECKED_EVENT = 'orchestrator.wave_dispatch.scope_checked';
|
|
63
|
+
|
|
64
|
+
/** The DEGRADATION record `scripts/materialize-wave-scope.mjs` writes per wave. */
|
|
65
|
+
export const SCOPE_MATERIALIZED_EVENT = 'orchestrator.wave_dispatch.scope_materialized';
|
|
66
|
+
|
|
67
|
+
/** The per-WAVE join verdict `--verify` emits. */
|
|
68
|
+
export const SCOPE_VERIFIED_EVENT = 'orchestrator.wave_dispatch.scope_verified';
|
|
69
|
+
|
|
52
70
|
/** Max characters retained for `agent_id` in the payload (same clamp as the send-side hook). */
|
|
53
71
|
const AGENT_ID_MAX = 120;
|
|
54
72
|
|
|
@@ -212,6 +230,330 @@ export function scopeEchoPayload(verdict, meta = {}) {
|
|
|
212
230
|
};
|
|
213
231
|
}
|
|
214
232
|
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
// --verify — join the two halves of one wave ON THE DIGEST (#1092)
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Closed verdict enum. Order is PRECEDENCE, not preference — the first matching
|
|
239
|
+
* row wins, and every (file, dispatch, echo) triple lands on exactly one row:
|
|
240
|
+
*
|
|
241
|
+
* duplicate-claim ≥2 DISTINCT agent ids claimed one digest at dispatch.
|
|
242
|
+
* That is agent A's scope reported for agent B, and it is
|
|
243
|
+
* a defect whatever the other two bits say — so it is
|
|
244
|
+
* checked first.
|
|
245
|
+
* echoed-not-injected an echo names a digest NO dispatch claimed.
|
|
246
|
+
* digest-unknown no scope FILE on disk carries this digest: the record
|
|
247
|
+
* exists but the artefact that would ground it does not
|
|
248
|
+
* (a scope file rewritten or reconciled away after the
|
|
249
|
+
* dispatch).
|
|
250
|
+
* matched file ∧ dispatch ∧ echo.
|
|
251
|
+
* injected-not-echoed file ∧ dispatch, no echo — the normal state DURING a
|
|
252
|
+
* wave, before the reports land.
|
|
253
|
+
* injection-missing file ∧ neither — a scope file no dispatch claimed and
|
|
254
|
+
* no agent echoed. This is AC-2: the omitted injection.
|
|
255
|
+
*
|
|
256
|
+
* Every member is kebab-case. `echo-only` is NOT one of the six: it is the
|
|
257
|
+
* DEGRADED value every verdict collapses to when the wave's transport is
|
|
258
|
+
* unobservable (see {@link verifyWaveScope}), so it is never produced by the
|
|
259
|
+
* precedence chain above and never belongs in this array.
|
|
260
|
+
*
|
|
261
|
+
* `injection-missing` was spelled `injection_missing` until 2026-09-16 — the
|
|
262
|
+
* one snake_case member of an otherwise kebab-case enum. Ledger records written
|
|
263
|
+
* before that date may still carry the old spelling in `by_verdict`; there is no
|
|
264
|
+
* dual-emit, so a consumer reading historical rows must accept both keys.
|
|
265
|
+
*/
|
|
266
|
+
export const SCOPE_VERDICTS = Object.freeze([
|
|
267
|
+
'duplicate-claim',
|
|
268
|
+
'echoed-not-injected',
|
|
269
|
+
'digest-unknown',
|
|
270
|
+
'matched',
|
|
271
|
+
'injected-not-echoed',
|
|
272
|
+
'injection-missing',
|
|
273
|
+
]);
|
|
274
|
+
|
|
275
|
+
/** A well-formed digest. Anything else is ignored rather than joined on. */
|
|
276
|
+
const DIGEST_RE = /^[0-9a-f]{8}$/;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Parse a JSONL ledger leniently: a malformed line is skipped, never thrown on
|
|
280
|
+
* — and COUNTED, which is the load-bearing half.
|
|
281
|
+
*
|
|
282
|
+
* A crashed writer leaves a truncated final line (`{"event":"…","wave":4` with
|
|
283
|
+
* no closing brace) — a measured shape in this repo's own ledger. Skipping it
|
|
284
|
+
* silently truncates the join, and a truncated join reports
|
|
285
|
+
* `dispatches N / injected N / echoed N` with every digest `matched`: a CLEAN
|
|
286
|
+
* wave, from the instrument built to detect silent failure. The count is what
|
|
287
|
+
* separates "nothing was wrong" from "I could not read part of the evidence".
|
|
288
|
+
*
|
|
289
|
+
* A malformed line is any NON-BLANK line that does not yield a plain object:
|
|
290
|
+
* one that does not start with `{`, one `JSON.parse` rejects, and one that
|
|
291
|
+
* parses to an array or a scalar. Blank lines are not malformed — a trailing
|
|
292
|
+
* newline is the normal end of a JSONL file.
|
|
293
|
+
*
|
|
294
|
+
* @param {string} raw
|
|
295
|
+
* @returns {{records: Array<Record<string, unknown>>, malformed: number}}
|
|
296
|
+
*/
|
|
297
|
+
function parseJsonl(raw) {
|
|
298
|
+
/** @type {Array<Record<string, unknown>>} */
|
|
299
|
+
const records = [];
|
|
300
|
+
let malformed = 0;
|
|
301
|
+
if (typeof raw !== 'string') return { records, malformed };
|
|
302
|
+
for (const line of raw.split('\n')) {
|
|
303
|
+
const trimmed = line.trim();
|
|
304
|
+
if (trimmed.length === 0) continue;
|
|
305
|
+
if (trimmed[0] !== '{') {
|
|
306
|
+
malformed += 1;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
const rec = JSON.parse(trimmed);
|
|
311
|
+
if (rec !== null && typeof rec === 'object' && !Array.isArray(rec)) records.push(rec);
|
|
312
|
+
else malformed += 1;
|
|
313
|
+
} catch {
|
|
314
|
+
malformed += 1; // a half-written line is not a verdict — but it is not nothing either
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return { records, malformed };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Digest every per-agent scope file of one wave — shape (a) of the two scope
|
|
322
|
+
* shapes (CLAUDE.md / AGENTS.md § allowedPaths).
|
|
323
|
+
*
|
|
324
|
+
* A file that cannot be read or parsed is the FILE-side counterpart of a
|
|
325
|
+
* truncated ledger line: skipping it silently makes a wave whose scope files are
|
|
326
|
+
* corrupt indistinguishable from one that was never injected (`digest-unknown` /
|
|
327
|
+
* `injection-missing`). The count therefore rides back on the returned Map as
|
|
328
|
+
* `malformedFiles` — an additive own property, so every existing consumer that
|
|
329
|
+
* only iterates the entries is unaffected. (A file that parses to a non-array or
|
|
330
|
+
* to an empty scope stays a silent skip: an empty scope is legitimate.)
|
|
331
|
+
*
|
|
332
|
+
* @param {string} stateDir
|
|
333
|
+
* @param {number} wave
|
|
334
|
+
* @param {{readDir?: typeof readdirSync, readFile?: typeof readFileSync}} [io]
|
|
335
|
+
* @returns {Map<string, string[]> & {malformedFiles: number}} digest → agent ids (file basenames)
|
|
336
|
+
*/
|
|
337
|
+
export function digestScopeFiles(stateDir, wave, { readDir = readdirSync, readFile = readFileSync } = {}) {
|
|
338
|
+
const out = new Map();
|
|
339
|
+
out.malformedFiles = 0;
|
|
340
|
+
const dir = resolve(String(stateDir), 'filescopes', `wave-${wave}`);
|
|
341
|
+
let names;
|
|
342
|
+
try {
|
|
343
|
+
names = readDir(dir);
|
|
344
|
+
} catch {
|
|
345
|
+
return out; // no directory yet is not a failure — it is "nothing materialized"
|
|
346
|
+
}
|
|
347
|
+
for (const name of names) {
|
|
348
|
+
const file = typeof name === 'string' ? name : name?.name;
|
|
349
|
+
if (typeof file !== 'string' || !file.endsWith('.json')) continue;
|
|
350
|
+
let parsed;
|
|
351
|
+
try {
|
|
352
|
+
parsed = JSON.parse(readFile(join(dir, file), 'utf8'));
|
|
353
|
+
} catch {
|
|
354
|
+
out.malformedFiles += 1; // unreadable or unparseable — not nothing
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (!Array.isArray(parsed) || normalizeScopePaths(parsed).length === 0) continue;
|
|
358
|
+
const digest = scopeDigest(parsed);
|
|
359
|
+
const ids = out.get(digest) ?? [];
|
|
360
|
+
ids.push(file.slice(0, -'.json'.length));
|
|
361
|
+
out.set(digest, ids);
|
|
362
|
+
}
|
|
363
|
+
return out;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Verify ONE wave's FILE-SCOPE injection end to end.
|
|
368
|
+
*
|
|
369
|
+
* ## Why the join key is the DIGEST and never `agent_id`
|
|
370
|
+
*
|
|
371
|
+
* Measured 2026-09-16 over this host's `.orchestrator/metrics/events.jsonl`
|
|
372
|
+
* corpus: 609 `scope_checked` records against 51 `scope_echo_checked`, and the
|
|
373
|
+
* two agent-id sets OVERLAP IN ZERO ELEMENTS — the send side records the
|
|
374
|
+
* dispatch tool's `description` plus `subagent_type`
|
|
375
|
+
* (`"i-3 #1353 refund event (session-orchestrator:code-implementer)"`), the
|
|
376
|
+
* receive side the coordinator's short handle (`"i-3"`). One session showed 23
|
|
377
|
+
* echoes against 18 injections and the halves could not be joined at all. The
|
|
378
|
+
* digest is the one value both halves derive from the SAME artefact, so it joins
|
|
379
|
+
* them by construction.
|
|
380
|
+
*
|
|
381
|
+
* ## Observability, not enforcement
|
|
382
|
+
*
|
|
383
|
+
* Every verdict exits 0. This tool reports; the wave-executor turns
|
|
384
|
+
* `injection-missing` / `duplicate-claim` into a STATE.md deviation
|
|
385
|
+
* (`wave-loop-review.md` step 3d-bis), never into a block.
|
|
386
|
+
*
|
|
387
|
+
* ## A partial read is never a clean wave
|
|
388
|
+
*
|
|
389
|
+
* `malformed_lines` counts the ledger lines that could not be parsed (a crashed
|
|
390
|
+
* writer's truncated append is the measured shape). It is ALWAYS present,
|
|
391
|
+
* including as `0`: the ledger read either happened or the file was absent, and
|
|
392
|
+
* both are measurements. A non-zero count means the join ran on INCOMPLETE
|
|
393
|
+
* evidence — the counts and verdicts below are a floor, not a census — and the
|
|
394
|
+
* human table says so beside them.
|
|
395
|
+
*
|
|
396
|
+
* `malformed_scope_files` is the same measurement on the FILE side (#1379 P2):
|
|
397
|
+
* a scope file that cannot be read or parsed was silently skipped, which made a
|
|
398
|
+
* corrupt wave yield `digest-unknown` / `injection-missing` verdicts
|
|
399
|
+
* indistinguishable from a genuinely missing injection. Always present,
|
|
400
|
+
* including as `0`.
|
|
401
|
+
*
|
|
402
|
+
* ## Transport degradation
|
|
403
|
+
*
|
|
404
|
+
* On a platform with no `PreToolUse` `Agent` matcher (Codex, Cursor, Pi) the
|
|
405
|
+
* send-side record cannot exist at all, so a missing dispatch is NOT evidence of
|
|
406
|
+
* a missing injection. When the wave's `scope_materialized` record says
|
|
407
|
+
* `transport_observable: false`, `transport` reads `unobservable` and every
|
|
408
|
+
* verdict degrades to `echo-only` — the receive half is all the platform can
|
|
409
|
+
* carry, and reporting `injection-missing` there would be an accusation derived
|
|
410
|
+
* from an instrument that is not installed.
|
|
411
|
+
*
|
|
412
|
+
* @param {object} params
|
|
413
|
+
* @param {number} params.wave
|
|
414
|
+
* @param {string} params.stateDir
|
|
415
|
+
* @param {string} [params.session] semantic (or raw) session id to filter on
|
|
416
|
+
* @param {string} [params.eventsPath] defaults to `.orchestrator/metrics/events.jsonl`
|
|
417
|
+
* @param {typeof readFileSync} [params.readFile]
|
|
418
|
+
* @param {typeof readdirSync} [params.readDir]
|
|
419
|
+
* @returns {{wave: number, transport: 'observable'|'unobservable', dispatches: number,
|
|
420
|
+
* injected: number, echoed: number, malformed_lines: number,
|
|
421
|
+
* by_verdict: Record<string, number>,
|
|
422
|
+
* agents: Array<{agent_id: string, verdict: string, digest: string}>}}
|
|
423
|
+
*/
|
|
424
|
+
export function verifyWaveScope({
|
|
425
|
+
wave,
|
|
426
|
+
stateDir,
|
|
427
|
+
session,
|
|
428
|
+
eventsPath,
|
|
429
|
+
readFile = readFileSync,
|
|
430
|
+
readDir = readdirSync,
|
|
431
|
+
}) {
|
|
432
|
+
const ledgerPath = eventsPath || join('.orchestrator', 'metrics', 'events.jsonl');
|
|
433
|
+
let records = [];
|
|
434
|
+
let malformedLines = 0;
|
|
435
|
+
try {
|
|
436
|
+
({ records, malformed: malformedLines } = parseJsonl(readFile(ledgerPath, 'utf8')));
|
|
437
|
+
} catch { /* no ledger yet — every count is legitimately 0 */ }
|
|
438
|
+
|
|
439
|
+
const mine = (rec) => {
|
|
440
|
+
if (rec.wave !== wave) return false;
|
|
441
|
+
if (!session) return true;
|
|
442
|
+
return rec.semantic_session_id === session || rec.session_id === session;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
/** @type {Map<string, Set<string>>} digest → distinct dispatching agent ids */
|
|
446
|
+
const dispatchIds = new Map();
|
|
447
|
+
/** @type {Map<string, string>} digest → first echoing agent id */
|
|
448
|
+
const echoIds = new Map();
|
|
449
|
+
let dispatches = 0;
|
|
450
|
+
let injected = 0;
|
|
451
|
+
let echoed = 0;
|
|
452
|
+
// A wave may be materialized more than once (#1103); the LAST record wins,
|
|
453
|
+
// because that is the state the dispatches below actually ran against.
|
|
454
|
+
let transportObservable = null;
|
|
455
|
+
|
|
456
|
+
for (const rec of records) {
|
|
457
|
+
if (rec.event === SCOPE_MATERIALIZED_EVENT && mine(rec)) {
|
|
458
|
+
if (typeof rec.transport_observable === 'boolean') transportObservable = rec.transport_observable;
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (rec.event === SCOPE_CHECKED_EVENT && mine(rec)) {
|
|
462
|
+
dispatches += 1;
|
|
463
|
+
if (rec.injected === true) injected += 1;
|
|
464
|
+
const digest = rec.scope_digest;
|
|
465
|
+
if (typeof digest === 'string' && DIGEST_RE.test(digest)) {
|
|
466
|
+
const ids = dispatchIds.get(digest) ?? new Set();
|
|
467
|
+
ids.add(typeof rec.agent_id === 'string' ? rec.agent_id : 'unnamed-agent');
|
|
468
|
+
dispatchIds.set(digest, ids);
|
|
469
|
+
}
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (rec.event === SCOPE_ECHO_EVENT && mine(rec)) {
|
|
473
|
+
if (rec.echoed === true) echoed += 1;
|
|
474
|
+
// The agent's OWN claim first (`actual_digest`); the coordinator-side
|
|
475
|
+
// expectation only as a fallback, so a mismatched echo still joins to the
|
|
476
|
+
// scope file it was checked against rather than vanishing from the report.
|
|
477
|
+
const digest = [rec.actual_digest, rec.expected_digest]
|
|
478
|
+
.find((d) => typeof d === 'string' && DIGEST_RE.test(d));
|
|
479
|
+
if (digest !== undefined && !echoIds.has(digest)) {
|
|
480
|
+
echoIds.set(digest, typeof rec.agent_id === 'string' ? rec.agent_id : 'unnamed-agent');
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const fileIds = digestScopeFiles(stateDir, wave, { readDir, readFile });
|
|
486
|
+
const digests = [...new Set([...fileIds.keys(), ...dispatchIds.keys(), ...echoIds.keys()])].sort();
|
|
487
|
+
|
|
488
|
+
const unobservable = transportObservable === false;
|
|
489
|
+
/** @type {Record<string, number>} */
|
|
490
|
+
const byVerdict = {};
|
|
491
|
+
const agents = digests.map((digest) => {
|
|
492
|
+
const claimants = dispatchIds.get(digest);
|
|
493
|
+
const hasDispatch = claimants !== undefined && claimants.size > 0;
|
|
494
|
+
const hasEcho = echoIds.has(digest);
|
|
495
|
+
const hasFile = fileIds.has(digest);
|
|
496
|
+
|
|
497
|
+
let verdict;
|
|
498
|
+
if (hasDispatch && claimants.size > 1) verdict = 'duplicate-claim';
|
|
499
|
+
else if (hasEcho && !hasDispatch) verdict = 'echoed-not-injected';
|
|
500
|
+
else if (!hasFile) verdict = 'digest-unknown';
|
|
501
|
+
else if (hasDispatch && hasEcho) verdict = 'matched';
|
|
502
|
+
else if (hasDispatch) verdict = 'injected-not-echoed';
|
|
503
|
+
else verdict = 'injection-missing';
|
|
504
|
+
if (unobservable) verdict = 'echo-only';
|
|
505
|
+
|
|
506
|
+
byVerdict[verdict] = (byVerdict[verdict] ?? 0) + 1;
|
|
507
|
+
const agentId = (hasDispatch ? [...claimants].sort().join(' + ') : null)
|
|
508
|
+
?? echoIds.get(digest)
|
|
509
|
+
?? (fileIds.get(digest) ?? []).join(' + ')
|
|
510
|
+
?? 'unknown';
|
|
511
|
+
return { agent_id: agentId, verdict, digest };
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
return {
|
|
515
|
+
wave,
|
|
516
|
+
transport: unobservable ? 'unobservable' : 'observable',
|
|
517
|
+
dispatches,
|
|
518
|
+
injected,
|
|
519
|
+
echoed,
|
|
520
|
+
malformed_lines: malformedLines,
|
|
521
|
+
malformed_scope_files: fileIds.malformedFiles ?? 0,
|
|
522
|
+
by_verdict: byVerdict,
|
|
523
|
+
agents,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Telemetry payload for a `--verify` run. Counts, closed enums and 8-hex digests
|
|
529
|
+
* ONLY — no agent id, no path, no prompt text: this record travels over the same
|
|
530
|
+
* unredacted Clank Event-Bus webhook as its two halves, and an `agent_id` is a
|
|
531
|
+
* free-form coordinator string that has carried private project slugs before.
|
|
532
|
+
*
|
|
533
|
+
* `malformed_lines` travels with the counts and, like them, is ALWAYS present
|
|
534
|
+
* including as `0` — it is the denominator's honesty check: without it a join
|
|
535
|
+
* that silently dropped half the ledger is indistinguishable in the record from
|
|
536
|
+
* a wave where nothing went wrong (`.claude/rules/host-resources.md` § HR-105).
|
|
537
|
+
* `malformed_scope_files` carries the same guarantee for the scope files the
|
|
538
|
+
* join reads (#1379 P2).
|
|
539
|
+
*
|
|
540
|
+
* @param {ReturnType<typeof verifyWaveScope>} report
|
|
541
|
+
* @returns {Record<string, unknown>}
|
|
542
|
+
*/
|
|
543
|
+
export function scopeVerifiedPayload(report) {
|
|
544
|
+
return {
|
|
545
|
+
wave: report.wave,
|
|
546
|
+
transport_observable: report.transport === 'observable',
|
|
547
|
+
dispatches: report.dispatches,
|
|
548
|
+
injected: report.injected,
|
|
549
|
+
echoed: report.echoed,
|
|
550
|
+
malformed_lines: report.malformed_lines ?? 0,
|
|
551
|
+
malformed_scope_files: report.malformed_scope_files ?? 0,
|
|
552
|
+
by_verdict: report.by_verdict,
|
|
553
|
+
digests: report.agents.map((a) => a.digest),
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
215
557
|
// ---------------------------------------------------------------------------
|
|
216
558
|
// CLI
|
|
217
559
|
// ---------------------------------------------------------------------------
|
|
@@ -229,9 +571,19 @@ const USAGE = `Usage:
|
|
|
229
571
|
an ${SCOPE_ECHO_EVENT} row to .orchestrator/metrics/events.jsonl
|
|
230
572
|
(best-effort: a failed emit never changes the verdict or the exit code).
|
|
231
573
|
|
|
574
|
+
scope-echo --verify --wave <N> --state-dir <dir> [--session <id>]
|
|
575
|
+
[--events <path>] [--json] [--emit]
|
|
576
|
+
Join ONE wave's send side (${SCOPE_CHECKED_EVENT}),
|
|
577
|
+
its scope files and its echoes ON THE DIGEST, and print the per-agent
|
|
578
|
+
verdict table. --emit additionally appends one ${SCOPE_VERIFIED_EVENT}
|
|
579
|
+
row per wave (counts and 8-hex digests only).
|
|
580
|
+
Verdicts: ${SCOPE_VERDICTS.join(' · ')} — plus echo-only when the wave's
|
|
581
|
+
transport is unobservable.
|
|
582
|
+
|
|
232
583
|
scope-echo --help
|
|
233
584
|
|
|
234
|
-
Exit codes: 0 for every verdict, 1 for a missing --scope-file
|
|
585
|
+
Exit codes: 0 for every verdict, 1 for a missing --scope-file (or, under
|
|
586
|
+
--verify, a missing --wave / --state-dir).
|
|
235
587
|
`;
|
|
236
588
|
|
|
237
589
|
/**
|
|
@@ -259,6 +611,74 @@ function parseArgv(argv) {
|
|
|
259
611
|
return out;
|
|
260
612
|
}
|
|
261
613
|
|
|
614
|
+
/**
|
|
615
|
+
* `--verify` mode — the per-wave join (see {@link verifyWaveScope}).
|
|
616
|
+
*
|
|
617
|
+
* Exit 0 for EVERY verdict, including `injection-missing`: this is an
|
|
618
|
+
* observability tool, and the wave-executor decides what a verdict costs
|
|
619
|
+
* (`wave-loop-review.md` step 3d-bis writes a STATE.md deviation, never a
|
|
620
|
+
* block). Exit 1 is reserved for the caller's own mistake — a missing required
|
|
621
|
+
* flag — per `.claude/rules/cli-design.md`.
|
|
622
|
+
*
|
|
623
|
+
* @param {Record<string, string|boolean>} args
|
|
624
|
+
* @returns {Promise<number>}
|
|
625
|
+
*/
|
|
626
|
+
async function mainVerify(args) {
|
|
627
|
+
const wave = Number(args.wave);
|
|
628
|
+
const stateDir = typeof args['state-dir'] === 'string' ? args['state-dir'] : '';
|
|
629
|
+
if (!Number.isSafeInteger(wave) || wave <= 0 || stateDir === '') {
|
|
630
|
+
process.stderr.write('scope-echo: --verify requires --wave <positive-int> and --state-dir <dir>\n');
|
|
631
|
+
process.stderr.write(USAGE);
|
|
632
|
+
return 1;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const report = verifyWaveScope({
|
|
636
|
+
wave,
|
|
637
|
+
stateDir,
|
|
638
|
+
session: typeof args.session === 'string' ? args.session : undefined,
|
|
639
|
+
eventsPath: typeof args.events === 'string' ? args.events : undefined,
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
if (args.emit) {
|
|
643
|
+
try {
|
|
644
|
+
const repoRoot = resolve(stateDir, '..');
|
|
645
|
+
const { emitEvent, sessionAttribution } = await import('./events.mjs');
|
|
646
|
+
await emitEvent(
|
|
647
|
+
SCOPE_VERIFIED_EVENT,
|
|
648
|
+
{ ...scopeVerifiedPayload(report), ...sessionAttribution(repoRoot) },
|
|
649
|
+
{ repoRoot },
|
|
650
|
+
);
|
|
651
|
+
} catch (err) {
|
|
652
|
+
process.stderr.write(`scope-echo: emit failed — ${err?.message ?? err}\n`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if (args.json) {
|
|
657
|
+
process.stdout.write(`${JSON.stringify(report)}\n`);
|
|
658
|
+
return 0;
|
|
659
|
+
}
|
|
660
|
+
const lines = [
|
|
661
|
+
`wave ${report.wave} — transport: ${report.transport} — `
|
|
662
|
+
+ `${report.injected}/${report.dispatches} injected, ${report.echoed} echoed`,
|
|
663
|
+
// A partial read must never read as a clean wave: the counts above are a
|
|
664
|
+
// FLOOR when lines were dropped, and this is the only place the human table
|
|
665
|
+
// can say so.
|
|
666
|
+
...(report.malformed_lines > 0
|
|
667
|
+
? [` WARNING: ${report.malformed_lines} malformed ledger line(s) skipped — `
|
|
668
|
+
+ 'counts and verdicts below are a floor, not a census']
|
|
669
|
+
: []),
|
|
670
|
+
// Same honesty check on the FILE side: an unparseable scope file makes a
|
|
671
|
+
// corrupt wave look exactly like one that was never injected.
|
|
672
|
+
...(report.malformed_scope_files > 0
|
|
673
|
+
? [` WARNING: ${report.malformed_scope_files} malformed scope file(s) skipped — `
|
|
674
|
+
+ 'digest-unknown / injection-missing below may be corruption, not a missing injection']
|
|
675
|
+
: []),
|
|
676
|
+
...report.agents.map((a) => ` ${a.digest} ${a.verdict.padEnd(20)} ${a.agent_id}`),
|
|
677
|
+
];
|
|
678
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
679
|
+
return 0;
|
|
680
|
+
}
|
|
681
|
+
|
|
262
682
|
/**
|
|
263
683
|
* @param {string[]} [argv]
|
|
264
684
|
* @returns {Promise<number>} process exit code: 0 for every verdict (this is an
|
|
@@ -272,6 +692,10 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
272
692
|
process.stdout.write(USAGE);
|
|
273
693
|
return 0;
|
|
274
694
|
}
|
|
695
|
+
// --verify is its own mode and takes no --scope-file: it reads EVERY scope
|
|
696
|
+
// file of the wave, so the check below must not reject it first.
|
|
697
|
+
if (args.verify) return mainVerify(args);
|
|
698
|
+
|
|
275
699
|
const scopeFilePath = typeof args['scope-file'] === 'string' ? args['scope-file'] : '';
|
|
276
700
|
if (!scopeFilePath) {
|
|
277
701
|
process.stderr.write('scope-echo: --scope-file <path> is required\n');
|
|
@@ -337,9 +761,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
337
761
|
return 0;
|
|
338
762
|
}
|
|
339
763
|
|
|
340
|
-
const invokedAsCli =
|
|
341
|
-
process.argv[1] !== undefined &&
|
|
342
|
-
resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));
|
|
764
|
+
const invokedAsCli = isMainModule(import.meta.url);
|
|
343
765
|
|
|
344
766
|
if (invokedAsCli) {
|
|
345
767
|
process.exitCode = await main();
|