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¶
-
Knowledge conflicts are type errors. Two teams asserting contradictory things about the same decision surfaces at
cue vettime, not in a meeting six months later. -
Every rejection must redirect. The
alternativefield is required on#Rejected. Dead ends become signposts. -
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.
-
Zero infrastructure. No database, no server, no shared state. Just
.cuefiles in git,cue vetin CI, andkgfor querying. -
Projections are computed, not maintained. PROV-O, DCAT, and Web Annotation views are CUE comprehensions over the same source data. They cannot drift.
-
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 Vocabulary —
kg: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