quicue-kg is a CUE-native knowledge graph for tracking architectural [=decisions=], validated [=insights=], reusable [=patterns=], and [=rejected=] approaches. It uses CUE's type system and lattice-based unification to provide compile-time validation, conflict detection, and zero-infrastructure federation across project boundaries.

This specification defines the data model, directory layout, type constraints, [=KGIndex|aggregation index=], and federation protocol for conforming .kg/ knowledge graphs.

This is an unofficial specification. It documents the quicue-kg framework as implemented in the quicue.ca/kg@v0 CUE module. Feedback is welcome via the project's issue tracker.

Introduction

Software projects accumulate knowledge broader than source code: why a technology was chosen, what approaches were tried and abandoned, which [=patterns=] recur across repositories. This knowledge typically scatters across wikis, chat logs, and individual memory. When it is lost, teams re-explore failed paths and make decisions without context.

quicue-kg stores this knowledge as typed CUE [=entries=] in a .kg/ directory alongside source code. The [[CUE]] language provides:

Design Goals

  1. Knowledge conflicts are type errors, detected at build time.
  2. Federation across projects requires no shared infrastructure.
  3. Every rejected approach MUST record a constructive alternative.
  4. Schema evolution is a quality ratchet: adding a required field makes every existing entry without it a validation error.

Conformance

As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else is normative.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in [[RFC2119]].

Conformance Classes

Conforming Knowledge Graph

A conforming knowledge graph is a knowledge graph in a .kg/ directory that:

  1. MUST contain a valid CUE module declaration.
  2. MUST declare package kg in all .cue files at the directory root.
  3. MUST contain at least one entry conforming to a core type.
  4. MUST pass cue vet without errors when evaluated against the quicue.ca/kg schemas.
  5. MUST NOT contain .cue files in subdirectories (CUE packages are directory-scoped; see ).

Conforming Processor

A conforming processor is a tool that:

  1. MUST validate input against the quicue-kg CUE schemas before processing.
  2. MUST reject entries with missing required fields as validation errors.
  3. MUST preserve the @type field in all serialization formats.

Terminology

Knowledge Graph
A typed, validated collection of interconnected knowledge entries stored in a .kg/ directory.
Entry
A single record in the knowledge graph, conforming to one of the four core types.
Struct-as-Set
The CUE idiom {[string]: true} used to represent set membership. Provides O(1) lookup, deduplication, and clean unification across projects. Used for related, used_in, and similar fields.
Unification
The CUE operation that merges two values. If compatible, the result is the most specific value satisfying both constraints. If conflicting, unification produces a type error. This is the foundation for federation.
Federation
The process of discovering, merging, and querying multiple knowledge graphs across project boundaries using CUE unification.
kg: prefix
The namespace prefix kg: expands to https://quicue.ca/kg#. It is used in @type fields and as a CURIE prefix in JSON-LD exports (see extensions).

CUE Concepts Used in This Specification

This section is non-normative. It summarizes CUE language features that are essential to understanding this specification. Readers familiar with CUE may skip this section. For a complete reference, see [[CUE]].

