a subject can mean different things per campaign (the Grip of Resolve is one Sevenforged relic, but “Guts bears it + this boon” is campaign-specific canon). Chose fork over the layer model (BL-054): campaign-specific canon lives on a divergent private copy, source stays clean; canon drift is the accepted tradeoff. articles.forked_from_id self-FK (forked_from/forks, dependent: :nullify), Article#fork!(user:, campaign: :unchanged, name:) deep-copies content + dm_notes (ActionText body, attachments preserved -> references rebuilt) and reattaches gallery image blobs (no re-upload); resets to private draft; ancestry children not copied; world follows target campaign. ArticlePolicy#fork? (owner / world owner / publicly-visible). MCP fork_article (130 tools) + get_article returns forked_from_id/forks; web POST /articles/:id/fork with a Fork button (any user who can fork, not just owners), a “↰ Forked from” lineage link, and a reverse “Forks” sidebar panel (above Hierarchy) listing this article’s forks scoped to campaigns the viewer owns. Seed parity (Grip of Resolve + Guts fork). 11 new tests (model/policy/controller), full suite 447 green. ADR-0059; docs/architecture.md + docs/mcp-tools.md updated. Mirrors the Adventure fork pattern (ADR-0034).
BL-063
Shipped