Skip to content

quicue-kg: CUE-Native Knowledge Graphs

Source files: core/ | ext/ | aggregate/ | vocab/ | kglib/ | cmd/kg/ | spec/ | federation guide

What It Is

quicue-kg is a knowledge graph framework where entries are CUE files in a .kg/ directory inside your git repo. The CUE type system validates entries at build time — missing fields, malformed IDs, empty evidence arrays, and contradictions between federated projects all surface as compile errors, not runtime surprises.

No database. No SPARQL endpoint. No wiki. Just .cue files, cue vet, and version control.

The Four Core Types

Every .kg/ tracks four categories of knowledge:

Decision (#Decision)

Architecture Decision Records with mandatory rationale and consequences.

d001: core.#Decision & {
    id:     "ADR-001"
    title:  "Use CUE for configuration"
    status: "accepted"               // proposed | accepted | deprecated | superseded
    date:   "2026-01-15"
    context:      "Need typed, composable configuration for infrastructure"
    decision:     "Use CUE language with quicue.ca patterns"
    rationale:    "Type safety catches config errors before deployment"
    consequences: ["All config must be valid CUE", "Team needs CUE training"]
}

Status lifecycle: proposed -> accepted -> deprecated or superseded. Supersession chains are tracked via supersedes? and exported as PROV-O revision history.

Insight (#Insight)

Validated discoveries with mandatory evidence, confidence level, and discovery method.

i001: core.#Insight & {
    id:          "INSIGHT-001"
    statement:   "CUE graph validation time is dominated by topology, not node count"
    evidence:    ["NHCF scenario: 18 nodes, 27 edges validates in 3.8s; wide fan-in is the bottleneck, not depth"]
    method:      "experiment"         // cross_reference | gap_analysis | statistics | experiment | observation
    confidence:  "high"               // high | medium | low
    discovered:  "2026-01-20"
    implication: "Graph complexity depends on edge density, not a fixed node ceiling"
}

Insights require at least one piece of evidence — unfounded claims fail validation. The method field enables tracking how knowledge was discovered.

Rejected (#Rejected)

Failed approaches with a required alternative. Every dead end must point somewhere constructive.

r001: core.#Rejected & {
    id:          "REJ-001"
    approach:    "Use SPARQL endpoint for federation"
    reason:      "Requires running infrastructure, contradicts zero-dependency goal"
    date:        "2026-01-10"
    alternative: "Use CUE unification over git-cloned .kg/ directories"
}

The alternative field is not optional. A rejection without a redirect is a conversation that loops — "we tried that and it didn't work" with no forward path. This constraint turns dead ends into signposts.

Pattern (#Pattern)

Reusable problem/solution pairs with cross-project usage tracking.

struct_as_set: core.#Pattern & {
    name:     "struct-as-set"
    category: "data-modeling"
    problem:  "Need O(1) set membership with clean merge semantics"
    solution: "Use {[string]: true} — CUE unifies two sets into their union"
    context:  "Any field representing membership, tags, or relationships"
    used_in:  {"quicue.ca": true, "quicue-kg": true, "cjlq": true}
}

The used_in field uses the struct-as-set idiom: when two projects federate and both use the same pattern, CUE unification automatically produces the union of their used_in sets. No merge logic needed.

How It Works

Directory Layout

your-project/
  .kg/
    cue.mod/
      module.cue          # CUE module declaration
      pkg/quicue.ca/kg/   # Schema symlink or registry reference
    project.cue           # Project identity (ext.#Context)
    index.cue             # Aggregation anchor (_index)
    decision-001.cue      # Individual entry files...
    insight-001.cue
    rejected-001.cue
    pattern-001.cue

All .cue files live at the .kg/ root. CUE packages are directory-scoped — files in subdirectories would be separate packages and couldn't cross-reference entries.

The Index

Each entry file self-registers into _index via CUE unification:

// In decision-001.cue
d001: core.#Decision & { ... }
_index: decisions: "ADR-001": d001

// In insight-001.cue
i001: core.#Insight & { ... }
_index: insights: "INSIGHT-001": i001

CUE merges all _index contributions across package files into a single value. The index.cue file provides the schema constraint and empty defaults:

_index: aggregate.#KGIndex & {
    project:   _proj.name
    decisions: {}
    insights:  {}
    rejected:  {}
    patterns:  {}
}

From this, #KGIndex computes summary views via comprehensions:

