kogral/docs/kogral/design-philosophy.md
2026-01-23 16:11:07 +00:00

11 KiB

Design Philosophy

The principles and values guiding KOGRAL design and implementation.

Core Tenets

1. Config-Driven, Not Hardcoded

Principle: All behavior should be configurable via schemas, not baked into code.

Why: Flexibility without code changes. Users adapt KOGRAL to their workflows, not vice versa.

Example:

// ❌ Bad: Hardcoded
impl EmbeddingProvider {
    fn new() -> Self {
        FastEmbedProvider::new("BAAI/bge-small-en-v1.5") // Can't change
    }
}

// ✅ Good: Config-driven
impl EmbeddingProvider {
    fn from_config(config: &EmbeddingConfig) -> Result<Box<dyn EmbeddingProvider>> {
        match config.provider {
            'fastembed => Ok(Box::new(FastEmbedProvider::new(&config.model)?)),
            'openai => Ok(Box::new(OpenAIProvider::new(&config.model)?)),
        }
    }
}

Benefits:

  • Switch embedding providers without recompilation
  • Different configs for dev/prod
  • User choice, not developer mandate

2. Type-Safe Configuration

Principle: Validate configuration before runtime, not during.

Why: Catch errors early, reduce runtime failures.

Implementation: Nickel contracts + serde validation = double validation

