Maintain the registry as a whole: sweep every bundle for structural and quality decay, rank what needs work by measured attention points, and dispatch scoped /deepen or /forge workers at it. Also evaluates the lane skills from their per-run log (`/librarian skills`). Keeps coverage memory in an Obsidian vault under librarian/ so each run knows what the last one touched, what is saturated, and what is owed. Run manually; a scheduler is a later wrapper. Use when nobody has looked at the registr...
Installs into .claude/skills of the current project.
Are you the author of Librarian?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/xkazm04-librarian)
---
name: librarian
description: "Maintain the registry as a whole: sweep every bundle for structural and quality decay, rank what needs work by measured attention points, and dispatch scoped /deepen or /forge workers at it. Also evaluates the lane skills from their per-run log (`/librarian skills`). Keeps coverage memory in an Obsidian vault under librarian/ so each run knows what the last one touched, what is saturated, and what is owed. Run manually; a scheduler is a later wrapper. Use when nobody has looked at the registry in a while."
category: ai-native
memory: project
version: 1.6.2
tags: registry, maintenance, coverage, dispatch, quality, upstream
---
# Librarian
The registry has three content engines and they are good. `/forge`
creates a bundle from a repository's ceiling. `deepen` raises one subject above any
repository, with research lanes and a saturation ledger. `research` runs inward: it
mines a source somebody handed us for what it changes here, and banks what it cannot
prove as a dated lead.
**This skill is none of them.** It is the layer that decides *what to run, where, and
whether it was worth running* - a warden over structure and a dispatcher over the
engines. It keeps the coverage memory that lets run N+1 know what run N did.
Say that out loud in every session, because the failure mode for a skill like this is
re-implementing deepen's research lanes badly under a new name. **If the answer is
"research this subject", the answer is `/deepen`, dispatched from here.** If it is
"mine this link", the answer is `/research`, and a human has to bring the link.
## Invocation
```
/librarian # sweep + report, writes no content
/librarian run [domain] # the full loop, dispatches workers
/librarian structure # the mechanical pass only - cheap enough for every merge
/librarian status # read the vault, touch nothing
/librarian upstream # the upstream lane alone: which mined trees have moved
/librarian reflect # update the standard + this skill from what the last runs taught
/librarian skills [name] # evaluate the lane skills from their run log - see "The skills pass"
```
## Never count anything yourself
`node scripts/librarian-scan.mjs --json` is the instrument. It emits, per subject:
technique and application counts, stack diversity, missing `use_when`, body mass,
oldest `verified_on`, expired and at-risk applications, version witness, consumer
demand where it exists, last-swept date, dry streak, and an attention score with its
reasons. `node scripts/check-currency.mjs --json` adds the decay detail.
This registry once reported a content gap at 0/267 over a corpus that was at 267/267,
because a counter read a different shape than the parser emitted. **Verify the
instrument before reporting a gap**: spot-check one number against one real file every
run. A model that counts its own corpus produces a confident number nobody can check.
## The loop
**1. Prove the instrument.** `check-bundles`, then `build-index`, then `build-catalog`
(that order - the catalog hash covers the index), then `build-knowledge-rules` (the
always-on consumer rule; CI gates it under "bundle index freshness" and it has been found
stale on trunk three times because this list used to omit it). Then the scan, and
`node scripts/build-registry-map.mjs --check` for the **consumer side**: how many recorded
verdicts in the fleet were judged against a subject that has since changed, by subject.
Only `--check` (and `--dry-run`) are read-only: `--json` is an OUTPUT format, not a
report mode, and it rebuilds and writes every project's `.ai/registry-map.json`
(2026-09-23, twelve trees dirtied mid-sweep). Confirm one figure by opening one file. If a gate is red, stop: you are about to rank a
corpus that does not parse.
Then the **upstream side**: `node scripts/upstream-check.mjs --self-test`, and only if it
passes, `node scripts/upstream-check.mjs --json`. The self-test asserts the compare call
against repositories known to have moved; without it, an all-quiet result is
indistinguishable from a dead API, and this repo has already paid for that lesson once.
It costs about 130 authenticated API calls against a limit of 5000/hr.
**2. Sweep and score.** Read the scan fresh. **Never carry forward last run's derived
numbers** - deepen learned this the expensive way, and the vault stores what was DONE,
never what was computed.
**3. Structure pass.** Cap breaches, taxonomy drift, orphan techniques, dead links,
file health. All mechanical, all fixable in-session, none needing a worker. A cap
breach is fixed by editing `taxonomy.json` and running
`node scripts/apply-taxonomy.mjs <bundle> --to nested --apply` - never by moving a
folder, because relative links encode depth and the mover is what rewrites them.
**4. Rank.** The scan orders by attention points. Then apply the three judgments a
script cannot:
- **Demand outranks structure.** A consumer deviation or a citation reported `gone`
beats any structural gap. But when `demandKnown` is false, demand is UNKNOWN, not
zero - say so in the report rather than ranking as though nobody needs anything.
- **Suppress the saturated.** A subject whose scan `dryStreak >= 2`, with no expired
clock and no event to point at, does not get re-run. That is deepen's law and it is
what stops the loop burning tokens on settled ground. The scan COMPUTES the streak
from the `idled` rows in `librarian/runs/*/result.json` (step 7b) - it no longer reads
a note's `dry_streak`, which had one writer that only ever wrote 0 (349 of 349 notes,
2026-09-23). `dryStreak: null` means no run has recorded a pass: the brake is UNKNOWN
for that subject, so say so rather than treating it as not saturated. The brake only
works if step 7b writes a worker that came back dry as `idled`, with a
`<domain>/<slug>` id - a bare slug is skipped.
- **Systemic beats individual.** When one defect dominates the worklist across dozens
of subjects, the fix is one systematic pass, not forty dispatches. Notice this
before you dispatch, not after.
- **A document dense in platform-set numbers, untouched for months, is decay without a
clock.** `node scripts/check-currency.mjs --volatile --json` adds a `volatile` key (the
default JSON is unchanged): documents ranked by dated facts scaled by age, across golden
paths and techniques as well as applications. Measured 2026-10-14, four documents a
research pass proved stale ranked 93, 260, 410 and 592 of 6862, so use it to shortlist
within a bundle you are already considering, not to open a dispatch on its own. Dispatch
the shortlisted subject to scoped `/deepen`, whose directed run starts from the same
command's per-subject listing.
- **A due lead is cheaper than a fresh scan.** `librarian/sources/` holds findings a
research run proved real and could not land, each with a return condition. Read them
before ranking: a lead whose condition has arrived is work somebody already scoped.
- **Upstream movement is a third party's news, and it ranks below ours.** A consumer
deviation, verdict debt or a due lead all outrank a repository having shipped a
release. The one exception is **citation risk**: when `upstream-check` reports
`pin-unreachable` or `rewritten`, the corpus cites a commit that moved or vanished and
every application resting on it is unverifiable *now*. That is decay, not news, and it
ranks with `check-currency`'s expiries. See [the lane's design](../../../docs/plans/upstream-lane-2026-09-04.md)
for why eligibility is measured as reach into the fleet's code rather than across the
corpus - the obvious instrument was built, run, and rejected on its output.
- **Verdict debt is demand too.** The map check reports, per subject, which projects
hold verdicts the registry has since moved under. A subject with stale verdicts in
three projects is being *used*; that outranks a structural gap in one nobody joins
to. And a subject whose last landing was never applied anywhere (no row in
`librarian/applied.md`) is a wiki page the sweep should not enrich further before it
has been tried once.
**5. Dispatch.** A fleet of scoped workers, **cap 10 concurrent, topped up one per
completion** - the number both existing skills converged on across measured runs. Each
worker owns exactly one subject folder and **resolves its path from
`knowledge/<bundle>/index.json`, never constructs one**. Cross-subject findings come
back as proposals for you to place; a worker that writes outside its folder is the
collision the scope rule exists to prevent.
Choose the engine by what is missing:
| finding | engine |
| --- | --- |
| subject is thin, stale, or contradicted | scoped `/deepen` |
| subject does not exist but should | forge wave (`/forge`) |
| a banked lead in `sources/` came due | scoped `/deepen` at the subject the lead names |
| subject is single-source (one stack, one origin) | `/reconcile` wave against an external counterpart |
| missing `use_when`, dead link, bad frontmatter | fix in-session; no worker |
| cap breach, misplaced subject | `apply-taxonomy.mjs`; no worker |
| a new technique or flipped rule with no `applied.md` row | apply worker at the project the map joins it to (`/intake` Phase 7.5 method), one per finding |
| stale verdicts in a project under a subject this run touched | that project's `/conform --stale`; hand it the list, do not judge from here |
| a mined repository is `due` in `upstream-check` | scoped `/intake <url> --delta` under [`docs/upstream-brief.md`](../../../docs/upstream-brief.md), one worker per repository |
**The upstream lane's caps are the point of it.** At most **3 delta re-scans per run and
6 per calendar month**, and they spend the same cap 10 and the same review ceiling as
everything else here - they get no budget of their own. 39 eligible repositories on a
monthly clock is a feed, and a feed is what this lane must never become. Rank the due
rows by condition-fired, then tier, then age; dispatch the top three; and **write every
due row you did not take into the run note with the reason**, or the next run re-derives
it. A repo whose `handoff:` is not yet EXECUTED is owed work, not a re-scan, and the
instrument already withholds it.
**6. Review diffs, not reports.** Purity grep over the upper layers, read every new
technique, check corrections against the file's prior voice. Not delegable, and it is
what sets the batch ceiling near 8 per sitting.
**7. Commit, write the vault, reflect.** Atomic commits per subject. Then update
`librarian/subjects/<domain>/<subject>.md` for everything touched, the domain note,
and one run note. **Record what you declined and why** - a decline nobody wrote down
gets re-proposed every run forever. Re-run `node scripts/upstream-check.mjs --ledger`:
a repository checked and found unmoved still gets its date carried forward, because
without that row "are we overdue?" has no answer.
**7b. Write the machine-readable result beside the note.** The run note explains this
run to a person and says nothing a program can read, so a dispatcher cannot tell a
landing from a refusal from a quiet pass. One file closes that, through the helper that
owns the rules (public-safe paths, a decline with its reason, unknown fields rejected,
atomic placement):
```sh
node scripts/lib/run-result.mjs write <draft.json> # -> librarian/runs/<run-id>/result.json
```
This skill can fill it almost entirely: `mode` (the invocation), `domain`, all five
`counts`, one `subjects[]` row per dispatch with the attention points **before and
after** (the scan produces both, and nothing else in the fleet records the delta) and
an `outcome` from the same closed set as the counts - `landed` / `declined` / `idled` /
`contended` / `dispatched`, where a worker that came back dry is `idled`, not `landed` -
`declined[]` with the same reasons step 7 already demands, `verdicts[]` for every
`applied.md` row this run earned, and `pr` once the pull request is open. Use a short
`--run <id>`, list the commits made so far - the commit that carries this file cannot
name itself - and keep `files[]` and `commits[].pathspec` to the **registry's own**
paths: a landing into a project is a `subjects[]` row, never a consumer's file list.
Guess nothing; a field this run cannot say honestly is `null`.
**8. Propagate.** The run is not over at the registry commit; that is where every run
before 2026-09-02 stopped, and the measured result was a fleet whose recorded verdicts
were 100% stale against the corpus with nothing having told any project. After the
generated files are rebuilt (`build-index`, `build-catalog`, `build-knowledge-rules`):
- `node scripts/build-registry-map.mjs` over the fleet. It carries every verdict
forward, marks the pairs whose subject moved `stale: true`, and prints the impact
table: subject → projects with stale verdicts. Commit each project's
`.ai/registry-map.json` with a pathspec on its active branch; never push.
- Write the impact table into the run note under **Impact** and into each touched
subject note - slugs and counts, never paths. It is the project's `/conform --stale`
queue, and it is next run's demand.
- Every new technique and every flipped rule this run landed owes a row in
`librarian/applied.md` (mode, verdict, or `unapplied` with a return condition).
Dispatch one apply worker per finding at the project the map joins it to, cap the
same as step 5, review their diffs the same as step 6. A run that lands six
techniques and applies none has enriched a wiki.
## The skills pass
`/librarian skills [name]` evaluates the **skills**, not the bundles. Its evidence is the
run log. Every lane skill appends one line per run to its own checkout's
`.ai/skill-runs.local.jsonl` (skill-reflection clause, "Run log") - the project writes,
the registry pulls, because a skill run may not write into another repository.
`scripts/runs-backfill.mjs` pulls those lines into `usage/runs/<device>.jsonl` and writes
the measured sidecar `<device>.exact.jsonl` from transcripts. The contract is
`scripts/lib/runs.mjs`; the lane is described in `docs/runs-lane.md`.
1. **Fill the measurement, then prove the instrument.** On this device,
`node scripts/runs-backfill.mjs` (drains every fleet checkout's `.ai/skill-runs.local.jsonl`
on this machine, stamps device/project/version, attaches exact tokens/model/effort), then
`node scripts/check-runs.mjs`. Red gate: stop. Rows it could not stamp stay in their local
file and are reported - name them. Spot-check one exact token figure against its
transcript. The other device's runs arrive only when a skills pass runs THERE - say which
devices this pass covers rather than treating the lane as complete.
2. **Commit the logs** before reading them, as their own commit on the working branch:
`git add usage/runs/` then `chore(runs): <device> - <n> rows since <last>`. A log that
sits uncommitted is invisible to the other machine and one careless staging call from
riding in someone else's commit. Nothing else goes in that commit.
3. **Aggregate with the instrument, never by hand.** `node scripts/runs-report.mjs --since 30d --json`
(or `--skill <name>`). Numbers come from the report; you read the comments.
4. **Judge each skill that ran**, per version: outcome mix (failed + aborted share),
difficulty trend, token trend - always naming the basis (`exact` or `estimate`, never
blended) - and the recurring themes in `comment`. Self-rated fields are weak evidence:
a skill that rates itself 2 and fails is a finding; a smooth self-report beside a rising
failure share is not reassurance. Verdict per skill: `healthy` / `watch` / `needs redesign`,
each with the rows that justify it (run ids). Too few runs to judge is `unknown`, not healthy.
5. **Write** `librarian/skills/<YYYY-MM-DD>[-n].md`: window, devices covered (and which had no
backfill), a table skill - version - runs - outcomes - difficulty - tokens (basis) - verdict,
then per-skill findings. Skill slugs, project slugs and run ids only - no paths.
6. **Redesign proposals go to the skill's LESSONS.md**, never into its SKILL.md: when two or
more runs' comments (distinct runs, ideally distinct projects) point at the same flaw,
append `## <version-judged> - <YYYY-MM-DD> - librarian` with a `### Redesign proposal`
sub-block citing the run ids. One comment is an anecdote - it goes in the note, not in
LESSONS. Commit per skill as `skill(<name>): lessons from the run log`, run
`node scripts/check-skills.mjs --since HEAD` first.
**The executing skill never reads this evidence.** Do not paste run-log findings into a
skill's SKILL.md, a dispatch brief, or a project overlay: a trace produced while the
executor could read its own diagnosis measures the diagnosis, not the skill
(agent-memory / diagnosis-withheld-from-the-executor). The skill improves when a human
accepts a proposal and a new version ships; the next runs then measure that version.
## The vault
```
librarian/index.md map of content
librarian/standard.md the bar every sweep grades against
librarian/projects.md which connected project relates to which bundle
librarian/domains/<domain>.md per bundle: last swept, shape, what is owed
librarian/subjects/<domain>/<subject>.md last touched, dry streak, open leads, declines
librarian/runs/<YYYY-MM-DD>-<n>.md what one run swept, dispatched, accepted, declined
librarian/runs/<run-id>/result.json the same run, for a program (rkb-run-result/1)
librarian/sources/index.md the ledger of external sources /research mined
librarian/sources/<YYYY-MM-DD>-<slug>.md what one source yielded, and what it did not
librarian/upstream.md every mined repository: when we last looked, what moved
librarian/skills/<YYYY-MM-DD>[-n].md one skills pass: per-skill verdicts from the run log
```
Obsidian-navigable: wikilinks between notes, one fact per note. It lives in the
registry because it is reviewed like everything else here and readable with no tool.
Subject notes are created when a subject is first touched, not up front - 186 empty
notes would be noise, and "no note" already means "never swept", which the scan reads.
**The lane is public.** Scores, slugs and dates only. Never a consumer's paths - the
same rule as `usage/` and `signals/`.
## Rules that are not negotiable
- **Open a pull request; never push to `main`.** Merging is adopting and it is a human
act - the whole governance model of this repository. This matters most when a
scheduler eventually runs the loop unattended: build it in now, or the scheduler is
the change that quietly breaks the model.
- **Work on a branch**, and verify `git log -1` is your commit before reporting.
- **Unknown is not zero.** An unwitnessed bundle is not a current one; an unswept
subject is not a healthy one. Report absence as absence.
- **Dry is a result.** A sweep that finds nothing worth dispatching is a finding.
Write it in the run note and stop. Do not pad a worklist to look productive.
- **Never edit the upper layers with product names.** The gate catches it; do not make
it work.
## Scheduling (not yet built)
The loop is idempotent and its whole state is files, so a cron wrapper is small. What
that wrapper must preserve: the pull-request rule above, a token budget per run, and two
triggers worth waking up for - `check-currency.mjs --fail-on-expired` for our own decay,
and `upstream-check.mjs --due --exit-code` (exit 3) for a mined tree that has moved. An
unattended run that finds nothing should write a one-line run note and exit, not
invent work.
Until that wrapper exists, say the honest thing: the upstream lane is *due-checkable* at
any time, not *scheduled*. `librarian/upstream.md` makes "how overdue are we" a fact
rather than a feeling, and that is the whole of the cadence guarantee today.