session-orchestrator 3.23.0 → 4.0.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 +18 -0
- package/.agents/skills/autopilot/SKILL.md +17 -0
- package/.agents/skills/bootstrap/SKILL.md +20 -0
- package/.agents/skills/brainstorm/SKILL.md +22 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
- package/.agents/skills/debug/SKILL.md +22 -0
- package/.agents/skills/discovery/SKILL.md +20 -0
- package/.agents/skills/dispatcher/SKILL.md +15 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
- package/.agents/skills/ecosystem-health/SKILL.md +20 -0
- package/.agents/skills/eli5/SKILL.md +20 -0
- package/.agents/skills/eval/SKILL.md +21 -0
- package/.agents/skills/evolve/SKILL.md +21 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
- package/.agents/skills/gitlab-ops/SKILL.md +20 -0
- package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
- package/.agents/skills/grill/SKILL.md +22 -0
- package/.agents/skills/hook-development/SKILL.md +15 -0
- package/.agents/skills/mcp-builder/SKILL.md +15 -0
- package/.agents/skills/memory-cleanup/SKILL.md +21 -0
- package/.agents/skills/mode-selector/SKILL.md +17 -0
- package/.agents/skills/npm-publish/SKILL.md +16 -0
- package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
- package/.agents/skills/persona-panel/SKILL.md +17 -0
- package/.agents/skills/plan/SKILL.md +20 -0
- package/.agents/skills/playwright-driver/SKILL.md +20 -0
- package/.agents/skills/quality-gates/SKILL.md +20 -0
- package/.agents/skills/reconcile/SKILL.md +21 -0
- package/.agents/skills/remote-offload/SKILL.md +20 -0
- package/.agents/skills/repo-audit/SKILL.md +16 -0
- package/.agents/skills/session-end/SKILL.md +20 -0
- package/.agents/skills/session-plan/SKILL.md +20 -0
- package/.agents/skills/session-start/SKILL.md +20 -0
- package/.agents/skills/spinout/SKILL.md +16 -0
- package/.agents/skills/sunset-review/SKILL.md +16 -0
- package/.agents/skills/test-runner/SKILL.md +20 -0
- package/.agents/skills/tmux-layout/SKILL.md +21 -0
- package/.agents/skills/using-orchestrator/SKILL.md +17 -0
- package/.agents/skills/vault-mirror/SKILL.md +15 -0
- package/.agents/skills/vault-sync/SKILL.md +15 -0
- package/.agents/skills/wave-executor/SKILL.md +20 -0
- package/.agents/skills/write-executable-plan/SKILL.md +22 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/commands/autopilot.md +2 -2
- package/.cursor/commands/bootstrap.md +1 -1
- package/.cursor/commands/brainstorm.md +1 -1
- package/.cursor/commands/debug.md +1 -1
- package/.cursor/commands/discovery.md +1 -1
- package/.cursor/commands/dispatcher.md +2 -2
- package/.cursor/commands/eli5.md +2 -2
- package/.cursor/commands/eval.md +2 -2
- package/.cursor/commands/evolve.md +1 -1
- package/.cursor/commands/go.md +1 -1
- package/.cursor/commands/grill.md +2 -2
- package/.cursor/commands/memory-cleanup.md +2 -2
- package/.cursor/commands/persona-panel.md +1 -1
- package/.cursor/commands/plan.md +1 -1
- package/.cursor/commands/portfolio.md +1 -1
- package/.cursor/commands/reconcile.md +2 -2
- package/.cursor/commands/release.md +2 -2
- package/.cursor/commands/session.md +2 -2
- package/.cursor/commands/spinout.md +2 -2
- package/.cursor/commands/sunset-review.md +2 -2
- package/.cursor/commands/templates-ack.md +2 -2
- package/.cursor/commands/test.md +2 -2
- package/.cursor/skills/brainstorm/SKILL.md +1 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/quality-gates/SKILL.md +1 -1
- package/.cursor/skills/remote-offload/SKILL.md +13 -0
- package/.orchestrator/policy/blocked-commands.json +121 -0
- package/.orchestrator/policy/ecosystem.schema.json +66 -0
- package/.orchestrator/policy/quality-gates.example.json +16 -0
- package/.orchestrator/policy/quality-gates.schema.json +38 -0
- package/.orchestrator/policy/templates-policy.json +27 -0
- package/.orchestrator/policy/test-profiles.json +47 -0
- package/AGENTS.md +225 -0
- package/CHANGELOG.md +1401 -0
- package/NOTICE +11 -6
- package/README.md +127 -92
- package/agents/db-specialist.md +0 -1
- package/agents/eval-judge.md +1 -1
- package/agents/skill-applied-judge.md +1 -1
- package/assets/wave-lifecycle.svg +98 -0
- package/commands/release.md +6 -3
- package/commands/session.md +18 -3
- package/docs/README.md +4 -0
- package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
- package/docs/baseline.md +67 -0
- package/docs/ci-setup.md +249 -48
- package/docs/codex-setup.md +66 -22
- package/docs/components.md +37 -16
- package/docs/cursor-setup.md +6 -2
- package/docs/events-schema.md +51 -10
- package/docs/instruction-delivery.md +62 -0
- package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
- package/docs/migration-v4.md +341 -0
- package/docs/pi-setup.md +6 -1
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/rule-authoring.md +85 -19
- package/docs/scope-collision-guard.md +8 -8
- package/docs/session-config-reference.md +120 -61
- package/docs/session-config-template.md +40 -33
- package/docs/telemetry/telemetry-claims.md +11 -10
- package/docs/telemetry.md +187 -4
- package/docs/vault-docs-architecture.md +50 -11
- package/hooks/_lib/atomic-json.mjs +111 -0
- package/hooks/_lib/hook-import-set.json +1487 -0
- package/hooks/_lib/subagent-paths.mjs +143 -0
- package/hooks/_lib/subagent-transcript.mjs +562 -0
- package/hooks/config-protection.mjs +2 -2
- package/hooks/cwd-change-restore.mjs +11 -31
- package/hooks/enforce-commands.mjs +69 -0
- package/hooks/enforce-scope.mjs +35 -6
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +10 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +3 -3
- package/hooks/on-session-end.mjs +280 -14
- package/hooks/on-session-start.mjs +153 -4
- package/hooks/on-stop.mjs +371 -17
- package/hooks/operator-steer.mjs +2 -2
- package/hooks/post-bash-write-verify.mjs +189 -4
- package/hooks/post-edit-import-probe.mjs +344 -0
- package/hooks/post-subagent-discovery-validator.mjs +278 -392
- package/hooks/post-tool-batch-wave-signal.mjs +272 -44
- package/hooks/post-tool-failure-corrective-context.mjs +11 -34
- package/hooks/post-tooluse-frontend-slop.mjs +3 -3
- package/hooks/pre-bash-destructive-guard.mjs +39 -13
- package/hooks/pre-bash-memory-propose-audit.mjs +13 -7
- package/hooks/skill-invocation-telemetry.mjs +17 -5
- package/hooks/subagent-telemetry.mjs +24 -30
- package/monitors/monitors.json +3 -3
- package/package.json +9 -1
- package/pi/prompts/session.md +2 -2
- package/plugin.json +27 -0
- package/scripts/autopilot.mjs +26 -12
- package/scripts/backfill-abandoned-sessions.mjs +130 -15
- package/scripts/backfill-learnings-from-vault.mjs +9 -3
- package/scripts/dialectic-deriver.mjs +73 -8
- package/scripts/emit-event.mjs +10 -2
- package/scripts/export-hw-learnings.mjs +113 -1
- package/scripts/generate-agents-skills.mjs +378 -0
- package/scripts/generate-cursor-adapter.mjs +45 -8
- package/scripts/generate-hook-import-set.mjs +249 -0
- package/scripts/lib/agent-status.mjs +13 -2
- package/scripts/lib/auq/parse.mjs +5 -29
- package/scripts/lib/auto-dialectic.mjs +68 -0
- package/scripts/lib/auto-dream.mjs +38 -36
- package/scripts/lib/autonomy/suitability.mjs +6 -0
- package/scripts/lib/autopilot/loop.mjs +2 -2
- package/scripts/lib/autopilot/worktree-pipeline.mjs +82 -6
- package/scripts/lib/build-live-signals.mjs +25 -22
- package/scripts/lib/ci-status-banner.mjs +220 -75
- package/scripts/lib/codex/plugin-contract.mjs +82 -6
- package/scripts/lib/cold-start-detector.mjs +23 -14
- package/scripts/lib/config/auto-dream.mjs +2 -1
- package/scripts/lib/config/block-header.mjs +63 -0
- package/scripts/lib/config/block-preprocess.mjs +177 -0
- package/scripts/lib/config/broken-window.mjs +2 -1
- package/scripts/lib/config/cold-start.mjs +2 -1
- package/scripts/lib/config/config-protection.mjs +22 -2
- package/scripts/lib/config/context-coverage.mjs +2 -1
- package/scripts/lib/config/cross-repo.mjs +2 -1
- package/scripts/lib/config/custom-phases.mjs +2 -1
- package/scripts/lib/config/dialectic.mjs +2 -1
- package/scripts/lib/config/discovery-validator.mjs +9 -3
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
- package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
- package/scripts/lib/config/docs-orchestrator.mjs +2 -1
- package/scripts/lib/config/docs-staleness.mjs +2 -1
- package/scripts/lib/config/drift-check.mjs +2 -1
- package/scripts/lib/config/eval.mjs +2 -1
- package/scripts/lib/config/events-rotation.mjs +2 -1
- package/scripts/lib/config/evolve.mjs +8 -2
- package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
- package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
- package/scripts/lib/config/handover-gate.mjs +2 -1
- package/scripts/lib/config/health-endpoints.mjs +388 -0
- package/scripts/lib/config/issue-budget.mjs +2 -1
- package/scripts/lib/config/loop-guard.mjs +2 -1
- package/scripts/lib/config/memory.mjs +2 -1
- package/scripts/lib/config/moc-staleness.mjs +2 -1
- package/scripts/lib/config/persona-gate-wave.mjs +2 -1
- package/scripts/lib/config/private-config-dir.mjs +67 -0
- package/scripts/lib/config/reconcile.mjs +2 -1
- package/scripts/lib/config/remote-hosts.mjs +234 -0
- package/scripts/lib/config/section-extractor.mjs +7 -1
- package/scripts/lib/config/skill-evolution.mjs +2 -1
- package/scripts/lib/config/slopcheck.mjs +2 -1
- package/scripts/lib/config/state-md-lock.mjs +2 -1
- package/scripts/lib/config/templates-first.mjs +2 -1
- package/scripts/lib/config/test.mjs +2 -1
- package/scripts/lib/config/vault-integration.mjs +7 -1
- package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
- package/scripts/lib/config/vault-staleness.mjs +2 -1
- package/scripts/lib/config/vault-sync.mjs +2 -1
- package/scripts/lib/config/verification-auto-fix.mjs +2 -1
- package/scripts/lib/config/wave-reviewers.mjs +2 -1
- package/scripts/lib/config/worktree-orphans.mjs +2 -1
- package/scripts/lib/config.mjs +31 -3
- package/scripts/lib/convergence-monitor.mjs +82 -16
- package/scripts/lib/dispatcher/enumerate.mjs +2 -17
- package/scripts/lib/dispatcher/rank.mjs +124 -48
- package/scripts/lib/ecosystem-health.mjs +16 -2
- package/scripts/lib/eval/engine.mjs +9 -1
- package/scripts/lib/eval/session-resolve.mjs +23 -4
- package/scripts/lib/events-schema.mjs +48 -0
- package/scripts/lib/events.mjs +256 -7
- package/scripts/lib/evolve/autonomy-verdict.mjs +9 -4
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +18 -1
- package/scripts/lib/frontmatter-guard.mjs +131 -13
- package/scripts/lib/gates/gate-full.mjs +26 -0
- package/scripts/lib/gates/gate-helpers.mjs +76 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +3 -15
- package/scripts/lib/hardware-pattern-detector.mjs +18 -1
- package/scripts/lib/harness-audit/categories/category1.mjs +17 -6
- package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
- package/scripts/lib/host-identity.mjs +50 -11
- package/scripts/lib/instruction-budget-guard.mjs +171 -5
- package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
- package/scripts/lib/learnings/io.mjs +60 -6
- package/scripts/lib/memory-banner.mjs +20 -8
- package/scripts/lib/memory-proposals/store.mjs +30 -22
- package/scripts/lib/owner-config-banner.mjs +43 -6
- package/scripts/lib/owner-config-loader.mjs +21 -10
- package/scripts/lib/owner-interview.mjs +3 -3
- package/scripts/lib/owner-yaml.mjs +207 -14
- package/scripts/lib/peer-discovery.mjs +20 -2
- package/scripts/lib/platform.mjs +108 -15
- package/scripts/lib/plugin-update-banner.mjs +406 -0
- package/scripts/lib/project-hygiene.mjs +38 -2
- package/scripts/lib/qg-command-drift-banner.mjs +50 -12
- package/scripts/lib/quality-gate.mjs +133 -44
- package/scripts/lib/reconcile/emitter.mjs +68 -6
- package/scripts/lib/reconcile/engine.mjs +249 -9
- package/scripts/lib/reconcile/idempotency.mjs +37 -4
- package/scripts/lib/reconcile/writer.mjs +40 -18
- package/scripts/lib/scope-gate.mjs +36 -0
- package/scripts/lib/session-close-backfill.mjs +125 -18
- package/scripts/lib/session-discovery.mjs +57 -3
- package/scripts/lib/session-end/phase-skip.mjs +2 -2
- package/scripts/lib/session-id.mjs +12 -23
- package/scripts/lib/session-identity/own-session.mjs +187 -11
- package/scripts/lib/session-lock-shape.mjs +43 -0
- package/scripts/lib/session-lock.mjs +5 -10
- package/scripts/lib/session-registry.mjs +25 -9
- package/scripts/lib/session-schema/constants.mjs +36 -2
- package/scripts/lib/session-schema/validator.mjs +38 -4
- package/scripts/lib/session-start-probes.mjs +18 -1
- package/scripts/lib/session-transition.mjs +1 -1
- package/scripts/lib/sessions-canonical.mjs +446 -0
- package/scripts/lib/sessions-staleness-banner.mjs +18 -11
- package/scripts/lib/skill-health/join.mjs +17 -4
- package/scripts/lib/state-md.mjs +78 -0
- package/scripts/lib/sunset/walker.mjs +6 -0
- package/scripts/lib/telemetry/schema.mjs +255 -17
- package/scripts/lib/telemetry/sync.mjs +417 -24
- package/scripts/lib/tmux-layout/telemetry.mjs +14 -2
- package/scripts/lib/validate/check-agents-skills.mjs +327 -0
- package/scripts/lib/validate/check-agents.mjs +3 -3
- package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
- package/scripts/lib/validate/check-doc-cli-commands.mjs +9 -33
- package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +370 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
- package/scripts/lib/validate/check-owner-leakage.mjs +281 -20
- package/scripts/lib/validate/check-skill-links.mjs +163 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +455 -0
- package/scripts/lib/validate/check-untracked-test-deps.mjs +10 -0
- package/scripts/lib/validate/check-unwired-features.mjs +0 -9
- package/scripts/lib/validate/check-validator-registration.mjs +254 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +6 -28
- package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
- package/scripts/lib/validate/markdown-fences.mjs +196 -0
- package/scripts/lib/vault-backfill/template.mjs +63 -6
- package/scripts/lib/vault-mirror/process.mjs +165 -42
- package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
- package/scripts/lib/vault-status/board-lock.mjs +185 -0
- package/scripts/lib/vault-status/board-writer.mjs +174 -135
- package/scripts/lib/vault-status/narrative-mirror.mjs +129 -37
- package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
- package/scripts/lib/wave-executor/remote-dispatch.mjs +502 -0
- package/scripts/lib/wave-resource-gate.mjs +133 -7
- package/scripts/lib/wave-sizing.mjs +4 -1
- package/scripts/lib/wave-transcript-tail.mjs +142 -8
- package/scripts/materialize-wave-scope.mjs +32 -9
- package/scripts/memory-propose.mjs +146 -8
- package/scripts/migrate-cold-start-seed.mjs +4 -1
- package/scripts/parse-config.mjs +60 -3
- package/scripts/promote-vault-strict.mjs +4 -15
- package/scripts/release.mjs +337 -29
- package/scripts/repair-invalid-sessions.mjs +3 -3
- package/scripts/run-quality-gate.mjs +128 -11
- package/scripts/site-numbers.mjs +36 -4
- package/scripts/sweep-expired-learnings.mjs +90 -0
- package/scripts/sync-vault-schema.mjs +3 -1
- package/scripts/telemetry.mjs +2 -2
- package/scripts/validate-plugin.mjs +187 -0
- package/scripts/validate-wave-scope.mjs +28 -8
- package/scripts/vault-consolidate.mjs +3 -11
- package/scripts/vault-integration-watcher.mjs +2 -4
- package/scripts/vault-mirror.mjs +111 -26
- package/scripts/wave-scope-binding.mjs +215 -0
- package/skills/_shared/instruction-file-resolution.md +10 -0
- package/skills/_shared/parallel-aware-auq.md +31 -2
- package/skills/_shared/parallel-aware-preamble.md +18 -4
- package/skills/_shared/platform-tools.md +1 -1
- package/skills/_shared/state-ownership.md +1 -1
- package/skills/architecture/SKILL.md +7 -5
- package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
- package/skills/autopilot/SKILL.md +4 -18
- package/skills/claude-md-drift-check/SKILL.md +5 -1
- package/skills/claude-md-drift-check/checker.mjs +62 -2
- package/skills/convergence-monitoring/SIGNALS.md +55 -0
- package/skills/discovery/probes/vault-staleness.mjs +37 -13
- package/skills/discovery/probes-arch.md +20 -18
- package/skills/dispatcher/SKILL.md +3 -2
- package/skills/ecosystem-health/SKILL.md +4 -1
- package/skills/ecosystem-health/wizard.md +5 -0
- package/skills/evolve/SKILL.md +87 -11
- package/skills/frontmatter-guard/SKILL.md +11 -5
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +38 -2
- package/skills/remote-offload/SKILL.md +89 -0
- package/skills/session-end/SKILL.md +18 -905
- package/skills/session-end/phase-3-6-tail.md +19 -9
- package/skills/session-end/plan-verification.md +221 -155
- package/skills/session-end/references/phase-2-quality-gate.md +93 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
- package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
- package/skills/session-end/references/session-summary-template.md +62 -0
- package/skills/session-plan/SKILL.md +49 -0
- package/skills/session-start/SKILL.md +41 -900
- package/skills/session-start/phase-8-5-express-path.md +1 -1
- package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
- package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +155 -0
- package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
- package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
- package/skills/vault-sync/validator.mjs +21 -27
- package/skills/wave-executor/SKILL.md +16 -2
- package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
- package/skills/wave-executor/references/wave-loop-review.md +570 -0
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
- package/skills/wave-executor/wave-loop.md +14 -1271
- package/templates/_shared/journey-manifest.md +10 -6
- package/.cursor/commands/autopilot-multi.md +0 -14
- package/.cursor/commands/contract-version-bump.md +0 -14
- package/.cursor/commands/journey-audit.md +0 -14
- package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
- package/.cursor/skills/daily/SKILL.md +0 -12
- package/.cursor/skills/domain-model/SKILL.md +0 -13
- package/.cursor/skills/journey-audit/SKILL.md +0 -13
- package/.cursor/skills/skill-creator/SKILL.md +0 -13
- package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
- package/commands/autopilot-multi.md +0 -74
- package/commands/contract-version-bump.md +0 -28
- package/commands/journey-audit.md +0 -43
- package/pi/prompts/autopilot-multi.md +0 -12
- package/pi/prompts/contract-version-bump.md +0 -12
- package/pi/prompts/journey-audit.md +0 -12
- package/scripts/autopilot-multi.mjs +0 -885
- package/scripts/backfill-learnings-expires.mjs +0 -196
- package/scripts/backfill-learnings.mjs +0 -203
- package/scripts/fleet-instruction-scan.mjs +0 -141
- package/scripts/lib/autopilot/dep-graph.mjs +0 -417
- package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
- package/scripts/lib/webhook-url.mjs +0 -105
- package/scripts/lifecycle-sim-v6.mjs +0 -347
- package/scripts/migrate-learnings-jsonl.mjs +0 -189
- package/scripts/migrate-subagents-jsonl.mjs +0 -196
- package/scripts/upload-social-preview.mjs +0 -316
- package/skills/_shared/model-selection.md +0 -64
- package/skills/contract-version-bump/SKILL.md +0 -219
- package/skills/daily/SKILL.md +0 -222
- package/skills/daily/generate.sh +0 -92
- package/skills/daily/templates/daily.md.tpl +0 -36
- package/skills/journey-audit/SKILL.md +0 -269
- package/skills/skill-creator/SKILL.md +0 -168
- package/skills/ubiquitous-language/SKILL.md +0 -97
- package/skills/vault-sync/package-lock.json +0 -40
- /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
- /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* defaults from the policy file or built-in defaults apply.
|
|
15
15
|
* --files <f1,f2,...> Comma-separated file list (incremental + per-file).
|
|
16
16
|
* --session-start-ref <r> Git ref for diff base (incremental, to find changed files).
|
|
17
|
+
* --ledger-root <path> Repo root the telemetry event is pinned to (and the
|
|
18
|
+
* root session attribution is read from). Only the
|
|
19
|
+
* pre-push hook passes it; see the emission block below.
|
|
17
20
|
* -h, --help Show this help and exit.
|
|
18
21
|
*
|
|
19
22
|
* Exit codes:
|
|
@@ -31,8 +34,8 @@
|
|
|
31
34
|
* scripts/lib/gates/gate-{baseline,incremental,full,per-file}.mjs
|
|
32
35
|
*/
|
|
33
36
|
|
|
34
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
35
|
-
import { join, dirname } from 'node:path';
|
|
37
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
38
|
+
import { join, dirname, resolve } from 'node:path';
|
|
36
39
|
import { fileURLToPath } from 'node:url';
|
|
37
40
|
import { spawnSync } from 'node:child_process';
|
|
38
41
|
|
|
@@ -80,7 +83,7 @@ const argv = process.argv.slice(2);
|
|
|
80
83
|
if (argv.includes('-h') || argv.includes('--help')) {
|
|
81
84
|
process.stdout.write(
|
|
82
85
|
'Usage: run-quality-gate.mjs --variant <variant> [--config <json-or-file>] ' +
|
|
83
|
-
'[--files <file1,file2,...>] [--session-start-ref <ref>]\n\n' +
|
|
86
|
+
'[--files <file1,file2,...>] [--session-start-ref <ref>] [--ledger-root <path>]\n\n' +
|
|
84
87
|
'Variants: baseline, incremental, full-gate, per-file\n\n' +
|
|
85
88
|
'Exit codes:\n' +
|
|
86
89
|
' 0 — pass (non-blocking variants always exit 0)\n' +
|
|
@@ -94,6 +97,7 @@ let variant = '';
|
|
|
94
97
|
let config = '';
|
|
95
98
|
let files = '';
|
|
96
99
|
let sessionStartRef = '';
|
|
100
|
+
let ledgerRootArg = '';
|
|
97
101
|
|
|
98
102
|
for (let i = 0; i < argv.length; i++) {
|
|
99
103
|
const arg = argv[i];
|
|
@@ -114,6 +118,10 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
114
118
|
if (i + 1 >= argv.length) die('Missing value for --session-start-ref');
|
|
115
119
|
sessionStartRef = argv[++i];
|
|
116
120
|
break;
|
|
121
|
+
case '--ledger-root':
|
|
122
|
+
if (i + 1 >= argv.length) die('Missing value for --ledger-root');
|
|
123
|
+
ledgerRootArg = argv[++i];
|
|
124
|
+
break;
|
|
117
125
|
default:
|
|
118
126
|
die(`Unknown argument: ${arg}`);
|
|
119
127
|
}
|
|
@@ -208,6 +216,76 @@ function suiteCountsFromGateStdout(stdout) {
|
|
|
208
216
|
return admitSuiteCounts(test);
|
|
209
217
|
}
|
|
210
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Lift `test.failed_files` out of a gate sub-script's JSON stdout envelope.
|
|
221
|
+
*
|
|
222
|
+
* Envelope adapter, same posture as {@link suiteCountsFromGateStdout}: it
|
|
223
|
+
* decides only whether a NAMED-FILE measurement exists, never what the names
|
|
224
|
+
* mean. `null` — never `[]` — for every non-measurement, so the caller OMITS
|
|
225
|
+
* the key instead of publishing an empty array that reads as "no file failed".
|
|
226
|
+
*
|
|
227
|
+
* Only `gate-full.mjs` publishes the key, and only when the runner printed a
|
|
228
|
+
* file-level summary; every other variant emits a bare status string.
|
|
229
|
+
*
|
|
230
|
+
* Never throws.
|
|
231
|
+
*
|
|
232
|
+
* @param {string} stdout — the gate sub-script's captured stdout.
|
|
233
|
+
* @returns {string[]|null}
|
|
234
|
+
*/
|
|
235
|
+
function failedFilesFromGateStdout(stdout) {
|
|
236
|
+
if (typeof stdout !== 'string' || !stdout.trim()) return null;
|
|
237
|
+
let parsed;
|
|
238
|
+
try {
|
|
239
|
+
parsed = JSON.parse(stdout);
|
|
240
|
+
} catch {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
const files = parsed?.test?.failed_files;
|
|
244
|
+
if (!Array.isArray(files) || files.length === 0) return null;
|
|
245
|
+
const named = files.filter((f) => typeof f === 'string' && f.trim());
|
|
246
|
+
return named.length > 0 ? named : null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Validate and resolve the `--ledger-root` flag (see the telemetry block below).
|
|
251
|
+
*
|
|
252
|
+
* Only the pre-push hook passes this, and it hands over a path the gate then
|
|
253
|
+
* WRITES to — so a typo must not silently create an `.orchestrator/metrics/`
|
|
254
|
+
* tree somewhere arbitrary. The check is therefore two-part: the value must be
|
|
255
|
+
* an existing DIRECTORY, and it must already contain an `.orchestrator/`
|
|
256
|
+
* directory — the marker of a root this harness has already been initialised in.
|
|
257
|
+
*
|
|
258
|
+
* The alternative check (`git rev-parse --show-toplevel` with cwd = that path,
|
|
259
|
+
* compared against the value) is rejected on cost: it spawns a process on every
|
|
260
|
+
* gate run to prove a property two `statSync` calls already prove. The one case
|
|
261
|
+
* it accepts and this one rejects — a git root that has never run the
|
|
262
|
+
* orchestrator — is precisely the case with no ledger to pin to.
|
|
263
|
+
*
|
|
264
|
+
* A bad value NEVER crashes the gate: it warns once on stderr and returns
|
|
265
|
+
* `null`, which restores the previous resolution
|
|
266
|
+
* (`CLAUDE_PROJECT_DIR ?? CODEX_PROJECT_DIR ?? repoRoot`) at the call sites.
|
|
267
|
+
* The gate's exit code is the authoritative output; telemetry is best-effort.
|
|
268
|
+
*
|
|
269
|
+
* @param {string} value — raw flag value (`''` when the flag was not passed).
|
|
270
|
+
* @returns {string|null} absolute, validated root — or `null` to fall back.
|
|
271
|
+
*/
|
|
272
|
+
function resolveLedgerRoot(value) {
|
|
273
|
+
const raw = (value || '').trim();
|
|
274
|
+
if (!raw) return null;
|
|
275
|
+
const abs = resolve(raw);
|
|
276
|
+
try {
|
|
277
|
+
if (statSync(abs).isDirectory() && statSync(join(abs, '.orchestrator')).isDirectory()) {
|
|
278
|
+
return abs;
|
|
279
|
+
}
|
|
280
|
+
} catch { /* falls through to the warn below */ }
|
|
281
|
+
warn(
|
|
282
|
+
`--ledger-root '${raw}' is not an initialised project root ` +
|
|
283
|
+
'(existing directory containing .orchestrator/) — falling back to the default ' +
|
|
284
|
+
'telemetry destination.',
|
|
285
|
+
);
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
211
289
|
/**
|
|
212
290
|
* Resolve the active wave number from the wave-scope sidecar (#966 step 1).
|
|
213
291
|
*
|
|
@@ -325,24 +403,63 @@ if (result.error && typeof result.status !== 'number') {
|
|
|
325
403
|
// (single emission path). `sessionAttribution` is the shared helper in
|
|
326
404
|
// events.mjs (#941); this CLI wrapper runs against the CWD `repoRoot`, so the
|
|
327
405
|
// bare emitEvent destination (SO_PROJECT_DIR default) is correct here.
|
|
406
|
+
//
|
|
407
|
+
// EXCEPT under the pre-push hook, which is the one caller that runs the gate in
|
|
408
|
+
// a tree that is about to be DELETED. `.husky/pre-push` materialises the tracked
|
|
409
|
+
// tree into a temp dir and deliberately scrubs every `*PROJECT_DIR` name before
|
|
410
|
+
// invoking the gate there, so `getProjectDir()` resolves to that temp tree (it
|
|
411
|
+
// carries both a CLAUDE.md (or AGENTS.md) and a .git) and the record lands in
|
|
412
|
+
// `<tmp>/.orchestrator/metrics/events.jsonl`, which the hook's EXIT trap then
|
|
413
|
+
// removes. Measured 2026-09-06: a pre-push run that BLOCKED a push left no
|
|
414
|
+
// `orchestrator.quality_gate.failed` line in this repo's ledger at all — the
|
|
415
|
+
// gate failure was, by construction, the one event that could never be recorded.
|
|
416
|
+
//
|
|
417
|
+
// `--ledger-root` is that hook's channel for handing back the root it already
|
|
418
|
+
// knows (`git rev-parse --show-toplevel`, read BEFORE it cds). It pins ONLY the
|
|
419
|
+
// telemetry destination and the attribution root — every other path the gate
|
|
420
|
+
// resolves stays inside the tree actually under test, which is the whole point
|
|
421
|
+
// of the materialisation. Absent (every other caller) → unchanged behaviour:
|
|
422
|
+
// `emitEvent`'s own default resolution.
|
|
423
|
+
//
|
|
424
|
+
// It is an ARGV FLAG and not an env var, and that is load-bearing. Measured
|
|
425
|
+
// 2026-09-06 with the env-var form: `SO_GATE_LEDGER_ROOT=$tmp npx vitest run
|
|
426
|
+
// tests/scripts/run-quality-gate.test.mjs -t "telemetry emission"` → `8 failed |
|
|
427
|
+
// 1 passed`. The chain was: hook exports the var → `npm run quality-gate` →
|
|
428
|
+
// `gate-full.mjs` spawns `npm test` → every vitest worker inherits it → the
|
|
429
|
+
// suite's own gate spawns spread `...process.env`, so the pinned root outranked
|
|
430
|
+
// their per-test project dir and the gate's telemetry tests wrote to the hook's
|
|
431
|
+
// root. The gate that releases 4.0.0 would have blocked on itself. An env var is
|
|
432
|
+
// inherited by every descendant; a flag reaches exactly one process.
|
|
433
|
+
//
|
|
328
434
|
// Best-effort: a telemetry failure must NEVER alter the gate's authoritative
|
|
329
435
|
// exit code — which is why the counts parse also lives inside this try.
|
|
330
436
|
const exitCode = result.status ?? 1;
|
|
437
|
+
const ledgerRoot = resolveLedgerRoot(ledgerRootArg);
|
|
331
438
|
try {
|
|
332
439
|
const counts = suiteCountsFromGateStdout(gateStdout);
|
|
440
|
+
// The names behind `counts.failed`. Absent, never `[]` — see
|
|
441
|
+
// `failedFilesFromGateStdout`.
|
|
442
|
+
const failedFiles = failedFilesFromGateStdout(gateStdout);
|
|
333
443
|
// Wave-scope sidecar is read from the SAME project dir the event lands in
|
|
334
444
|
// (emitEvent's own destination precedence), so a tmp-scoped run cannot pick
|
|
335
445
|
// up the host repo's live wave. Mirrors the hook's projectDir resolution.
|
|
336
446
|
const waveNumber = resolveWaveNumber(
|
|
337
|
-
process.env.CLAUDE_PROJECT_DIR ?? process.env.CODEX_PROJECT_DIR ?? repoRoot,
|
|
447
|
+
ledgerRoot ?? process.env.CLAUDE_PROJECT_DIR ?? process.env.CODEX_PROJECT_DIR ?? repoRoot,
|
|
448
|
+
);
|
|
449
|
+
await emitEvent(
|
|
450
|
+
`orchestrator.quality_gate.${exitCode === 0 ? 'passed' : 'failed'}`,
|
|
451
|
+
{
|
|
452
|
+
variant,
|
|
453
|
+
exit_code: exitCode,
|
|
454
|
+
...(counts ? { counts } : {}),
|
|
455
|
+
...(failedFiles ? { failed_files: failedFiles } : {}),
|
|
456
|
+
...(waveNumber !== null ? { wave_number: waveNumber } : {}),
|
|
457
|
+
...sessionAttribution(ledgerRoot ?? repoRoot),
|
|
458
|
+
},
|
|
459
|
+
// `{}` is byte-identical to omitting the argument (`opts.repoRoot ??
|
|
460
|
+
// getProjectDir()`), so the default path is untouched.
|
|
461
|
+
ledgerRoot ? { repoRoot: ledgerRoot } : {},
|
|
338
462
|
);
|
|
339
|
-
await emitEvent(`orchestrator.quality_gate.${exitCode === 0 ? 'passed' : 'failed'}`, {
|
|
340
|
-
variant,
|
|
341
|
-
exit_code: exitCode,
|
|
342
|
-
...(counts ? { counts } : {}),
|
|
343
|
-
...(waveNumber !== null ? { wave_number: waveNumber } : {}),
|
|
344
|
-
...sessionAttribution(repoRoot),
|
|
345
|
-
});
|
|
346
463
|
} catch { /* best-effort telemetry — gate result is authoritative */ }
|
|
347
464
|
|
|
348
465
|
process.exit(exitCode);
|
package/scripts/site-numbers.mjs
CHANGED
|
@@ -79,6 +79,7 @@ import { execFileSync } from 'node:child_process';
|
|
|
79
79
|
import { fileURLToPath } from 'node:url';
|
|
80
80
|
|
|
81
81
|
import { writeStdoutLineSync, writeJsonAtomicSync } from './lib/io.mjs';
|
|
82
|
+
import { readCanonicalSessions } from './lib/sessions-canonical.mjs';
|
|
82
83
|
|
|
83
84
|
/** Machine-readable schema tag for the --json envelope. */
|
|
84
85
|
export const SCHEMA = 'site-numbers/1';
|
|
@@ -401,10 +402,31 @@ export const METRIC_DEFS = Object.freeze([
|
|
|
401
402
|
// Gitignored ledger (`.gitignore`: local-only observability data) — absent
|
|
402
403
|
// in every fresh clone, CI checkout and tarball build.
|
|
403
404
|
snapshotFallback: true,
|
|
404
|
-
|
|
405
|
+
// #1186: `grep -c` over-counts — sessions.jsonl is append-only, so the same
|
|
406
|
+
// physical session can carry more than one line (crash-recovery re-appends,
|
|
407
|
+
// #1068 stub/supersede pairs). readCanonicalSessions collapses those to one
|
|
408
|
+
// record per session_id before the count; see scripts/lib/sessions-canonical.mjs
|
|
409
|
+
// for the three collapse rules. `learnings` below stays a raw line count —
|
|
410
|
+
// that ledger has no session_id-shaped identity to dedupe on.
|
|
411
|
+
// Measured on THIS repo's own ledger @ 2ccea0f2: raw line count 289 vs
|
|
412
|
+
// canonical (deduped) count 278 — an over-count of 11 records, which is the
|
|
413
|
+
// bug's magnitude this migration removes.
|
|
414
|
+
source: 'grep -c . .orchestrator/metrics/sessions.jsonl (then deduped per session_id — see readCanonicalSessions, #1186)',
|
|
405
415
|
compute: (root) => {
|
|
406
|
-
const
|
|
407
|
-
|
|
416
|
+
const file = join(root, '.orchestrator', 'metrics', 'sessions.jsonl');
|
|
417
|
+
// Same absent/non-file guard as countJsonlEntries, kept explicit here
|
|
418
|
+
// because readCanonicalSessions returns [] (not null) on a missing ledger
|
|
419
|
+
// — collapsing that into fmtCount(0) would silently defeat the
|
|
420
|
+
// snapshotFallback precedence above (a fresh clone would report "0
|
|
421
|
+
// sessions" instead of falling back to the tracked snapshot).
|
|
422
|
+
if (!existsSync(file) || !statSync(file).isFile()) return null;
|
|
423
|
+
// Same reader every other #1186 consumer uses (autopilot.mjs,
|
|
424
|
+
// build-live-signals.mjs, telemetry/sync.mjs) — a record with no
|
|
425
|
+
// `session_id` is dropped rather than counted, because it cannot be
|
|
426
|
+
// identified for dedup. Measured @ 2ccea0f2 on this repo's own ledger:
|
|
427
|
+
// 0 such rows (raw 289 lines == 289 identified), so the drop is inert
|
|
428
|
+
// here; it only matters for a future malformed/legacy row.
|
|
429
|
+
return fmtCount(readCanonicalSessions({ filePath: file }).length);
|
|
408
430
|
},
|
|
409
431
|
},
|
|
410
432
|
{
|
|
@@ -623,7 +645,17 @@ export function collect(root) {
|
|
|
623
645
|
]) {
|
|
624
646
|
const r = countJsonlEntries(join(root, '.orchestrator', 'metrics', file));
|
|
625
647
|
if (r && r.malformed > 0) {
|
|
626
|
-
|
|
648
|
+
// #1186: the two ledgers now disagree on what a malformed line DOES to
|
|
649
|
+
// the published count. `learnings` is still the raw countJsonlEntries
|
|
650
|
+
// total, which counts a malformed line as an entry regardless of its
|
|
651
|
+
// content. `sessions` is readCanonicalSessions, whose parse loop skips a
|
|
652
|
+
// line it cannot JSON.parse — so a malformed session line is silently
|
|
653
|
+
// ABSENT from the count rather than included in it.
|
|
654
|
+
const consequence =
|
|
655
|
+
name === 'sessions'
|
|
656
|
+
? `they are DROPPED from the sessions count (readCanonicalSessions skips unparseable lines)`
|
|
657
|
+
: `the ${name} count includes them`;
|
|
658
|
+
warnings.push(`${file}: ${r.malformed} non-empty line(s) are not valid JSON — ${consequence}`);
|
|
627
659
|
}
|
|
628
660
|
}
|
|
629
661
|
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* Usage:
|
|
24
24
|
* node scripts/sweep-expired-learnings.mjs [--prune] [--dry-run|--apply] [--json]
|
|
25
25
|
* [--grace-days N] [--entries PATH] [--file PATH] [--archive PATH]
|
|
26
|
+
* [--appended N] [--boosted M] [--duration-ms D] [--skipped a,b] [--repo-root PATH]
|
|
26
27
|
*
|
|
27
28
|
* Flags:
|
|
28
29
|
* --prune Decision-driven prune+consolidate+rewrite instead of the
|
|
@@ -40,6 +41,23 @@
|
|
|
40
41
|
* store.
|
|
41
42
|
* --file PATH Learnings store (default: .orchestrator/metrics/learnings.jsonl)
|
|
42
43
|
* --archive PATH Archive sidecar (default: .orchestrator/metrics/learnings-archive.jsonl)
|
|
44
|
+
* --appended N PRUNE ONLY (#1206). New learnings written this `/evolve`
|
|
45
|
+
* run (Step 3.5(4)); folded into the mechanical
|
|
46
|
+
* `orchestrator.evolve.completed` emit alongside this
|
|
47
|
+
* call's own `pruned` count. Default 0.
|
|
48
|
+
* --boosted M PRUNE ONLY (#1206). Existing learnings reinforced this
|
|
49
|
+
* run (Step 3.5(2)). Default 0.
|
|
50
|
+
* --duration-ms D PRUNE ONLY (#1206). Elapsed ms since the run's telemetry
|
|
51
|
+
* start marker. Default 0.
|
|
52
|
+
* --skipped a,b PRUNE ONLY (#1206). Comma-separated list of optional
|
|
53
|
+
* steps that ran but were themselves skipped this run
|
|
54
|
+
* (HR-105 — e.g. `skill-evolution-off`). Default none.
|
|
55
|
+
* --repo-root PATH PRUNE ONLY (#1206). Repo root the
|
|
56
|
+
* `orchestrator.evolve.completed` record is pinned to.
|
|
57
|
+
* No default and NO process.cwd() fallback (#1119) — when
|
|
58
|
+
* omitted, no event is emitted at all (stderr WARN, exit
|
|
59
|
+
* code unaffected). Emission is best-effort and never
|
|
60
|
+
* changes this command's exit code or stdout contract.
|
|
43
61
|
*
|
|
44
62
|
* Exit codes:
|
|
45
63
|
* 0 Success (including no-op when nothing is archive-eligible)
|
|
@@ -51,6 +69,7 @@
|
|
|
51
69
|
import { existsSync } from 'node:fs';
|
|
52
70
|
import { sweepExpiredLearnings, pruneLearnings } from './lib/learnings/expiry-sweep.mjs';
|
|
53
71
|
import { readLearnings } from './lib/learnings/io.mjs';
|
|
72
|
+
import { emitEvolveCompleted } from './lib/learnings/evolve-telemetry.mjs';
|
|
54
73
|
|
|
55
74
|
const DEFAULT_FILE = '.orchestrator/metrics/learnings.jsonl';
|
|
56
75
|
const DEFAULT_ARCHIVE = '.orchestrator/metrics/learnings-archive.jsonl';
|
|
@@ -97,6 +116,20 @@ function parseArgs(argv) {
|
|
|
97
116
|
entries: null,
|
|
98
117
|
file: DEFAULT_FILE,
|
|
99
118
|
archive: DEFAULT_ARCHIVE,
|
|
119
|
+
appended: 0,
|
|
120
|
+
boosted: 0,
|
|
121
|
+
durationMs: 0,
|
|
122
|
+
skipped: [],
|
|
123
|
+
// #1119 — NO process.cwd() fallback. Most CLI callers (every test in this
|
|
124
|
+
// file except the SKILL.md-extraction fixture, which sets its own tmp
|
|
125
|
+
// `cwd`) invoke this script from the repo root without `--repo-root`; a
|
|
126
|
+
// cwd fallback would silently append synthetic `orchestrator.evolve.
|
|
127
|
+
// completed` records to the operator's REAL fleet ledger on every
|
|
128
|
+
// `--prune --apply` run in `npm test`. `emitEvolveCompleted()` already
|
|
129
|
+
// refuses to emit (stderr WARN, no throw) when repoRoot is absent — same
|
|
130
|
+
// fail-closed contract as `emitReconcileCompleted` / `express-path.mjs`.
|
|
131
|
+
repoRoot: null,
|
|
132
|
+
telemetryExplicit: false,
|
|
100
133
|
};
|
|
101
134
|
for (let i = 0; i < argv.length; i++) {
|
|
102
135
|
const a = argv[i];
|
|
@@ -122,6 +155,37 @@ function parseArgs(argv) {
|
|
|
122
155
|
args.file = argv[++i];
|
|
123
156
|
} else if (a === '--archive') {
|
|
124
157
|
args.archive = argv[++i];
|
|
158
|
+
} else if (a === '--appended') {
|
|
159
|
+
const raw = argv[++i];
|
|
160
|
+
const v = Number(raw);
|
|
161
|
+
if (!Number.isInteger(v) || v < 0) {
|
|
162
|
+
usageError(`--appended requires a non-negative integer, got: ${raw}`);
|
|
163
|
+
}
|
|
164
|
+
args.appended = v;
|
|
165
|
+
args.telemetryExplicit = true;
|
|
166
|
+
} else if (a === '--boosted') {
|
|
167
|
+
const raw = argv[++i];
|
|
168
|
+
const v = Number(raw);
|
|
169
|
+
if (!Number.isInteger(v) || v < 0) {
|
|
170
|
+
usageError(`--boosted requires a non-negative integer, got: ${raw}`);
|
|
171
|
+
}
|
|
172
|
+
args.boosted = v;
|
|
173
|
+
args.telemetryExplicit = true;
|
|
174
|
+
} else if (a === '--duration-ms') {
|
|
175
|
+
const raw = argv[++i];
|
|
176
|
+
const v = Number(raw);
|
|
177
|
+
if (!Number.isFinite(v) || v < 0) {
|
|
178
|
+
usageError(`--duration-ms requires a non-negative number, got: ${raw}`);
|
|
179
|
+
}
|
|
180
|
+
args.durationMs = v;
|
|
181
|
+
args.telemetryExplicit = true;
|
|
182
|
+
} else if (a === '--skipped') {
|
|
183
|
+
const raw = argv[++i];
|
|
184
|
+
args.skipped = typeof raw === 'string' ? raw.split(',').filter((s) => s.length > 0) : [];
|
|
185
|
+
args.telemetryExplicit = true;
|
|
186
|
+
} else if (a === '--repo-root') {
|
|
187
|
+
args.repoRoot = argv[++i];
|
|
188
|
+
args.telemetryExplicit = true;
|
|
125
189
|
} else if (a === '--help' || a === '-h') {
|
|
126
190
|
printHelp();
|
|
127
191
|
process.exit(0);
|
|
@@ -140,6 +204,12 @@ function parseArgs(argv) {
|
|
|
140
204
|
if (!args.prune && args.entries !== null) {
|
|
141
205
|
usageError('--entries is only valid with --prune');
|
|
142
206
|
}
|
|
207
|
+
if (!args.prune && args.telemetryExplicit) {
|
|
208
|
+
usageError(
|
|
209
|
+
'--appended/--boosted/--duration-ms/--skipped/--repo-root are only valid with --prune ' +
|
|
210
|
+
'(the sweep path never emits orchestrator.evolve.completed)',
|
|
211
|
+
);
|
|
212
|
+
}
|
|
143
213
|
return args;
|
|
144
214
|
}
|
|
145
215
|
|
|
@@ -265,6 +335,26 @@ async function runPrune(args) {
|
|
|
265
335
|
...result,
|
|
266
336
|
};
|
|
267
337
|
|
|
338
|
+
// #1206 — the ONE mechanical call site for `orchestrator.evolve.completed`:
|
|
339
|
+
// this `--prune --apply` invocation IS `/evolve analyze`'s Step 3.5(5) store
|
|
340
|
+
// write, so folding the emit in here (instead of a separate
|
|
341
|
+
// `emit-event.mjs` call in skill prose) means the event can no longer be
|
|
342
|
+
// forgotten independently of the write it reports on. Gated on `!dryRun` —
|
|
343
|
+
// a `--prune --dry-run` preview (the docs' own recommended pre-check for a
|
|
344
|
+
// hand-assembled `--entries` sidecar) never wrote anything, so it must not
|
|
345
|
+
// report a completed run either. Best-effort — never changes this
|
|
346
|
+
// command's exit code or stdout contract.
|
|
347
|
+
if (!args.dryRun) {
|
|
348
|
+
await emitEvolveCompleted({
|
|
349
|
+
repoRoot: args.repoRoot,
|
|
350
|
+
appended: args.appended,
|
|
351
|
+
boosted: args.boosted,
|
|
352
|
+
pruned: result.archived,
|
|
353
|
+
durationMs: args.durationMs,
|
|
354
|
+
skipped: args.skipped,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
268
358
|
if (args.json) {
|
|
269
359
|
process.stdout.write(JSON.stringify(summary) + '\n');
|
|
270
360
|
} else {
|
|
@@ -141,7 +141,9 @@ function stripCommentLine(line) {
|
|
|
141
141
|
return null;
|
|
142
142
|
}
|
|
143
143
|
// Single-line doc comment
|
|
144
|
-
|
|
144
|
+
// A bare `//` (blank line inside a canonical comment run) is a comment too —
|
|
145
|
+
// without this the generated block carried a stray `//` (measured 2026-09-03).
|
|
146
|
+
if (trimmed === '//' || (trimmed.startsWith('// ') && !trimmed.startsWith('// ──'))) {
|
|
145
147
|
// Keep structural separator comments, drop narrative ones
|
|
146
148
|
return null;
|
|
147
149
|
}
|
package/scripts/telemetry.mjs
CHANGED
|
@@ -33,7 +33,7 @@ import { queueStats } from './lib/telemetry/queue.mjs';
|
|
|
33
33
|
import { flush, buildBatch } from './lib/telemetry/sync.mjs';
|
|
34
34
|
import { loadOwnerConfig } from './lib/owner-yaml.mjs';
|
|
35
35
|
import { readPluginVersionFromPackageJson } from './lib/bootstrap-lock-freshness.mjs';
|
|
36
|
-
import {
|
|
36
|
+
import { getPluginRoot } from './lib/platform.mjs';
|
|
37
37
|
|
|
38
38
|
const EXIT_OK = 0;
|
|
39
39
|
const EXIT_USER = 1;
|
|
@@ -69,7 +69,7 @@ ENV KILL-SWITCHES
|
|
|
69
69
|
|
|
70
70
|
/** Plugin version for `--version` — single-sourced via readPluginVersionFromPackageJson (null → 'unknown'). */
|
|
71
71
|
function readPkgVersion() {
|
|
72
|
-
return readPluginVersionFromPackageJson(
|
|
72
|
+
return readPluginVersionFromPackageJson(getPluginRoot()) ?? 'unknown';
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// ---------------------------------------------------------------------------
|
|
@@ -94,6 +94,42 @@ function runCheck(script) {
|
|
|
94
94
|
return result.status ?? 1;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Advisory second pass for `check-skill-script-paths.mjs` (#1208): run it a
|
|
99
|
+
* second time with `--strict-sh --json` so the flag has a run surface inside
|
|
100
|
+
* validate-plugin, without promoting `.sh` findings to blocking here — the
|
|
101
|
+
* BLOCKING `.mjs`-only call above stays exactly as it is. Same non-blocking
|
|
102
|
+
* shape as the `check-unwired-features.mjs` / `check-learning-provenance.mjs`
|
|
103
|
+
* / `check-vcs-repo-flag.mjs` calls further down (exit code deliberately
|
|
104
|
+
* ignored; `checkFailed` is never set from this call). `runCheck()` cannot be
|
|
105
|
+
* reused here because it always spawns `[script, PLUGIN_ROOT]` with no room
|
|
106
|
+
* for extra flags, so this is a small dedicated spawn instead.
|
|
107
|
+
*
|
|
108
|
+
* @returns {void}
|
|
109
|
+
*/
|
|
110
|
+
function runStrictShAdvisory() {
|
|
111
|
+
const result = spawnSync(
|
|
112
|
+
'node',
|
|
113
|
+
[path.join(VALIDATE_DIR, 'check-skill-script-paths.mjs'), PLUGIN_ROOT, '--strict-sh', '--json'],
|
|
114
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
|
|
115
|
+
);
|
|
116
|
+
let parsed;
|
|
117
|
+
try {
|
|
118
|
+
parsed = JSON.parse(result.stdout || '');
|
|
119
|
+
} catch {
|
|
120
|
+
process.stdout.write(
|
|
121
|
+
' WARN: check-skill-script-paths --strict-sh: could not parse JSON output (advisory)\n',
|
|
122
|
+
);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const shDead = (parsed.findings || []).filter(
|
|
126
|
+
(f) => f.kind === 'missing-path' && f.path.endsWith('.sh'),
|
|
127
|
+
).length;
|
|
128
|
+
process.stdout.write(
|
|
129
|
+
` check-skill-script-paths --strict-sh: ${shDead} dead .sh citation(s) (advisory)\n`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
97
133
|
/**
|
|
98
134
|
* Run the surface-count drift family (issue #663) from the claude-md-drift-check
|
|
99
135
|
* checker over the doc surfaces that carry artifact counts (README.md +
|
|
@@ -175,6 +211,105 @@ function runDriftCheck() {
|
|
|
175
211
|
return 0;
|
|
176
212
|
}
|
|
177
213
|
|
|
214
|
+
/**
|
|
215
|
+
* BLOCKING (#1224): the committed hook-reachable allowlist
|
|
216
|
+
* (`hooks/_lib/hook-import-set.json`) must match a fresh crawl of the four hook
|
|
217
|
+
* manifests. The allowlist is what gates `hooks/post-edit-import-probe.mjs`, so
|
|
218
|
+
* a stale set silently NARROWS that probe — a newly hook-reachable module would
|
|
219
|
+
* be edited without ever being checked, which is precisely the 2026-09-04
|
|
220
|
+
* host-wide-block incident class the probe exists to catch. A rotting allowlist
|
|
221
|
+
* is therefore a real defect, not a cosmetic drift, hence FAIL rather than WARN.
|
|
222
|
+
*
|
|
223
|
+
* `scripts/generate-hook-import-set.mjs --check` owns the comparison (it exits 1
|
|
224
|
+
* on drift); this adapter only translates it into validate-plugin's vocabulary.
|
|
225
|
+
* `runCheck()` cannot be reused — it spawns from `scripts/lib/validate/` and
|
|
226
|
+
* passes PLUGIN_ROOT positionally, while the generator takes `--plugin-root`.
|
|
227
|
+
*
|
|
228
|
+
* @returns {number} 0 when in sync, 1 on drift or a generator error.
|
|
229
|
+
*/
|
|
230
|
+
function runHookImportSetCheck() {
|
|
231
|
+
console.log('--- Check: hook-import-set drift (#1224) ---');
|
|
232
|
+
const script = path.join(SCRIPT_DIR, 'generate-hook-import-set.mjs');
|
|
233
|
+
const result = spawnSync(
|
|
234
|
+
process.execPath,
|
|
235
|
+
[script, '--plugin-root', PLUGIN_ROOT, '--check'],
|
|
236
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
|
|
237
|
+
);
|
|
238
|
+
const combined = ((result.stdout ?? '') + (result.stderr ?? '')).trim();
|
|
239
|
+
|
|
240
|
+
// A silent exit 0 is NOT a pass. The generator's CLI entry is gated on
|
|
241
|
+
// `import.meta.url === file://${process.argv[1]}`, which does not hold when
|
|
242
|
+
// the spawned path traverses a symlink (measured: `/tmp` → `/private/tmp` on
|
|
243
|
+
// macOS makes the whole CLI body a no-op that still exits 0). Demanding the
|
|
244
|
+
// in-sync marker turns that failure mode from a silent pass into a finding.
|
|
245
|
+
const inSync = combined.match(/(\d+) modules, in sync/);
|
|
246
|
+
if (result.status === 0 && inSync) {
|
|
247
|
+
console.log(` PASS: hook-import-set: in sync (${inSync[1]} entries)`);
|
|
248
|
+
totalPass += 1;
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
if (result.status === 0) {
|
|
252
|
+
console.log(' FAIL: hook-import-set: generator exited 0 without reporting a comparison '
|
|
253
|
+
+ `(no in-sync marker in output: ${JSON.stringify(combined.slice(0, 120))})`);
|
|
254
|
+
totalFail += 1;
|
|
255
|
+
return 1;
|
|
256
|
+
}
|
|
257
|
+
const detail = combined.split('\n').filter(Boolean).pop()
|
|
258
|
+
?? `generator exited ${result.status}`;
|
|
259
|
+
console.log(` FAIL: hook-import-set: ${detail.replace(/^✗\s*hook-import-set:\s*/, '')}`);
|
|
260
|
+
console.log(' Remedy: node scripts/generate-hook-import-set.mjs && stage hooks/_lib/hook-import-set.json');
|
|
261
|
+
totalFail += 1;
|
|
262
|
+
return 1;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* BLOCKING: the cross-harness portable surface (`AGENTS.md` +
|
|
267
|
+
* `.agents/skills/`) must match a fresh generation from `CLAUDE.md` +
|
|
268
|
+
* `skills/`. 7 of 8 surveyed harnesses read `AGENTS.md` and never `CLAUDE.md`,
|
|
269
|
+
* so a stale or absent copy means those harnesses silently see NO Session
|
|
270
|
+
* Config — a failure that is invisible from inside Claude Code, which is
|
|
271
|
+
* exactly why it needs a mechanical gate rather than a convention.
|
|
272
|
+
*
|
|
273
|
+
* `scripts/generate-agents-skills.mjs --check` owns the comparison (exit 1 on
|
|
274
|
+
* drift); this adapter only translates it. `runCheck()` cannot be reused — it
|
|
275
|
+
* spawns from `scripts/lib/validate/` and passes PLUGIN_ROOT positionally,
|
|
276
|
+
* while the generator takes `--plugin-root`. The in-sync marker is demanded
|
|
277
|
+
* for the same reason `runHookImportSetCheck()` demands one: a spawn whose
|
|
278
|
+
* path traverses a symlink can make the generator's CLI body a no-op that
|
|
279
|
+
* still exits 0.
|
|
280
|
+
*
|
|
281
|
+
* @returns {number} 0 when in sync, 1 on drift or a generator error.
|
|
282
|
+
*/
|
|
283
|
+
function runAgentsSurfaceCheck() {
|
|
284
|
+
console.log('--- Check: cross-harness portable surface (AGENTS.md + .agents/skills/) ---');
|
|
285
|
+
const script = path.join(SCRIPT_DIR, 'generate-agents-skills.mjs');
|
|
286
|
+
const result = spawnSync(
|
|
287
|
+
process.execPath,
|
|
288
|
+
[script, '--plugin-root', PLUGIN_ROOT, '--check'],
|
|
289
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
|
|
290
|
+
);
|
|
291
|
+
const combined = ((result.stdout ?? '') + (result.stderr ?? '')).trim();
|
|
292
|
+
|
|
293
|
+
const inSync = combined.match(/agents-surface: (\d+) artefact\(s\), in sync/);
|
|
294
|
+
if (result.status === 0 && inSync) {
|
|
295
|
+
console.log(` PASS: agents-surface: in sync (${inSync[1]} artefacts)`);
|
|
296
|
+
totalPass += 1;
|
|
297
|
+
return 0;
|
|
298
|
+
}
|
|
299
|
+
if (result.status === 0) {
|
|
300
|
+
console.log(' FAIL: agents-surface: generator exited 0 without reporting a comparison '
|
|
301
|
+
+ `(no in-sync marker in output: ${JSON.stringify(combined.slice(0, 120))})`);
|
|
302
|
+
totalFail += 1;
|
|
303
|
+
return 1;
|
|
304
|
+
}
|
|
305
|
+
for (const line of combined.split('\n').filter((l) => l.startsWith('✗'))) {
|
|
306
|
+
console.log(` FAIL: ${line.replace(/^✗\s*/, '')}`);
|
|
307
|
+
totalFail += 1;
|
|
308
|
+
}
|
|
309
|
+
console.log(' Remedy: node scripts/generate-agents-skills.mjs');
|
|
310
|
+
return 1;
|
|
311
|
+
}
|
|
312
|
+
|
|
178
313
|
// ---------------------------------------------------------------------------
|
|
179
314
|
// Run all checks — same order as validate-plugin.sh
|
|
180
315
|
// plugin.json checks are prerequisite; abort early if they fail.
|
|
@@ -220,6 +355,9 @@ if (runDriftCheck() !== 0) checkFailed = 1;
|
|
|
220
355
|
process.stdout.write('\n');
|
|
221
356
|
if (runCheck('check-hooks-symmetry.mjs') !== 0) checkFailed = 1;
|
|
222
357
|
|
|
358
|
+
process.stdout.write('\n');
|
|
359
|
+
if (runHookImportSetCheck() !== 0) checkFailed = 1;
|
|
360
|
+
|
|
223
361
|
process.stdout.write('\n');
|
|
224
362
|
if (runCheck('check-guard-requires-parity.mjs') !== 0) checkFailed = 1;
|
|
225
363
|
|
|
@@ -247,6 +385,18 @@ if (runCheck('check-pi-prompts.mjs') !== 0) checkFailed = 1;
|
|
|
247
385
|
process.stdout.write('\n');
|
|
248
386
|
if (runCheck('check-cursor-adapter.mjs') !== 0) checkFailed = 1;
|
|
249
387
|
|
|
388
|
+
// BLOCKING: the vendor-neutral portable surface — root AGENTS.md ↔ CLAUDE.md
|
|
389
|
+
// alias, root plugin.json (agent-plugins.org 1.0.0) manifest-version parity
|
|
390
|
+
// against package.json, and the .agents/skills/ mirror against the
|
|
391
|
+
// agentskills.io field list. The generator's own --check runs first (below);
|
|
392
|
+
// this checker is the INDEPENDENT oracle over the same artefacts, so a
|
|
393
|
+
// generator bug cannot vouch for its own output.
|
|
394
|
+
process.stdout.write('\n');
|
|
395
|
+
if (runCheck('check-agents-skills.mjs') !== 0) checkFailed = 1;
|
|
396
|
+
|
|
397
|
+
process.stdout.write('\n');
|
|
398
|
+
if (runAgentsSurfaceCheck() !== 0) checkFailed = 1;
|
|
399
|
+
|
|
250
400
|
process.stdout.write('\n');
|
|
251
401
|
if (runCheck('check-session-plan-routing.mjs') !== 0) checkFailed = 1;
|
|
252
402
|
|
|
@@ -318,6 +468,27 @@ runCheck('check-vcs-repo-flag.mjs');
|
|
|
318
468
|
process.stdout.write('\n');
|
|
319
469
|
runCheck('check-doc-cli-commands.mjs');
|
|
320
470
|
|
|
471
|
+
// BLOCKING (#1176): a `scripts/**.mjs` path cited in skills/, commands/ or
|
|
472
|
+
// agents/ prose that does not exist. Unlike the WARN-only check above, the
|
|
473
|
+
// oracle is this repository's own filesystem — no version skew of a foreign
|
|
474
|
+
// binary can red an unrelated commit, so a finding is always a real defect.
|
|
475
|
+
// A deliberately absent path is annotated in place:
|
|
476
|
+
// `<!-- path-check: planned #<iid> | historical | example -->`, honoured on the
|
|
477
|
+
// citation's own line or the line immediately above.
|
|
478
|
+
process.stdout.write('\n');
|
|
479
|
+
if (runCheck('check-skill-script-paths.mjs') !== 0) checkFailed = 1;
|
|
480
|
+
runStrictShAdvisory();
|
|
481
|
+
|
|
482
|
+
// BLOCKING (#1157 follow-up): a relative markdown link under skills/, commands/,
|
|
483
|
+
// agents/ or .claude/rules/ that does not resolve from the LINKING file's own
|
|
484
|
+
// directory. The check above judges cited SCRIPT paths; this one judges links
|
|
485
|
+
// between the instruction files themselves — the defect class a `references/`
|
|
486
|
+
// split creates, where the moved text is byte-identical and every `./sibling.md`
|
|
487
|
+
// in it now points one directory too deep. Three such links shipped broken on
|
|
488
|
+
// 2026-09-06 and no gate saw them; the worst silently detached the six
|
|
489
|
+
// session-end tail phases from their dispatcher.
|
|
490
|
+
if (runCheck('check-skill-links.mjs') !== 0) checkFailed = 1;
|
|
491
|
+
|
|
321
492
|
// WARN-only: a state-mutating `git` call in tests/ that names no target resolves
|
|
322
493
|
// its destination from the ambient cwd (or an inherited GIT_DIR) — the 2026-08-19
|
|
323
494
|
// incident, where fixture commits, a fixture remote and a fixture identity landed
|
|
@@ -337,6 +508,22 @@ runCheck('check-doc-cli-commands.mjs');
|
|
|
337
508
|
process.stdout.write('\n');
|
|
338
509
|
runCheck('check-test-git-config-target.mjs');
|
|
339
510
|
|
|
511
|
+
// BLOCKING (#1183): every `emitEvent()` call site under hooks/**/*.mjs must be
|
|
512
|
+
// try/catch-guarded (emitEvent() throws EventValidationError on a malformed
|
|
513
|
+
// record — an unguarded call aborts the hook process). Five pre-existing
|
|
514
|
+
// unguarded sites are baselined inside the checker itself and report WARN,
|
|
515
|
+
// never FAIL, so this can be blocking-by-default without going red on
|
|
516
|
+
// arrival; only a NEW unguarded site fails the build.
|
|
517
|
+
process.stdout.write('\n');
|
|
518
|
+
if (runCheck('check-hooks-emit-event-guard.mjs') !== 0) checkFailed = 1;
|
|
519
|
+
|
|
520
|
+
// BLOCKING (#1184): every scripts/lib/validate/check-*.mjs must be referenced
|
|
521
|
+
// by basename from this file, .husky/pre-commit, or .gitlab-ci.yml — or carry
|
|
522
|
+
// a `// registration: standalone <reason>` header marker declaring itself
|
|
523
|
+
// deliberately CLI-only. A checker nobody runs is built work with no effect.
|
|
524
|
+
process.stdout.write('\n');
|
|
525
|
+
if (runCheck('check-validator-registration.mjs') !== 0) checkFailed = 1;
|
|
526
|
+
|
|
340
527
|
// ---------------------------------------------------------------------------
|
|
341
528
|
// Summary
|
|
342
529
|
// ---------------------------------------------------------------------------
|