Types are values.
CUE has no separate type system. A definition like #Decision is itself a value — a partially-specified struct. Writing core.#Decision & {id: "ADR-001", ...} is not "instantiating a type" but unifying two values. The result must satisfy all constraints from both sides. This is why schema validation and data authoring are the same operation.
Definitions (#).
Identifiers prefixed with # are definitions. By default, definitions are closed: they reject undeclared fields. However, adding ... makes a definition open — it accepts additional fields beyond those declared in the schema. The quicue-kg core types are open: required fields are validated, but users may add domain-specific fields freely. Definitions are not emitted during export (cue export). Regular identifiers are open and are emitted.
Hidden fields (_).
Identifiers prefixed with _ are hidden — not emitted during export. The index uses _index as a hidden field so that cue export .kg/ doesn't dump the entire aggregation. To export it, use cue export .kg/ -e _index.summary.
Disjunctions as enums.
"proposed" | "accepted" | "deprecated" | "superseded" is a disjunction — the value must be exactly one of the listed options. This is how CUE expresses enumerated types. Any value outside the set is a validation error.
Constraint composition.
CUE uses & (unification) to compose constraints: string & !="" means "a string that is not empty." [...string] & [_, ...] means "a list of strings with at least one element" (_ matches any value). =~"^ADR-\\d{3}$" constrains a string by regex.
Monotonic refinement.
CUE's value lattice only narrows. A field constrained to "high" | "medium" | "low" can later be refined to "high" but can never be widened to accept "maybe". This is the mechanism behind progressive refinement: knowledge accumulates, it cannot silently disappear.

Directory Layout

A conforming knowledge graph resides in a .kg/ directory at the root of a project. All CUE files MUST be at the directory root level — CUE packages are directory-scoped, meaning files in subdirectories constitute separate package instances even if they declare the same package name.

project/
  .kg/
    cue.mod/
      module.cue          # Module declaration
      pkg/
        quicue.ca/
          kg/             # Schema dependency (symlink or registry)
    decisions.cue         # Entry files (package kg)
    insights.cue
    rejected.cue
    patterns.cue
    index.cue             # aggregate.#KGIndex (recommended)
    

Module Declaration

The .kg/cue.mod/module.cue file MUST declare a CUE module. The module name SHOULD follow the pattern {project-domain}/.kg but MAY use any valid CUE module identifier.

// .kg/cue.mod/module.cue
module: "local.project/kg"
language: version: "v0.15.4"
source: kind: "self"

Schema Dependency

The quicue-kg schemas MUST be available to the CUE evaluator. This MAY be achieved through any of the following mechanisms:

  1. OCI Registry (GHCR) — set CUE_REGISTRY='quicue.ca=ghcr.io/quicue/cue-modules,registry.cue.works' and run cue mod tidy.
  2. Local symlink — create a symlink at .kg/cue.mod/pkg/quicue.ca/kg/ pointing to a local checkout of the quicue-kg repository.

Package Declaration

Every .cue file at the root of the .kg/ directory MUST declare package kg. Files MUST NOT be placed in subdirectories.

package kg

import "quicue.ca/kg/core@v0"

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

File Organization

Entries SHOULD be organized by type into separate files for readability.

FileContentsStatus
decisions.cueAll #Decision entriesRECOMMENDED
insights.cueAll #Insight entriesRECOMMENDED
rejected.cueAll #Rejected entriesRECOMMENDED
patterns.cueAll #Pattern entriesRECOMMENDED
index.cueaggregate.#KGIndexRECOMMENDED

A conforming knowledge graph MAY combine all entries into a single file. File boundaries are not semantically significant — CUE evaluates all files in a package as a single logical unit.

Core Types

Core types define the four fundamental [=entry=] categories that every conforming knowledge graph MUST support. They are defined in the quicue.ca/kg/core@v0 package and have no external dependencies.

All core types share the following conventions:

Decision (#Decision)

A Decision records an architecture decision with mandatory rationale and consequences. Decisions follow a status lifecycle from proposed through accepted to eventual deprecated or superseded.

FieldTypeConstraintRequiredDescription
@type string Fixed: "kg:Decision" Yes Type discriminator.
id string =~"^ADR-\d{3}$" Yes Unique identifier (e.g., ADR-001).
title string Non-empty Yes Human-readable summary of the decision.
status string "proposed" | "accepted" | "deprecated" | "superseded" Yes Current lifecycle status.
date string =~"^\d{4}-\d{2}-\d{2}$" Yes Date the decision was made (ISO 8601).
context string Non-empty Yes What prompted this decision.
decision string Non-empty Yes What was decided.
rationale string Non-empty Yes Why this decision was made.
consequences [...string] At least one element Yes What follows from this decision.
supersedes string =~"^ADR-\d{3}$" No ID of the decision this supersedes.
appliesTo [...{...}] No Structured scope annotations (open).
related {[string]: true} Struct-as-set No Cross-references to other entries.
d001: core.#Decision & {
    id:     "ADR-001"
    title:  "Use CUE for configuration"
    status: "accepted"
    date:   "2026-01-15"

    context:      "Need a type-safe configuration language."
    decision:     "Use CUE for all configuration and schema definitions."
    rationale:    "CUE provides compile-time validation and lattice-based types."
    consequences: ["All config files are .cue", "Validated with cue vet"]
}

Insight (#Insight)

An Insight records a validated discovery with mandatory evidence and an explicit confidence level. The method field tracks how the insight was discovered, supporting reproducibility.

FieldTypeConstraintRequiredDescription
@typestring Fixed: "kg:Insight" YesType discriminator.
idstring =~"^INSIGHT-\d{3}$" YesUnique identifier.
statementstring Non-empty YesWhat was discovered.
evidence[...string] At least one element YesSupporting evidence.
methodstring "cross_reference" | "gap_analysis" | "statistics" | "experiment" | "observation" YesDiscovery method.
confidencestring "high" | "medium" | "low" YesHow certain we are.
discoveredstring ISO 8601 date YesWhen discovered.
implicationstring Non-empty YesWhat this means for the project.
action_items[...string] NoSuggested follow-up actions.
related{[string]: true} Struct-as-set NoCross-references.

Rejected (#Rejected)

A Rejected [=entry=] records an approach that was tried and abandoned. The alternative field is REQUIRED — every rejection must suggest what to do instead. This prevents the "we tried that" conversation loop by turning dead ends into redirects.

FieldTypeConstraintRequiredDescription
@typestring Fixed: "kg:Rejected" YesType discriminator.
idstring =~"^REJ-\d{3}$" YesUnique identifier.
approachstring Non-empty YesWhat was tried.
reasonstring Non-empty YesWhy it failed.
datestring ISO 8601 date YesWhen rejected.
alternativestring Non-empty YesWhat to do instead.
related{[string]: true} Struct-as-set NoCross-references.

Pattern (#Pattern)

A Pattern captures a reusable problem/solution pair with cross-project usage tracking. The used_in field uses the struct-as-set idiom, which means federating two projects that both use the same pattern automatically produces the union of their used_in sets.

FieldTypeConstraintRequiredDescription
@typestring Fixed: "kg:Pattern" YesType discriminator.
namestring Non-empty YesPattern name (also serves as ID).
categorystring Non-empty YesClassification category.
problemstring Non-empty YesWhat problem this solves.
solutionstring Non-empty YesHow to solve it.
contextstring Non-empty YesWhen to apply this pattern.
used_in{[string]: true} Struct-as-set YesProjects using this pattern.
examplestring NoUsage example.
related{[string]: true} Struct-as-set NoCross-references.

Index

The KGIndex is the computed aggregation of all [=entries=] in a knowledge graph. It collects entries by type, computes summary counts, and derives cross-cutting views (decisions by status, insights by confidence). All computed fields use CUE comprehensions — they are never hand-maintained.

The index is defined in the quicue.ca/kg/aggregate@v0 package.

FieldTypeComputedDescription
projectstring NoProject name (user-provided).
decisions{[string]: #Decision} NoAll decision entries, keyed by ID.
insights{[string]: #Insight} NoAll insight entries, keyed by ID.
rejected{[string]: #Rejected} NoAll rejected entries, keyed by ID.
patterns{[string]: #Pattern} NoAll pattern entries, keyed by name.
summarystruct YesCounts: total_decisions, total_insights, total_rejected, total_patterns, total.
by_statusstruct YesDecisions grouped by status value.
by_confidencestruct YesInsights grouped by confidence level.
// .kg/index.cue
package kg

import "quicue.ca/kg/aggregate@v0"

_index: aggregate.#KGIndex & {
    project:   "my-project"
    decisions: { "ADR-001": d001 }
    insights:  { "INSIGHT-001": i001 }
    rejected:  { "REJ-001": r001 }
    patterns:  { "struct-as-set": p_struct_as_set }
}

// Export: cue export .kg/ -e _index.summary --out json

Federation

Federation merges multiple knowledge graphs across project boundaries. Unlike systems that require shared databases or SPARQL endpoints, quicue-kg federation uses CUE unification as its merge primitive. The transport layer is git clone. The conflict detector is the CUE type checker.

Discovery

A conforming processor discovers knowledge graphs by walking the filesystem looking for .kg/cue.mod/module.cue:

  1. Walk from a set of root directories.
  2. For each directory, check for .kg/cue.mod/module.cue.
  3. If found, validate the .kg/ directory with cue vet.
  4. If validation passes, include in the federation set.
# Discover all .kg/ directories under ~/projects/
kg fed ~/projects/

# Load specific directories
kg --dir ~/api/.kg --dir ~/web/.kg --dir ~/infra/.kg index

Merging

Federation merging follows CUE unification semantics:

Conflicts are features, not bugs. A type error during federation means two projects assert contradictory facts. The error requires explicit human resolution — no silent last-write-wins.

Remote Sources

A conforming processor MAY support remote sources specified as git URLs. Remote sources are shallow-cloned, the .kg/ directory is located, and entries are loaded and merged as with local sources.

# Local + remote
kg --dir .kg --dir https://github.com/org/other-project.git search "caching"

# The --dir flag is repeatable. All commands support it.
kg --dir ~/a/.kg --dir ~/b/.kg --dir ~/c/.kg serve

Write operations (add) target the first --dir value only.

Security Considerations

Sensitive Architectural Information

Knowledge graph entries MAY contain sensitive architectural decisions, such as security architecture choices, authentication strategy rationale, or infrastructure topology information. Implementors SHOULD consider:

Credential Prohibition

Knowledge graph entries MUST NOT contain credentials, secrets, API keys, tokens, or other authentication material. The .kg/ directory is for structural and architectural knowledge, not operational secrets.

Federation Metadata Exposure

Federation exposes project metadata across organizational boundaries:

Organizations SHOULD establish federation policies that define which projects may be federated and with whom.

Extensions

The core specification intentionally covers only the four fundamental entry types. Domain-specific extensions are defined in companion packages:

PackageTypesPurpose
quicue.ca/kg/ext@v0 #Derivation, #Context, #Workspace Data pipeline provenance, project identity, multi-repo topology
quicue.ca/kg/aggregate@v0 #KGLint, #Provenance, #Annotations, #DatasetEntry, #FederatedCatalog Quality checks, W3C projections (PROV-O, Web Annotation, DCAT)

Extension types are OPTIONAL. Core types MUST NOT import from extension packages. Extensions MAY import from core.

CUE Schema Reference

Normative CUE type definitions. Canonical source: quicue.ca/kg/core@v0.

#Decision

#Decision: {
    "@type": "kg:Decision"
    id:      =~"^ADR-\\d{3}$"
    title:   string & !=""
    status:  "proposed" | "accepted" | "deprecated" | "superseded"
    date:    =~"^\\d{4}-\\d{2}-\\d{2}$"

    context:      string & !=""
    decision:     string & !=""
    rationale:    string & !=""
    consequences: [...string] & [_, ...]

    supersedes?: =~"^ADR-\\d{3}$"
    appliesTo?:  [...{...}]
    related?:    {[string]: true}
    ...
}

#Insight

#Insight: {
    "@type":    "kg:Insight"
    id:         =~"^INSIGHT-\\d{3}$"
    statement:  string & !=""
    evidence:   [...string] & [_, ...]
    method:     "cross_reference" | "gap_analysis" | "statistics" | "experiment" | "observation"
    confidence: "high" | "medium" | "low"
    discovered: =~"^\\d{4}-\\d{2}-\\d{2}$"

    implication:   string & !=""
    action_items?: [...string]
    related?:      {[string]: true}
    ...
}

#Rejected

#Rejected: {
    "@type":     "kg:Rejected"
    id:          =~"^REJ-\\d{3}$"
    approach:    string & !=""
    reason:      string & !=""
    date:        =~"^\\d{4}-\\d{2}-\\d{2}$"
    alternative: string & !=""
    related?:    {[string]: true}
    ...
}

#Pattern

#Pattern: {
    "@type":  "kg:Pattern"
    name:     string & !=""
    category: string & !=""
    problem:  string & !=""
    solution: string & !=""
    context:  string & !=""
    example?: string
    used_in:  {[string]: true}
    related?: {[string]: true}
    ...
}

Complete Example

A minimal but complete .kg/ directory using all four core types.

package kg

import "quicue.ca/kg/core@v0"

d001: core.#Decision & {
    id:     "ADR-001"
    title:  "Use CUE for configuration"
    status: "accepted"
    date:   "2026-01-15"

    context:      "Need a type-safe configuration language."
    decision:     "Use CUE for all configuration and schema definitions."
    rationale:    "Compile-time validation and lattice-based type system."
    consequences: ["All config files are .cue, validated with cue vet"]
}
package kg

import "quicue.ca/kg/core@v0"

i001: core.#Insight & {
    id:         "INSIGHT-001"
    statement:  "CUE unification is sufficient for cross-project merging"
    evidence:   ["Federated 5 .kg/ directories without conflicts"]
    method:     "experiment"
    confidence: "high"
    discovered: "2026-02-10"
    implication: "No external query engine needed for federation."
    related: {"ADR-001": true}
}
package kg

import "quicue.ca/kg/core@v0"

r001: core.#Rejected & {
    id:          "REJ-001"
    approach:    "Store knowledge graph in SQLite"
    reason:      "Adds runtime dependency. Loses CUE type safety."
    date:        "2026-01-10"
    alternative: "Use flat CUE files in .kg/ with cue vet for validation."
}
package kg

