# Wiki Merge

> Merge an overlap cluster of Vault concepts into one canonical concept, rewrite wikilinks, and preserve a dry-run/apply boundary. Use when $wiki-curate has identified a winner and fold-in slugs. Not for general wiki auditing or automatic article promotion.

- Skill: `flonat/wiki-merge` (Agent Skill)
- Install (CLI): `npx skillmds@latest add flonat/wiki-merge`
- Raw SKILL.md: https://api.skillmd.com/api/skills/flonat/wiki-merge/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: flonat (https://skillmd.com/u/flonat)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/flonat/wiki-merge

---


# Wiki Merge — Collapse an Overlap Cluster

> Folds one or more concept slugs into a single canonical entry.
> Preserves provenance, rewrites incoming wikilinks across atlas /
> books / concepts / project knowledge folders, deletes the losers,
> and denylists them so the Saturday `wiki-grow` cron doesn't
> re-promote.

## When to Use

- Right after `wiki-curate` flags an overlap cluster in Section A
  and you've decided which slug should be canonical.
- Manual cleanup when you notice two concepts that should be one.

## When NOT to Use

- For trivial renames — keep both files and add an alias.
- For tag normalisation — that's edit-in-place, not a merge.
- When the "overlap" turns out to be a genuine sub-concept relation
  — keep both, add a `[[wikilink]]` reference between them instead.

---

## What it does

```
uv run python scripts/wiki-merge.py <winner> <loser1> [<loser2> ...]
uv run python scripts/wiki-merge.py --apply <winner> <loser1> [<loser2> ...]
```

Steps performed (in order, only on `--apply`):

1. **Read** winner + every loser concept file under `~/vault/concepts/`.
2. **Build merged body** — winner's body verbatim, then each loser's
   body under a `## From <loser-slug>` section. Each section includes
   a small admonition naming the source project + promotion date for
   traceability. Sub-headings in loser bodies are demoted by one
   level (`##` → `###`) so they don't compete with the new section
   delimiter.
3. **Build merged frontmatter** — winner's fields preserved; tags
   becomes the union of winner ∪ loser tags; a new `fold_in_sources:`
   block records each loser's slug + source_project + promoted_on
   date; `last_merged:` is set to today.
4. **Write merged content** back to the winner's file.
5. **Rewrite wikilinks** — every `[[<loser-slug>]]` (with or without
   alias suffix) across atlas topics, book chapters, other concepts,
   and project knowledge folders gets rewritten to
   `[[<winner-slug>|<original-display-text>]]`. The alias keeps the
   original display label visible so the change isn't jarring on
   pages that referenced the loser by name.
6. **Delete loser files** from `~/vault/concepts/`.
7. **Append to denylist** — each loser slug is added to
   `~/vault/concepts/.denylist` with a comment noting the
   merge date. This stops the Saturday `wiki-grow` cron from
   re-promoting the loser slug if a project knowledge article with
   that slug still exists.

## Dry-run output

Default invocation (no `--apply`) prints a plan:

```
Merge plan (dry-run):
  Winner:  goodharts-law  (concepts/goodharts-law.md)
  Fold in: goodhart-taxonomy, goodhart-resistance
  Merged file size: 11666 chars (winner was 3516)
  Wikilink rewrites: 12 file(s)
    concepts/strategic-classification.md: 1 occurrence(s)
    .../knowledge/goodhart-resistance.md: 1 occurrence(s)
    ...
  Denylist additions: goodhart-taxonomy, goodhart-resistance
  Files to delete: concepts/goodhart-taxonomy.md, concepts/goodhart-resistance.md

(dry-run — pass --apply to perform the merge)
```

Inspect the file count and the rewrite targets. If anything looks
wrong (e.g. a loser slug is wildly overrepresented across the corpus
suggesting it's actually a distinct concept), abort and reconsider.

## Apply

```
uv run python scripts/wiki-merge.py --apply goodharts-law goodhart-taxonomy goodhart-resistance
```

Performs all six steps. After the run:

- `~/vault/concepts/goodharts-law.md` contains the merged
  content with fold-in provenance in frontmatter.
- `goodhart-taxonomy.md` and `goodhart-resistance.md` are deleted.
- All atlas/books/concepts/project-knowledge files that wikilinked to
  the loser slugs now wikilink to the winner with the original
  display label preserved as the alias.
- `~/vault/concepts/.denylist` has two new lines.

**Verify with `wiki-curate`** — the goodhart cluster should be gone
on the next run.

---

## Safety properties

- **Dry-run is default.** Forgetting `--apply` previews only.
- **Loser frontmatter preserved in `fold_in_sources:`** — even though
  the loser file is deleted, the merged file records source_project +
  promoted_on for each fold-in, so the provenance is recoverable.
- **Wikilinks preserve display labels** — rewrite produces
  `[[winner|Original Display]]` form, so a topic page that wrote
  `[[Goodhart Taxonomy]]` will still render "Goodhart Taxonomy" after
  the merge, just pointing at `goodharts-law` now.
- **Denylist prevents re-promotion** — if a project still has
  `<loser-slug>.md` in its knowledge folder, the next Saturday cron
  won't re-promote it.

## Anti-patterns

- **Don't merge a curated concept INTO an auto-generated one.** If
  you must, swap winner/loser. Curated content should be the
  canonical surface.
- **Don't merge concepts from different domains** just because slugs
  share a token (e.g. `model` token in `language-model` and
  `linear-model`). Use judgement.
- **Don't merge without dry-running first.** The dry-run output is
  the only confirmation step.

---

## Cross-References

| Skill | Relationship |
|-------|-------------|
| `wiki-curate` | Identifies the clusters this skill collapses. |
| `wiki-grow` | Writer of auto-promoted concepts; denylist entries from `wiki-merge` prevent it from re-promoting losers. |
| `compile-knowledge` | Source of project knowledge articles that get promoted to concepts. |

