session-orchestrator 5.11.0 → 5.13.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/.claude-plugin/marketplace.json +12 -1
- package/.claude-plugin/plugin.json +4 -1
- package/.codex-plugin/agents/explorer.toml +1 -1
- package/.codex-plugin/agents/session-reviewer.toml +2 -2
- package/.codex-plugin/agents/wave-worker.toml +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +8 -0
- package/.orchestrator/policy/public-export.json +30 -0
- package/CHANGELOG.md +180 -3
- package/README.md +126 -117
- package/agents/analyst.md +7 -2
- package/agents/architect-reviewer.md +7 -2
- package/agents/code-implementer.md +4 -2
- package/agents/db-specialist.md +9 -2
- package/agents/dialectic-deriver.md +9 -3
- package/agents/docs-writer.md +7 -2
- package/agents/eval-judge.md +7 -1
- package/agents/qa-strategist.md +7 -2
- package/agents/security-reviewer.md +9 -2
- package/agents/session-reviewer.md +18 -2
- package/agents/skill-applied-judge.md +7 -1
- package/agents/test-writer.md +9 -2
- package/agents/ui-developer.md +9 -2
- package/agents/ux-evaluator.md +6 -1
- package/commands/session.md +4 -2
- package/commands/templates-ack.md +89 -18
- package/docs/agent-authoring.md +30 -11
- package/docs/ci-setup.md +38 -2
- package/docs/codex-setup.md +33 -9
- package/docs/components.md +13 -2
- package/docs/cursor-setup.md +3 -3
- package/docs/events-schema.md +5 -4
- package/docs/evidence-candidates.md +63 -0
- package/docs/fleet-checkin-audit.md +7 -4
- package/docs/github-mirror-protection.md +150 -3
- package/docs/install.md +102 -25
- package/docs/instruction-delivery.md +1 -1
- package/docs/migration-v4.md +2 -2
- package/docs/mods.md +118 -0
- package/docs/owner-config-schema.md +1 -0
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/pm-skills-marketplace.md +4 -3
- package/docs/rule-authoring.md +24 -13
- package/docs/scope-collision-guard.md +2 -1
- package/docs/session-config-reference.md +55 -21
- package/docs/session-config-template.md +32 -24
- package/docs/sessions-ledger-repair.md +137 -0
- package/docs/vault-docs-architecture.md +1 -1
- package/hooks/_lib/hook-import-set.json +406 -73
- package/hooks/_lib/hook-root.mjs +301 -0
- package/hooks/_lib/lock-bootstrap.mjs +434 -4
- package/hooks/_lib/orchestrator-ignore.mjs +3 -0
- package/hooks/_lib/subagent-transcript.mjs +2 -2
- package/hooks/_lib/vcs-create-matcher.mjs +13 -17
- package/hooks/cwd-change-restore.mjs +5 -1
- package/hooks/enforce-commands.mjs +4 -3
- package/hooks/enforce-scope.mjs +11 -1
- package/hooks/hooks-codex.json +48 -2
- package/hooks/hooks.json +31 -31
- package/hooks/on-session-end.mjs +38 -15
- package/hooks/on-session-start.mjs +158 -59
- package/hooks/on-stop.mjs +11 -6
- package/hooks/post-bash-issue-budget-refund.mjs +6 -1
- package/hooks/post-bash-write-verify.mjs +8 -4
- package/hooks/post-edit-validate.mjs +45 -52
- package/hooks/post-subagent-discovery-validator.mjs +2 -2
- package/hooks/post-tool-batch-wave-signal.mjs +8 -4
- package/hooks/post-tool-failure-corrective-context.mjs +5 -1
- package/hooks/pre-bash-destructive-guard.mjs +220 -8
- package/hooks/pre-bash-issue-budget.mjs +6 -1
- package/hooks/pre-bash-sessions-ledger-guard.mjs +691 -123
- package/hooks/pre-bash-staging-fence.mjs +258 -14
- package/hooks/pre-bash-templates-first.mjs +28 -27
- package/hooks/run-node.sh +1 -1
- package/hooks/skill-invocation-telemetry.mjs +15 -8
- package/hooks/subagent-telemetry.mjs +77 -14
- package/hooks/wave-scope-commit-guard.mjs +143 -14
- package/mods/so-hud/.claude-plugin/plugin.json +11 -0
- package/mods/so-hud/README.md +57 -0
- package/mods/so-hud/hooks/beacon-io.mjs +101 -0
- package/mods/so-hud/hooks/heredoc-scan.ts +394 -0
- package/mods/so-hud/hooks/hooks.json +1 -0
- package/mods/so-hud/hooks/logic.ts +182 -0
- package/mods/so-hud/hooks/register.tsx +670 -0
- package/mods/so-hud/hooks/rules.ts +2998 -0
- package/mods/so-hud/tsconfig.json +8 -0
- package/mods/so-hud/types/index.d.ts +36 -0
- package/package.json +4 -3
- package/rules/_index.md +1 -1
- package/rules/always-on/ask-via-tool.md +1 -1
- package/rules/always-on/loop-and-monitor.md +17 -194
- package/rules/always-on/parallel-sessions.md +3 -1
- package/rules/opt-in-stack/backend-data.md +1 -1
- package/rules/opt-in-stack/backend.md +1 -1
- package/rules/opt-in-stack/security-web.md +5 -1
- package/scripts/agent-usage-census.mjs +79 -0
- package/scripts/apply-session-learnings.mjs +34 -6
- package/scripts/archive-closed-prds.mjs +21 -5
- package/scripts/autopilot.mjs +11 -5
- package/scripts/backfill-abandoned-sessions.mjs +13 -3
- package/scripts/backfill-evidence-digest.mjs +2 -1
- package/scripts/backfill-learnings-from-vault.mjs +61 -6
- package/scripts/backfill-sessions.mjs +16 -2
- package/scripts/check-package-manager.mjs +26 -8
- package/scripts/check-sessions-integrity.mjs +4 -1
- package/scripts/ci/assert-vitest-green.mjs +3 -0
- package/scripts/ci/windows-smoke.mjs +201 -0
- package/scripts/close-precheck.mjs +55 -0
- package/scripts/dialectic-deriver.mjs +20 -12
- package/scripts/emit-event.mjs +2 -2
- package/scripts/emit-session.mjs +239 -20
- package/scripts/eval-session.mjs +17 -3
- package/scripts/export-hw-learnings.mjs +29 -2
- package/scripts/fleet-checkin-audit.mjs +8 -4
- package/scripts/fleet-checkin.mjs +73 -3
- package/scripts/gc-stale-worktrees.mjs +6 -1
- package/scripts/github-export.mjs +128 -0
- package/scripts/github-protection-audit.mjs +3 -1
- package/scripts/handover-queue-check.mjs +209 -0
- package/scripts/harness-audit.mjs +6 -4
- package/scripts/lib/agent-frontmatter.mjs +57 -10
- package/scripts/lib/agent-status.mjs +125 -7
- package/scripts/lib/agent-usage-census.mjs +394 -0
- package/scripts/lib/auto-dialectic.mjs +17 -10
- package/scripts/lib/auto-dream.mjs +22 -14
- package/scripts/lib/autopilot/loop.mjs +3 -1
- package/scripts/lib/autopilot/recent-runs.mjs +4 -2
- package/scripts/lib/autopilot/stall-sampler.mjs +2 -1
- package/scripts/lib/autopilot/telemetry.mjs +5 -1
- package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
- package/scripts/lib/baseline-archetypes.mjs +10 -2
- package/scripts/lib/blocked-commands-policy.mjs +4 -3
- package/scripts/lib/build-live-signals.mjs +13 -9
- package/scripts/lib/ci-status-banner.mjs +329 -24
- package/scripts/lib/codex/plugin-contract.mjs +8 -0
- package/scripts/lib/cold-start-detector.mjs +5 -2
- package/scripts/lib/command-blocker.mjs +603 -43
- package/scripts/lib/common.mjs +3 -2
- package/scripts/lib/config/cross-repo.mjs +74 -7
- package/scripts/lib/config/host-paths.mjs +6 -2
- package/scripts/lib/config/remote-hosts.mjs +2 -2
- package/scripts/lib/config/section-extractor.mjs +65 -11
- package/scripts/lib/config/template-keys.mjs +153 -0
- package/scripts/lib/config/vault-integration.mjs +1 -0
- package/scripts/lib/config.mjs +3 -3
- package/scripts/lib/coordinator-snapshot.mjs +1 -1
- package/scripts/lib/discovery/triage-state.mjs +2 -1
- package/scripts/lib/dispatcher/rank.mjs +5 -7
- package/scripts/lib/ecosystem-health.mjs +2 -2
- package/scripts/lib/eval/engine.mjs +3 -2
- package/scripts/lib/eval/session-resolve.mjs +6 -2
- package/scripts/lib/eval/sink.mjs +4 -3
- package/scripts/lib/events-retention-banner.mjs +3 -2
- package/scripts/lib/events-schema.mjs +10 -0
- package/scripts/lib/events.mjs +23 -12
- package/scripts/lib/evolve/autonomy-verdict.mjs +4 -9
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +8 -18
- package/scripts/lib/fleet-protocol.mjs +21 -0
- package/scripts/lib/gates/gate-full.mjs +172 -11
- package/scripts/lib/gates/gate-helpers.mjs +236 -23
- package/scripts/lib/git-config-drift.mjs +141 -7
- package/scripts/lib/github-export.mjs +447 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +16 -5
- package/scripts/lib/handover-queue-marks.mjs +159 -0
- package/scripts/lib/hardware-pattern-detector.mjs +103 -13
- package/scripts/lib/harness-audit/categories/category1.mjs +16 -4
- package/scripts/lib/harness-audit/categories/category4.mjs +11 -8
- package/scripts/lib/harness-audit/categories/category6.mjs +25 -5
- package/scripts/lib/harness-audit/categories/category9.mjs +10 -4
- package/scripts/lib/host-identity.mjs +9 -5
- package/scripts/lib/http-request.mjs +64 -0
- package/scripts/lib/instruction-budget-guard.mjs +30 -22
- package/scripts/lib/io.mjs +6 -3
- package/scripts/lib/issue-budget-reconcile.mjs +3 -2
- package/scripts/lib/issue-budget.mjs +36 -7
- package/scripts/lib/learnings/evidence-candidates.mjs +154 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +99 -7
- package/scripts/lib/learnings/io.mjs +289 -15
- package/scripts/lib/learnings/select.mjs +53 -4
- package/scripts/lib/learnings/store-role.mjs +209 -0
- package/scripts/lib/learnings/surface.mjs +46 -7
- package/scripts/lib/ledger-path.mjs +113 -0
- package/scripts/lib/maintenance-due-banner.mjs +63 -12
- package/scripts/lib/memory-banner.mjs +5 -6
- package/scripts/lib/memory-proposals/collector.mjs +5 -2
- package/scripts/lib/memory-proposals/sink.mjs +7 -4
- package/scripts/lib/memory-proposals/store.mjs +22 -5
- package/scripts/lib/mode-selector-accuracy.mjs +2 -1
- package/scripts/lib/no-follow-open.mjs +57 -0
- package/scripts/lib/orphan-reaper/reaper-audit.mjs +4 -2
- package/scripts/lib/own-lock-root.mjs +95 -0
- package/scripts/lib/owner-interview.mjs +2 -1
- package/scripts/lib/owner-yaml.mjs +70 -7
- package/scripts/lib/platform.mjs +161 -9
- package/scripts/lib/plugin-update-banner.mjs +15 -11
- package/scripts/lib/pre-push-local-partial.mjs +117 -0
- package/scripts/lib/pre-push-remote-gate.mjs +151 -25
- package/scripts/lib/process-group.mjs +270 -34
- package/scripts/lib/project-hygiene.mjs +28 -0
- package/scripts/lib/quality-gate.mjs +33 -9
- package/scripts/lib/reconcile/backlog.mjs +29 -15
- package/scripts/lib/reconcile/engine.mjs +26 -2
- package/scripts/lib/reconcile/idempotency.mjs +65 -3
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +53 -12
- package/scripts/lib/reconcile/verdict-ledger.mjs +224 -0
- package/scripts/lib/reconcile-nudge-banner.mjs +40 -6
- package/scripts/lib/resource-probe/evaluate.mjs +32 -9
- package/scripts/lib/resource-probe/probe-platform.mjs +3 -1
- package/scripts/lib/rule-loader.mjs +20 -10
- package/scripts/lib/scope-baseline.mjs +123 -8
- package/scripts/lib/scope-echo.mjs +5 -2
- package/scripts/lib/scope-gate.mjs +55 -20
- package/scripts/lib/session-close-backfill.mjs +75 -7
- package/scripts/lib/session-end/close-precheck.mjs +151 -0
- package/scripts/lib/session-end/phase-skip.mjs +8 -7
- package/scripts/lib/session-end/tail-runner.mjs +7 -2
- package/scripts/lib/session-end/vault-orphans.mjs +205 -0
- package/scripts/lib/session-id.mjs +9 -1
- package/scripts/lib/session-identity/own-session.mjs +63 -0
- package/scripts/lib/session-invocation.mjs +14 -3
- package/scripts/lib/session-lock.mjs +114 -0
- package/scripts/lib/session-record-repair.mjs +245 -35
- package/scripts/lib/session-registry.mjs +85 -12
- package/scripts/lib/session-schema/constants.mjs +9 -0
- package/scripts/lib/session-schema/filters.mjs +56 -1
- package/scripts/lib/session-schema/validator.mjs +15 -5
- package/scripts/lib/session-start-probes.mjs +149 -22
- package/scripts/lib/session-token-rollup.mjs +2 -1
- package/scripts/lib/session-work-trees.mjs +166 -0
- package/scripts/lib/sessions-canonical.mjs +61 -3
- package/scripts/lib/sessions-integrity-banner.mjs +17 -39
- package/scripts/lib/sessions-staleness-banner.mjs +8 -5
- package/scripts/lib/sh-quote.mjs +1 -0
- package/scripts/lib/skill-evidence-window.mjs +5 -1
- package/scripts/lib/skill-health/join.mjs +10 -9
- package/scripts/lib/skill-invocations-schema.mjs +13 -0
- package/scripts/lib/skill-judgments-schema.mjs +6 -2
- package/scripts/lib/snapshot-probe.mjs +414 -0
- package/scripts/lib/so-hud/collect.mjs +174 -0
- package/scripts/lib/so-hud/runtime.mjs +87 -0
- package/scripts/lib/spiral-carryover.mjs +8 -1
- package/scripts/lib/state-md/frontmatter-mutators.mjs +266 -8
- package/scripts/lib/state-md/idle-reset.mjs +248 -0
- package/scripts/lib/state-md/mission-status.mjs +5 -1
- package/scripts/lib/state-md/recommendations.mjs +26 -4
- package/scripts/lib/state-md-peer-guard.mjs +2 -0
- package/scripts/lib/state-md.mjs +31 -8
- package/scripts/lib/sunset/walker.mjs +4 -3
- package/scripts/lib/telemetry/consent.mjs +4 -2
- package/scripts/lib/telemetry/queue.mjs +3 -3
- package/scripts/lib/telemetry/sync.mjs +45 -9
- package/scripts/lib/test-runner/artifact-paths.mjs +10 -2
- package/scripts/lib/tmux-layout/layouts.mjs +20 -3
- package/scripts/lib/ux-grill/paths.mjs +5 -1
- package/scripts/lib/validate/check-agents.mjs +37 -6
- package/scripts/lib/validate/check-component-paths.mjs +25 -1
- package/scripts/lib/validate/check-guard-requires-parity.mjs +3 -3
- package/scripts/lib/validate/check-hooks-symmetry.mjs +53 -32
- package/scripts/lib/validate/check-json-files.mjs +3 -1
- package/scripts/lib/validate/check-learning-provenance.mjs +3 -2
- package/scripts/lib/validate/check-learnings-shell-writes.mjs +10 -4
- package/scripts/lib/validate/check-metrics-ledger-root.mjs +234 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +159 -11
- package/scripts/lib/validate/check-test-posix-shell.mjs +246 -0
- package/scripts/lib/validate/check-unwired-features.mjs +4 -2
- package/scripts/lib/validate/check-vcs-repo-flag-positional.mjs +58 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +259 -73
- package/scripts/lib/validate/check-workflow-continue-on-error.mjs +63 -0
- package/scripts/lib/validate/confidential-names.mjs +3 -1
- package/scripts/lib/vault-mirror/migrate-ids.mjs +321 -0
- package/scripts/lib/vault-mirror/namespace.mjs +22 -9
- package/scripts/lib/vault-mirror/process.mjs +197 -65
- package/scripts/lib/vault-mirror/prune.mjs +5 -2
- package/scripts/lib/vault-mirror/render-learnings.mjs +8 -7
- package/scripts/lib/vault-mirror/render-sessions.mjs +36 -53
- package/scripts/lib/vault-mirror/retention.mjs +2 -2
- package/scripts/lib/vault-mirror/utils.mjs +106 -13
- package/scripts/lib/vault-staleness-banner.mjs +4 -7
- package/scripts/lib/vault-status/board-writer.mjs +34 -5
- package/scripts/lib/vault-status/narrative-mirror.mjs +66 -7
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +20 -0
- package/scripts/lib/win-process-tree.mjs +137 -0
- package/scripts/lib/workspace.mjs +1 -1
- package/scripts/lib/worktree/constants.mjs +1 -1
- package/scripts/lib/worktree/lifecycle.mjs +1 -1
- package/scripts/lib/worktree/listing.mjs +1 -1
- package/scripts/lib/zx.mjs +50 -0
- package/scripts/mcp-server.mjs +16 -7
- package/scripts/mcp-server.sh +13 -83
- package/scripts/measure-policy-cache-effectiveness.mjs +7 -5
- package/scripts/memory-propose.mjs +10 -3
- package/scripts/migrate-cold-start-seed.mjs +7 -0
- package/scripts/migrate-legacy-learnings.sh +22 -0
- package/scripts/migrate-sessions-jsonl.mjs +16 -2
- package/scripts/migrate-vault-paths.mjs +29 -13
- package/scripts/parse-config.mjs +71 -14
- package/scripts/print-learnings-index.mjs +25 -2
- package/scripts/release-session-lock.mjs +94 -7
- package/scripts/release.mjs +451 -24
- package/scripts/relocate-vault-corpus.mjs +11 -7
- package/scripts/repair-invalid-sessions.mjs +68 -3
- package/scripts/resolve-session-invocation.mjs +57 -8
- package/scripts/run-migrate-v2-cross-repo.mjs +56 -1
- package/scripts/run-quality-gate.mjs +33 -7
- package/scripts/self-update.mjs +56 -7
- package/scripts/session-commit-files.mjs +133 -0
- package/scripts/site-numbers.mjs +52 -9
- package/scripts/snapshot-probe.mjs +156 -0
- package/scripts/so-hud.mjs +68 -0
- package/scripts/state-md-idle-reset.mjs +82 -0
- package/scripts/sweep-expired-learnings.mjs +54 -6
- package/scripts/sweep-expired-rules.mjs +4 -1
- package/scripts/validate-plugin.mjs +22 -0
- package/scripts/vault-integration-watcher.mjs +54 -6
- package/scripts/vault-mirror.mjs +9 -0
- package/scripts/vault-orphans.mjs +171 -0
- package/skills/_shared/fleet-protocol.md +166 -3
- package/skills/_shared/instruction-file-resolution.md +1 -1
- package/skills/_shared/monitor-patterns.md +44 -25
- package/skills/_shared/platform-tools.md +7 -0
- package/skills/_shared/state-ownership.md +5 -4
- package/skills/architecture/references/domain-model.md +1 -1
- package/skills/autopilot/SKILL.md +3 -2
- package/skills/bootstrap/SKILL.md +3 -1
- package/skills/bootstrap/_shared-template.md +4 -4
- package/skills/bootstrap/fast-template.md +1 -1
- package/skills/bootstrap/private-contract.md +1 -1
- package/skills/bootstrap/references/bootstrap-retroactive-flow.md +17 -9
- package/skills/bootstrap/templates/agents/project-code-review.md +5 -1
- package/skills/bootstrap/templates/agents/project-discovery.md +5 -1
- package/skills/bootstrap/templates/agents/project-quality-gate.md +5 -1
- package/skills/claude-md-drift-check/SKILL.md +3 -3
- package/skills/claude-md-drift-check/checker.mjs +61 -101
- package/skills/close/SKILL.md +20 -7
- package/skills/discovery/probes/docs-staleness.mjs +7 -5
- package/skills/discovery/probes/frontend-slop.mjs +7 -3
- package/skills/discovery/probes/ssot-code-diff.mjs +5 -3
- package/skills/discovery/probes/supply-chain-slopcheck.mjs +10 -8
- package/skills/discovery/probes/vault-narrative-staleness.mjs +30 -10
- package/skills/discovery/probes/vault-staleness.mjs +5 -3
- package/skills/discovery/probes-session.md +3 -0
- package/skills/discovery/probes-vault.md +2 -0
- package/skills/dispatcher/SKILL.md +1 -1
- package/skills/eval/SKILL.md +3 -1
- package/skills/evolve/SKILL.md +2 -0
- package/skills/evolve/references/evolve-analyze-mode.md +3 -3
- package/skills/evolve/references/evolve-dialectic-mode.md +1 -1
- package/skills/gitlab-ops/SKILL.md +7 -6
- package/skills/gitlab-portfolio/SKILL.md +2 -2
- package/skills/go/SKILL.md +13 -1
- package/skills/npm-publish/SKILL.md +5 -2
- package/skills/plan/SKILL.md +23 -10
- package/skills/plan/mode-feature.md +13 -8
- package/skills/plan/mode-new.md +6 -14
- package/skills/plan/prd-feature-template.md +19 -0
- package/skills/plan/prd-full-template.md +12 -5
- package/skills/plan/prd-premortem-panel.md +48 -0
- package/skills/plan/prd-reviewer-prompt.md +11 -3
- package/skills/plan/soul.md +1 -1
- package/skills/portfolio/SKILL.md +5 -6
- package/skills/quality-gates/SKILL.md +2 -4
- package/skills/reconcile/SKILL.md +27 -4
- package/skills/release/SKILL.md +16 -8
- package/skills/session-end/SKILL.md +97 -13
- package/skills/session-end/phase-3-2-docs-verification.md +1 -0
- package/skills/session-end/phase-3-6-tail.md +11 -7
- package/skills/session-end/phase-3-7a-recommendations.md +9 -2
- package/skills/session-end/plan-verification.md +16 -1
- package/skills/session-end/references/phase-3-7e-vault-orphans.md +63 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +25 -13
- package/skills/session-end/references/phase-5-issue-cleanup.md +1 -1
- package/skills/session-end/references/session-summary-template.md +8 -0
- package/skills/session-end/session-metrics-write.md +7 -4
- package/skills/session-plan/SKILL.md +5 -3
- package/skills/session-start/SKILL.md +10 -4
- package/skills/session-start/phase-4-5-resource-health.md +11 -5
- package/skills/session-start/phase-7-5-mode-selector.md +3 -1
- package/skills/session-start/phase-8-5-express-path.md +4 -3
- package/skills/session-start/references/phase-1-2-session-lock.md +7 -5
- package/skills/session-start/references/phase-1-5-session-continuity.md +13 -5
- package/skills/session-start/references/phase-1-7-vault-status-board.md +1 -1
- package/skills/session-start/references/phase-4-ssot-environment-check.md +3 -2
- package/skills/tmux-layout/SKILL.md +2 -2
- package/skills/vault-sync/validator.mjs +3 -2
- package/skills/wave-executor/SKILL.md +5 -4
- package/skills/wave-executor/references/wave-executor-state-init.md +3 -3
- package/skills/wave-executor/references/wave-loop-dispatch.md +11 -5
- package/skills/wave-executor/references/wave-loop-review.md +13 -5
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +1 -1
- package/AGENTS.md +0 -207
- package/assets/og-card.png +0 -0
- package/assets/og-card.svg +0 -47
- package/assets/wave-lifecycle.svg +0 -98
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* migrate-vault-paths.mjs — one-shot cross-repo migration for username-drift in vault/project paths.
|
|
4
4
|
*
|
|
5
5
|
* Rewrites a literal "from" username path-segment to a "to" segment across markdown
|
|
6
|
-
* files under
|
|
6
|
+
* files under the projects root (getConfinementRoot(): SO_PROJECTS_ROOT >
|
|
7
|
+
* owner.yaml paths.projects-root > ~/Projects). Only the username path segment changes; the trailing
|
|
7
8
|
* path is preserved.
|
|
8
9
|
*
|
|
9
10
|
* Typical use: a host username change (`/Users/oldname/` → `/Users/newname/`) leaves
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
* Target repo resolution (in priority order):
|
|
24
25
|
* 1. --repos <comma,list> (explicit CLI override)
|
|
25
26
|
* 2. audited-repos: [...] in the same vault-migration-rules.yaml
|
|
26
|
-
* 3. <none, no --repos> → scan only the auto-discover sweep under
|
|
27
|
+
* 3. <none, no --repos> → scan only the auto-discover sweep under <projects-root>/**.
|
|
27
28
|
*
|
|
28
29
|
* Flags:
|
|
29
30
|
* --from SEG Literal source segment (e.g. '/Users/oldname/'). Required if no config entry.
|
|
@@ -61,13 +62,13 @@ import { parseColumnFlags, CliFlagError } from './lib/cli-flags.mjs';
|
|
|
61
62
|
import { isMainModule } from './lib/is-main-module.mjs';
|
|
62
63
|
import { atomicWriteText, envelopeToError } from './lib/io.mjs';
|
|
63
64
|
import { loadHostPaths, resolveHostPath } from './lib/config/host-paths.mjs';
|
|
65
|
+
import { getConfinementRoot } from './lib/config/cross-repo.mjs';
|
|
64
66
|
|
|
65
67
|
// ---------------------------------------------------------------------------
|
|
66
68
|
// Constants
|
|
67
69
|
// ---------------------------------------------------------------------------
|
|
68
70
|
|
|
69
71
|
const HOME = os.homedir();
|
|
70
|
-
const PROJECTS_ROOT = path.join(HOME, 'Projects');
|
|
71
72
|
|
|
72
73
|
// Set at startup from CLI args or config. Helpers read these; main() resolves them.
|
|
73
74
|
let OLD_SEGMENT = null;
|
|
@@ -221,7 +222,8 @@ function printHelp() {
|
|
|
221
222
|
`Usage: migrate-vault-paths.mjs --from <old/> --to <new/> [--dry-run|--apply] [--repos <comma,list>] [--json] [--help]
|
|
222
223
|
|
|
223
224
|
Rewrites a literal "from" path segment to a "to" segment across markdown files
|
|
224
|
-
under
|
|
225
|
+
under the projects root (SO_PROJECTS_ROOT / owner.yaml paths.projects-root,
|
|
226
|
+
default ~/Projects), skipping tests/, fixtures/, .git/, vault-backups, and
|
|
225
227
|
historical contexts (decisions.md, history/, archive*).
|
|
226
228
|
|
|
227
229
|
Both --from and --to are required unless username-rewrites[0] is set in
|
|
@@ -236,7 +238,7 @@ Options:
|
|
|
236
238
|
--apply Write fixes in-place (atomic: tmp + rename)
|
|
237
239
|
--repos LIST Comma-separated repo names or paths to scan; falls back
|
|
238
240
|
to audited-repos: [...] in vault-migration-rules.yaml,
|
|
239
|
-
then to
|
|
241
|
+
then to <projects-root>/** auto-discovery sweep
|
|
240
242
|
--json Emit JSONL records on stdout (one per hit)
|
|
241
243
|
--help, -h Show this help
|
|
242
244
|
|
|
@@ -249,8 +251,21 @@ Exit codes: 0 success 1 input error 2 I/O error
|
|
|
249
251
|
// Repo discovery
|
|
250
252
|
// ---------------------------------------------------------------------------
|
|
251
253
|
|
|
254
|
+
let _projectsRoot;
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Projects root for this run, resolved once (#1622): getConfinementRoot() reads
|
|
258
|
+
* owner.yaml and WARNs on an invalid SO_PROJECTS_ROOT, so per-repo calls repeated both.
|
|
259
|
+
* @returns {string}
|
|
260
|
+
*/
|
|
261
|
+
function projectsRoot() {
|
|
262
|
+
_projectsRoot ??= getConfinementRoot();
|
|
263
|
+
return _projectsRoot;
|
|
264
|
+
}
|
|
265
|
+
|
|
252
266
|
/**
|
|
253
|
-
* Resolve a repo name to an absolute path by searching
|
|
267
|
+
* Resolve a repo name to an absolute path by searching <projects-root>/**
|
|
268
|
+
* (getConfinementRoot(), #1567).
|
|
254
269
|
* If the input is already an absolute path that exists, returns it as-is.
|
|
255
270
|
* Returns null when no match is found.
|
|
256
271
|
*/
|
|
@@ -258,10 +273,10 @@ function resolveRepoPath(nameOrPath) {
|
|
|
258
273
|
if (path.isAbsolute(nameOrPath)) {
|
|
259
274
|
return existsSync(nameOrPath) ? nameOrPath : null;
|
|
260
275
|
}
|
|
261
|
-
// Search up to 4 levels deep under
|
|
276
|
+
// Search up to 4 levels deep under the projects root
|
|
262
277
|
const result = spawnSync(
|
|
263
278
|
'find',
|
|
264
|
-
[
|
|
279
|
+
[projectsRoot(), '-maxdepth', '4', '-type', 'd', '-name', nameOrPath],
|
|
265
280
|
{ encoding: 'utf8' },
|
|
266
281
|
);
|
|
267
282
|
if (result.status !== 0) return null;
|
|
@@ -358,14 +373,15 @@ function findMissingSegmentFiles(roots) {
|
|
|
358
373
|
}
|
|
359
374
|
|
|
360
375
|
/**
|
|
361
|
-
* Auto-discover ALL repos under
|
|
376
|
+
* Auto-discover ALL repos under <projects-root>/** that contain the OLD_SEGMENT
|
|
362
377
|
* literal OR a missing-segment `vault-dir:` drift. Used in tandem with the
|
|
363
378
|
* default-target list to find drift we don't already know about.
|
|
364
379
|
*/
|
|
365
380
|
function discoverAdditionalFiles() {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
381
|
+
const root = projectsRoot();
|
|
382
|
+
if (!existsSync(root)) return [];
|
|
383
|
+
const set = new Set(findCandidateFiles([root]));
|
|
384
|
+
for (const f of findMissingSegmentFiles([root])) set.add(f);
|
|
369
385
|
return [...set];
|
|
370
386
|
}
|
|
371
387
|
|
|
@@ -599,7 +615,7 @@ async function main() {
|
|
|
599
615
|
let candidateFiles = [...candidateSet];
|
|
600
616
|
|
|
601
617
|
// If NO explicit repo list was given (neither --repos nor config.audited-repos),
|
|
602
|
-
// expand scan to discover anything else under
|
|
618
|
+
// expand scan to discover anything else under <projects-root>/**.
|
|
603
619
|
if (!explicitRepos) {
|
|
604
620
|
const additional = discoverAdditionalFiles();
|
|
605
621
|
const set = new Set(candidateFiles);
|
package/scripts/parse-config.mjs
CHANGED
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Output: Single JSON object to stdout with ALL config fields (defaults applied).
|
|
10
10
|
* `--json` is an explicit NO-OP alias for that default — accepted because
|
|
11
|
-
* `docs/session-config-template.md` (
|
|
12
|
-
* `node scripts/parse-config.mjs --json` as THE
|
|
13
|
-
* flag used to be swallowed by the
|
|
14
|
-
* `File not found: --json` (exit 1).
|
|
15
|
-
* requires every CLI to accept
|
|
11
|
+
* `docs/session-config-template.md` ("How to use" and "Validation &
|
|
12
|
+
* Bootstrap") documents `node scripts/parse-config.mjs --json` as THE
|
|
13
|
+
* validation command, and the flag used to be swallowed by the
|
|
14
|
+
* positional-path slot and rejected with `File not found: --json` (exit 1).
|
|
15
|
+
* `cli-design.md` § JSON-First Output also requires every CLI to accept
|
|
16
|
+
* `--json`.
|
|
16
17
|
* Exit codes: 0 success, 1 error (message to stderr)
|
|
17
18
|
*
|
|
18
19
|
* Unparsable lines inside `## Session Config` (#1097) are reported per line on
|
|
@@ -20,6 +21,11 @@
|
|
|
20
21
|
* `enforcement: strict`; `off` is silent. stdout is unchanged for any
|
|
21
22
|
* well-formed block — a warning never alters the emitted JSON.
|
|
22
23
|
*
|
|
24
|
+
* Unknown top-level keys (#1562) — a key the plugin's own
|
|
25
|
+
* `docs/session-config-template.md` does not document, i.e. a typo or a key
|
|
26
|
+
* from a newer plugin version — are reported per line on stderr under `warn`
|
|
27
|
+
* AND `strict` (a report, never a refusal); `off` is silent. stdout unchanged.
|
|
28
|
+
*
|
|
23
29
|
* Environment:
|
|
24
30
|
* SO_CONFIG_FILE — override filename (e.g. "AGENTS.md") resolved from project root
|
|
25
31
|
* SO_SKIP_CONFIG_VALIDATION — set to "1" to bypass validate-config.mjs
|
|
@@ -30,7 +36,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
30
36
|
import { join, dirname, resolve, parse as parsePath } from 'node:path';
|
|
31
37
|
import { fileURLToPath } from 'node:url';
|
|
32
38
|
import { parseSessionConfig } from './lib/config.mjs';
|
|
33
|
-
import { collectUnparsableLines } from './lib/config/section-extractor.mjs';
|
|
39
|
+
import { collectUnparsableLines, collectTopLevelKeyLines } from './lib/config/section-extractor.mjs';
|
|
40
|
+
import { knownTemplateKeys } from './lib/config/template-keys.mjs';
|
|
34
41
|
import { findUnterminatedComment } from './lib/config/block-preprocess.mjs';
|
|
35
42
|
import { ENFORCEMENT_VALUES } from './lib/config-schema.mjs';
|
|
36
43
|
|
|
@@ -189,15 +196,15 @@ try {
|
|
|
189
196
|
|
|
190
197
|
const unparsableLines = collectUnparsableLines(content);
|
|
191
198
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const enforcement = ENFORCEMENT_VALUES.has(config.enforcement) ? config.enforcement : 'warn';
|
|
199
|
+
// Belt-and-braces, not a live branch: `parseSessionConfig` already REFUSES
|
|
200
|
+
// an out-of-vocabulary enforcement value above (measured: `enforcement:
|
|
201
|
+
// banana` exits 1 with "must be strict|warn|off" and never reaches here), so
|
|
202
|
+
// `config.enforcement` is one of the three by the time this line runs. The
|
|
203
|
+
// fallback exists for the direction that matters if that ever loosens — an
|
|
204
|
+
// unknown value must degrade to `warn`, never arm the `strict` refusal.
|
|
205
|
+
const enforcement = ENFORCEMENT_VALUES.has(config.enforcement) ? config.enforcement : 'warn';
|
|
200
206
|
|
|
207
|
+
if (unparsableLines.length > 0) {
|
|
201
208
|
if (enforcement !== 'off') {
|
|
202
209
|
// Ceiling: 20 named lines. A whole prose section pasted into the block is
|
|
203
210
|
// one defect, not 200, and the strict path below exits immediately after
|
|
@@ -230,6 +237,56 @@ if (unparsableLines.length > 0) {
|
|
|
230
237
|
}
|
|
231
238
|
}
|
|
232
239
|
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// Unknown-key report (#1562) — a typo'd key is not a silent default either
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
//
|
|
244
|
+
// A well-formed line with a mis-spelled key (`persistance: true`) parses
|
|
245
|
+
// cleanly, so the gate above never sees it: the key lands in the KV map under a
|
|
246
|
+
// name nothing reads, and the real key keeps its default. The population of
|
|
247
|
+
// known keys is the plugin's OWN template (first + last `## Session Config`
|
|
248
|
+
// block, the population drift-check Check 6 reads) plus every top-level key
|
|
249
|
+
// `parseSessionConfig()` emits, so it moves with the plugin version, never
|
|
250
|
+
// with the repo.
|
|
251
|
+
//
|
|
252
|
+
// A report, never a refusal — even under `strict`: a key from a newer plugin
|
|
253
|
+
// version is not a defect of the repo. Three states stay apart on purpose:
|
|
254
|
+
// measured with findings (one line per key), measured clean (silence), and NOT
|
|
255
|
+
// measured (one line saying why) — "the check could not run" must never read
|
|
256
|
+
// like "no unknown keys".
|
|
257
|
+
|
|
258
|
+
if (enforcement !== 'off') {
|
|
259
|
+
const templatePath = join(SCRIPT_DIR, '..', 'docs', 'session-config-template.md');
|
|
260
|
+
let known = null;
|
|
261
|
+
let notRunReason = null;
|
|
262
|
+
try {
|
|
263
|
+
known = knownTemplateKeys(readFileSync(templatePath, 'utf8'));
|
|
264
|
+
if (!known) notRunReason = 'it has no distinct starter and opt-in `## Session Config` blocks';
|
|
265
|
+
// A key the parser itself emits is read, wherever the template documents it:
|
|
266
|
+
// `skill-evolution` and `dispatcher-autonomy` are parsed from the whole file
|
|
267
|
+
// (their parity-exempt template blocks sit outside `## Session Config`), so
|
|
268
|
+
// the same key inside the block is applied and must not read "no effect".
|
|
269
|
+
else for (const key of Object.keys(config)) known.add(key);
|
|
270
|
+
} catch (err) {
|
|
271
|
+
notRunReason = err.code === 'ENOENT' ? 'it was not found' : `it could not be read (${err.code ?? err.message})`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (notRunReason) {
|
|
275
|
+
process.stderr.write(
|
|
276
|
+
`⚠ parse-config.mjs: unknown Session Config key check did not run — ${templatePath}: ${notRunReason}\n`,
|
|
277
|
+
);
|
|
278
|
+
} else {
|
|
279
|
+
const configName = parsePath(configFile).base;
|
|
280
|
+
for (const { line, key } of collectTopLevelKeyLines(content)) {
|
|
281
|
+
if (known.has(key)) continue;
|
|
282
|
+
process.stderr.write(
|
|
283
|
+
`⚠ ${configName}:${line}: unknown Session Config key '${key}' — not in docs/session-config-template.md ` +
|
|
284
|
+
'(a typo, or a key from a newer plugin version); it has no effect\n',
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
233
290
|
// jq -n produces pretty-printed JSON without a trailing newline — match that format
|
|
234
291
|
const assembledJson = JSON.stringify(config, null, 2);
|
|
235
292
|
|
|
@@ -101,6 +101,7 @@ import { join, dirname } from 'node:path';
|
|
|
101
101
|
import { fileURLToPath } from 'node:url';
|
|
102
102
|
|
|
103
103
|
import { findProjectRoot } from './lib/common.mjs';
|
|
104
|
+
import { resolveLedgerPath } from './lib/ledger-path.mjs';
|
|
104
105
|
import { eventsFilePath } from './lib/events.mjs';
|
|
105
106
|
import { resolveStateArtifactPath } from './lib/state-md.mjs';
|
|
106
107
|
import {
|
|
@@ -154,6 +155,11 @@ Options:
|
|
|
154
155
|
--max-chars <n> Hard cap on the rendered index body (default: ${LEARNINGS_INDEX_MAX_CHARS}).
|
|
155
156
|
--pool-size <n> Active entries pulled before ranking (default: ${CANDIDATE_POOL_SIZE}).
|
|
156
157
|
--learnings <path> Learnings JSONL (default: .orchestrator/metrics/learnings.jsonl).
|
|
158
|
+
--evidence-candidates <path> Opt into a bounded local candidate JSON artifact.
|
|
159
|
+
Requires --source-repo and an exact non-empty --task-text.
|
|
160
|
+
Invalid/unavailable artifacts keep the baseline output;
|
|
161
|
+
provider status/counts go to stderr. No provider is run.
|
|
162
|
+
--source-repo <id> Explicit opaque repo ID bound to the candidate artifact.
|
|
157
163
|
--rules-dir <path> Natively-delivered rule corpus (default: .claude/rules).
|
|
158
164
|
Learnings already delivered as a rule file there are
|
|
159
165
|
excluded from the index (#1019). A path that does not
|
|
@@ -208,6 +214,8 @@ try {
|
|
|
208
214
|
'max-chars': { type: 'string' },
|
|
209
215
|
'pool-size': { type: 'string' },
|
|
210
216
|
learnings: { type: 'string' },
|
|
217
|
+
'evidence-candidates': { type: 'string' },
|
|
218
|
+
'source-repo': { type: 'string' },
|
|
211
219
|
'rules-dir': { type: 'string' },
|
|
212
220
|
'no-event': { type: 'boolean', default: false },
|
|
213
221
|
json: { type: 'boolean', default: false },
|
|
@@ -219,6 +227,14 @@ try {
|
|
|
219
227
|
}
|
|
220
228
|
|
|
221
229
|
const opts = parsed.values;
|
|
230
|
+
if (opts['evidence-candidates'] !== undefined &&
|
|
231
|
+
(!opts['evidence-candidates'] || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/.test(opts['source-repo'] ?? '') ||
|
|
232
|
+
!opts['task-text']?.trim() || opts['task-text'].length > 4000)) {
|
|
233
|
+
fail('--evidence-candidates requires a path, --source-repo ID and non-empty --task-text (at most 4000 characters)', 1);
|
|
234
|
+
}
|
|
235
|
+
if (opts['source-repo'] !== undefined && opts['evidence-candidates'] === undefined) {
|
|
236
|
+
fail('--source-repo requires --evidence-candidates', 1);
|
|
237
|
+
}
|
|
222
238
|
|
|
223
239
|
/**
|
|
224
240
|
* Parse a `--max-*` value: a non-negative integer. Anything else is a
|
|
@@ -249,7 +265,7 @@ const poolSize = parseCap(opts['pool-size'], CANDIDATE_POOL_SIZE, '--pool-size')
|
|
|
249
265
|
const repoRoot = findProjectRoot(process.cwd());
|
|
250
266
|
const learningsPath = opts.learnings
|
|
251
267
|
? opts.learnings
|
|
252
|
-
:
|
|
268
|
+
: resolveLedgerPath('.orchestrator/metrics/learnings.jsonl', repoRoot); // #1722
|
|
253
269
|
const eventsPath = eventsFilePath(repoRoot); // #1514 point 1: the repo's one ledger
|
|
254
270
|
// #1019 — the natively-delivered rule corpus. Every `.claude/rules/*.md` reaches
|
|
255
271
|
// a dispatched agent in FULL through Claude Code's own project-instruction
|
|
@@ -338,7 +354,11 @@ try {
|
|
|
338
354
|
selection = await selectLearningsFromFile(
|
|
339
355
|
learningsPath,
|
|
340
356
|
{ file_paths: scopePaths, text: taskText },
|
|
341
|
-
{ maxScoped, maxGlobal, maxChars, poolSize, rulesDir
|
|
357
|
+
{ maxScoped, maxGlobal, maxChars, poolSize, rulesDir,
|
|
358
|
+
...(opts['evidence-candidates'] !== undefined ? {
|
|
359
|
+
evidenceCandidates: opts['evidence-candidates'], sourceRepo: opts['source-repo'],
|
|
360
|
+
} : {}),
|
|
361
|
+
},
|
|
342
362
|
);
|
|
343
363
|
} catch (err) {
|
|
344
364
|
// `selectLearningsFromFile` is contractually total (contract point 1), so this
|
|
@@ -350,6 +370,9 @@ try {
|
|
|
350
370
|
|
|
351
371
|
const selected = selection.entries;
|
|
352
372
|
const scopeMatched = selection.scopeMatched;
|
|
373
|
+
if (selection.evidence) {
|
|
374
|
+
process.stderr.write(`[evidence-candidates] ${JSON.stringify(selection.evidence)}\n`);
|
|
375
|
+
}
|
|
353
376
|
|
|
354
377
|
// ---------------------------------------------------------------------------
|
|
355
378
|
// Render — the WRAPPER only; per-entry lines come from the selector
|
|
@@ -43,12 +43,15 @@
|
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
45
|
import { parseArgs } from 'node:util';
|
|
46
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
46
47
|
import path from 'node:path';
|
|
47
48
|
|
|
48
|
-
import { readLockDetailed, loadOwnerProof, release } from './lib/session-lock.mjs';
|
|
49
|
+
import { readLockDetailed, loadOwnerProof, release, writeReleaseMarker } from './lib/session-lock.mjs';
|
|
49
50
|
import { LOCK_RELEASED_EVENT } from './lib/session-transition.mjs';
|
|
50
51
|
import { emitEvent } from './lib/events.mjs';
|
|
51
52
|
import { isMainModule } from './lib/is-main-module.mjs';
|
|
53
|
+
import { registrySessionRoot, resolveOwnLockRootFor } from './lib/own-lock-root.mjs';
|
|
54
|
+
import { dotGitAncestor } from './lib/platform.mjs';
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
57
|
* `caller` value stamped into the breadcrumb, so this fourth release site stays
|
|
@@ -69,18 +72,31 @@ WHAT IT DOES
|
|
|
69
72
|
3. Emits orchestrator.session.lock.released with the ids read BEFORE the
|
|
70
73
|
delete, plus caller="${PHASE_CALLER}".
|
|
71
74
|
4. RE-READS the lock path and exits 0 only when our lock is provably gone.
|
|
75
|
+
5. Leaves .orchestrator/runtime/lock-released/<raw-id>.json, so a compaction
|
|
76
|
+
or resume of this session does not re-create the lock (#1651); the
|
|
77
|
+
session's next deliberate acquire() removes it.
|
|
72
78
|
|
|
73
79
|
FLAGS
|
|
74
80
|
--session-id <id> REQUIRED. The RAW (physical) session id that owns the lock
|
|
75
81
|
— .orchestrator/session.lock "session_id", never STATE.md
|
|
76
82
|
"session" and never the semantic id. A lock owned by a
|
|
77
83
|
different id is NEVER released.
|
|
78
|
-
--repo-root <path> Repo whose lock is released
|
|
84
|
+
--repo-root <path> Repo whose lock is released. Default: the checkout whose
|
|
85
|
+
lock carries --session-id — the host registry's
|
|
86
|
+
session_root, the cwd's checkout, its main checkout, or a
|
|
87
|
+
registered linked worktree, in that order; with none of
|
|
88
|
+
them, the cwd's checkout (#1730). The JSON result names
|
|
89
|
+
the root (repo_root) and how it was found (root_source:
|
|
90
|
+
flag|registry|cwd|main-checkout|worktree|none).
|
|
79
91
|
--json Machine-readable result on stdout.
|
|
80
92
|
-h, --help Show this help and exit 0.
|
|
81
93
|
|
|
82
94
|
OUTCOMES
|
|
83
95
|
absent no lock file — nothing released, NO event, exit 0.
|
|
96
|
+
|
|
97
|
+
Without --repo-root the #1651 marker (absent and released alike) goes to the
|
|
98
|
+
lock's root, the cwd's checkout and the registry's session_root, existing
|
|
99
|
+
checkouts only; release_marker_roots lists each root with its result.
|
|
84
100
|
deleted our lock was removed and re-read as gone.
|
|
85
101
|
already-gone ownership matched but the lock had already vanished; the event
|
|
86
102
|
is still written, because a lock removed by a third party while
|
|
@@ -137,6 +153,53 @@ function ourLockIsGone(repoRoot, sessionId) {
|
|
|
137
153
|
return false;
|
|
138
154
|
}
|
|
139
155
|
|
|
156
|
+
/** @param {string} p */
|
|
157
|
+
function canonical(p) {
|
|
158
|
+
try { return realpathSync.native(p); } catch { return path.resolve(p); }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Where the #1651 marker goes (#1730). A later compaction's SessionStart looks
|
|
163
|
+
* for it at `resolveSessionStartRoot`, which depends on THAT compaction's cwd —
|
|
164
|
+
* unknown here. So it goes to every root that reader can land on: the lock's
|
|
165
|
+
* root, the cwd's checkout and the registry's session_root. Only existing
|
|
166
|
+
* checkouts (a `.git` entry): a stale registry entry must not re-create the
|
|
167
|
+
* directory tree of a removed worktree. The marker is keyed on this session's
|
|
168
|
+
* own raw id, so an extra copy changes nothing for another session. With
|
|
169
|
+
* --repo-root the given root alone, as before.
|
|
170
|
+
*
|
|
171
|
+
* @param {{ root: string, source: string }} resolved
|
|
172
|
+
* @param {string} sessionId
|
|
173
|
+
* @returns {string[]}
|
|
174
|
+
*/
|
|
175
|
+
function markerRootsFor(resolved, sessionId) {
|
|
176
|
+
if (resolved.source === 'flag') return [resolved.root];
|
|
177
|
+
const cwdTop = dotGitAncestor(process.cwd()) || path.resolve(process.cwd());
|
|
178
|
+
const extra = [cwdTop, registrySessionRoot(sessionId)]
|
|
179
|
+
.filter((r) => typeof r === 'string' && existsSync(path.join(r, '.git')));
|
|
180
|
+
return [...new Set([resolved.root, ...extra].map(canonical))];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Write the marker at each root; a failed copy is named on stderr and in the
|
|
185
|
+
* result, never hidden behind one that was written.
|
|
186
|
+
*
|
|
187
|
+
* @param {string[]} roots
|
|
188
|
+
* @param {string} sessionId
|
|
189
|
+
* @returns {Array<{ root: string, ok: boolean }>}
|
|
190
|
+
*/
|
|
191
|
+
function writeMarkers(roots, sessionId) {
|
|
192
|
+
return roots.map((root) => {
|
|
193
|
+
const marker = writeReleaseMarker({ repoRoot: root, sessionId });
|
|
194
|
+
if (marker.ok !== true) {
|
|
195
|
+
process.stderr.write(
|
|
196
|
+
`release-session-lock: release marker not written at ${root} (${marker.reason}) — a compaction there may re-create the lock\n`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
return { root, ok: marker.ok === true };
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
140
203
|
/**
|
|
141
204
|
* CLI entry point.
|
|
142
205
|
*
|
|
@@ -175,11 +238,20 @@ export async function main({ argv = process.argv.slice(2) } = {}) {
|
|
|
175
238
|
return 1;
|
|
176
239
|
}
|
|
177
240
|
|
|
178
|
-
|
|
241
|
+
// #1730: `/close` runs this without --repo-root, and its cwd is often not the
|
|
242
|
+
// checkout holding the lock — a worktree session's lock sits in the main
|
|
243
|
+
// checkout, or the shell cwd reset to the main checkout while the lock is in
|
|
244
|
+
// a worktree. Reading at the cwd answered `absent` with exit 0 and the lock
|
|
245
|
+
// stayed. An explicit --repo-root is taken as given.
|
|
246
|
+
const resolved = values['repo-root'] !== undefined
|
|
247
|
+
? { root: path.resolve(values['repo-root']), source: 'flag' }
|
|
248
|
+
: resolveOwnLockRootFor({ sessionId, cwd: process.cwd() });
|
|
249
|
+
const repoRoot = resolved.root;
|
|
179
250
|
|
|
180
251
|
const emitResult = async (result) => {
|
|
181
|
-
|
|
182
|
-
|
|
252
|
+
const located = { ...result, repo_root: repoRoot, root_source: resolved.source };
|
|
253
|
+
if (values.json) process.stdout.write(JSON.stringify(located, null, 2) + '\n');
|
|
254
|
+
else printHuman(located);
|
|
183
255
|
};
|
|
184
256
|
|
|
185
257
|
// -- Step 1: read the lock, and capture the ids BEFORE anything is deleted --
|
|
@@ -188,8 +260,14 @@ export async function main({ argv = process.argv.slice(2) } = {}) {
|
|
|
188
260
|
if (lockDetail.status === 'absent') {
|
|
189
261
|
// Nothing was released, so nothing terminal happened: NO event. Writing one
|
|
190
262
|
// here would put a fictional lifecycle end into the stream — the idempotent
|
|
191
|
-
// re-run case is not a release.
|
|
192
|
-
|
|
263
|
+
// re-run case is not a release. The #1651 marker IS written: a session that
|
|
264
|
+
// closes without a lock (bootstrap fs-error, or an earlier release) must not
|
|
265
|
+
// get one re-created by a later compaction either (roots: markerRootsFor).
|
|
266
|
+
const markers = writeMarkers(markerRootsFor(resolved, sessionId), sessionId);
|
|
267
|
+
const result = {
|
|
268
|
+
ok: true, outcome: 'absent', verified: true, session_id: sessionId, event_emitted: false,
|
|
269
|
+
release_marker: markers.every((m) => m.ok), release_marker_roots: markers,
|
|
270
|
+
};
|
|
193
271
|
await emitResult(result);
|
|
194
272
|
return 0;
|
|
195
273
|
}
|
|
@@ -249,6 +327,13 @@ export async function main({ argv = process.argv.slice(2) } = {}) {
|
|
|
249
327
|
const verified = ourLockIsGone(repoRoot, sessionId);
|
|
250
328
|
const outcome = alreadyGone ? 'already-gone' : 'deleted';
|
|
251
329
|
|
|
330
|
+
// #1651: leave "released on purpose" behind, so the SessionStart bootstrap
|
|
331
|
+
// does not re-create this lock after a compaction or resume of the same raw
|
|
332
|
+
// session (a headless run may hold the slot by then). Best-effort: a failed
|
|
333
|
+
// marker write is reported, but the release itself stands.
|
|
334
|
+
const markers = verified ? writeMarkers(markerRootsFor(resolved, sessionId), sessionId) : [];
|
|
335
|
+
const markerWritten = verified && markers.every((m) => m.ok);
|
|
336
|
+
|
|
252
337
|
// -- Step 4: the terminal breadcrumb --------------------------------------
|
|
253
338
|
// The ids are passed EXPLICITLY: the lock is gone by now, so emitEvent's
|
|
254
339
|
// lock-derived correlation would find nothing to attribute this record to.
|
|
@@ -275,6 +360,8 @@ export async function main({ argv = process.argv.slice(2) } = {}) {
|
|
|
275
360
|
...(semanticSessionId !== null ? { semantic_session_id: semanticSessionId } : {}),
|
|
276
361
|
event_emitted: eventEmitted,
|
|
277
362
|
...(emitError !== null ? { event_error: emitError } : {}),
|
|
363
|
+
release_marker: markerWritten,
|
|
364
|
+
release_marker_roots: markers,
|
|
278
365
|
};
|
|
279
366
|
await emitResult(result);
|
|
280
367
|
|