Language · Reference

Schema.

KGL schemas define node types, required fields, and allowed relationships. They are warning-only — a file without required fields is still valid KGL, but a linter will flag the missing fields. Write .kgl files freely first, add schema validation as your graph matures.

Schema files

Schema is defined in _schema.kgl files. Two kinds:

Root schema

path
/_schema.kgl

Defines global rules for the entire graph. All node type and relationship type definitions here apply everywhere unless overridden by a local schema.

Local schema

paths
/people/_schema.kgl
/projects/_schema.kgl

Extends or tightens the root schema for a specific folder. Inherits all root definitions. Local definitions for the same type override the root definition for files in that folder only.

Schema lookup for any .kgl file: local _schema.kgl first, then walk up to root. First definition of a type wins.

Node type definitions

_schema.kgl
@NodeType Person
    name!:   text
    joined?: date
    role?:   text

Field markers

MarkerMeaning
field!: typeRequired. Linter warns if absent on a matching node.
field?: typeOptional. Documents the field exists without requiring it.

Fields without a marker are undocumented — they can still appear in data files, but the schema says nothing about them.

Inheritance

_schema.kgl
@NodeType Contractor
    extends:       Person
    contract-end!: date

extends pulls in all fields from the named type. The extending type can add new required or optional fields on top.

Relationship type definitions

_schema.kgl
@RelType employs
    from:   Person → Project|Service
    role!:  text
    since!: date
    note?:  text

from — endpoint constraints

syntax
from: SourceType → TargetType

Constrains which node types may appear on each side of the relationship. A linter warns if a relationship is used between incompatible types.

Both sides support type unions:

example
from: Project|Service → Project|Service

This means: either a Project or Service may link to either a Project or Service using this relationship.

Relationship properties

Properties defined under a @RelType work the same as node fields — ! for required, ? for optional. A linter warns if a required relationship property is missing when that relationship is used in a data file.

Local override example

people/_schema.kgl
# people/_schema.kgl
# Extends root schema for the /people folder.

@NodeType Person
    extends: Person
    team!:   text
    github?: text

This adds team as required and github as optional for all @Person nodes in the /people folder. The root schema's name!, joined?, and role? fields are still inherited.

Complete root schema example

_schema.kgl
# _schema.kgl

## Node types

@NodeType Person
    joined!: date
    role?:   text

@NodeType Contractor
    extends:       Person
    contract-end!: date

@NodeType Project
    status!:   text
    deadline?: date

@NodeType Service
    owner!: text
    url?:   text

@NodeType Decision
    date!:    date
    status!:  text
    context?: text


## Relationship types

@RelType reports-to
    from:   Person → Person
    since?: date

@RelType employs
    from:   Person → Project|Service
    role?:  text
    since!: date

@RelType staffed-by
    from:   Project → Person
    role!:  text
    since!: date

@RelType depends-on
    from:         Project|Service → Project|Service
    since?:       date
    sunset-date?: date
    note?:        text

@RelType mentors
    from:     Person → Person
    started!: date
    focus?:   text

@RelType owned-by
    from:   Project|Service → Person
    since?: date

Linter warnings

A linter checking this schema would warn on:

Missing required field

people/charlie.kgl — linter output
@Person Charlie Watts
# ⚠ warning: missing required field 'joined' (from @NodeType Person in _schema.kgl)
# ⚠ warning: missing required field 'team' (from @NodeType Person in people/_schema.kgl)

Type mismatch on relationship

linter output
[depends-on] → people/diana.kgl#Diana Park
# ⚠ warning: relationship 'depends-on' expects Project|Service → Project|Service
#             but source is @Person — type mismatch

Missing required relationship property

linter output
[staffed-by] → people/alice.kgl#Alice Nguyen
# ⚠ warning: missing required property 'role' on relationship 'staffed-by'
# ⚠ warning: missing required property 'since' on relationship 'staffed-by'

Design notes