Full Spec: https://raw.githubusercontent.com/ldclabs/KIP/refs/heads/main/SPECIFICATION.md
KIP is a graph-oriented protocol for an agent's long-term memory brain. The graph contains Concept Nodes (entities) and Proposition Links (facts). LLMs read/write via KQL (query: FIND), KML (manipulate: UPSERT/UPDATE/MERGE/DELETE), and META (ground/introspect/round-trip: SEARCH/DESCRIBE/EXPORT). Data uses a JSON-compatible value model; KIP object literals allow unquoted identifier keys as shorthand for JSON string keys.
| Element | Identity | Required fields | Optional |
|---|---|---|---|
| Concept Node | id OR {type, name} |
type (UpperCamelCase), name |
attributes, metadata |
| Proposition Link | id OR (subject, predicate, object) |
subject/object (concept or link id), predicate (snake_case) |
attributes, metadata |
subject and object may reference another Proposition Link, enabling higher-order facts.
- Primitives:
string,number,boolean,null. - Complex:
Array,Object— allowed inattributes/metadata;FILTERoperates only on primitive comparison values. - Object keys: quoted JSON string keys and unquoted identifier keys are both accepted; unquoted keys are normalized as strings.
- Syntax:
[a-zA-Z_][a-zA-Z0-9_]*. Case-sensitive. ?— query variable (?drug).$— system meta-type ($ConceptType,$self,$system).:— parameter placeholder in command text (:name,:limit).
| Element | Style | Examples |
|---|---|---|
| Concept Types | UpperCamelCase |
Drug, ClinicalTrial |
| Proposition Predicates | snake_case |
treats, has_side_effect |
| Attribute / Metadata Keys | snake_case |
risk_level, created_at |
| Variables | ? + snake_case |
?drug, ?side_effect |
Required for schema-level names and variables; recommended for attribute / metadata keys. Wrong case on a type/predicate (e.g. drug vs Drug) → KIP_2001.
In FIND / FILTER / ORDER BY:
- Concept:
?var.id,?var.type,?var.name - Proposition:
?var.id,?var.subject,?var.predicate,?var.object - Attributes:
?var.attributes.<key> - Metadata:
?var.metadata.<key> - Whole object:
?var.attributes/?var.metadata— full-object projection inFIND(not comparable inFILTER).
KIP is self-describing: every legal type/predicate is itself a node.
{type: "$ConceptType", name: "Drug"}registersDrugas a concept type.{type: "$PropositionType", name: "treats"}registerstreatsas a predicate.
Using an unregistered type/predicate → KIP_2001.
- Shallow merge:
SET ATTRIBUTESandWITH METADATAoverwrite only specified keys; unspecified keys remain. Array/Object values are overwritten at the key (no recursive deep merge) — supply the full array when updating. - Proposition uniqueness: at most one link per
(subject, predicate, object). DuplicateUPSERT→ updates attributes/metadata of the existing link. expires_atis a signal, not auto-filter: expired knowledge stays queryable until a background$systemprocess cleans it. AddFILTER(IS_NULL(?x.metadata.expires_at) || ?x.metadata.expires_at > <now>)to skip expired entries.
Metadata keys starting with _ are engine-maintained and read-only to KML (writing them → KIP_2002). Readable via dot notation like any metadata:
| Field | Semantics |
|---|---|
_version |
Monotonic mutation counter (starts at 1). Target of EXPECT VERSION. |
_updated_at |
Engine-recorded ISO-8601 time of last mutation. |
_score |
Transient normalized SEARCH relevance [0,1]; never persisted. |
_merged_from |
Provenance trail left by MERGE ("<Type>:<name>" entries). |
EXPECT VERSION <n> (optional line in UPSERT CONCEPT/PROPOSITION blocks, right after the identity clause): block executes only if the element's _version equals <n>; EXPECT VERSION 0 = must-not-exist (create-only). On mismatch the whole UPSERT aborts with KIP_3005 → re-read, re-merge, retry. Use it for every read-modify-write of array/object values (e.g., $self attributes, logs).
FIND( <variables_or_aggregations> )
WHERE { <patterns_and_filters> }
ORDER BY <expr> [ASC|DESC], <expr> [ASC|DESC], ...
LIMIT <integer>
CURSOR "<token>"ORDER BY / LIMIT / CURSOR are optional.
- Variables / dot-paths:
FIND(?a, ?b.name, ?b.attributes.risk_level) - Aggregations:
COUNT(?v),COUNT(DISTINCT ?v),SUM(?v),AVG(?v),MIN(?v),MAX(?v). - Implicit
GROUP BY: whenFINDmixes plain expressions with aggregations, all non-aggregated expressions form the grouping key. With only aggregations, the whole result set is one group. - Null handling: aggregations ignore
null(unbound) values —COUNT(?v)over anOPTIONAL-miss group returns0. - Solution dedup: duplicate solutions (identical bindings) collapse (set semantics) before
ORDER BY/LIMIT; distinct solutions projecting equal values are kept.
?var {id: "<id>"} // by id
?var {type: "<Type>", name: "<name>"} // exact
?var {type: "<Type>"} // broad
?var {name: "<name>"} // broadWhen used directly as subject/object inside a proposition clause, omit the variable name: (?p, "treats", {type: "Symptom", name: "Headache"}).
?link (id: "<id>") // by id
?link (?subject, "<predicate>", ?object) // structural
?link (?subject, ?pred, ?object) // predicate VARIABLE — associative recall
(?u, "stated", (?s, "<pred>", ?o)) // higher-order (object is a link)The leading ?link is optional; endpoints are ?var, an unnamed {...} concept clause, or an unnamed nested (...) proposition clause. Do not attach a variable name to an embedded endpoint clause — bind it in a separate clause first, then reference the variable.
Predicate variables: ?pred binds the predicate name (string); project it in FIND, test it in FILTER (string ops, IN), unify it across clauses. No quantifiers/alternatives on a variable (?p{1,3} invalid). Constrain at least one endpoint and add LIMIT — engines MAY reject a fully unconstrained (?s, ?p, ?o) with KIP_4002. The ego-graph ("what surrounds X?") pattern:
FIND(?pred, ?neighbor)
WHERE {
?link ({type: "Person", name: "Alice"}, ?pred, ?neighbor)
FILTER(?pred != "belongs_to_domain")
} LIMIT 50Predicate path modifiers (literal predicates only):
- Hops:
"<pred>"{m,n},"<pred>"{m,},"<pred>"{n}.m == 0includes a zero-hop reflexive match (subject == object, no edge traversed). - Alternatives:
"<p1>" | "<p2>" | ....
| Category | Operators / Functions |
|---|---|
| Comparison | ==, !=, <, >, <=, >= |
| Logical | &&, ||, ! |
| Membership | IN(?expr, [v1, v2, ...]) |
| Null check | IS_NULL(?expr), IS_NOT_NULL(?expr) |
| String | CONTAINS, STARTS_WITH, ENDS_WITH, REGEX |
FILTER(?drug.attributes.risk_level < 3 && CONTAINS(?drug.name, "acid"))
FILTER(IN(?event.attributes.event_class, ["Conversation", "SelfReflection"]))
FILTER(IS_NOT_NULL(?node.metadata.expires_at))
FILTER(?event.attributes.start_time > "2025-01-01T00:00:00Z") // ISO-8601 string compareExternal vars visible inside; internal vars visible outside (null if no match). Dot-notation projection on an unbound var yields null, and IS_NULL(?var) is true.
?drug {type: "Drug"}
OPTIONAL { (?drug, "has_side_effect", ?side_effect) }
// ?side_effect == null when none existsExternal vars visible inside; internal vars are private (not visible outside). Discards the solution if the inner pattern matches.
?drug {type: "Drug"}
NOT { (?drug, "belongs_to_class", {name: "NSAID"}) }External vars are not visible inside UNION (independent scope). Internal vars are visible outside. Both branches run independently; rows are union-ed and deduplicated. Same-named variables in both branches are independent bindings; absent variables become null.
?drug {type: "Drug"}
(?drug, "treats", {name: "Headache"})
UNION {
?drug {type: "Drug"}
(?drug, "treats", {name: "Fever"})
}| Clause | External vars visible inside? | Internal vars visible outside? |
|---|---|---|
FILTER |
Yes | N/A |
OPTIONAL |
Yes | Yes (null on miss) |
NOT |
Yes | No (private) |
UNION |
No (independent) | Yes |
ORDER BY <expr> [ASC|DESC], <expr> [ASC|DESC], ...— one or more comma-separated sort keys, left to right; defaultASC. Each key: a variable, a dot-path, or an aggregation expression that also appears inFIND(e.g.,ORDER BY COUNT(?n) ASC).nullalways sorts last regardless of direction. Memory-ranking idiom:ORDER BY ?e.attributes.salience_score DESC, ?e.attributes.start_time DESC. Bare?varkeys only for primitive bindings (e.g., predicate variables); otherwise sort by a dot-path.LIMIT NorLIMIT :param.CURSOR "<token>"orCURSOR :param— opaque pagination token from a previous response'snext_cursor.
// Optional + filter
FIND(?drug.name, ?side_effect.name)
WHERE {
?drug {type: "Drug"}
OPTIONAL { (?drug, "has_side_effect", ?side_effect) }
FILTER(?drug.attributes.risk_level < 3)
}
// Aggregation + NOT + ORDER BY + LIMIT
FIND(?drug.name, ?drug.attributes.risk_level)
WHERE {
?drug {type: "Drug"}
(?drug, "treats", {name: "Headache"})
NOT { (?drug, "belongs_to_class", {name: "NSAID"}) }
FILTER(?drug.attributes.risk_level < 4)
}
ORDER BY ?drug.attributes.risk_level ASC
LIMIT 20
// Higher-order: confidence that a user stated a fact
FIND(?statement.metadata.confidence)
WHERE {
?fact ({type: "Drug", name: "Aspirin"}, "treats", {type: "Symptom", name: "Headache"})
?statement ({type: "Person", name: "John Doe"}, "stated", ?fact)
}Four statements: UPSERT (identity-addressed create-or-update), UPDATE (pattern-matched bulk mutation), MERGE (atomic entity consolidation), DELETE (targeted removal).
UPSERT {
CONCEPT ?handle {
{type: "<Type>", name: "<name>"} // match-or-create
// OR {id: "<id>"} // match-only (must exist)
EXPECT VERSION <n> // optional CAS guard (see §1.8)
SET ATTRIBUTES { <key>: <value>, ... }
SET PROPOSITIONS {
("<predicate>", ?other_handle)
("<predicate>", ?other_handle) WITH METADATA { <key>: <value>, ... }
("<predicate>", {type: "<T>", name: "<N>"}) // target must exist or KIP_3002
("<predicate>", {id: "<id>"})
("<predicate>", (id: "<link_id>"))
("<predicate>", (?s, "<pred>", ?o)) // higher-order
}
}
WITH METADATA { ... } // local metadata (concept block)
PROPOSITION ?prop_handle { // ?prop_handle is optional
(?subject, "<predicate>", ?object) // endpoints: ?handle, {...}, or (...)
// OR (id: "<id>") // match-only
EXPECT VERSION <n> // optional CAS guard (see §1.8)
SET ATTRIBUTES { ... }
}
WITH METADATA { ... } // local metadata (proposition block)
}
WITH METADATA { ... } // global default for all itemsRules:
- Sequential, top-to-bottom. Handles must be defined before reference. Dependencies form a DAG (no cycles).
- Shallow merge for
SET ATTRIBUTES/WITH METADATA. SET PROPOSITIONSis additive — new links are added or updated; never deletes unspecified ones. Any item may appendWITH METADATA { ... }.- Metadata precedence: inner
WITH METADATAoverrides outer key-by-key (shallow); unspecified keys inherit from outer, and specifiednullstill overrides. Lifecycle keys (expires_at,memory_tier) belong in the target block's ownWITH METADATA, never in the statement-level default — the default shallow-merges onto every element in the statement, silently stamping an episodic TTL onto matched durable nodes (e.g., a Person). - Existing target refs:
{type, name},{id},(id: ...), and nested proposition targets must already exist, or returnKIP_3002. - Provenance: always set
source,author,confidenceinWITH METADATA. EXPECT VERSIONmismatch aborts the entireUPSERTatomically withKIP_3005— re-read, re-merge, retry.
Response: {"blocks": <n>, "upsert_concept_nodes": ["<id>", ...], "upsert_proposition_links": ["<id>", ...]} — blocks counts executed UPSERT statements (a capsule may carry several); the arrays list every top-level CONCEPT / PROPOSITION block's ID in execution order. Links from SET PROPOSITIONS are not itemized (FIND them when IDs are needed); dry_run leaves the arrays empty.
- Prefer deterministic identity
{type: "T", name: "N"}for concepts. - Use deterministic Event names so retries do not duplicate.
- Avoid random names/ids unless retries are guaranteed stable.
When stable memory needs a new type/predicate:
- Define it as
$ConceptType/$PropositionType. - Assign it to the
CoreSchemadomain viabelongs_to_domain. - Keep definitions minimal and broadly reusable.
Common predicates worth registering early: prefers, knows, collaborates_with, interested_in, working_on, derived_from, belongs_to_class.
UPSERT {
CONCEPT ?prefers_def {
{type: "$PropositionType", name: "prefers"}
SET ATTRIBUTES {
description: "Subject indicates a stable preference for an object.",
subject_types: ["Person"],
object_types: ["*"]
}
SET PROPOSITIONS { ("belongs_to_domain", {type: "Domain", name: "CoreSchema"}) }
}
}
WITH METADATA { source: "SchemaEvolution", author: "$self", confidence: 0.9 }UPDATE ?target
SET ATTRIBUTES { <key>: <value_or_expr>, ... } // ≥1 of the two SET blocks
SET METADATA { <key>: <value_or_expr>, ... } // `_` keys rejected (KIP_2002)
WHERE { <patterns binding ?target> }
LIMIT N // optional blast-radius capAtomic: all matched elements update or none. Update expressions (numeric, computed per element from ?target's own state only): ADD(a, b), MUL(a, b), CLAMP(x, lo, hi), COALESCE(x, default). A null/non-number expression skips that key for that element. Scan bounds: LIMIT caps updates, not the scan — a fully unconstrained (?s, ?p, ?o) pattern is a full-graph scan the engine may reject on large graphs (KIP_4002); shard sweeps by predicate, endpoint type, or domain. The memory-metabolism workhorse:
// Confidence decay across all predicates, one command — small graphs only:
// past the engine's scan cap, shard per predicate ((?s, :predicate, ?o)) instead
// (spare structural links and axiomatic 1.0 truths)
UPDATE ?link
SET METADATA { confidence: CLAMP(MUL(?link.metadata.confidence, :factor), 0.0, 1.0), decay_applied_at: :now }
WHERE {
?link (?s, ?p, ?o)
FILTER(?p != "belongs_to_domain")
FILTER(IS_NULL(?link.metadata.superseded) || ?link.metadata.superseded != true)
FILTER(?link.metadata.created_at < :threshold)
FILTER(?link.metadata.confidence > 0.3 && ?link.metadata.confidence < 1.0)
// Idempotency guard: one decay per link per cycle; re-run until updated < LIMIT,
// reusing the same :cycle_start across re-runs and crash-retries
FILTER(IS_NULL(?link.metadata.decay_applied_at) || ?link.metadata.decay_applied_at < :cycle_start)
} LIMIT 500
// Reinforce without read-modify-write
UPDATE ?pref
SET ATTRIBUTES { evidence_count: ADD(COALESCE(?pref.attributes.evidence_count, 0), 1), last_observed: :now }
WHERE { ?pref {type: "Preference", name: :pref_name} }Response: {"updated": <n>, "matched": <m>} — matched by WHERE (after LIMIT), actually mutated.
MERGE CONCEPT ?source INTO ?target
WHERE { ?source {type: "<T>", name: "<dup>"} ?target {type: "<T>", name: "<canonical>"} }Each variable must match exactly one node, same type (0 → KIP_3002; >1 → KIP_3003; type mismatch → KIP_2002). Atomically: repoints all of source's links to target (link ids preserved; (s,p,o) collisions keep target's link, fill its missing keys, drop the duplicate), fills target's missing attributes (target wins; aliases unioned + source name appended to target's aliases), deletes source, records _merged_from (the source's own _merged_from entries carry over). Re-running after success → KIP_3002 = "already merged" (engines SHOULD hint this when the target's _merged_from lists the source). Protected nodes → KIP_3004.
Prefer: metadata → attribute → proposition → concept.
// Attributes
DELETE ATTRIBUTES {"risk_category", "old_id"} FROM ?drug
WHERE { ?drug {type: "Drug", name: "Aspirin"} }
// Metadata
DELETE METADATA {"old_source"} FROM ?drug
WHERE { ?drug {type: "Drug", name: "Aspirin"} }
// Propositions
DELETE PROPOSITIONS ?link
WHERE {
?link (?s, "treats", ?o)
FILTER(?link.metadata.source == "untrusted_source_v1")
}
// Concept (DETACH is mandatory; removes all incident links)
DELETE CONCEPT ?drug DETACH
WHERE { ?drug {type: "Drug", name: "OutdatedDrug"} }DELETE ATTRIBUTES / DELETE METADATA targets may be concept or proposition variables. Always verify with FIND before DELETE CONCEPT; DETACH cascades through higher-order propositions. KIP_3004 protects meta-types, the Domain type and belongs_to_domain definitions, core domains, $self/$system identity tuples, and their core_directives; ordinary $self attributes may evolve. Response: ATTRIBUTES/METADATA → {"updated_concepts": <n>, "updated_propositions": <m>} (key removal mutates, deletes nothing); PROPOSITIONS → {"deleted_propositions": <n>}; CONCEPT → {"deleted_concepts": <n>, "deleted_propositions": <m>} (cascade audit).
DESCRIBE PRIMER // Agent identity + Domain Map
DESCRIBE DOMAINS // top-level domains
DESCRIBE CONCEPT TYPES [LIMIT N] [CURSOR "<t>"] // list concept types
DESCRIBE CONCEPT TYPE "<Type>" // schema of one type
DESCRIBE PROPOSITION TYPES [LIMIT N] [CURSOR "<t>"]
DESCRIBE PROPOSITION TYPE "<predicate>"
SEARCH CONCEPT "<term>"|:term [WITH TYPE "<Type>"|:type]
[MODE "keyword"|"semantic"|"hybrid"|:mode] [THRESHOLD <0..1>|:threshold] [LIMIT N|:limit]
SEARCH PROPOSITION "<term>"|:term [WITH TYPE "<predicate>"|:type] [MODE ...] [THRESHOLD ...] [LIMIT N|:limit]
- Modes:
keyword(lexical),semantic(meaning-based; engine owns embeddings — text in, never vectors),hybrid(fused; recommended default). OmittedMODE→hybridwhere supported, elsekeyword; engines without semantic capability silently degrade tokeyword. - Grounding fields: engines MUST index
name+attributes.aliases; SHOULD indexdescriptionand salient text attributes. - Scoring: each hit carries transient
metadata._score([0,1], descending order);THRESHOLDdrops weak hits — a weak match is worse than an honest miss. - Use
SEARCHto resolve fuzzy names → exact{type, name}before structuredFIND; useMODE "semantic"when the probe is a meaning, not a name.
EXPORT ?target WHERE { ... } [LIMIT N] [CURSOR "<t>"]Serializes matched concepts/propositions into an idempotent UPSERT capsule for backup, migration, and agent-to-agent knowledge exchange. Bind everything the capsule needs: a nodes-only WHERE exports unconnected concepts — also bind the links among them, their belongs_to_domain links, and the Domain node itself via separate UNION branches (each branch binds ?target independently; without the Domain node, importing into a fresh nexus fails with KIP_3002). Endpoints outside the export set become {type, name} refs (must exist on import); outside proposition endpoints become nested structural (s, "p", o) clauses (link IDs are not portable; must exist on import); reserved _ metadata is never exported; export needed $ConceptType/$PropositionType definitions separately if the destination may lack them. Response: {"capsule": "<KIP script>", "concepts": n, "propositions": m}, plus next_cursor when more remain — re-issue with CURSOR to continue; each page is an independently valid capsule.
execute_kip_readonly— KQL (FIND) and META (DESCRIBE/SEARCH/EXPORT) only.execute_kip— full read/write (adds KML:UPSERT/UPDATE/MERGE/DELETE).
command(String) ORcommands(Array) — exactly one MUST be provided.commandselement: a string (uses sharedparameters) or{command, parameters}(independent).parameters(Object)::name→ JSON value substitution. Placeholders must occupy a complete KIP value position (name: :name,LIMIT :limit,SEARCH CONCEPT :term); never embed inside a string literal ("Hello :name"is invalid — substitution uses JSON serialization).dry_run(Boolean): validate only.
Batch error semantics: KQL / META / syntax errors are returned inline and execution continues. The first KML (UPSERT / UPDATE / MERGE / DELETE) error stops the batch.
// Single read-only
{
"function": {
"name": "execute_kip_readonly",
"arguments": {
"command": "FIND(?n) WHERE { ?n {name: :name} }",
"parameters": { "name": "Aspirin" }
}
}
}
// Batch read/write
{
"function": {
"name": "execute_kip",
"arguments": {
"commands": [
"DESCRIBE PRIMER",
{ "command": "UPSERT { ... :val ... }", "parameters": { "val": 123 } }
],
"parameters": { "global_param": "value" }
}
}
}- Single response:
{ "result": ... }or{ "error": { "code", "message", "hint"? } }, with optionalnext_cursor. - Batch response:
{ "result": [<single_response>, ...] }; KML stop-on-error may make the array shorter than submitted commands. - Result shapes:
FIND→ columnar — one index-aligned column per expression (single expression unwrapped:FIND(?n)→ array of node objects; bare?var→ full objects; non-grouped aggregation → scalar; grouped → aligned columns, e.g.[["DomainA","DomainB"],[15,3]]);SEARCH→ array of hits (descending_score);DESCRIBE PRIMER→{identity, domain_map, total_domains},TYPESlists → name arrays (+next_cursor), single type → definition node;UPSERT→{"blocks", "upsert_concept_nodes", "upsert_proposition_links"};UPDATE→{"updated", "matched"};DELETE→{"deleted_*"}/{"updated_*"}counters;EXPORT→{"capsule", "concepts", "propositions"}.
// Single success
{ "result": [ { "id": "...", "type": "Drug", "name": "Aspirin" } ], "next_cursor": "token_xyz" }
// Batch (one entry per command)
{ "result": [
{ "result": { ... } },
{ "result": [...], "next_cursor": "abc" },
{ "error": { "code": "KIP_2001", "message": "...", "hint": "..." } }
] }
// Error
{ "error": { "code": "KIP_2001", "message": "TypeMismatch: 'drug' is not a valid type. Did you mean 'Drug'?", "hint": "Check Schema with DESCRIBE." } }| Entity | Purpose |
|---|---|
{type: "$ConceptType", name: "$ConceptType"} |
Meta-meta (self-referential genesis) |
{type: "$ConceptType", name: "$PropositionType"} |
Meta for predicates |
{type: "$ConceptType", name: "Domain"} |
Organizational unit type |
{type: "$PropositionType", name: "belongs_to_domain"} |
Domain membership predicate |
{type: "Domain", name: "CoreSchema"} |
Holds core schema definitions |
{type: "Domain", name: "Unsorted"} |
Holding area for uncategorized items |
{type: "Domain", name: "Archived"} |
Deprecated/obsolete items |
{type: "Domain", name: "System"} |
Operational home for memory-system nodes (e.g., SleepTask instances) |
{type: "$ConceptType", name: "Person"} |
Actors (AI, Human, Org, System) |
{type: "$ConceptType", name: "Event"} |
Episodic memory |
{type: "$ConceptType", name: "Preference"} |
First-class stable preference facts |
{type: "$ConceptType", name: "Insight"} |
Self-reflective lessons of the agent |
{type: "$ConceptType", name: "Commitment"} |
Prospective promises & deadlines |
{type: "$ConceptType", name: "SleepTask"} |
Background maintenance tasks |
{type: "Person", name: "$self"} |
The waking mind (conversational agent) |
{type: "Person", name: "$system"} |
The sleeping mind (maintenance agent) |
Core predicates (pre-bootstrapped $PropositionTypes): belongs_to_domain, involves (Event → Person), mentions (Event → any), consolidated_to (Event → semantic), derived_from (semantic → Event), prefers (Person → Preference), learned (Person → Insight), committed_to (Person → Commitment), owed_to (Commitment → Person), assigned_to (SleepTask → Person).
Provenance
| Field | Type | Description |
|---|---|---|
source |
string | array | Origin (conversation id, document id, url) |
author |
string | Asserter ($self, $system, user id) |
confidence |
number | [0, 1] |
evidence |
array<string> | References supporting the assertion |
Temporality / Lifecycle
| Field | Type | Description |
|---|---|---|
created_at / observed_at |
string | ISO-8601 |
expires_at |
string | ISO-8601 — signal for $system cleanup; not auto-filtered |
valid_from / valid_until |
string | ISO-8601 validity window |
status |
string | active | draft | reviewed | deprecated | retracted — assertion lifecycle, distinct from a type's own attributes.status |
memory_tier |
string | short-term | long-term |
superseded |
bool | true for historical (state-evolved) facts |
superseded_by / supersedes |
string | Pointers across the evolution chain |
superseded_at |
string | ISO-8601 time when the assertion was superseded |
Context / Auditing
| Field | Type | Description |
|---|---|---|
relevance_tags |
array<string> | Topic / domain tags |
access_level |
string | public | private |
review_info |
object | Structured review history |
Reserved System Fields (_ namespace — engine-maintained, read-only to KML; see §1.8)
| Field | Type | Description |
|---|---|---|
_version |
number | Monotonic mutation counter; target of EXPECT VERSION |
_updated_at |
string | ISO-8601 last-mutation time (engine truth) |
_score |
number | Transient SEARCH relevance [0,1]; never persisted |
_merged_from |
array<string> | MERGE provenance trail |
| Code | Name | Meaning |
|---|---|---|
KIP_1001 |
InvalidSyntax |
Parse or structural error |
KIP_1002 |
InvalidIdentifier |
Illegal identifier format |
KIP_2001 |
TypeMismatch |
Unknown type or predicate |
KIP_2002 |
ConstraintViolation |
Schema constraint violated (incl. writing _ reserved keys, cross-type MERGE) |
KIP_2003 |
InvalidValueType |
JSON value type mismatches schema |
KIP_3001 |
ReferenceError |
Undefined variable or handle |
KIP_3002 |
NotFound |
Referenced node/link does not exist |
KIP_3003 |
DuplicateExists |
Uniqueness constraint violated; MERGE variable matched >1 node |
KIP_3004 |
ImmutableTarget |
Protected system structure modified/deleted |
KIP_3005 |
VersionConflict |
EXPECT VERSION mismatch — re-read, re-merge, retry |
KIP_4001 |
ExecutionTimeout |
Query exceeded execution time |
KIP_4002 |
ResourceExhausted |
Result/resource limit exceeded |
KIP_4003 |
InternalError |
Unknown internal system error |
- Ground before structured query: use
SEARCH CONCEPT "<term>"(andDESCRIBEfor unknown types) beforeFIND— names are ambiguous. When the probe is a meaning rather than a name, useMODE "semantic"/"hybrid"with aTHRESHOLD. - Cross-language: the graph stores English
name/descriptionwith optionalaliases; for non-English queries, send bilingualSEARCHprobes in parallel via thecommandsarray. - Define before use: any new type/predicate must be registered via
$ConceptType/$PropositionTypefirst, then assigned to aDomain. - Idempotent writes: prefer
{type, name}identity; avoid random ids/names unless retries are stable. - Always attach provenance:
WITH METADATA { source, author, confidence, ... }— knowledge without provenance is untrusted. - State evolution > deletion: when a fact changes, mark the old proposition
superseded: true(withsuperseded_by,superseded_at) and upsert the new one withsupersedes. Keep history. - Respect
expires_atsemantics: it is a signal, not a filter. Add explicitFILTER(IS_NULL(?x.metadata.expires_at) || ?x.metadata.expires_at > <now>)only when the query implies "currently valid". Hard deletion belongs to$systemsleep cycles. - Smallest delete that fixes the issue: metadata → attribute → proposition →
DELETE CONCEPT ... DETACH. AlwaysFINDfirst. Never modify/delete protected core: meta-types, theDomaintype andbelongs_to_domaindefinitions, core domains,$self/$systemidentity tuples, orcore_directives. - Batch independent operations in
commandsto reduce round-trips. Remember: KML errors stop the batch; KQL/META/syntax errors return inline. - Mind variable scope:
NOThides internal bindings;UNIONdoesn't see external bindings;OPTIONALprojectsnullon miss. - Use
OPTIONALfor "may exist",NOTfor "must not exist",UNIONfor "either branch",FILTERfor value predicates. - Higher-order propositions
(?u, "stated", (?s, ?p, ?o))are first-class — use them for provenance, beliefs, and meta-claims rather than flattening into attributes. OPTIONALprojection of unbound variables yieldsnullandIS_NULLreturnstrue— safe for downstreamFILTER.- Confidence transparency: when synthesizing answers, surface
confidenceand recency; prefer highevidence_countconsolidated patterns over raw single Events. - Explore with predicate variables:
(?seed, ?pred, ?neighbor)is the one-query "what do I know about X?" primitive — constrain the seed, exclude noisy predicates inFILTER, and alwaysLIMIT. - Bulk mutation belongs to
UPDATE: decay, counters, status sweeps, salience refresh — one pattern-matchedUPDATEwithADD/MUL/CLAMP/COALESCEbeats N per-elementUPSERTs, and never needs a prior read for pure increments. - Guard read-modify-write with
EXPECT VERSION: read_versiontogether with the value, merge in memory, write back guarded; onKIP_3005re-read and retry. Required discipline for$selfattributes and any shared array/object value. - Deduplicate with
MERGE, not by hand: one atomicMERGE CONCEPT ?dup INTO ?canonicalrepoints every link and preserves aliases/provenance; verify both nodes withFINDfirst. - Reads are reads: the protocol keeps no access statistics (tracking reads would turn every query into a write, and recall frequency ≠ importance). Decide decay and landmark promotion from author-maintained signals:
evidence_count(observation),last_observed(recency),salience_score(impact),expires_at(declared intent). - Memories are portable: use
EXPORTfor backup, migration, and sharing knowledge between agents — and remember imports need the schema and referenced endpoints to exist first.