summary: {
    total_decisions: len(decisions)
    total_insights:  len(insights)
    total_rejected:  len(rejected)
    total_patterns:  len(patterns)
    total:           total_decisions + total_insights + total_rejected + total_patterns
}
by_status: {for s in ["proposed", "accepted", "deprecated", "superseded"] {
    (s): {for k, v in decisions if v.status == s {(k): v.title}}
}}

These views are never hand-maintained. They stay in sync by construction.

Validation

kg vet          # Runs cue vet against all .kg/ directories
kg lint         # Heuristic checks: TODOs, stale proposals, confidence upgrades
kg settle       # Referential integrity: all related links resolve

What validation catches: - id: "DECISION-1" — fails regex ^ADR-\d{3}$ - evidence: [] — fails minimum length 1 - alternative: "" — fails non-empty constraint - name: "" — fails non-empty constraint - Conflicting entries during federation — fails CUE unification

The CLI

A single kg binary (cmd/kg/) provides 13 commands:

Command Source Purpose
kg init init.go Scaffold .kg/ with module, project, and index files
kg add <kind> add.go Create a new entry (decision, insight, pattern, rejected)
kg vet vet.go Validate all .kg/ directories via cue vet
kg lint lint.go Quality heuristics (TODOs, stale proposals, confidence)
kg settle settle.go Referential integrity checks
kg index [--full] index.go Export summary or full index as JSON
kg search <query> search.go Full-text search across all entries
kg graph [--dot] graph.go Export relationship graph (D3 JSON or Graphviz DOT)
kg fed <roots...> fed.go Discover and federate .kg/ directories under given paths
kg export-static <dir> export.go Export entries.json, graph.json, summary.json for static hosting
kg serve [--port] serve.go Start web server with REST API and D3 explorer
kg tui tui.go Launch terminal UI (Bubble Tea, vim keys)
kg lsp lsp.go Start LSP server for editor integration (hover, completion)

Typical Workflow

# Start a new knowledge graph
kg init
# Edit .kg/project.cue to set your project name

# Record a decision
kg add decision --title "Use PostgreSQL" --context "Need ACID transactions" \
    --decision "PostgreSQL 16" --rationale "Mature, well-supported"

# Record what you tried and abandoned
kg add rejected --approach "SQLite" --reason "Concurrent writes fail" \
    --alternative "PostgreSQL with connection pooling"

# Validate
kg vet

# Query
kg search "PostgreSQL"
kg index
kg graph --dot | dot -Tsvg > graph.svg

Federation

See also: federation guide for the full protocol, comparison tables, and architecture diagrams.

Federation merges knowledge across project boundaries. No shared infrastructure — just git clone + CUE unification.

# Merge local projects
kg --dir ~/api/.kg --dir ~/web/.kg --dir ~/infra/.kg search "caching"

# Include remote repos
kg --dir .kg --dir https://github.com/org/other-project.git index

# Discover all .kg/ directories under a root
kg fed ~/projects/ --json

Merge Semantics

Follows CUE unification rules:

Situation Result
Different IDs Combined into one namespace
Same ID, compatible values Unified (set fields merge as union)
Same ID, conflicting values Type error (explicit contradiction)

Conflicts are features: they surface contradictory claims across teams. Two projects asserting different things about the same decision is a type error that requires human resolution — no silent last-write-wins.

Pattern Federation Example

Project A:

struct_as_set: core.#Pattern & {
    name: "struct-as-set"
    used_in: {"project-a": true}
    // ...
}

Project B:

struct_as_set: core.#Pattern & {
    name: "struct-as-set"
    used_in: {"project-b": true}
    // ...
}

Federated result:

struct_as_set: core.#Pattern & {
    name: "struct-as-set"
    used_in: {"project-a": true, "project-b": true}  // union via unification
    // ...
}

Semantic Web Projections

The same entries automatically project to W3C vocabularies via CUE comprehensions:

Projection Vocabulary What It Maps
#Provenance PROV-O Decisions as prov:Activity, insights as prov:Entity, supersession as prov:wasRevisionOf
#Annotations Web Annotation (OA) Insights as oa:Annotation (commenting), rejected as oa:Annotation (questioning)
#DatasetEntry DCAT Each project's .kg/ as a dcat:Dataset with distributions
#FederatedCatalog DCAT Aggregates projects into dcat:Catalog for discovery

These projections are computed, not maintained. Adding a decision automatically adds a prov:Activity to the provenance graph.

Export:

cue export .kg/ -e _provenance.graph --out json > provenance.jsonld
cue export .kg/ -e _annotations.graph --out json > annotations.jsonld

Extension Types

