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
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* migrate-ids.mjs — one-time repair of the frontmatter `id` of notes the vault
|
|
4
|
+
* mirror wrote before #1601/#1602.
|
|
5
|
+
*
|
|
6
|
+
* Why a separate pass: the mirror's hot path (`process.mjs`) never rewrites a
|
|
7
|
+
* note because only its `id` is wrong. Its only rewrite is a full re-render
|
|
8
|
+
* from the ledger record, and that re-render brought back plaintext that an
|
|
9
|
+
* earlier run had masked to `[REDACTED]` (#1025) whenever the secret was
|
|
10
|
+
* missing from the current env. That was the release-review finding HOCH 1 on
|
|
11
|
+
* MR !148. This pass changes ONE line and nothing else.
|
|
12
|
+
*
|
|
13
|
+
* Contract:
|
|
14
|
+
* - Only notes with the vault-mirror `_generator` marker are considered, in
|
|
15
|
+
* `40-learnings/`, `50-sessions/` and their `90-archive/mirror/` copies,
|
|
16
|
+
* flat (`<zone>/<file>`) or namespaced (`<zone>/<ns>/<file>`). Month
|
|
17
|
+
* rollups are skipped (their id is not derived from the filename).
|
|
18
|
+
* - The new id is `noteIdFor(<ns>, <filename slug>)`: the same id the
|
|
19
|
+
* generator renders today. The namespace is the folder; a flat note uses
|
|
20
|
+
* its own `source-repo` (as `fmNamesSlug` in process.mjs does).
|
|
21
|
+
* - `schema` changes: the old id breaks the vault-sync schema (missing, over
|
|
22
|
+
* 128 chars, all digits, not a kebab slug). Always planned.
|
|
23
|
+
* - `namespace` changes: the old id is the bare pre-#1602 slug. Planned only
|
|
24
|
+
* with `includeBare`, because it touches every older generated note once.
|
|
25
|
+
* - Two notes of the same layer (live, or `90-archive/`) that would end up
|
|
26
|
+
* with the same id are both left alone and reported as `conflicts`. A live
|
|
27
|
+
* note and its archive copy never conflict: the vault-sync validator does
|
|
28
|
+
* not check `90-archive/`, so blocking the live repair on its copy would
|
|
29
|
+
* leave the schema error in place.
|
|
30
|
+
* - A note whose frontmatter has no `id:` line is not planned (the one-line
|
|
31
|
+
* rewrite has no line to change); it is reported as `unrepairable`.
|
|
32
|
+
* - Apply replaces only the `id:` line inside the frontmatter
|
|
33
|
+
* (`withNoteId`), checks that nothing else changed, re-reads the file and
|
|
34
|
+
* skips it if it changed since the plan, and writes atomically: a tmp
|
|
35
|
+
* sibling in the same folder, then rename (`atomicWriteWithBackup`).
|
|
36
|
+
* - Apply refuses to write anything when the plan has more changes than
|
|
37
|
+
* `maxChanges`. The CLI requires `--max-changes` with `--apply`, so the
|
|
38
|
+
* count comes from a dry run first.
|
|
39
|
+
*
|
|
40
|
+
* Usage (dry run is the default and writes nothing):
|
|
41
|
+
* node scripts/lib/vault-mirror/migrate-ids.mjs --vault-dir PATH [--include-bare]
|
|
42
|
+
* node scripts/lib/vault-mirror/migrate-ids.mjs --vault-dir PATH [--include-bare] --apply --max-changes N
|
|
43
|
+
*
|
|
44
|
+
* Output: one JSON line per planned change ({path, from, to, reason}), then
|
|
45
|
+
* one summary line. Paths are vault-relative. The `from` values are the
|
|
46
|
+
* record needed to undo a run.
|
|
47
|
+
*
|
|
48
|
+
* Exit codes: 0 success · 1 usage error · 2 refused (over the cap), per-file
|
|
49
|
+
* errors, or `unrepairable` notes left after --apply
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
53
|
+
import { join, resolve } from 'node:path';
|
|
54
|
+
import yaml from 'js-yaml';
|
|
55
|
+
|
|
56
|
+
import { atomicWriteWithBackup } from '../io.mjs';
|
|
57
|
+
import { expandTilde } from '../common.mjs';
|
|
58
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
59
|
+
import { NOTE_ID_MAX_LENGTH, isValidSlug, noteIdFor, noteIdMatches, parseFrontmatter, slugifyIdSafe, withNoteId } from './utils.mjs';
|
|
60
|
+
import { ARCHIVE_ROOT, GENERATOR_MARKER, ROLLUP_PREFIX } from './retention.mjs';
|
|
61
|
+
|
|
62
|
+
const ZONES = ['40-learnings', '50-sessions'];
|
|
63
|
+
/** The vault-sync schema's id pattern (`skills/vault-sync/validator.mjs` slugRegex). */
|
|
64
|
+
const SCHEMA_SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
65
|
+
const ID_LINE = /^id:(.*)$/m;
|
|
66
|
+
|
|
67
|
+
/** Every `.md` of a zone root: flat files and one folder level below. */
|
|
68
|
+
function listZoneFiles(vaultDir, zoneRel) {
|
|
69
|
+
const out = [];
|
|
70
|
+
let top;
|
|
71
|
+
try {
|
|
72
|
+
top = readdirSync(join(vaultDir, zoneRel), { withFileTypes: true });
|
|
73
|
+
} catch {
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
for (const ent of top) {
|
|
77
|
+
if (ent.isFile() && ent.name.endsWith('.md')) out.push({ rel: `${zoneRel}/${ent.name}`, dirNs: '', name: ent.name });
|
|
78
|
+
else if (ent.isDirectory()) {
|
|
79
|
+
let inner;
|
|
80
|
+
try {
|
|
81
|
+
inner = readdirSync(join(vaultDir, zoneRel, ent.name), { withFileTypes: true });
|
|
82
|
+
} catch {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
for (const f of inner) {
|
|
86
|
+
if (f.isFile() && f.name.endsWith('.md')) out.push({ rel: `${zoneRel}/${ent.name}/${f.name}`, dirNs: ent.name, name: f.name });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Does the on-disk id break the vault-sync schema (`z.string().regex(slug).max(128)`)?
|
|
95
|
+
* `cur` is the parseFrontmatter value (quotes stripped, always a string);
|
|
96
|
+
* `raw` is the literal after `id:`. An unquoted `433` is a YAML NUMBER even
|
|
97
|
+
* though parseFrontmatter hands it back as a string — the #1601 shape — so
|
|
98
|
+
* the raw literal goes through a real YAML parse too.
|
|
99
|
+
*/
|
|
100
|
+
function idViolatesSchema(cur, raw) {
|
|
101
|
+
if (typeof cur !== 'string' || cur.length > NOTE_ID_MAX_LENGTH || !SCHEMA_SLUG.test(cur)) return true;
|
|
102
|
+
try {
|
|
103
|
+
return typeof yaml.load(`id: ${raw}`)?.id !== 'string';
|
|
104
|
+
} catch {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** The leading frontmatter block, or null. */
|
|
110
|
+
function frontmatterHead(content) {
|
|
111
|
+
if (!content.startsWith('---')) return null;
|
|
112
|
+
const end = content.indexOf('\n---', 3);
|
|
113
|
+
return end === -1 ? null : content.slice(0, end);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Plan the id migration. Read-only.
|
|
118
|
+
*
|
|
119
|
+
* @param {{ vaultDir: string, includeBare?: boolean }} opts
|
|
120
|
+
* @returns {{ changes: Array<{path: string, from: string, to: string, reason: 'schema'|'namespace'}>,
|
|
121
|
+
* conflicts: Array<{path: string, to: string, reason: 'schema'|'namespace'}>, unrepairable: Array<{path: string, reason: 'no-id-line'}>,
|
|
122
|
+
* scanned: number, unrecognised: number, contents: Map<string, string> }}
|
|
123
|
+
* `contents` holds the bytes each change was planned against (apply compares them).
|
|
124
|
+
*/
|
|
125
|
+
export function planIdMigration({ vaultDir, includeBare = false }) {
|
|
126
|
+
const candidates = [];
|
|
127
|
+
// `<layer>:<id>` → count of notes that carry or would carry it. Counted per
|
|
128
|
+
// layer: a live note and its archive copy share an id by design.
|
|
129
|
+
const targets = new Map();
|
|
130
|
+
const unrepairable = [];
|
|
131
|
+
let scanned = 0;
|
|
132
|
+
let unrecognised = 0;
|
|
133
|
+
const zoneRoots = [...ZONES, ...ZONES.map((z) => `${ARCHIVE_ROOT}/${z}`)];
|
|
134
|
+
for (const zoneRel of zoneRoots) {
|
|
135
|
+
const layer = zoneRel.startsWith(`${ARCHIVE_ROOT}/`) ? 'archive' : 'live';
|
|
136
|
+
for (const { rel, dirNs, name } of listZoneFiles(vaultDir, zoneRel)) {
|
|
137
|
+
if (name.startsWith(ROLLUP_PREFIX)) continue;
|
|
138
|
+
let content;
|
|
139
|
+
try {
|
|
140
|
+
content = readFileSync(join(vaultDir, rel), 'utf8');
|
|
141
|
+
} catch {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const fm = parseFrontmatter(content);
|
|
145
|
+
if (!fm || fm['_generator'] !== GENERATOR_MARKER) continue;
|
|
146
|
+
const head = frontmatterHead(content);
|
|
147
|
+
if (head === null) continue;
|
|
148
|
+
scanned += 1;
|
|
149
|
+
const stem = name.slice(0, -'.md'.length);
|
|
150
|
+
// The generator's id base: a learning's filename IS its slug; a session
|
|
151
|
+
// file is named by the raw session id, which the renderer slugifies.
|
|
152
|
+
const base = isValidSlug(stem) ? stem : (slugifyIdSafe(stem) ?? stem);
|
|
153
|
+
const ns = dirNs || (typeof fm['source-repo'] === 'string' ? fm['source-repo'] : null);
|
|
154
|
+
const to = noteIdFor(ns, base);
|
|
155
|
+
const cur = typeof fm['id'] === 'string' ? fm['id'] : undefined;
|
|
156
|
+
const rawLine = head.match(ID_LINE);
|
|
157
|
+
if (!rawLine) {
|
|
158
|
+
unrepairable.push({ path: rel, reason: 'no-id-line' });
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const from = rawLine[1].trim();
|
|
162
|
+
const violates = idViolatesSchema(cur, from);
|
|
163
|
+
let reason = null;
|
|
164
|
+
if (violates) reason = 'schema';
|
|
165
|
+
else if (cur !== to) {
|
|
166
|
+
const bare = cur === base || cur === stem || noteIdMatches(cur, base, fm['source-repo']);
|
|
167
|
+
if (bare && includeBare) reason = 'namespace';
|
|
168
|
+
else if (!bare) unrecognised += 1;
|
|
169
|
+
}
|
|
170
|
+
const finalId = reason === null ? cur : to;
|
|
171
|
+
if (typeof finalId === 'string') targets.set(`${layer}:${finalId}`, (targets.get(`${layer}:${finalId}`) ?? 0) + 1);
|
|
172
|
+
if (reason !== null) candidates.push({ path: rel, from, to, reason, content, layer });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const changes = [];
|
|
176
|
+
const conflicts = [];
|
|
177
|
+
const contents = new Map();
|
|
178
|
+
for (const c of candidates) {
|
|
179
|
+
if ((targets.get(`${c.layer}:${c.to}`) ?? 0) > 1) {
|
|
180
|
+
conflicts.push({ path: c.path, to: c.to, reason: c.reason });
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
changes.push({ path: c.path, from: c.from, to: c.to, reason: c.reason });
|
|
184
|
+
contents.set(c.path, c.content);
|
|
185
|
+
}
|
|
186
|
+
return { changes, conflicts, unrepairable, scanned, unrecognised, contents };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* True when `next` differs from `content` in the frontmatter `id:` line only.
|
|
191
|
+
* A belt against a future change to `withNoteId`, not an independent check:
|
|
192
|
+
* it strips the line with the same `id:` pattern `withNoteId` rewrites, so it
|
|
193
|
+
* only catches a `withNoteId` that starts touching anything else
|
|
194
|
+
* (BV-002: data-loss prevention).
|
|
195
|
+
*/
|
|
196
|
+
function onlyIdLineChanged(content, next) {
|
|
197
|
+
const strip = (s) => {
|
|
198
|
+
const head = frontmatterHead(s);
|
|
199
|
+
return head === null ? s : head.replace(ID_LINE, 'id:') + s.slice(head.length);
|
|
200
|
+
};
|
|
201
|
+
return strip(content) === strip(next);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Apply a plan from {@link planIdMigration}.
|
|
206
|
+
*
|
|
207
|
+
* @param {ReturnType<typeof planIdMigration>} plan
|
|
208
|
+
* @param {{ vaultDir: string, maxChanges: number, fs?: object }} opts
|
|
209
|
+
* `fs` is passed to `atomicWriteWithBackup` (test seam).
|
|
210
|
+
* @returns {{ ok: boolean, refused?: 'over-limit', written: string[],
|
|
211
|
+
* skipped: Array<{path: string, reason: string}>, errors: Array<{path: string, error: string}> }}
|
|
212
|
+
*/
|
|
213
|
+
export function applyIdMigration(plan, { vaultDir, maxChanges, fs: injectedFs } = {}) {
|
|
214
|
+
const written = [];
|
|
215
|
+
const skipped = [];
|
|
216
|
+
const errors = [];
|
|
217
|
+
if (!Number.isInteger(maxChanges) || maxChanges < 0 || plan.changes.length > maxChanges) {
|
|
218
|
+
return { ok: false, refused: 'over-limit', written, skipped, errors };
|
|
219
|
+
}
|
|
220
|
+
for (const change of plan.changes) {
|
|
221
|
+
const abs = join(vaultDir, change.path);
|
|
222
|
+
let content;
|
|
223
|
+
try {
|
|
224
|
+
content = readFileSync(abs, 'utf8');
|
|
225
|
+
} catch (err) {
|
|
226
|
+
errors.push({ path: change.path, error: err?.code ?? 'read-failed' });
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (content !== plan.contents.get(change.path)) {
|
|
230
|
+
skipped.push({ path: change.path, reason: 'changed-since-plan' });
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const next = withNoteId(content, change.to);
|
|
234
|
+
if (next === content || !onlyIdLineChanged(content, next)) {
|
|
235
|
+
skipped.push({ path: change.path, reason: 'not-a-single-line-change' });
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const res = atomicWriteWithBackup(abs, next, { tmpPrefix: '.vault-mirror-migrate-ids.tmp', ...(injectedFs ? { fs: injectedFs } : {}) });
|
|
239
|
+
if (res.ok) written.push(change.path);
|
|
240
|
+
else errors.push({ path: change.path, error: res.code ?? 'write-failed' });
|
|
241
|
+
}
|
|
242
|
+
return { ok: errors.length === 0, written, skipped, errors };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const HELP = `Usage: node scripts/lib/vault-mirror/migrate-ids.mjs --vault-dir PATH [--include-bare]
|
|
246
|
+
[--apply --max-changes N]
|
|
247
|
+
|
|
248
|
+
Repairs the frontmatter id of vault-mirror notes (#1601/#1602). Dry run is the
|
|
249
|
+
default: it prints the planned changes and writes nothing. --apply needs
|
|
250
|
+
--max-changes N (take N from the dry run); a plan with more changes is refused.
|
|
251
|
+
|
|
252
|
+
--include-bare Also namespace ids that are valid but carry no repo prefix
|
|
253
|
+
`;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @param {string[]} argv
|
|
257
|
+
* @returns {{ vaultDir: string|null, apply: boolean, includeBare: boolean, maxChanges: number|null, help: boolean }}
|
|
258
|
+
*/
|
|
259
|
+
export function parseArgs(argv) {
|
|
260
|
+
const args = { vaultDir: null, apply: false, includeBare: false, maxChanges: null, help: false };
|
|
261
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
262
|
+
const a = argv[i];
|
|
263
|
+
if (a === '--help' || a === '-h') args.help = true;
|
|
264
|
+
else if (a === '--apply') args.apply = true;
|
|
265
|
+
else if (a === '--include-bare') args.includeBare = true;
|
|
266
|
+
else if (a === '--vault-dir') args.vaultDir = argv[++i] ?? null;
|
|
267
|
+
else if (a === '--max-changes') {
|
|
268
|
+
const raw = argv[++i];
|
|
269
|
+
const n = Number(raw);
|
|
270
|
+
if (!Number.isInteger(n) || n < 0) throw new Error(`invalid --max-changes "${raw}"`);
|
|
271
|
+
args.maxChanges = n;
|
|
272
|
+
} else throw new Error(`unknown argument "${a}"`);
|
|
273
|
+
}
|
|
274
|
+
return args;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (isMainModule(import.meta.url)) {
|
|
278
|
+
let args;
|
|
279
|
+
try {
|
|
280
|
+
args = parseArgs(process.argv.slice(2));
|
|
281
|
+
} catch (err) {
|
|
282
|
+
process.stderr.write(`migrate-ids: ${err.message}\n`);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
if (args.help) {
|
|
286
|
+
process.stdout.write(HELP);
|
|
287
|
+
process.exit(0);
|
|
288
|
+
}
|
|
289
|
+
if (!args.vaultDir) {
|
|
290
|
+
process.stderr.write('migrate-ids: --vault-dir is required\n');
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
if (args.apply && args.maxChanges === null) {
|
|
294
|
+
process.stderr.write('migrate-ids: --apply needs --max-changes N (run without --apply first to see N)\n');
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
const vaultDir = resolve(expandTilde(args.vaultDir));
|
|
298
|
+
const plan = planIdMigration({ vaultDir, includeBare: args.includeBare });
|
|
299
|
+
for (const c of plan.changes) process.stdout.write(`${JSON.stringify(c)}\n`);
|
|
300
|
+
const summary = {
|
|
301
|
+
mode: args.apply ? 'apply' : 'dry-run',
|
|
302
|
+
scanned: plan.scanned,
|
|
303
|
+
planned: plan.changes.length,
|
|
304
|
+
conflicts: plan.conflicts,
|
|
305
|
+
unrepairable: plan.unrepairable,
|
|
306
|
+
unrecognised: plan.unrecognised,
|
|
307
|
+
};
|
|
308
|
+
if (plan.unrepairable.length > 0) {
|
|
309
|
+
process.stderr.write(`migrate-ids: ${plan.unrepairable.length} note(s) have no id line and are not repaired; see "unrepairable"\n`);
|
|
310
|
+
}
|
|
311
|
+
if (!args.apply) {
|
|
312
|
+
process.stdout.write(`${JSON.stringify(summary)}\n`);
|
|
313
|
+
process.exit(0);
|
|
314
|
+
}
|
|
315
|
+
const res = applyIdMigration(plan, { vaultDir, maxChanges: args.maxChanges });
|
|
316
|
+
process.stdout.write(`${JSON.stringify({ ...summary, ...res, written: res.written.length })}\n`);
|
|
317
|
+
// A run that leaves schema violations behind (no id line, or a schema repair
|
|
318
|
+
// blocked by a conflict) is not a success, even if every planned write landed.
|
|
319
|
+
const schemaLeft = plan.unrepairable.length + plan.conflicts.filter((c) => c.reason === 'schema').length;
|
|
320
|
+
process.exit(res.ok && schemaLeft === 0 ? 0 : 2);
|
|
321
|
+
}
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
|
|
56
56
|
import { basename, resolve } from 'node:path';
|
|
57
57
|
|
|
58
|
-
import {
|
|
59
|
-
import { isOwnerLeakySegment } from '../../lib/validate/check-owner-leakage.mjs';
|
|
58
|
+
import { legacySubjectToSlug } from './utils.mjs';
|
|
59
|
+
import { inspectOwnerPatterns, isOwnerLeakySegment } from '../../lib/validate/check-owner-leakage.mjs';
|
|
60
60
|
import { loadPseudonymMap } from './pseudonym-map.mjs';
|
|
61
61
|
import { loadHostPaths, resolveHostPath } from '../config/host-paths.mjs';
|
|
62
62
|
import { isQueryFailure, resolvePreferredRemote } from '../vcs-repo-spec.mjs';
|
|
@@ -261,6 +261,22 @@ function lookupPseudonym(base, seg) {
|
|
|
261
261
|
return null;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
/**
|
|
265
|
+
* WARN line for a redaction. An unsafe private config dir (#1629, e.g. 0777 under
|
|
266
|
+
* umask 000) makes isOwnerLeakySegment() fail closed with CP1 for EVERY segment —
|
|
267
|
+
* the redaction stays, but calling that a leak hid the real cause.
|
|
268
|
+
*
|
|
269
|
+
* @param {string} where
|
|
270
|
+
* @param {string} pattern
|
|
271
|
+
* @returns {string}
|
|
272
|
+
*/
|
|
273
|
+
function redactionWarning(where, pattern) {
|
|
274
|
+
if (inspectOwnerPatterns().reason === 'unsafe-config-dir') {
|
|
275
|
+
return `WARN vault-mirror/namespace: owner policy unavailable (unsafe config dir); redacting ${where} to 'redacted-repo'\n`;
|
|
276
|
+
}
|
|
277
|
+
return `WARN vault-mirror/namespace: owner-privacy leak detected in ${where} (pattern: ${pattern}); redacting to 'redacted-repo'\n`;
|
|
278
|
+
}
|
|
279
|
+
|
|
264
280
|
/**
|
|
265
281
|
* Resolve the sanitised repository namespace segment for vault path scoping.
|
|
266
282
|
*
|
|
@@ -311,7 +327,8 @@ export function resolveRepoNamespace({ vaultName = null, repoRoot = null } = {})
|
|
|
311
327
|
: (declaredSlug ?? deriveRepo({ repoRoot: root }));
|
|
312
328
|
|
|
313
329
|
// Sanitise: collapse to last path segment, lowercase, strip non-[a-z0-9-].
|
|
314
|
-
|
|
330
|
+
// Repository storage identity stays historical; only new note names transliterate.
|
|
331
|
+
const seg = legacySubjectToSlug(base);
|
|
315
332
|
|
|
316
333
|
// Leak-guard: check both the raw base AND the sanitised segment.
|
|
317
334
|
// A personal home path or a private project slug must be caught before writing
|
|
@@ -331,9 +348,7 @@ export function resolveRepoNamespace({ vaultName = null, repoRoot = null } = {})
|
|
|
331
348
|
if (rawMatch !== null) {
|
|
332
349
|
const pseudonym = lookupPseudonym(base, seg);
|
|
333
350
|
if (pseudonym) return pseudonym;
|
|
334
|
-
process.stderr.write(
|
|
335
|
-
`WARN vault-mirror/namespace: owner-privacy leak detected in repo identifier (pattern: ${rawMatch}); redacting to 'redacted-repo'\n`,
|
|
336
|
-
);
|
|
351
|
+
process.stderr.write(redactionWarning('repo identifier', rawMatch));
|
|
337
352
|
return 'redacted-repo';
|
|
338
353
|
}
|
|
339
354
|
|
|
@@ -341,9 +356,7 @@ export function resolveRepoNamespace({ vaultName = null, repoRoot = null } = {})
|
|
|
341
356
|
if (segMatch !== null) {
|
|
342
357
|
const pseudonym = lookupPseudonym(base, seg);
|
|
343
358
|
if (pseudonym) return pseudonym;
|
|
344
|
-
process.stderr.write(
|
|
345
|
-
`WARN vault-mirror/namespace: owner-privacy leak detected in sanitised namespace segment (pattern: ${segMatch}); redacting to 'redacted-repo'\n`,
|
|
346
|
-
);
|
|
359
|
+
process.stderr.write(redactionWarning('sanitised namespace segment', segMatch));
|
|
347
360
|
return 'redacted-repo';
|
|
348
361
|
}
|
|
349
362
|
|