# Schema defines valid values
EmbeddingProvider = [| 'openai, 'claude, 'fastembed |]

# Typo caught at export time, not runtime
{ provider = 'opena1 }  # Error: Invalid variant

Benefits:

  • Errors found during nickel export, not during execution
  • Self-documenting: schema is the spec
  • Refactoring safe: change schema, find all usages

3. Local-First, Cloud-Optional

Principle: Core functionality works offline, cloud is enhancement.

Why: Privacy, cost control, offline development.

Examples:

Feature Local Cloud
Storage Filesystem SurrealDB
Embeddings fastembed OpenAI/Claude
Search Text-based Semantic

Benefits:

  • Works on planes, trains, areas with poor internet
  • No API costs for small projects
  • Privacy-sensitive projects keep data local
  • Production can use cloud for scale

4. Git-Friendly by Default

Principle: Knowledge should version alongside code.

Why: Knowledge describes code, should evolve with it.

Implementation:

  • Markdown + YAML frontmatter (text-based, diffable)
  • One file per node (granular commits)
  • Wikilinks preserved (works in Logseq, Obsidian)

Benefits:

# 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)

5. AI-Native, Human-Readable

Principle: Optimized for AI consumption, readable by humans.

Why: Best of both worlds - AI-assisted discovery, human verification.

Implementation:

  • Structured: YAML frontmatter for AI parsing
  • Semantic: Embeddings for AI queries
  • Readable: Markdown for human consumption
  • Linked: Typed relationships for AI traversal

Example:

---
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

6. Composition Over Inheritance

Principle: Build systems by composing small, focused components.

Why: Flexibility, testability, maintainability.

Implementation:

// Small, focused traits
trait Storage { ... }
trait EmbeddingProvider { ... }
trait TemplateEngine { ... }

// Compose into systems
struct KnowledgeBase {
    storage: Box<dyn Storage>,
    embeddings: Option<Box<dyn EmbeddingProvider>>,
    templates: TemplateEngine,
}

Benefits:

  • Swap storage without affecting embeddings
  • Disable embeddings without breaking storage
  • Test components in isolation
  • Add new providers by implementing trait

7. Fail Fast, Fail Clearly

Principle: Detect errors early, provide clear messages.

Why: Developer experience - fast feedback, actionable errors.

Implementation:

// ❌ Bad: Silent failure
fn load_config(path: &Path) -> Option<Config> {
    std::fs::read_to_string(path)
        .ok()
        .and_then(|s| serde_json::from_str(&s).ok())
}

// ✅ Good: Explicit errors
fn load_config(path: &Path) -> Result<Config, ConfigError> {
    let content = std::fs::read_to_string(path)
        .map_err(|e| ConfigError::ReadFailed(path.to_path_buf(), e))?;

    serde_json::from_str(&content)
        .map_err(|e| ConfigError::ParseFailed(path.to_path_buf(), e))
}

Error messages:

❌ "Failed to load config"
✅ "Failed to read config file '/path/to/config.ncl': Permission denied"

8. Convention Over Configuration (With Escape Hatches)

Principle: Sane defaults, but everything customizable.

Why: Easy to start, flexible as you grow.

Examples:

# 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" },
}

Conventions:

  • .kogral/ directory in project root
  • Filesystem storage by default
  • YAML frontmatter + markdown body
  • Standard template names (note.md.tera, decision.md.tera)

Escape hatches:

  • --kogral-dir to use different location
  • Configure alternative storage backends
  • Custom frontmatter schemas
  • Override any template

9. Documentation as Code

Principle: Documentation lives with code, versioned together.

Why: Outdated docs are worse than no docs.

Implementation:

  • ADRs in .kogral/decisions/ (alongside code)
  • Guidelines in .kogral/guidelines/ (versioned with code)
  • Patterns in .kogral/patterns/ (evolve with implementations)

Benefits:

# 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

10. Optimize for Discoverability

Principle: Knowledge is useless if you can't find it.

Why: The point is to use knowledge, not just store it.

Features:

  • Text search: Find exact keywords
  • Semantic search: Find related concepts
  • Graph traversal: Follow relationships
  • Tag filtering: Narrow by category
  • MCP integration: AI-assisted discovery

Example:

User doesn't remember exact term, but knows the concept:

"Find anything about handling errors gracefully"

KOGRAL finds (semantically):

  • "Error Handling with thiserror" (pattern)
  • "Result Type Best Practices" (guideline)
  • "Panic Recovery" (note)
  • "Graceful Degradation" (pattern)

No exact keyword match needed, concept match sufficient.

11. Build for Humans, Enable AI

Principle: Humans are the primary users, AI is the assistant.

Why: AI should enhance human workflows, not replace them.

Implementation:

  • Human-readable formats: Markdown, YAML
  • Human-editable: Any text editor works
  • Human-discoverable: ls .kogral/notes/ shows files
  • AI-enhanced: MCP for AI-assisted queries

Example:

# 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

12. Embrace the Graph

Principle: Knowledge is interconnected, embrace the relationships.

Why: Context comes from connections, not isolation.

Implementation:

  • Typed relationships (not just "related")
  • Bidirectional traversal
  • Relationship strength (0.0-1.0)
  • Multi-hop queries

Example:

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

Anti-Patterns to Avoid

1. Hardcoding Behavior

// Don't
const EMBEDDING_MODEL: &str = "BAAI/bge-small-en-v1.5";

// Do
let model = config.embeddings.model.as_str();

2. Runtime Schema Validation

// Don't validate at runtime
let provider = config.provider; // Might be invalid

// Do validate at export time (Nickel contracts)
provider | [| 'openai, 'claude, 'fastembed |]

3. Opaque Errors

// Don't
Err("Failed".into())

// Do
Err(KbError::NodeNotFound(id.to_string()))

4. Coupling Components

// Don't
impl KnowledgeBase {
    fn search(&self) -> Vec<Node> {
        let embeddings = FastEmbedProvider::new(); // Hardcoded!
        // ...
    }
}

// Do
impl KnowledgeBase {
    fn search(&self) -> Vec<Node> {
        if let Some(provider) = &self.embeddings {
            // Use configured provider
        }
    }
}

5. Proprietary Formats

// Don't
Binary blob: [0x4B, 0x42, 0x01, ...]

// Do
Markdown + YAML:
---
id: note-1
---
# Content

Influences

KOGRAL draws inspiration from:

  • Logseq: Outliner, graph view, wikilinks
  • Obsidian: Markdown-first, local storage, plugins
  • Zettelkasten: Atomic notes, links, emergence
  • ADR: Decision records, context preservation
  • Git: Version control, branching, merging
  • Nickel: Type-safe configuration, contracts
  • MCP: AI integration protocol
  • SurrealDB: Graph database, relationships

Implementation Principles

Rust

  • Zero unsafe code (#![forbid(unsafe_code)])
  • No unwrap() in production code
  • Always use Result<T> for fallibility
  • Comprehensive error types with thiserror
  • Full test coverage (100+ tests)

Nickel

  • Contracts for validation
  • Defaults in schemas
  • Documentation in contracts
  • Composition via imports

NuShell

  • Structured data pipelines
  • Error handling in scripts
  • Colored output for UX
  • Dry-run modes

Evolution Strategy

KOGRAL follows these guidelines for evolution:

  1. Backward Compatibility: Don't break existing configs
  2. Deprecation Period: Warn before removal (1 major version)
  3. Migration Tools: Provide automated migrations
  4. Semantic Versioning: MAJOR.MINOR.PATCH strictly

Conclusion

These principles guide every decision:

Config-driven: Behavior in schemas, not code Type-safe: Validate before runtime Local-first: Works offline, cloud optional Git-friendly: Knowledge versions with code AI-native: Optimized for AI, readable by humans Composable: Small pieces, loosely coupled Fast feedback: Fail early, clear errors Discoverable: Easy to find what you need

The goal: Knowledge management that developers actually use.

Next Steps