cite-wide 0.3.0

Enrich citations: fill in every citation in your vault, and see each change first

One command fills every citation's missing title, author, date, and publisher, shows you each change, and writes only what you approve.

Enrich citations: fill in every citation in your vault, and see each change first

Why Care?

If you keep a citation file per source, you know how they drift. Some were saved with a title and a URL. Some were saved before the footnote had any text, so they're empty. Some hold whatever an older extraction scraped, and that can be an author of "3 minutes" or a title of "Page not found".

On 2026-10-06 we cleaned up the 75 citations in our own vault with throwaway scripts run outside Obsidian. Author coverage went from 43 to 70 of 70, and we filled 49 reference texts, 63 publishers, and dozens of logos and dates. It worked, but only someone at a terminal could run it. Now it's two commands in Cite Wide, and nothing is written until you've looked.

What's New?

  • Enrich all citations. Fills in what's missing in every citation file in your Citations folder.

  • Enrich this citation. The same, for the footnote under your cursor or the citation file you have open.

  • You see the plan first. A modal lists every citation with changes, old → new for each field, with a checkbox per citation. Close it and nothing changes.

  • It only fills gaps. A real title, author, or date you wrote is never overwritten. Junk is treated as a gap: error-page titles ("Page not found | MRU"), bot checks ("Just a moment…"), and non-names in the author field ("3 minutes", "Super User").

  • Links that survive renames. filesUsedIn is rewritten as [[full/path|Alias]] links to the notes that cite each source today, so a renamed note no longer makes a citation look orphaned.

  • A report you can act on. Each run saves Citations/_reports/Enrichment-<YYYY-MM-DD-HHmm>.md, listing what changed, dead or blocked links, citations no note defines, and fields still empty.

  • Safe to re-run. An enriched vault proposes no changes, and no file is touched.

How it works

read the vault once ──► index every [^id] and [^id]: (symlinked folders included)
        │
        ▼
for each citation, 4 at a time:  footnote ─► page <meta> tags ─► Jina Reader (if gaps) ─► publisher homepage (brand)
        │
        ▼
planCitationChanges(current, fetched, usage) → [{ key, from, to, reason }]   ← pure, unit-tested
        │
        ▼
review modal (old → new, checkbox per citation)  ── Cancel: nothing written
        │ Apply
        ▼
processFrontMatter, approved fields only ──► report note in _reports/

The fetching is the same pipeline Promote to canonical source already uses, unchanged. What's new is the orchestration and, above all, the planner: one pure function that decides, field by field, whether to fill, replace, or leave alone. Every rule in it comes from something that went wrong in the vault run.

What went wrong in the vaultThe rule now
Error pages stored as titles ("Page not found | MRU")A junk title counts as empty, and the footnote's title wins
What BMW&rsquo;s Corporate VC…Entity-encoded titles are decoded
23 stored authors that weren't namesAn author that isn't name-shaped is replaced, or emptied when no name is known
21 filesUsedIn paths pointing at renamed notesRewritten as links to the notes that cite it today; kept when none do
The temptation to mark sources canonical in bulkcanonical is never set; promotion stays a deliberate act

Under the Hood

  • Pure core, thin I/O. src/services/enrichCitationsService.ts holds buildUsageIndex, planCitationChanges, applyFieldChanges, and renderEnrichmentReport, with no Obsidian or network calls. src/services/enrichCitationsRunner.ts reads the vault, runs the worker pool, and writes.

  • Writes go through processFrontMatter. The one-off run had to write YAML line by line to avoid restyling files; inside Obsidian, the app's own frontmatter API does that job. applyFieldChanges also skips any field whose value changed after it was planned, so an edit you make while the modal is open is never overwritten.

  • The access date is not a change. date_recently_accessed is refreshed alongside real changes, but a citation whose only change would be that date isn't listed or written. That's what makes a re-run touch zero files.

  • Usage is indexed once per run, not once per citation, and the Citations folder itself (with its _reports, _text, and _files folders) doesn't count as usage.

  • Tests first. 21 new tests ran red before the services existed: every planner case from the spec's acceptance list, using the vault's real values; the usage index over a stub vault; the report as a pure function; and the runner end to end over a stub vault with requestUrl stubbed, so no test touches the network. pnpm test: 115 pass. ESLint reports 0 errors and 0 warnings; no new dependencies.

What's Next

  • Operator check: run Enrich all citations on the lossless vault. It was already enriched by the one-off run, so the modal should propose little or nothing. Anything it does propose is either a gap the scripts missed or a planner rule worth a second look.

  • cited_in_files on canonical sources is left as is, per the spec's "everything else untouched"; a later pass could keep it in step with filesUsedIn.

References

  • Spec: context-v/specs/Enrich-Citations.md

  • The run it productizes: context-v/issues/Enriching-the-Lossless-Vault-Citations.md