session-orchestrator 3.24.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/architecture/SKILL.md +18 -0
- package/.agents/skills/autopilot/SKILL.md +17 -0
- package/.agents/skills/bootstrap/SKILL.md +20 -0
- package/.agents/skills/brainstorm/SKILL.md +22 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
- package/.agents/skills/debug/SKILL.md +22 -0
- package/.agents/skills/discovery/SKILL.md +20 -0
- package/.agents/skills/dispatcher/SKILL.md +15 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
- package/.agents/skills/ecosystem-health/SKILL.md +20 -0
- package/.agents/skills/eli5/SKILL.md +20 -0
- package/.agents/skills/eval/SKILL.md +21 -0
- package/.agents/skills/evolve/SKILL.md +21 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
- package/.agents/skills/gitlab-ops/SKILL.md +20 -0
- package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
- package/.agents/skills/grill/SKILL.md +22 -0
- package/.agents/skills/hook-development/SKILL.md +15 -0
- package/.agents/skills/mcp-builder/SKILL.md +15 -0
- package/.agents/skills/memory-cleanup/SKILL.md +21 -0
- package/.agents/skills/mode-selector/SKILL.md +17 -0
- package/.agents/skills/npm-publish/SKILL.md +16 -0
- package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
- package/.agents/skills/persona-panel/SKILL.md +17 -0
- package/.agents/skills/plan/SKILL.md +20 -0
- package/.agents/skills/playwright-driver/SKILL.md +20 -0
- package/.agents/skills/quality-gates/SKILL.md +20 -0
- package/.agents/skills/reconcile/SKILL.md +21 -0
- package/.agents/skills/remote-offload/SKILL.md +20 -0
- package/.agents/skills/repo-audit/SKILL.md +16 -0
- package/.agents/skills/session-end/SKILL.md +20 -0
- package/.agents/skills/session-plan/SKILL.md +20 -0
- package/.agents/skills/session-start/SKILL.md +20 -0
- package/.agents/skills/spinout/SKILL.md +16 -0
- package/.agents/skills/sunset-review/SKILL.md +16 -0
- package/.agents/skills/test-runner/SKILL.md +20 -0
- package/.agents/skills/tmux-layout/SKILL.md +21 -0
- package/.agents/skills/using-orchestrator/SKILL.md +17 -0
- package/.agents/skills/vault-mirror/SKILL.md +15 -0
- package/.agents/skills/vault-sync/SKILL.md +15 -0
- package/.agents/skills/wave-executor/SKILL.md +20 -0
- package/.agents/skills/write-executable-plan/SKILL.md +22 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +3 -2
- package/.codex-plugin/skills/architecture/SKILL.md +20 -0
- package/.codex-plugin/skills/autopilot/SKILL.md +21 -0
- package/.codex-plugin/skills/autopilot/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/bootstrap/SKILL.md +22 -0
- package/.codex-plugin/skills/bootstrap/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/brainstorm/SKILL.md +22 -0
- package/.codex-plugin/skills/brainstorm/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/claude-md-drift-check/SKILL.md +17 -0
- package/.codex-plugin/skills/close/SKILL.md +21 -0
- package/.codex-plugin/skills/close/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +24 -0
- package/.codex-plugin/skills/debug/SKILL.md +21 -0
- package/.codex-plugin/skills/debug/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/discovery/SKILL.md +21 -0
- package/.codex-plugin/skills/discovery/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/dispatcher/SKILL.md +21 -0
- package/.codex-plugin/skills/dispatcher/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/docs-orchestrator/SKILL.md +20 -0
- package/.codex-plugin/skills/ecosystem-health/SKILL.md +22 -0
- package/.codex-plugin/skills/eli5/SKILL.md +21 -0
- package/.codex-plugin/skills/eli5/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/eval/SKILL.md +21 -0
- package/.codex-plugin/skills/eval/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/evolve/SKILL.md +21 -0
- package/.codex-plugin/skills/evolve/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/frontmatter-guard/SKILL.md +17 -0
- package/.codex-plugin/skills/gitlab-ops/SKILL.md +22 -0
- package/.codex-plugin/skills/gitlab-portfolio/SKILL.md +17 -0
- package/.codex-plugin/skills/go/SKILL.md +22 -0
- package/.codex-plugin/skills/go/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/grill/SKILL.md +21 -0
- package/.codex-plugin/skills/grill/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/harness-audit/SKILL.md +19 -0
- package/.codex-plugin/skills/harness-audit/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/hook-development/SKILL.md +17 -0
- package/.codex-plugin/skills/mcp-builder/SKILL.md +17 -0
- package/.codex-plugin/skills/memory-cleanup/SKILL.md +21 -0
- package/.codex-plugin/skills/memory-cleanup/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/mode-selector/SKILL.md +19 -0
- package/.codex-plugin/skills/npm-publish/SKILL.md +18 -0
- package/.codex-plugin/skills/peekaboo-driver/SKILL.md +20 -0
- package/.codex-plugin/skills/persona-panel/SKILL.md +22 -0
- package/.codex-plugin/skills/persona-panel/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/plan/SKILL.md +22 -0
- package/.codex-plugin/skills/plan/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/playwright-driver/SKILL.md +22 -0
- package/.codex-plugin/skills/portfolio/SKILL.md +21 -0
- package/.codex-plugin/skills/portfolio/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/quality-gates/SKILL.md +22 -0
- package/.codex-plugin/skills/reconcile/SKILL.md +21 -0
- package/.codex-plugin/skills/reconcile/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/release/SKILL.md +22 -0
- package/.codex-plugin/skills/release/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/remote-offload/SKILL.md +22 -0
- package/.codex-plugin/skills/repo-audit/SKILL.md +19 -0
- package/.codex-plugin/skills/repo-audit/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/session/SKILL.md +21 -0
- package/.codex-plugin/skills/session/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/session-end/SKILL.md +22 -0
- package/.codex-plugin/skills/session-plan/SKILL.md +22 -0
- package/.codex-plugin/skills/session-start/SKILL.md +22 -0
- package/.codex-plugin/skills/spinout/SKILL.md +21 -0
- package/.codex-plugin/skills/spinout/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/sunset-review/SKILL.md +21 -0
- package/.codex-plugin/skills/sunset-review/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/templates-ack/SKILL.md +21 -0
- package/.codex-plugin/skills/templates-ack/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/test/SKILL.md +21 -0
- package/.codex-plugin/skills/test/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/test-runner/SKILL.md +22 -0
- package/.codex-plugin/skills/tmux-layout/SKILL.md +23 -0
- package/.codex-plugin/skills/using-orchestrator/SKILL.md +19 -0
- package/.codex-plugin/skills/vault-mirror/SKILL.md +17 -0
- package/.codex-plugin/skills/vault-sync/SKILL.md +17 -0
- package/.codex-plugin/skills/wave-executor/SKILL.md +22 -0
- package/.codex-plugin/skills/write-executable-plan/SKILL.md +24 -0
- package/.cursor/commands/autopilot.md +2 -2
- package/.cursor/commands/bootstrap.md +1 -1
- package/.cursor/commands/brainstorm.md +1 -1
- package/.cursor/commands/debug.md +1 -1
- package/.cursor/commands/discovery.md +1 -1
- package/.cursor/commands/dispatcher.md +2 -2
- package/.cursor/commands/eli5.md +2 -2
- package/.cursor/commands/eval.md +2 -2
- package/.cursor/commands/evolve.md +1 -1
- package/.cursor/commands/go.md +1 -1
- package/.cursor/commands/grill.md +2 -2
- package/.cursor/commands/memory-cleanup.md +2 -2
- package/.cursor/commands/persona-panel.md +1 -1
- package/.cursor/commands/plan.md +1 -1
- package/.cursor/commands/portfolio.md +1 -1
- package/.cursor/commands/reconcile.md +2 -2
- package/.cursor/commands/release.md +2 -2
- package/.cursor/commands/session.md +2 -2
- package/.cursor/commands/spinout.md +2 -2
- package/.cursor/commands/sunset-review.md +2 -2
- package/.cursor/commands/templates-ack.md +2 -2
- package/.cursor/commands/test.md +2 -2
- package/.cursor/skills/brainstorm/SKILL.md +1 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/quality-gates/SKILL.md +1 -1
- package/.cursor/skills/remote-offload/SKILL.md +1 -1
- package/.cursor-plugin/plugin.json +30 -0
- package/.orchestrator/policy/blocked-commands.json +121 -0
- package/.orchestrator/policy/ecosystem.schema.json +66 -0
- package/.orchestrator/policy/quality-gates.example.json +16 -0
- package/.orchestrator/policy/quality-gates.schema.json +38 -0
- package/.orchestrator/policy/templates-policy.json +27 -0
- package/.orchestrator/policy/test-profiles.json +47 -0
- package/AGENTS.md +225 -0
- package/CHANGELOG.md +1314 -2
- package/NOTICE +11 -6
- package/README.md +135 -94
- package/agents/eval-judge.md +1 -1
- package/agents/skill-applied-judge.md +1 -1
- package/assets/wave-lifecycle.svg +98 -0
- package/commands/release.md +6 -3
- package/commands/session.md +18 -3
- package/docs/README.md +4 -0
- package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
- package/docs/baseline.md +67 -0
- package/docs/ci-setup.md +108 -62
- package/docs/codex-setup.md +107 -29
- package/docs/components.md +38 -16
- package/docs/cursor-setup.md +6 -2
- package/docs/events-schema.md +9 -6
- package/docs/instruction-delivery.md +69 -0
- package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
- package/docs/migration-v4.md +365 -0
- package/docs/pi-setup.md +6 -1
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/rule-authoring.md +85 -19
- package/docs/scope-collision-guard.md +5 -5
- package/docs/session-config-reference.md +57 -56
- package/docs/session-config-template.md +6 -29
- package/docs/telemetry.md +157 -3
- package/docs/vault-docs-architecture.md +50 -11
- package/hooks/_lib/hook-import-set.json +1488 -0
- package/hooks/_lib/subagent-transcript.mjs +562 -0
- package/hooks/config-protection.mjs +2 -2
- package/hooks/cwd-change-restore.mjs +2 -2
- package/hooks/enforce-commands.mjs +69 -0
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +10 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +3 -3
- package/hooks/on-session-end.mjs +2 -2
- package/hooks/on-session-start.mjs +103 -2
- package/hooks/on-stop.mjs +60 -14
- package/hooks/operator-steer.mjs +2 -2
- package/hooks/post-bash-write-verify.mjs +85 -0
- package/hooks/post-edit-import-probe.mjs +344 -0
- package/hooks/post-subagent-discovery-validator.mjs +187 -431
- package/hooks/post-tool-batch-wave-signal.mjs +118 -4
- package/hooks/post-tool-failure-corrective-context.mjs +2 -2
- package/hooks/post-tooluse-frontend-slop.mjs +3 -3
- package/hooks/pre-bash-destructive-guard.mjs +39 -13
- package/hooks/skill-invocation-telemetry.mjs +17 -5
- package/hooks/subagent-telemetry.mjs +13 -4
- package/monitors/monitors.json +3 -3
- package/package.json +9 -1
- package/pi/prompts/session.md +2 -2
- package/scripts/backfill-abandoned-sessions.mjs +50 -4
- package/scripts/backfill-learnings-from-vault.mjs +9 -3
- package/scripts/dialectic-deriver.mjs +73 -8
- package/scripts/export-hw-learnings.mjs +113 -1
- package/scripts/generate-agents-skills.mjs +378 -0
- package/scripts/generate-codex-skills.mjs +246 -0
- package/scripts/generate-cursor-adapter.mjs +45 -8
- package/scripts/generate-hook-import-set.mjs +292 -0
- package/scripts/lib/agent-status.mjs +13 -2
- package/scripts/lib/auto-dream.mjs +38 -36
- package/scripts/lib/autonomy/suitability.mjs +6 -0
- package/scripts/lib/autopilot/loop.mjs +2 -2
- package/scripts/lib/ci-status-banner.mjs +220 -75
- package/scripts/lib/codex/plugin-contract.mjs +88 -6
- package/scripts/lib/config/auto-dream.mjs +2 -1
- package/scripts/lib/config/block-header.mjs +8 -0
- package/scripts/lib/config/block-preprocess.mjs +177 -0
- package/scripts/lib/config/broken-window.mjs +2 -1
- package/scripts/lib/config/cold-start.mjs +2 -1
- package/scripts/lib/config/config-protection.mjs +22 -2
- package/scripts/lib/config/context-coverage.mjs +2 -1
- package/scripts/lib/config/cross-repo.mjs +2 -1
- package/scripts/lib/config/custom-phases.mjs +2 -1
- package/scripts/lib/config/dialectic.mjs +2 -1
- package/scripts/lib/config/discovery-validator.mjs +2 -1
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
- package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
- package/scripts/lib/config/docs-orchestrator.mjs +2 -1
- package/scripts/lib/config/docs-staleness.mjs +2 -1
- package/scripts/lib/config/drift-check.mjs +2 -1
- package/scripts/lib/config/eval.mjs +2 -1
- package/scripts/lib/config/events-rotation.mjs +2 -1
- package/scripts/lib/config/evolve.mjs +8 -2
- package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
- package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
- package/scripts/lib/config/handover-gate.mjs +2 -1
- package/scripts/lib/config/health-endpoints.mjs +7 -2
- package/scripts/lib/config/host-paths.mjs +20 -4
- package/scripts/lib/config/issue-budget.mjs +2 -1
- package/scripts/lib/config/loop-guard.mjs +2 -1
- package/scripts/lib/config/memory.mjs +2 -1
- package/scripts/lib/config/moc-staleness.mjs +2 -1
- package/scripts/lib/config/persona-gate-wave.mjs +2 -1
- package/scripts/lib/config/private-config-dir.mjs +67 -0
- package/scripts/lib/config/reconcile.mjs +2 -1
- package/scripts/lib/config/remote-hosts.mjs +2 -1
- package/scripts/lib/config/section-extractor.mjs +7 -1
- package/scripts/lib/config/skill-evolution.mjs +2 -1
- package/scripts/lib/config/slopcheck.mjs +2 -1
- package/scripts/lib/config/state-md-lock.mjs +2 -1
- package/scripts/lib/config/templates-first.mjs +2 -1
- package/scripts/lib/config/test.mjs +2 -1
- package/scripts/lib/config/vault-integration.mjs +7 -1
- package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
- package/scripts/lib/config/vault-staleness.mjs +2 -1
- package/scripts/lib/config/vault-sync.mjs +2 -1
- package/scripts/lib/config/verification-auto-fix.mjs +2 -1
- package/scripts/lib/config/wave-reviewers.mjs +2 -1
- package/scripts/lib/config/worktree-orphans.mjs +2 -1
- package/scripts/lib/convergence-monitor.mjs +82 -16
- package/scripts/lib/dispatcher/rank.mjs +124 -48
- package/scripts/lib/ecosystem-health.mjs +16 -2
- package/scripts/lib/eval/engine.mjs +9 -1
- package/scripts/lib/eval/session-resolve.mjs +23 -4
- package/scripts/lib/events.mjs +22 -6
- package/scripts/lib/frontmatter-guard.mjs +131 -13
- package/scripts/lib/gates/gate-full.mjs +30 -0
- package/scripts/lib/gates/gate-helpers.mjs +76 -0
- package/scripts/lib/hardware-pattern-detector.mjs +18 -1
- package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
- package/scripts/lib/host-identity.mjs +50 -11
- package/scripts/lib/instruction-budget-guard.mjs +171 -5
- package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
- package/scripts/lib/learnings/io.mjs +60 -6
- package/scripts/lib/memory-proposals/store.mjs +30 -22
- package/scripts/lib/owner-config-banner.mjs +41 -6
- package/scripts/lib/owner-config-loader.mjs +21 -10
- package/scripts/lib/owner-interview.mjs +3 -3
- package/scripts/lib/owner-yaml.mjs +215 -15
- package/scripts/lib/platform.mjs +108 -15
- package/scripts/lib/plugin-update-banner.mjs +414 -0
- package/scripts/lib/project-hygiene.mjs +38 -2
- package/scripts/lib/qg-command-drift-banner.mjs +50 -12
- package/scripts/lib/quality-gate.mjs +133 -44
- package/scripts/lib/reconcile/emitter.mjs +68 -6
- package/scripts/lib/reconcile/engine.mjs +51 -11
- package/scripts/lib/reconcile/idempotency.mjs +37 -4
- package/scripts/lib/reconcile/writer.mjs +40 -18
- package/scripts/lib/session-close-backfill.mjs +67 -9
- package/scripts/lib/session-id.mjs +12 -23
- package/scripts/lib/session-identity/own-session.mjs +125 -10
- package/scripts/lib/session-lock-shape.mjs +43 -0
- package/scripts/lib/session-lock.mjs +5 -10
- package/scripts/lib/session-registry.mjs +25 -9
- package/scripts/lib/session-schema/constants.mjs +64 -3
- package/scripts/lib/session-schema/validator.mjs +38 -4
- package/scripts/lib/session-start-probes.mjs +30 -1
- package/scripts/lib/sessions-staleness-banner.mjs +18 -11
- package/scripts/lib/skill-health/join.mjs +17 -4
- package/scripts/lib/state-md.mjs +78 -0
- package/scripts/lib/sunset/walker.mjs +6 -0
- package/scripts/lib/telemetry/schema.mjs +202 -9
- package/scripts/lib/telemetry/sync.mjs +368 -12
- package/scripts/lib/telemetry-flush-health-banner.mjs +211 -0
- package/scripts/lib/validate/check-agents-skills.mjs +327 -0
- package/scripts/lib/validate/check-agents.mjs +3 -3
- package/scripts/lib/validate/check-codex-skills.mjs +191 -0
- package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
- package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
- package/scripts/lib/validate/check-owner-leakage.mjs +319 -22
- package/scripts/lib/validate/check-skill-links.mjs +193 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +47 -28
- package/scripts/lib/validate/check-test-git-config-target.mjs +192 -12
- package/scripts/lib/validate/check-unwired-features.mjs +163 -15
- package/scripts/lib/validate/check-validator-registration.mjs +10 -4
- package/scripts/lib/validate/confidential-names.mjs +95 -30
- package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
- package/scripts/lib/validate/repo-files.mjs +48 -14
- package/scripts/lib/vault-backfill/template.mjs +63 -6
- package/scripts/lib/vault-mirror/process.mjs +165 -42
- package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
- package/scripts/lib/vault-status/narrative-mirror.mjs +127 -18
- package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
- package/scripts/lib/wave-executor/remote-dispatch.mjs +5 -7
- package/scripts/lib/wave-resource-gate.mjs +8 -2
- package/scripts/lib/wave-sizing.mjs +4 -1
- package/scripts/lib/wave-transcript-tail.mjs +118 -4
- package/scripts/materialize-wave-scope.mjs +12 -5
- package/scripts/memory-propose.mjs +19 -5
- package/scripts/migrate-cold-start-seed.mjs +4 -1
- package/scripts/parse-config.mjs +60 -3
- package/scripts/release.mjs +430 -31
- package/scripts/repair-invalid-sessions.mjs +3 -3
- package/scripts/run-quality-gate.mjs +128 -11
- package/scripts/site-numbers.mjs +344 -8
- package/scripts/sweep-expired-learnings.mjs +90 -0
- package/scripts/sync-vault-schema.mjs +3 -1
- package/scripts/telemetry.mjs +2 -2
- package/scripts/validate-plugin.mjs +164 -0
- package/scripts/validate-wave-scope.mjs +28 -8
- package/scripts/wave-scope-binding.mjs +215 -0
- package/skills/_shared/instruction-file-resolution.md +10 -0
- package/skills/_shared/parallel-aware-preamble.md +1 -0
- package/skills/_shared/platform-tools.md +1 -1
- package/skills/_shared/state-ownership.md +1 -1
- package/skills/architecture/SKILL.md +7 -5
- package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
- package/skills/autopilot/SKILL.md +4 -18
- package/skills/claude-md-drift-check/SKILL.md +5 -1
- package/skills/claude-md-drift-check/checker.mjs +62 -2
- package/skills/convergence-monitoring/SIGNALS.md +55 -0
- package/skills/discovery/probes/vault-staleness.mjs +37 -13
- package/skills/discovery/probes-arch.md +20 -18
- package/skills/dispatcher/SKILL.md +3 -2
- package/skills/evolve/SKILL.md +65 -26
- package/skills/frontmatter-guard/SKILL.md +11 -5
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +33 -0
- package/skills/remote-offload/SKILL.md +1 -1
- package/skills/session-end/SKILL.md +18 -905
- package/skills/session-end/phase-3-6-tail.md +10 -3
- package/skills/session-end/plan-verification.md +221 -155
- package/skills/session-end/references/phase-2-quality-gate.md +93 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
- package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
- package/skills/session-end/references/session-summary-template.md +62 -0
- package/skills/session-plan/SKILL.md +49 -0
- package/skills/session-start/SKILL.md +22 -904
- package/skills/session-start/phase-8-5-express-path.md +1 -1
- package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
- package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +160 -0
- package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
- package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
- package/skills/vault-sync/SKILL.md +10 -0
- package/skills/vault-sync/validator.mjs +21 -27
- package/skills/wave-executor/SKILL.md +15 -1
- package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
- package/skills/wave-executor/references/wave-loop-review.md +570 -0
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
- package/skills/wave-executor/wave-loop.md +14 -1309
- package/templates/_shared/journey-manifest.md +10 -6
- package/.cursor/commands/autopilot-multi.md +0 -14
- package/.cursor/commands/contract-version-bump.md +0 -14
- package/.cursor/commands/journey-audit.md +0 -14
- package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
- package/.cursor/skills/daily/SKILL.md +0 -12
- package/.cursor/skills/domain-model/SKILL.md +0 -13
- package/.cursor/skills/journey-audit/SKILL.md +0 -13
- package/.cursor/skills/skill-creator/SKILL.md +0 -13
- package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
- package/commands/autopilot-multi.md +0 -74
- package/commands/contract-version-bump.md +0 -28
- package/commands/journey-audit.md +0 -43
- package/pi/prompts/autopilot-multi.md +0 -12
- package/pi/prompts/contract-version-bump.md +0 -12
- package/pi/prompts/journey-audit.md +0 -12
- package/scripts/autopilot-multi.mjs +0 -885
- package/scripts/backfill-learnings-expires.mjs +0 -196
- package/scripts/backfill-learnings.mjs +0 -203
- package/scripts/fleet-instruction-scan.mjs +0 -141
- package/scripts/lib/autopilot/dep-graph.mjs +0 -417
- package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
- package/scripts/lib/webhook-url.mjs +0 -105
- package/scripts/lifecycle-sim-v6.mjs +0 -347
- package/scripts/migrate-learnings-jsonl.mjs +0 -189
- package/scripts/migrate-subagents-jsonl.mjs +0 -196
- package/scripts/upload-social-preview.mjs +0 -316
- package/skills/_shared/model-selection.md +0 -64
- package/skills/contract-version-bump/SKILL.md +0 -219
- package/skills/daily/SKILL.md +0 -222
- package/skills/daily/generate.sh +0 -92
- package/skills/daily/templates/daily.md.tpl +0 -36
- package/skills/journey-audit/SKILL.md +0 -270
- package/skills/skill-creator/SKILL.md +0 -168
- package/skills/ubiquitous-language/SKILL.md +0 -97
- package/skills/vault-sync/package-lock.json +0 -40
- /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
- /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
package/scripts/release.mjs
CHANGED
|
@@ -81,13 +81,14 @@ import {
|
|
|
81
81
|
rmSync,
|
|
82
82
|
realpathSync,
|
|
83
83
|
} from 'node:fs';
|
|
84
|
-
import { join } from 'node:path';
|
|
84
|
+
import { join, relative as relativePath, sep } from 'node:path';
|
|
85
85
|
import { tmpdir } from 'node:os';
|
|
86
86
|
import { spawnSync } from 'node:child_process';
|
|
87
87
|
import { parseArgs } from 'node:util';
|
|
88
88
|
import { fileURLToPath } from 'node:url';
|
|
89
89
|
|
|
90
90
|
import { resolveRepoSpec } from './lib/vcs-repo-spec.mjs';
|
|
91
|
+
import { enumerateRepoFiles } from './lib/validate/enumerate-repo-files.mjs';
|
|
91
92
|
|
|
92
93
|
const PACKAGE_NAME = 'session-orchestrator';
|
|
93
94
|
const SPAWN_OPTS = { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 };
|
|
@@ -119,6 +120,13 @@ export const SURFACES = [
|
|
|
119
120
|
file: '.claude-plugin/plugin.json',
|
|
120
121
|
patterns: [/"version":\s*"(\d+\.\d+\.\d+)"/],
|
|
121
122
|
},
|
|
123
|
+
{
|
|
124
|
+
// Cursor's native manifest replaces the root Agent Plugins manifest so
|
|
125
|
+
// Codex can load its own component paths and cache version independently.
|
|
126
|
+
// It remains a required version surface for every release.
|
|
127
|
+
file: '.cursor-plugin/plugin.json',
|
|
128
|
+
patterns: [/"version":\s*"(\d+\.\d+\.\d+)"/],
|
|
129
|
+
},
|
|
122
130
|
{
|
|
123
131
|
file: '.claude-plugin/marketplace.json',
|
|
124
132
|
patterns: [/"version":\s*"(\d+\.\d+\.\d+)"/g],
|
|
@@ -181,6 +189,14 @@ export const SURFACES = [
|
|
|
181
189
|
patterns: [/data-metric="version"[^>]*>(\d+\.\d+\.\d+)</g],
|
|
182
190
|
checkOnly: true,
|
|
183
191
|
},
|
|
192
|
+
{
|
|
193
|
+
// The German landing page (2026-09-07 redesign) mirrors the EN metric cells
|
|
194
|
+
// one-to-one; same single writer (scripts/site-numbers.mjs --write), so it is
|
|
195
|
+
// checkOnly for the same reason as site/index.html above.
|
|
196
|
+
file: 'site/de/index.html',
|
|
197
|
+
patterns: [/data-metric="version"[^>]*>(\d+\.\d+\.\d+)</g],
|
|
198
|
+
checkOnly: true,
|
|
199
|
+
},
|
|
184
200
|
{
|
|
185
201
|
file: 'site/llms.txt',
|
|
186
202
|
patterns: [/Version:\s*(\d+\.\d+\.\d+)/],
|
|
@@ -307,7 +323,18 @@ function hasPathSegment(path, segment) {
|
|
|
307
323
|
|
|
308
324
|
export const LEAKAGE_PATTERNS = [
|
|
309
325
|
{ name: 'tests/', matches: (path) => hasPathSegment(path, 'tests') },
|
|
310
|
-
|
|
326
|
+
// `.orchestrator/policy/` is CARVED OUT and everything else under
|
|
327
|
+
// `.orchestrator/` still leaks: the destructive-guard FLOOR policy ships to npm
|
|
328
|
+
// consumers since 4.0.0 (Codex P1 — measured: `npm pack` carried 0 policy
|
|
329
|
+
// entries, so `loadEffectivePolicy` returned `rules:null` and the guard allowed
|
|
330
|
+
// everything on a consumer install). The carve-out is a `policy/` PATH segment,
|
|
331
|
+
// not a prefix: `.orchestrator/metrics/`, `.orchestrator/tmp/`,
|
|
332
|
+
// `.orchestrator/runtime/`, `*.lock` and the `.orchestrator/policy-backup/`
|
|
333
|
+
// prefix trick are all still caught.
|
|
334
|
+
{
|
|
335
|
+
name: '.orchestrator/',
|
|
336
|
+
matches: (path) => /\.orchestrator\//.test(path) && !/^\.orchestrator\/policy\//.test(path),
|
|
337
|
+
},
|
|
311
338
|
{ name: '.claude/', matches: (path) => hasPathSegment(path, '.claude') },
|
|
312
339
|
{ name: '.github/', matches: (path) => /\.github\//.test(path) },
|
|
313
340
|
{ name: 'node_modules', matches: (path) => /node_modules/.test(path) },
|
|
@@ -341,7 +368,18 @@ const INTENTIONAL_TEST_ASSET_PATHS = new Set([
|
|
|
341
368
|
// tests/scripts/site-numbers.test.mjs, which forbids ANY vX.Y.Z and the current
|
|
342
369
|
// package version outside a `data-metric` cell on EVERY shipped page, and
|
|
343
370
|
// exempts exactly the lines marked `site-numbers:historical`.
|
|
344
|
-
|
|
371
|
+
// 4.0.1 (measured 2026-09-07): the 4.0.0 sweep would have flagged five files whose only literal
|
|
372
|
+
// is PROSE HISTORY of the major ("moved out of agents/ in 4.0.0", "removed in 4.0.0",
|
|
373
|
+
// "releases 4.0.0 would have blocked on itself") — CLAUDE.md, its generated twin AGENTS.md,
|
|
374
|
+
// CONTRIBUTING.md, NOTICE and .husky/pre-push. None of them is a version SURFACE (no
|
|
375
|
+
// `"version":`, badge or `vX.Y.Z` form), so they join the history allowlist rather than being
|
|
376
|
+
// reworded to dodge the sweep.
|
|
377
|
+
// Four more prose-history files surfaced once the detail line stopped truncating at five hits
|
|
378
|
+
// (same 2026-09-07 sweep): site/llms-full.txt ("The v4.0.0 release REMOVES public surfaces" — its
|
|
379
|
+
// version SURFACE is checked separately by the SURFACES row, so the sweep on it is redundant),
|
|
380
|
+
// skills/architecture/references/domain-model.md ("Merged here in v4.0.0"), skills/autopilot/SKILL.md
|
|
381
|
+
// ("4.0.0 — see docs/migration-v4.md") and templates/_shared/journey-manifest.md ("Retired … in 4.0.0").
|
|
382
|
+
export const HISTORY_ALLOWLIST = /^(CHANGELOG\.md|README\.md|CLAUDE\.md|AGENTS\.md|CONTRIBUTING\.md|NOTICE|\.husky\/pre-push|docs\/|tests\/|skills\/npm-publish\/|skills\/architecture\/references\/domain-model\.md|skills\/autopilot\/SKILL\.md|templates\/_shared\/journey-manifest\.md|scripts\/release\.mjs|\.orchestrator\/|site\/leaderboard\.json|site\/guide\/index\.html|site\/llms-full\.txt|commands\/release\.md)/;
|
|
345
383
|
|
|
346
384
|
/** Pure check over packed-entry lines. Returns violations: {name, line}[]. */
|
|
347
385
|
export function checkLeakage(lines) {
|
|
@@ -422,15 +460,129 @@ export const MIN_PACKED_ENTRIES = 400;
|
|
|
422
460
|
// ---------------------------------------------------------------------------
|
|
423
461
|
|
|
424
462
|
/**
|
|
425
|
-
*
|
|
463
|
+
* A regex matching `literal` as a VERSION TOKEN, not as a substring.
|
|
464
|
+
*
|
|
465
|
+
* THE BUG (measured 2026-09-07, mid-release): the sweep matched the previous tag `4.0.0`
|
|
466
|
+
* inside `>=24.0.0` — `package.json`'s own engines field and a `scripts/lib/` string that
|
|
467
|
+
* quotes it — so a release could not be cut without either rewording an engines constraint or
|
|
468
|
+
* widening the allowlist over two files that carry no version surface at all. The boundary is
|
|
469
|
+
* therefore part of WHAT IS SWEPT FOR, not an allowlist row: `24.0.0`, `14.0.0` and `4.0.0.1`
|
|
470
|
+
* are different literals, at every path, forever.
|
|
471
|
+
*
|
|
472
|
+
* @param {string} literal — the previous release version
|
|
473
|
+
* @returns {RegExp} global regex; `4.0.0` matches only when not preceded by `[0-9.]` and not
|
|
474
|
+
* continued by a further numeric component (`(?!\.?[0-9])`).
|
|
475
|
+
*/
|
|
476
|
+
function versionTokenRegex(literal) {
|
|
477
|
+
return new RegExp(`(?<![0-9.])${literal.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?!\\.?[0-9])`, 'g');
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Is EVERY token occurrence of `literal` on this line a dependency range (`^X.Y.Z` / `~X.Y.Z`)?
|
|
482
|
+
*
|
|
483
|
+
* One bare occurrence anywhere on the line is enough to call the whole line drift — a comment
|
|
484
|
+
* that also happens to mention a ranged dep must not be excused by that mention. Occurrences are
|
|
485
|
+
* counted with {@link versionTokenRegex}, so `>=24.0.0` is not an occurrence of `4.0.0` here
|
|
486
|
+
* either — otherwise a line pinning `^4.0.0` beside an engines constraint would read as drift.
|
|
487
|
+
*
|
|
488
|
+
* @param {string} content — the matching line's text
|
|
489
|
+
* @param {string} literal — the previous release version, matched as a token
|
|
490
|
+
* @returns {boolean}
|
|
491
|
+
*/
|
|
492
|
+
export function isDependencyRangeOnly(content, literal) {
|
|
493
|
+
const re = versionTokenRegex(literal);
|
|
494
|
+
let seen = 0;
|
|
495
|
+
for (let m = re.exec(content); m; m = re.exec(content)) {
|
|
496
|
+
seen += 1;
|
|
497
|
+
const before = m.index > 0 ? content[m.index - 1] : '';
|
|
498
|
+
if (before !== '^' && before !== '~') return false;
|
|
499
|
+
}
|
|
500
|
+
return seen > 0;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/** Lockfiles whose dependency entries are third-party history, never our surface. */
|
|
504
|
+
const LOCKFILE_BASENAMES = new Set(['package-lock.json', 'npm-shrinkwrap.json']);
|
|
505
|
+
|
|
506
|
+
/** Code files in which a `//`, `*`, `/*` or `#` line is comment prose, never a version surface. */
|
|
507
|
+
const CODE_COMMENT_EXTENSIONS = new Set(['.mjs', '.js', '.cjs', '.ts', '.sh']);
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Is this `path:line:content` row version HISTORY rather than a stale surface?
|
|
511
|
+
*
|
|
512
|
+
* Three classes, all measured on the 4.0.1 cut (2026-09-07) as FALSE POSITIVES of the raw
|
|
513
|
+
* substring sweep, and all expressed as PREDICATES for the same reason the range carve-out
|
|
514
|
+
* above is one: a per-path allowlist row fixes the instance and leaves the class open.
|
|
515
|
+
*
|
|
516
|
+
* 1. **Dependency range** — `^X.Y.Z` / `~X.Y.Z` (see {@link isDependencyRangeOnly}).
|
|
517
|
+
* 2. **Lockfile dependency entry** — `package-lock.json` carried 72 hits for `4.0.0`, every one
|
|
518
|
+
* a third-party package version or an engines range. Our OWN entry there is still swept: the
|
|
519
|
+
* root package's `"version"` line, which npm writes in the `packages[""]` record at the top
|
|
520
|
+
* of the file. NAMED CEILING (BV-004): "at the top" is read as `line <= 20`, which covers
|
|
521
|
+
* every lockfileVersion-3 file npm writes today (the root record starts at line 5). Revisit
|
|
522
|
+
* trigger: a lockfile whose root `"version"` sits below line 20 — then key on the enclosing
|
|
523
|
+
* JSON path instead of the line number.
|
|
524
|
+
* 3. **Comment prose in a code file** — `// (pre-4.0.0 checkouts, …)` and a `* since 4.0.0`
|
|
525
|
+
* docblock line. No SURFACES pattern is ever a comment (every one is `"version": "X.Y.Z"`,
|
|
526
|
+
* `vX.Y.Z` or a badge), so excusing comment lines cannot mask a stale surface.
|
|
527
|
+
*
|
|
528
|
+
* @param {string} file — repo-relative path
|
|
529
|
+
* @param {number} line — 1-based line number
|
|
530
|
+
* @param {string} content — the matching line's text
|
|
531
|
+
* @param {string} prevTag — the previous release literal being swept for
|
|
532
|
+
* @returns {boolean} true = history, skip the row
|
|
533
|
+
*/
|
|
534
|
+
function isHistoryRow(file, line, content, prevTag) {
|
|
535
|
+
if (!versionTokenRegex(prevTag).test(content)) return true;
|
|
536
|
+
if (isDependencyRangeOnly(content, prevTag)) return true;
|
|
537
|
+
const base = file.split('/').pop();
|
|
538
|
+
if (LOCKFILE_BASENAMES.has(base)) {
|
|
539
|
+
const isRootVersionLine = line <= 20 && new RegExp(`"version":\\s*"${prevTag.replace(/\./g, '\\.')}"`).test(content);
|
|
540
|
+
return !isRootVersionLine;
|
|
541
|
+
}
|
|
542
|
+
const dot = base.lastIndexOf('.');
|
|
543
|
+
const ext = dot === -1 ? '' : base.slice(dot);
|
|
544
|
+
if (CODE_COMMENT_EXTENSIONS.has(ext)) {
|
|
545
|
+
const trimmed = content.trim();
|
|
546
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) return true;
|
|
547
|
+
if (ext === '.sh' && trimmed.startsWith('#')) return true;
|
|
548
|
+
}
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Drift sweep verdict over a grep-shaped result (`-l` file list or `-n` line hits).
|
|
426
554
|
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
555
|
+
* Exit-code contract, kept identical to `git grep`'s because that is what this
|
|
556
|
+
* evaluator was written against and what {@link collectDriftHits} now emits:
|
|
557
|
+
* 0 = matches found, 1 = no match (the success case here), anything else = it
|
|
558
|
+
* did not run. Measured on git 2.x: a bad regex and a bad pathspec both exit
|
|
559
|
+
* 128; git also documents 2 for usage errors. The old inline code read
|
|
560
|
+
* `.stdout` without ever looking at `.status`, so BOTH the no-match case and
|
|
561
|
+
* the it-crashed case produced an empty hit list and the same reassuring
|
|
562
|
+
* detail line, "no file still carries X". A sweep that never ran is not a
|
|
563
|
+
* clean sweep.
|
|
564
|
+
*
|
|
565
|
+
* ONE CLASS OF MATCH IS NOT OURS TO BUMP: a dependency RANGE that happens to equal our own
|
|
566
|
+
* previous version. `skills/vault-sync` pins `zod` at `^3.24.0` (the projects-baseline pin), so
|
|
567
|
+
* the 4.0.0 sweep collected two files whose literal belongs to zod and must NOT move when we
|
|
568
|
+
* release. The carve-out is deliberately a PREDICATE and not two allowlist rows: a per-path row
|
|
569
|
+
* fixes this instance and leaves the class open for the next dependency that lands on our version
|
|
570
|
+
* number — the unenumerable-table failure `.claude/rules/measurement-discipline.md` records. A
|
|
571
|
+
* caret- or tilde-prefixed literal is a range, and no version surface of this package is ever
|
|
572
|
+
* written as one (see SURFACES above: every pattern is an exact `"version": "X.Y.Z"`, `vX.Y.Z`
|
|
573
|
+
* or badge form), so the predicate cannot mask a stale surface.
|
|
574
|
+
*
|
|
575
|
+
* THREE FURTHER CLASSES are history for the same reason, all measured 2026-09-07 mid-release
|
|
576
|
+
* and all decided by {@link isHistoryRow}, never by an allowlist row: a literal that is only a
|
|
577
|
+
* SUBSTRING of a longer version (`4.0.0` inside `>=24.0.0`), a `package-lock.json` /
|
|
578
|
+
* `npm-shrinkwrap.json` row that is not the root package's own `"version"` line (72 of the 72
|
|
579
|
+
* lockfile hits on that cut were third-party), and COMMENT PROSE in a code file
|
|
580
|
+
* (`// (pre-4.0.0 checkouts, …)`). See that function for each one's ceiling.
|
|
581
|
+
*
|
|
582
|
+
* Accepts BOTH `git grep` output shapes. A bare `path` (from `-l`) carries no content and is
|
|
583
|
+
* therefore always a hit — the fail-closed reading, unchanged, and the shape {@link collectDriftHits}
|
|
584
|
+
* emits for a file it could not READ. `path:line:content` (from `-n`)
|
|
585
|
+
* is judged per line, and the file counts as drift as soon as ONE matching line is not a range.
|
|
434
586
|
*
|
|
435
587
|
* @param {{status: number, stdout?: string, stderr?: string}} grep
|
|
436
588
|
* @param {string} prevTag — the previous release literal being swept for
|
|
@@ -441,21 +593,102 @@ export function evaluateDriftSweep(grep, prevTag, allowlist) {
|
|
|
441
593
|
if (grep.status !== 0 && grep.status !== 1) {
|
|
442
594
|
return {
|
|
443
595
|
ok: false,
|
|
444
|
-
detail: `
|
|
596
|
+
detail: `drift sweep did not run (exit ${grep.status}): ${(grep.stderr || '').trim().slice(0, 200)} — sweep for ${prevTag} is inconclusive`,
|
|
445
597
|
};
|
|
446
598
|
}
|
|
447
|
-
const hits =
|
|
448
|
-
|
|
449
|
-
.
|
|
450
|
-
|
|
599
|
+
const hits = [];
|
|
600
|
+
for (const row of (grep.stdout || '').split('\n').filter(Boolean)) {
|
|
601
|
+
const withContent = row.match(/^(.+?):(\d+):(.*)$/);
|
|
602
|
+
const file = withContent ? withContent[1] : row;
|
|
603
|
+
if (allowlist.test(file)) continue;
|
|
604
|
+
if (withContent && isHistoryRow(file, Number(withContent[2]), withContent[3], prevTag)) continue;
|
|
605
|
+
if (!hits.includes(file)) hits.push(file);
|
|
606
|
+
}
|
|
451
607
|
return {
|
|
452
608
|
ok: hits.length === 0,
|
|
453
609
|
detail: hits.length
|
|
454
|
-
? `still carry ${prevTag}: ${hits.slice(0, 5).join(', ')}`
|
|
455
|
-
: `no
|
|
610
|
+
? `still carry ${prevTag} (${hits.length} file(s)): ${hits.slice(0, 5).join(', ')}${hits.length > 5 ? ', …' : ''}`
|
|
611
|
+
: `no file outside the allowlist still carries ${prevTag} (tracked + untracked-not-ignored)`,
|
|
456
612
|
};
|
|
457
613
|
}
|
|
458
614
|
|
|
615
|
+
/**
|
|
616
|
+
* Bytes of a file inspected when deciding whether it is binary.
|
|
617
|
+
*
|
|
618
|
+
* A NUL byte in the first 8 KB is the same heuristic `git grep`/`grep` use to
|
|
619
|
+
* declare a file binary. NAMED CEILING (BV-004): a text file whose only NUL
|
|
620
|
+
* sits past 8 KB is scanned as text (harmless — it produces no version match),
|
|
621
|
+
* and a binary whose first 8 KB happen to be NUL-free is scanned as text and
|
|
622
|
+
* may emit mojibake rows. Revisit trigger: the first drift-sweep row naming a
|
|
623
|
+
* file nobody recognises as text.
|
|
624
|
+
*/
|
|
625
|
+
const BINARY_SNIFF_BYTES = 8192;
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Produce the drift-sweep hit list over the files that EXIST in the working
|
|
629
|
+
* tree — tracked or not — in the exact `{status, stdout, stderr}` shape
|
|
630
|
+
* {@link evaluateDriftSweep} already consumes.
|
|
631
|
+
*
|
|
632
|
+
* ## Why not `git grep` any more (#1248)
|
|
633
|
+
*
|
|
634
|
+
* `git grep` searches the INDEX: it is blind to an untracked file, so the
|
|
635
|
+
* moment a stale version literal is most likely to exist (a doc or manifest
|
|
636
|
+
* written for this release and not yet staged) is exactly the moment the sweep
|
|
637
|
+
* cannot see it and reports clean. The population is now
|
|
638
|
+
* `scripts/lib/validate/enumerate-repo-files.mjs`
|
|
639
|
+
* (`git ls-files --cached --others --exclude-standard`) — tracked PLUS
|
|
640
|
+
* untracked, minus everything `.gitignore` excludes, so `node_modules/` and
|
|
641
|
+
* the peer worktrees under `.claude/worktrees/` stay out without a prune list
|
|
642
|
+
* having to guess at them.
|
|
643
|
+
*
|
|
644
|
+
* What `git grep` bought — searching hidden directories a plain `rg` skips,
|
|
645
|
+
* which is how the forgotten `.codex-plugin` manifest was found — is kept:
|
|
646
|
+
* `git ls-files` lists dotted paths like any other.
|
|
647
|
+
*
|
|
648
|
+
* ## Fail-closed, in two places
|
|
649
|
+
*
|
|
650
|
+
* Enumeration itself throwing is reported as exit 128, which
|
|
651
|
+
* {@link evaluateDriftSweep} reads as "inconclusive" = FAIL — the same reading
|
|
652
|
+
* a crashed `git grep` got. A single file that enumerates but cannot be READ
|
|
653
|
+
* is emitted as a CONTENT-LESS row, which that evaluator already treats as a
|
|
654
|
+
* hit: a file we could not sweep is never silently a clean file.
|
|
655
|
+
*
|
|
656
|
+
* @param {object} options
|
|
657
|
+
* @param {string} options.repoRoot absolute repository root
|
|
658
|
+
* @param {string} options.prevTag the previous release literal to sweep for
|
|
659
|
+
* @param {(o: {repoRoot: string}) => string[]} [options.enumerate] injection seam for tests
|
|
660
|
+
* @param {(absolute: string) => Buffer} [options.read] injection seam for tests
|
|
661
|
+
* @returns {{status: number, stdout: string, stderr: string}}
|
|
662
|
+
*/
|
|
663
|
+
export function collectDriftHits({ repoRoot, prevTag, enumerate = enumerateRepoFiles, read = readFileSync }) {
|
|
664
|
+
let files;
|
|
665
|
+
try {
|
|
666
|
+
files = enumerate({ repoRoot });
|
|
667
|
+
} catch (err) {
|
|
668
|
+
return { status: 128, stdout: '', stderr: `enumerateRepoFiles failed: ${err && err.message}` };
|
|
669
|
+
}
|
|
670
|
+
const rows = [];
|
|
671
|
+
for (const absolute of files) {
|
|
672
|
+
const relative = relativePath(repoRoot, absolute).split(sep).join('/');
|
|
673
|
+
let buf;
|
|
674
|
+
try {
|
|
675
|
+
buf = read(absolute);
|
|
676
|
+
} catch {
|
|
677
|
+
// Content-less row = hit (see the evaluator's contract above).
|
|
678
|
+
rows.push(relative);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
if (buf.subarray(0, BINARY_SNIFF_BYTES).includes(0)) continue;
|
|
682
|
+
const text = buf.toString('utf8');
|
|
683
|
+
if (!text.includes(prevTag)) continue;
|
|
684
|
+
const lines = text.split('\n');
|
|
685
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
686
|
+
if (lines[i].includes(prevTag)) rows.push(`${relative}:${i + 1}:${lines[i]}`);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return { status: rows.length ? 0 : 1, stdout: rows.length ? `${rows.join('\n')}\n` : '', stderr: '' };
|
|
690
|
+
}
|
|
691
|
+
|
|
459
692
|
/**
|
|
460
693
|
* Registry-collision verdict over `npm view <pkg> versions --json`.
|
|
461
694
|
*
|
|
@@ -589,6 +822,53 @@ export function evaluateNpmAuth(whoami) {
|
|
|
589
822
|
: { ok: false, detail: 'npm whoami exited 0 with an empty identity — the token could not be confirmed' };
|
|
590
823
|
}
|
|
591
824
|
|
|
825
|
+
/**
|
|
826
|
+
* Turn a `checkCiStatus` result into the `ci-green-on-head` preflight row.
|
|
827
|
+
*
|
|
828
|
+
* Three input states since #1031, and the middle one is why this is a named
|
|
829
|
+
* function rather than a ternary: `degraded` means the check could NOT be READ.
|
|
830
|
+
* Interpolating `ci.status` there printed `status: undefined` — a red row whose
|
|
831
|
+
* detail names no cause, which reads as "CI is broken" when the truth is "we
|
|
832
|
+
* never found out". Only an actual `status: 'green'` reading passes; a release
|
|
833
|
+
* must never proceed on an unknown CI state.
|
|
834
|
+
*
|
|
835
|
+
* @param {null | {status?: string, failingJobName?: string, degraded?: string}} ci
|
|
836
|
+
* @returns {{ok: boolean, detail: string}}
|
|
837
|
+
*/
|
|
838
|
+
export function evaluateCiRow(ci) {
|
|
839
|
+
if (ci === null || ci === undefined) return { ok: false, detail: 'CI status unavailable' };
|
|
840
|
+
if (ci.degraded) return { ok: false, detail: `CI status unknown (${ci.degraded})` };
|
|
841
|
+
const job = ci.failingJobName ? ` (${ci.failingJobName})` : '';
|
|
842
|
+
return { ok: ci.status === 'green', detail: `status: ${ci.status}${job}` };
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Turn the GitHub-mirror CI reading into the `ci-green-on-head-github` row.
|
|
847
|
+
*
|
|
848
|
+
* WHY A SECOND CI ROW AT ALL: `ci-green-on-head` reads whatever platform
|
|
849
|
+
* `detectVcsFamily` picks for `origin` — GitLab here — and the GitLab pipeline
|
|
850
|
+
* runs Linux only. The **macOS** matrix leg exists solely on the GitHub mirror
|
|
851
|
+
* (`.github/workflows/test.yml`), i.e. on the operator's own platform. A
|
|
852
|
+
* release could therefore go out fully green with the macOS leg red, and the
|
|
853
|
+
* release path would never have asked.
|
|
854
|
+
*
|
|
855
|
+
* SELF-DISABLING, and that is the one place this row is allowed to pass without
|
|
856
|
+
* evidence: a checkout with no `github` remote has no mirror to be red, so the
|
|
857
|
+
* row is `skipped` rather than red. Everything else keeps the three-state
|
|
858
|
+
* contract of `evaluateCiRow` unchanged — `unknown` and `degraded` FAIL, because
|
|
859
|
+
* "we could not read the mirror" is not "the mirror is green" (the fail-closed
|
|
860
|
+
* house rule at the top of this file).
|
|
861
|
+
*
|
|
862
|
+
* @param {string|undefined} repoSpec — `resolveRepoSpec({vcs:'github'})`, undefined when no github remote resolves
|
|
863
|
+
* @param {null | {status?: string, failingJobName?: string, degraded?: string}} ci
|
|
864
|
+
* @returns {{ok: boolean, detail: string}}
|
|
865
|
+
*/
|
|
866
|
+
export function evaluateGithubCiRow(repoSpec, ci) {
|
|
867
|
+
if (!repoSpec) return { ok: true, detail: 'skipped — no github remote' };
|
|
868
|
+
const row = evaluateCiRow(ci);
|
|
869
|
+
return { ok: row.ok, detail: `${repoSpec} — ${row.detail}` };
|
|
870
|
+
}
|
|
871
|
+
|
|
592
872
|
/**
|
|
593
873
|
* Flag-combination gate, applied before any work.
|
|
594
874
|
*
|
|
@@ -697,12 +977,18 @@ async function preflight(repoRoot, target, { skipCi = false } = {}) {
|
|
|
697
977
|
const changelog = checkChangelogEntry(readFileSync(join(repoRoot, 'CHANGELOG.md'), 'utf8'), target);
|
|
698
978
|
add('changelog-entry', changelog.ok, changelog.problems.join('; '));
|
|
699
979
|
|
|
700
|
-
// 3b. Drift sweep: no
|
|
701
|
-
//
|
|
702
|
-
//
|
|
703
|
-
//
|
|
704
|
-
//
|
|
705
|
-
//
|
|
980
|
+
// 3b. Drift sweep: no file outside the surfaces table + allowlist may still
|
|
981
|
+
// carry the previous release's version literal.
|
|
982
|
+
//
|
|
983
|
+
// POPULATION (#1248, closed here): this used to be one `git grep`, which
|
|
984
|
+
// searches the INDEX and is therefore blind to UNTRACKED files — the sweep
|
|
985
|
+
// measured "no TRACKED file still carries X" while its row read as a
|
|
986
|
+
// whole-tree census. It now enumerates via
|
|
987
|
+
// `scripts/lib/validate/enumerate-repo-files.mjs` (tracked PLUS
|
|
988
|
+
// untracked-not-ignored) and greps in-process; see {@link collectDriftHits}
|
|
989
|
+
// for the fail-closed contract and why hidden directories (the forgotten
|
|
990
|
+
// .codex-plugin manifest) are still covered. Allowlisted: files that
|
|
991
|
+
// legitimately carry version HISTORY.
|
|
706
992
|
const tagList = run('git', ['tag', '-l', 'v*', '--sort=-v:refname'], { cwd: repoRoot });
|
|
707
993
|
const prevTag = (tagList.stdout || '')
|
|
708
994
|
.split('\n').map((t) => t.trim().replace(/^v/, ''))
|
|
@@ -712,8 +998,11 @@ async function preflight(repoRoot, target, { skipCi = false } = {}) {
|
|
|
712
998
|
// one of them means the sweep is unnecessary.
|
|
713
999
|
add('drift-sweep', false, `git tag -l failed (exit ${tagList.status}) — cannot determine the previous release to sweep for`);
|
|
714
1000
|
} else if (prevTag) {
|
|
715
|
-
|
|
716
|
-
|
|
1001
|
+
// Line-shaped rows (not a bare file list): the verdict needs the matching LINE, because a
|
|
1002
|
+
// caret-ranged dependency that equals our previous version is not drift and a file list
|
|
1003
|
+
// cannot show that.
|
|
1004
|
+
const hits = collectDriftHits({ repoRoot, prevTag });
|
|
1005
|
+
const sweep = evaluateDriftSweep(hits, prevTag, HISTORY_ALLOWLIST);
|
|
717
1006
|
add('drift-sweep', sweep.ok, sweep.detail);
|
|
718
1007
|
} else {
|
|
719
1008
|
add('drift-sweep', true, 'no previous tag to sweep against');
|
|
@@ -769,8 +1058,29 @@ async function preflight(repoRoot, target, { skipCi = false } = {}) {
|
|
|
769
1058
|
} else {
|
|
770
1059
|
const { checkCiStatus } = await import('./lib/ci-status-banner.mjs');
|
|
771
1060
|
const ci = await checkCiStatus({ repoRoot, timeoutMs: 15000 });
|
|
772
|
-
const
|
|
773
|
-
add('ci-green-on-head',
|
|
1061
|
+
const row = evaluateCiRow(ci);
|
|
1062
|
+
add('ci-green-on-head', row.ok, row.detail);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// 6b. CI green on the GitHub mirror too. The row above asks the platform
|
|
1066
|
+
// `detectVcsFamily` picks for `origin` (GitLab), whose pipeline is Linux-only;
|
|
1067
|
+
// the macOS matrix leg lives exclusively in `.github/workflows/test.yml`.
|
|
1068
|
+
// `vcs: 'github'` forces the probe onto the mirror without touching the
|
|
1069
|
+
// detection order. The GitHub check-runs path reads `commits/HEAD`, i.e. the
|
|
1070
|
+
// mirror's default-branch head — which is HEAD only because `head-pushed-github`
|
|
1071
|
+
// above proves github/main == local HEAD; that row is this one's precondition,
|
|
1072
|
+
// not a duplicate of it.
|
|
1073
|
+
if (skipCi) {
|
|
1074
|
+
add('ci-green-on-head-github', true, 'SKIPPED via --skip-ci');
|
|
1075
|
+
} else {
|
|
1076
|
+
const githubSpec = resolveRepoSpec({ repoRoot, vcs: 'github' });
|
|
1077
|
+
let githubCi = null;
|
|
1078
|
+
if (githubSpec) {
|
|
1079
|
+
const { checkCiStatus } = await import('./lib/ci-status-banner.mjs');
|
|
1080
|
+
githubCi = await checkCiStatus({ repoRoot, vcs: 'github', timeoutMs: 15000 });
|
|
1081
|
+
}
|
|
1082
|
+
const row = evaluateGithubCiRow(githubSpec, githubCi);
|
|
1083
|
+
add('ci-green-on-head-github', row.ok, row.detail);
|
|
774
1084
|
}
|
|
775
1085
|
|
|
776
1086
|
// 7. Leakage gate over the actual pack file list.
|
|
@@ -952,6 +1262,37 @@ export function waitForRegistryPropagation(repoRoot, target, deps = {}) {
|
|
|
952
1262
|
* @param {{runImpl?: Function, waitImpl?: Function, attempts?: number, delaySeconds?: number}} [deps]
|
|
953
1263
|
* @returns {{receipt: {confirmed: boolean, target: string, detail: string}, propagation: ReturnType<typeof waitForRegistryPropagation>}}
|
|
954
1264
|
*/
|
|
1265
|
+
/**
|
|
1266
|
+
* The exact `npm publish` invocation: argv plus spawn options.
|
|
1267
|
+
*
|
|
1268
|
+
* WHY THE ENV PIN, AND WHY THIS IS A NAMED FUNCTION: `npm_config_loglevel` is
|
|
1269
|
+
* INHERITED, and the `+ <pkg>@<version>` receipt line — the ONE piece of
|
|
1270
|
+
* evidence `evaluatePublishReceipt` accepts as the irreversible boundary — is
|
|
1271
|
+
* printed at `notice` level. Any ancestor that ran under `npm run --silent`
|
|
1272
|
+
* (the husky pre-push gate does exactly that) therefore hands this child a
|
|
1273
|
+
* silent loglevel, npm publishes successfully and prints nothing, the receipt
|
|
1274
|
+
* reads as unconfirmed, `publish()` throws, and `main()` exits 2 = "pre-receipt,
|
|
1275
|
+
* safe to rerun" while the registry already holds the version. That is the worst
|
|
1276
|
+
* failure this file can produce, and it is the same inherited-silent trap the
|
|
1277
|
+
* leakage gate's `npm pack --dry-run` was already pinned against (see preflight
|
|
1278
|
+
* step 7). The pin makes the receipt independent of the caller's environment.
|
|
1279
|
+
*
|
|
1280
|
+
* Exported because `publish()` itself deliberately is not (it is the
|
|
1281
|
+
* irreversible act) — the invocation it builds is pure, so the pin is testable
|
|
1282
|
+
* without a publishable seam.
|
|
1283
|
+
*
|
|
1284
|
+
* @param {string} repoRoot
|
|
1285
|
+
* @param {string} userconfigPath — the 0600 temp npmrc carrying the token
|
|
1286
|
+
* @returns {{cmd: string, args: string[], opts: {cwd: string, env: object}}}
|
|
1287
|
+
*/
|
|
1288
|
+
export function publishInvocation(repoRoot, userconfigPath) {
|
|
1289
|
+
return {
|
|
1290
|
+
cmd: 'npm',
|
|
1291
|
+
args: ['publish', '--access', 'public', '--userconfig', userconfigPath],
|
|
1292
|
+
opts: { cwd: repoRoot, env: { ...process.env, npm_config_loglevel: 'notice' } },
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
|
|
955
1296
|
// Deliberately NOT exported: this is the irreversible act, and every production
|
|
956
1297
|
// path to it runs through main() -> preflight() (leakage gate, CI gate, dirty-tree
|
|
957
1298
|
// gate). Exporting it made the whole gate chain bypassable by any importer, and no
|
|
@@ -959,9 +1300,10 @@ export function waitForRegistryPropagation(repoRoot, target, deps = {}) {
|
|
|
959
1300
|
function publish(repoRoot, target, deps = {}) {
|
|
960
1301
|
const token = loadNpmToken(repoRoot);
|
|
961
1302
|
const runImpl = deps.runImpl ?? run;
|
|
962
|
-
const res = withTempUserconfig(token, (tmpRc) =>
|
|
963
|
-
|
|
964
|
-
|
|
1303
|
+
const res = withTempUserconfig(token, (tmpRc) => {
|
|
1304
|
+
const call = publishInvocation(repoRoot, tmpRc);
|
|
1305
|
+
return runImpl(call.cmd, call.args, call.opts);
|
|
1306
|
+
});
|
|
965
1307
|
const receipt = evaluatePublishReceipt(res, target);
|
|
966
1308
|
if (!receipt.confirmed) throw new Error(receipt.detail);
|
|
967
1309
|
|
|
@@ -1099,6 +1441,46 @@ export async function runPublishRelease(repoRoot, target, deps = {}) {
|
|
|
1099
1441
|
};
|
|
1100
1442
|
}
|
|
1101
1443
|
|
|
1444
|
+
/**
|
|
1445
|
+
* Render the partial tag/push state left behind by a failed post-receipt tail.
|
|
1446
|
+
*
|
|
1447
|
+
* Pure so the reconciliation lines are unit-testable against a synthetic
|
|
1448
|
+
* outcome: the branch that produces them only exists after an irreversible npm
|
|
1449
|
+
* publish, which no test may perform.
|
|
1450
|
+
*
|
|
1451
|
+
* Absent progress is reported as absent, never as "nothing happened": a
|
|
1452
|
+
* `tagAndPushImpl` that threw before attaching `releaseProgress` leaves state
|
|
1453
|
+
* genuinely unknown, and the operator must inspect rather than assume.
|
|
1454
|
+
*
|
|
1455
|
+
* @param {{tag?: string|null, localTagCreated?: boolean, remotes?: Array<{remote: string, mainPushed?: boolean, tagPushed?: boolean}>}} [tagProgress]
|
|
1456
|
+
* @returns {string[]}
|
|
1457
|
+
*/
|
|
1458
|
+
export function describeTagProgress(tagProgress) {
|
|
1459
|
+
const lines = ['\nPARTIAL TAG/PUSH STATE (the npm receipt is already irreversible):'];
|
|
1460
|
+
if (!tagProgress || typeof tagProgress !== 'object') {
|
|
1461
|
+
lines.push(' tag/push progress was not recorded — inspect `git tag -l` and both remotes manually.');
|
|
1462
|
+
return lines;
|
|
1463
|
+
}
|
|
1464
|
+
const tag = tagProgress.tag || 'the release tag';
|
|
1465
|
+
lines.push(
|
|
1466
|
+
tagProgress.localTagCreated === true
|
|
1467
|
+
? ` local tag ${tag}: CREATED (the next \`--check\` will fail \`tag-free-local\` until it is pushed or deleted).`
|
|
1468
|
+
: ` local tag ${tag}: not created.`,
|
|
1469
|
+
);
|
|
1470
|
+
const remotes = Array.isArray(tagProgress.remotes) ? tagProgress.remotes : [];
|
|
1471
|
+
if (remotes.length === 0) {
|
|
1472
|
+
lines.push(' no remote was reached — neither main nor the tag was pushed anywhere.');
|
|
1473
|
+
return lines;
|
|
1474
|
+
}
|
|
1475
|
+
for (const remote of remotes) {
|
|
1476
|
+
lines.push(
|
|
1477
|
+
` ${remote.remote}: main ${remote.mainPushed === true ? 'pushed' : 'NOT pushed'}, ` +
|
|
1478
|
+
`tag ${remote.tagPushed === true ? 'pushed' : 'NOT pushed'}.`,
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
return lines;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1102
1484
|
/**
|
|
1103
1485
|
* Print a completed publish-tail outcome and return the CLI exit code.
|
|
1104
1486
|
*
|
|
@@ -1120,6 +1502,16 @@ export function printPublishOutcome(outcome, target, io = {}) {
|
|
|
1120
1502
|
}
|
|
1121
1503
|
if (!tagAndPushFailed) {
|
|
1122
1504
|
log(` tagged ${outcome.tag} (AFTER publish) and pushed main+tag to: ${outcome.pushed.join(', ')}.`);
|
|
1505
|
+
} else {
|
|
1506
|
+
// The npm receipt is already irreversible at this point, so the ONLY thing
|
|
1507
|
+
// that helps the operator is the exact partial state tag-and-push reached.
|
|
1508
|
+
// `runPublishRelease` collects it (`tagProgress.localTagCreated` plus a
|
|
1509
|
+
// per-remote `{mainPushed, tagPushed}`); this printer used to reference
|
|
1510
|
+
// none of it and suppressed the `pushed:` line as well, so the operator was
|
|
1511
|
+
// told only THAT it failed. The local-tag line matters twice over: a
|
|
1512
|
+
// created local tag makes the next `--check` fail `tag-free-local`, which
|
|
1513
|
+
// reads as a mysterious collision unless it was announced here.
|
|
1514
|
+
for (const line of describeTagProgress(outcome.tagProgress)) error(line);
|
|
1123
1515
|
}
|
|
1124
1516
|
|
|
1125
1517
|
if (outcome.release.skipped) {
|
|
@@ -1377,6 +1769,13 @@ async function main() {
|
|
|
1377
1769
|
if (!values.publish) return 0;
|
|
1378
1770
|
|
|
1379
1771
|
console.log(`\nPublishing ${PACKAGE_NAME}@${target} ...`);
|
|
1772
|
+
// No spawn timeout is set anywhere in this file, on purpose: a kill in the
|
|
1773
|
+
// middle of `npm publish` or a tag push is the very failure the receipt
|
|
1774
|
+
// boundary exists to avoid. The wall-clock cost is real, though — each of
|
|
1775
|
+
// the two `git push` remotes re-runs the husky pre-push full gate (~2.5 min
|
|
1776
|
+
// each, measured) and the site poll waits up to 120 s.
|
|
1777
|
+
console.log(' This tail can run ~7 minutes (pre-push gate x2 remotes + up to 120s site poll).');
|
|
1778
|
+
console.log(' Run it with a >=600s command timeout or in the background — do NOT kill it mid-run.');
|
|
1380
1779
|
const outcome = await runPublishRelease(repoRoot, target, { publishImpl: publish });
|
|
1381
1780
|
return printPublishOutcome(outcome, target);
|
|
1382
1781
|
}
|
|
@@ -40,7 +40,7 @@ import path from 'node:path';
|
|
|
40
40
|
import { fileURLToPath } from 'node:url';
|
|
41
41
|
|
|
42
42
|
import { repairLedger, CANONICAL_LEDGER_REL } from './lib/session-record-repair.mjs';
|
|
43
|
-
import {
|
|
43
|
+
import { getProjectDir } from './lib/platform.mjs';
|
|
44
44
|
|
|
45
45
|
const USAGE =
|
|
46
46
|
'Usage: node scripts/repair-invalid-sessions.mjs [--dry-run|--apply] [--json]\n' +
|
|
@@ -52,7 +52,7 @@ const USAGE =
|
|
|
52
52
|
CANONICAL_LEDGER_REL +
|
|
53
53
|
');\n' +
|
|
54
54
|
' must resolve inside <repo-root>/.orchestrator/metrics/\n' +
|
|
55
|
-
' --repo-root project root (default: resolved
|
|
55
|
+
' --repo-root project root (default: resolved project dir)\n' +
|
|
56
56
|
'Exit codes: 0 completed, 1 arg error, 2 system error, 3 post-verification failed\n';
|
|
57
57
|
|
|
58
58
|
/**
|
|
@@ -168,7 +168,7 @@ async function main() {
|
|
|
168
168
|
|
|
169
169
|
// --apply is an explicit opt-in; absent it (or with --dry-run) we never write.
|
|
170
170
|
const apply = values.apply === true && values['dry-run'] !== true;
|
|
171
|
-
const repoRoot = values['repo-root'] ||
|
|
171
|
+
const repoRoot = values['repo-root'] || getProjectDir();
|
|
172
172
|
const requestedFile = values.file || path.join(repoRoot, CANONICAL_LEDGER_REL);
|
|
173
173
|
|
|
174
174
|
// MED-3: bound the target to <repoRoot>/.orchestrator/metrics/ BEFORE any I/O.
|