跳到正文
cccskills
免费在 GitHub 公开

vault-mirror

Use when you need to populate the Meta-Vault with machine-generated notes derived from session-orchestrator JSONL records. Converts entries from `.orchestrator/metrics/sessions.jsonl` and `.orchestrator/metrics/learnings.jsonl` into vault-conformant Markdown under `50-sessions/` and `40-learnings/`. Called automatically at session-end Phase 3.7 and after evolve Phase 3.5 — only when `vault-integration.enabled=true` and `vault-integration.mode != "off"`. Idempotent: re-runs safely; skips hand-authored notes. Triggers: "mirror to vault", "sync session notes to vault", "write learning notes to vault", "vault-mirror failed at session close". <example>Context: session-end is finalizing, vault-integration.mode is "warn". user: "/close" assistant: "Running vault-mirror to write 50-sessions/session-2026-05-17.md from the closing session record — 1 created, 0 skipped."</example>

中文简介正在准备中。当前显示原文说明。

Kanevry/session-orchestrator星标 52(GitHub 52 + cccskills 0)

实测评分 未实测

查看安装方法

包含的文件(1)

  • SKILL.md32.0 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Vault Mirror Skill

Project-instruction file resolution: CLAUDE.md and AGENTS.md (Codex CLI) are transparent aliases — see skills/_shared/instruction-file-resolution.md. When this skill mentions Session Config in CLAUDE.md, the alias rule applies.

Purpose

vault-mirror populates the Meta-Vault with machine-generated notes derived from structured JSONL records. It converts entries from .orchestrator/metrics/sessions.jsonl and .orchestrator/metrics/learnings.jsonl into vault-conformant Markdown files under numeric-prefix subdirectories. This is distinct from vault-sync, which validates the vault — vault-sync validates the vault; vault-mirror populates it. The two skills are complementary: vault-mirror writes notes, vault-sync checks that the vault as a whole remains conformant.

When Invoked

vault-mirror is called in two places:

  • Phase 3.7 of session-end (skills/session-end/session-metrics-write.md) — mirrors the sessions.jsonl entry for the closing session.
  • Phase 3.5 of evolve (skills/evolve/SKILL.md) — mirrors all learnings.jsonl entries added during the evolve cycle.

Both call sites are conditional: vault-mirror runs only when vault-integration.enabled == true AND vault-integration.mode != "off" in the project's Session Config. When either condition is not met, the call site skips silently and vault-mirror is never invoked.

Inputs

scripts/vault-mirror.mjs is the implementation (call it by its plugin path, "$PLUGIN_ROOT/scripts/vault-mirror.mjs" — never cd into the plugin checkout to run it). --vault-dir, --source and --kind are required; node "$PLUGIN_ROOT/scripts/vault-mirror.mjs" --help lists every flag.

