Knowledge Layers
Status: Long-term architectural target. Audience: Contributors thinking about how source files, graph structure, and query history relate to one another.
Exocortex treats source files, graph nodes, and queries as three layers of the same knowledge organism, with explicit interfaces between them. This document explains the layering, the data flow, and the boundaries each layer enforces.
The three layers
┌──────────────────────────────────────────────────────────┐│ L1: SOURCE (human-authored) ││ vault/{domain}/{section}/{subsection}/{file}.md ││ • Markdown + frontmatter + wikilinks ││ • User controls layout, naming, taxonomy ││ • Stable mental model for the human author │└──────────────────────────┬───────────────────────────────┘ │ parse: frontmatter + content + links + path │ extract: entities, claims, decisions, metrics ▼┌──────────────────────────────────────────────────────────┐│ L2: KNOWLEDGE GRAPH (semantic) ││ AGE graph + pgvector + typed edges ││ • Nodes: SourceFile, Concept, Entity (Person, Project, ││ Decision, Metric, Pillar, etc.) ││ • Edges: WIKILINK_TO, MENTIONS, INFORMS, DERIVED_FROM, ││ BELONGS_TO_SECTION, CROSS_CUTS ││ • Embeddings on chunks (chunked by section heading) ││ • Multi-inheritance via edges (a doc can belong to many ││ sections at once) │└──────────────────────────┬───────────────────────────────┘ │ enrich: │ • query memory (recursive) │ • LLM extraction (entities, claims) │ • cross-domain matching ▼┌──────────────────────────────────────────────────────────┐│ L3: QUERY MEMORY (recursive enrichment) ││ query_log table + Question/Answer nodes ││ • Nodes: Question, Answer, AnsweredBy ││ • Edges: ASKED_BY (user), RETRIEVED (sources used), ││ VALIDATED_BY (human review) ││ • Sources: Claude Desktop, GraphRAG MCP, web clients │└──────────────────────────────────────────────────────────┘Surfaces (Obsidian wiki, Notion mirror, Telegram digest, CLI output) are compiled from L2 — never written into directly. Compiled views are disposable; the graph is canonical.
L1: Source files
Source files live in a folder hierarchy that makes sense to the human author. A typical client knowledge base might look like this:
vault/work/{client}/{domain}/ 00_HOME/ ← MOCs, dashboards (cross-cutting) 01_STRATEGY/ ← strategy documents 02_OPERATING_MODEL/ ← roles, governance 03_GOVERNANCE/ ← decision types, log 04_ROADMAP/ ← phases, gates 05_<pillar-a>/ ← topic area 06_<pillar-b>/ ... 15_MEETINGS_AND_RITUALS/ ← cadence 16_TEMPLATES/ ← reusable templates 99_ARCHIVE/ ← historicalWhat’s strong about this layout:
- Numbered sections are a stable mental model.
- Thematic grouping is easy to author iteratively.
- Frontmatter already carries taxonomy (
tags,type,status). - Wikilinks express cross-section relationships explicitly.
What’s weak for retrieval:
- Hierarchy is single inheritance — a file lives in one folder — but real concepts cross-cut. A single document may logically belong to five sections at once.
- Folder names aren’t in the graph by default (a chunk from
06_DESIGN_SYSTEM/Tokens/doesn’t know it’s “DS / Tokens” unless the parser captures it from the path). - Frontmatter
typedrifts (policyvsdecision-typevsplaybook). The boundary between taxonomy and folksonomy needs to be explicit. - Wikilinks are user-curated — high quality but low recall.
L1 → L2: parsing source into the graph
For each file in vault/, the parser emits:
-
SourceFilenode with properties:path(relative to the vault root)section_path(e.g.["01_STRATEGY", "02_Pillars"]— array, queryable)title(from H1, frontmattertitle, or filename)tags(from frontmatter)type,status(from frontmatter)last_modified(file mtime)content(full markdown)content_embedding(vector, per chunk)
-
Wikilink →
WIKILINK_TOedge — explicit user-curated relations. -
Frontmatter taxonomy → typed nodes:
tags: [pillar/p2-design-system]→BELONGS_TO_PILLARedge to aPillar P2 - DSnodetags: [decision]→ typed as aDecisionnodetags: [governance]→ typed as aGovernancenode
-
Path-based edges:
BELONGS_TO_SECTION→ section node (e.g.01_STRATEGY)- Section nodes carry
PARENT_OFedges for hierarchy.
-
Content-extracted edges (via LLM):
- Person mentions →
MENTIONS_PERSON - Project mentions →
CLASSIFIED_AS_PROJECT - Metric mentions →
DEFINES_METRIC/REFERENCES_METRIC - Decision references →
INFORMED_BY_DECISION - Roadmap items →
IMPLEMENTS_ROADMAP_ITEM
- Person mentions →
-
Cross-cutting concepts (multi-inheritance): A document in
10_TOPIC_X/may semantically belong to several pillars at once. LLM extraction detects “this doc cross-references Pillar X, Stack Y, Metric Z” and emitsCROSS_CUTSedges.
What this gives you:
- “Show all docs about Topic X” — graph query traverses
BELONGS_TO_PILLAR+CROSS_CUTSedges, returns canonical files plus cross-referencing material. - “What decisions affect Pillar 2?” —
INFORMED_BY_DECISIONedges filtered by pillar. - “What’s Person A’s view on Topic Y?” —
MENTIONS_PERSONedges where speaker matches.
L2 → wiki: compiled views
The wiki is not a copy of the source with UUID filenames. It is a set of deliberate compiled views of the graph.
A reasonable layout for a client knowledge base:
wiki/work/{client}/{domain}/├── _index.md ← MOC: section nav + recent activity├── by-section/ ← mirrors source structure (links)│ ├── 00-home.md│ ├── 01-strategy.md│ ├── ...│ └── 99-archive.md├── by-pillar/ ← canonical pillar pages│ ├── p1-quality.md ← aggregates all docs tagged p1│ ├── p2-design-system.md│ ├── ...├── by-tag/ ← all frontmatter tags├── by-type/ ← decision, playbook, gate, etc.└── concepts/ ← extracted entity pages ├── decisions.md ├── metrics.md ├── people.md └── roadmap-items.mdEach wiki/ page is a view of the graph, not a copy of source.
Links from wiki pages point back to vault/{actual_path} with the
original naming and structure preserved.
A user landing on wiki/work/<client>/<domain>/by-pillar/p4-topic.md
sees:
- All source files tagged P4 (with real titles, not UUIDs).
- Cross-references to other pillars.
- Related decisions, metrics, people.
- Recent meetings touching P4.
The wiki becomes a GraphRAG-rendered semantic map, not a file flatten.
Cross-cluster relations
A real client engagement is rarely a single folder. It tends to span multiple corners of the vault and external systems:
vault/work/{client}/{domain}/ ← primary knowledge basevault/work/{client}/{other-domain}/ ← e.g. pricing, contractswiki/work/clients/{client}.md ← client overview (compiled)wiki/work/{client}/{domain}/ ← compiled viewsbacklog/work/{client}/ ← kanban taskswiki/work/meetings/src/ ← meeting noteswiki/work/people/{names}.md ← people involved+ Notion (via sync module) ← tasks+ Email threads ← already in graph+ Meeting transcripts ← via transcript moduleGoal: all of these layers connect into one client supercluster in the graph:
Client: <name>├── Domain: KnowledgeBase (N source files)│ ├── Section: 01_STRATEGY (subgraph)│ ├── Section: 04_ROADMAP (subgraph)│ ├── ...│├── Domain: Roadmap (kanban tasks) ──── IMPLEMENTS_ROADMAP_ITEM│├── Domain: People│ ├── PersonA ── WORKS_ON ── Sections│ ├── PersonB ── APPROVES ── Phase Gates│ └── Vendors ── DELIVERS ── Roadmap items│├── Domain: Meetings│ ├── Sync ── DISCUSSES ── Decisions│ └── Action items ── ASSIGNED_TO ── People│└── Domain: External (CMS stack, analytics, etc.)Cross-cluster queries then become natural:
- “What does Client A have in common with Client B?” (similar challenges, shared patterns)
- “Vendor performance across clients” (cross-client scorecard)
Should queries feed the graph?
Yes, but with explicit boundaries. Three levels of escalation:
Level 1: Telemetry (always on)
Every query is logged as:
- Question text + timestamp
- Source (
claude_desktop_mcp,graph_rag_api, etc.) - Retrieved nodes (which chunks / files were served)
- Latency, tokens, cost
Value: you see what you ask about, how often, and where the knowledge gaps are. A “10 most-asked questions this month” dashboard is a natural growth opportunity.
Schema:
CREATE TABLE query_log ( id UUID PRIMARY KEY, tenant_id UUID, source TEXT, -- 'claude_desktop_mcp', etc. question TEXT, question_embedding VECTOR(1536), retrieved_node_ids UUID[], -- audit trail latency_ms INT, tokens INT, cost_usd NUMERIC, asked_at TIMESTAMPTZ DEFAULT NOW());Level 2: Question as searchable entity (opt-in)
Question nodes in the graph, with embeddings:
- A future query similar to a past one returns “you asked something like this 3 weeks ago, here was the answer + sources”.
- Builds a personal FAQ.
- Detects: same question asked 5× → candidate for a canonical doc.
Risks:
- Privacy: queries can be sensitive (“how much did we pay vendor X”).
- Pollution: random one-off questions add noise.
- Recursive: question references question → potentially infinite loop.
Mitigation:
- Off by default, explicit opt-in per source (e.g. Claude Desktop yes, ad-hoc MCP no).
- Auto-prune questions with fewer than 2 retrieved nodes (low signal).
- TTL: questions older than 90 days without re-use → archived.
Level 3: Answer as validated knowledge (manual promotion)
Some Q&A turns out useful enough that the user promotes the answer
into a SourceFile-like node:
- User clicks “save answer to vault” in the client.
- Becomes a new file in
vault/work/{domain}/answers/{ts}-{slug}.md. - Goes through the normal source → graph pipeline.
- BUT tagged
provenance: ai_answerso future retrieval can deprioritize it if a better human-authored source emerges.
This is a critical guardrail: AI answers do not become automatically authoritative. They must be human-curated. Otherwise the feedback loop — AI cites AI answers — drifts away from ground truth.
Recommended phasing
| Phase | Scope | Risk profile |
|---|---|---|
| 1 | Telemetry only | Zero risk, all reward |
| 2 | Question nodes for trusted srcs | Low — opt-in, audited |
| 3 | Answer promotion w/ human review | Bounded — manual gate |
Provenance: the AI-cites-AI guardrail
Every file Exocortex writes into the vault carries a provenance
frontmatter field. Retrieval ranking respects it:
human > ai_assisted > ai_extracted > ai_authored (validated) > ai_authored (unvalidated)This prevents the failure mode where AI-generated synthesis is re-ingested, re-cited, and gradually crowds out the human source of truth.
See the Module System for how plugins should declare provenance on the artifacts they emit.