import "quicue.ca/kg/core@v0"

p_struct_as_set: core.#Pattern & {
    name:     "Struct-as-Set"
    category: "data-modeling"
    problem:  "Arrays allow duplicates and lack O(1) membership testing."
    solution: "Use {[string]: true} for set-valued fields."
    context:  "Any field representing set membership."
    used_in:  {"my-project": true}
}
package kg

import "quicue.ca/kg/aggregate@v0"

_index: aggregate.#KGIndex & {
    project:   "my-project"
    decisions: { "ADR-001": d001 }
    insights:  { "INSIGHT-001": i001 }
    rejected:  { "REJ-001": r001 }
    patterns:  { "struct-as-set": p_struct_as_set }
}
# Validate
cue vet .kg/

# Export summary
cue export .kg/ -e _index.summary --out json
# {"total_decisions":1,"total_insights":1,"total_rejected":1,"total_patterns":1,"total":4}

# Federate with another project
kg --dir .kg --dir ~/other-project/.kg graph

Design Rationale

This section is non-normative.

Why CUE?

Why Constructive Rejection?

A rejection without an alternative is a dead end with no signpost. By requiring alternative, every dead end becomes a redirect.

Why Struct-as-Set?

Why Flat Directory?

CUE packages are directory-scoped. Files in subdirectories are separate packages, even with the same package name. The flat directory requirement ensures all entries share a single package scope, enabling cross-referencing and aggregation.