FlagTypeRequiredDescription
--vault-dirpathyesAbsolute path to the Meta-Vault root directory. Must exist.
--sourcepathyesPath to the JSONL file to read (one JSON object per line). Must exist. Pass it absolute — a relative path resolves against the cwd.
--kindsession or learningyesDetermines which generator and target path are used.
--repo-rootpathnoThe repo whose ledger --source is (#1503). Its .vault.yaml slug / git remote decides the vault namespace. Default: the project holding --source (<root>/.orchestrator/metrics/<file> → <root>, so a monorepo package with its own .orchestrator/ keeps its own namespace), else the git repo containing it, else the cwd (only for a loose source outside any git repo, with a stderr WARN). Callers pass --repo-root "$PWD" from the repo root.
--dry-runflagnoParse and resolve paths but do not write any files. Emits action lines as normal.

Empty lines in the JSONL source are silently skipped.

Outputs

One JSON line is written to stdout for each non-empty JSONL entry processed. Exit code reflects the run outcome. Diagnostics (SKIP …, WARN …, refusals) go to stderr and are NOT JSON — a caller that captures 2>&1 must parse line by line (jq -R -r 'fromjson? | objects | …'), or one SKIP line ends the parse and drops every action after it.

Action values

actionMeaning
createdEntry did not exist in the vault; file created.
updatedEntry existed (generator marker present, same id) with an older updated date; file overwritten.
skipped-noopEntry existed, same id, updated date not advanced; file unchanged.
skipped-handwrittenA file at the target path has no _generator marker (or an unknown generator); left untouched.
skipped-collision-resolvedA file at the target path has the generator marker but a different id; a disambiguated slug was used instead.
skipped-foreign-ownerA generator note at the target path was rendered from another record (#1503), and this write must not replace it. Sessions: its source-record differs from this write and occurs nowhere in this --source ledger (reason: "source-record mismatch"), or its id names a different session (reason: "id mismatch"). Learnings: only for the disambiguated <slug>-<uuid8>.md file — a mismatch on the main <slug>.md is routed to disambiguation instead (skipped-collision-resolved, see Idempotency rule 6). One SKIP foreign owner line goes to stderr; --force does not bypass it. A leak in the foreign note is masked in place — owner, content and source-record kept — and the line carries healed_leak: true. A note without source-record (written before #1503) stays writable.
skipped-invalidEntry is missing one or more required fields; entry skipped, processing continues.
skipped-quality-lowLearning confidence below vault-mirror.quality.min-confidence (CLI: --quality-min-confidence, default 0.5), or a metrics-only session whose note ALREADY exists (left untouched; the pruner decides). Carries reason and path: null. Runs before --force; --force does not bypass it.
skipped-metrics-onlySession record below the narrative gate (#1513, see Retention): no note; one row was added to the month rollup 50-sessions/<repo>/_rollup-YYYY-MM.md (path). An unchanged rollup is skipped-noop.
skipped-expiredLearning past its expires_at with no note yet (#1513): nothing is created.
skipped-duplicate-insightA mirror-owned, non-archived note in the same namespace (or the flat layout) already carries the same normalised insight (#1513); path names it. Only consulted where a NEW file would be created.

Output line shape

{"action":"created","path":"50-sessions/session-orchestrator/session-2026-04-13.md","kind":"session","id":"session-2026-04-13"}

path is relative to --vault-dir.

Exit codes

CodeMeaning
0Success (including idempotent no-ops and per-entry skips).
1Malformed JSON on a JSONL line — fatal, processing stops. Also returned when required CLI args are missing.
2Filesystem error or pre-loop refusal: --vault-dir not found, vault not canonical, --source not found, --source in a different repo than --repo-root (source-repo-mismatch, #1503), or an unexpected write error.

Target Paths

KindTarget
session<vault-dir>/50-sessions/<repo>/<session-id>.md
learning<vault-dir>/40-learnings/<repo>/<slug>.md

Subdirectories are created automatically with mkdirSync({ recursive: true }) when writing (not in --dry-run mode).

The numeric prefix (50-sessions/, 40-learnings/) follows the vault folder ordering convention so that sessions and learnings appear in the correct position in the vault tree relative to other note types.

Per-project namespacing (#660). New writes are namespaced under a per-repo subdirectory <repo>/, so a single shared vault can hold notes from multiple projects without cross-repo slug/id collisions. <repo> is resolved by resolveRepoNamespace() (scripts/lib/vault-mirror/namespace.mjs): the optional vault-integration.vault-name Session Config key (CLI: --vault-name) when set, else the repo root's .vault.yaml slug, else its preferred git remote via deriveRepo(), sanitised to a single kebab segment. The repo root is --repo-root, else the project holding --source (<root>/.orchestrator/metrics/), else the git repo containing it, else the cwd — never the cwd when the source belongs to a project (#1503: running from another repo's checkout once wrote one project's ledger over another project's notes). Owner-privacy leaks (personal home path / private project slug / personal name) are redacted to redacted-repo before any write. The legacy flat layout (40-learnings/<slug>.md) is still read — the writer dual-probes the flat path so a pre-existing flat note is never duplicated; a one-time relocation of the historical flat corpus is tracked as a follow-up.

Path contract note: Issue #187 shipped the flat numeric-prefix layout (deferring the per-<repo> subfolder it sketched). Issue #660 adds that per-repo subfolder for write-isolation (above) while keeping the numeric-prefix ordering. If you need a different layout, file a new issue — do NOT silently change the script.

Learning-coverage self-healing after a store recovery (#740). vault-mirror always mirrors the CURRENT learnings.jsonl store contents at run time — if an earlier session's store was truncated or corrupted and later recovered (e.g. from a .bak), the resulting transient coverage gap in the vault is closed automatically by the next mirror run, with no manual backfill required. Note also that vault-mirror.quality.min-narrative-chars (see Retention below) gates sessions only — processLearning never applies a narrative-length filter, so a short learning insight is never skipped for length; only the confidence gate (vault-integration.quality.min-confidence) applies to learnings.

Host-local pseudonym mapping (Epic #725 D5). Without a map, EVERY owner-leaky repo namespace collapses to a single redacted-repo/ subdir — which breaks the #660 write-isolation (N private repos share one directory → id/slug collisions) and destroys cross-repo attribution. A host-local pseudonym map fixes this: each real repo slug is assigned a stable, non-leaky pseudonym, so the vault gets 40-learnings/<pseudonym>/ (and matching source-repo frontmatter, via W2's attribution threading) instead of everyone landing in redacted-repo/. The map is a JSON object { "real-slug": "pseudonym-slug", … } at a host-local path referenced by owner.yaml → paths.namespace-map-path (env SO_NAMESPACE_MAP overrides; precedence env > owner.yaml > unset). resolveRepoNamespace() consults the map ONLY at the redaction site (i.e. only when a repo would otherwise be redacted), looking up both the sanitised segment and the raw identifier; a hit returns the pseudonym and skips redaction, so clean/public repos never touch the map (or its lazy owner.yaml read). Privacy guarantee: the map file lives OUTSIDE every repo (alongside owner.yaml under ~/.config/session-orchestrator/) and is never committed — only the pseudonyms reach the vault, the real names stay host-local. Each pseudonym is validated (must be a kebab slug AND must not itself be owner-leaky); invalid or leaky pseudonyms are dropped with a WARN. Fallback: an absent, unreadable, or malformed map — and any UNMAPPED owner-leaky repo — still redacts to redacted-repo, identical to pre-#725 behaviour. Implementation: scripts/lib/vault-mirror/pseudonym-map.mjs (loadPseudonymMap) + namespace.mjs.

Retention (#1513)

The mirror used to write every record and never mark or remove anything; by 2026-10-03 the two zones were 78 % of the Meta-Vault, 91 % of the session notes carried nothing beyond git, and 2.826 expired learnings still read status: draft. The rules live in ONE module, scripts/lib/vault-mirror/retention.mjs, shared by the mirror and the pruner:

  1. Narrative gate (sessions). A record gets its own note only when its free-text fields notes, narrative, summary (strings or string arrays, whitespace collapsed) reach vault-mirror.quality.min-narrative-chars (default 400; 0 disables). Wave/agent/file tables are not narrative. All three renderer generations print exactly these fields into ## Notes (sessionNarrativeText), which is what the pruner measures; an existing note with less free text than its record is rewritten once (heal), and when a ledger holds the same session_id twice the winning record inherits the narrative fields it lacks from the line it supersedes. Below the gate the record becomes one row (date, session id, type, waves, agents, files) in 50-sessions/<repo>/_rollup-YYYY-MM.md, keyed by session id, sorted and rendered deterministically — re-runs are no-ops. Calibration (Jev audit, 3.003 notes): free text under 300 chars held 1 of 2.802 notes scored >= 2.5 "beyond git"; from 800 chars on, 18 of 55.
  2. Legacy slug (learnings). The pre-#725 slug (subject without the whitespace → hyphen pre-map) is recognised: when only that variant of THIS record's note exists, it is updated in place instead of a canonical twin being created. New notes always get the canonical slug.
  3. Write-time dedupe (learnings). Before a new file is created, the normalised ## Insight (case, whitespace, typographic quotes/dashes, trailing punctuation ignored) is looked up among the mirror-owned, non-archived notes of the namespace, and among flat notes whose source-repo is this namespace → skipped-duplicate-insight. Placeholder insights ((legacy record …, (none recorded), …) and keys under 40 characters never count as the same learning.
  4. Namespace alias. Folders equal modulo hyphens are one namespace; the one with more hyphens is canonical (gotzendorferv2 → gotzendorfer-v2). The mirror resolves this before every write and for its auto-commit scope; the pruner uses the same function (canonicalAmong).
  5. Expiry and leaving the store (learnings). An expired record (expires_at before today) is never created; its existing note is re-rendered with status: archived + archived-reason: expired. After the live pass, --kind learning also reads the sibling learnings-archive.jsonl and marks each archived record's existing note archived with archived-reason: expired (_archive_reason: expired) or superseded (every other reason) — never creating one, never touching a note a live record still owns. archived + archived-reason instead of status: expired|superseded on purpose: the vault frontmatter status enum (SSOT projects-baseline) has no such values, and a status outside it fails vault-sync in any vault that validates the mirror zones.

Bestand: scripts/vault-mirror-prune.mjs. Applies the same rules to notes written before them. Dry run is the default and writes nothing; --manifest PATH writes the JSON manifest (path, reason, action, target, keep, rollup). Expiry is decided first, so an expired or archived copy is never the one kept. Reasons: duplicate (canonical copy among the LIVE copies: own namespace folder > any namespace folder > status: verified > later expires > source-record > post-#725 slug > newer updated), superseded (same slug modulo hyphens, different insight; newest kept), expired, metrics-only-session (its rollup row is written first), namespace-alias (a folder that equals another modulo hyphens and has fewer of them, or --alias FROM=TO), flat-relocate (flat note whose source-repo names its namespace). --apply writes the rollups, then moves archived notes to 90-archive/mirror/<zone>/<same path> with status: archived + archived-reason, then renames the relocated ones — never deletes, never touches a note without the _generator marker, re-checks ownership right before each move, refuses any source or target that resolves outside the vault (realpath of the nearest existing ancestor), writes a note whose archive target is occupied by OTHER content beside it as <name>.dup-<hash8>.md (listed under applied.suffixed in the manifest), and is idempotent (a second run plans nothing). 90-archive/ is outside vault-sync's checked zones but inside its link-target register, so wiki-links into archived notes keep resolving; the mirror's source-session index walks 90-archive/mirror/50-sessions/ for the same reason, and backfill-learnings-from-vault.mjs falls back to 90-archive/mirror/40-learnings/.

Order (runbook). (1) Update the plugin on EVERY host that runs session-end against this vault (M4 and M5): an old mirror re-creates every note the pruner moved. (2) Run one mirror pass per repo ledger so session notes written before the narrative fields were rendered are healed — otherwise the pruner reads them as metrics-only. (3) Dry run, read the manifest. (4) --apply, then commit the vault.

# (2) per repo, from its canonical checkout
node scripts/vault-mirror.mjs --vault-dir <vault> --source <repo>/.orchestrator/metrics/sessions.jsonl --kind session --repo-root <repo> --no-commit
# (3) + (4)
node scripts/vault-mirror-prune.mjs --vault-dir <vault> --manifest /tmp/vault-prune-manifest.json   # plan
node scripts/vault-mirror-prune.mjs --vault-dir <vault> --apply --manifest /tmp/vault-prune-applied.json

Idempotency

vault-mirror is safe to run multiple times against the same JSONL source:

  1. No file exists → create.
  2. File exists, _generator marker present, same id, updated date not advanced → skipped-noop, file unchanged.
  3. File exists, _generator marker present, same id, updated date advanced → updated, file overwritten.
  4. File exists, no _generator marker → skipped-handwritten, file untouched. The absence of the marker means the file was written by a human and must not be overwritten automatically.
  5. File exists, _generator marker present, different id → slug collision. A disambiguated slug is derived by appending -<first-8-chars-of-entry-uuid> (hyphens stripped from the UUID before taking the prefix). The original file is left unchanged; the new note is written at the disambiguated path with action skipped-collision-resolved. Sessions have no disambiguation: a different-id session note is skipped-foreign-owner.
  6. File exists, _generator marker present, different record (#1503). Every note records the record it was rendered from in source-record (learnings: the record id; sessions: raw_session_id, else started_at). Checked before rules 2–3, because an id match proves nothing — subjects and semantic session ids recur across repos, and an expired learning re-learned in the same repo gets a new id. Learnings: handled as the slug collision of rule 5 — the new record lands at <slug>-<uuid8>.md (skipped-collision-resolved with reason: "source-record mismatch"), the existing note is not overwritten. Sessions: skipped-foreign-owner, unless the note's key occurs on any line of this --source ledger (superseded duplicates included — a backfill stub replaced by its real record stays this repo's note). source-repo is deliberately not an owner signal: a note at the target path already sits in the writer's namespace folder, so a different source-repo only means the note was moved or the repo relabelled. A leak in a foreign note is masked in place, never rewritten from this record: the note keeps its owner, content and source-record, only the values the current masker knows become [REDACTED], and this record is routed exactly as for any foreign note (the action carries healed_leak: true). If the masked frontmatter would no longer parse as YAML (a value that starts with the secret), the note is left unchanged and one stderr WARN names it. Rewriting it from this record would be the #1503 overwrite, triggered by a secret. Limitation: this cannot protect a note written before source-record existed; the repo-root resolution and the source-repo-mismatch refusal are what prevent the incident class.

The generator marker value is session-orchestrator-vault-mirror@1 and appears in the YAML frontmatter as _generator: session-orchestrator-vault-mirror@1.

Auto-Commit Phase

After a successful mirror pass, scripts/lib/vault-mirror/auto-commit.mjs (autoCommitVaultMirror(vaultDirPath, sessionId, repo)) optionally commits the freshly-written mirror artifacts in 40-learnings/ and 50-sessions/ as a single chore(vault): mirror … commit. It runs unattended at session-end Phase 3.7 / evolve Phase 3.5. The phase is fail-safe: it never throws, emits one JSON action line on stdout, and aborts (unstaging everything) if any staged path is not a generator-stamped mirror artifact. When the optional repo namespace is passed (#660), staging is scoped to 40-learnings/<repo> + 50-sessions/<repo> and the phase additionally aborts with reason cross-repo-staged-changes if any staged file belongs to a different repo's namespace — so one project's mirror run can never commit another project's notes. Omitting repo preserves the legacy whole-directory behaviour.

Pre-commit hook bypass (--no-verify)

The auto-commit deliberately commits with --no-verify, skipping the vault repo's pre-commit hooks. This bypass is intentional (issue #603), not gratuitous, for two reasons:

  1. Redundant validation. Before committing, the phase runs isMirrorArtifact() on every staged path — a per-file frontmatter check for the _generator: session-orchestrator-vault-mirror@1 marker. Any staged file lacking the marker triggers a full unstage and an auto-commit-skipped no-op. The committed files were written by vault-mirror's own generator, which already enforces conformant frontmatter (and the quality gate). The vault's pre-commit frontmatter / wiki-link validator would only re-check what is already guaranteed.
  2. Must not block an unattended close. This is a machine auto-commit during session-end. An interactive, slow, or failing vault-side hook would stall the close. Bypassing it keeps session-end non-blocking.

Per .claude/rules/development.md Git Safety Protocol — "Never skip hooks (--no-verify) unless the user has explicitly asked for it" — the bypass is permissible here because it is explicit, documented, and committing already-validated content. A regression test in tests/lib/vault-mirror/auto-commit.test.mjs pins --no-verify into the commit git-args so a future silent removal is caught. If the vault's pre-commit hooks ever need to run on these commits, remove --no-verify together with this note and the test.

Failure Modes

ConditionBehaviour
Entry missing a required fieldskipped-invalid emitted on stdout, error written to stderr, exit 0 (processing continues for remaining entries).
Malformed JSON on a JSONL lineError written to stderr, exit 1 (processing stops).
--vault-dir not foundError written to stderr, exit 2.
--source file not foundError written to stderr, exit 2.
--source lies in a different repo than --repo-rootRefusal written to stderr (both namespaces named), exit 2, nothing written; run event aborted: "source-repo-mismatch".
Session note rendered from a record outside this ledgerskipped-foreign-owner emitted on stdout with a reason, SKIP foreign owner line on stderr, file left unchanged.
--source outside any git repoOne WARN line on stderr naming the namespace used (it comes from the cwd or an unverifiable --repo-root); the run proceeds.
Hand-written file at target pathskipped-handwritten emitted on stdout, note written to stderr, file left unchanged.
Unknown _generator value in existing fileTreated as hand-written: skipped-handwritten, file left unchanged.
Unexpected filesystem write errorError written to stderr, exit 2.

Examples

Mirror the sessions.jsonl for the current project into a vault — run from the project's repo root, call the script by its plugin path:

node "$PLUGIN_ROOT/scripts/vault-mirror.mjs" \
  --vault-dir ~/Projects/vault \
  --repo-root "$PWD" \
  --source "$PWD/.orchestrator/metrics/sessions.jsonl" \
  --kind session

Dry-run a learnings mirror to preview actions without writing:

node "$PLUGIN_ROOT/scripts/vault-mirror.mjs" \
  --vault-dir ~/Projects/vault \
  --repo-root "$PWD" \
  --source "$PWD/.orchestrator/metrics/learnings.jsonl" \
  --kind learning \
  --dry-run

Never cd into the plugin checkout to run it: the namespace comes from the repo the ledger belongs to, and an explicit --repo-root that disagrees with --source is refused with exit 2 (source-repo-mismatch).

Live State

Phase 1 shipped and actively running. As of 2026-05-09, the skill has produced:

  • 847 learning notes under vault://40-learnings/ — each carries _generator: session-orchestrator-vault-mirror@1 in the YAML frontmatter.
  • 466 session notes under vault://50-sessions/ — same generator stamp.

Both target directories were verified by direct ls | wc -l measurement against ~/Projects/vault/. The numeric-prefix layout (40-learnings/, 50-sessions/) confirmed correct per the Target Paths spec above.

Phase-2: Flat-Corpus Relocation (#700)

scripts/relocate-vault-corpus.mjs is a one-time operator utility that migrates the legacy flat vault corpus (40-learnings/*.md + 50-sessions/*.md at depth 1) into the per-repo namespace subdirectories introduced by #660. Running it is never required for vault-mirror to function — the mirror writes to <repo>/ subfolders since #660 automatically. Relocation is a clean-up step for vaults that accumulated notes before per-repo namespacing existed.

Classifier logic

The classifier (scripts/lib/vault-relocation-rules.mjs) reads each file's YAML frontmatter:

  • Session notes (type: session): derives repo from repo: frontmatter → project/<slug> tag → (optional, --with-backfill only) backfill index → _unsorted fallback.
  • Learning notes (type: learning): derives repo from project: wikilink → source: free-text field → source_session: wikilink (transitive: looks up the session's own namespace) → _unsorted fallback.

Every derived value routes through resolveRepoNamespace() (the same CP1/CP6/CP10 leak-guard used by vault-mirror) so private slugs redact to redacted-repo and no personal home path leaks into a namespace.

repo: backfill (--with-backfill, #700)

Most legacy session notes (~466 of 653 at last census) carry no repo: field, so they — and every learning that transitively points at them via source_session: — fall to _unsorted. --with-backfill infers the owning repo from an authoritative signal rather than a guess: the note's id: is joined against each sibling repo's own .orchestrator/metrics/sessions.jsonl session_id.

  • HIGH — id found in exactly one repo's sessions.jsonl → that repo. (An id present in >1 repo is ambiguous → SKIP, never a guess.)
  • MEDIUM — id not found, but the branch+date parsed from the id matches exactly one repo → that repo. (Colliding branch+date pairs → SKIP.)
  • SKIP — no unambiguous signal → stays _unsorted (non-destructive).

The inference lives in the pure module scripts/lib/vault-repo-backfill.mjs (inferRepoForSession / buildBackfillIndex / isBackfillDerivable); the CLI builds the cross-repo sessions.jsonl index and threads it into both buildSessionRepoIndex (so backfilled sessions lift their transitive learnings) and the classify loop. Every inferred slug still passes through resolveRepoNamespace() — a mis-inferred private slug redacts to redacted-repo and is excluded from any confident move. Backfilled sessions feed --derivable-only exactly like repo:-carrying ones.

Non-derivable files (_unsorted, redacted-repo, unknown-repo) are moved to a _unsorted/ subfolder (or skipped entirely when --derivable-only is set).

Modes

ModeFlagWhat happens
Preview (default)--dry-runRead-only scan — reports would-move lines, writes nothing. Always run this first.
Apply--applyExecutes git mv (stages only — no commit). Operator reviews diff and commits separately.
Confident-only apply--apply --derivable-onlyLike --apply but skips files with confident===false (_unsorted, redacted-repo, unknown-repo destinations).
Backfill (opt-in)--with-backfillInfer repo: for repo-less session notes via the authoritative id: → sibling-repo sessions.jsonl join (HIGH/MEDIUM/SKIP). Composes with any mode; without it, output is byte-identical to before.
Repos root--repos-root <dir>Parent dir holding the sibling repos whose sessions.jsonl the backfill scans (default: parent of --vault-dir; Archiv/dot-dirs excluded).
Rollback--rollback <manifest>Reverses a prior --apply run using the reverse-manifest written at <vault-dir>/.orchestrator/relocation-manifest-<ISO>.json.

Scope filters: --learnings-only / --sessions-only restrict to one corpus root.

Safety model

  1. Dry-run is the default. --apply must be explicit; there is no accidental write path.
  2. --vault-dir is required. No default prevents silently operating on a wrong directory.
  3. Stage-only. --apply calls git mv (which stages) but never commits. The operator owns the commit.
  4. Reverse-manifest. Every --apply run writes <vault-dir>/.orchestrator/relocation-manifest-<ISO>.json. Pass this path to --rollback to undo.
  5. Structural idempotency. Files already at depth ≥ 2 (already namespaced) are never enumerated or moved.
  6. Destination-collision guard. If the target path already exists, the file is skipped with reason: dest-exists — no data is ever overwritten.
  7. Leak-guard guarantee. Every derived namespace value runs through resolveRepoNamespace() before use. Private slugs redact to redacted-repo; files that would land in redacted-repo/ are excluded by --derivable-only or left flat if the operator chooses.

Usage

# Step 1: Preview (dry-run default)
node scripts/relocate-vault-corpus.mjs --vault-dir ~/Projects/vault

# Step 2: Move only confident files (skip _unsorted/redacted-repo)
node scripts/relocate-vault-corpus.mjs --vault-dir ~/Projects/vault --apply --derivable-only

# Step 2b: Lift coverage further by backfilling repo: for repo-less sessions (preview first)
node scripts/relocate-vault-corpus.mjs --vault-dir ~/Projects/vault --with-backfill --derivable-only          # dry-run
node scripts/relocate-vault-corpus.mjs --vault-dir ~/Projects/vault --with-backfill --derivable-only --apply  # then apply

# Step 3 (optional): Roll back if something looks wrong
node scripts/relocate-vault-corpus.mjs --rollback ~/Projects/vault/.orchestrator/relocation-manifest-<ISO>.json

Exit codes: 0 success (including dry-run); 1 input/arg error; 2 IO error. Data goes to stdout; diagnostics and summary go to stderr. Add --json for JSONL output (one record per file).

N-root canonical guard (named vaults)

scripts/vault-mirror.mjs enforces that the --vault-dir target is a known vault (guard: git remote ends with a canonical vault suffix). Phase-2 generalises this from a single /agents/vault suffix to N named suffixes via scripts/lib/named-vault-resolver.mjs. When vaults: is declared in owner.yaml (see owner-persona.md), the guard uses .some() across all configured suffixes. When vaults: is absent, behaviour is byte-identical to the pre-#700 single-suffix path.

Configuration

vault-mirror respects the vault-integration block in the project's Session Config (CLAUDE.md, or AGENTS.md on Codex CLI). The script itself does not read Session Config — the calling skill (session-end, evolve) is responsible for reading the config and deciding whether to invoke vault-mirror at all.

FieldTypeDefaultMeaning
vault-integration.enabledbooleanfalseWhen false, the calling skill skips vault-mirror entirely.
vault-integration.vault-dirstring—Absolute or ~-prefixed path to the Meta-Vault root. Passed as --vault-dir.
vault-integration.modestrict, warn, or offwarnWhen off, the calling skill skips vault-mirror. When warn, mirror failures are surfaced as warnings but do not block session close. When strict, mirror failures block session close.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Use when the user asks to improve architecture, find refactoring opportunities, surface deepening opportunities, consolidate tightly-coupled modules, or make a codebase more testable and AI-navigable. Surfaces shallow modules and hypothetical seams using a precise vocabulary (Module / Interface / Implementation / Depth / Seam / Adapter / Leverage / Locality from LANGUAGE.md).

中文简介正在准备中。当前显示原文说明。

Kanevry/session-orchestrator522026年10月26日 更新

autopilot

免费

Use this skill when running an autonomous session-orchestration loop. Chains session-start → session-plan → wave-executor → session-end for N iterations with all 10 kill-switches (SPIRAL, FAILED wave, carryover > 50%, max-hours, max-sessions, resource-overload, token-budget, stall-timeout, sub-threshold confidence, user-abort). Reads Mode-Selector output (Phase B) to decide auto-execute vs. fallback. Writes one autopilot.jsonl record per loop run. Phase C scaffold (issue #277); implementation lives in scripts/lib/autopilot.mjs (Phase C-1 follow-up).

中文简介正在准备中。当前显示原文说明。

Kanevry/session-orchestrator522026年10月26日 更新

bootstrap

免费

Use this skill when scaffolding the minimum repository structure required by session-orchestrator. Invoked automatically by the Bootstrap Gate when CLAUDE.md, Session Config, or bootstrap.lock is missing. Also available as /bootstrap for manual invocation. Three intensity tiers: fast (demos/spikes), standard (MVPs), deep (production/team).

中文简介正在准备中。当前显示原文说明。

Kanevry/session-orchestrator522026年10月26日 更新

Use when you have a feature idea but the scope or UX is still ambiguous — runs a lightweight Socratic design dialogue (3-5 AUQ rounds) and writes a spec markdown file. Use BEFORE /plan feature when product intent needs validation; skip to /plan feature when scope is already clear. HARD-GATE prevents any code work until the design is user-approved.

中文简介正在准备中。当前显示原文说明。

Kanevry/session-orchestrator522026年10月26日 更新

Use when detecting drift between CLAUDE.md (or AGENTS.md, the Codex CLI alias) / _meta narrative and live repository state. Ten checks: absolute-path resolution, 01-projects/ count claims, issue-reference freshness, session-file existence, command-count sync, session-config-parity (mandatory template keys = error, opt-in gaps = note, unknown local keys = warning), vault-dir-parity (CLAUDE.md vs AGENTS.md), generated-rule-staleness (WARN-only), rule-scoping (paths:/globs: frontmatter defects, dangling rule citations, zero-match globs), and docs-parity (docs/components.md count-claims vs on-disk counts, template-vs-reference config-key parity, stale .claude/metrics/ paths). Full per-check spec in the body table. Invoked as an opt-in session-end phase; mirrors vault-sync's lean JSON+exit-code contract.

中文简介正在准备中。当前显示原文说明。

Kanevry/session-orchestrator522026年10月26日 更新

close

免费

End session with verification, commits, and documentation

中文简介正在准备中。当前显示原文说明。

Kanevry/session-orchestrator522026年10月26日 更新

查看 Kanevry 的全部技能

报告此技能的问题