kogral/docs/book/print.html
2026-01-23 16:11:07 +00:00

6878 lines
289 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="rust sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>KOGRAL Documentation</title>
<meta name="robots" content="noindex">
<!-- Custom HTML head -->
<meta name="description" content="Complete documentation for KOGRAL - Git-native knowledge graphs for developer teams">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "";
const default_light_theme = "rust";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="toc.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('rust')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">KOGRAL Documentation</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/your-org/knowledge-base" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="kogral-documentation"><a class="header" href="#kogral-documentation">KOGRAL Documentation</a></h1>
<p>Welcome to the KOGRAL documentation! This directory contains comprehensive documentation for KOGRAL (<strong>KO</strong>wledge <strong>GRA</strong>phs, <strong>L</strong>ocal-first), built with <a href="https://rust-lang.github.io/mdBook/">mdBook</a>.</p>
<h2 id="-reading-the-documentation"><a class="header" href="#-reading-the-documentation">📚 Reading the Documentation</a></h2>
<p>You have several options for reading the documentation:</p>
<h3 id="option-1-serve-locally-with-mdbook-recommended"><a class="header" href="#option-1-serve-locally-with-mdbook-recommended">Option 1: Serve Locally with mdBook (Recommended)</a></h3>
<p>The best reading experience with navigation, search, and live reload:</p>
<pre><code class="language-bash"># Serve documentation at http://localhost:3000
just docs::serve
</code></pre>
<p>This will:</p>
<ul>
<li>Build the mdBook</li>
<li>Start a local web server on port 3000</li>
<li>Open your browser automatically</li>
<li>Watch for changes and auto-reload</li>
</ul>
<h3 id="option-2-build-static-html"><a class="header" href="#option-2-build-static-html">Option 2: Build Static HTML</a></h3>
<p>Generate static HTML files you can browse offline:</p>
<pre><code class="language-bash"># Build mdBook to docs/book/
just docs::build
</code></pre>
<p>Then open <code>docs/book/index.html</code> in your browser.</p>
<h3 id="option-3-read-markdown-files-directly"><a class="header" href="#option-3-read-markdown-files-directly">Option 3: Read Markdown Files Directly</a></h3>
<p>All documentation is written in Markdown and can be read directly:</p>
<ul>
<li>Browse via GitHub/GitLab web interface</li>
<li>Use your editor's Markdown preview</li>
<li>Read from terminal with <code>bat</code>, <code>glow</code>, or similar tools</li>
</ul>
<p><strong>Navigation</strong>: See <a href="SUMMARY.html">SUMMARY.md</a> for the complete table of contents.</p>
<h2 id="-documentation-commands"><a class="header" href="#-documentation-commands">🛠️ Documentation Commands</a></h2>
<p>We use <code>just</code> recipes for documentation tasks. All commands assume you're in the project root directory.</p>
<h3 id="build-and-serve"><a class="header" href="#build-and-serve">Build and Serve</a></h3>
<pre><code class="language-bash"># Serve documentation locally (recommended)
just docs::serve
# Build static HTML
just docs::build
# Watch and rebuild on file changes
just docs::watch
</code></pre>
<h3 id="validation"><a class="header" href="#validation">Validation</a></h3>
<pre><code class="language-bash"># Test code examples in documentation
just docs::test
# Check for broken links
just docs::check-links
</code></pre>
<h3 id="cleanup"><a class="header" href="#cleanup">Cleanup</a></h3>
<pre><code class="language-bash"># Remove build artifacts
just docs::clean
</code></pre>
<h3 id="view-all-documentation-commands"><a class="header" href="#view-all-documentation-commands">View All Documentation Commands</a></h3>
<pre><code class="language-bash">just docs::help
</code></pre>
<h2 id="-installing-mdbook"><a class="header" href="#-installing-mdbook">📦 Installing mdBook</a></h2>
<p>mdBook is required to build and serve the documentation.</p>
<h3 id="install-via-cargo"><a class="header" href="#install-via-cargo">Install via Cargo</a></h3>
<pre><code class="language-bash">cargo install mdbook
</code></pre>
<h3 id="install-optional-tools"><a class="header" href="#install-optional-tools">Install Optional Tools</a></h3>
<p>For enhanced functionality:</p>
<pre><code class="language-bash"># Link checker (validates internal/external links)
cargo install mdbook-linkcheck
# Mermaid diagram support
cargo install mdbook-mermaid
# PlantUML diagram support
cargo install mdbook-plantuml
</code></pre>
<h3 id="verify-installation"><a class="header" href="#verify-installation">Verify Installation</a></h3>
<pre><code class="language-bash">mdbook --version
# Should output: mdbook v0.4.x or later
</code></pre>
<h2 id="-documentation-structure"><a class="header" href="#-documentation-structure">📖 Documentation Structure</a></h2>
<p>The documentation is organized into the following sections:</p>
<h3 id="1-kogral-definition-kogral"><a class="header" href="#1-kogral-definition-kogral">1. <strong>KOGRAL Definition</strong> (<code>kogral/</code>)</a></h3>
<ul>
<li>What is KOGRAL and why it exists</li>
<li>Core concepts (nodes, edges, graphs)</li>
<li>Design philosophy</li>
</ul>
<h3 id="2-guides-guides"><a class="header" href="#2-guides-guides">2. <strong>Guides</strong> (<code>guides/</code>)</a></h3>
<ul>
<li>Quick start (5 minutes)</li>
<li>Installation guide</li>
<li>Use cases with examples</li>
</ul>
<h3 id="3-architecture-architecture"><a class="header" href="#3-architecture-architecture">3. <strong>Architecture</strong> (<code>architecture/</code>)</a></h3>
<ul>
<li>System overview with diagrams</li>
<li>Config-driven architecture</li>
<li>Graph model details</li>
<li>ADRs (Architectural Decision Records)</li>
</ul>
<h3 id="4-setup-setup"><a class="header" href="#4-setup-setup">4. <strong>Setup</strong> (<code>setup/</code>)</a></h3>
<ul>
<li>Initial setup</li>
<li>Development environment</li>
<li>Production deployment</li>
<li>Testing environment</li>
<li>CI/CD integration</li>
</ul>
<h3 id="5-configuration-config"><a class="header" href="#5-configuration-config">5. <strong>Configuration</strong> (<code>config/</code>)</a></h3>
<ul>
<li>Configuration overview</li>
<li>Nickel schema reference</li>
<li>Runtime configuration</li>
<li>Environment modes (dev/prod/test)</li>
</ul>
<h3 id="6-storage-storage"><a class="header" href="#6-storage-storage">6. <strong>Storage</strong> (<code>storage/</code>)</a></h3>
<ul>
<li>Storage architecture (hybrid strategy)</li>
<li>Filesystem backend</li>
<li>SurrealDB backend</li>
<li>In-memory backend</li>
<li>Sync mechanism</li>
</ul>
<h3 id="7-ai--embeddings-ai"><a class="header" href="#7-ai--embeddings-ai">7. <strong>AI &amp; Embeddings</strong> (<code>ai/</code>)</a></h3>
<ul>
<li>Semantic search</li>
<li>Embedding providers</li>
<li>Provider comparison</li>
<li>Configuration examples</li>
</ul>
<h3 id="8-templates-templates"><a class="header" href="#8-templates-templates">8. <strong>Templates</strong> (<code>templates/</code>)</a></h3>
<ul>
<li>Template system (Tera)</li>
<li>Document templates</li>
<li>Export templates</li>
<li>Custom templates</li>
</ul>
<h3 id="9-cli-reference-cli"><a class="header" href="#9-cli-reference-cli">9. <strong>CLI Reference</strong> (<code>cli/</code>)</a></h3>
<ul>
<li>All kb-cli commands</li>
<li>Common workflows</li>
<li>Advanced usage</li>
<li>Troubleshooting</li>
</ul>
<h3 id="10-apps--integrations-apps"><a class="header" href="#10-apps--integrations-apps">10. <strong>Apps &amp; Integrations</strong> (<code>apps/</code>)</a></h3>
<ul>
<li>MCP quick guide (Claude Code)</li>
<li>Logseq integration</li>
<li>Vapora integration</li>
</ul>
<h3 id="11-api-reference-api"><a class="header" href="#11-api-reference-api">11. <strong>API Reference</strong> (<code>api/</code>)</a></h3>
<ul>
<li>MCP tools specification</li>
<li>Storage trait</li>
<li>Embedding trait</li>
<li>REST API (future)</li>
</ul>
<h3 id="12-contributing-contributing"><a class="header" href="#12-contributing-contributing">12. <strong>Contributing</strong> (<code>contributing/</code>)</a></h3>
<ul>
<li>Development setup</li>
<li>Code guidelines</li>
<li>Testing standards</li>
<li>Documentation guidelines</li>
</ul>
<h2 id="-visual-diagrams"><a class="header" href="#-visual-diagrams">🎨 Visual Diagrams</a></h2>
<p>The documentation includes SVG diagrams for visual understanding:</p>
<ul>
<li><strong><a href="diagrams/architecture-overview.svg">architecture-overview.svg</a></strong> - Complete system architecture</li>
<li><strong><a href="diagrams/core-concepts.svg">core-concepts.svg</a></strong> - Node types and relationships</li>
<li><strong><a href="diagrams/config-composition.svg">config-composition.svg</a></strong> - Configuration flow (Nickel → JSON → Rust)</li>
<li><strong><a href="diagrams/storage-architecture.svg">storage-architecture.svg</a></strong> - Hybrid storage strategy</li>
</ul>
<p>These diagrams are embedded in relevant documentation pages and can be viewed standalone in a browser.</p>
<h2 id="-searching-the-documentation"><a class="header" href="#-searching-the-documentation">🔍 Searching the Documentation</a></h2>
<p>When using <code>just docs::serve</code>, you get a built-in search feature:</p>
<ol>
<li>Click the search icon (🔍) in the top-left corner</li>
<li>Type your query</li>
<li>Press Enter to navigate results</li>
</ol>
<p>The search indexes all documentation content including:</p>
<ul>
<li>Page titles</li>
<li>Headers</li>
<li>Body text</li>
<li>Code examples (optionally)</li>
</ul>
<h2 id="-editing-documentation"><a class="header" href="#-editing-documentation">✏️ Editing Documentation</a></h2>
<h3 id="file-format"><a class="header" href="#file-format">File Format</a></h3>
<p>All documentation is written in <strong>GitHub Flavored Markdown</strong> with mdBook extensions.</p>
<p>See <a href="contributing/documentation.html">contributing/documentation.md</a> for detailed editing guidelines.</p>
<h3 id="adding-a-new-page"><a class="header" href="#adding-a-new-page">Adding a New Page</a></h3>
<ol>
<li>Create the markdown file in the appropriate directory</li>
<li>Add it to <code>SUMMARY.md</code> for navigation</li>
<li>Build to verify: <code>just docs::build</code></li>
</ol>
<h3 id="adding-a-new-section"><a class="header" href="#adding-a-new-section">Adding a New Section</a></h3>
<ol>
<li>Create the directory</li>
<li>Add a <code>README.md</code> for the section landing page</li>
<li>Add section to <code>SUMMARY.md</code></li>
</ol>
<h2 id="-testing-documentation"><a class="header" href="#-testing-documentation">🧪 Testing Documentation</a></h2>
<h3 id="test-code-examples"><a class="header" href="#test-code-examples">Test Code Examples</a></h3>
<pre><code class="language-bash">just docs::test
</code></pre>
<p>This runs all Rust code examples in the documentation to ensure they compile.</p>
<h3 id="check-links"><a class="header" href="#check-links">Check Links</a></h3>
<pre><code class="language-bash">just docs::check-links
</code></pre>
<p>This validates all internal and external links.</p>
<h2 id="-documentation-standards"><a class="header" href="#-documentation-standards">📝 Documentation Standards</a></h2>
<p>When contributing to documentation:</p>
<ol>
<li><strong>Use clear, concise language</strong> - Write for developers and AI agents</li>
<li><strong>Include code examples</strong> - Show, don't just tell</li>
<li><strong>Add diagrams where helpful</strong> - Visual aids improve understanding</li>
<li><strong>Link related concepts</strong> - Help readers discover related content</li>
<li><strong>Test code examples</strong> - Ensure code compiles and works</li>
<li><strong>Use consistent formatting</strong> - Follow existing page structure</li>
<li><strong>Update SUMMARY.md</strong> - New pages must be in navigation</li>
<li><strong>Run checks before committing</strong>:</li>
</ol>
<pre><code class="language-bash">just docs::build
just docs::test
just docs::check-links
</code></pre>
<h2 id="-tips"><a class="header" href="#-tips">💡 Tips</a></h2>
<h3 id="live-reload-while-writing"><a class="header" href="#live-reload-while-writing">Live Reload While Writing</a></h3>
<pre><code class="language-bash">just docs::watch
</code></pre>
<p>This watches for changes and rebuilds automatically. Open http://localhost:3000 in your browser to see updates in real-time.</p>
<h3 id="markdown-preview-in-editor"><a class="header" href="#markdown-preview-in-editor">Markdown Preview in Editor</a></h3>
<p>Most editors have Markdown preview:</p>
<ul>
<li><strong>VS Code</strong>: <code>Ctrl+Shift+V</code> (Cmd+Shift+V on Mac)</li>
<li><strong>IntelliJ/CLion</strong>: Preview pane (right side)</li>
<li><strong>Vim/Neovim</strong>: Use plugins like <code>markdown-preview.nvim</code></li>
</ul>
<h3 id="quick-reference"><a class="header" href="#quick-reference">Quick Reference</a></h3>
<ul>
<li><strong>SUMMARY.md</strong> - Table of contents (edit to add/remove pages)</li>
<li><strong>book.toml</strong> - mdBook configuration</li>
<li><strong>theme/</strong> - Custom CSS/JS (if needed)</li>
<li><strong>diagrams/</strong> - SVG diagrams</li>
</ul>
<h2 id="-troubleshooting"><a class="header" href="#-troubleshooting">🐛 Troubleshooting</a></h2>
<h3 id="mdbook-command-not-found"><a class="header" href="#mdbook-command-not-found">mdbook command not found</a></h3>
<pre><code class="language-bash"># Install mdBook
cargo install mdbook
# Verify installation
mdbook --version
</code></pre>
<h3 id="port-3000-already-in-use"><a class="header" href="#port-3000-already-in-use">Port 3000 already in use</a></h3>
<pre><code class="language-bash"># Serve on different port
cd docs
mdbook serve --port 3001
</code></pre>
<h3 id="links-broken-after-moving-files"><a class="header" href="#links-broken-after-moving-files">Links broken after moving files</a></h3>
<pre><code class="language-bash"># Check all links
just docs::check-links
# Update internal links in affected files
# Then rebuild
just docs::build
</code></pre>
<h2 id="-resources"><a class="header" href="#-resources">📚 Resources</a></h2>
<ul>
<li><a href="https://rust-lang.github.io/mdBook/">mdBook User Guide</a></li>
<li><a href="https://github.github.com/gfm/">GitHub Flavored Markdown Spec</a></li>
<li><a href="https://www.markdownguide.org/">Markdown Guide</a></li>
</ul>
<h2 id="-contributing-to-documentation"><a class="header" href="#-contributing-to-documentation">🤝 Contributing to Documentation</a></h2>
<p>Documentation improvements are always welcome! To contribute:</p>
<ol>
<li>Fork the repository</li>
<li>Create a feature branch</li>
<li>Make your changes</li>
<li>Test with <code>just docs::build</code> and <code>just docs::test</code></li>
<li>Submit a pull request</li>
</ol>
<p>See <a href="contributing/documentation.html">contributing/documentation.md</a> for detailed guidelines.</p>
<hr />
<p><strong>Happy documenting! 📖</strong></p>
<p>If you have questions or need help, please open an issue or reach out to the maintainers.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="what-is-kogral"><a class="header" href="#what-is-kogral">What is KOGRAL?</a></h1>
<p>KOGRAL (<strong>KO</strong>wledge <strong>GRA</strong>phs, <strong>L</strong>ocal-first) is a <strong>git-native knowledge graph system</strong> designed for developer teams to capture, connect, and query structured knowledge.</p>
<h2 id="purpose"><a class="header" href="#purpose">Purpose</a></h2>
<p>KOGRAL solves the problem of <strong>knowledge fragmentation</strong> in software development:</p>
<ul>
<li>📝 Notes scattered across tools</li>
<li>🤔 Decisions lost in chat histories</li>
<li>📚 Guidelines buried in wikis</li>
<li>🔄 Patterns rediscovered repeatedly</li>
<li>🤖 AI agents lacking project context</li>
</ul>
<h2 id="solution"><a class="header" href="#solution">Solution</a></h2>
<p>KOGRAL provides a <strong>unified, queryable knowledge graph</strong> that:</p>
<ol>
<li><strong>Captures</strong> knowledge in structured, git-friendly markdown</li>
<li><strong>Connects</strong> concepts through typed relationships</li>
<li><strong>Queries</strong> via text and semantic similarity</li>
<li><strong>Integrates</strong> with AI tools (Claude Code via MCP)</li>
<li><strong>Syncs</strong> across local (filesystem) and shared (SurrealDB) storage</li>
</ol>
<h2 id="core-philosophy"><a class="header" href="#core-philosophy">Core Philosophy</a></h2>
<h3 id="config-driven"><a class="header" href="#config-driven">Config-Driven</a></h3>
<p>Behavior defined in Nickel schemas, not hardcoded:</p>
<pre><code class="language-nickel">{
graph = { name = "my-project" },
storage = { primary = 'filesystem },
embeddings = { provider = 'fastembed },
templates = { templates_dir = "templates" },
}
</code></pre>
<p>Every aspect configurable: storage, embeddings, templates, query behavior.</p>
<h3 id="git-friendly"><a class="header" href="#git-friendly">Git-Friendly</a></h3>
<p>Knowledge stored as markdown files with YAML frontmatter:</p>
<pre><code class="language-markdown">---
id: note-123
type: note
title: Error Handling Patterns
tags: [rust, error-handling]
---
# Error Handling Patterns
Use `thiserror` for custom error types...
</code></pre>
<p>Changes tracked via git, reviewable in PRs, mergeable across branches.</p>
<h3 id="ai-native"><a class="header" href="#ai-native">AI-Native</a></h3>
<p>Built for agent collaboration:</p>
<ul>
<li><strong>MCP Protocol</strong>: Native integration with Claude Code</li>
<li><strong>Semantic Search</strong>: Find concepts, not just keywords</li>
<li><strong>Auto-Linking</strong>: Suggest relationships based on content</li>
<li><strong>Context Injection</strong>: Agents query relevant guidelines before coding</li>
</ul>
<h2 id="key-concepts"><a class="header" href="#key-concepts">Key Concepts</a></h2>
<h3 id="nodes"><a class="header" href="#nodes">Nodes</a></h3>
<p>6 types of knowledge nodes:</p>
<div class="table-wrapper"><table><thead><tr><th>Type</th><th>Purpose</th><th>Example</th></tr></thead><tbody>
<tr><td><strong>Note</strong></td><td>General observations</td><td>"Rust ownership patterns"</td></tr>
<tr><td><strong>Decision</strong></td><td>ADRs (Architectural Decision Records)</td><td>"Use SurrealDB for storage"</td></tr>
<tr><td><strong>Guideline</strong></td><td>Code standards</td><td>"Error handling with thiserror"</td></tr>
<tr><td><strong>Pattern</strong></td><td>Reusable solutions</td><td>"Repository pattern for DB access"</td></tr>
<tr><td><strong>Journal</strong></td><td>Daily reflections</td><td>"2026-01-17 progress notes"</td></tr>
<tr><td><strong>Execution</strong></td><td>Agent task records</td><td>"Implemented auth module"</td></tr>
</tbody></table>
</div>
<h3 id="relationships"><a class="header" href="#relationships">Relationships</a></h3>
<p>6 typed edges connecting nodes:</p>
<div class="table-wrapper"><table><thead><tr><th>Relation</th><th>Meaning</th><th>Example</th></tr></thead><tbody>
<tr><td><code>relates_to</code></td><td>Conceptual link</td><td>Note ↔ Note</td></tr>
<tr><td><code>depends_on</code></td><td>Prerequisite</td><td>Pattern → Guideline</td></tr>
<tr><td><code>implements</code></td><td>Concrete realization</td><td>Code → Pattern</td></tr>
<tr><td><code>extends</code></td><td>Inheritance</td><td>ProjectGuideline → BaseGuideline</td></tr>
<tr><td><code>supersedes</code></td><td>Replacement</td><td>DecisionV2 → DecisionV1</td></tr>
<tr><td><code>explains</code></td><td>Documentation</td><td>Note → Execution</td></tr>
</tbody></table>
</div>
<h3 id="multi-graph-architecture"><a class="header" href="#multi-graph-architecture">Multi-Graph Architecture</a></h3>
<p><strong>Local Graph</strong> (per project):</p>
<ul>
<li>Stored in <code>.kogral/</code> directory</li>
<li>Git-tracked for version control</li>
<li>Project-specific knowledge</li>
</ul>
<p><strong>Shared Graph</strong> (organization-wide):</p>
<ul>
<li>Centralized guidelines and patterns</li>
<li>SurrealDB for scalability</li>
<li>Inherited by projects</li>
</ul>
<p><strong>Inheritance Resolution</strong>:</p>
<pre><code>Shared Guidelines (priority: 50)
Project Guidelines (priority: 100)
Effective Guidelines (higher priority wins)
</code></pre>
<h2 id="use-cases"><a class="header" href="#use-cases">Use Cases</a></h2>
<h3 id="for-developers"><a class="header" href="#for-developers">For Developers</a></h3>
<ul>
<li><strong>Capture decisions</strong> as you make them (ADRs)</li>
<li><strong>Document patterns</strong> for future reference</li>
<li><strong>Track daily progress</strong> in journal entries</li>
<li><strong>Query past decisions</strong> before new implementations</li>
</ul>
<h3 id="for-teams"><a class="header" href="#for-teams">For Teams</a></h3>
<ul>
<li><strong>Share guidelines</strong> across projects</li>
<li><strong>Standardize patterns</strong> organization-wide</li>
<li><strong>Onboard new members</strong> with searchable knowledge</li>
<li><strong>Review decisions</strong> in context with git history</li>
</ul>
<h3 id="for-ai-agents"><a class="header" href="#for-ai-agents">For AI Agents</a></h3>
<ul>
<li><strong>Query guidelines</strong> before generating code</li>
<li><strong>Check past decisions</strong> for context</li>
<li><strong>Document executions</strong> for audit trails</li>
<li><strong>Suggest related patterns</strong> during implementation</li>
</ul>
<h2 id="comparison-with-other-tools"><a class="header" href="#comparison-with-other-tools">Comparison with Other Tools</a></h2>
<h3 id="vs-logseq"><a class="header" href="#vs-logseq">vs. Logseq</a></h3>
<div class="table-wrapper"><table><thead><tr><th>Feature</th><th>KOGRAL</th><th>Logseq</th></tr></thead><tbody>
<tr><td><strong>Storage</strong></td><td>Git-friendly markdown + DB</td><td>Local markdown</td></tr>
<tr><td><strong>AI Integration</strong></td><td>Native MCP protocol</td><td>Plugin-based</td></tr>
<tr><td><strong>Config</strong></td><td>Type-safe Nickel schemas</td><td>JSON files</td></tr>
<tr><td><strong>Multi-Backend</strong></td><td>Filesystem + SurrealDB</td><td>Filesystem only</td></tr>
<tr><td><strong>Semantic Search</strong></td><td>Multiple AI providers</td><td>Local only</td></tr>
<tr><td><strong>Graph Queries</strong></td><td>SurrealDB graph queries</td><td>Block references</td></tr>
</tbody></table>
</div>
<p><strong>Compatibility</strong>: KOGRAL can import/export Logseq graphs for visual editing.</p>
<h3 id="vs-obsidian"><a class="header" href="#vs-obsidian">vs. Obsidian</a></h3>
<div class="table-wrapper"><table><thead><tr><th>Feature</th><th>KOGRAL</th><th>Obsidian</th></tr></thead><tbody>
<tr><td><strong>Target Audience</strong></td><td>Developers + AI agents</td><td>Knowledge workers</td></tr>
<tr><td><strong>Focus</strong></td><td>Structured knowledge graph</td><td>Flexible note-taking</td></tr>
<tr><td><strong>Configuration</strong></td><td>Config-driven (Nickel)</td><td>Settings UI</td></tr>
<tr><td><strong>CLI</strong></td><td>Full CLI + MCP server</td><td>Limited CLI</td></tr>
<tr><td><strong>Version Control</strong></td><td>Git-native</td><td>Git plugin</td></tr>
</tbody></table>
</div>
<p><strong>Use Case</strong>: KOGRAL for developer knowledge, Obsidian for personal notes.</p>
<h3 id="vs-notionconfluence"><a class="header" href="#vs-notionconfluence">vs. Notion/Confluence</a></h3>
<div class="table-wrapper"><table><thead><tr><th>Feature</th><th>KOGRAL</th><th>Notion/Confluence</th></tr></thead><tbody>
<tr><td><strong>Storage</strong></td><td>Local-first</td><td>Cloud-only</td></tr>
<tr><td><strong>Format</strong></td><td>Plain markdown</td><td>Proprietary</td></tr>
<tr><td><strong>AI Access</strong></td><td>Programmatic API</td><td>Web scraping</td></tr>
<tr><td><strong>Offline</strong></td><td>Full functionality</td><td>Limited</td></tr>
<tr><td><strong>Privacy</strong></td><td>Self-hosted</td><td>Third-party servers</td></tr>
</tbody></table>
</div>
<p><strong>Advantage</strong>: KOGRAL keeps sensitive knowledge on-premises.</p>
<h2 id="architecture-overview"><a class="header" href="#architecture-overview">Architecture Overview</a></h2>
<pre><code>┌─────────────────────────────────────────────────────┐
│ kb-core │
│ (Rust library: graph, storage, config, query) │
└──────────────┬─────────────┬────────────────────────┘
│ │
┌─────────┴──────┐ ┌──┴─────────┐
│ │ │ │
┌────▼─────┐ ┌──────▼──▼───┐ ┌─────▼─────┐
│ kb-cli │ │ kb-mcp │ │ NuShell │
│ (13 cmds)│ │ (MCP server) │ │ (scripts) │
└──────────┘ └──────────────┘ └───────────┘
</code></pre>
<p><strong>Layers</strong>:</p>
<ol>
<li><strong>kb-core</strong>: Core library (models, storage, query)</li>
<li><strong>kb-cli</strong>: Command-line interface</li>
<li><strong>kb-mcp</strong>: MCP server for AI integration</li>
<li><strong>Scripts</strong>: NuShell automation (sync, backup, etc.)</li>
</ol>
<h2 id="when-to-use-kogral"><a class="header" href="#when-to-use-kogral">When to Use KOGRAL</a></h2>
<p><strong>Good fit</strong>:</p>
<ul>
<li>Developer teams building software</li>
<li>Projects with architectural decisions</li>
<li>Organizations standardizing patterns</li>
<li>AI-assisted development workflows</li>
<li>Knowledge requiring version control</li>
</ul>
<p><strong>Not ideal for</strong>:</p>
<ul>
<li>Personal journaling (use Obsidian)</li>
<li>Team wikis (use Notion)</li>
<li>Customer documentation (use mdBook)</li>
<li>Simple note-taking (use Logseq)</li>
</ul>
<h2 id="next-steps"><a class="header" href="#next-steps">Next Steps</a></h2>
<ul>
<li><strong>New to KOGRAL?</strong><a href="kogral/../guides/installation.html">Installation Guide</a></li>
<li><strong>Ready to start?</strong><a href="kogral/../guides/quickstart.html">Quick Start</a></li>
<li><strong>Want examples?</strong><a href="kogral/../guides/use-cases.html">Use Cases</a></li>
<li><strong>Understand design?</strong><a href="kogral/../architecture/overview.html">Architecture Overview</a></li>
</ul>
<hr />
<blockquote>
<p>"Knowledge is only valuable when it's accessible, connected, and queryable."</p>
</blockquote>
<div style="break-before: page; page-break-before: always;"></div><h1 id="core-concepts"><a class="header" href="#core-concepts">Core Concepts</a></h1>
<p>Understanding the fundamental concepts behind KOGRAL.</p>
<h2 id="the-knowledge-graph"><a class="header" href="#the-knowledge-graph">The Knowledge Graph</a></h2>
<p>At its core, KOGRAL is a <strong>directed graph</strong> where:</p>
<ul>
<li><strong>Nodes</strong> = pieces of knowledge (notes, decisions, guidelines, patterns)</li>
<li><strong>Edges</strong> = typed relationships between concepts</li>
</ul>
<p><img src="kogral/../diagrams/core-concepts.svg" alt="Node Types and Relationships" /></p>
<p>This graph structure enables:</p>
<ul>
<li><strong>Discovery</strong>: Find related concepts through traversal</li>
<li><strong>Context</strong>: Understand how ideas connect</li>
<li><strong>Evolution</strong>: Track how knowledge changes over time</li>
</ul>
<h2 id="node-types"><a class="header" href="#node-types">Node Types</a></h2>
<h3 id="1-note"><a class="header" href="#1-note">1. Note</a></h3>
<p><strong>Purpose</strong>: Capture general observations, learnings, and discoveries.</p>
<p><strong>When to use</strong>:</p>
<ul>
<li>Documenting a concept you learned</li>
<li>Recording implementation details</li>
<li>Capturing meeting notes</li>
<li>Quick knowledge capture</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code class="language-yaml">---
type: note
title: Async Trait Patterns in Rust
tags: [rust, async, patterns]
---
# Async Trait Patterns in Rust
Using async traits with the async-trait crate...
</code></pre>
<h3 id="2-decision-adr"><a class="header" href="#2-decision-adr">2. Decision (ADR)</a></h3>
<p><strong>Purpose</strong>: Record architectural decisions with full context.</p>
<p><strong>When to use</strong>:</p>
<ul>
<li>Choosing between alternatives (REST vs GraphQL)</li>
<li>Major technical decisions (database selection)</li>
<li>Trade-off analysis</li>
<li>Explaining "why" for future reference</li>
</ul>
<p><strong>Structure</strong>:</p>
<ul>
<li><strong>Context</strong>: Background and problem</li>
<li><strong>Decision</strong>: What was chosen</li>
<li><strong>Consequences</strong>: Positive and negative outcomes</li>
<li><strong>Alternatives</strong>: What was considered but rejected</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code class="language-yaml">---
type: decision
title: Use SurrealDB for Storage
status: accepted
---
## Context
Need a graph database that supports our relationship model...
## Decision
Adopt SurrealDB as the primary storage backend.
## Consequences
+ Better graph query performance
+ Native relationship support
- Additional infrastructure dependency
- Team learning curve
</code></pre>
<h3 id="3-guideline"><a class="header" href="#3-guideline">3. Guideline</a></h3>
<p><strong>Purpose</strong>: Define coding standards, best practices, and conventions.</p>
<p><strong>When to use</strong>:</p>
<ul>
<li>Code style rules</li>
<li>Architecture patterns to follow</li>
<li>Security requirements</li>
<li>Testing standards</li>
</ul>
<p><strong>Can be</strong>:</p>
<ul>
<li><strong>Shared</strong>: Organization-wide (in shared KOGRAL)</li>
<li><strong>Project-specific</strong>: Overrides shared guidelines</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code class="language-yaml">---
type: guideline
language: rust
category: error-handling
---
# Rust Error Handling Guidelines
1. Use `thiserror` for custom error types
2. Never use `unwrap()` in production code
3. Always propagate errors with `?`
</code></pre>
<h3 id="4-pattern"><a class="header" href="#4-pattern">4. Pattern</a></h3>
<p><strong>Purpose</strong>: Document reusable solutions to common problems.</p>
<p><strong>When to use</strong>:</p>
<ul>
<li>Recurring implementation patterns</li>
<li>Tested solutions</li>
<li>Best practice implementations</li>
<li>Code templates</li>
</ul>
<p><strong>Structure</strong>:</p>
<ul>
<li><strong>Problem</strong>: What challenge does this solve?</li>
<li><strong>Solution</strong>: The pattern/approach</li>
<li><strong>Context</strong>: When to use/not use</li>
<li><strong>Example</strong>: Working code</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code class="language-yaml">---
type: pattern
title: Repository Pattern for Database Access
tags: [architecture, database, pattern]
---
## Problem
Need consistent database access across modules.
## Solution
Repository pattern with trait abstraction...
## Example
\`\`\`rust
trait UserRepository {
async fn find_by_id(&amp;self, id: Uuid) -&gt; Result&lt;User&gt;;
}
\`\`\`
</code></pre>
<h3 id="5-journal"><a class="header" href="#5-journal">5. Journal</a></h3>
<p><strong>Purpose</strong>: Daily development log for tracking progress and reflections.</p>
<p><strong>When to use</strong>:</p>
<ul>
<li>End of day summaries</li>
<li>Daily standup notes</li>
<li>Progress tracking</li>
<li>Blocker documentation</li>
</ul>
<p><strong>Auto-linked</strong>: KOGRAL can auto-link journal entries to mentioned concepts.</p>
<p><strong>Example</strong>:</p>
<pre><code class="language-yaml">---
type: journal
date: 2026-01-17
---
## Progress
- Implemented authentication module
- Fixed cache race condition
## Blockers
- Need API versioning discussion
## Learnings
- tokio::select! perfect for timeouts
</code></pre>
<h3 id="6-execution"><a class="header" href="#6-execution">6. Execution</a></h3>
<p><strong>Purpose</strong>: Record AI agent execution results (from Vapora integration).</p>
<p><strong>When to use</strong>:</p>
<ul>
<li>Agent task completion</li>
<li>Execution metrics</li>
<li>Agent decision history</li>
<li>Audit trail</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code class="language-yaml">---
type: execution
task_type: code_generation
agent: rust-expert
outcome: success
duration: 45s
---
Generated authentication module following project guidelines.
</code></pre>
<h2 id="relationship-types"><a class="header" href="#relationship-types">Relationship Types</a></h2>
<h3 id="1-relates_to"><a class="header" href="#1-relates_to">1. <code>relates_to</code></a></h3>
<p><strong>Meaning</strong>: General conceptual relationship.</p>
<p><strong>Use</strong>: Connect related ideas without specific dependency.</p>
<p><strong>Example</strong>:</p>
<pre><code>[Note: Async Patterns] --relates_to--&gt; [Note: Tokio Runtime]
</code></pre>
<h3 id="2-depends_on"><a class="header" href="#2-depends_on">2. <code>depends_on</code></a></h3>
<p><strong>Meaning</strong>: Prerequisite relationship. Source requires target to exist/be understood first.</p>
<p><strong>Use</strong>: Learning paths, implementation order.</p>
<p><strong>Example</strong>:</p>
<pre><code>[Pattern: Advanced Error Handling] --depends_on--&gt; [Guideline: Basic Errors]
</code></pre>
<h3 id="3-implements"><a class="header" href="#3-implements">3. <code>implements</code></a></h3>
<p><strong>Meaning</strong>: Concrete implementation of an abstract concept.</p>
<p><strong>Use</strong>: Connect code to patterns/guidelines.</p>
<p><strong>Example</strong>:</p>
<pre><code>[Note: Auth Module Implementation] --implements--&gt; [Pattern: Repository Pattern]
</code></pre>
<h3 id="4-extends"><a class="header" href="#4-extends">4. <code>extends</code></a></h3>
<p><strong>Meaning</strong>: Inheritance/extension relationship.</p>
<p><strong>Use</strong>: Guideline overrides, pattern variations.</p>
<p><strong>Example</strong>:</p>
<pre><code>[Guideline: Project Error Handling] --extends--&gt; [Guideline: Shared Error Handling]
</code></pre>
<h3 id="5-supersedes"><a class="header" href="#5-supersedes">5. <code>supersedes</code></a></h3>
<p><strong>Meaning</strong>: Replacement relationship. Source replaces target.</p>
<p><strong>Use</strong>: Track evolution of decisions/patterns.</p>
<p><strong>Example</strong>:</p>
<pre><code>[Decision: Use GraphQL v2] --supersedes--&gt; [Decision: Use REST]
</code></pre>
<h3 id="6-explains"><a class="header" href="#6-explains">6. <code>explains</code></a></h3>
<p><strong>Meaning</strong>: Documentation/clarification relationship.</p>
<p><strong>Use</strong>: Connect notes to implementations, executions to rationale.</p>
<p><strong>Example</strong>:</p>
<pre><code>[Note: Why We Chose Rust] --explains--&gt; [Decision: Adopt Rust]
</code></pre>
<h2 id="multi-graph-architecture-1"><a class="header" href="#multi-graph-architecture-1">Multi-Graph Architecture</a></h2>
<p>KB supports <strong>multiple knowledge graphs</strong>:</p>
<h3 id="local-graph-project-specific"><a class="header" href="#local-graph-project-specific">Local Graph (Project-Specific)</a></h3>
<p><strong>Location</strong>: <code>.kogral/</code> in project directory</p>
<p><strong>Purpose</strong>: Project-specific knowledge</p>
<ul>
<li>Project decisions</li>
<li>Implementation notes</li>
<li>Local patterns</li>
<li>Daily journals</li>
</ul>
<p><strong>Storage</strong>: Filesystem (git-tracked)</p>
<p><strong>Scope</strong>: Single project</p>
<h3 id="shared-graph-organization-wide"><a class="header" href="#shared-graph-organization-wide">Shared Graph (Organization-Wide)</a></h3>
<p><strong>Location</strong>: Configurable (e.g., <code>~/org/.kogral-shared</code>)</p>
<p><strong>Purpose</strong>: Shared organizational knowledge</p>
<ul>
<li>Coding guidelines</li>
<li>Standard patterns</li>
<li>Architecture principles</li>
<li>Security policies</li>
</ul>
<p><strong>Storage</strong>: SurrealDB (centralized) or filesystem (synced)</p>
<p><strong>Scope</strong>: All projects</p>
<h3 id="inheritance-model"><a class="header" href="#inheritance-model">Inheritance Model</a></h3>
<pre><code>Shared Guidelines (priority: 50)
↓ [inherited by]
Project Guidelines (priority: 100)
↓ [effective]
Combined Guidelines (higher priority wins)
</code></pre>
<p><strong>Example</strong>:</p>
<p>Shared guideline: "Use Result<T> for errors"
Project override: "Use Result<T> + log all errors with tracing"
Effective: Both rules apply, project adds requirement</p>
<h2 id="config-driven-behavior"><a class="header" href="#config-driven-behavior">Config-Driven Behavior</a></h2>
<p><strong>Everything is configurable</strong> via Nickel schemas:</p>
<pre><code class="language-nickel">{
graph = { name = "my-project" }, # Graph metadata
storage = { primary = 'filesystem }, # Where to store
embeddings = { provider = 'fastembed }, # AI provider
templates = { /* ... */ }, # Document templates
query = { similarity_threshold = 0.6 }, # Search behavior
}
</code></pre>
<p><strong>No hardcoding</strong>: Change behavior without code changes.</p>
<h2 id="semantic-search"><a class="header" href="#semantic-search">Semantic Search</a></h2>
<p>Beyond keyword matching, KOGRAL uses <strong>embeddings</strong> to find concepts:</p>
<p><strong>Keyword search</strong>: "Find 'error handling'"</p>
<ul>
<li>Matches exact phrase "error handling"</li>
</ul>
<p><strong>Semantic search</strong>: "How to handle failures gracefully?"</p>
<ul>
<li>Finds: error handling, exception patterns, Result types, panic recovery</li>
<li>Understands: "failures" = errors, "gracefully" = best practices</li>
</ul>
<p><strong>How it works</strong>:</p>
<ol>
<li>Text → Embedding (384 or 1536 dimensional vector)</li>
<li>Similarity search (cosine distance)</li>
<li>Return nodes above threshold (e.g., 0.6)</li>
</ol>
<p><strong>Providers</strong>:</p>
<ul>
<li><strong>fastembed</strong>: Local, free, offline (384d)</li>
<li><strong>OpenAI</strong>: Cloud, best quality (1536d)</li>
<li><strong>Claude</strong>: Cloud, excellent (1024d)</li>
<li><strong>Ollama</strong>: Self-hosted (768d)</li>
</ul>
<h2 id="templates"><a class="header" href="#templates">Templates</a></h2>
<p><strong>Tera templates</strong> generate consistent documents:</p>
<pre><code class="language-jinja2">---
id: {{ id }}
type: {{ type }}
title: {{ title }}
tags: [{% for tag in tags %}"{{ tag }}"{% endfor %}]
---
# {{ title }}
{{ content }}
</code></pre>
<p><strong>Customizable</strong>: Override templates per project.</p>
<p><strong>Export formats</strong>: Logseq, JSON, Markdown.</p>
<h2 id="mcp-integration"><a class="header" href="#mcp-integration">MCP Integration</a></h2>
<p><strong>Model Context Protocol</strong> connects KOGRAL to Claude Code:</p>
<pre><code>Claude Code
↓ [JSON-RPC 2.0]
kb-mcp Server
↓ [Rust API]
kb-core Library
↓ [Storage]
Knowledge Graph
</code></pre>
<p><strong>Tools</strong>: <code>kogral/search</code>, <code>kogral/add_note</code>, <code>kogral/get_guidelines</code>, etc.</p>
<p><strong>Resources</strong>: <code>kogral://project/notes</code>, <code>kogral://shared/guidelines</code></p>
<p><strong>Prompts</strong>: <code>kogral/summarize_project</code>, <code>kogral/find_related</code></p>
<h2 id="git-friendly-storage"><a class="header" href="#git-friendly-storage">Git-Friendly Storage</a></h2>
<p><strong>Markdown + YAML frontmatter</strong> = git-tracked knowledge:</p>
<pre><code class="language-markdown">---
id: note-123
type: note
title: My Note
---
# Content here
</code></pre>
<p><strong>Benefits</strong>:</p>
<ul>
<li>✅ Diffs in PRs (reviewable changes)</li>
<li>✅ Branches (experiment with knowledge)</li>
<li>✅ Merges (combine knowledge from feature branches)</li>
<li>✅ History (track evolution over time)</li>
<li>✅ Blame (who added this knowledge?)</li>
</ul>
<h2 id="logseq-content-blocks"><a class="header" href="#logseq-content-blocks">Logseq Content Blocks</a></h2>
<p>KOGRAL provides full support for <strong>Logseq-style outliner blocks</strong> with rich metadata and structure.</p>
<h3 id="what-are-blocks"><a class="header" href="#what-are-blocks">What are Blocks?</a></h3>
<p>Blocks are the fundamental unit of content in Logseq's outliner format:</p>
<ul>
<li>Each bullet point is a block</li>
<li>Blocks can have children (nested blocks)</li>
<li>Blocks support tasks, tags, and custom properties</li>
<li>Blocks can reference other blocks or pages</li>
</ul>
<h3 id="block-features"><a class="header" href="#block-features">Block Features</a></h3>
<p><strong>Task Status</strong>:</p>
<pre><code class="language-markdown">- TODO Implement authentication #high-priority
- DOING Write tests
- DONE Deploy to staging
- LATER Consider GraphQL API
- NOW Fix critical bug
- WAITING Code review from @alice
- CANCELLED Old approach
</code></pre>
<p><strong>Inline Tags</strong>:</p>
<pre><code class="language-markdown">- Learning Rust ownership #rust #learning #card
- Prevents data races at compile time
- Borrowing rules enforce safety
</code></pre>
<p><strong>Custom Properties</strong>:</p>
<pre><code class="language-markdown">- Research paper summary
priority:: high
reviewed:: 2026-01-17
source:: https://example.com/paper.pdf
- Key findings...
</code></pre>
<p><strong>Block and Page References</strong>:</p>
<pre><code class="language-markdown">- Meeting notes from [[2026-01-17]]
- Discussed architecture ((block-ref-123))
- Action items: [[Project Roadmap]]
</code></pre>
<p><strong>Hierarchical Structure</strong>:</p>
<pre><code class="language-markdown">- Parent block #top-level
- Child block 1
- Nested child
- Child block 2
</code></pre>
<h3 id="configuration"><a class="header" href="#configuration">Configuration</a></h3>
<p>Blocks support is opt-in via configuration:</p>
<pre><code class="language-nickel">{
blocks = {
enabled = true, # Enable blocks parsing
parse_on_import = true, # Auto-parse from Logseq imports
serialize_on_export = true, # Serialize to outliner format
enable_mcp_tools = true, # Enable block-related MCP tools
},
}
</code></pre>
<h3 id="use-cases-1"><a class="header" href="#use-cases-1">Use Cases</a></h3>
<p><strong>1. Task Management</strong>:</p>
<pre><code class="language-markdown">- TODO Weekly sprint planning #meeting
- DONE Review last sprint
- DOING Plan current sprint
- TODO Assign tasks
</code></pre>
<p><strong>2. Flashcards (Spaced Repetition)</strong>:</p>
<pre><code class="language-markdown">- What is Rust's ownership model? #card #rust
- Ownership prevents data races at compile time
- Each value has a single owner
- When owner goes out of scope, value is dropped
</code></pre>
<p><strong>3. Knowledge Capture with Metadata</strong>:</p>
<pre><code class="language-markdown">- Tokio async runtime patterns #rust #async
category:: architecture
difficulty:: intermediate
- Use tokio::select! for concurrent operations
- spawn_blocking() for CPU-intensive work
</code></pre>
<p><strong>4. Linked Notes</strong>:</p>
<pre><code class="language-markdown">- Discussed [[ADR-001]] in architecture meeting
- Decided on SurrealDB
- See ((meeting-notes-block-id)) for details
</code></pre>
<h3 id="block-queries-mcp-tools"><a class="header" href="#block-queries-mcp-tools">Block Queries (MCP Tools)</a></h3>
<p>Query blocks across your knowledge base:</p>
<p><strong>Find blocks by tag</strong>:</p>
<pre><code class="language-json">{
"tool": "kogral/find_blocks",
"arguments": { "tag": "card" }
}
</code></pre>
<p><strong>Find all TODOs</strong>:</p>
<pre><code class="language-json">{
"tool": "kogral/find_todos",
"arguments": { "limit": 20 }
}
</code></pre>
<p><strong>Find flashcards</strong>:</p>
<pre><code class="language-json">{
"tool": "kogral/find_cards",
"arguments": { "limit": 10 }
}
</code></pre>
<p><strong>Find blocks by property</strong>:</p>
<pre><code class="language-json">{
"tool": "kogral/find_blocks",
"arguments": {
"property_key": "priority",
"property_value": "high"
}
}
</code></pre>
<h3 id="architecture"><a class="header" href="#architecture">Architecture</a></h3>
<p><strong>Hybrid Model</strong>:</p>
<ul>
<li>Content stored as markdown string (source of truth)</li>
<li>Blocks lazily parsed on first access</li>
<li>Cached block structure for fast queries</li>
<li>Bidirectional: markdown ↔ blocks</li>
</ul>
<p><strong>BlockParser</strong>:</p>
<ul>
<li>Parse outliner markdown → Block structures</li>
<li>Serialize Block structures → outliner markdown</li>
<li>Preserve all metadata (tags, status, properties, references)</li>
<li>Round-trip fidelity for Logseq compatibility</li>
</ul>
<p><strong>Storage</strong>:</p>
<ul>
<li>Filesystem: markdown with blocks inline</li>
<li>SurrealDB: dedicated <code>block</code> table with indexes</li>
<li>Indexes: tags, status, parent_id, full-text search</li>
</ul>
<p><strong>See Also</strong>:</p>
<ul>
<li><a href="kogral/../architecture/adrs/004-logseq-blocks-support.html">ADR-004: Logseq Blocks Support</a></li>
<li><a href="kogral/../architecture/logseq-blocks-design.html">Logseq Blocks Design</a></li>
<li><a href="kogral/../api/mcp-tools.html#block-tools">MCP Block Tools</a></li>
</ul>
<h2 id="key-principles"><a class="header" href="#key-principles">Key Principles</a></h2>
<ol>
<li><strong>Capture During Work</strong>: Don't wait, document as you go</li>
<li><strong>Link as You Learn</strong>: Connect related concepts immediately</li>
<li><strong>Query When Needed</strong>: AI-assisted discovery of relevant knowledge</li>
<li><strong>Evolve Over Time</strong>: Update decisions, supersede patterns</li>
<li><strong>Share Wisely</strong>: Shared guidelines, local specifics</li>
</ol>
<h2 id="next-steps-1"><a class="header" href="#next-steps-1">Next Steps</a></h2>
<ul>
<li><strong>Understand motivation</strong>: <a href="kogral/why-kogral.html">Why KOGRAL?</a></li>
<li><strong>Learn philosophy</strong>: <a href="kogral/design-philosophy.html">Design Philosophy</a></li>
<li><strong>See architecture</strong>: <a href="kogral/../architecture/overview.html">System Overview</a></li>
<li><strong>Start using</strong>: <a href="kogral/../guides/quickstart.html">Quick Start</a></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="why-kb"><a class="header" href="#why-kb">Why KB?</a></h1>
<p>Understanding the motivation behind the Knowledge Base system.</p>
<h2 id="the-problem-knowledge-fragmentation"><a class="header" href="#the-problem-knowledge-fragmentation">The Problem: Knowledge Fragmentation</a></h2>
<p>Modern software development generates enormous amounts of knowledge:</p>
<p>📝 <strong>Notes</strong>: Implementation details, learnings, discoveries
🤔 <strong>Decisions</strong>: Why we chose X over Y
📚 <strong>Guidelines</strong>: How we write code, architecture patterns
🔄 <strong>Patterns</strong>: Reusable solutions we've discovered
💭 <strong>Discussions</strong>: Slack threads, meeting notes, PR comments
🐛 <strong>Bug Fixes</strong>: How we solved issues</p>
<p><strong>But where does this knowledge live?</strong></p>
<ul>
<li>Meeting notes in Google Docs</li>
<li>Decisions buried in Slack threads</li>
<li>Guidelines in a wiki nobody updates</li>
<li>Patterns in someone's head</li>
<li>PR discussions lost in GitHub history</li>
<li>Bug solutions in closed Jira tickets</li>
</ul>
<p><strong>The result?</strong></p>
<p><strong>Rediscovering solutions</strong> to problems we've already solved
<strong>Inconsistent practices</strong> because guidelines aren't accessible
<strong>Slow onboarding</strong> as new developers lack context
<strong>Lost context</strong> when team members leave
<strong>Repeated mistakes</strong> because past lessons aren't preserved</p>
<h2 id="the-solution-unified-queryable-knowledge-graph"><a class="header" href="#the-solution-unified-queryable-knowledge-graph">The Solution: Unified, Queryable Knowledge Graph</a></h2>
<p>Knowledge Base provides a <strong>single source of truth</strong> for project knowledge:</p>
<p><strong>Capture once</strong>: Notes, decisions, guidelines, patterns
<strong>Connect concepts</strong>: Typed relationships between ideas
<strong>Query naturally</strong>: Text and semantic search
<strong>AI-assisted</strong>: Claude Code integration via MCP
<strong>Git-tracked</strong>: Version control for knowledge
<strong>Shared wisdom</strong>: Organization-wide guidelines + project-specific</p>
<h2 id="what-makes-kb-different"><a class="header" href="#what-makes-kb-different">What Makes KB Different?</a></h2>
<h3 id="1-git-native"><a class="header" href="#1-git-native">1. Git-Native</a></h3>
<p><strong>Other tools</strong>: Notion, Confluence, wikis in web apps</p>
<p><strong>KB</strong>: Markdown files in <code>.kogral/</code> directory</p>
<p><strong>Benefits</strong>:</p>
<pre><code class="language-bash"># Review knowledge changes in PRs
git diff .kogral/decisions/
# Branch knowledge with code
git checkout feature-branch
# .kogral/ follows the branch
# Merge knowledge from feature work
git merge feature-branch
# Knowledge merges like code
</code></pre>
<p><strong>Result</strong>: Knowledge versioned alongside code it describes.</p>
<h3 id="2-ai-native"><a class="header" href="#2-ai-native">2. AI-Native</a></h3>
<p><strong>Other tools</strong>: Manual search, browse folders</p>
<p><strong>KB</strong>: AI queries via Claude Code</p>
<p><strong>Example</strong>:</p>
<pre><code>You: "Find anything about error handling"
Claude: [Searches semantically, not just keywords]
Found 5 concepts:
- Pattern: Error Handling with thiserror
- Guideline: Result Type Best Practices
- Decision: Use anyhow for Application Errors
- Note: Custom Error Types
- Journal: Fixed error propagation bug
[All related, even without exact keyword match]
</code></pre>
<p><strong>Result</strong>: Find concepts, not just documents with keywords.</p>
<h3 id="3-config-driven"><a class="header" href="#3-config-driven">3. Config-Driven</a></h3>
<p><strong>Other tools</strong>: Hardcoded behavior, limited customization</p>
<p><strong>KB</strong>: Nickel schemas define everything</p>
<p><strong>Example</strong>:</p>
<pre><code class="language-nickel"># Development: local embeddings, no costs
{ embeddings = { provider = 'fastembed } }
# Production: cloud embeddings, best quality
{ embeddings = { provider = 'openai, model = "text-embedding-3-large" } }
</code></pre>
<p><strong>Result</strong>: Adapt behavior without code changes.</p>
<h3 id="4-multi-graph"><a class="header" href="#4-multi-graph">4. Multi-Graph</a></h3>
<p><strong>Other tools</strong>: One knowledge base per project</p>
<p><strong>KB</strong>: Shared organizational knowledge + project-specific</p>
<p><strong>Structure</strong>:</p>
<pre><code>Organization Shared KB
├── Rust Guidelines (applies to all projects)
├── Security Patterns (applies to all projects)
└── Testing Standards (applies to all projects)
Project A KB
├── Inherits shared guidelines
├── Project-specific decisions
└── Can override shared guidelines
Project B KB
├── Inherits same shared guidelines
├── Different project decisions
└── Different overrides
</code></pre>
<p><strong>Result</strong>: Consistency across organization, flexibility per project.</p>
<h3 id="5-structured-relationships"><a class="header" href="#5-structured-relationships">5. Structured Relationships</a></h3>
<p><strong>Other tools</strong>: Backlinks, tags, folders</p>
<p><strong>KB</strong>: Typed relationships with meaning</p>
<p><strong>Example</strong>:</p>
<pre><code>Pattern: Repository Pattern
↑ [implements]
Note: User Service Implementation
↑ [relates_to]
Decision: Use PostgreSQL
↑ [depends_on]
Guideline: Database Access Patterns
</code></pre>
<p><strong>Result</strong>: Understand how knowledge connects, not just that it connects.</p>
<h2 id="real-world-impact"><a class="header" href="#real-world-impact">Real-World Impact</a></h2>
<h3 id="before-kb"><a class="header" href="#before-kb">Before KB</a></h3>
<p><strong>New developer joins</strong>:</p>
<ol>
<li>Read outdated wiki (2 hours)</li>
<li>Ask teammates for context (1 hour per question × 10 questions)</li>
<li>Discover guidelines by reading code (days)</li>
<li>Miss important decisions (leads to mistakes)</li>
</ol>
<p><strong>Time to productivity</strong>: 2-4 weeks</p>
<h3 id="with-kb"><a class="header" href="#with-kb">With KB</a></h3>
<p><strong>New developer joins</strong>:</p>
<ol>
<li><code>kb search "architectural decisions"</code> → finds all ADRs</li>
<li>Ask Claude: "What are our coding guidelines?" → gets current guidelines with inheritance</li>
<li>Browse related notes via graph traversal</li>
<li>Full context available, no tribal knowledge</li>
</ol>
<p><strong>Time to productivity</strong>: 3-5 days</p>
<h3 id="before-kb-1"><a class="header" href="#before-kb-1">Before KB</a></h3>
<p><strong>Team makes decision</strong>:</p>
<ol>
<li>Discussion in Slack (lost after a week)</li>
<li>Someone documents in wiki (maybe)</li>
<li>6 months later: "Why did we choose X?" → nobody remembers</li>
<li>Re-debate the same decision</li>
</ol>
<h3 id="with-kb-1"><a class="header" href="#with-kb-1">With KB</a></h3>
<p><strong>Team makes decision</strong>:</p>
<ol>
<li>Discussion captured as ADR during meeting</li>
<li>Context, decision, consequences documented</li>
<li>Linked to related patterns and guidelines</li>
<li>6 months later: <code>kb show decision-use-x</code> → full context instantly</li>
</ol>
<h3 id="before-kb-2"><a class="header" href="#before-kb-2">Before KB</a></h3>
<p><strong>Solving a bug</strong>:</p>
<ol>
<li>Encounter race condition in cache</li>
<li>Debug for 2 hours</li>
<li>Fix it</li>
<li>Solution lost in PR comments</li>
</ol>
<p><strong>Two months later</strong>: Different developer, same bug, 2 more hours</p>
<h3 id="with-kb-2"><a class="header" href="#with-kb-2">With KB</a></h3>
<p><strong>Solving a bug</strong>:</p>
<ol>
<li>Encounter race condition</li>
<li>Ask Claude: "Have we seen cache race conditions before?"</li>
<li>Claude finds journal entry from 2 months ago with solution</li>
<li>Apply fix in 10 minutes</li>
</ol>
<p><strong>Two months later</strong>: Same query, same instant solution</p>
<h2 id="who-benefits"><a class="header" href="#who-benefits">Who Benefits?</a></h2>
<h3 id="individual-developers"><a class="header" href="#individual-developers">Individual Developers</a></h3>
<p><strong>Personal knowledge base</strong>: Capture learnings, build expertise
<strong>Quick recall</strong>: "How did I solve this before?"
<strong>Context switching</strong>: Return to old projects with full context
<strong>Career growth</strong>: Document what you learn, portfolio of knowledge</p>
<h3 id="teams"><a class="header" href="#teams">Teams</a></h3>
<p><strong>Shared context</strong>: Everyone has access to team knowledge
<strong>Onboarding</strong>: New members ramp up faster
<strong>Consistency</strong>: Follow shared guidelines and patterns
<strong>Collaboration</strong>: Build on each other's knowledge</p>
<h3 id="organizations"><a class="header" href="#organizations">Organizations</a></h3>
<p><strong>Institutional memory</strong>: Knowledge persists beyond individual tenure
<strong>Best practices</strong>: Standardize across teams
<strong>Compliance</strong>: Document security and architecture decisions
<strong>Efficiency</strong>: Stop solving the same problems repeatedly</p>
<h3 id="ai-agents"><a class="header" href="#ai-agents">AI Agents</a></h3>
<p><strong>Context injection</strong>: Agents query guidelines before generating code
<strong>Decision awareness</strong>: Agents check past decisions
<strong>Pattern following</strong>: Agents use documented patterns
<strong>Execution tracking</strong>: Agent actions documented automatically</p>
<h2 id="when-not-to-use-kb"><a class="header" href="#when-not-to-use-kb">When NOT to Use KB</a></h2>
<p>KB is <strong>not</strong> ideal for:</p>
<p><strong>Personal journaling</strong>: Use Obsidian or Logseq
<strong>Team wikis</strong>: Use Notion or Confluence
<strong>Customer docs</strong>: Use mdBook or Docusaurus
<strong>Project management</strong>: Use Jira or Linear
<strong>Code comments</strong>: Use inline documentation</p>
<p>KB is <strong>perfect</strong> for:</p>
<p>✅ Developer knowledge graphs
✅ Architectural decision records
✅ Pattern libraries
✅ Coding guidelines
✅ Technical context
✅ AI-assisted development</p>
<h2 id="the-vision"><a class="header" href="#the-vision">The Vision</a></h2>
<p><strong>Today's development</strong>:</p>
<pre><code>Developer → writes code → commits
</code></pre>
<p><strong>With KB</strong>:</p>
<pre><code>Developer → writes code → documents decisions → links to patterns → commits code + knowledge
AI Agent queries knowledge → generates better code
Team discovers patterns → reuses solutions → faster development
</code></pre>
<p><strong>Knowledge becomes an active participant in development</strong>, not an afterthought.</p>
<h2 id="design-philosophy"><a class="header" href="#design-philosophy">Design Philosophy</a></h2>
<p>Three core principles drive KB:</p>
<h3 id="1-knowledge-should-be-captured-during-work-not-after"><a class="header" href="#1-knowledge-should-be-captured-during-work-not-after">1. <strong>Knowledge should be captured during work, not after</strong></a></h3>
<p>❌ "I'll document this later" → never happens
✅ "Claude, document this decision" → done in 30 seconds</p>
<h3 id="2-knowledge-should-be-connected-not-isolated"><a class="header" href="#2-knowledge-should-be-connected-not-isolated">2. <strong>Knowledge should be connected, not isolated</strong></a></h3>
<p>❌ Standalone documents in folders
✅ Graph of interconnected concepts</p>
<h3 id="3-knowledge-should-be-queryable-not-browsable"><a class="header" href="#3-knowledge-should-be-queryable-not-browsable">3. <strong>Knowledge should be queryable, not browsable</strong></a></h3>
<p>❌ "Let me look through 50 docs to find..."
✅ "Find anything related to error handling" → instant semantic results</p>
<h2 id="get-started"><a class="header" href="#get-started">Get Started</a></h2>
<p>Ready to stop losing knowledge?</p>
<ul>
<li><strong>Understand concepts</strong>: <a href="kogral/core-concepts.html">Core Concepts</a></li>
<li><strong>Learn philosophy</strong>: <a href="kogral/design-philosophy.html">Design Philosophy</a></li>
<li><strong>Quick start</strong>: <a href="kogral/../guides/quickstart.html">Quick Start Guide</a></li>
<li><strong>See examples</strong>: <a href="kogral/../guides/use-cases.html">Use Cases</a></li>
</ul>
<hr />
<blockquote>
<p>"The best time to document was during implementation. The second best time is now."</p>
</blockquote>
<p>But with KB + AI, "now" is instant.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="design-philosophy-1"><a class="header" href="#design-philosophy-1">Design Philosophy</a></h1>
<p>The principles and values guiding Knowledge Base design and implementation.</p>
<h2 id="core-tenets"><a class="header" href="#core-tenets">Core Tenets</a></h2>
<h3 id="1-config-driven-not-hardcoded"><a class="header" href="#1-config-driven-not-hardcoded">1. Config-Driven, Not Hardcoded</a></h3>
<p><strong>Principle</strong>: All behavior should be configurable via schemas, not baked into code.</p>
<p><strong>Why</strong>: Flexibility without code changes. Users adapt KB to their workflows, not vice versa.</p>
<p><strong>Example</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// ❌ Bad: Hardcoded
impl EmbeddingProvider {
fn new() -&gt; Self {
FastEmbedProvider::new("BAAI/bge-small-en-v1.5") // Can't change
}
}
// ✅ Good: Config-driven
impl EmbeddingProvider {
fn from_config(config: &amp;EmbeddingConfig) -&gt; Result&lt;Box&lt;dyn EmbeddingProvider&gt;&gt; {
match config.provider {
'fastembed =&gt; Ok(Box::new(FastEmbedProvider::new(&amp;config.model)?)),
'openai =&gt; Ok(Box::new(OpenAIProvider::new(&amp;config.model)?)),
}
}
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Benefits</strong>:</p>
<ul>
<li>Switch embedding providers without recompilation</li>
<li>Different configs for dev/prod</li>
<li>User choice, not developer mandate</li>
</ul>
<h3 id="2-type-safe-configuration"><a class="header" href="#2-type-safe-configuration">2. Type-Safe Configuration</a></h3>
<p><strong>Principle</strong>: Validate configuration before runtime, not during.</p>
<p><strong>Why</strong>: Catch errors early, reduce runtime failures.</p>
<p><strong>Implementation</strong>: Nickel contracts + serde validation = double validation</p>
<pre><code class="language-nickel"># Schema defines valid values
EmbeddingProvider = [| 'openai, 'claude, 'fastembed |]
# Typo caught at export time, not runtime
{ provider = 'opena1 } # Error: Invalid variant
</code></pre>
<p><strong>Benefits</strong>:</p>
<ul>
<li>Errors found during <code>nickel export</code>, not during execution</li>
<li>Self-documenting: schema is the spec</li>
<li>Refactoring safe: change schema, find all usages</li>
</ul>
<h3 id="3-local-first-cloud-optional"><a class="header" href="#3-local-first-cloud-optional">3. Local-First, Cloud-Optional</a></h3>
<p><strong>Principle</strong>: Core functionality works offline, cloud is enhancement.</p>
<p><strong>Why</strong>: Privacy, cost control, offline development.</p>
<p><strong>Examples</strong>:</p>
<div class="table-wrapper"><table><thead><tr><th>Feature</th><th>Local</th><th>Cloud</th></tr></thead><tbody>
<tr><td>Storage</td><td>Filesystem</td><td>SurrealDB</td></tr>
<tr><td>Embeddings</td><td>fastembed</td><td>OpenAI/Claude</td></tr>
<tr><td>Search</td><td>Text-based</td><td>Semantic</td></tr>
</tbody></table>
</div>
<p><strong>Benefits</strong>:</p>
<ul>
<li>Works on planes, trains, areas with poor internet</li>
<li>No API costs for small projects</li>
<li>Privacy-sensitive projects keep data local</li>
<li>Production can use cloud for scale</li>
</ul>
<h3 id="4-git-friendly-by-default"><a class="header" href="#4-git-friendly-by-default">4. Git-Friendly by Default</a></h3>
<p><strong>Principle</strong>: Knowledge should version alongside code.</p>
<p><strong>Why</strong>: Knowledge describes code, should evolve with it.</p>
<p><strong>Implementation</strong>:</p>
<ul>
<li>Markdown + YAML frontmatter (text-based, diffable)</li>
<li>One file per node (granular commits)</li>
<li>Wikilinks preserved (works in Logseq, Obsidian)</li>
</ul>
<p><strong>Benefits</strong>:</p>
<pre><code class="language-bash"># Review knowledge changes in PRs
git diff .kogral/decisions/
# Knowledge follows branches
git checkout feature-x
# .kogral/ reflects feature-x decisions
# Knowledge merges
git merge feature-x
# Merge conflicts = knowledge conflicts (resolve intentionally)
</code></pre>
<h3 id="5-ai-native-human-readable"><a class="header" href="#5-ai-native-human-readable">5. AI-Native, Human-Readable</a></h3>
<p><strong>Principle</strong>: Optimized for AI consumption, readable by humans.</p>
<p><strong>Why</strong>: Best of both worlds - AI-assisted discovery, human verification.</p>
<p><strong>Implementation</strong>:</p>
<ul>
<li><strong>Structured</strong>: YAML frontmatter for AI parsing</li>
<li><strong>Semantic</strong>: Embeddings for AI queries</li>
<li><strong>Readable</strong>: Markdown for human consumption</li>
<li><strong>Linked</strong>: Typed relationships for AI traversal</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code class="language-markdown">---
id: note-auth
type: note
title: Authentication Implementation
tags: [security, auth]
relates_to: [guideline-security, pattern-jwt]
---
# Authentication Implementation
Humans read this markdown normally.
AI can:
- Parse frontmatter for metadata
- Extract tags for filtering
- Follow relates_to links
- Generate embeddings for semantic search
</code></pre>
<h3 id="6-composition-over-inheritance"><a class="header" href="#6-composition-over-inheritance">6. Composition Over Inheritance</a></h3>
<p><strong>Principle</strong>: Build systems by composing small, focused components.</p>
<p><strong>Why</strong>: Flexibility, testability, maintainability.</p>
<p><strong>Implementation</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Small, focused traits
trait Storage { ... }
trait EmbeddingProvider { ... }
trait TemplateEngine { ... }
// Compose into systems
struct KnowledgeBase {
storage: Box&lt;dyn Storage&gt;,
embeddings: Option&lt;Box&lt;dyn EmbeddingProvider&gt;&gt;,
templates: TemplateEngine,
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Benefits</strong>:</p>
<ul>
<li>Swap storage without affecting embeddings</li>
<li>Disable embeddings without breaking storage</li>
<li>Test components in isolation</li>
<li>Add new providers by implementing trait</li>
</ul>
<h3 id="7-fail-fast-fail-clearly"><a class="header" href="#7-fail-fast-fail-clearly">7. Fail Fast, Fail Clearly</a></h3>
<p><strong>Principle</strong>: Detect errors early, provide clear messages.</p>
<p><strong>Why</strong>: Developer experience - fast feedback, actionable errors.</p>
<p><strong>Implementation</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// ❌ Bad: Silent failure
fn load_config(path: &amp;Path) -&gt; Option&lt;Config&gt; {
std::fs::read_to_string(path)
.ok()
.and_then(|s| serde_json::from_str(&amp;s).ok())
}
// ✅ Good: Explicit errors
fn load_config(path: &amp;Path) -&gt; Result&lt;Config, ConfigError&gt; {
let content = std::fs::read_to_string(path)
.map_err(|e| ConfigError::ReadFailed(path.to_path_buf(), e))?;
serde_json::from_str(&amp;content)
.map_err(|e| ConfigError::ParseFailed(path.to_path_buf(), e))
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Error messages</strong>:</p>
<pre><code>❌ "Failed to load config"
✅ "Failed to read config file '/path/to/config.ncl': Permission denied"
</code></pre>
<h3 id="8-convention-over-configuration-with-escape-hatches"><a class="header" href="#8-convention-over-configuration-with-escape-hatches">8. Convention Over Configuration (With Escape Hatches)</a></h3>
<p><strong>Principle</strong>: Sane defaults, but everything customizable.</p>
<p><strong>Why</strong>: Easy to start, flexible as you grow.</p>
<p><strong>Examples</strong>:</p>
<pre><code class="language-nickel"># Minimal config (uses conventions)
{ graph = { name = "my-project" } }
# Defaults: filesystem storage, no embeddings, standard templates
# Full config (explicit everything)
{
graph = { name = "my-project" },
storage = { primary = 'surrealdb, /* ... */ },
embeddings = { provider = 'openai, /* ... */ },
templates = { templates_dir = "custom" },
}
</code></pre>
<p><strong>Conventions</strong>:</p>
<ul>
<li><code>.kogral/</code> directory in project root</li>
<li>Filesystem storage by default</li>
<li>YAML frontmatter + markdown body</li>
<li>Standard template names (note.md.tera, decision.md.tera)</li>
</ul>
<p><strong>Escape hatches</strong>:</p>
<ul>
<li><code>--kb-dir</code> to use different location</li>
<li>Configure alternative storage backends</li>
<li>Custom frontmatter schemas</li>
<li>Override any template</li>
</ul>
<h3 id="9-documentation-as-code"><a class="header" href="#9-documentation-as-code">9. Documentation as Code</a></h3>
<p><strong>Principle</strong>: Documentation lives with code, versioned together.</p>
<p><strong>Why</strong>: Outdated docs are worse than no docs.</p>
<p><strong>Implementation</strong>:</p>
<ul>
<li>ADRs in <code>.kogral/decisions/</code> (alongside code)</li>
<li>Guidelines in <code>.kogral/guidelines/</code> (versioned with code)</li>
<li>Patterns in <code>.kogral/patterns/</code> (evolve with implementations)</li>
</ul>
<p><strong>Benefits</strong>:</p>
<pre><code class="language-bash"># Code and docs branch together
git checkout old-version
# .kogral/ reflects that version's decisions
# Code and docs merge together
git merge feature
# Merge includes new patterns, updated guidelines
# Code and docs reviewed together
# PR shows code + decision + guideline updates
</code></pre>
<h3 id="10-optimize-for-discoverability"><a class="header" href="#10-optimize-for-discoverability">10. Optimize for Discoverability</a></h3>
<p><strong>Principle</strong>: Knowledge is useless if you can't find it.</p>
<p><strong>Why</strong>: The point is to <strong>use</strong> knowledge, not just store it.</p>
<p><strong>Features</strong>:</p>
<ul>
<li><strong>Text search</strong>: Find exact keywords</li>
<li><strong>Semantic search</strong>: Find related concepts</li>
<li><strong>Graph traversal</strong>: Follow relationships</li>
<li><strong>Tag filtering</strong>: Narrow by category</li>
<li><strong>MCP integration</strong>: AI-assisted discovery</li>
</ul>
<p><strong>Example</strong>:</p>
<p>User doesn't remember exact term, but knows the concept:</p>
<pre><code>"Find anything about handling errors gracefully"
</code></pre>
<p>KB finds (semantically):</p>
<ul>
<li>"Error Handling with thiserror" (pattern)</li>
<li>"Result Type Best Practices" (guideline)</li>
<li>"Panic Recovery" (note)</li>
<li>"Graceful Degradation" (pattern)</li>
</ul>
<p>No exact keyword match needed, concept match sufficient.</p>
<h3 id="11-build-for-humans-enable-ai"><a class="header" href="#11-build-for-humans-enable-ai">11. Build for Humans, Enable AI</a></h3>
<p><strong>Principle</strong>: Humans are the primary users, AI is the assistant.</p>
<p><strong>Why</strong>: AI should enhance human workflows, not replace them.</p>
<p><strong>Implementation</strong>:</p>
<ul>
<li><strong>Human-readable formats</strong>: Markdown, YAML</li>
<li><strong>Human-editable</strong>: Any text editor works</li>
<li><strong>Human-discoverable</strong>: <code>ls .kogral/notes/</code> shows files</li>
<li><strong>AI-enhanced</strong>: MCP for AI-assisted queries</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code class="language-bash"># Human workflow
vim .kogral/notes/my-note.md # Edit directly
git add .kogral/notes/my-note.md
git commit -m "Add note about X"
# AI-enhanced workflow
# (in Claude Code)
"Add a note about X with tags Y, Z"
# AI creates file, human reviews
</code></pre>
<h3 id="12-embrace-the-graph"><a class="header" href="#12-embrace-the-graph">12. Embrace the Graph</a></h3>
<p><strong>Principle</strong>: Knowledge is interconnected, embrace the relationships.</p>
<p><strong>Why</strong>: Context comes from connections, not isolation.</p>
<p><strong>Implementation</strong>:</p>
<ul>
<li>Typed relationships (not just "related")</li>
<li>Bidirectional traversal</li>
<li>Relationship strength (0.0-1.0)</li>
<li>Multi-hop queries</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code>Find all patterns that:
- Are implemented by current project
- Depend on shared guidelines
- Were added in the last 6 months
# Graph query, not sequential file search
</code></pre>
<h2 id="anti-patterns-to-avoid"><a class="header" href="#anti-patterns-to-avoid">Anti-Patterns to Avoid</a></h2>
<h3 id="1--hardcoding-behavior"><a class="header" href="#1--hardcoding-behavior">1. ❌ Hardcoding Behavior</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Don't
const EMBEDDING_MODEL: &amp;str = "BAAI/bge-small-en-v1.5";
// Do
let model = config.embeddings.model.as_str();
<span class="boring">}</span></code></pre></pre>
<h3 id="2--runtime-schema-validation"><a class="header" href="#2--runtime-schema-validation">2. ❌ Runtime Schema Validation</a></h3>
<pre><code class="language-nickel">// Don't validate at runtime
let provider = config.provider; // Might be invalid
// Do validate at export time (Nickel contracts)
provider | [| 'openai, 'claude, 'fastembed |]
</code></pre>
<h3 id="3--opaque-errors"><a class="header" href="#3--opaque-errors">3. ❌ Opaque Errors</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Don't
Err("Failed".into())
// Do
Err(KbError::NodeNotFound(id.to_string()))
<span class="boring">}</span></code></pre></pre>
<h3 id="4--coupling-components"><a class="header" href="#4--coupling-components">4. ❌ Coupling Components</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Don't
impl KnowledgeBase {
fn search(&amp;self) -&gt; Vec&lt;Node&gt; {
let embeddings = FastEmbedProvider::new(); // Hardcoded!
// ...
}
}
// Do
impl KnowledgeBase {
fn search(&amp;self) -&gt; Vec&lt;Node&gt; {
if let Some(provider) = &amp;self.embeddings {
// Use configured provider
}
}
}
<span class="boring">}</span></code></pre></pre>
<h3 id="5--proprietary-formats"><a class="header" href="#5--proprietary-formats">5. ❌ Proprietary Formats</a></h3>
<pre><code>// Don't
Binary blob: [0x4B, 0x42, 0x01, ...]
// Do
Markdown + YAML:
---
id: note-1
---
# Content
</code></pre>
<h2 id="influences"><a class="header" href="#influences">Influences</a></h2>
<p>KB draws inspiration from:</p>
<ul>
<li><strong>Logseq</strong>: Outliner, graph view, wikilinks</li>
<li><strong>Obsidian</strong>: Markdown-first, local storage, plugins</li>
<li><strong>Zettelkasten</strong>: Atomic notes, links, emergence</li>
<li><strong>ADR</strong>: Decision records, context preservation</li>
<li><strong>Git</strong>: Version control, branching, merging</li>
<li><strong>Nickel</strong>: Type-safe configuration, contracts</li>
<li><strong>MCP</strong>: AI integration protocol</li>
<li><strong>SurrealDB</strong>: Graph database, relationships</li>
</ul>
<h2 id="implementation-principles"><a class="header" href="#implementation-principles">Implementation Principles</a></h2>
<h3 id="rust"><a class="header" href="#rust">Rust</a></h3>
<ul>
<li>Zero unsafe code (<code>#![forbid(unsafe_code)]</code>)</li>
<li>No <code>unwrap()</code> in production code</li>
<li>Always use <code>Result&lt;T&gt;</code> for fallibility</li>
<li>Comprehensive error types with <code>thiserror</code></li>
<li>Full test coverage (100+ tests)</li>
</ul>
<h3 id="nickel"><a class="header" href="#nickel">Nickel</a></h3>
<ul>
<li>Contracts for validation</li>
<li>Defaults in schemas</li>
<li>Documentation in contracts</li>
<li>Composition via imports</li>
</ul>
<h3 id="nushell"><a class="header" href="#nushell">NuShell</a></h3>
<ul>
<li>Structured data pipelines</li>
<li>Error handling in scripts</li>
<li>Colored output for UX</li>
<li>Dry-run modes</li>
</ul>
<h2 id="evolution-strategy"><a class="header" href="#evolution-strategy">Evolution Strategy</a></h2>
<p>KB follows these guidelines for evolution:</p>
<ol>
<li><strong>Backward Compatibility</strong>: Don't break existing configs</li>
<li><strong>Deprecation Period</strong>: Warn before removal (1 major version)</li>
<li><strong>Migration Tools</strong>: Provide automated migrations</li>
<li><strong>Semantic Versioning</strong>: MAJOR.MINOR.PATCH strictly</li>
</ol>
<h2 id="conclusion"><a class="header" href="#conclusion">Conclusion</a></h2>
<p>These principles guide every decision:</p>
<p><strong>Config-driven</strong>: Behavior in schemas, not code
<strong>Type-safe</strong>: Validate before runtime
<strong>Local-first</strong>: Works offline, cloud optional
<strong>Git-friendly</strong>: Knowledge versions with code
<strong>AI-native</strong>: Optimized for AI, readable by humans
<strong>Composable</strong>: Small pieces, loosely coupled
<strong>Fast feedback</strong>: Fail early, clear errors
<strong>Discoverable</strong>: Easy to find what you need</p>
<p>The goal: <strong>Knowledge management that developers actually use.</strong></p>
<h2 id="next-steps-2"><a class="header" href="#next-steps-2">Next Steps</a></h2>
<ul>
<li><strong>See it in action</strong>: <a href="kogral/../guides/use-cases.html">Use Cases</a></li>
<li><strong>Understand architecture</strong>: <a href="kogral/../architecture/overview.html">System Overview</a></li>
<li><strong>Start using</strong>: <a href="kogral/../guides/quickstart.html">Quick Start</a></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="installation"><a class="header" href="#installation">Installation</a></h1>
<p>This guide covers installing and setting up the KOGRAL.</p>
<h2 id="prerequisites"><a class="header" href="#prerequisites">Prerequisites</a></h2>
<h3 id="required"><a class="header" href="#required">Required</a></h3>
<ul>
<li>
<p><strong>Rust</strong> 1.70 or later</p>
<pre><code class="language-bash">curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
</code></pre>
</li>
<li>
<p><strong>Nickel</strong> CLI for configuration management</p>
<pre><code class="language-bash">cargo install nickel-lang-cli
</code></pre>
</li>
</ul>
<h3 id="optional"><a class="header" href="#optional">Optional</a></h3>
<ul>
<li>
<p><strong>NuShell</strong> for maintenance scripts</p>
<pre><code class="language-bash">cargo install nu
</code></pre>
</li>
<li>
<p><strong>SurrealDB</strong> for scalable storage backend</p>
<pre><code class="language-bash"># macOS
brew install surrealdb/tap/surreal
# Linux/Windows
curl -sSf https://install.surrealdb.com | sh
</code></pre>
</li>
</ul>
<h2 id="installation-methods"><a class="header" href="#installation-methods">Installation Methods</a></h2>
<h3 id="method-1-install-from-source-recommended"><a class="header" href="#method-1-install-from-source-recommended">Method 1: Install from Source (Recommended)</a></h3>
<pre><code class="language-bash"># Clone the repository
git clone &lt;repository-url&gt; knowledge-base
cd knowledge-base
# Build the workspace
cargo build --release
# Install CLI tool
cargo install --path crates/kb-cli
# Verify installation
kb --version
</code></pre>
<h3 id="method-2-build-specific-crates"><a class="header" href="#method-2-build-specific-crates">Method 2: Build Specific Crates</a></h3>
<pre><code class="language-bash"># Build only kb-core library
cargo build --package kb-core --release
# Build only kb-cli
cargo build --package kb-cli --release
# Build only kogral-mcp server
cargo build --package kb-mcp --release
</code></pre>
<h3 id="method-3-build-with-features"><a class="header" href="#method-3-build-with-features">Method 3: Build with Features</a></h3>
<pre><code class="language-bash"># Build with all features (filesystem + SurrealDB + fastembed)
cargo build --workspace --all-features --release
# Build with specific features
cargo build --package kb-core --features "surrealdb,fastembed" --release
</code></pre>
<h2 id="feature-flags"><a class="header" href="#feature-flags">Feature Flags</a></h2>
<p>kb-core supports optional features:</p>
<div class="table-wrapper"><table><thead><tr><th>Feature</th><th>Description</th><th>Default</th></tr></thead><tbody>
<tr><td><code>filesystem</code></td><td>Filesystem storage backend</td><td>✅ Yes</td></tr>
<tr><td><code>surrealdb</code></td><td>SurrealDB storage backend</td><td>❌ No</td></tr>
<tr><td><code>fastembed</code></td><td>Local embedding generation</td><td>❌ No</td></tr>
<tr><td><code>full</code></td><td>All features enabled</td><td>❌ No</td></tr>
</tbody></table>
</div>
<p>Example usage:</p>
<pre><code class="language-bash"># Enable SurrealDB backend
cargo build --features surrealdb
# Enable all features
cargo build --features full
</code></pre>
<h2 id="environment-setup"><a class="header" href="#environment-setup">Environment Setup</a></h2>
<h3 id="1-initialize-a-knowledge-base"><a class="header" href="#1-initialize-a-knowledge-base">1. Initialize a Knowledge Base</a></h3>
<pre><code class="language-bash"># Navigate to your project
cd /path/to/your/project
# Initialize .kb directory
kb init
# Or initialize with custom name
kb init --name "My Project" --description "Project knowledge base"
</code></pre>
<p>This creates:</p>
<pre><code>your-project/
└── .kogral/
├── config.toml
├── notes/
├── decisions/
├── guidelines/
├── patterns/
└── journal/
</code></pre>
<h3 id="2-configure-nickel-schemas-optional"><a class="header" href="#2-configure-nickel-schemas-optional">2. Configure Nickel Schemas (Optional)</a></h3>
<p>If you want advanced configuration:</p>
<pre><code class="language-bash"># Copy default config
cp /path/to/knowledge-base/config/defaults.ncl .kogral/config.ncl
# Edit configuration
$EDITOR .kogral/config.ncl
# Export to TOML (for kb-cli compatibility)
nickel export --format json .kogral/config.ncl | kb config import
</code></pre>
<h3 id="3-set-up-shared-knowledge-base-optional"><a class="header" href="#3-set-up-shared-knowledge-base-optional">3. Set Up Shared Knowledge Base (Optional)</a></h3>
<p>For shared guidelines and patterns across projects:</p>
<pre><code class="language-bash"># Create shared KB location
mkdir -p ~/Tools/.kogral-shared
cd ~/Tools/.kogral-shared
# Initialize shared KB
kb init --name "Shared Knowledge" --description "Cross-project guidelines"
# Configure inheritance in project
kb config set inheritance.base ~/Tools/.kogral-shared
</code></pre>
<h2 id="verify-installation-1"><a class="header" href="#verify-installation-1">Verify Installation</a></h2>
<h3 id="test-cli"><a class="header" href="#test-cli">Test CLI</a></h3>
<pre><code class="language-bash"># Check version
kb --version
# Show help
kb --help
# Test initialization (dry-run)
kb init --dry-run
</code></pre>
<h3 id="test-mcp-server"><a class="header" href="#test-mcp-server">Test MCP Server</a></h3>
<pre><code class="language-bash"># Start MCP server in test mode
kb serve --transport stdio
# In another terminal, test with echo
echo '{"jsonrpc":"2.0","id":1,"method":"kogral/search","params":{"query":"test"}}' | kb serve
</code></pre>
<h3 id="run-tests"><a class="header" href="#run-tests">Run Tests</a></h3>
<pre><code class="language-bash"># Run all tests
cargo test --workspace
# Run integration tests
cargo test --package kb-core --test '*'
# Run with all features
cargo test --workspace --all-features
</code></pre>
<h2 id="surrealdb-setup-optional"><a class="header" href="#surrealdb-setup-optional">SurrealDB Setup (Optional)</a></h2>
<p>If using SurrealDB backend:</p>
<h3 id="start-surrealdb-server"><a class="header" href="#start-surrealdb-server">Start SurrealDB Server</a></h3>
<pre><code class="language-bash"># Start server
surreal start --log trace --user root --pass root file://data.db
# Or use memory backend for testing
surreal start --log trace --user root --pass root memory
</code></pre>
<h3 id="configure-kb-core"><a class="header" href="#configure-kb-core">Configure kb-core</a></h3>
<p>Edit <code>.kogral/config.ncl</code>:</p>
<pre><code class="language-nickel">{
storage = {
primary = 'filesystem,
secondary = {
enabled = true,
type = 'surrealdb,
url = "ws://localhost:8000",
namespace = "kb",
database = "default",
username = "root",
password = "root",
},
},
}
</code></pre>
<h3 id="initialize-schema"><a class="header" href="#initialize-schema">Initialize Schema</a></h3>
<pre><code class="language-bash"># Run migration script
nu scripts/kb-migrate.nu --target latest
# Or manually import schema
surreal import --conn ws://localhost:8000 --user root --pass root --ns kb --db default schema.surql
</code></pre>
<h2 id="embedding-provider-setup-optional"><a class="header" href="#embedding-provider-setup-optional">Embedding Provider Setup (Optional)</a></h2>
<h3 id="local-fastembed"><a class="header" href="#local-fastembed">Local fastembed</a></h3>
<pre><code class="language-bash"># Build with fastembed feature
cargo build --package kb-core --features fastembed
# Models will be downloaded on first use
</code></pre>
<h3 id="api-providers-openai-claude-ollama"><a class="header" href="#api-providers-openai-claude-ollama">API Providers (OpenAI, Claude, Ollama)</a></h3>
<p>Set environment variables:</p>
<pre><code class="language-bash"># OpenAI
export OPENAI_API_KEY="sk-..."
# Claude (Anthropic)
export ANTHROPIC_API_KEY="sk-ant-..."
# Ollama (local)
export OLLAMA_API_BASE="http://localhost:11434"
</code></pre>
<p>Configure provider in <code>.kogral/config.ncl</code>:</p>
<pre><code class="language-nickel">{
embeddings = {
enabled = true,
provider = 'openai, # or 'claude, 'ollama, 'fastembed
model = "text-embedding-3-small",
api_key_env = "OPENAI_API_KEY",
},
}
</code></pre>
<h2 id="claude-code-integration"><a class="header" href="#claude-code-integration">Claude Code Integration</a></h2>
<p>To use kb-mcp with Claude Code:</p>
<h3 id="1-build-mcp-server"><a class="header" href="#1-build-mcp-server">1. Build MCP Server</a></h3>
<pre><code class="language-bash">cargo build --package kb-mcp --release
</code></pre>
<h3 id="2-configure-claude-code"><a class="header" href="#2-configure-claude-code">2. Configure Claude Code</a></h3>
<p>Add to <code>~/.config/claude/config.json</code>:</p>
<pre><code class="language-json">{
"mcpServers": {
"kogral-mcp": {
"command": "/path/to/knowledge-base/target/release/kb-mcp",
"args": ["serve"],
"env": {}
}
}
}
</code></pre>
<h3 id="3-test-connection"><a class="header" href="#3-test-connection">3. Test Connection</a></h3>
<p>Start Claude Code and verify:</p>
<pre><code>&gt; kb/search "test"
</code></pre>
<h2 id="troubleshooting"><a class="header" href="#troubleshooting">Troubleshooting</a></h2>
<h3 id="nickel-not-found"><a class="header" href="#nickel-not-found">Nickel Not Found</a></h3>
<pre><code class="language-bash"># Verify nickel is installed
nickel --version
# If not, install
cargo install nickel-lang-cli
# Add cargo bin to PATH
export PATH="$HOME/.cargo/bin:$PATH"
</code></pre>
<h3 id="compilation-errors"><a class="header" href="#compilation-errors">Compilation Errors</a></h3>
<pre><code class="language-bash"># Update Rust
rustup update stable
# Clean and rebuild
cargo clean
cargo build --workspace
</code></pre>
<h3 id="surrealdb-connection-failed"><a class="header" href="#surrealdb-connection-failed">SurrealDB Connection Failed</a></h3>
<pre><code class="language-bash"># Check SurrealDB is running
curl http://localhost:8000/health
# Start SurrealDB with correct settings
surreal start --bind 0.0.0.0:8000 --user root --pass root memory
</code></pre>
<h3 id="mcp-server-not-responding"><a class="header" href="#mcp-server-not-responding">MCP Server Not Responding</a></h3>
<pre><code class="language-bash"># Test stdio communication
echo '{"jsonrpc":"2.0","id":1,"method":"ping"}' | kb serve
# Check logs
kb serve --log-level debug
</code></pre>
<h2 id="next-steps-3"><a class="header" href="#next-steps-3">Next Steps</a></h2>
<ul>
<li><a href="guides/user-guide/quickstart.html">Quick Start Guide</a> - Create your first knowledge base</li>
<li><a href="guides/user-guide/configuration.html">Configuration Reference</a> - Customize behavior</li>
<li><a href="guides/api/mcp-tools.html">MCP Tools</a> - Integrate with Claude Code</li>
</ul>
<h2 id="uninstallation"><a class="header" href="#uninstallation">Uninstallation</a></h2>
<pre><code class="language-bash"># Remove installed binary
cargo uninstall kb-cli
# Remove project knowledge base
rm -rf /path/to/project/.kogral
# Remove shared knowledge base
rm -rf ~/Tools/.kogral-shared
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="quick-start-guide"><a class="header" href="#quick-start-guide">Quick Start Guide</a></h1>
<p>Get up and running with the KOGRAL in 5 minutes.</p>
<h2 id="prerequisites-1"><a class="header" href="#prerequisites-1">Prerequisites</a></h2>
<ul>
<li>Rust 1.70+ installed</li>
<li>Nickel CLI installed (<code>cargo install nickel-lang-cli</code>)</li>
<li>kb-cli installed (see <a href="guides/../installation.html">Installation</a>)</li>
</ul>
<h2 id="step-1-initialize-your-knowledge-base"><a class="header" href="#step-1-initialize-your-knowledge-base">Step 1: Initialize Your Knowledge Base</a></h2>
<p>Navigate to your project directory and initialize:</p>
<pre><code class="language-bash">cd /path/to/your/project
kb init
</code></pre>
<p>This creates a <code>.kogral/</code> directory with the following structure:</p>
<pre><code>.kogral/
├── config.toml # Configuration
├── notes/ # General notes
├── decisions/ # Architectural decisions
├── guidelines/ # Project guidelines
├── patterns/ # Reusable patterns
└── journal/ # Daily journal entries
</code></pre>
<h2 id="step-2-create-your-first-note"><a class="header" href="#step-2-create-your-first-note">Step 2: Create Your First Note</a></h2>
<p>Add a note to your knowledge base:</p>
<pre><code class="language-bash">kb add note "Getting Started with Rust" \
--tags rust,programming,learning \
--content "Key concepts for learning Rust effectively."
</code></pre>
<p>Or create interactively:</p>
<pre><code class="language-bash">kb add note
# Follow the prompts to enter title, tags, and content
</code></pre>
<h2 id="step-3-create-a-decision-record"><a class="header" href="#step-3-create-a-decision-record">Step 3: Create a Decision Record</a></h2>
<p>Document an architectural decision:</p>
<pre><code class="language-bash">kb add decision "Use SurrealDB for Storage" \
--context "Need scalable storage for knowledge graph" \
--decision "Adopt SurrealDB for its graph capabilities" \
--consequence "Better query performance" \
--consequence "Additional infrastructure dependency"
</code></pre>
<h2 id="step-4-link-nodes-together"><a class="header" href="#step-4-link-nodes-together">Step 4: Link Nodes Together</a></h2>
<p>Create relationships between nodes:</p>
<pre><code class="language-bash"># Find node IDs
kb list
# Create a relationship
kb link &lt;note-id&gt; &lt;decision-id&gt; relates_to
</code></pre>
<p>Available relationship types:</p>
<ul>
<li><code>relates_to</code> - General conceptual link</li>
<li><code>depends_on</code> - Dependency relationship</li>
<li><code>implements</code> - Implementation of a concept</li>
<li><code>extends</code> - Inheritance/extension</li>
<li><code>supersedes</code> - Replaces older version</li>
<li><code>explains</code> - Documentation/clarification</li>
</ul>
<h2 id="step-5-search-your-knowledge-base"><a class="header" href="#step-5-search-your-knowledge-base">Step 5: Search Your Knowledge Base</a></h2>
<p>Search by text:</p>
<pre><code class="language-bash"># Simple search
kb search "rust"
# Filter by type
kb search "architecture" --type decision
# Limit results
kb search "error handling" --limit 5
</code></pre>
<p>Search semantically (requires embeddings):</p>
<pre><code class="language-bash"># Semantic search finds conceptually related content
kb search "memory safety" --semantic --threshold 0.7
</code></pre>
<h2 id="step-6-view-node-details"><a class="header" href="#step-6-view-node-details">Step 6: View Node Details</a></h2>
<pre><code class="language-bash"># Show node by ID
kb show &lt;node-id&gt;
# Show node with relationships
kb show &lt;node-id&gt; --with-relationships
</code></pre>
<h2 id="step-7-edit-documents-directly"><a class="header" href="#step-7-edit-documents-directly">Step 7: Edit Documents Directly</a></h2>
<p>All knowledge base documents are markdown files you can edit:</p>
<pre><code class="language-bash"># Open in your editor
$EDITOR .kogral/notes/getting-started-with-rust.md
# Or use your favorite markdown editor
code .kogral/notes/
</code></pre>
<p>Document format:</p>
<pre><code class="language-markdown">---
id: unique-id
type: note
title: Getting Started with Rust
created: 2026-01-17T10:30:00Z
modified: 2026-01-17T10:30:00Z
tags: [rust, programming, learning]
status: active
---
# Getting Started with Rust
Content goes here with [[wikilinks]] to other nodes.
## Key Concepts
- Ownership
- Borrowing
- Lifetimes
</code></pre>
<h2 id="step-8-create-a-daily-journal-entry"><a class="header" href="#step-8-create-a-daily-journal-entry">Step 8: Create a Daily Journal Entry</a></h2>
<p>Start journaling your development progress:</p>
<pre><code class="language-bash">kb add journal "Today's Progress" \
--content "Learned about trait objects and dynamic dispatch in Rust."
</code></pre>
<p>Or open today's journal directly:</p>
<pre><code class="language-bash">$EDITOR .kogral/journal/$(date +%Y-%m-%d).md
</code></pre>
<h2 id="step-9-export-to-logseq-optional"><a class="header" href="#step-9-export-to-logseq-optional">Step 9: Export to Logseq (Optional)</a></h2>
<p>If you use Logseq, export your knowledge base:</p>
<pre><code class="language-bash">nu scripts/kb-export-logseq.nu /path/to/logseq-graph
</code></pre>
<p>This creates a Logseq-compatible graph you can open in Logseq for visual editing.</p>
<h2 id="step-10-start-mcp-server-for-claude-code"><a class="header" href="#step-10-start-mcp-server-for-claude-code">Step 10: Start MCP Server for Claude Code</a></h2>
<p>Integrate with Claude Code for AI-assisted knowledge management:</p>
<pre><code class="language-bash"># Start MCP server
kb serve
# Configure in ~/.config/claude/config.json
# Then use in Claude Code:
# &gt; kb/search "rust ownership"
</code></pre>
<h2 id="common-workflows"><a class="header" href="#common-workflows">Common Workflows</a></h2>
<h3 id="capture-quick-notes"><a class="header" href="#capture-quick-notes">Capture Quick Notes</a></h3>
<pre><code class="language-bash"># Quick note
kb add note "Remember to check error handling in parser module" --tags todo,parser
</code></pre>
<h3 id="document-architectural-decisions"><a class="header" href="#document-architectural-decisions">Document Architectural Decisions</a></h3>
<pre><code class="language-bash"># Create ADR
kb add decision "Adopt Async Rust for I/O Operations" \
--status accepted \
--tags architecture,async
</code></pre>
<h3 id="build-a-pattern-library"><a class="header" href="#build-a-pattern-library">Build a Pattern Library</a></h3>
<pre><code class="language-bash"># Add a pattern
kb add pattern "Error Handling with thiserror" \
--tags rust,error-handling \
--content "Standard pattern for error types in this project."
</code></pre>
<h3 id="track-daily-progress"><a class="header" href="#track-daily-progress">Track Daily Progress</a></h3>
<pre><code class="language-bash"># Add journal entry
kb add journal --content "Implemented search functionality. Need to add semantic search next."
</code></pre>
<h2 id="next-steps-4"><a class="header" href="#next-steps-4">Next Steps</a></h2>
<h3 id="customize-configuration"><a class="header" href="#customize-configuration">Customize Configuration</a></h3>
<p>Edit <code>.kogral/config.ncl</code> for advanced configuration:</p>
<pre><code class="language-nickel">{
graph = {
name = "My Project",
version = "1.0.0",
},
embeddings = {
enabled = true,
provider = 'fastembed,
},
templates = {
templates_dir = "templates",
},
}
</code></pre>
<p>See <a href="guides/configuration.html">Configuration Reference</a> for all options.</p>
<h3 id="set-up-shared-guidelines"><a class="header" href="#set-up-shared-guidelines">Set Up Shared Guidelines</a></h3>
<p>Create a shared knowledge base for organization-wide standards:</p>
<pre><code class="language-bash"># Create shared KB
mkdir -p ~/Tools/.kogral-shared
cd ~/Tools/.kogral-shared
kb init --name "Shared Guidelines"
# Add guidelines
kb add guideline "Rust Error Handling" \
--language rust \
--category error-handling
# Configure inheritance in projects
kb config set inheritance.base ~/Tools/.kogral-shared
</code></pre>
<h3 id="automate-with-nushell-scripts"><a class="header" href="#automate-with-nushell-scripts">Automate with NuShell Scripts</a></h3>
<pre><code class="language-bash"># Backup regularly
nu scripts/kb-backup.nu --compress
# Sync with SurrealDB
nu scripts/kb-sync.nu --direction bidirectional
# Generate statistics
nu scripts/kb-stats.nu --show-tags
</code></pre>
<h3 id="integrate-with-git"><a class="header" href="#integrate-with-git">Integrate with Git</a></h3>
<pre><code class="language-bash"># Add to version control
git add .kogral/
git commit -m "docs: Add knowledge base"
# Add to .gitignore (optional: exclude certain types)
echo ".kogral/journal/" &gt;&gt; .gitignore
</code></pre>
<h2 id="tips-and-tricks"><a class="header" href="#tips-and-tricks">Tips and Tricks</a></h2>
<h3 id="use-wikilinks"><a class="header" href="#use-wikilinks">Use Wikilinks</a></h3>
<p>Link to other nodes naturally in markdown:</p>
<pre><code class="language-markdown">See [[getting-started-with-rust]] for basics.
Related decision: [[use-surrealdb-for-storage]].
</code></pre>
<h3 id="reference-code"><a class="header" href="#reference-code">Reference Code</a></h3>
<p>Link to specific code locations:</p>
<pre><code class="language-markdown">Error handling implementation: @src/parser.rs:42
</code></pre>
<h3 id="tag-consistently"><a class="header" href="#tag-consistently">Tag Consistently</a></h3>
<p>Use consistent tagging for better searchability:</p>
<pre><code class="language-bash"># Good tagging
--tags rust,error-handling,pattern
# Avoid
--tags Rust,ErrorHandling,patterns
</code></pre>
<h3 id="leverage-templates"><a class="header" href="#leverage-templates">Leverage Templates</a></h3>
<p>Customize templates for your workflow:</p>
<pre><code class="language-bash"># Copy template
cp templates/note.md.tera templates/meeting-notes.md.tera
# Edit for meeting notes format
$EDITOR templates/meeting-notes.md.tera
</code></pre>
<h2 id="troubleshooting-1"><a class="header" href="#troubleshooting-1">Troubleshooting</a></h2>
<h3 id="kb-directory-not-found"><a class="header" href="#kb-directory-not-found">"KB directory not found"</a></h3>
<pre><code class="language-bash"># Make sure you initialized
kb init
# Or specify KB directory
kb --kb-dir /path/to/.kb search "query"
</code></pre>
<h3 id="node-not-found"><a class="header" href="#node-not-found">"Node not found"</a></h3>
<pre><code class="language-bash"># List all nodes to find ID
kb list
# Search for node
kb search "partial title"
</code></pre>
<h3 id="failed-to-parse-frontmatter"><a class="header" href="#failed-to-parse-frontmatter">"Failed to parse frontmatter"</a></h3>
<p>Check your markdown file has valid YAML frontmatter:</p>
<pre><code class="language-yaml">---
id: my-note
type: note
title: My Note
---
</code></pre>
<h2 id="further-reading"><a class="header" href="#further-reading">Further Reading</a></h2>
<ul>
<li><a href="guides/configuration.html">Configuration Reference</a> - Full configuration options</li>
<li><a href="guides/cli-commands.html">CLI Commands</a> - All available commands</li>
<li><a href="guides/document-format.html">Document Format</a> - Markdown and frontmatter details</li>
<li><a href="guides/../api/mcp-tools.html">MCP Tools</a> - Claude Code integration</li>
</ul>
<h2 id="getting-help"><a class="header" href="#getting-help">Getting Help</a></h2>
<ul>
<li>Check <code>kb --help</code> for command usage</li>
<li>Read inline help: <code>kb add --help</code></li>
<li>Report issues on GitHub</li>
<li>Join community discussions</li>
</ul>
<hr />
<p><strong>Congratulations!</strong> You've created your first knowledge base. Start capturing knowledge and building connections.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="daily-workflows"><a class="header" href="#daily-workflows">Daily Workflows</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="use-cases-2"><a class="header" href="#use-cases-2">Use Cases</a></h1>
<p>Real-world scenarios showing how KOGRAL solves common development challenges.</p>
<h2 id="use-case-1-onboarding-new-team-members"><a class="header" href="#use-case-1-onboarding-new-team-members">Use Case 1: Onboarding New Team Members</a></h2>
<h3 id="problem"><a class="header" href="#problem">Problem</a></h3>
<p>New developer joins the team. Needs to understand:</p>
<ul>
<li>Why we chose SurrealDB over PostgreSQL</li>
<li>How error handling works in this project</li>
<li>What coding patterns we follow</li>
<li>Recent architectural decisions</li>
</ul>
<h3 id="solution-with-kogral"><a class="header" href="#solution-with-kogral">Solution with KOGRAL</a></h3>
<p><strong>Team lead</strong>:</p>
<pre><code class="language-bash"># Export project knowledge to Logseq for visual exploration
kb export logseq ~/onboarding-docs
</code></pre>
<p><strong>New developer</strong> (with Claude Code):</p>
<pre><code>Summarize our architectural decisions from the last 6 months
</code></pre>
<p><strong>Claude</strong> (via MCP):</p>
<pre><code>Found 8 decisions:
1. **Use SurrealDB for Storage** (accepted, 3 months ago)
- Context: Need graph database for knowledge relationships
- Consequences: Better queries, additional infrastructure
2. **Adopt Rust for Core Logic** (accepted, 5 months ago)
- Context: Type safety and performance critical
- Consequences: Steeper learning curve, excellent reliability
... [6 more decisions]
</code></pre>
<p><strong>New developer</strong>:</p>
<pre><code>What are our error handling guidelines?
</code></pre>
<p><strong>Claude</strong> (via <code>kb/get_guidelines</code>):</p>
<pre><code>## Rust Error Handling Guidelines
### Project Guidelines (priority: 150)
- Use thiserror for custom error types
- Never unwrap() in production
- Propagate with ? operator
### Shared Guidelines (priority: 50)
- Always use Result&lt;T&gt; for fallible operations
- Add context with .context()
[Links to full guideline documents]
</code></pre>
<p><strong>Result</strong>: New developer productive in days, not weeks.</p>
<hr />
<h2 id="use-case-2-architectural-decision-records-adr"><a class="header" href="#use-case-2-architectural-decision-records-adr">Use Case 2: Architectural Decision Records (ADR)</a></h2>
<h3 id="problem-1"><a class="header" href="#problem-1">Problem</a></h3>
<p>Team discussing whether to add Redis caching. Multiple opinions, need to document the decision for future reference.</p>
<h3 id="solution-with-kogral-1"><a class="header" href="#solution-with-kogral-1">Solution with KOGRAL</a></h3>
<p><strong>During meeting</strong>:</p>
<pre><code>Document decision: Add Redis caching for API responses
Context:
- Current database queries average 200ms
- 80% of queries are repeated within 5 minutes
- Need sub-50ms response times for user experience
Decision:
- Add Redis with 5-minute TTL for GET endpoints
- Cache invalidation on POST/PUT/DELETE
Consequences:
- ✅ Expected 80% reduction in database load
- ✅ Sub-50ms response times achieved
- ❌ Additional infrastructure (Redis server)
- ❌ Cache consistency challenges
- ❌ Debugging complexity increases
Status: proposed (pending infrastructure setup)
</code></pre>
<p><strong>Claude</strong> (creates ADR):</p>
<pre><code>Created: decision-add-redis-caching
Type: decision
Status: proposed
Added relationships:
- Relates to: note-performance-requirements
- Depends on: decision-use-docker-compose (infrastructure)
Decision documented and linked to related knowledge.
</code></pre>
<p><strong>Two months later</strong>, someone asks "Why Redis?":</p>
<pre><code>kb show decision-add-redis-caching
</code></pre>
<p>Full context available instantly.</p>
<hr />
<h2 id="use-case-3-pattern-library"><a class="header" href="#use-case-3-pattern-library">Use Case 3: Pattern Library</a></h2>
<h3 id="problem-2"><a class="header" href="#problem-2">Problem</a></h3>
<p>Team solves the same problems repeatedly:</p>
<ul>
<li>How to structure error types</li>
<li>Database connection patterns</li>
<li>Testing strategies</li>
</ul>
<p>Each developer rediscovers solutions.</p>
<h3 id="solution-with-kogral-2"><a class="header" href="#solution-with-kogral-2">Solution with KOGRAL</a></h3>
<p><strong>First time</strong> solving error handling:</p>
<pre><code class="language-bash">kb add pattern "Error Handling with thiserror" \
--tags rust,error-handling,pattern \
--content "$(cat docs/error-pattern.md)"
</code></pre>
<p><strong>Next time</strong> someone needs error handling:</p>
<pre><code>I'm implementing a new module with errors. What's our standard pattern?
</code></pre>
<p><strong>Claude</strong> (via <code>kogral/search</code>):</p>
<pre><code>Found pattern: Error Handling with thiserror
## Pattern: Error Handling with thiserror
### Problem
Need structured, type-safe error handling with good error messages.
### Solution
```rust
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ModuleError {
#[error("Database connection failed: {0}")]
Database(#[from] sqlx::Error),
#[error("Validation failed: {0}")]
Validation(String),
}
</code></pre>
<h3 id="usage"><a class="header" href="#usage">Usage</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn do_work() -&gt; Result&lt;(), ModuleError&gt; {
let conn = connect_db()?; // Auto-converts via #[from]
validate_input()?;
Ok(())
}
<span class="boring">}</span></code></pre></pre>
<p>[Full pattern with examples]</p>
<p>Would you like me to create a new error module following this pattern?</p>
<pre><code>
**Result**: Consistent error handling across codebase, zero time wasted rediscovering patterns.
---
## Use Case 4: Daily Development Journal
### Problem
Developer wants to track:
- What they worked on each day
- Blockers encountered
- Solutions discovered
- Ideas for future work
### Solution with KOGRAL
**End of each day**:
```bash
kb add journal "2026-01-17" --content "
## Progress
- Implemented user authentication
- Fixed race condition in cache layer
- Started work on API rate limiting
## Blockers
- SurrealDB migration tool not working with nested objects
- Need to discuss API versioning strategy with team
## Learnings
- tokio::select! is perfect for timeout patterns
- serde_json::Value useful for dynamic JSON handling
## Tomorrow
- Finish rate limiting implementation
- Review PR #234
- Team meeting: discuss API versioning
"
</code></pre>
<p><strong>With Claude Code</strong> (even easier):</p>
<pre><code>End of day journal:
Implemented authentication, fixed cache race condition, started rate limiting.
Blocked on SurrealDB migrations and need API versioning discussion.
Learned about tokio::select! for timeouts.
Tomorrow: finish rate limiting, review PR #234, versioning meeting.
</code></pre>
<p><strong>Claude</strong> (creates journal entry):</p>
<pre><code>Created journal entry: 2026-01-17
Tags auto-detected: authentication, cache, rate-limiting, surrealdb
Linked to:
- pattern-timeout-handling (implements)
- decision-use-surrealdb (relates_to)
Journal entry saved.
</code></pre>
<p><strong>Two weeks later</strong> (forgot how you solved that race condition):</p>
<pre><code>How did I fix the cache race condition?
</code></pre>
<p><strong>Claude</strong> (semantic search):</p>
<pre><code>From your journal entry on 2026-01-17:
"Fixed race condition in cache layer by using DashMap instead of
RwLock&lt;HashMap&gt;. DashMap provides lock-free concurrent access."
[Link to full journal entry]
</code></pre>
<p><strong>Result</strong>: Development history preserved, solutions easy to recall.</p>
<hr />
<h2 id="use-case-5-code-review-preparation"><a class="header" href="#use-case-5-code-review-preparation">Use Case 5: Code Review Preparation</a></h2>
<h3 id="problem-3"><a class="header" href="#problem-3">Problem</a></h3>
<p>Preparing for code review. Need to:</p>
<ul>
<li>Document why certain decisions were made</li>
<li>Link implementation to requirements</li>
<li>Explain trade-offs to reviewers</li>
</ul>
<h3 id="solution-with-kogral-3"><a class="header" href="#solution-with-kogral-3">Solution with KOGRAL</a></h3>
<p><strong>Before creating PR</strong>:</p>
<pre><code>I just implemented the caching layer.
Add a note documenting the implementation and link it to our caching decision.
</code></pre>
<p><strong>Claude</strong> (via <code>kb/add_note</code> + <code>kb/link</code>):</p>
<pre><code>Created note: note-caching-layer-implementation
Content:
# Caching Layer Implementation
## Approach
- Redis with 5-minute TTL
- Cache-aside pattern
- Invalidation on mutations
## Trade-offs
- Chose simplicity over advanced features (no LRU eviction)
- 5-minute TTL balances freshness vs cache hit rate
- Cache-aside pattern chosen over write-through (simpler)
## Testing
- Unit tests for cache logic
- Integration tests with real Redis
- Load test: 1000 req/s sustained
Links:
- Implements: decision-add-redis-caching
- Follows: pattern-cache-aside
- Tests: guideline-integration-testing
Note added and linked to decision-add-redis-caching.
</code></pre>
<p><strong>In PR description</strong>:</p>
<pre><code class="language-markdown">## Implementation
Implements caching layer per decision-add-redis-caching.
See note-caching-layer-implementation for detailed rationale.
## Trade-offs
[Copy from note]
</code></pre>
<p><strong>Reviewers</strong>:</p>
<pre><code>kb show decision-add-redis-caching
kb show note-caching-layer-implementation
</code></pre>
<p>Full context available, review focuses on code quality not "why this approach?"</p>
<hr />
<h2 id="use-case-6-cross-project-guidelines"><a class="header" href="#use-case-6-cross-project-guidelines">Use Case 6: Cross-Project Guidelines</a></h2>
<h3 id="problem-4"><a class="header" href="#problem-4">Problem</a></h3>
<p>Organization has 10 projects. Each should follow:</p>
<ul>
<li>Same Rust error handling patterns</li>
<li>Same security guidelines</li>
<li>Same testing standards</li>
</ul>
<p>Currently: duplicated wikis, inconsistent practices.</p>
<h3 id="solution-with-kogral-4"><a class="header" href="#solution-with-kogral-4">Solution with KOGRAL</a></h3>
<p><strong>Setup shared KOGRAL</strong>:</p>
<pre><code class="language-bash"># Create organization-wide shared KB
mkdir -p ~/org/.kogral-shared
cd ~/org/.kogral-shared
kb init --name "Organization Guidelines"
# Add shared guidelines
kb add guideline "Rust Error Handling" \
--language rust \
--category error-handling \
--content "$(cat org-rust-errors.md)"
kb add guideline "Security Best Practices" \
--category security \
--content "$(cat org-security.md)"
</code></pre>
<p><strong>In each project</strong>:</p>
<pre><code class="language-nickel"># project/.kogral/config.ncl
{
inheritance = {
base = "~/org/.kogral-shared",
priority = 100, # Project overrides shared
},
}
</code></pre>
<p><strong>Developer in Project A</strong>:</p>
<pre><code>What are our error handling guidelines?
</code></pre>
<p><strong>Claude</strong> (via <code>kb/get_guidelines</code> with inheritance):</p>
<pre><code>## Rust Error Handling Guidelines
### Shared Guidelines (priority: 50)
From ~/org/.kogral-shared:
- Use thiserror for custom errors
- Never unwrap() in production
- Always propagate with ?
### Project Guidelines (priority: 100)
From project .kogral/:
- Additionally: log all errors with tracing
- Use anyhow for application-level errors
[Combined effective guidelines]
</code></pre>
<p><strong>Result</strong>:</p>
<ul>
<li>Consistent practices across organization</li>
<li>Projects can specialize where needed</li>
<li>Single source of truth for shared knowledge</li>
</ul>
<hr />
<h2 id="use-case-7-bug-investigation"><a class="header" href="#use-case-7-bug-investigation">Use Case 7: Bug Investigation</a></h2>
<h3 id="problem-5"><a class="header" href="#problem-5">Problem</a></h3>
<p>Bug in production. Developer needs to:</p>
<ul>
<li>Understand relevant decisions</li>
<li>Find related code patterns</li>
<li>Check if similar issues were solved before</li>
</ul>
<h3 id="solution-with-kogral-5"><a class="header" href="#solution-with-kogral-5">Solution with KOGRAL</a></h3>
<p><strong>Developer</strong>:</p>
<pre><code>We have a race condition in the cache layer.
What past decisions or patterns might be relevant?
</code></pre>
<p><strong>Claude</strong> (semantic search):</p>
<pre><code>Found 4 relevant items:
1. **Decision: Use DashMap for Concurrent Access** (accepted, 1 month ago)
"Chose DashMap over RwLock&lt;HashMap&gt; to avoid race conditions in cache"
2. **Pattern: Safe Concurrent Cache Access**
"Always use lock-free data structures for high-contention caches"
3. **Note: Cache Race Condition Fix** (from 2026-01-17 journal)
"Fixed race in cache by switching to DashMap"
4. **Guideline: Concurrency Safety**
"Use atomic operations and lock-free structures where possible"
It looks like we've seen this before! Check the journal entry from 2026-01-17.
</code></pre>
<p><strong>Developer reads journal</strong>:</p>
<pre><code class="language-bash">kb show journal-2026-01-17
</code></pre>
<p><strong>Finds</strong>:</p>
<ul>
<li>Exact same issue</li>
<li>Solution: switch to DashMap</li>
<li>Code example included</li>
<li>Performance benchmarks</li>
</ul>
<p><strong>Applies fix</strong> in 10 minutes instead of 2 hours of debugging.</p>
<hr />
<h2 id="use-case-8-api-design-collaboration"><a class="header" href="#use-case-8-api-design-collaboration">Use Case 8: API Design Collaboration</a></h2>
<h3 id="problem-6"><a class="header" href="#problem-6">Problem</a></h3>
<p>Team discussing new API design. Multiple approaches:</p>
<ul>
<li>REST vs GraphQL</li>
<li>Versioning strategy</li>
<li>Authentication method</li>
</ul>
<p>Need to capture discussion and decision.</p>
<h3 id="solution-with-kogral-6"><a class="header" href="#solution-with-kogral-6">Solution with KOGRAL</a></h3>
<p><strong>During design meeting</strong> (using Claude Code):</p>
<pre><code>We're designing the new API. Let me capture this discussion:
Approaches discussed:
1. REST with versioned endpoints (/v1/, /v2/)
2. GraphQL with schema versioning
3. gRPC for internal services
Team consensus:
- REST for public API (easier for partners)
- GraphQL for internal admin tools (flexibility)
- gRPC for service-to-service (performance)
Create a decision record for each, linking them together.
</code></pre>
<p><strong>Claude</strong> (creates 3 linked ADRs):</p>
<pre><code>Created 3 decisions:
1. decision-rest-public-api
Status: accepted
Context: Public API needs broad compatibility
Decision: REST with /v1/, /v2/ versioning
2. decision-graphql-admin
Status: accepted
Context: Admin tools need flexible queries
Decision: GraphQL with schema evolution
3. decision-grpc-internal
Status: accepted
Context: Service-to-service needs performance
Decision: gRPC with protobuf
Linked:
decision-rest-public-api --[relates_to]--&gt; decision-graphql-admin
decision-rest-public-api --[relates_to]--&gt; decision-grpc-internal
decision-graphql-admin --[relates_to]--&gt; decision-grpc-internal
All decisions documented and interconnected.
</code></pre>
<p><strong>Two months later</strong>, someone asks "Why REST not GraphQL for public API?":</p>
<pre><code class="language-bash">kb show decision-rest-public-api
</code></pre>
<p>Full context preserved, including relationship to GraphQL decision.</p>
<hr />
<h2 id="summary"><a class="header" href="#summary">Summary</a></h2>
<p>Knowledge Base excels at:</p>
<p><strong>Onboarding</strong>: Historical context instantly available
<strong>Decision Making</strong>: ADRs preserve rationale
<strong>Pattern Reuse</strong>: Solutions documented once, used forever
<strong>Daily Tracking</strong>: Development journal with semantic search
<strong>Code Review</strong>: Implementation rationale linked to decisions
<strong>Cross-Project</strong>: Shared guidelines with project overrides
<strong>Bug Investigation</strong>: Past solutions easily discovered
<strong>Collaboration</strong>: Discussions captured and interconnected</p>
<p><strong>Common Theme</strong>: Knowledge captured during work, queryable when needed, connected to related concepts.</p>
<hr />
<h2 id="next-steps-5"><a class="header" href="#next-steps-5">Next Steps</a></h2>
<ul>
<li><strong>Start simple</strong>: <a href="guides/quickstart.html">Quick Start Guide</a></li>
<li><strong>Integrate AI</strong>: <a href="guides/../apps/mcp-quickguide.html">MCP Quick Guide</a></li>
<li><strong>Advanced features</strong>: <a href="guides/../config/overview.html">Configuration Reference</a></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="system-architecture"><a class="header" href="#system-architecture">System Architecture</a></h1>
<p>Comprehensive overview of the KOGRAL architecture.</p>
<h2 id="high-level-architecture"><a class="header" href="#high-level-architecture">High-Level Architecture</a></h2>
<p><img src="architecture/../diagrams/architecture-overview.svg" alt="Architecture Overview" /></p>
<p>The KOGRAL consists of three main layers:</p>
<ol>
<li><strong>User Interfaces</strong>: kb-cli (terminal), kb-mcp (AI integration), NuShell scripts (automation)</li>
<li><strong>Core Library (kb-core)</strong>: Rust library with graph engine, storage abstraction, embeddings, query engine</li>
<li><strong>Storage Backends</strong>: Filesystem (git-friendly), SurrealDB (scalable), In-Memory (cache/testing)</li>
</ol>
<h2 id="component-details"><a class="header" href="#component-details">Component Details</a></h2>
<h3 id="kb-cli-command-line-interface"><a class="header" href="#kb-cli-command-line-interface">kb-cli (Command-Line Interface)</a></h3>
<p><strong>Purpose</strong>: Primary user interface for local knowledge management.</p>
<p><strong>Commands</strong> (13 total):</p>
<ul>
<li><code>init</code>: Initialize <code>.kogral/</code> directory</li>
<li><code>add</code>: Create nodes (note, decision, guideline, pattern, journal)</li>
<li><code>search</code>: Text and semantic search</li>
<li><code>link</code>: Create relationships between nodes</li>
<li><code>list</code>: List all nodes</li>
<li><code>show</code>: Display node details</li>
<li><code>delete</code>: Remove nodes</li>
<li><code>graph</code>: Visualize knowledge graph</li>
<li><code>sync</code>: Sync filesystem ↔ SurrealDB</li>
<li><code>serve</code>: Start MCP server</li>
<li><code>import</code>: Import from Logseq</li>
<li><code>export</code>: Export to Logseq/JSON</li>
<li><code>config</code>: Manage configuration</li>
</ul>
<p><strong>Technology</strong>: Rust + clap (derive API)</p>
<p><strong>Features</strong>:</p>
<ul>
<li>Colored terminal output</li>
<li>Interactive prompts</li>
<li>Dry-run modes</li>
<li>Validation before operations</li>
</ul>
<h3 id="kb-mcp-mcp-server"><a class="header" href="#kb-mcp-mcp-server">kb-mcp (MCP Server)</a></h3>
<p><strong>Purpose</strong>: AI integration via Model Context Protocol.</p>
<p><strong>Protocol</strong>: JSON-RPC 2.0 over stdio</p>
<p><strong>Components</strong>:</p>
<ol>
<li>
<p><strong>Tools</strong> (7):</p>
<ul>
<li><code>kogral/search</code>: Query knowledge base</li>
<li><code>kb/add_note</code>: Create notes</li>
<li><code>kb/add_decision</code>: Create ADRs</li>
<li><code>kb/link</code>: Create relationships</li>
<li><code>kb/get_guidelines</code>: Retrieve guidelines with inheritance</li>
<li><code>kb/list_graphs</code>: List available graphs</li>
<li><code>kb/export</code>: Export to formats</li>
</ul>
</li>
<li>
<p><strong>Resources</strong> (6 URIs):</p>
<ul>
<li><code>kogral://project/notes</code></li>
<li><code>kogral://project/decisions</code></li>
<li><code>kogral://project/guidelines</code></li>
<li><code>kogral://project/patterns</code></li>
<li><code>kogral://shared/guidelines</code></li>
<li><code>kogral://shared/patterns</code></li>
</ul>
</li>
<li>
<p><strong>Prompts</strong> (2):</p>
<ul>
<li><code>kb/summarize_project</code>: Generate project summary</li>
<li><code>kb/find_related</code>: Find related nodes</li>
</ul>
</li>
</ol>
<p><strong>Integration</strong>: Claude Code via <code>~/.config/claude/config.json</code></p>
<h3 id="nushell-scripts"><a class="header" href="#nushell-scripts">NuShell Scripts</a></h3>
<p><strong>Purpose</strong>: Automation and maintenance tasks.</p>
<p><strong>Scripts</strong> (6):</p>
<ul>
<li><code>kb-sync.nu</code>: Filesystem ↔ SurrealDB sync</li>
<li><code>kb-backup.nu</code>: Archive knowledge base</li>
<li><code>kb-reindex.nu</code>: Rebuild embeddings</li>
<li><code>kb-import-logseq.nu</code>: Import from Logseq</li>
<li><code>kb-export-logseq.nu</code>: Export to Logseq</li>
<li><code>kb-stats.nu</code>: Graph statistics</li>
</ul>
<p><strong>Features</strong>:</p>
<ul>
<li>Colored output</li>
<li>Dry-run modes</li>
<li>Progress indicators</li>
<li>Error handling</li>
</ul>
<h2 id="core-library-kb-core"><a class="header" href="#core-library-kb-core">Core Library (kb-core)</a></h2>
<h3 id="models"><a class="header" href="#models">Models</a></h3>
<p><strong>Graph</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct Graph {
pub name: String,
pub version: String,
pub nodes: HashMap&lt;String, Node&gt;, // ID → Node
pub edges: Vec&lt;Edge&gt;,
pub metadata: HashMap&lt;String, Value&gt;,
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Node</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct Node {
pub id: String,
pub node_type: NodeType,
pub title: String,
pub content: String,
pub tags: Vec&lt;String&gt;,
pub status: NodeStatus,
pub created: DateTime&lt;Utc&gt;,
pub modified: DateTime&lt;Utc&gt;,
// ... relationships, metadata
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Edge</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct Edge {
pub from: String,
pub to: String,
pub relation: EdgeType,
pub strength: f32,
pub created: DateTime&lt;Utc&gt;,
}
<span class="boring">}</span></code></pre></pre>
<h3 id="storage-trait"><a class="header" href="#storage-trait">Storage Trait</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[async_trait]
pub trait Storage: Send + Sync {
async fn save_graph(&amp;self, graph: &amp;Graph) -&gt; Result&lt;()&gt;;
async fn load_graph(&amp;self, name: &amp;str) -&gt; Result&lt;Graph&gt;;
async fn delete_graph(&amp;self, name: &amp;str) -&gt; Result&lt;()&gt;;
async fn list_graphs(&amp;self) -&gt; Result&lt;Vec&lt;String&gt;&gt;;
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Implementations</strong>:</p>
<ol>
<li><code>FilesystemStorage</code>: Git-friendly markdown files</li>
<li><code>MemoryStorage</code>: In-memory with DashMap</li>
<li><code>SurrealDbStorage</code>: Scalable graph database</li>
</ol>
<h3 id="embedding-provider-trait"><a class="header" href="#embedding-provider-trait">Embedding Provider Trait</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[async_trait]
pub trait EmbeddingProvider: Send + Sync {
async fn embed(&amp;self, texts: Vec&lt;String&gt;) -&gt; Result&lt;Vec&lt;Vec&lt;f32&gt;&gt;&gt;;
fn dimensions(&amp;self) -&gt; usize;
fn model_name(&amp;self) -&gt; &amp;str;
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Implementations</strong>:</p>
<ol>
<li><code>FastEmbedProvider</code>: Local fastembed</li>
<li><code>RigEmbeddingProvider</code>: OpenAI, Claude, Ollama (via rig-core)</li>
</ol>
<h3 id="parser"><a class="header" href="#parser">Parser</a></h3>
<p><strong>Input</strong>: Markdown file with YAML frontmatter</p>
<p><strong>Output</strong>: <code>Node</code> struct</p>
<p><strong>Features</strong>:</p>
<ul>
<li>YAML frontmatter extraction</li>
<li>Markdown body parsing</li>
<li>Wikilink detection (<code>[[linked-note]]</code>)</li>
<li>Code reference parsing (<code>@file.rs:42</code>)</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><code class="language-markdown">---
id: note-123
type: note
title: My Note
tags: [rust, async]
---
# My Note
Content with [[other-note]] and @src/main.rs:10
</code></pre>
<p></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>Node {
id: "note-123",
node_type: NodeType::Note,
title: "My Note",
content: "Content with [[other-note]] and @src/main.rs:10",
tags: vec!["rust", "async"],
// ... parsed wikilinks, code refs
}
<span class="boring">}</span></code></pre></pre>
<h2 id="configuration-system"><a class="header" href="#configuration-system">Configuration System</a></h2>
<h3 id="nickel-schema"><a class="header" href="#nickel-schema">Nickel Schema</a></h3>
<pre><code class="language-nickel"># schemas/kb-config.ncl
{
KbConfig = {
graph | GraphConfig,
storage | StorageConfig,
embeddings | EmbeddingConfig,
templates | TemplateConfig,
query | QueryConfig,
mcp | McpConfig,
sync | SyncConfig,
},
}
</code></pre>
<h3 id="loading-process"><a class="header" href="#loading-process">Loading Process</a></h3>
<pre><code>User writes: .kogral/config.ncl
↓ [nickel export --format json]
JSON intermediate
↓ [serde_json::from_str]
KbConfig struct (Rust)
Runtime behavior
</code></pre>
<p><strong>Double Validation</strong>:</p>
<ol>
<li>Nickel contracts: Type-safe, enum validation</li>
<li>Serde deserialization: Rust type checking</li>
</ol>
<p><strong>Benefits</strong>:</p>
<ul>
<li>Errors caught at export time</li>
<li>Runtime guaranteed valid config</li>
<li>Self-documenting schemas</li>
</ul>
<h2 id="storage-architecture"><a class="header" href="#storage-architecture">Storage Architecture</a></h2>
<h3 id="hybrid-strategy"><a class="header" href="#hybrid-strategy">Hybrid Strategy</a></h3>
<p><strong>Local Graph</strong> (per project):</p>
<ul>
<li>Storage: Filesystem (<code>.kogral/</code> directory)</li>
<li>Format: Markdown + YAML frontmatter</li>
<li>Version control: Git</li>
<li>Scope: Project-specific knowledge</li>
</ul>
<p><strong>Shared Graph</strong> (organization):</p>
<ul>
<li>Storage: SurrealDB (or synced filesystem)</li>
<li>Format: Same markdown (for compatibility)</li>
<li>Version control: Optional</li>
<li>Scope: Organization-wide guidelines</li>
</ul>
<p><strong>Sync</strong>:</p>
<pre><code>Filesystem (.kogral/)
↕ [bidirectional sync]
SurrealDB (central)
</code></pre>
<h3 id="file-layout"><a class="header" href="#file-layout">File Layout</a></h3>
<pre><code>.kogral/
├── config.toml # Graph metadata
├── notes/
│ ├── async-patterns.md # Individual note
│ └── error-handling.md
├── decisions/
│ ├── 0001-use-rust.md # ADR format
│ └── 0002-surrealdb.md
├── guidelines/
│ ├── rust-errors.md # Project guideline
│ └── testing.md
├── patterns/
│ └── repository.md
└── journal/
├── 2026-01-17.md # Daily journal
└── 2026-01-18.md
</code></pre>
<h2 id="query-engine"><a class="header" href="#query-engine">Query Engine</a></h2>
<h3 id="text-search"><a class="header" href="#text-search">Text Search</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let results = graph.nodes.values()
.filter(|node| {
node.title.contains(&amp;query) ||
node.content.contains(&amp;query) ||
node.tags.iter().any(|tag| tag.contains(&amp;query))
})
.collect();
<span class="boring">}</span></code></pre></pre>
<h3 id="semantic-search-1"><a class="header" href="#semantic-search-1">Semantic Search</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let query_embedding = embeddings.embed(vec![query]).await?;
let mut scored: Vec&lt;_&gt; = graph.nodes.values()
.filter_map(|node| {
let node_embedding = node.embedding.as_ref()?;
let similarity = cosine_similarity(&amp;query_embedding[0], node_embedding);
(similarity &gt;= threshold).then_some((node, similarity))
})
.collect();
scored.sort_by(|a, b| b.1.partial_cmp(&amp;a.1).unwrap());
<span class="boring">}</span></code></pre></pre>
<h3 id="cross-graph-query"><a class="header" href="#cross-graph-query">Cross-Graph Query</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Query both project and shared graphs
let project_results = project_graph.search(&amp;query).await?;
let shared_results = shared_graph.search(&amp;query).await?;
// Merge with deduplication
let combined = merge_results(project_results, shared_results);
<span class="boring">}</span></code></pre></pre>
<h2 id="mcp-protocol-flow"><a class="header" href="#mcp-protocol-flow">MCP Protocol Flow</a></h2>
<pre><code>Claude Code kb-mcp kb-core
│ │ │
├─ JSON-RPC request ───→ │ │
│ kb/search │ │
│ {"query": "rust"} │ │
│ ├─ search() ──────────→ │
│ │ │
│ │ Query engine
│ │ Text + semantic
│ │ │
│ │ ←──── results ─────────┤
│ │ │
│ ←─ JSON-RPC response ──┤ │
│ {"results": [...]} │ │
</code></pre>
<h2 id="template-system"><a class="header" href="#template-system">Template System</a></h2>
<p><strong>Engine</strong>: Tera (Jinja2-like)</p>
<p><strong>Templates</strong>:</p>
<ol>
<li>
<p><strong>Document Templates</strong> (6):</p>
<ul>
<li><code>note.md.tera</code></li>
<li><code>decision.md.tera</code></li>
<li><code>guideline.md.tera</code></li>
<li><code>pattern.md.tera</code></li>
<li><code>journal.md.tera</code></li>
<li><code>execution.md.tera</code></li>
</ul>
</li>
<li>
<p><strong>Export Templates</strong> (4):</p>
<ul>
<li><code>logseq-page.md.tera</code></li>
<li><code>logseq-journal.md.tera</code></li>
<li><code>summary.md.tera</code></li>
<li><code>graph.json.tera</code></li>
</ul>
</li>
</ol>
<p><strong>Usage</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let mut tera = Tera::new("templates/**/*.tera")?;
let rendered = tera.render("note.md.tera", &amp;context)?;
<span class="boring">}</span></code></pre></pre>
<h2 id="error-handling"><a class="header" href="#error-handling">Error Handling</a></h2>
<p><strong>Strategy</strong>: <code>thiserror</code> for structured errors</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[derive(Error, Debug)]
pub enum KbError {
#[error("Storage error: {0}")]
Storage(String),
#[error("Node not found: {0}")]
NodeNotFound(String),
#[error("Configuration error: {0}")]
Config(String),
#[error("Parse error: {0}")]
Parse(String),
#[error("Embedding error: {0}")]
Embedding(String),
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Propagation</strong>: <code>?</code> operator throughout</p>
<h2 id="testing-strategy"><a class="header" href="#testing-strategy">Testing Strategy</a></h2>
<p><strong>Unit Tests</strong>: Per module (models, parser, storage)</p>
<p><strong>Integration Tests</strong>: Full workflow (add → save → load → query)</p>
<p><strong>Test Coverage</strong>:</p>
<ul>
<li>kb-core: 48 tests</li>
<li>kb-mcp: 5 tests</li>
<li>Total: 56 tests</li>
</ul>
<p><strong>Test Data</strong>: Fixtures in <code>tests/fixtures/</code></p>
<h2 id="performance-considerations"><a class="header" href="#performance-considerations">Performance Considerations</a></h2>
<p><strong>Node Lookup</strong>: O(1) via HashMap</p>
<p><strong>Semantic Search</strong>: O(n) with early termination (threshold filter)</p>
<p><strong>Storage</strong>:</p>
<ul>
<li>Filesystem: Lazy loading (load on demand)</li>
<li>Memory: Full graph in RAM</li>
<li>SurrealDB: Query optimization (indexes)</li>
</ul>
<p><strong>Embeddings</strong>:</p>
<ul>
<li>Cache embeddings in node metadata</li>
<li>Batch processing (configurable batch size)</li>
<li>Async generation (non-blocking)</li>
</ul>
<h2 id="security"><a class="header" href="#security">Security</a></h2>
<p><strong>No unsafe code</strong>: <code>#![forbid(unsafe_code)]</code></p>
<p><strong>Input validation</strong>:</p>
<ul>
<li>Nickel contracts validate config</li>
<li>serde validates JSON</li>
<li>Custom validation for user input</li>
</ul>
<p><strong>File operations</strong>:</p>
<ul>
<li>Path sanitization (no <code>../</code> traversal)</li>
<li>Permissions checking</li>
<li>Atomic writes (temp file + rename)</li>
</ul>
<h2 id="scalability"><a class="header" href="#scalability">Scalability</a></h2>
<p><strong>Small Projects</strong> (&lt; 1000 nodes):</p>
<ul>
<li>Filesystem storage</li>
<li>In-memory search</li>
<li>Local embeddings (fastembed)</li>
</ul>
<p><strong>Medium Projects</strong> (1000-10,000 nodes):</p>
<ul>
<li>Filesystem + SurrealDB sync</li>
<li>Semantic search with caching</li>
<li>Cloud embeddings (OpenAI/Claude)</li>
</ul>
<p><strong>Large Organizations</strong> (&gt; 10,000 nodes):</p>
<ul>
<li>SurrealDB primary</li>
<li>Distributed embeddings</li>
<li>Multi-graph federation</li>
</ul>
<h2 id="next-steps-6"><a class="header" href="#next-steps-6">Next Steps</a></h2>
<ul>
<li><strong>Graph Model Details</strong>: <a href="architecture/graph-model.html">Graph Model</a></li>
<li><strong>Storage Deep Dive</strong>: <a href="architecture/storage-architecture.html">Storage Architecture</a></li>
<li><strong>ADRs</strong>: <a href="architecture/adrs/001-nickel-vs-toml.html">Architectural Decisions</a></li>
<li><strong>Implementation</strong>: <a href="architecture/../contributing/development.html">Development Guide</a></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="graph-model"><a class="header" href="#graph-model">Graph Model</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="config-driven-architecture"><a class="header" href="#config-driven-architecture">Config-Driven Architecture</a></h1>
<p>The KOGRAL follows a <strong>config-driven architecture</strong> where all behavior is defined through Nickel configuration files rather than hardcoded in Rust.</p>
<h2 id="philosophy"><a class="header" href="#philosophy">Philosophy</a></h2>
<p><strong>"Configuration, not code, defines behavior"</strong></p>
<p>Instead of hardcoding storage backends, embedding providers, or query parameters, KB uses a layered configuration system that composes settings from multiple sources:</p>
<ol>
<li><strong>Schema contracts</strong> (type definitions)</li>
<li><strong>Defaults</strong> (base values)</li>
<li><strong>Mode overlays</strong> (dev/prod/test optimizations)</li>
<li><strong>User customizations</strong> (project-specific overrides)</li>
</ol>
<p>This approach provides:</p>
<ul>
<li><strong>Type safety</strong> - Nickel contracts validate configuration before runtime</li>
<li><strong>Composability</strong> - Mix and match configurations for different environments</li>
<li><strong>Discoverability</strong> - Self-documenting schemas with inline documentation</li>
<li><strong>Hot-reload</strong> - Change behavior without recompiling Rust code</li>
<li><strong>Double validation</strong> - Nickel contracts + serde ensure correctness</li>
</ul>
<h2 id="configuration-composition-flow"><a class="header" href="#configuration-composition-flow">Configuration Composition Flow</a></h2>
<p><img src="architecture/../diagrams/config-composition.svg" alt="Configuration Composition" /></p>
<p>The configuration system uses a <strong>four-layer composition pattern</strong>:</p>
<h3 id="layer-1-schema-contracts"><a class="header" href="#layer-1-schema-contracts">Layer 1: Schema Contracts</a></h3>
<p><strong>Location</strong>: <code>schemas/kb/contracts.ncl</code></p>
<p><strong>Purpose</strong>: Define types and validation rules using Nickel contracts.</p>
<p><strong>Example</strong>:</p>
<pre><code class="language-nickel">{
StorageType = [| 'filesystem, 'memory, 'surrealdb |],
StorageConfig = {
primary | StorageType
| doc "Primary storage backend"
| default = 'filesystem,
secondary | SecondaryStorageConfig
| doc "Optional secondary storage"
| default = { enabled = false },
},
}
</code></pre>
<p><strong>Benefits</strong>:</p>
<ul>
<li>Enum validation (only valid storage types accepted)</li>
<li>Required vs optional fields</li>
<li>Default values for optional fields</li>
<li>Documentation attached to types</li>
</ul>
<h3 id="layer-2-defaults"><a class="header" href="#layer-2-defaults">Layer 2: Defaults</a></h3>
<p><strong>Location</strong>: <code>schemas/kb/defaults.ncl</code></p>
<p><strong>Purpose</strong>: Provide sensible base values for all configuration options.</p>
<p><strong>Example</strong>:</p>
<pre><code class="language-nickel">{
base = {
storage = {
primary = 'filesystem,
secondary = {
enabled = false,
type = 'surrealdb,
url = "ws://localhost:8000",
},
},
embeddings = {
enabled = true,
provider = 'fastembed,
model = "BAAI/bge-small-en-v1.5",
dimensions = 384,
},
} | contracts.KbConfig,
}
</code></pre>
<p><strong>Validated by</strong>: <code>contracts.KbConfig</code> contract ensures defaults are valid.</p>
<h3 id="layer-3-mode-overlays"><a class="header" href="#layer-3-mode-overlays">Layer 3: Mode Overlays</a></h3>
<p><strong>Location</strong>: <code>schemas/kb/modes/{dev,prod,test}.ncl</code></p>
<p><strong>Purpose</strong>: Environment-specific optimizations and tuning.</p>
<h4 id="development-mode-devncl"><a class="header" href="#development-mode-devncl">Development Mode (<code>dev.ncl</code>)</a></h4>
<p>Optimized for: Fast iteration, local development, debugging</p>
<pre><code class="language-nickel">{
storage = {
primary = 'filesystem,
secondary = { enabled = false }, # No database overhead
},
embeddings = {
provider = 'fastembed, # Local, no API costs
},
sync = {
auto_index = false, # Manual control
},
}
</code></pre>
<h4 id="production-mode-prodncl"><a class="header" href="#production-mode-prodncl">Production Mode (<code>prod.ncl</code>)</a></h4>
<p>Optimized for: Performance, reliability, scalability</p>
<pre><code class="language-nickel">{
storage = {
secondary = { enabled = true }, # SurrealDB for scale
},
embeddings = {
provider = 'openai, # High-quality cloud embeddings
model = "text-embedding-3-small",
dimensions = 1536,
},
sync = {
auto_index = true,
debounce_ms = 300, # Fast response
},
}
</code></pre>
<h4 id="test-mode-testncl"><a class="header" href="#test-mode-testncl">Test Mode (<code>test.ncl</code>)</a></h4>
<p>Optimized for: Fast tests, isolation, determinism</p>
<pre><code class="language-nickel">{
storage = {
primary = 'memory, # Ephemeral, no disk I/O
},
embeddings = {
enabled = false, # Disable for speed
},
sync = {
auto_index = false,
debounce_ms = 0, # No delays in tests
},
}
</code></pre>
<h3 id="layer-4-user-customizations"><a class="header" href="#layer-4-user-customizations">Layer 4: User Customizations</a></h3>
<p><strong>Location</strong>: <code>.kb-config/core/kb.ncl</code> or <code>.kb-config/platform/{dev,prod,test}.ncl</code></p>
<p><strong>Purpose</strong>: Project-specific or deployment-specific overrides.</p>
<p><strong>Example</strong> (user project config):</p>
<pre><code class="language-nickel">let mode = import "../../schemas/kb/modes/dev.ncl" in
let user_custom = {
graph = {
name = "my-project",
},
embeddings = {
provider = 'claude, # Override to use Claude
model = "claude-3-haiku-20240307",
},
query = {
similarity_threshold = 0.7, # Stricter threshold
},
} in
helpers.compose_config defaults.base mode user_custom
| contracts.KbConfig
</code></pre>
<h2 id="composition-mechanism"><a class="header" href="#composition-mechanism">Composition Mechanism</a></h2>
<p>The <code>helpers.ncl</code> module provides the composition function:</p>
<pre><code class="language-nickel">{
# Recursively merge with override precedence
merge_with_override = fun base override =&gt; /* ... */,
# Compose three layers
compose_config = fun defaults mode_config user_custom =&gt;
let with_mode = merge_with_override defaults mode_config in
merge_with_override with_mode user_custom,
}
</code></pre>
<p><strong>Merge behavior</strong>:</p>
<ul>
<li>Records are merged recursively</li>
<li>Override values take precedence over base values</li>
<li>Arrays are not merged, override replaces base</li>
<li>Null in override keeps base value</li>
</ul>
<p><strong>Example merge</strong>:</p>
<pre><code class="language-nickel">base = { storage = { primary = 'filesystem }, embeddings = { enabled = true } }
override = { storage = { primary = 'memory } }
# Result: { storage = { primary = 'memory }, embeddings = { enabled = true } }
</code></pre>
<h2 id="export-to-json"><a class="header" href="#export-to-json">Export to JSON</a></h2>
<p>Once composed, the Nickel configuration is exported to JSON for Rust consumption:</p>
<pre><code class="language-bash">nickel export --format json .kb-config/core/kb.ncl &gt; .kb-config/targets/kb-core.json
</code></pre>
<p><strong>Output</strong> (<code>.kb-config/targets/kb-core.json</code>):</p>
<pre><code class="language-json">{
"graph": {
"name": "my-project",
"version": "1.0.0"
},
"storage": {
"primary": "memory",
"secondary": {
"enabled": false
}
},
"embeddings": {
"enabled": true,
"provider": "claude",
"model": "claude-3-haiku-20240307",
"dimensions": 768
}
}
</code></pre>
<h2 id="rust-integration"><a class="header" href="#rust-integration">Rust Integration</a></h2>
<p>The Rust code deserializes the JSON into typed structs using serde:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
pub struct KbConfig {
pub graph: GraphConfig,
pub storage: StorageConfig,
pub embeddings: EmbeddingConfig,
pub templates: TemplateConfig,
pub query: QueryConfig,
pub mcp: McpConfig,
pub sync: SyncConfig,
}
impl KbConfig {
pub fn from_file(path: &amp;Path) -&gt; Result&lt;Self&gt; {
let json = std::fs::read_to_string(path)?;
let config: KbConfig = serde_json::from_str(&amp;json)?;
Ok(config)
}
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Usage in kb-core</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let config = KbConfig::from_file(".kb-config/targets/kb-core.json")?;
// Config drives behavior
let storage: Box&lt;dyn Storage&gt; = match config.storage.primary {
StorageType::Filesystem =&gt; Box::new(FilesystemStorage::new(&amp;config)?),
StorageType::Memory =&gt; Box::new(MemoryStorage::new()),
StorageType::SurrealDb =&gt; Box::new(SurrealDbStorage::new(&amp;config).await?),
};
let embeddings: Box&lt;dyn EmbeddingProvider&gt; = match config.embeddings.provider {
EmbeddingProviderType::FastEmbed =&gt; Box::new(FastEmbedProvider::new()?),
EmbeddingProviderType::OpenAI =&gt; Box::new(RigEmbeddingProvider::openai(&amp;config)?),
EmbeddingProviderType::Claude =&gt; Box::new(RigEmbeddingProvider::claude(&amp;config)?),
EmbeddingProviderType::Ollama =&gt; Box::new(RigEmbeddingProvider::ollama(&amp;config)?),
};
<span class="boring">}</span></code></pre></pre>
<h2 id="double-validation"><a class="header" href="#double-validation">Double Validation</a></h2>
<p>Configuration is validated <strong>twice</strong>:</p>
<h3 id="1-nickel-contract-validation"><a class="header" href="#1-nickel-contract-validation">1. Nickel Contract Validation</a></h3>
<p>At export time, Nickel validates:</p>
<ul>
<li>✅ Types match contracts (e.g., <code>primary | StorageType</code>)</li>
<li>✅ Required fields are present</li>
<li>✅ Enums have valid values</li>
<li>✅ Nested structure is correct</li>
</ul>
<p><strong>Error example</strong>:</p>
<pre><code>error: contract broken by a value
┌─ .kb-config/core/kb.ncl:15:5
15│ primary = 'invalid,
│ ^^^^^^^^^^^^^^^^^^^ applied to this expression
= This value is not in the enum ['filesystem, 'memory, 'surrealdb]
</code></pre>
<h3 id="2-serde-deserialization-validation"><a class="header" href="#2-serde-deserialization-validation">2. Serde Deserialization Validation</a></h3>
<p>At runtime, serde validates:</p>
<ul>
<li>✅ JSON structure matches Rust types</li>
<li>✅ Field names match (with rename support)</li>
<li>✅ Values can be converted to Rust types</li>
<li>✅ Required fields are not null</li>
</ul>
<p><strong>Error example</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>Error: missing field `graph` at line 1 column 123
<span class="boring">}</span></code></pre></pre>
<h2 id="benefits-of-config-driven-architecture"><a class="header" href="#benefits-of-config-driven-architecture">Benefits of Config-Driven Architecture</a></h2>
<h3 id="1-zero-hardcoding"><a class="header" href="#1-zero-hardcoding">1. Zero Hardcoding</a></h3>
<p><strong>Bad</strong> (hardcoded):</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Hardcoded - requires recompilation to change
let storage = FilesystemStorage::new("/fixed/path");
let threshold = 0.6;
<span class="boring">}</span></code></pre></pre>
<p><strong>Good</strong> (config-driven):</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Config-driven - change via .ncl file
let storage = create_storage(&amp;config)?;
let threshold = config.query.similarity_threshold;
<span class="boring">}</span></code></pre></pre>
<h3 id="2-environment-flexibility"><a class="header" href="#2-environment-flexibility">2. Environment Flexibility</a></h3>
<p>Same codebase, different behavior:</p>
<pre><code class="language-bash"># Development
nickel export .kb-config/platform/dev.ncl &gt; targets/kb-core.json
# → Filesystem storage, fastembed, no auto-sync
# Production
nickel export .kb-config/platform/prod.ncl &gt; targets/kb-core.json
# → SurrealDB enabled, OpenAI embeddings, auto-sync
# Testing
nickel export .kb-config/platform/test.ncl &gt; targets/kb-core.json
# → In-memory storage, no embeddings, isolated
</code></pre>
<h3 id="3-self-documenting"><a class="header" href="#3-self-documenting">3. Self-Documenting</a></h3>
<p>Nickel contracts include inline documentation:</p>
<pre><code class="language-nickel">StorageType = [| 'filesystem, 'memory, 'surrealdb |]
| doc "Storage backend type: filesystem (git-tracked), memory (ephemeral), surrealdb (scalable)",
</code></pre>
<p>IDEs can show this documentation when editing <code>.ncl</code> files.</p>
<h3 id="4-type-safe-evolution"><a class="header" href="#4-type-safe-evolution">4. Type-Safe Evolution</a></h3>
<p>When adding new features:</p>
<ol>
<li>Update contract in <code>contracts.ncl</code></li>
<li>Add default in <code>defaults.ncl</code></li>
<li>Export validates existing configs</li>
<li>Rust compilation validates deserialization</li>
</ol>
<p>Breaking changes are caught <strong>before runtime</strong>.</p>
<h3 id="5-testability"><a class="header" href="#5-testability">5. Testability</a></h3>
<p>Different test scenarios without code changes:</p>
<pre><code class="language-nickel"># test-semantic-search.ncl
let test_config = defaults.base &amp; {
embeddings = { enabled = true, provider = 'fastembed },
query = { similarity_threshold = 0.3 },
} in test_config
</code></pre>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[test]
fn test_semantic_search() {
let config = KbConfig::from_file("test-semantic-search.json")?;
// Config drives test behavior
}
<span class="boring">}</span></code></pre></pre>
<h2 id="configuration-discovery"><a class="header" href="#configuration-discovery">Configuration Discovery</a></h2>
<p>KB tools automatically discover configuration:</p>
<ol>
<li><strong>Check <code>.kb-config/targets/kb-core.json</code></strong> (pre-exported)</li>
<li><strong>Check <code>.kb-config/core/kb.ncl</code></strong> (export on-demand)</li>
<li><strong>Check environment variable <code>KB_CONFIG</code></strong></li>
<li><strong>Fall back to embedded defaults</strong></li>
</ol>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl KbConfig {
pub fn discover() -&gt; Result&lt;Self&gt; {
if let Ok(config) = Self::from_file(".kb-config/targets/kb-core.json") {
return Ok(config);
}
if Path::new(".kb-config/core/kb.ncl").exists() {
// Export and load
let output = Command::new("nickel")
.args(["export", "--format", "json", ".kb-config/core/kb.ncl"])
.output()?;
return serde_json::from_slice(&amp;output.stdout)?;
}
if let Ok(path) = std::env::var("KB_CONFIG") {
return Self::from_file(&amp;path);
}
Ok(Self::default()) // Embedded defaults
}
}
<span class="boring">}</span></code></pre></pre>
<h2 id="integration-with-justfile"><a class="header" href="#integration-with-justfile">Integration with justfile</a></h2>
<p>The <code>justfile</code> integrates configuration validation:</p>
<pre><code class="language-just"># Validate all Nickel configs
nickel-validate-all:
@echo "Validating Nickel schemas..."
nickel typecheck schemas/kb/contracts.ncl
nickel typecheck schemas/kb/defaults.ncl
nickel typecheck schemas/kb/helpers.ncl
nickel typecheck schemas/kb/modes/dev.ncl
nickel typecheck schemas/kb/modes/prod.ncl
nickel typecheck schemas/kb/modes/test.ncl
nickel typecheck .kb-config/core/kb.ncl
# Export all platform configs
nickel-export-all:
@echo "Exporting platform configs to JSON..."
@mkdir -p .kb-config/targets
nickel export --format json .kb-config/platform/dev.ncl &gt; .kb-config/targets/kb-dev.json
nickel export --format json .kb-config/platform/prod.ncl &gt; .kb-config/targets/kb-prod.json
nickel export --format json .kb-config/platform/test.ncl &gt; .kb-config/targets/kb-test.json
@echo "Exported 3 configurations to .kb-config/targets/"
</code></pre>
<p>Usage:</p>
<pre><code class="language-bash">just nickel::validate-all # Check configs are valid
just nickel::export-all # Generate JSON for all environments
</code></pre>
<h2 id="best-practices-1"><a class="header" href="#best-practices-1">Best Practices</a></h2>
<h3 id="1-never-hardcode"><a class="header" href="#1-never-hardcode">1. Never Hardcode</a></h3>
<p>If it's behavior, it's config:</p>
<ul>
<li>Storage paths</li>
<li>API endpoints</li>
<li>Thresholds</li>
<li>Timeouts</li>
<li>Feature flags</li>
<li>Provider selection</li>
</ul>
<h3 id="2-use-modes-for-environment-differences"><a class="header" href="#2-use-modes-for-environment-differences">2. Use Modes for Environment Differences</a></h3>
<p>Don't put environment-specific values in user config:</p>
<p><strong>Bad</strong>:</p>
<pre><code class="language-nickel"># user config with env-specific values
{
storage = {
url = if env == "prod" then "prod-url" else "dev-url" # Don't do this
}
}
</code></pre>
<p><strong>Good</strong>:</p>
<pre><code class="language-nickel"># modes/prod.ncl
{ storage = { url = "prod-url" } }
# modes/dev.ncl
{ storage = { url = "dev-url" } }
# user config is environment-agnostic
{ graph = { name = "my-project" } }
</code></pre>
<h3 id="3-document-complex-fields"><a class="header" href="#3-document-complex-fields">3. Document Complex Fields</a></h3>
<p>Use Nickel's <code>doc</code> metadata:</p>
<pre><code class="language-nickel">similarity_threshold | Number
| doc "Minimum cosine similarity (0-1) for semantic search matches. Higher = stricter."
| default = 0.6,
</code></pre>
<h3 id="4-validate-early"><a class="header" href="#4-validate-early">4. Validate Early</a></h3>
<p>Run <code>nickel typecheck</code> in CI/CD before building Rust code.</p>
<h3 id="5-version-configs"><a class="header" href="#5-version-configs">5. Version Configs</a></h3>
<p>Track <code>.ncl</code> files in git, ignore <code>.kb-config/targets/*.json</code> (generated).</p>
<h2 id="see-also"><a class="header" href="#see-also">See Also</a></h2>
<ul>
<li><strong>Schema Reference</strong>: <a href="architecture/../config/schema.html">Configuration Schema</a></li>
<li><strong>User Guide</strong>: <a href="architecture/../config/overview.html">Configuration Guide</a></li>
<li><strong>ADR</strong>: <a href="architecture/adrs/001-nickel-vs-toml.html">Why Nickel vs TOML</a></li>
<li><strong>Examples</strong>: <code>.kb-config/core/kb.ncl</code>, <code>.kb-config/platform/*.ncl</code></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="storage-architecture-1"><a class="header" href="#storage-architecture-1">Storage Architecture</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="logseq-blocks-support---architecture-design"><a class="header" href="#logseq-blocks-support---architecture-design">Logseq Blocks Support - Architecture Design</a></h1>
<h2 id="problem-statement"><a class="header" href="#problem-statement">Problem Statement</a></h2>
<p>Logseq uses <strong>content blocks</strong> as the fundamental unit of information, not full documents. Each block can have:</p>
<ul>
<li><strong>Properties</strong>: <code>#card</code>, <code>TODO</code>, <code>DONE</code>, custom properties</li>
<li><strong>Tags</strong>: Inline tags like <code>#flashcard</code>, <code>#important</code></li>
<li><strong>References</strong>: Block references <code>((block-id))</code>, page references <code>[[page]]</code></li>
<li><strong>Nesting</strong>: Outliner-style hierarchy (parent-child blocks)</li>
<li><strong>Metadata</strong>: Block-level properties (unlike page-level frontmatter)</li>
</ul>
<p><strong>Current KB limitation</strong>: Nodes only have <code>content: String</code> (flat markdown). Importing from Logseq loses block structure and properties.</p>
<p><strong>Requirement</strong>: Support round-trip import/export with full block fidelity:</p>
<pre><code>Logseq Graph → KOGRAL Import → KOGRAL Storage → KOGRAL Export → Logseq Graph
(blocks preserved) (blocks preserved)
</code></pre>
<h2 id="use-cases-3"><a class="header" href="#use-cases-3">Use Cases</a></h2>
<h3 id="1-flashcards-card"><a class="header" href="#1-flashcards-card">1. Flashcards (<code>#card</code>)</a></h3>
<p><strong>Logseq</strong>:</p>
<pre><code class="language-markdown">- What is Rust's ownership model? #card
- Rust uses ownership, borrowing, and lifetimes
- Three rules: one owner, many borrows XOR one mutable
</code></pre>
<p><strong>KB needs to preserve</strong>:</p>
<ul>
<li>Block with <code>#card</code> property</li>
<li>Nested answer blocks</li>
<li>Ability to query all cards</li>
</ul>
<h3 id="2-task-tracking-tododone"><a class="header" href="#2-task-tracking-tododone">2. Task Tracking (<code>TODO</code>/<code>DONE</code>)</a></h3>
<p><strong>Logseq</strong>:</p>
<pre><code class="language-markdown">- TODO Implement block parser #rust
- DONE Research block structure
- TODO Write parser tests
</code></pre>
<p><strong>KB needs to preserve</strong>:</p>
<ul>
<li>Task status per block</li>
<li>Hierarchical task breakdown</li>
<li>Tags on tasks</li>
</ul>
<h3 id="3-block-references"><a class="header" href="#3-block-references">3. Block References</a></h3>
<p><strong>Logseq</strong>:</p>
<pre><code class="language-markdown">- Core concept: ((block-uuid-123))
- See also: [[Related Page]]
</code></pre>
<p><strong>KB needs to preserve</strong>:</p>
<ul>
<li>Block-to-block links (not just page-to-page)</li>
<li>UUID references</li>
</ul>
<h3 id="4-block-properties"><a class="header" href="#4-block-properties">4. Block Properties</a></h3>
<p><strong>Logseq</strong>:</p>
<pre><code class="language-markdown">- This is a block with properties
property1:: value1
property2:: value2
</code></pre>
<p><strong>KB needs to preserve</strong>:</p>
<ul>
<li>Custom key-value properties per block</li>
<li>Property inheritance/override</li>
</ul>
<h2 id="design-options"><a class="header" href="#design-options">Design Options</a></h2>
<h3 id="option-a-blocks-as-first-class-data-structure"><a class="header" href="#option-a-blocks-as-first-class-data-structure">Option A: Blocks as First-Class Data Structure</a></h3>
<p><strong>Add <code>blocks</code> field to Node</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct Node {
// ... existing fields ...
pub content: String, // Backward compat: flat markdown
pub blocks: Option&lt;Vec&lt;Block&gt;&gt;, // NEW: Structured blocks
}
pub struct Block {
pub id: String, // UUID or auto-generated
pub content: String, // Block text
pub properties: BlockProperties, // Tags, status, custom props
pub children: Vec&lt;Block&gt;, // Nested blocks
pub created: DateTime&lt;Utc&gt;,
pub modified: DateTime&lt;Utc&gt;,
}
pub struct BlockProperties {
pub tags: Vec&lt;String&gt;, // #card, #important
pub status: Option&lt;TaskStatus&gt;, // TODO, DONE, WAITING
pub custom: HashMap&lt;String, String&gt;, // property:: value
}
pub enum TaskStatus {
Todo,
Doing,
Done,
Waiting,
Cancelled,
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Pros</strong>:</p>
<ul>
<li>✅ Type-safe, explicit structure</li>
<li>✅ Queryable (find all #card blocks)</li>
<li>✅ Preserves hierarchy</li>
<li>✅ Supports block-level operations</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>❌ Adds complexity to Node</li>
<li>❌ Dual representation (content + blocks)</li>
<li>❌ Requires migration of existing data</li>
</ul>
<h3 id="option-b-parser-only-approach"><a class="header" href="#option-b-parser-only-approach">Option B: Parser-Only Approach</a></h3>
<p><strong>Keep <code>content: String</code>, parse blocks on-demand</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct BlockParser;
impl BlockParser {
// Parse markdown content into block structure
fn parse(content: &amp;str) -&gt; Vec&lt;Block&gt;;
// Serialize blocks back to markdown
fn serialize(blocks: &amp;[Block]) -&gt; String;
}
// Usage
let blocks = BlockParser::parse(&amp;node.content);
let filtered = blocks.iter().filter(|b| b.properties.tags.contains("card"));
<span class="boring">}</span></code></pre></pre>
<p><strong>Pros</strong>:</p>
<ul>
<li>✅ No schema changes</li>
<li>✅ Backward compatible</li>
<li>✅ Simple storage (still just String)</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>❌ Parse overhead on every access</li>
<li>❌ Can't query blocks in database (SurrealDB)</li>
<li>❌ Harder to index/search blocks</li>
</ul>
<h3 id="option-c-hybrid-approach-recommended"><a class="header" href="#option-c-hybrid-approach-recommended">Option C: Hybrid Approach (RECOMMENDED)</a></h3>
<p><strong>Combine both: structured storage + lazy parsing</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct Node {
// ... existing fields ...
pub content: String, // Source of truth (markdown)
#[serde(skip_serializing_if = "Option::is_none")]
pub blocks: Option&lt;Vec&lt;Block&gt;&gt;, // Cached structure (parsed)
}
impl Node {
// Parse blocks from content if not already cached
pub fn get_blocks(&amp;mut self) -&gt; &amp;Vec&lt;Block&gt; {
if self.blocks.is_none() {
self.blocks = Some(BlockParser::parse(&amp;self.content));
}
self.blocks.as_ref().unwrap()
}
// Update content from blocks (when blocks modified)
pub fn sync_blocks_to_content(&amp;mut self) {
if let Some(ref blocks) = self.blocks {
self.content = BlockParser::serialize(blocks);
}
}
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Storage Strategy</strong>:</p>
<ol>
<li>
<p><strong>Filesystem</strong> - Store as markdown (Logseq compatible):</p>
<pre><code class="language-markdown">- Block 1 #card
- Nested block
- Block 2 TODO
</code></pre>
</li>
<li>
<p><strong>SurrealDB</strong> - Store both:</p>
<pre><code class="language-sql">DEFINE TABLE block SCHEMAFULL;
DEFINE FIELD node_id ON block TYPE record(node);
DEFINE FIELD block_id ON block TYPE string;
DEFINE FIELD content ON block TYPE string;
DEFINE FIELD properties ON block TYPE object;
DEFINE FIELD parent_id ON block TYPE option&lt;string&gt;;
-- Index for queries
DEFINE INDEX block_tags ON block COLUMNS properties.tags;
DEFINE INDEX block_status ON block COLUMNS properties.status;
</code></pre>
</li>
</ol>
<p><strong>Pros</strong>:</p>
<ul>
<li>✅ Best of both worlds</li>
<li>✅ Filesystem stays Logseq-compatible</li>
<li>✅ SurrealDB can query blocks</li>
<li>✅ Lazy parsing (only when needed)</li>
<li>✅ Backward compatible</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>⚠️ Need to keep content/blocks in sync</li>
<li>⚠️ More complex implementation</li>
</ul>
<h2 id="recommended-implementation"><a class="header" href="#recommended-implementation">Recommended Implementation</a></h2>
<p><strong>Phase 1: Data Model</strong></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// crates/kb-core/src/models/block.rs
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// A content block (Logseq-style)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Block {
/// Unique block identifier (UUID)
pub id: String,
/// Block content (markdown text, excluding nested blocks)
pub content: String,
/// Block properties (tags, status, custom)
pub properties: BlockProperties,
/// Child blocks (nested hierarchy)
#[serde(default)]
pub children: Vec&lt;Block&gt;,
/// Creation timestamp
pub created: DateTime&lt;Utc&gt;,
/// Last modification timestamp
pub modified: DateTime&lt;Utc&gt;,
/// Parent block ID (if nested)
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_id: Option&lt;String&gt;,
}
/// Block-level properties
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct BlockProperties {
/// Tags (e.g., #card, #important)
#[serde(default)]
pub tags: Vec&lt;String&gt;,
/// Task status (TODO, DONE, etc.)
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option&lt;TaskStatus&gt;,
/// Custom properties (property:: value)
#[serde(default)]
pub custom: HashMap&lt;String, String&gt;,
/// Block references ((uuid))
#[serde(default)]
pub block_refs: Vec&lt;String&gt;,
/// Page references ([[page]])
#[serde(default)]
pub page_refs: Vec&lt;String&gt;,
}
/// Task status for TODO blocks
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum TaskStatus {
Todo,
Doing,
Done,
Later,
Now,
Waiting,
Cancelled,
}
impl Block {
/// Create a new block with content
pub fn new(content: String) -&gt; Self {
use uuid::Uuid;
Self {
id: Uuid::new_v4().to_string(),
content,
properties: BlockProperties::default(),
children: Vec::new(),
created: Utc::now(),
modified: Utc::now(),
parent_id: None,
}
}
/// Add a child block
pub fn add_child(&amp;mut self, mut child: Block) {
child.parent_id = Some(self.id.clone());
self.children.push(child);
self.modified = Utc::now();
}
/// Add a tag to this block
pub fn add_tag(&amp;mut self, tag: String) {
if !self.properties.tags.contains(&amp;tag) {
self.properties.tags.push(tag);
self.modified = Utc::now();
}
}
/// Set task status
pub fn set_status(&amp;mut self, status: TaskStatus) {
self.properties.status = Some(status);
self.modified = Utc::now();
}
/// Get all blocks (self + descendants) as flat list
pub fn flatten(&amp;self) -&gt; Vec&lt;&amp;Block&gt; {
let mut result = vec![self];
for child in &amp;self.children {
result.extend(child.flatten());
}
result
}
/// Find block by ID in tree
pub fn find(&amp;self, id: &amp;str) -&gt; Option&lt;&amp;Block&gt; {
if self.id == id {
return Some(self);
}
for child in &amp;self.children {
if let Some(found) = child.find(id) {
return Some(found);
}
}
None
}
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Phase 2: Update Node Model</strong></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// crates/kb-core/src/models.rs (modifications)
use crate::models::block::Block;
pub struct Node {
// ... existing fields ...
pub content: String,
/// Structured blocks (optional, parsed from content)
#[serde(skip_serializing_if = "Option::is_none")]
pub blocks: Option&lt;Vec&lt;Block&gt;&gt;,
}
impl Node {
/// Get blocks, parsing from content if needed
pub fn get_blocks(&amp;mut self) -&gt; Result&lt;&amp;Vec&lt;Block&gt;&gt; {
if self.blocks.is_none() {
self.blocks = Some(crate::parser::BlockParser::parse(&amp;self.content)?);
}
Ok(self.blocks.as_ref().unwrap())
}
/// Update content from blocks
pub fn sync_blocks_to_content(&amp;mut self) {
if let Some(ref blocks) = self.blocks {
self.content = crate::parser::BlockParser::serialize(blocks);
}
}
/// Find all blocks with a specific tag
pub fn find_blocks_by_tag(&amp;mut self, tag: &amp;str) -&gt; Result&lt;Vec&lt;&amp;Block&gt;&gt; {
let blocks = self.get_blocks()?;
let mut result = Vec::new();
for block in blocks {
for b in block.flatten() {
if b.properties.tags.iter().any(|t| t == tag) {
result.push(b);
}
}
}
Ok(result)
}
/// Find all TODO blocks
pub fn find_todos(&amp;mut self) -&gt; Result&lt;Vec&lt;&amp;Block&gt;&gt; {
let blocks = self.get_blocks()?;
let mut result = Vec::new();
for block in blocks {
for b in block.flatten() {
if matches!(b.properties.status, Some(TaskStatus::Todo)) {
result.push(b);
}
}
}
Ok(result)
}
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Phase 3: Block Parser</strong></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// crates/kb-core/src/parser/block_parser.rs
use crate::models::block::{Block, BlockProperties, TaskStatus};
use regex::Regex;
pub struct BlockParser;
impl BlockParser {
/// Parse markdown content into block structure
///
/// Handles:
/// - Outliner format (- prefix with indentation)
/// - Tags (#card, #important)
/// - Task status (TODO, DONE)
/// - Properties (property:: value)
/// - Block references (((uuid)))
/// - Page references ([[page]])
pub fn parse(content: &amp;str) -&gt; Result&lt;Vec&lt;Block&gt;&gt; {
let mut blocks = Vec::new();
let mut stack: Vec&lt;(usize, Block)&gt; = Vec::new(); // (indent_level, block)
for line in content.lines() {
// Detect indentation level
let indent = count_indent(line);
let trimmed = line.trim_start();
// Skip empty lines
if trimmed.is_empty() {
continue;
}
// Parse block line
if let Some(block_content) = trimmed.strip_prefix("- ") {
let mut block = Self::parse_block_line(block_content)?;
// Pop stack until we find parent level
while let Some((level, _)) = stack.last() {
if *level &lt; indent {
break;
}
stack.pop();
}
// Add as child to parent or as root
if let Some((_, parent)) = stack.last_mut() {
parent.add_child(block.clone());
} else {
blocks.push(block.clone());
}
stack.push((indent, block));
}
}
Ok(blocks)
}
/// Parse a single block line (after "- " prefix)
fn parse_block_line(line: &amp;str) -&gt; Result&lt;Block&gt; {
let mut block = Block::new(String::new());
let mut properties = BlockProperties::default();
// Extract task status (TODO, DONE, etc.)
let (status, remaining) = Self::extract_task_status(line);
properties.status = status;
// Extract tags (#card, #important)
let (tags, remaining) = Self::extract_tags(remaining);
properties.tags = tags;
// Extract properties (property:: value)
let (custom_props, remaining) = Self::extract_properties(remaining);
properties.custom = custom_props;
// Extract block references (((uuid)))
let (block_refs, remaining) = Self::extract_block_refs(remaining);
properties.block_refs = block_refs;
// Extract page references ([[page]])
let (page_refs, content) = Self::extract_page_refs(remaining);
properties.page_refs = page_refs;
block.content = content.trim().to_string();
block.properties = properties;
Ok(block)
}
/// Serialize blocks back to markdown
pub fn serialize(blocks: &amp;[Block]) -&gt; String {
let mut result = String::new();
for block in blocks {
Self::serialize_block(&amp;mut result, block, 0);
}
result
}
fn serialize_block(output: &amp;mut String, block: &amp;Block, indent: usize) {
// Write indent
for _ in 0..indent {
output.push_str(" ");
}
// Write prefix
output.push_str("- ");
// Write task status
if let Some(status) = block.properties.status {
output.push_str(&amp;format!("{:?} ", status).to_uppercase());
}
// Write content
output.push_str(&amp;block.content);
// Write tags
for tag in &amp;block.properties.tags {
output.push_str(&amp;format!(" #{}", tag));
}
// Write properties
if !block.properties.custom.is_empty() {
output.push('\n');
for (key, value) in &amp;block.properties.custom {
for _ in 0..=indent {
output.push_str(" ");
}
output.push_str(&amp;format!("{}:: {}\n", key, value));
}
}
output.push('\n');
// Write children recursively
for child in &amp;block.children {
Self::serialize_block(output, child, indent + 1);
}
}
// Helper methods for extraction
fn extract_task_status(line: &amp;str) -&gt; (Option&lt;TaskStatus&gt;, &amp;str) {
let line = line.trim_start();
if let Some(rest) = line.strip_prefix("TODO ") {
(Some(TaskStatus::Todo), rest)
} else if let Some(rest) = line.strip_prefix("DONE ") {
(Some(TaskStatus::Done), rest)
} else if let Some(rest) = line.strip_prefix("DOING ") {
(Some(TaskStatus::Doing), rest)
} else if let Some(rest) = line.strip_prefix("LATER ") {
(Some(TaskStatus::Later), rest)
} else if let Some(rest) = line.strip_prefix("NOW ") {
(Some(TaskStatus::Now), rest)
} else if let Some(rest) = line.strip_prefix("WAITING ") {
(Some(TaskStatus::Waiting), rest)
} else if let Some(rest) = line.strip_prefix("CANCELLED ") {
(Some(TaskStatus::Cancelled), rest)
} else {
(None, line)
}
}
fn extract_tags(line: &amp;str) -&gt; (Vec&lt;String&gt;, String) {
let tag_regex = Regex::new(r"#(\w+)").unwrap();
let mut tags = Vec::new();
let mut result = line.to_string();
for cap in tag_regex.captures_iter(line) {
if let Some(tag) = cap.get(1) {
tags.push(tag.as_str().to_string());
result = result.replace(&amp;format!("#{}", tag.as_str()), "");
}
}
(tags, result.trim().to_string())
}
fn extract_properties(line: &amp;str) -&gt; (HashMap&lt;String, String&gt;, String) {
let prop_regex = Regex::new(r"(\w+)::\s*([^\n]+)").unwrap();
let mut props = HashMap::new();
let mut result = line.to_string();
for cap in prop_regex.captures_iter(line) {
if let (Some(key), Some(value)) = (cap.get(1), cap.get(2)) {
props.insert(key.as_str().to_string(), value.as_str().trim().to_string());
result = result.replace(&amp;cap[0], "");
}
}
(props, result.trim().to_string())
}
fn extract_block_refs(line: &amp;str) -&gt; (Vec&lt;String&gt;, String) {
let ref_regex = Regex::new(r"\(\(([^)]+)\)\)").unwrap();
let mut refs = Vec::new();
let mut result = line.to_string();
for cap in ref_regex.captures_iter(line) {
if let Some(uuid) = cap.get(1) {
refs.push(uuid.as_str().to_string());
result = result.replace(&amp;cap[0], "");
}
}
(refs, result.trim().to_string())
}
fn extract_page_refs(line: &amp;str) -&gt; (Vec&lt;String&gt;, String) {
let page_regex = Regex::new(r"\[\[([^\]]+)\]\]").unwrap();
let mut pages = Vec::new();
let result = line.to_string();
for cap in page_regex.captures_iter(line) {
if let Some(page) = cap.get(1) {
pages.push(page.as_str().to_string());
// Keep [[page]] in content for now (backward compat)
}
}
(pages, result)
}
}
fn count_indent(line: &amp;str) -&gt; usize {
line.chars().take_while(|c| c.is_whitespace()).count() / 2
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Phase 4: Logseq Import/Export</strong></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// crates/kb-core/src/logseq.rs
use crate::models::{Node, NodeType};
use crate::models::block::Block;
use crate::parser::BlockParser;
pub struct LogseqImporter;
impl LogseqImporter {
/// Import a Logseq page (markdown file) as a Node
pub fn import_page(path: &amp;Path) -&gt; Result&lt;Node&gt; {
let content = std::fs::read_to_string(path)?;
// Extract frontmatter if present
let (frontmatter, body) = Self::split_frontmatter(&amp;content);
// Parse blocks from body
let blocks = BlockParser::parse(&amp;body)?;
// Create node with blocks
let mut node = Node::new(NodeType::Note, Self::extract_title(path));
node.content = body;
node.blocks = Some(blocks);
// Apply frontmatter properties
if let Some(fm) = frontmatter {
Self::apply_frontmatter(&amp;mut node, &amp;fm)?;
}
Ok(node)
}
fn split_frontmatter(content: &amp;str) -&gt; (Option&lt;String&gt;, String) {
if content.starts_with("---\n") {
if let Some(end) = content[4..].find("\n---\n") {
let frontmatter = content[4..4 + end].to_string();
let body = content[4 + end + 5..].to_string();
return (Some(frontmatter), body);
}
}
(None, content.to_string())
}
fn extract_title(path: &amp;Path) -&gt; String {
path.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("Untitled")
.to_string()
}
fn apply_frontmatter(node: &amp;mut Node, frontmatter: &amp;str) -&gt; Result&lt;()&gt; {
// Parse YAML frontmatter and apply to node
// ... implementation ...
Ok(())
}
}
pub struct LogseqExporter;
impl LogseqExporter {
/// Export a Node to Logseq page format
pub fn export_page(node: &amp;Node, path: &amp;Path) -&gt; Result&lt;()&gt; {
let mut output = String::new();
// Generate frontmatter
output.push_str("---\n");
output.push_str(&amp;Self::generate_frontmatter(node)?);
output.push_str("---\n\n");
// Serialize blocks or use content
if let Some(ref blocks) = node.blocks {
output.push_str(&amp;BlockParser::serialize(blocks));
} else {
output.push_str(&amp;node.content);
}
std::fs::write(path, output)?;
Ok(())
}
fn generate_frontmatter(node: &amp;Node) -&gt; Result&lt;String&gt; {
let mut fm = String::new();
fm.push_str(&amp;format!("title: {}\n", node.title));
fm.push_str(&amp;format!("tags: {}\n", node.tags.join(", ")));
// ... more frontmatter fields ...
Ok(fm)
}
}
<span class="boring">}</span></code></pre></pre>
<h2 id="query-api-extensions"><a class="header" href="#query-api-extensions">Query API Extensions</a></h2>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// New methods in Graph or Query module
impl Graph {
/// Find all blocks with a specific tag across all nodes
pub fn find_blocks_by_tag(&amp;mut self, tag: &amp;str) -&gt; Vec&lt;(&amp;Node, &amp;Block)&gt; {
let mut results = Vec::new();
for node in self.nodes.values_mut() {
if let Ok(blocks) = node.find_blocks_by_tag(tag) {
for block in blocks {
results.push((node as &amp;Node, block));
}
}
}
results
}
/// Find all flashcards (#card blocks)
pub fn find_flashcards(&amp;mut self) -&gt; Vec&lt;(&amp;Node, &amp;Block)&gt; {
self.find_blocks_by_tag("card")
}
/// Find all TODO items across knowledge base
pub fn find_all_todos(&amp;mut self) -&gt; Vec&lt;(&amp;Node, &amp;Block)&gt; {
let mut results = Vec::new();
for node in self.nodes.values_mut() {
if let Ok(todos) = node.find_todos() {
for block in todos {
results.push((node as &amp;Node, block));
}
}
}
results
}
}
<span class="boring">}</span></code></pre></pre>
<h2 id="mcp-tool-extensions"><a class="header" href="#mcp-tool-extensions">MCP Tool Extensions</a></h2>
<pre><code class="language-json">{
"name": "kogral/find_blocks",
"description": "Find blocks by tag, status, or properties",
"inputSchema": {
"type": "object",
"properties": {
"tag": { "type": "string", "description": "Filter by tag (e.g., 'card')" },
"status": { "type": "string", "enum": ["TODO", "DONE", "DOING"] },
"property": { "type": "string", "description": "Custom property key" },
"value": { "type": "string", "description": "Property value to match" }
}
}
}
</code></pre>
<h2 id="configuration-1"><a class="header" href="#configuration-1">Configuration</a></h2>
<pre><code class="language-nickel"># schemas/kb/contracts.ncl (additions)
BlockConfig = {
enabled | Bool
| doc "Enable block-level parsing and storage"
| default = true,
preserve_hierarchy | Bool
| doc "Preserve block nesting on import/export"
| default = true,
parse_on_load | Bool
| doc "Automatically parse blocks when loading nodes"
| default = false, # Lazy parsing by default
supported_statuses | Array String
| doc "Supported task statuses"
| default = ["TODO", "DONE", "DOING", "LATER", "NOW", "WAITING", "CANCELLED"],
}
KbConfig = {
# ... existing fields ...
blocks | BlockConfig
| doc "Block-level features configuration"
| default = {},
}
</code></pre>
<h2 id="migration-path"><a class="header" href="#migration-path">Migration Path</a></h2>
<p><strong>Phase 1</strong>: Add Block models (no behavior change)
<strong>Phase 2</strong>: Add BlockParser (opt-in via config)
<strong>Phase 3</strong>: Update Logseq import/export
<strong>Phase 4</strong>: Add block queries to CLI/MCP
<strong>Phase 5</strong>: SurrealDB block indexing</p>
<p><strong>Backward Compatibility</strong>:</p>
<ul>
<li>Existing nodes without <code>blocks</code> field work as before</li>
<li><code>content</code> remains source of truth</li>
<li><code>blocks</code> is optional cache/structure</li>
<li>Config flag <code>blocks.enabled</code> to opt-in</li>
</ul>
<h2 id="testing-strategy-1"><a class="header" href="#testing-strategy-1">Testing Strategy</a></h2>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_simple_block() {
let content = "- This is a block #card";
let blocks = BlockParser::parse(content).unwrap();
assert_eq!(blocks.len(), 1);
assert_eq!(blocks[0].content, "This is a block");
assert_eq!(blocks[0].properties.tags, vec!["card"]);
}
#[test]
fn test_parse_nested_blocks() {
let content = r#"
- Parent block
- Child block 1
- Child block 2
- Grandchild
"#;
let blocks = BlockParser::parse(content).unwrap();
assert_eq!(blocks.len(), 1);
assert_eq!(blocks[0].children.len(), 2);
assert_eq!(blocks[0].children[1].children.len(), 1);
}
#[test]
fn test_parse_todo() {
let content = "- TODO Implement feature #rust";
let blocks = BlockParser::parse(content).unwrap();
assert_eq!(blocks[0].properties.status, Some(TaskStatus::Todo));
assert_eq!(blocks[0].content, "Implement feature");
assert_eq!(blocks[0].properties.tags, vec!["rust"]);
}
#[test]
fn test_roundtrip() {
let original = r#"- Block 1 #card
- Nested
- TODO Block 2
priority:: high
"#;
let blocks = BlockParser::parse(original).unwrap();
let serialized = BlockParser::serialize(&amp;blocks);
let reparsed = BlockParser::parse(&amp;serialized).unwrap();
assert_eq!(blocks.len(), reparsed.len());
assert_eq!(blocks[0].properties, reparsed[0].properties);
}
}
<span class="boring">}</span></code></pre></pre>
<h2 id="summary-1"><a class="header" href="#summary-1">Summary</a></h2>
<p><strong>Recommended Approach</strong>: Hybrid (Option C)</p>
<ul>
<li><strong>Add</strong> <code>Block</code> struct with properties, hierarchy</li>
<li><strong>Extend</strong> <code>Node</code> with optional <code>blocks: Option&lt;Vec&lt;Block&gt;&gt;</code></li>
<li><strong>Implement</strong> bidirectional parser (markdown ↔ blocks)</li>
<li><strong>Preserve</strong> <code>content</code> as source of truth (backward compat)</li>
<li><strong>Enable</strong> block queries in CLI/MCP</li>
<li><strong>Support</strong> round-trip Logseq import/export</li>
</ul>
<p><strong>Benefits</strong>:</p>
<ul>
<li>✅ Full Logseq compatibility</li>
<li>✅ Queryable blocks (find #card, TODO, etc.)</li>
<li>✅ Backward compatible</li>
<li>✅ Extensible (custom properties)</li>
<li>✅ Type-safe structure</li>
</ul>
<p><strong>Trade-offs</strong>:</p>
<ul>
<li>⚠️ Added complexity</li>
<li>⚠️ Need to sync content ↔ blocks</li>
<li>⚠️ More storage for SurrealDB backend</li>
</ul>
<p><strong>Next Steps</strong>:</p>
<ol>
<li>Review and approve design</li>
<li>Implement Phase 1 (Block models)</li>
<li>Implement Phase 2 (BlockParser)</li>
<li>Update Logseq import/export</li>
<li>Add block queries to MCP/CLI</li>
</ol>
<div style="break-before: page; page-break-before: always;"></div><h1 id="adr-001-nickel-vs-toml-for-configuration"><a class="header" href="#adr-001-nickel-vs-toml-for-configuration">ADR-001: Nickel vs TOML for Configuration</a></h1>
<p><strong>Status</strong>: Accepted</p>
<p><strong>Date</strong>: 2026-01-17</p>
<p><strong>Deciders</strong>: Architecture Team</p>
<p><strong>Context</strong>: Configuration Strategy for Knowledge Base System</p>
<hr />
<h2 id="context"><a class="header" href="#context">Context</a></h2>
<p>The KOGRAL requires a flexible, type-safe configuration format that supports:</p>
<ol>
<li><strong>Complex nested structures</strong> (graph settings, storage configs, embedding providers)</li>
<li><strong>Type validation</strong> (prevent runtime errors from config mistakes)</li>
<li><strong>Composition and inheritance</strong> (shared configs, environment-specific overrides)</li>
<li><strong>Documentation</strong> (self-documenting schemas)</li>
<li><strong>Validation before runtime</strong> (catch errors early)</li>
</ol>
<p>We evaluated two primary options:</p>
<h3 id="option-1-toml-traditional-config-format"><a class="header" href="#option-1-toml-traditional-config-format">Option 1: TOML (Traditional Config Format)</a></h3>
<p><strong>Pros</strong>:</p>
<ul>
<li>Widely adopted in Rust ecosystem (<code>Cargo.toml</code>)</li>
<li>Simple, human-readable syntax</li>
<li>Native <code>serde</code> support</li>
<li>IDE support (syntax highlighting, completion)</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>No type system (validation only at runtime)</li>
<li>Limited composition (no imports, no functions)</li>
<li>No schema validation (errors discovered during execution)</li>
<li>Verbose for complex nested structures</li>
<li>No documentation in config files</li>
</ul>
<p><strong>Example TOML</strong>:</p>
<pre><code class="language-toml">[graph]
name = "my-project"
version = "1.0.0"
[storage]
primary = "filesystem" # String, not validated as enum
[storage.secondary]
enabled = true
type = "surrealdb" # Typo would fail at runtime
url = "ws://localhost:8000"
[embeddings]
enabled = true
provider = "openai" # No validation of valid providers
model = "text-embedding-3-small"
</code></pre>
<p><strong>Problems</strong>:</p>
<ul>
<li>Typos in enum values (<code>"surrealdb"</code> vs <code>"surealdb"</code>) fail at runtime</li>
<li>No validation that <code>provider = "openai"</code> requires <code>api_key_env</code></li>
<li>No documentation of valid options</li>
<li>No way to compose configs (e.g., base config + environment override)</li>
</ul>
<h3 id="option-2-nickel-functional-configuration-language"><a class="header" href="#option-2-nickel-functional-configuration-language">Option 2: Nickel (Functional Configuration Language)</a></h3>
<p><strong>Pros</strong>:</p>
<ul>
<li><strong>Type system</strong> with contracts (validate before runtime)</li>
<li><strong>Composition</strong> via imports and merging</li>
<li><strong>Documentation</strong> in schemas (self-documenting)</li>
<li><strong>Validation</strong> at export time (catch errors early)</li>
<li><strong>Functions</strong> for conditional logic</li>
<li><strong>Default values</strong> in schema definitions</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>Less familiar to Rust developers</li>
<li>Requires separate <code>nickel</code> CLI tool</li>
<li>Smaller ecosystem</li>
<li>Steeper learning curve</li>
</ul>
<p><strong>Example Nickel</strong>:</p>
<pre><code class="language-nickel"># schemas/kb-config.ncl
{
KbConfig = {
graph | GraphConfig,
storage | StorageConfig,
embeddings | EmbeddingConfig,
},
StorageConfig = {
primary | [| 'filesystem, 'memory |], # Enum validated at export
secondary | {
enabled | Bool,
type | [| 'surrealdb, 'sqlite |], # Typos caught immediately
url | String,
} | optional,
},
EmbeddingConfig = {
enabled | Bool,
provider | [| 'openai, 'claude, 'fastembed |], # Valid providers enforced
model | String,
api_key_env | String | doc "Environment variable for API key",
},
}
</code></pre>
<p><strong>Benefits</strong>:</p>
<ul>
<li>Typos in enum values caught at <code>nickel export</code> time</li>
<li>Schema enforces required fields based on provider</li>
<li>Documentation embedded in schema</li>
<li>Config can be composed: <code>import "base.ncl" &amp; { /* overrides */ }</code></li>
</ul>
<hr />
<h2 id="decision"><a class="header" href="#decision">Decision</a></h2>
<p><strong>We will use Nickel for configuration.</strong></p>
<p><strong>Implementation</strong>:</p>
<ol>
<li>Define schemas in <code>schemas/*.ncl</code> with type contracts</li>
<li>Users write configs in <code>.kogral/config.ncl</code></li>
<li>Export to JSON via CLI: <code>nickel export --format json config.ncl</code></li>
<li>Load JSON in Rust via <code>serde_json</code> into typed structs</li>
</ol>
<p><strong>Pattern</strong> (double validation):</p>
<pre><code>Nickel Config (.ncl)
↓ [nickel export]
JSON (validated by Nickel contracts)
↓ [serde_json::from_str]
Rust Struct (validated by serde)
Runtime (guaranteed valid config)
</code></pre>
<p><strong>Bridge Code</strong> (<code>kb-core/src/config/nickel.rs</code>):</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn load_config&lt;P: AsRef&lt;Path&gt;&gt;(path: P) -&gt; Result&lt;KbConfig&gt; {
// Export Nickel to JSON
let json = export_nickel_to_json(path)?;
// Deserialize to Rust struct
let config: KbConfig = serde_json::from_str(&amp;json)?;
Ok(config)
}
fn export_nickel_to_json&lt;P: AsRef&lt;Path&gt;&gt;(path: P) -&gt; Result&lt;String&gt; {
let output = Command::new("nickel")
.arg("export")
.arg("--format").arg("json")
.arg(path.as_ref())
.output()?;
Ok(String::from_utf8(output.stdout)?)
}
<span class="boring">}</span></code></pre></pre>
<hr />
<h2 id="consequences"><a class="header" href="#consequences">Consequences</a></h2>
<h3 id="positive"><a class="header" href="#positive">Positive</a></h3>
<p><strong>Type Safety</strong>: Config errors caught before runtime</p>
<ul>
<li>Invalid enum values fail at export: <code>'filesystm</code> → error</li>
<li>Missing required fields detected: no <code>graph.name</code> → error</li>
<li>Type mismatches prevented: <code>enabled = "yes"</code> → error (expects Bool)</li>
</ul>
<p><strong>Self-Documenting</strong>: Schemas serve as documentation</p>
<ul>
<li><code>| doc "Environment variable for API key"</code> describes fields</li>
<li>Enum options visible in schema: <code>[| 'openai, 'claude, 'fastembed |]</code></li>
<li>Default values explicit: <code>| default = 'filesystem</code></li>
</ul>
<p><strong>Composition</strong>: Config reuse and overrides</p>
<pre><code class="language-nickel"># base.ncl
{ graph = { version = "1.0.0" } }
# project.ncl
import "base.ncl" &amp; { graph = { name = "my-project" } }
</code></pre>
<p><strong>Validation Before Deployment</strong>: Catch errors in CI</p>
<pre><code class="language-bash"># CI pipeline
nickel typecheck config.ncl
nickel export --format json config.ncl &gt; /dev/null
</code></pre>
<p><strong>Conditional Logic</strong>: Environment-specific configs</p>
<pre><code class="language-nickel">let is_prod = std.string.is_match "prod" (std.env.get "ENV") in
{
embeddings = {
provider = if is_prod then 'openai else 'fastembed,
},
}
</code></pre>
<h3 id="negative"><a class="header" href="#negative">Negative</a></h3>
<p><strong>Learning Curve</strong>: Team must learn Nickel syntax</p>
<ul>
<li><strong>Mitigation</strong>: Provide comprehensive examples in <code>config/</code> directory</li>
<li><strong>Mitigation</strong>: Document common patterns in <code>docs/config/</code></li>
</ul>
<p><strong>Tool Dependency</strong>: Requires <code>nickel</code> CLI installed</p>
<ul>
<li><strong>Mitigation</strong>: Document installation in setup guide</li>
<li><strong>Mitigation</strong>: Check <code>nickel</code> availability in <code>kb init</code> command</li>
</ul>
<p><strong>IDE Support</strong>: Limited compared to TOML</p>
<ul>
<li><strong>Mitigation</strong>: Use LSP (nickel-lang-lsp) for VSCode/Neovim</li>
<li><strong>Mitigation</strong>: Syntax highlighting available for major editors</li>
</ul>
<p><strong>Ecosystem Size</strong>: Smaller than TOML</p>
<ul>
<li><strong>Mitigation</strong>: Nickel actively developed by Tweag</li>
<li><strong>Mitigation</strong>: Stable language specification (v1.0+)</li>
</ul>
<h3 id="neutral"><a class="header" href="#neutral">Neutral</a></h3>
<p><strong>Two-Stage Loading</strong>: Nickel → JSON → Rust</p>
<ul>
<li>Not a performance concern (config loaded once at startup)</li>
<li>Adds resilience (double validation)</li>
<li>Allows runtime config inspection (read JSON directly)</li>
</ul>
<hr />
<h2 id="alternatives-considered"><a class="header" href="#alternatives-considered">Alternatives Considered</a></h2>
<h3 id="json-schema"><a class="header" href="#json-schema">JSON Schema</a></h3>
<p><strong>Rejected</strong>: Not ergonomic for humans to write</p>
<ul>
<li>No comments</li>
<li>Verbose syntax (<code>{"key": "value"}</code> vs <code>key = value</code>)</li>
<li>JSON Schema separate from config (duplication)</li>
</ul>
<h3 id="yaml"><a class="header" href="#yaml">YAML</a></h3>
<p><strong>Rejected</strong>: No type system, ambiguous parsing</p>
<ul>
<li>Boolean confusion: <code>yes</code>/<code>no</code>/<code>on</code>/<code>off</code>/<code>true</code>/<code>false</code></li>
<li>Indentation-sensitive (error-prone)</li>
<li>No validation without external tools</li>
</ul>
<h3 id="dhall"><a class="header" href="#dhall">Dhall</a></h3>
<p><strong>Rejected</strong>: More complex than needed</p>
<ul>
<li>Turing-incomplete by design (limits use cases)</li>
<li>Smaller ecosystem than Nickel</li>
<li>Steeper learning curve</li>
</ul>
<h3 id="kcl-kusionstack-configuration-language"><a class="header" href="#kcl-kusionstack-configuration-language">KCL (KusionStack Configuration Language)</a></h3>
<p><strong>Rejected</strong>: Kubernetes-focused, less general-purpose</p>
<ul>
<li>Designed for K8s manifests</li>
<li>Less mature than Nickel for general config</li>
</ul>
<hr />
<h2 id="implementation-timeline"><a class="header" href="#implementation-timeline">Implementation Timeline</a></h2>
<ol>
<li>✅ Define base schemas (<code>schemas/kb-config.ncl</code>)</li>
<li>✅ Implement Nickel loader (<code>kb-core/src/config/nickel.rs</code>)</li>
<li>✅ Create example configs (<code>config/defaults.ncl</code>, <code>config/production.ncl</code>)</li>
<li>✅ Document Nickel usage (<code>docs/config/nickel-schemas.md</code>)</li>
<li>⏳ Add LSP recommendations to setup guide</li>
<li>⏳ Create Nickel → TOML migration tool (for existing users)</li>
</ol>
<hr />
<h2 id="monitoring"><a class="header" href="#monitoring">Monitoring</a></h2>
<p><strong>Success Criteria</strong>:</p>
<ul>
<li>Config errors caught at export time (not runtime)</li>
<li>Users can compose configs for different environments</li>
<li>Team comfortable with Nickel syntax within 2 weeks</li>
</ul>
<p><strong>Metrics</strong>:</p>
<ul>
<li>Number of config validation errors caught before runtime</li>
<li>Time to diagnose config issues (should decrease)</li>
<li>User feedback on config complexity</li>
</ul>
<hr />
<h2 id="references"><a class="header" href="#references">References</a></h2>
<ul>
<li><a href="https://nickel-lang.org/">Nickel Language</a></li>
<li><a href="https://nickel-lang.org/user-manual/introduction">Nickel User Manual</a></li>
<li><a href="architecture/adrs/../../crates/kb-core/src/config/README.html">platform-config pattern</a> (reference implementation)</li>
<li><a href="https://toml.io/">TOML Specification</a></li>
</ul>
<hr />
<h2 id="revision-history"><a class="header" href="#revision-history">Revision History</a></h2>
<div class="table-wrapper"><table><thead><tr><th>Date</th><th>Author</th><th>Change</th></tr></thead><tbody>
<tr><td>2026-01-17</td><td>Architecture Team</td><td>Initial decision</td></tr>
</tbody></table>
</div>
<hr />
<p><strong>Next ADR</strong>: <a href="architecture/adrs/002-fastembed-ai-providers.html">ADR-002: FastEmbed via AI Providers</a></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="adr-002-fastembed-via-ai-providers-for-embeddings"><a class="header" href="#adr-002-fastembed-via-ai-providers-for-embeddings">ADR-002: FastEmbed via AI Providers for Embeddings</a></h1>
<p><strong>Status</strong>: Accepted</p>
<p><strong>Date</strong>: 2026-01-17</p>
<p><strong>Deciders</strong>: Architecture Team</p>
<p><strong>Context</strong>: Embedding Strategy for Semantic Search</p>
<hr />
<h2 id="context-1"><a class="header" href="#context-1">Context</a></h2>
<p>The KOGRAL requires embedding generation for semantic search capabilities. Embeddings convert text into numerical vectors that capture semantic meaning, enabling "find concepts" rather than just "find keywords".</p>
<p><strong>Requirements</strong>:</p>
<ol>
<li><strong>Local-First Option</strong>: Must work offline without external API dependencies</li>
<li><strong>Production Scalability</strong>: Support cloud AI providers for large-scale deployments</li>
<li><strong>Multiple Providers</strong>: Flexibility to choose based on cost, quality, privacy</li>
<li><strong>Cost-Effective Development</strong>: Free local embeddings for development and testing</li>
<li><strong>Quality</strong>: Good enough embeddings for finding related concepts</li>
</ol>
<p><strong>Options Evaluated</strong>:</p>
<h3 id="option-1-only-local-embeddings-fastembed"><a class="header" href="#option-1-only-local-embeddings-fastembed">Option 1: Only Local Embeddings (fastembed)</a></h3>
<p><strong>Pros</strong>:</p>
<ul>
<li>No API costs</li>
<li>Works offline</li>
<li>Privacy-preserving (no data leaves machine)</li>
<li>Fast (local GPU acceleration possible)</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>Limited model quality compared to cloud providers</li>
<li>Resource-intensive (requires download ~100MB models)</li>
<li>Single provider lock-in (fastembed library)</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use fastembed::{TextEmbedding, InitOptions};
let model = TextEmbedding::try_new(InitOptions {
model_name: "BAAI/bge-small-en-v1.5",
..Default::default()
})?;
let embeddings = model.embed(vec!["Hello world"], None)?;
// Output: Vec&lt;Vec&lt;f32&gt;&gt; with 384 dimensions
<span class="boring">}</span></code></pre></pre>
<h3 id="option-2-only-cloud-ai-providers-openai-claude-etc"><a class="header" href="#option-2-only-cloud-ai-providers-openai-claude-etc">Option 2: Only Cloud AI Providers (OpenAI, Claude, etc.)</a></h3>
<p><strong>Pros</strong>:</p>
<ul>
<li>State-of-the-art embedding quality</li>
<li>No local resource usage</li>
<li>Latest models available</li>
<li>Scalable to millions of documents</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>Requires API keys (cost per embedding)</li>
<li>Network dependency (no offline mode)</li>
<li>Privacy concerns (data sent to third parties)</li>
<li>Vendor lock-in risk</li>
</ul>
<p><strong>Example</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use rig::providers::openai;
let client = openai::Client::new("sk-...");
let embeddings = client.embeddings("text-embedding-3-small")
.embed_documents(vec!["Hello world"]).await?;
// Output: Vec&lt;Vec&lt;f32&gt;&gt; with 1536 dimensions
<span class="boring">}</span></code></pre></pre>
<h3 id="option-3-hybrid-strategy-fastembed--ai-providers-via-rig-core"><a class="header" href="#option-3-hybrid-strategy-fastembed--ai-providers-via-rig-core">Option 3: Hybrid Strategy (fastembed + AI providers via rig-core)</a></h3>
<p><strong>Pros</strong>:</p>
<ul>
<li>✅ Best of both worlds: local dev, cloud production</li>
<li>✅ User choice: privacy-first or quality-first</li>
<li>✅ Cost flexibility: free for small projects, paid for scale</li>
<li>✅ Unified interface via <code>rig-core</code> library</li>
<li>✅ Easy provider switching (config-driven)</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>❌ More complex implementation (multiple providers)</li>
<li>❌ Dimension mismatch between providers (384 vs 1536)</li>
<li>❌ Additional dependencies (<code>rig-core</code>, <code>fastembed</code>)</li>
</ul>
<hr />
<h2 id="decision-1"><a class="header" href="#decision-1">Decision</a></h2>
<p><strong>We will use a hybrid strategy: fastembed (local) + AI providers (via rig-core).</strong></p>
<p><strong>Implementation</strong>:</p>
<ol>
<li><strong>Default</strong>: <code>fastembed</code> with <code>BAAI/bge-small-en-v1.5</code> (384 dimensions)</li>
<li><strong>Optional</strong>: OpenAI, Claude, Ollama via <code>rig-core</code> (configurable)</li>
<li><strong>Interface</strong>: <code>EmbeddingProvider</code> trait abstracts provider details</li>
<li><strong>Config-Driven</strong>: Provider selection via Nickel configuration</li>
</ol>
<p><strong>Architecture</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[async_trait]
pub trait EmbeddingProvider: Send + Sync {
async fn embed(&amp;self, texts: Vec&lt;String&gt;) -&gt; Result&lt;Vec&lt;Vec&lt;f32&gt;&gt;&gt;;
fn dimensions(&amp;self) -&gt; usize;
fn model_name(&amp;self) -&gt; &amp;str;
}
// Local implementation
pub struct FastEmbedProvider {
model: TextEmbedding,
}
impl FastEmbedProvider {
pub fn new(model_name: &amp;str) -&gt; Result&lt;Self&gt; {
let model = TextEmbedding::try_new(InitOptions {
model_name: model_name.into(),
..Default::default()
})?;
Ok(Self { model })
}
}
#[async_trait]
impl EmbeddingProvider for FastEmbedProvider {
async fn embed(&amp;self, texts: Vec&lt;String&gt;) -&gt; Result&lt;Vec&lt;Vec&lt;f32&gt;&gt;&gt; {
Ok(self.model.embed(texts, None)?)
}
fn dimensions(&amp;self) -&gt; usize { 384 }
fn model_name(&amp;self) -&gt; &amp;str { "BAAI/bge-small-en-v1.5" }
}
// Cloud provider implementation (via rig-core)
pub struct RigEmbeddingProvider {
client: rig::Client,
model: String,
dimensions: usize,
}
#[async_trait]
impl EmbeddingProvider for RigEmbeddingProvider {
async fn embed(&amp;self, texts: Vec&lt;String&gt;) -&gt; Result&lt;Vec&lt;Vec&lt;f32&gt;&gt;&gt; {
let embeddings = self.client
.embeddings(&amp;self.model)
.embed_documents(texts)
.await?;
Ok(embeddings)
}
fn dimensions(&amp;self) -&gt; usize { self.dimensions }
fn model_name(&amp;self) -&gt; &amp;str { &amp;self.model }
}
<span class="boring">}</span></code></pre></pre>
<p><strong>Configuration</strong> (Nickel):</p>
<pre><code class="language-nickel"># Local development (default)
{
embeddings = {
enabled = true,
provider = 'fastembed,
model = "BAAI/bge-small-en-v1.5",
dimensions = 384,
},
}
# Production with OpenAI
{
embeddings = {
enabled = true,
provider = 'openai,
model = "text-embedding-3-small",
dimensions = 1536,
api_key_env = "OPENAI_API_KEY",
},
}
# Self-hosted with Ollama
{
embeddings = {
enabled = true,
provider = 'ollama,
model = "nomic-embed-text",
dimensions = 768,
},
}
</code></pre>
<p><strong>Provider Selection</strong> (<code>kb-core/src/embeddings/mod.rs</code>):</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn create_provider(config: &amp;EmbeddingConfig) -&gt; Result&lt;Box&lt;dyn EmbeddingProvider&gt;&gt; {
match config.provider {
EmbeddingProviderType::FastEmbed =&gt; {
Ok(Box::new(FastEmbedProvider::new(&amp;config.model)?))
}
EmbeddingProviderType::OpenAI =&gt; {
let api_key = std::env::var(&amp;config.api_key_env)?;
Ok(Box::new(RigEmbeddingProvider::new_openai(api_key, &amp;config.model)?))
}
EmbeddingProviderType::Claude =&gt; {
let api_key = std::env::var(&amp;config.api_key_env)?;
Ok(Box::new(RigEmbeddingProvider::new_claude(api_key, &amp;config.model)?))
}
EmbeddingProviderType::Ollama =&gt; {
Ok(Box::new(RigEmbeddingProvider::new_ollama(&amp;config.model)?))
}
}
}
<span class="boring">}</span></code></pre></pre>
<hr />
<h2 id="consequences-1"><a class="header" href="#consequences-1">Consequences</a></h2>
<h3 id="positive-1"><a class="header" href="#positive-1">Positive</a></h3>
<p><strong>Development Flexibility</strong>:</p>
<ul>
<li>Developers can use <code>fastembed</code> without API keys</li>
<li>Fast feedback loop (local embeddings, no network calls)</li>
<li>Works offline (train trips, flights)</li>
</ul>
<p><strong>Production Quality</strong>:</p>
<ul>
<li>Production deployments can use OpenAI/Claude for better quality</li>
<li>Latest embedding models available</li>
<li>Scalable to millions of documents</li>
</ul>
<p><strong>Privacy Control</strong>:</p>
<ul>
<li>Privacy-sensitive projects use local embeddings</li>
<li>Public projects can use cloud providers</li>
<li>User choice via configuration</li>
</ul>
<p><strong>Cost Optimization</strong>:</p>
<ul>
<li>Small projects: free (fastembed)</li>
<li>Large projects: pay for quality (cloud providers)</li>
<li>Hybrid: important docs via cloud, bulk via local</li>
</ul>
<p><strong>Unified Interface</strong>:</p>
<ul>
<li><code>EmbeddingProvider</code> trait abstracts provider details</li>
<li>Query code doesn't know/care about provider</li>
<li>Easy to add new providers</li>
</ul>
<h3 id="negative-1"><a class="header" href="#negative-1">Negative</a></h3>
<p><strong>Dimension Mismatch</strong>:</p>
<ul>
<li>fastembed: 384 dimensions</li>
<li>OpenAI: 1536 dimensions</li>
<li>Cannot mix in same index</li>
</ul>
<p><strong>Mitigation</strong>:</p>
<ul>
<li>Store provider + dimensions in node metadata</li>
<li>Rebuild index when changing providers</li>
<li>Document dimension constraints</li>
</ul>
<p><strong>Model Download</strong>:</p>
<ul>
<li>First use of fastembed downloads ~100MB model</li>
<li>Slow initial startup</li>
</ul>
<p><strong>Mitigation</strong>:</p>
<ul>
<li>Pre-download in Docker images</li>
<li>Document model download in setup guide</li>
<li>Cache models in <code>~/.cache/fastembed</code></li>
</ul>
<p><strong>Complex Configuration</strong>:</p>
<ul>
<li>Multiple provider options may confuse users</li>
</ul>
<p><strong>Mitigation</strong>:</p>
<ul>
<li>Sane default (fastembed)</li>
<li>Clear examples for each provider</li>
<li>Validation errors explain misconfigurations</li>
</ul>
<h3 id="neutral-1"><a class="header" href="#neutral-1">Neutral</a></h3>
<p><strong>Dependency Trade-off</strong>:</p>
<ul>
<li><code>fastembed</code> adds ~5MB to binary</li>
<li><code>rig-core</code> adds ~2MB</li>
<li>Total: ~7MB overhead</li>
</ul>
<p>Not a concern for CLI/MCP server use case.</p>
<hr />
<h2 id="provider-comparison"><a class="header" href="#provider-comparison">Provider Comparison</a></h2>
<div class="table-wrapper"><table><thead><tr><th>Provider</th><th>Dimensions</th><th>Quality</th><th>Cost</th><th>Privacy</th><th>Offline</th></tr></thead><tbody>
<tr><td><strong>fastembed</strong></td><td>384</td><td>Good</td><td>Free</td><td>✅ Local</td><td>✅ Yes</td></tr>
<tr><td><strong>OpenAI</strong></td><td>1536</td><td>Excellent</td><td>$0.0001/1K</td><td>❌ Cloud</td><td>❌ No</td></tr>
<tr><td><strong>Claude</strong></td><td>1024</td><td>Excellent</td><td>$0.00025/1K</td><td>❌ Cloud</td><td>❌ No</td></tr>
<tr><td><strong>Ollama</strong></td><td>768</td><td>Very Good</td><td>Free</td><td>✅ Local</td><td>✅ Yes</td></tr>
</tbody></table>
</div>
<p><strong>Recommendation by Use Case</strong>:</p>
<ul>
<li><strong>Development</strong>: fastembed (fast, free, offline)</li>
<li><strong>Small Teams</strong>: fastembed or Ollama (privacy, no costs)</li>
<li><strong>Enterprise</strong>: OpenAI or Claude (best quality, scalable)</li>
<li><strong>Self-Hosted</strong>: Ollama (good quality, local control)</li>
</ul>
<hr />
<h2 id="implementation-timeline-1"><a class="header" href="#implementation-timeline-1">Implementation Timeline</a></h2>
<ol>
<li>✅ Define <code>EmbeddingProvider</code> trait</li>
<li>✅ Implement FastEmbedProvider (stub, feature-gated)</li>
<li>✅ Implement RigEmbeddingProvider (stub, feature-gated)</li>
<li>⏳ Complete FastEmbed integration with model download</li>
<li>⏳ Complete rig-core integration (OpenAI, Claude, Ollama)</li>
<li>⏳ Add query engine with similarity search</li>
<li>⏳ Document provider selection and trade-offs</li>
</ol>
<hr />
<h2 id="monitoring-1"><a class="header" href="#monitoring-1">Monitoring</a></h2>
<p><strong>Success Criteria</strong>:</p>
<ul>
<li>Users can switch providers via config change</li>
<li>Local embeddings work without API keys</li>
<li>Production deployments use cloud providers successfully</li>
<li>Query quality acceptable for both local and cloud embeddings</li>
</ul>
<p><strong>Metrics</strong>:</p>
<ul>
<li>Embedding generation latency (local vs cloud)</li>
<li>Query accuracy (precision@10 for semantic search)</li>
<li>API costs (cloud providers)</li>
<li>User satisfaction (feedback on search quality)</li>
</ul>
<hr />
<h2 id="references-1"><a class="header" href="#references-1">References</a></h2>
<ul>
<li><a href="https://github.com/Anush008/fastembed-rs">fastembed Documentation</a></li>
<li><a href="https://github.com/0xPlaygrounds/rig">rig-core Documentation</a></li>
<li><a href="https://platform.openai.com/docs/guides/embeddings">OpenAI Embeddings API</a></li>
<li><a href="https://huggingface.co/BAAI/bge-small-en-v1.5">BAAI/bge Models</a></li>
<li><a href="https://ollama.com/blog/embedding-models">Ollama Embeddings</a></li>
</ul>
<hr />
<h2 id="revision-history-1"><a class="header" href="#revision-history-1">Revision History</a></h2>
<div class="table-wrapper"><table><thead><tr><th>Date</th><th>Author</th><th>Change</th></tr></thead><tbody>
<tr><td>2026-01-17</td><td>Architecture Team</td><td>Initial decision</td></tr>
</tbody></table>
</div>
<hr />
<p><strong>Previous ADR</strong>: <a href="architecture/adrs/001-nickel-vs-toml.html">ADR-001: Nickel vs TOML</a>
<strong>Next ADR</strong>: <a href="architecture/adrs/003-hybrid-storage.html">ADR-003: Hybrid Storage Strategy</a></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="adr-003-hybrid-storage-strategy"><a class="header" href="#adr-003-hybrid-storage-strategy">ADR-003: Hybrid Storage Strategy</a></h1>
<p><strong>Status</strong>: Accepted</p>
<p><strong>Date</strong>: 2026-01-17</p>
<p><strong>Deciders</strong>: Architecture Team</p>
<p><strong>Context</strong>: Storage Backend Strategy for Knowledge Base</p>
<hr />
<h2 id="context-2"><a class="header" href="#context-2">Context</a></h2>
<p>The KOGRAL needs to store knowledge graphs with these requirements:</p>
<ol>
<li><strong>Git-Friendly</strong>: Knowledge should version alongside code</li>
<li><strong>Scalable</strong>: Support small projects (10s of nodes) to large organizations (10,000+ nodes)</li>
<li><strong>Queryable</strong>: Efficient graph queries and relationship traversal</li>
<li><strong>Offline-Capable</strong>: Work without network access</li>
<li><strong>Collaborative</strong>: Support shared organizational knowledge</li>
<li><strong>Cost-Effective</strong>: Free for small projects, reasonable cost at scale</li>
</ol>
<p><strong>Constraints</strong>:</p>
<ul>
<li>Developers want to edit knowledge in text editors</li>
<li>Organizations want centralized guideline management</li>
<li>Git workflows essential for code-adjacent knowledge</li>
<li>Large graphs need database performance</li>
</ul>
<h3 id="option-1-filesystem-only"><a class="header" href="#option-1-filesystem-only">Option 1: Filesystem Only</a></h3>
<p><strong>Approach</strong>: Store everything as markdown files</p>
<p><strong>Pros</strong>:</p>
<ul>
<li>✅ Git-native (perfect for versioning)</li>
<li>✅ Text editor friendly</li>
<li>✅ No dependencies</li>
<li>✅ Works offline</li>
<li>✅ Free</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>❌ Poor performance for large graphs (100 0+ nodes)</li>
<li>❌ No efficient graph queries</li>
<li>❌ Difficult to share across projects</li>
<li>❌ Manual sync for collaboration</li>
</ul>
<p><strong>Scalability</strong>: Good for &lt; 100 nodes, poor beyond</p>
<h3 id="option-2-database-only-surrealdb"><a class="header" href="#option-2-database-only-surrealdb">Option 2: Database Only (SurrealDB)</a></h3>
<p><strong>Approach</strong>: Store all knowledge in SurrealDB graph database</p>
<p><strong>Pros</strong>:</p>
<ul>
<li>✅ Excellent query performance</li>
<li>✅ Native graph relationships</li>
<li>✅ Scalable to millions of nodes</li>
<li>✅ Centralized for collaboration</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>❌ Not git-trackable</li>
<li>❌ Requires running database server</li>
<li>❌ Can't edit with text editor</li>
<li>❌ Network dependency</li>
<li>❌ Infrastructure cost</li>
</ul>
<p><strong>Scalability</strong>: Excellent, but loses developer workflow benefits</p>
<h3 id="option-3-hybrid-filesystem--surrealdb"><a class="header" href="#option-3-hybrid-filesystem--surrealdb">Option 3: Hybrid (Filesystem + SurrealDB)</a></h3>
<p><strong>Approach</strong>: Filesystem for local project knowledge, SurrealDB for shared organizational knowledge</p>
<p><strong>Pros</strong>:</p>
<ul>
<li>✅ Git-friendly for project knowledge</li>
<li>✅ Text editor friendly</li>
<li>✅ Scalable for shared knowledge</li>
<li>✅ Works offline (local graph)</li>
<li>✅ Collaborative (shared graph)</li>
<li>✅ Cost-effective (DB only for shared)</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li>❌ More complex implementation</li>
<li>❌ Sync mechanism needed</li>
<li>❌ Two storage systems to manage</li>
</ul>
<p><strong>Scalability</strong>: Excellent - best of both worlds</p>
<hr />
<h2 id="decision-2"><a class="header" href="#decision-2">Decision</a></h2>
<p><strong>We will use a hybrid storage strategy: Filesystem (local) + SurrealDB (shared).</strong></p>
<p><strong>Architecture</strong>:</p>
<pre><code>┌─────────────────────────────────────────────────────────────┐
│ Project A (.kogral/) │
│ Storage: Filesystem (git-tracked) │
│ Scope: Project-specific notes, decisions, patterns │
│ Access: Local only │
└──────────────────┬──────────────────────────────────────────┘
│ [inherits]
┌─────────────────────────────────────────────────────────────┐
│ Shared KB (SurrealDB or synced filesystem) │
│ Storage: SurrealDB (scalable) or filesystem (synced) │
│ Scope: Organization-wide guidelines, patterns │
│ Access: All projects │
└─────────────────────────────────────────────────────────────┘
</code></pre>
<p><strong>Implementation</strong>:</p>
<pre><code class="language-nickel"># Project config
{
storage = {
primary = 'filesystem, # Local project knowledge
secondary = {
enabled = true,
type = 'surrealdb, # Shared knowledge
url = "ws://kb-central.company.com:8000",
namespace = "organization",
database = "shared-kb",
},
},
inheritance = {
base = "surrealdb://organization/shared-kb", # Inherit from shared
priority = 100, # Project overrides shared
},
}
</code></pre>
<p><strong>Sync Strategy</strong>:</p>
<pre><code>.kogral/ (Filesystem)
↓ [on save]
Watch for changes
↓ [debounced]
Sync to SurrealDB
Shared graph updated
↓ [on query]
Merge local + shared results
</code></pre>
<hr />
<h2 id="consequences-2"><a class="header" href="#consequences-2">Consequences</a></h2>
<h3 id="positive-2"><a class="header" href="#positive-2">Positive</a></h3>
<p><strong>Developer Workflow Preserved</strong>:</p>
<pre><code class="language-bash"># Local knowledge workflow (unchanged)
vim .kogral/notes/my-note.md
git add .kogral/notes/my-note.md
git commit -m "Add implementation note"
git push
</code></pre>
<p><strong>Git Integration</strong>:</p>
<ul>
<li>Project knowledge versioned with code</li>
<li>Branches include relevant knowledge</li>
<li>Merges resolve knowledge conflicts</li>
<li>PR reviews include knowledge changes</li>
</ul>
<p><strong>Offline Development</strong>:</p>
<ul>
<li>Full functionality without network</li>
<li>Shared guidelines cached locally</li>
<li>Sync when reconnected</li>
</ul>
<p><strong>Scalability</strong>:</p>
<ul>
<li>Projects: filesystem (100s of nodes, fine performance)</li>
<li>Organization: SurrealDB (10,000+ nodes, excellent performance)</li>
</ul>
<p><strong>Collaboration</strong>:</p>
<ul>
<li>Shared guidelines accessible to all projects</li>
<li>Updates to shared knowledge propagate automatically</li>
<li>Consistent practices across organization</li>
</ul>
<p><strong>Cost-Effective</strong>:</p>
<ul>
<li>Small projects: free (filesystem only)</li>
<li>Organizations: SurrealDB for shared only (not all project knowledge)</li>
</ul>
<p><strong>Gradual Adoption</strong>:</p>
<ul>
<li>Start with filesystem only</li>
<li>Add SurrealDB when needed</li>
<li>Feature-gated (<code>--features surrealdb</code>)</li>
</ul>
<h3 id="negative-2"><a class="header" href="#negative-2">Negative</a></h3>
<p><strong>Complexity</strong>:</p>
<ul>
<li>Two storage implementations</li>
<li>Sync mechanism required</li>
<li>Conflict resolution needed</li>
</ul>
<p><strong>Mitigation</strong>:</p>
<ul>
<li>Storage trait abstracts differences</li>
<li>Sync is optional (can disable)</li>
<li>Conflicts rare (guidelines change infrequently)</li>
</ul>
<p><strong>Sync Latency</strong>:</p>
<ul>
<li>Changes to shared KB not instant in all projects</li>
</ul>
<p><strong>Mitigation</strong>:</p>
<ul>
<li>Acceptable latency (guidelines don't change rapidly)</li>
<li>Manual sync command available (<code>kb sync</code>)</li>
<li>Auto-sync on query (fetch latest)</li>
</ul>
<p><strong>Infrastructure Requirement</strong>:</p>
<ul>
<li>SurrealDB server needed for shared KB</li>
</ul>
<p><strong>Mitigation</strong>:</p>
<ul>
<li>Optional (can use synced filesystem instead)</li>
<li>Docker Compose for easy setup</li>
<li>Managed SurrealDB Cloud option</li>
</ul>
<h3 id="neutral-2"><a class="header" href="#neutral-2">Neutral</a></h3>
<p><strong>Storage Trait Implementation</strong>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[async_trait]
pub trait Storage {
async fn save_graph(&amp;self, graph: &amp;Graph) -&gt; Result&lt;()&gt;;
async fn load_graph(&amp;self, name: &amp;str) -&gt; Result&lt;Graph&gt;;
async fn list_graphs(&amp;self) -&gt; Result&lt;Vec&lt;String&gt;&gt;;
}
// Three implementations
impl Storage for FilesystemStorage { /* ... */ }
impl Storage for SurrealDbStorage { /* ... */ }
impl Storage for MemoryStorage { /* ... */ }
<span class="boring">}</span></code></pre></pre>
<p>Abstraction makes multi-backend manageable.</p>
<hr />
<h2 id="use-cases-4"><a class="header" href="#use-cases-4">Use Cases</a></h2>
<h3 id="small-project-solo-developer"><a class="header" href="#small-project-solo-developer">Small Project (Solo Developer)</a></h3>
<p><strong>Config</strong>:</p>
<pre><code class="language-nickel">{ storage = { primary = 'filesystem } }
</code></pre>
<p><strong>Behavior</strong>:</p>
<ul>
<li>All knowledge in <code>.kogral/</code> directory</li>
<li>Git-tracked with code</li>
<li>No database required</li>
<li>Works offline</li>
</ul>
<h3 id="medium-project-team"><a class="header" href="#medium-project-team">Medium Project (Team)</a></h3>
<p><strong>Config</strong>:</p>
<pre><code class="language-nickel">{
storage = {
primary = 'filesystem,
secondary = {
enabled = true,
type = 'surrealdb,
url = "ws://team-kb.local:8000",
},
},
}
</code></pre>
<p><strong>Behavior</strong>:</p>
<ul>
<li>Project knowledge in <code>.kogral/</code> (git-tracked)</li>
<li>Shared team patterns in SurrealDB</li>
<li>Automatic sync</li>
<li>Offline fallback to cached</li>
</ul>
<h3 id="large-organization"><a class="header" href="#large-organization">Large Organization</a></h3>
<p><strong>Config</strong>:</p>
<pre><code class="language-nickel">{
storage = {
primary = 'filesystem,
secondary = {
enabled = true,
type = 'surrealdb,
url = "ws://kb.company.com:8000",
namespace = "engineering",
database = "shared-kb",
},
},
inheritance = {
base = "surrealdb://engineering/shared-kb",
guidelines = [
"surrealdb://engineering/rust-guidelines",
"surrealdb://engineering/security-policies",
],
},
}
</code></pre>
<p><strong>Behavior</strong>:</p>
<ul>
<li>Project-specific in <code>.kogral/</code></li>
<li>Organization guidelines in SurrealDB</li>
<li>Security policies enforced</li>
<li>Automatic guideline updates</li>
</ul>
<hr />
<h2 id="sync-mechanism"><a class="header" href="#sync-mechanism">Sync Mechanism</a></h2>
<h3 id="filesystem--surrealdb"><a class="header" href="#filesystem--surrealdb">Filesystem → SurrealDB</a></h3>
<p><strong>Trigger</strong>: File changes detected (via <code>notify</code> crate)</p>
<p><strong>Process</strong>:</p>
<ol>
<li>Parse changed markdown file</li>
<li>Convert to Node struct</li>
<li>Upsert to SurrealDB</li>
<li>Update relationships</li>
</ol>
<p><strong>Debouncing</strong>: 500ms (configurable)</p>
<h3 id="surrealdb--filesystem"><a class="header" href="#surrealdb--filesystem">SurrealDB → Filesystem</a></h3>
<p><strong>Trigger</strong>: Query for shared knowledge</p>
<p><strong>Process</strong>:</p>
<ol>
<li>Query SurrealDB for shared nodes</li>
<li>Cache locally (in-memory or filesystem)</li>
<li>Merge with local results</li>
<li>Return combined</li>
</ol>
<p><strong>Caching</strong>: TTL-based (5 minutes default)</p>
<h3 id="conflict-resolution"><a class="header" href="#conflict-resolution">Conflict Resolution</a></h3>
<p><strong>Strategy</strong>: Last-write-wins with version tracking</p>
<p><strong>Example</strong>:</p>
<pre><code>Project A: Updates shared guideline (v1 → v2)
Project B: Has cached v1
On Project B query:
- Detects v2 available
- Fetches v2
- Updates cache
- Uses v2 going forward
</code></pre>
<hr />
<h2 id="alternatives-considered-1"><a class="header" href="#alternatives-considered-1">Alternatives Considered</a></h2>
<h3 id="git-submodules-for-shared-knowledge"><a class="header" href="#git-submodules-for-shared-knowledge">Git Submodules for Shared Knowledge</a></h3>
<p><strong>Rejected</strong>: Cumbersome workflow</p>
<ul>
<li>Requires manual submodule update</li>
<li>Merge conflicts in shared submodule</li>
<li>Not discoverable (need to know submodule exists)</li>
</ul>
<h3 id="syncthing-for-filesystem-sync"><a class="header" href="#syncthing-for-filesystem-sync">Syncthing for Filesystem Sync</a></h3>
<p><strong>Rejected</strong>: Not designed for this use case</p>
<ul>
<li>No query optimization</li>
<li>No relationship indexes</li>
<li>Sync conflicts difficult to resolve</li>
</ul>
<h3 id="postgresql-with-json"><a class="header" href="#postgresql-with-json">PostgreSQL with JSON</a></h3>
<p><strong>Rejected</strong>: Not a graph database</p>
<ul>
<li>Poor graph query performance</li>
<li>Relationship traversal requires complex SQL joins</li>
<li>No native graph features</li>
</ul>
<hr />
<h2 id="migration-path-1"><a class="header" href="#migration-path-1">Migration Path</a></h2>
<h3 id="phase-1-filesystem-only-current"><a class="header" href="#phase-1-filesystem-only-current">Phase 1: Filesystem Only (Current)</a></h3>
<ul>
<li>All storage via filesystem</li>
<li>Git-tracked</li>
<li>No database required</li>
</ul>
<h3 id="phase-2-optional-surrealdb"><a class="header" href="#phase-2-optional-surrealdb">Phase 2: Optional SurrealDB</a></h3>
<ul>
<li>Add SurrealDB support (feature-gated)</li>
<li>Manual sync command</li>
<li>Shared KB opt-in</li>
</ul>
<h3 id="phase-3-automatic-sync"><a class="header" href="#phase-3-automatic-sync">Phase 3: Automatic Sync</a></h3>
<ul>
<li>File watching</li>
<li>Auto-sync on changes</li>
<li>Background sync</li>
</ul>
<h3 id="phase-4-multi-tenant-surrealdb"><a class="header" href="#phase-4-multi-tenant-surrealdb">Phase 4: Multi-Tenant SurrealDB</a></h3>
<ul>
<li>Organization namespaces</li>
<li>Access control</li>
<li>Audit logs</li>
</ul>
<hr />
<h2 id="monitoring-2"><a class="header" href="#monitoring-2">Monitoring</a></h2>
<p><strong>Success Criteria</strong>:</p>
<ul>
<li>Developers don't notice hybrid complexity</li>
<li>Sync completes &lt; 1 second for typical changes</li>
<li>Shared guidelines accessible in &lt; 100ms</li>
<li>Zero data loss in sync</li>
</ul>
<p><strong>Metrics</strong>:</p>
<ul>
<li>Sync latency (P50, P95, P99)</li>
<li>Cache hit rate (shared knowledge)</li>
<li>Conflict rate (expect &lt; 0.1%)</li>
<li>User satisfaction</li>
</ul>
<hr />
<h2 id="references-2"><a class="header" href="#references-2">References</a></h2>
<ul>
<li><a href="https://surrealdb.com/docs">SurrealDB Documentation</a></li>
<li><a href="architecture/adrs/../../crates/kb-core/src/storage/mod.rs">Storage Trait Implementation</a></li>
<li><a href="architecture/adrs/../../crates/kb-core/src/storage/filesystem.rs">FilesystemStorage</a></li>
<li><a href="architecture/adrs/../../crates/kb-core/src/storage/surrealdb.rs">SurrealDbStorage</a></li>
<li><a href="architecture/adrs/../../scripts/kb-sync.nu">Sync Mechanism</a></li>
</ul>
<hr />
<h2 id="revision-history-2"><a class="header" href="#revision-history-2">Revision History</a></h2>
<div class="table-wrapper"><table><thead><tr><th>Date</th><th>Author</th><th>Change</th></tr></thead><tbody>
<tr><td>2026-01-17</td><td>Architecture Team</td><td>Initial decision</td></tr>
</tbody></table>
</div>
<hr />
<p><strong>Previous ADR</strong>: <a href="architecture/adrs/002-fastembed-ai-providers.html">ADR-002: FastEmbed via AI Providers</a>
<strong>Next ADR</strong>: <a href="architecture/adrs/004-logseq-compatibility.html">ADR-004: Logseq Compatibility</a></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="adr-004-logseq-blocks-support"><a class="header" href="#adr-004-logseq-blocks-support">ADR-004: Logseq Blocks Support</a></h1>
<h2 id="status"><a class="header" href="#status">Status</a></h2>
<p><strong>Proposed</strong> (Design phase)</p>
<h2 id="context-3"><a class="header" href="#context-3">Context</a></h2>
<p>Logseq uses <strong>content blocks</strong> as the fundamental unit of information, not full documents. KB currently treats <code>Node.content</code> as flat markdown string, which loses block-level features on import/export:</p>
<p><strong>Lost features</strong>:</p>
<ul>
<li>Block properties (<code>#card</code>, <code>TODO</code>, custom properties)</li>
<li>Block hierarchy (outliner nesting)</li>
<li>Block references (<code>((block-uuid))</code>)</li>
<li>Block-level queries (find all flashcards, TODOs)</li>
</ul>
<p><strong>User requirement</strong>: Round-trip Logseq import/export with full fidelity:</p>
<pre><code>Logseq → KOGRAL Import → KOGRAL Storage → KOGRAL Export → Logseq
(blocks preserved at every step)
</code></pre>
<h2 id="decision-3"><a class="header" href="#decision-3">Decision</a></h2>
<p>Implement <strong>Hybrid Block Support</strong> (structured + markdown):</p>
<h3 id="1-add-block-data-structure"><a class="header" href="#1-add-block-data-structure">1. Add Block Data Structure</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct Block {
pub id: String, // UUID
pub content: String, // Block text
pub properties: BlockProperties, // Tags, status, custom
pub children: Vec&lt;Block&gt;, // Nested blocks
// ... timestamps ...
}
pub struct BlockProperties {
pub tags: Vec&lt;String&gt;, // #card, #important
pub status: Option&lt;TaskStatus&gt;, // TODO, DONE, etc.
pub custom: HashMap&lt;String, String&gt;, // property:: value
pub block_refs: Vec&lt;String&gt;, // ((uuid))
pub page_refs: Vec&lt;String&gt;, // [[page]]
}
<span class="boring">}</span></code></pre></pre>
<h3 id="2-extend-node-model"><a class="header" href="#2-extend-node-model">2. Extend Node Model</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct Node {
// ... existing fields ...
pub content: String, // Source of truth (markdown)
pub blocks: Option&lt;Vec&lt;Block&gt;&gt;, // Cached structure (optional)
}
<span class="boring">}</span></code></pre></pre>
<h3 id="3-bidirectional-parser"><a class="header" href="#3-bidirectional-parser">3. Bidirectional Parser</a></h3>
<ul>
<li><strong>Parse</strong>: Markdown → <code>Vec&lt;Block&gt;</code> (lazy, on-demand)</li>
<li><strong>Serialize</strong>: <code>Vec&lt;Block&gt;</code> → Markdown (for export)</li>
</ul>
<h3 id="4-storage-strategy"><a class="header" href="#4-storage-strategy">4. Storage Strategy</a></h3>
<p><strong>Filesystem</strong> (git-friendly, Logseq-compatible):</p>
<pre><code class="language-markdown">- Block 1 #card
- Nested answer
- TODO Block 2
priority:: high
</code></pre>
<p><strong>SurrealDB</strong> (queryable):</p>
<pre><code class="language-sql">DEFINE TABLE block;
DEFINE FIELD node_id ON block TYPE record(node);
DEFINE FIELD block_id ON block TYPE string;
DEFINE FIELD properties ON block TYPE object;
DEFINE INDEX block_tags ON block COLUMNS properties.tags;
</code></pre>
<h3 id="5-query-extensions"><a class="header" href="#5-query-extensions">5. Query Extensions</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Find all flashcards
graph.find_blocks_by_tag("card")
// Find all TODOs
graph.find_all_todos()
// Find blocks with custom property
node.find_blocks_by_property("priority", "high")
<span class="boring">}</span></code></pre></pre>
<h2 id="consequences-3"><a class="header" href="#consequences-3">Consequences</a></h2>
<h3 id="positive-3"><a class="header" href="#positive-3">Positive</a></h3>
<p><strong>Full Logseq Compatibility</strong> - Import/export preserves all block features
<strong>Queryable Blocks</strong> - Find #card, TODO, custom properties across KOGRAL
<strong>Backward Compatible</strong> - Existing nodes without blocks still work
<strong>Type-Safe</strong> - Structured data instead of regex parsing everywhere
<strong>Extensible</strong> - Custom block properties supported
<strong>Hierarchy Preserved</strong> - Nested blocks maintain parent-child relationships</p>
<h3 id="negative-3"><a class="header" href="#negative-3">Negative</a></h3>
<p>⚠️ <strong>Added Complexity</strong> - New data structures, parser, sync logic
⚠️ <strong>Dual Representation</strong> - Must keep <code>content</code> and <code>blocks</code> in sync
⚠️ <strong>Storage Overhead</strong> - SurrealDB stores both markdown and structure
⚠️ <strong>Migration Required</strong> - Existing data needs parsing to populate blocks</p>
<h3 id="neutral-3"><a class="header" href="#neutral-3">Neutral</a></h3>
<p>⚙️ <strong>Lazy Parsing</strong> - Blocks parsed on-demand (not stored by default)
⚙️ <strong>Opt-In</strong> - Config flag <code>blocks.enabled</code> to activate features
⚙️ <strong>Gradual Adoption</strong> - Can implement in phases</p>
<h2 id="implementation-phases"><a class="header" href="#implementation-phases">Implementation Phases</a></h2>
<p><strong>Phase 1: Foundation</strong> (No behavior change)</p>
<ul>
<li>Add <code>Block</code> struct to <code>models/block.rs</code></li>
<li>Add optional <code>blocks</code> field to <code>Node</code></li>
<li>Add config: <code>blocks.enabled = false</code> (default off)</li>
</ul>
<p><strong>Phase 2: Parser</strong></p>
<ul>
<li>Implement <code>BlockParser::parse()</code> (markdown → blocks)</li>
<li>Implement <code>BlockParser::serialize()</code> (blocks → markdown)</li>
<li>Add <code>Node::get_blocks()</code> method (lazy parsing)</li>
</ul>
<p><strong>Phase 3: Logseq Integration</strong></p>
<ul>
<li>Update <code>LogseqImporter</code> to parse blocks</li>
<li>Update <code>LogseqExporter</code> to serialize blocks</li>
<li>Test round-trip (Logseq → KB → Logseq)</li>
</ul>
<p><strong>Phase 4: Query API</strong></p>
<ul>
<li>Add <code>Graph::find_blocks_by_tag()</code></li>
<li>Add <code>Graph::find_all_todos()</code></li>
<li>Add <code>Node::find_blocks_by_property()</code></li>
</ul>
<p><strong>Phase 5: MCP/CLI Integration</strong></p>
<ul>
<li>Add <code>kb/find_blocks</code> MCP tool</li>
<li>Add <code>kb find-cards</code> CLI command</li>
<li>Add <code>kb find-todos</code> CLI command</li>
</ul>
<p><strong>Phase 6: SurrealDB Backend</strong></p>
<ul>
<li>Create <code>block</code> table schema</li>
<li>Index on tags, status, properties</li>
<li>Store blocks alongside nodes</li>
</ul>
<h2 id="alternatives-considered-2"><a class="header" href="#alternatives-considered-2">Alternatives Considered</a></h2>
<h3 id="alternative-1-blocks-as-first-class-nodes"><a class="header" href="#alternative-1-blocks-as-first-class-nodes">Alternative 1: Blocks as First-Class Nodes</a></h3>
<p>Convert each Logseq block to a separate KOGRAL Node.</p>
<p><strong>Rejected</strong>: Too granular, explosion of nodes, loses document context.</p>
<h3 id="alternative-2-parser-only-no-storage"><a class="header" href="#alternative-2-parser-only-no-storage">Alternative 2: Parser-Only (No Storage)</a></h3>
<p>Keep <code>content: String</code>, parse blocks on every access.</p>
<p><strong>Rejected</strong>: Can't query blocks in database, parse overhead, can't index.</p>
<h3 id="alternative-3-metadata-field"><a class="header" href="#alternative-3-metadata-field">Alternative 3: Metadata Field</a></h3>
<p>Store blocks in <code>metadata: HashMap&lt;String, Value&gt;</code>.</p>
<p><strong>Rejected</strong>: Not type-safe, harder to query, no schema validation.</p>
<h2 id="references-3"><a class="header" href="#references-3">References</a></h2>
<ul>
<li><a href="https://docs.logseq.com/#/page/blocks">Logseq Block Format</a></li>
<li><a href="architecture/adrs/../logseq-blocks-design.html">Full Design Document</a></li>
<li><a href="https://github.com/.../issues/XXX">Implementation Tracking</a></li>
</ul>
<h2 id="notes"><a class="header" href="#notes">Notes</a></h2>
<p><strong>Backward Compatibility Strategy</strong>:</p>
<ul>
<li><code>content</code> remains source of truth</li>
<li><code>blocks</code> is optional enhancement</li>
<li>Old code works unchanged</li>
<li>New features opt-in via config</li>
</ul>
<p><strong>Migration Path</strong>:</p>
<ul>
<li>Existing users: blocks disabled by default</li>
<li>New users: blocks enabled, parsed on import</li>
<li>Manual: <code>kb reindex --parse-blocks</code> to populate</li>
</ul>
<hr />
<p><strong>Decision Date</strong>: 2026-01-17
<strong>Approvers</strong>: TBD
<strong>Review Date</strong>: After Phase 2 implementation</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="adr-005-mcp-protocol-for-ai-integration"><a class="header" href="#adr-005-mcp-protocol-for-ai-integration">ADR-005: MCP Protocol for AI Integration</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="prerequisites-2"><a class="header" href="#prerequisites-2">Prerequisites</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="installation-methods-1"><a class="header" href="#installation-methods-1">Installation Methods</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="project-initialization"><a class="header" href="#project-initialization">Project Initialization</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="environment-configuration"><a class="header" href="#environment-configuration">Environment Configuration</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="verification"><a class="header" href="#verification">Verification</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="configuration-overview"><a class="header" href="#configuration-overview">Configuration Overview</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="nickel-schemas"><a class="header" href="#nickel-schemas">Nickel Schemas</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="graph-settings"><a class="header" href="#graph-settings">Graph Settings</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="inheritance"><a class="header" href="#inheritance">Inheritance</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="examples"><a class="header" href="#examples">Examples</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="storage-backends"><a class="header" href="#storage-backends">Storage Backends</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="filesystem-storage"><a class="header" href="#filesystem-storage">Filesystem Storage</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="surrealdb-storage"><a class="header" href="#surrealdb-storage">SurrealDB Storage</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="in-memory-storage"><a class="header" href="#in-memory-storage">In-Memory Storage</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="sync-strategies"><a class="header" href="#sync-strategies">Sync Strategies</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="embeddings-overview"><a class="header" href="#embeddings-overview">Embeddings Overview</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="provider-configuration"><a class="header" href="#provider-configuration">Provider Configuration</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="fastembed-local"><a class="header" href="#fastembed-local">FastEmbed Local</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="openai-integration"><a class="header" href="#openai-integration">OpenAI Integration</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="claude-integration"><a class="header" href="#claude-integration">Claude Integration</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="ollama-integration"><a class="header" href="#ollama-integration">Ollama Integration</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="semantic-search-2"><a class="header" href="#semantic-search-2">Semantic Search</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="template-system-1"><a class="header" href="#template-system-1">Template System</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="document-templates"><a class="header" href="#document-templates">Document Templates</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="export-templates"><a class="header" href="#export-templates">Export Templates</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="customization"><a class="header" href="#customization">Customization</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="cli-overview"><a class="header" href="#cli-overview">CLI Overview</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="commands-reference"><a class="header" href="#commands-reference">Commands Reference</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="workflows"><a class="header" href="#workflows">Workflows</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="nushell-scripts-1"><a class="header" href="#nushell-scripts-1">NuShell Scripts</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="mcp-quick-guide"><a class="header" href="#mcp-quick-guide">MCP Quick Guide</a></h1>
<p>Fast-track guide to integrating KOGRAL with Claude Code via the Model Context Protocol (MCP).</p>
<h2 id="what-is-mcp"><a class="header" href="#what-is-mcp">What is MCP?</a></h2>
<p>MCP (Model Context Protocol) is a protocol that allows Claude Code to interact with external tools and data sources. The kogral-mcp server exposes your KOGRAL knowledge base to Claude Code for AI-assisted knowledge management.</p>
<h2 id="quick-setup-5-minutes"><a class="header" href="#quick-setup-5-minutes">Quick Setup (5 Minutes)</a></h2>
<h3 id="step-1-build-mcp-server"><a class="header" href="#step-1-build-mcp-server">Step 1: Build MCP Server</a></h3>
<pre><code class="language-bash"># Build kogral-mcp server
cargo build --package kb-mcp --release
# Verify binary
ls -lh target/release/kb-mcp
</code></pre>
<h3 id="step-2-configure-claude-code"><a class="header" href="#step-2-configure-claude-code">Step 2: Configure Claude Code</a></h3>
<p>Add to <code>~/.config/claude/config.json</code>:</p>
<pre><code class="language-json">{
"mcpServers": {
"kogral-mcp": {
"command": "/path/to/knowledge-base/target/release/kb-mcp",
"args": ["serve"],
"env": {
"KOGRAL_DIR": "/path/to/your/project/.kogral"
}
}
}
}
</code></pre>
<p>Replace <code>/path/to/knowledge-base</code> and <code>/path/to/your/project/.kogral</code> with your actual paths.</p>
<h3 id="step-3-initialize-kogral"><a class="header" href="#step-3-initialize-kogral">Step 3: Initialize KOGRAL</a></h3>
<pre><code class="language-bash"># Navigate to your project
cd /path/to/your/project
# Initialize .kb directory
kb init
</code></pre>
<h3 id="step-4-start-claude-code"><a class="header" href="#step-4-start-claude-code">Step 4: Start Claude Code</a></h3>
<pre><code class="language-bash"># Start Claude Code (will auto-connect to kb-mcp)
claude-code
</code></pre>
<h3 id="step-5-test-connection"><a class="header" href="#step-5-test-connection">Step 5: Test Connection</a></h3>
<p>In Claude Code, try:</p>
<pre><code>Search for "error handling"
</code></pre>
<p>Claude will use the <code>kogral/search</code> tool to query your knowledge base.</p>
<hr />
<h2 id="essential-commands"><a class="header" href="#essential-commands">Essential Commands</a></h2>
<h3 id="search-knowledge-base"><a class="header" href="#search-knowledge-base">Search Knowledge Base</a></h3>
<p><strong>Natural language</strong>:</p>
<pre><code>Find notes about Rust error handling
</code></pre>
<p><strong>Claude translates to</strong>:</p>
<pre><code class="language-json">{
"tool": "kogral/search",
"params": {
"query": "error handling",
"type": "note",
"semantic": true
}
}
</code></pre>
<h3 id="add-note"><a class="header" href="#add-note">Add Note</a></h3>
<p><strong>Natural language</strong>:</p>
<pre><code>Add a note about async Rust patterns with tags rust, async, patterns
</code></pre>
<p><strong>Claude translates to</strong>:</p>
<pre><code class="language-json">{
"tool": "kogral/add_note",
"params": {
"title": "Async Rust Patterns",
"content": "...",
"tags": ["rust", "async", "patterns"]
}
}
</code></pre>
<h3 id="add-decision"><a class="header" href="#add-decision">Add Decision</a></h3>
<p><strong>Natural language</strong>:</p>
<pre><code>Document decision to use SurrealDB for storage
</code></pre>
<p><strong>Claude translates to</strong>:</p>
<pre><code class="language-json">{
"tool": "kogral/add_decision",
"params": {
"title": "Use SurrealDB for Storage",
"context": "Need scalable graph database",
"decision": "Adopt SurrealDB as primary storage backend",
"consequences": ["Better query performance", "Additional infrastructure"]
}
}
</code></pre>
<h3 id="get-guidelines"><a class="header" href="#get-guidelines">Get Guidelines</a></h3>
<p><strong>Natural language</strong>:</p>
<pre><code>What are our Rust error handling guidelines?
</code></pre>
<p><strong>Claude translates to</strong>:</p>
<pre><code class="language-json">{
"tool": "kogral/get_guidelines",
"params": {
"language": "rust",
"category": "error-handling"
}
}
</code></pre>
<h3 id="link-concepts"><a class="header" href="#link-concepts">Link Concepts</a></h3>
<p><strong>Natural language</strong>:</p>
<pre><code>Link note-async-patterns to decision-use-tokio as implements
</code></pre>
<p><strong>Claude translates to</strong>:</p>
<pre><code class="language-json">{
"tool": "kogral/link",
"params": {
"from": "note-async-patterns",
"to": "decision-use-tokio",
"relation": "implements"
}
}
</code></pre>
<hr />
<h2 id="common-workflows-1"><a class="header" href="#common-workflows-1">Common Workflows</a></h2>
<h3 id="capture-meeting-notes"><a class="header" href="#capture-meeting-notes">Capture Meeting Notes</a></h3>
<p><strong>You</strong>:</p>
<pre><code>We just decided in our meeting to migrate from REST to GraphQL.
Context: current REST API is too chatty, requires multiple round trips.
Consequences: better client performance, but team needs to learn GraphQL.
Create a decision record.
</code></pre>
<p><strong>Claude</strong> (uses <code>kb/add_decision</code>):</p>
<pre><code>Created decision: decision-migrate-to-graphql
Status: proposed
Context: Current REST API requires multiple round trips (chatty)
Decision: Migrate from REST to GraphQL API
Consequences:
- ✅ Better client performance
- ❌ Team learning curve for GraphQL
</code></pre>
<h3 id="find-related-knowledge"><a class="header" href="#find-related-knowledge">Find Related Knowledge</a></h3>
<p><strong>You</strong>:</p>
<pre><code>I'm working on error handling. What related notes and patterns do we have?
</code></pre>
<p><strong>Claude</strong> (uses <code>kogral/search</code> with semantic=true):</p>
<pre><code>Found 5 related items:
1. **Error Handling with thiserror** (pattern)
Tags: rust, error-handling
Created: 2026-01-15
2. **Result Type Best Practices** (guideline)
Tags: rust, error-handling, best-practices
Created: 2026-01-10
3. **Decision: Use anyhow for Application Errors** (decision)
Status: accepted
Created: 2026-01-08
4. **Custom Error Types** (note)
Tags: rust, error-handling
Created: 2026-01-05
5. **Error Propagation Patterns** (pattern)
Tags: rust, error-handling
Created: 2026-01-03
Would you like me to link your current work to any of these?
</code></pre>
<h3 id="document-implementation"><a class="header" href="#document-implementation">Document Implementation</a></h3>
<p><strong>You</strong>:</p>
<pre><code>I just implemented the user authentication module following our security guidelines.
Add a note and link it to our security guideline.
</code></pre>
<p><strong>Claude</strong> (uses <code>kb/add_note</code> + <code>kb/link</code>):</p>
<pre><code>Created note: note-user-auth-implementation
Tags: security, authentication, implementation
Linked:
note-user-auth-implementation --[implements]--&gt; guideline-security-practices
Done! Your implementation is now documented and connected to our guidelines.
</code></pre>
<h3 id="review-project-knowledge"><a class="header" href="#review-project-knowledge">Review Project Knowledge</a></h3>
<p><strong>You</strong>:</p>
<pre><code>Summarize our project knowledge: what decisions have we made, what patterns are we using?
</code></pre>
<p><strong>Claude</strong> (uses <code>kb/summarize_project</code> prompt):</p>
<pre><code># Project Knowledge Summary
**Total Nodes**: 42
## Recent Decisions (5)
- Use SurrealDB for Storage (accepted, 2 days ago)
- Adopt MCP for AI Integration (accepted, 5 days ago)
- Nickel for Configuration (accepted, 1 week ago)
## Active Patterns (8)
- Error Handling with thiserror
- Repository Pattern for DB
- Builder Pattern for Complex Objects
- ...
## Top Tags
1. rust (23 nodes)
2. architecture (12 nodes)
3. error-handling (8 nodes)
4. patterns (7 nodes)
## Guideline Coverage
- Rust: 5 guidelines
- Testing: 3 guidelines
- Security: 2 guidelines
</code></pre>
<hr />
<h2 id="advanced-usage"><a class="header" href="#advanced-usage">Advanced Usage</a></h2>
<h3 id="conditional-queries"><a class="header" href="#conditional-queries">Conditional Queries</a></h3>
<p><strong>You</strong>:</p>
<pre><code>Find all decisions made in the last week that are still proposed (not accepted)
</code></pre>
<p><strong>Claude</strong> (combines <code>kogral/search</code> with filtering):</p>
<pre><code>Found 2 proposed decisions from last week:
1. **Migrate to GraphQL** (proposed)
Created: 3 days ago
Context: REST API is too chatty...
2. **Add Redis Caching** (proposed)
Created: 5 days ago
Context: Database queries are slow...
Would you like to review these and update their status?
</code></pre>
<h3 id="cross-reference-check"><a class="header" href="#cross-reference-check">Cross-Reference Check</a></h3>
<p><strong>You</strong>:</p>
<pre><code>Before I implement X, check if we have any decisions or guidelines about it
</code></pre>
<p><strong>Claude</strong> (uses <code>kogral/search</code> + <code>kogral/get_guidelines</code>):</p>
<pre><code>Checking existing knowledge about X...
**Decisions**: None found
**Guidelines**: 1 relevant guideline found
- guideline-module-structure: "Keep modules focused and small"
**Related Notes**: 2 notes mention similar topics
- note-module-design-patterns
- note-separation-of-concerns
Recommendation: Your implementation of X looks aligned with our guidelines.
Consider linking it to guideline-module-structure when done.
</code></pre>
<h3 id="export-for-sharing"><a class="header" href="#export-for-sharing">Export for Sharing</a></h3>
<p><strong>You</strong>:</p>
<pre><code>Export our architectural decisions to Logseq so I can review them visually
</code></pre>
<p><strong>Claude</strong> (uses <code>kb/export</code>):</p>
<pre><code>Exporting to Logseq format...
✓ Exported 12 decisions to /Users/akasha/logseq-export/
- 12 decision records
- Preserved wikilinks
- Added Logseq properties
You can now open /Users/akasha/logseq-export/ in Logseq to visualize the decision graph.
</code></pre>
<hr />
<h2 id="tips-and-tricks-1"><a class="header" href="#tips-and-tricks-1">Tips and Tricks</a></h2>
<h3 id="1-use-natural-language"><a class="header" href="#1-use-natural-language">1. Use Natural Language</a></h3>
<p>Don't worry about exact tool syntax. Claude understands:</p>
<p><strong>Don't say</strong>: "Use kb/search with query='rust' and type='pattern'"
<strong>Do say</strong>: "Find Rust patterns in KOGRAL"</p>
<h3 id="2-be-specific-with-tags"><a class="header" href="#2-be-specific-with-tags">2. Be Specific with Tags</a></h3>
<p>When adding notes, use consistent tags:</p>
<p><strong>Good</strong>: <code>tags: rust, error-handling, pattern</code>
<strong>Bad</strong>: <code>tags: Rust, ErrorHandling, patterns</code></p>
<h3 id="3-link-as-you-go"><a class="header" href="#3-link-as-you-go">3. Link as You Go</a></h3>
<p>After creating notes, ask Claude to link them:</p>
<pre><code>Link this note to our error handling guideline as 'implements'
</code></pre>
<h3 id="4-review-regularly"><a class="header" href="#4-review-regularly">4. Review Regularly</a></h3>
<p>Ask Claude for summaries:</p>
<pre><code>What have we documented this week?
</code></pre>
<h3 id="5-use-semantic-search"><a class="header" href="#5-use-semantic-search">5. Use Semantic Search</a></h3>
<p>For conceptual queries:</p>
<pre><code>Find anything related to "making code maintainable"
</code></pre>
<p>Not just keyword "maintainable", but concepts like refactoring, clean code, patterns, etc.</p>
<hr />
<h2 id="troubleshooting-2"><a class="header" href="#troubleshooting-2">Troubleshooting</a></h2>
<h3 id="mcp-server-not-responding-1"><a class="header" href="#mcp-server-not-responding-1">"MCP server not responding"</a></h3>
<pre><code class="language-bash"># Check kb-mcp is built
ls target/release/kb-mcp
# Test manually
echo '{"jsonrpc":"2.0","id":1,"method":"kogral/search","params":{"query":"test"}}' \
| target/release/kb-mcp serve
</code></pre>
<h3 id="kb-directory-not-found-1"><a class="header" href="#kb-directory-not-found-1">"KB directory not found"</a></h3>
<pre><code class="language-bash"># Verify .kb exists
ls -la /path/to/project/.kogral
# Initialize if missing
cd /path/to/project
kb init
</code></pre>
<h3 id="permission-denied"><a class="header" href="#permission-denied">"Permission denied"</a></h3>
<pre><code class="language-bash"># Make binary executable
chmod +x target/release/kb-mcp
# Check environment variable
echo $KOGRAL_DIR
</code></pre>
<h3 id="empty-search-results"><a class="header" href="#empty-search-results">"Empty search results"</a></h3>
<pre><code class="language-bash"># Add some test content
kb add note "Test Note" --content "Test content"
# Try search again in Claude Code
</code></pre>
<hr />
<h2 id="next-steps-7"><a class="header" href="#next-steps-7">Next Steps</a></h2>
<ul>
<li><strong>Read</strong>: <a href="apps/../api/mcp-tools.html">MCP Tools API Reference</a> for all available tools</li>
<li><strong>Explore</strong>: <a href="apps/../guides/use-cases.html">Use Cases</a> for more examples</li>
<li><strong>Configure</strong>: <a href="apps/../config/overview.html">Configuration Reference</a> to customize behavior</li>
<li><strong>Integrate</strong>: <a href="apps/claude-code.html">Claude Code Integration</a> for advanced setup</li>
</ul>
<hr />
<h2 id="quick-reference-card"><a class="header" href="#quick-reference-card">Quick Reference Card</a></h2>
<div class="table-wrapper"><table><thead><tr><th>Task</th><th>Say to Claude</th></tr></thead><tbody>
<tr><td>Search</td><td>"Find notes about X"</td></tr>
<tr><td>Add note</td><td>"Add a note about X with tags Y, Z"</td></tr>
<tr><td>Add decision</td><td>"Document decision to use X for Y"</td></tr>
<tr><td>Get guidelines</td><td>"What are our X guidelines?"</td></tr>
<tr><td>Link nodes</td><td>"Link A to B as implements"</td></tr>
<tr><td>Summarize</td><td>"Summarize project knowledge"</td></tr>
<tr><td>Export</td><td>"Export to Logseq format"</td></tr>
</tbody></table>
</div>
<hr />
<p><strong>Remember</strong>: Claude Code with MCP turns KOGRAL into an active participant in your development workflow. Ask questions, capture decisions, and let AI help you maintain your project's knowledge graph.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="claude-code-integration-1"><a class="header" href="#claude-code-integration-1">Claude Code Integration</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="logseq-integration"><a class="header" href="#logseq-integration">Logseq Integration</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="obsidian-compatibility"><a class="header" href="#obsidian-compatibility">Obsidian Compatibility</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="git-workflows"><a class="header" href="#git-workflows">Git Workflows</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="mcp-protocol"><a class="header" href="#mcp-protocol">MCP Protocol</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="mcp-tools-api-reference"><a class="header" href="#mcp-tools-api-reference">MCP Tools API Reference</a></h1>
<p>Complete reference for the Model Context Protocol (MCP) server tools and resources.</p>
<h2 id="overview"><a class="header" href="#overview">Overview</a></h2>
<p>The kogral-mcp server implements the MCP protocol (JSON-RPC 2.0) for Claude Code integration. It provides:</p>
<ul>
<li><strong>10 Tools</strong>: Operations for querying and modifying knowledge base (7 core + 3 block tools)</li>
<li><strong>6 Resources</strong>: Access to knowledge graph content via URIs</li>
<li><strong>2 Prompts</strong>: Guided workflows for common tasks</li>
</ul>
<h2 id="server-configuration"><a class="header" href="#server-configuration">Server Configuration</a></h2>
<h3 id="start-mcp-server"><a class="header" href="#start-mcp-server">Start MCP Server</a></h3>
<pre><code class="language-bash"># Stdio transport (local use)
kb serve
# Or run directly
kb-mcp serve
</code></pre>
<h3 id="claude-code-configuration"><a class="header" href="#claude-code-configuration">Claude Code Configuration</a></h3>
<p>Add to <code>~/.config/claude/config.json</code>:</p>
<pre><code class="language-json">{
"mcpServers": {
"kogral-mcp": {
"command": "/path/to/kb-mcp",
"args": ["serve"],
"env": {
"KOGRAL_DIR": "/path/to/project/.kogral"
}
}
}
}
</code></pre>
<h2 id="tools"><a class="header" href="#tools">Tools</a></h2>
<h3 id="kbsearch"><a class="header" href="#kbsearch">kb/search</a></h3>
<p>Search the knowledge base using text and/or semantic similarity.</p>
<p><strong>Input Schema</strong>:</p>
<pre><code class="language-json">{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query"
},
"type": {
"type": "string",
"enum": ["note", "decision", "guideline", "pattern", "journal", "execution", "all"],
"description": "Filter by node type",
"default": "all"
},
"project": {
"type": "string",
"description": "Limit search to specific project graph"
},
"semantic": {
"type": "boolean",
"description": "Enable semantic similarity search",
"default": true
},
"threshold": {
"type": "number",
"description": "Minimum similarity threshold (0-1)",
"default": 0.4
},
"limit": {
"type": "integer",
"description": "Maximum number of results",
"default": 10
}
},
"required": ["query"]
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 1,
"method": "kogral/search",
"params": {
"query": "error handling patterns in Rust",
"type": "pattern",
"semantic": true,
"threshold": 0.6,
"limit": 5
}
}
</code></pre>
<p><strong>Example Response</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 1,
"result": {
"type": "text",
"text": "Found 3 result(s):\n\n1. Error Handling with thiserror (pattern, score: 0.85)\n Tags: rust, error-handling\n Created: 2026-01-15\n \n2. Result Type Best Practices (guideline, score: 0.72)\n Tags: rust, error-handling, best-practices\n Created: 2026-01-10\n\n3. Custom Error Types (note, score: 0.65)\n Tags: rust, error-handling\n Created: 2026-01-08"
}
}
</code></pre>
<h3 id="kbadd_note"><a class="header" href="#kbadd_note">kb/add_note</a></h3>
<p>Add a new note to the knowledge base.</p>
<p><strong>Input Schema</strong>:</p>
<pre><code class="language-json">{
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Note title"
},
"content": {
"type": "string",
"description": "Note content (markdown)"
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Tags for categorization",
"default": []
},
"relates_to": {
"type": "array",
"items": { "type": "string" },
"description": "Related node IDs",
"default": []
},
"project": {
"type": "string",
"description": "Project graph name",
"default": "default"
}
},
"required": ["title", "content"]
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 2,
"method": "kogral/add_note",
"params": {
"title": "Async Trait Patterns",
"content": "Common patterns for using async traits in Rust:\n\n1. Use `async-trait` crate\n2. Box return types for flexibility\n3. Consider Send + Sync bounds",
"tags": ["rust", "async", "patterns"],
"relates_to": ["pattern-error-handling"]
}
}
</code></pre>
<p><strong>Example Response</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 2,
"result": {
"type": "text",
"text": "Note added successfully: note-async-trait-patterns (ID: note-abc123)"
}
}
</code></pre>
<h3 id="kbadd_decision"><a class="header" href="#kbadd_decision">kb/add_decision</a></h3>
<p>Create an Architectural Decision Record (ADR).</p>
<p><strong>Input Schema</strong>:</p>
<pre><code class="language-json">{
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Decision title"
},
"context": {
"type": "string",
"description": "Decision context and background"
},
"decision": {
"type": "string",
"description": "The decision made"
},
"consequences": {
"type": "array",
"items": { "type": "string" },
"description": "List of consequences",
"default": []
},
"status": {
"type": "string",
"enum": ["proposed", "accepted", "rejected", "deprecated", "superseded"],
"default": "proposed"
},
"tags": {
"type": "array",
"items": { "type": "string" },
"default": []
}
},
"required": ["title", "context", "decision"]
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 3,
"method": "kogral/add_decision",
"params": {
"title": "Use SurrealDB for Knowledge Graph Storage",
"context": "Need a scalable storage solution that supports graph queries and can handle growing knowledge base",
"decision": "Adopt SurrealDB as the primary storage backend for production deployments",
"consequences": [
"Better query performance for graph traversal",
"Native support for relationships",
"Additional infrastructure dependency",
"Team needs to learn SurrealDB query language"
],
"status": "accepted",
"tags": ["architecture", "storage", "surrealdb"]
}
}
</code></pre>
<p><strong>Example Response</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 3,
"result": {
"type": "text",
"text": "Decision added: decision-use-surrealdb (ID: decision-xyz789)\nStatus: accepted"
}
}
</code></pre>
<h3 id="kblink"><a class="header" href="#kblink">kb/link</a></h3>
<p>Create a relationship between two nodes.</p>
<p><strong>Input Schema</strong>:</p>
<pre><code class="language-json">{
"type": "object",
"properties": {
"from": {
"type": "string",
"description": "Source node ID"
},
"to": {
"type": "string",
"description": "Target node ID"
},
"relation": {
"type": "string",
"enum": ["relates_to", "depends_on", "implements", "extends", "supersedes", "explains"],
"description": "Relationship type"
},
"strength": {
"type": "number",
"description": "Relationship strength (0-1)",
"minimum": 0,
"maximum": 1,
"default": 1.0
}
},
"required": ["from", "to", "relation"]
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 4,
"method": "kogral/link",
"params": {
"from": "note-async-trait-patterns",
"to": "pattern-error-handling",
"relation": "relates_to",
"strength": 0.8
}
}
</code></pre>
<p><strong>Example Response</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 4,
"result": {
"type": "text",
"text": "Link created: note-async-trait-patterns --[relates_to]--&gt; pattern-error-handling (strength: 0.8)"
}
}
</code></pre>
<p><strong>Relationship Types</strong>:</p>
<ul>
<li><code>relates_to</code>: General conceptual relationship</li>
<li><code>depends_on</code>: Dependency (from depends on to)</li>
<li><code>implements</code>: Implementation of concept/pattern</li>
<li><code>extends</code>: Inherits or extends another node</li>
<li><code>supersedes</code>: Replaces an older version</li>
<li><code>explains</code>: Provides documentation/clarification</li>
</ul>
<h3 id="kbget_guidelines"><a class="header" href="#kbget_guidelines">kb/get_guidelines</a></h3>
<p>Retrieve guidelines for current project with inheritance resolution.</p>
<p><strong>Input Schema</strong>:</p>
<pre><code class="language-json">{
"type": "object",
"properties": {
"language": {
"type": "string",
"description": "Programming language (e.g., rust, nushell)"
},
"category": {
"type": "string",
"description": "Guideline category (e.g., error-handling, testing)"
},
"include_base": {
"type": "boolean",
"description": "Include shared/base guidelines",
"default": true
}
}
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 5,
"method": "kogral/get_guidelines",
"params": {
"language": "rust",
"category": "error-handling",
"include_base": true
}
}
</code></pre>
<p><strong>Example Response</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 5,
"result": {
"type": "text",
"text": "Guidelines for rust/error-handling:\n\n## Project Guidelines (priority: 150)\n\n1. Custom Error Types (guideline-custom-errors)\n - Use thiserror for error definitions\n - Implement From traits for conversions\n - Source: .kogral/guidelines/rust-errors.md\n\n## Shared Guidelines (priority: 50)\n\n1. Result Type Best Practices (guideline-result-best-practices)\n - Always use Result&lt;T&gt; for fallible operations\n - Never use unwrap() in production\n - Source: ~/Tools/.kogral-shared/guidelines/rust-errors.md\n\n2. Error Propagation (guideline-error-propagation)\n - Use ? operator for error propagation\n - Add context with .context()\n - Source: ~/Tools/.kogral-shared/guidelines/rust-errors.md"
}
}
</code></pre>
<h3 id="kblist_graphs"><a class="header" href="#kblist_graphs">kb/list_graphs</a></h3>
<p>List available knowledge graphs.</p>
<p><strong>Input Schema</strong>:</p>
<pre><code class="language-json">{
"type": "object",
"properties": {}
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 6,
"method": "kb/list_graphs"
}
</code></pre>
<p><strong>Example Response</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 6,
"result": {
"type": "text",
"text": "Available graphs:\n\n- default (Current project)\n Path: /path/to/project/.kogral\n Nodes: 42\n Last modified: 2026-01-17T10:30:00Z\n\n- shared (Shared guidelines)\n Path: ~/Tools/.kogral-shared\n Nodes: 156\n Last modified: 2026-01-15T14:20:00Z"
}
}
</code></pre>
<h3 id="kbexport"><a class="header" href="#kbexport">kb/export</a></h3>
<p>Export knowledge base to various formats.</p>
<p><strong>Input Schema</strong>:</p>
<pre><code class="language-json">{
"type": "object",
"properties": {
"format": {
"type": "string",
"enum": ["logseq", "json", "markdown"],
"description": "Export format"
},
"output_path": {
"type": "string",
"description": "Output file or directory path"
},
"include_types": {
"type": "array",
"items": {
"type": "string",
"enum": ["note", "decision", "guideline", "pattern", "journal", "execution"]
},
"description": "Node types to include",
"default": ["note", "decision", "guideline", "pattern"]
},
"skip_journals": {
"type": "boolean",
"default": true
}
},
"required": ["format", "output_path"]
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 7,
"method": "kogral/export",
"params": {
"format": "logseq",
"output_path": "/Users/akasha/logseq-graph",
"include_types": ["note", "decision", "guideline"],
"skip_journals": true
}
}
</code></pre>
<p><strong>Example Response</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 7,
"result": {
"type": "text",
"text": "Export completed:\n\nFormat: Logseq\nOutput: /Users/akasha/logseq-graph\nExported: 42 nodes\n - 23 notes\n - 12 decisions\n - 7 guidelines\n\nLogseq graph ready to open."
}
}
</code></pre>
<h2 id="block-tools"><a class="header" href="#block-tools">Block Tools</a></h2>
<p>Tools for querying Logseq content blocks. Requires <code>blocks.enable_mcp_tools = true</code> in configuration.</p>
<h3 id="kbfind_blocks"><a class="header" href="#kbfind_blocks">kb/find_blocks</a></h3>
<p>Find blocks by tag, task status, or custom property across the knowledge base.</p>
<p><strong>Input Schema</strong>:</p>
<pre><code class="language-json">{
"type": "object",
"properties": {
"tag": {
"type": "string",
"description": "Find blocks with this tag (e.g., 'card', 'important')"
},
"status": {
"type": "string",
"enum": ["TODO", "DOING", "DONE", "LATER", "NOW", "WAITING", "CANCELLED"],
"description": "Find blocks with this task status"
},
"property_key": {
"type": "string",
"description": "Custom property key to search for"
},
"property_value": {
"type": "string",
"description": "Custom property value to match"
},
"limit": {
"type": "integer",
"description": "Maximum number of results",
"default": 20
}
}
}
</code></pre>
<p><strong>Note</strong>: Provide one of: <code>tag</code>, <code>status</code>, or both <code>property_key</code> and <code>property_value</code>.</p>
<p><strong>Example Request</strong> (find blocks by tag):</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 8,
"method": "kogral/find_blocks",
"params": {
"tag": "high-priority",
"limit": 10
}
}
</code></pre>
<p><strong>Example Response</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 8,
"result": {
"type": "text",
"text": "Found 3 blocks with tag '#high-priority':\n\n**Project Tasks** (project-tasks-123)\n - TODO Implement authentication #high-priority\n - TODO Fix security vulnerability #high-priority\n\n**Sprint Planning** (sprint-planning-456)\n - DOING Refactor database layer #high-priority"
}
}
</code></pre>
<p><strong>Example Request</strong> (find blocks by property):</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 9,
"method": "kogral/find_blocks",
"params": {
"property_key": "priority",
"property_value": "high",
"limit": 15
}
}
</code></pre>
<h3 id="kbfind_todos"><a class="header" href="#kbfind_todos">kb/find_todos</a></h3>
<p>Find all TODO blocks across the knowledge base.</p>
<p><strong>Input Schema</strong>:</p>
<pre><code class="language-json">{
"type": "object",
"properties": {
"limit": {
"type": "integer",
"description": "Maximum number of results",
"default": 20
}
}
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 10,
"method": "kogral/find_todos",
"params": {
"limit": 25
}
}
</code></pre>
<p><strong>Example Response</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 10,
"result": {
"type": "text",
"text": "Found 8 TODO blocks:\n\n**Project Tasks** (project-tasks-123)\n - TODO Implement authentication\n - TODO Write integration tests\n - TODO Update documentation\n\n**Bug Fixes** (bug-fixes-456)\n - TODO Fix race condition in cache\n - TODO Address memory leak\n\n**Research** (research-789)\n - TODO Evaluate GraphQL alternatives\n - TODO Benchmark new approach\n - TODO Document findings"
}
}
</code></pre>
<h3 id="kbfind_cards"><a class="header" href="#kbfind_cards">kb/find_cards</a></h3>
<p>Find all flashcard blocks (blocks tagged with #card) for spaced repetition learning.</p>
<p><strong>Input Schema</strong>:</p>
<pre><code class="language-json">{
"type": "object",
"properties": {
"limit": {
"type": "integer",
"description": "Maximum number of flashcards",
"default": 10
}
}
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 11,
"method": "kogral/find_cards",
"params": {
"limit": 5
}
}
</code></pre>
<p><strong>Example Response</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 11,
"result": {
"type": "text",
"text": "Found 3 flashcards:\n\n**Rust Learning** (rust-learning-123)\n - What is Rust's ownership model? #card #rust\n - Ownership prevents data races at compile time\n - Each value has a single owner\n\n**System Design** (system-design-456)\n - What is the CAP theorem? #card #distributed-systems\n - Consistency, Availability, Partition tolerance\n - Can only guarantee 2 of 3\n\n**Algorithms** (algorithms-789)\n - What is the time complexity of quicksort? #card #algorithms\n - Average: O(n log n)\n - Worst case: O(n²)"
}
}
</code></pre>
<p><strong>Use Cases</strong>:</p>
<ul>
<li><strong>kb/find_blocks</strong>: General block search by metadata</li>
<li><strong>kb/find_todos</strong>: Task management and tracking</li>
<li><strong>kb/find_cards</strong>: Spaced repetition learning system</li>
</ul>
<p><strong>See Also</strong>:</p>
<ul>
<li><a href="api/../kb/core-concepts.html#logseq-content-blocks">Logseq Blocks Support</a></li>
<li><a href="api/../architecture/adrs/004-logseq-blocks-support.html">ADR-004: Logseq Blocks Support</a></li>
</ul>
<h2 id="resources"><a class="header" href="#resources">Resources</a></h2>
<p>Resources provide read-only access to knowledge graph content via URIs.</p>
<h3 id="kogralprojectnotes"><a class="header" href="#kogralprojectnotes">kogral://project/notes</a></h3>
<p>Access project notes.</p>
<p><strong>URI</strong>: <code>kogral://project/notes</code> or <code>kogral://project/notes/{note-id}</code></p>
<p><strong>Example</strong>: Read all project notes</p>
<pre><code>kogral://project/notes
</code></pre>
<p><strong>Example</strong>: Read specific note</p>
<pre><code>kogral://project/notes/async-trait-patterns
</code></pre>
<h3 id="kogralprojectdecisions"><a class="header" href="#kogralprojectdecisions">kogral://project/decisions</a></h3>
<p>Access project decisions (ADRs).</p>
<p><strong>URI</strong>: <code>kogral://project/decisions</code> or <code>kogral://project/decisions/{decision-id}</code></p>
<h3 id="kogralprojectguidelines"><a class="header" href="#kogralprojectguidelines">kogral://project/guidelines</a></h3>
<p>Access project-specific guidelines.</p>
<p><strong>URI</strong>: <code>kogral://project/guidelines</code> or <code>kogral://project/guidelines/{guideline-id}</code></p>
<h3 id="kogralprojectpatterns"><a class="header" href="#kogralprojectpatterns">kogral://project/patterns</a></h3>
<p>Access project patterns.</p>
<p><strong>URI</strong>: <code>kogral://project/patterns</code> or <code>kogral://project/patterns/{pattern-id}</code></p>
<h3 id="kogralsharedguidelines"><a class="header" href="#kogralsharedguidelines">kogral://shared/guidelines</a></h3>
<p>Access shared guidelines (inherited).</p>
<p><strong>URI</strong>: <code>kogral://shared/guidelines</code> or <code>kogral://shared/guidelines/{guideline-id}</code></p>
<h3 id="kogralsharedpatterns"><a class="header" href="#kogralsharedpatterns">kogral://shared/patterns</a></h3>
<p>Access shared patterns (inherited).</p>
<p><strong>URI</strong>: <code>kogral://shared/patterns</code> or <code>kogral://shared/patterns/{pattern-id}</code></p>
<h2 id="prompts"><a class="header" href="#prompts">Prompts</a></h2>
<p>Prompts provide guided workflows for common tasks.</p>
<h3 id="kbsummarize_project"><a class="header" href="#kbsummarize_project">kb/summarize_project</a></h3>
<p>Generate a comprehensive project knowledge summary.</p>
<p><strong>Arguments</strong>:</p>
<pre><code class="language-json">{
"project": {
"type": "string",
"description": "Project graph name",
"default": "default"
}
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 8,
"method": "kogral/summarize_project",
"params": {
"project": "default"
}
}
</code></pre>
<p><strong>Returns</strong>: Prompt messages with project summary including:</p>
<ul>
<li>Total node counts by type</li>
<li>Recent additions</li>
<li>Top tags</li>
<li>Key decisions</li>
<li>Active patterns</li>
</ul>
<h3 id="kbfind_related"><a class="header" href="#kbfind_related">kb/find_related</a></h3>
<p>Find nodes related to a specific topic or node.</p>
<p><strong>Arguments</strong>:</p>
<pre><code class="language-json">{
"node_id": {
"type": "string",
"description": "Node ID to find relations for"
},
"depth": {
"type": "integer",
"description": "Maximum traversal depth",
"default": 2
}
}
</code></pre>
<p><strong>Example Request</strong>:</p>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 9,
"method": "kb/find_related",
"params": {
"node_id": "pattern-error-handling",
"depth": 2
}
}
</code></pre>
<p><strong>Returns</strong>: Prompt messages with:</p>
<ul>
<li>Direct relationships</li>
<li>Indirect relationships (depth 2+)</li>
<li>Common tags</li>
<li>Related guidelines</li>
</ul>
<h2 id="error-handling-1"><a class="header" href="#error-handling-1">Error Handling</a></h2>
<h3 id="error-codes"><a class="header" href="#error-codes">Error Codes</a></h3>
<p>Standard JSON-RPC 2.0 error codes:</p>
<div class="table-wrapper"><table><thead><tr><th>Code</th><th>Meaning</th><th>Description</th></tr></thead><tbody>
<tr><td>-32700</td><td>Parse error</td><td>Invalid JSON</td></tr>
<tr><td>-32600</td><td>Invalid Request</td><td>Missing required fields</td></tr>
<tr><td>-32601</td><td>Method not found</td><td>Unknown tool/resource</td></tr>
<tr><td>-32602</td><td>Invalid params</td><td>Parameter validation failed</td></tr>
<tr><td>-32603</td><td>Internal error</td><td>Server-side error</td></tr>
</tbody></table>
</div>
<h3 id="example-error-response"><a class="header" href="#example-error-response">Example Error Response</a></h3>
<pre><code class="language-json">{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params",
"data": {
"details": "Field 'query' is required but missing",
"field": "query"
}
}
}
</code></pre>
<h2 id="best-practices-2"><a class="header" href="#best-practices-2">Best Practices</a></h2>
<h3 id="1-use-semantic-search-for-discovery"><a class="header" href="#1-use-semantic-search-for-discovery">1. Use Semantic Search for Discovery</a></h3>
<pre><code class="language-json">{
"method": "kogral/search",
"params": {
"query": "how to handle database connection errors",
"semantic": true,
"threshold": 0.5
}
}
</code></pre>
<h3 id="2-link-related-concepts"><a class="header" href="#2-link-related-concepts">2. Link Related Concepts</a></h3>
<pre><code class="language-json">{
"method": "kogral/link",
"params": {
"from": "note-new-discovery",
"to": "pattern-related-pattern",
"relation": "implements"
}
}
</code></pre>
<h3 id="3-query-guidelines-before-implementation"><a class="header" href="#3-query-guidelines-before-implementation">3. Query Guidelines Before Implementation</a></h3>
<pre><code class="language-json">{
"method": "kogral/get_guidelines",
"params": {
"language": "rust",
"category": "testing"
}
}
</code></pre>
<h3 id="4-document-decisions-with-adrs"><a class="header" href="#4-document-decisions-with-adrs">4. Document Decisions with ADRs</a></h3>
<pre><code class="language-json">{
"method": "kogral/add_decision",
"params": {
"title": "Use X for Y",
"context": "Background...",
"decision": "We will...",
"consequences": ["Pro 1", "Con 1"]
}
}
</code></pre>
<h2 id="see-also-1"><a class="header" href="#see-also-1">See Also</a></h2>
<ul>
<li><a href="https://modelcontextprotocol.io/docs">MCP Specification</a></li>
<li><a href="api/../user-guide/quickstart.html">Quick Start Guide</a></li>
<li><a href="api/../user-guide/configuration.html">Configuration Reference</a></li>
<li><a href="api/../../crates/kb-mcp/src/">kb-mcp Source Code</a></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="resources-1"><a class="header" href="#resources-1">Resources</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="rust-api"><a class="header" href="#rust-api">Rust API</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="development-setup"><a class="header" href="#development-setup">Development Setup</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="code-standards"><a class="header" href="#code-standards">Code Standards</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="testing"><a class="header" href="#testing">Testing</a></h1>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js"></script>
<script src="mark.min.js"></script>
<script src="searcher.js"></script>
<script src="clipboard.min.js"></script>
<script src="highlight.js"></script>
<script src="book.js"></script>
<!-- Custom JS scripts -->
<script>
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
</div>
</body>
</html>