The four seams
The pattern is cut so each of these changes touches one layer, not all of them.- Add a source. Register another MCP server, MERGE a new
DomainSource, write a small adapter that emitsHighlight/Documentshapes. The graph slice, the scoring strategy, and the publishing strategy do not change. - Add a node type. Extend the
knowledge-garden/schema with a new node (e.g. re-introduceBookmarkorAnnotationwhen their producers ship). Existing nodes and edges keep their writers-of-record; the new node gets its own. - Add a strategy. Write a new
contract.yaml+strategy.pythat reads from existing tables and the existing graph. The most common shape is a sibling extractor (LLM-based concept distillation), a sibling publisher (Mintlify, Quartz), or a sibling correlator (claim-evidence linking). - Replace a backend. Swap one MCP for another with the same shape — Notion-for-Obsidian on the publish side, Readwise-for-Reader-only on the ingest side. The strategy that talks to that backend changes; nothing else does.
A worked extension — adding Raindrop as a second source
Goal: ingest Raindrop bookmarks alongside Readwise highlights, so the same Concept extraction folds bookmarks into the same graph. The strategy you write is small; the rest of the pattern adapts automatically.Register the Raindrop MCP
Add a
raindrop: entry to mcp_servers.servers: in your fracta.yaml. The catalog already has a candidate entry at mcp-servers/raindrop/server.yaml; copy the registration shape it documents. Run fracta registry list and confirm raindrop reaches ok.MERGE a new DomainSource
On the first strategy run, MERGE the source node so the 4-tier resolution chain stays whole:
Write a small `raindrop_distill` strategy
Mirror
highlight_distill’s shape, but read from Raindrop’s list_bookmarks (or equivalent) instead of readwise.list_highlights. Emit Highlight nodes with id prefixed raindrop:<id>; emit Document nodes for the source URLs. Reuse highlight_distill’s extractor fan-out — call the same three concept-* MCPs and apply the same merge algorithm. The asymmetric scoring function and the routing thresholds are unchanged.Run cross-source-concepts as usual
cross_source_concepts already counts domain_source_count via CAPTURED_FROM -> DomainSource. The moment Raindrop highlights land in the graph, every Concept they mention sees its diversity term lift — Concepts present in both Readwise and Raindrop will rank higher than single-source Concepts. No code change needed in this strategy.Publish via the existing `notion-publish`
The publish strategy reads
Concept nodes by name; it does not care which source they came from. Pages it renders will include supporting Highlights from both sources, with extracted_by and source attribution visible on each MENTIONS edge. No code change needed.Adding a node type
The v1 schema is deliberately small. Two node types that were considered for v1 and held back are documented here so you know how to bring them back when their producers ship.Bookmark and Annotation
Held back because no producer in v1 emits them. Re-introduce when Raindrop (Bookmark) or a PDF / web annotator MCP (Annotation) lands. Add the node files under internal/schema/embedfs/graph-schema/knowledge-garden/particulars/, wire MENTIONS from them to Concept / Entity (same shape as Highlight -> Concept), and give the producer strategy discovered authority over the new node type. Existing checkpoint rules continue to work; you may want to add <node>_missing_captured_from analogues.
Standalone Note
Collapsed in v1 into a note: string property on Highlight (the Readwise highlight-note field). No daily-note producer ships, so the standalone node would have been empty. If you wire a daily-note source (Logseq, Obsidian daily journal), re-introduce Note as a particular and give the new producer authority — Highlight.note continues to serve Readwise’s flavour separately.
Adding a sibling strategy
The three slots that make sense as siblings to the v1 strategies:highlight_distill_llm— same contract output ashighlight_distill, but uses an LLM-based extractor instead of the three NLP MCPs. Useful when you want narrative-aware concept extraction (the NLP extractors are surface-form). Sameextraction_scoresemantics; same routing thresholds.mintlify_publish/quartz_publish/ghost_publish— alternate sinks. ThePublication.sinkproperty is the seam: each sibling populates the same node type with a differentsinkvalue. The graph stays canonical; sinks multiply.claim_extract— the future writer-of-record forClaimnodes. ReadsHighlight/Documenttext, emitsClaim+EVIDENCES/CONTRADICTSedges. TheClaimnode type already exists in the schema waiting for a producer.
Replacing a backend
Swapping backends works cleanly because the strategies talk to MCP servers by name and the gateway abstracts the transport. The two most common swaps:- Notion -> Obsidian (publish). Write an
obsidian_publishstrategy with the samePublicationshape (sink: obsidian,external_id: <vault-path>,content_hash). Theactions:block inbinding.yamlpoints at a different MCP; the idempotency logic is identical. - Readwise -> Reader-only (ingest). Both share the same hosted MCP and most of the same tools; the difference is which
list_*you call. Areader_distillstrategy mirroringhighlight_distillbut readinglist_documentsonly is a small fork — keep both around if you want.
What this pattern won’t support
The honest list. If any of these match your workload, the pattern needs more than an extension — it needs a redesign.- Real-time streaming. The pattern is batch by design.
highlight_distillwatermark-paginates against Readwise;notion_publishre-publishes on idempotent re-run. There is no event bus, no incremental delta beyond the watermark, no streaming subscription path. If you need sub-minute latency, this isn’t the pattern. - Multi-tenant graph isolation. The graph slice is a single tenant — every Concept is global. Per-user namespacing would require schema changes and a routing layer in front of the publish strategy.
- More than ~5k highlights per single ingest. The extractor fan-out is bounded by gateway concurrency and the Readwise 20-req/min ceiling. For multi-year first-pulls, bucket the watermark explicitly (
--watermark 2024-01-01then--watermark 2024-07-01, etc.) rather than backfilling in one shot. - Alias resolution beyond surface-form.
popperandkarl poppercommit as separate Concepts in v1; theconcept_low_extraction_high_confidencecheckpoint rule surfaces survivors for hand-merging. A future spec introduces either an embedder MCP or an LLM-based resolver. Until then, alias merging is a manual graph-edit step. - Public-web garden publishing. Notion is private-by-default; the pattern publishes to a private database. A
mintlify_publish/quartz_publishsibling is the unblock — named here, not shipped. - Long-form documents past extractor context. Highlights longer than ~240 tokens log a warning and proceed with first-window extraction. Full chunking-with-merge is a planned follow-up; until then, long podcast transcripts and full articles are partially-processed.
Sibling patterns and future directions
Where the Reading Garden is the knowledge-garden flavour of fracta’s pattern library, sibling patterns answer different jobs with the same primitives:- Project Companion (PARA-shaped): adds
Project/Area/Resource/Archivenodes alongside the knowledge-garden schema. Reusesnotion_publishviasink: notion. Targeted at active-work tracking rather than reading. - Code Garden: ingests git commits + PR diffs + code-review threads; distills into
Module/Decision/PatternConcepts. Wires to GitHub MCP rather than Readwise. - Research Notebook: extends the schema with first-class
ClaimandQuestionwriters; wires Zotero + ArXiv MCPs; produces a citation-graph-aware Notion publication.