Beyond the four core types, optional extensions provide:

Type Package Purpose
#Context ext Project identity — name, description, status, module, repo, license
#Workspace ext Multi-repo topology — components, symlinks, deploy targets
#Derivation ext Data pipeline provenance — canon purity, source tracking
#KGLint aggregate Structural quality checks via comprehensions

Core types never import from extensions. Extensions may import from core. This keeps the foundation dependency-free.

Open Schemas

All core types use ... (CUE open struct), allowing domain-specific fields:

d001: core.#Decision & {
    id:     "ADR-001"
    title:  "Use CUE for configuration"
    // ... required fields ...

    // Domain-specific additions — validated as part of the struct
    cost_estimate: "$5,000"
    team:          "platform"
    sprint:        12
}

The required fields are still enforced. The ... just means you can add your own. People are their own lexicographers — your project's knowledge may have structure that no framework author anticipated.

Architecture

┌──────────────────────────────────────────────────┐
│                    kg binary                      │
├──────────┬──────────┬──────────┬─────────────────┤
│ CLI      │ TUI      │ Web      │ LSP             │
│ (Cobra)  │ (Bubble  │ (Echo +  │ (glsp,          │
│          │  Tea)    │  D3.js)  │  JSON-RPC)      │
├──────────┴──────────┴──────────┴─────────────────┤
│                   kglib                           │
│  loader.go  index.go  merge.go  mutate.go        │
│  graph.go   types.go  remote.go                  │
├──────────────────────────────────────────────────┤
│              CUE schemas                          │
│  core/  ext/  aggregate/  vocab/                  │
├──────────────────────────────────────────────────┤
│           .kg/ directories                        │
│  (git-versioned, per-project)                     │
└──────────────────────────────────────────────────┘

All four UIs consume the same kglib.Index. The loader shells out to cue export . -e _index to bridge from CUE to Go. Everything above that is pure Go.

Quick Start

# Build from source
cd quicue-kg
go build -o kg ./cmd/kg

# Initialize in any project
cd ~/my-project
kg init

# Edit project identity
$EDITOR .kg/project.cue

# Start recording knowledge
kg add decision --title "Use Redis for caching" \
    --context "API response times >500ms" \
    --decision "Redis with 5-minute TTL" \
    --rationale "Sub-millisecond reads, mature ecosystem"

kg add insight --statement "95th percentile drops to 50ms with caching" \
    --evidence "Load test with 1000 concurrent users" \
    --implication "Can handle projected 10x traffic growth"

kg add rejected --approach "In-memory LRU cache" \
    --reason "Lost on process restart, no sharing across instances" \
    --alternative "Redis — persistent, shared, supports TTL"

kg add pattern --name "cache-aside" --category "performance" \
    --problem "Repeated expensive computations" \
    --solution "Check cache before compute, populate on miss" \
    --context "Any read-heavy workload with tolerance for staleness" \
    --project "my-project"

# Validate
kg vet

# Explore
kg index
kg search "cache"
kg tui
kg serve --port 8080

Design Principles

  1. Knowledge conflicts are type errors. Two teams asserting contradictory things about the same decision surfaces at cue vet time, not in a meeting six months later.

  2. Every rejection must redirect. The alternative field is required on #Rejected. Dead ends become signposts.

  3. Schema evolution is a quality ratchet. Adding a required field to a core type makes every existing entry without it a validation error. Knowledge quality only goes up.

  4. Zero infrastructure. No database, no server, no shared state. Just .cue files in git, cue vet in CI, and kg for querying.

  5. Projections are computed, not maintained. PROV-O, DCAT, and Web Annotation views are CUE comprehensions over the same source data. They cannot drift.

  6. People are their own lexicographers. Core types are open (...). Your domain-specific fields coexist with the validated schema. The framework doesn't dictate what knowledge looks like — it validates the parts it knows about.

See Also

  • Specification — normative spec (conformance classes, field constraints, federation protocol)
  • Federation Guide — merge semantics, conflict resolution, comparison to other tools
  • JSON-LD Vocabularykg: namespace, PROV-O/DCAT/OA mappings
  • Examples: minimal — smallest valid .kg/ (one decision)
  • Examples: full — complete .kg/ with all entry types
  • Tests: valid — reference instances for all types
  • Tests: invalid — constraint violation cases
  • Web UI — embedded D3.js force-directed graph explorer
  • TUI — Bubble Tea terminal interface (vim keys)
  • LSP — editor integration (hover, completion)
  • REST API — Echo server powering kg serve