Appendix A: Ebbinghaus-Roynard Bootstrap¶
A complete, ready-to-use knowledge-layer scoring configuration implementing the Ebbinghaus-Roynard four-layer decomposition.
On database open, if config.Memory.DecayEnabled is true and the target namespace has no existing knowledge-policy schema entries at all, NornicDB seeds that namespace with this built-in bootstrap. It does not overwrite or merge with an existing knowledge-policy schema: if any decay bundles, decay bindings, promotion profiles, or promotion policies already exist, bootstrap is skipped. The built-in bootstrap covers the Ebbinghaus-Roynard defaults excluding the canonical-graph-ledger layer; that layer should be installed separately with a dedicated CGL bootstrap tailored to Roynard's model.
Overview¶
This bootstrap implements the Ebbinghaus-Roynard model described in "The Missing Knowledge Layer in Cognitive Architectures for AI Agents" (Roynard, 2026; arXiv:2604.11364). The paper identifies a category error in applying uniform cognitive decay to all content types and proposes a four-layer decomposition with distinct persistence semantics:
| Layer | Content Type | Persistence Semantic | NornicDB Implementation |
|---|---|---|---|
| Knowledge | Facts, claims, entities | Supersession, not forgetting | :KnowledgeFact — decayEnabled: false; superseded via :SUPERSEDES edges |
| Memory | Experiences, episodes, sessions | Ebbinghaus forgetting + consolidation | :MemoryEpisode — exponential decay, 7-day half-life, scoreFrom: 'VERSION' |
| Wisdom | Behavioral directives, patterns | Evidence-gated revision with stability tiers | :WisdomDirective — decayEnabled: false; stability tiers via promotion |
| Intelligence | The model itself | Frozen in weights | The LLM — its effects persist only through writes to the other three layers |
NornicDB implements the three persistence layers (Knowledge, Memory, Wisdom) as database-native primitives. The Intelligence layer is the model itself — it leaves no trace in storage and its effects persist only through writes to the other three layers. This is a fundamental architectural distinction, not a gap: there is nothing to configure because the model's weights are outside the persistence boundary.
This bootstrap provides the complete DDL to configure all three persistence layers, their edges, promotion tiers, and multi-agent session gating. Replace the label names with your domain's terminology.
Quick Start¶
For a brand new decay-enabled database, these defaults are already installed automatically. Run the DDL below only when you want to inspect, customize, or reproduce the built-in bootstrap manually via the NornicDB shell or Bolt client.
Step 1: Decay Profile Bundles¶
Knowledge layer — no decay¶
Knowledge facts are permanent. They are superseded by newer evidence through graph operations, not by time-based decay. The Ebbinghaus curve does not apply here.
CREATE DECAY PROFILE knowledge_fact_retention OPTIONS {
decayEnabled: false,
visibilityThreshold: 0.0,
function: 'none',
scoreFrom: 'CREATED'
}
Memory layer — Ebbinghaus exponential decay (7-day half-life)¶
Memory episodes decay according to the Ebbinghaus forgetting curve: score(t) = e^(-ln(2) × t / 604800). Episodes that are not accessed or consolidated within this window lose score and eventually become invisible. Suppressed episodes are deindexed but remain accessible through reveal().
CREATE DECAY PROFILE memory_episode_retention OPTIONS {
halfLifeSeconds: 604800,
function: 'exponential',
visibilityThreshold: 0.10,
scoreFrom: 'VERSION'
}
Memory summary — slower decay for summarized content¶
Episode summaries retain value even as the raw experience fades. A 14-day half-life lets the curve fade, and scoreFloor set to match visibilityThreshold keeps the summary visible forever once the curve drops to that level — the floor and threshold are independent levers, so this works only because they are equal here. With scoreFloor: 0.0 the summary would disappear once the curve crossed 0.10; with scoreFloor: 0.10 (matching threshold) it plateaus at 0.10, visible but ranked at the bottom.
CREATE DECAY PROFILE session_summary OPTIONS {
halfLifeSeconds: 1209600,
function: 'exponential',
visibilityThreshold: 0.10,
scoreFloor: 0.10 -- equals threshold → "visible but ranked lowest after fade"
}
Wisdom layer — no time-based decay¶
Wisdom directives are revised by evidence-gated graph operations, not by time. Stability tiers (managed through promotion policies) gate revision.
CREATE DECAY PROFILE wisdom_directive_retention OPTIONS {
decayEnabled: false,
visibilityThreshold: 0.0,
function: 'none',
scoreFrom: 'CREATED'
}
Evidence edges — 30-day decay¶
The relevance of supporting evidence fades, even though the Knowledge fact it supports does not. A 30-day half-life balances recency and retention.
CREATE DECAY PROFILE evidence_decay OPTIONS {
halfLifeSeconds: 2592000,
function: 'exponential',
visibilityThreshold: 0.10,
scoreFrom: 'CREATED'
}
Step 2: Decay Profile Bindings¶
Memory Episodes — full Ebbinghaus decay with bi-temporal properties¶
CREATE DECAY PROFILE memory_episode_retention_binding
FOR (n:MemoryEpisode)
APPLY {
DECAY PROFILE 'memory_episode_retention'
DECAY VISIBILITY THRESHOLD 0.10
n.tenantId NO DECAY
n.agentId NO DECAY
n.sessionId NO DECAY
n.system_created_at NO DECAY
n.system_expired_at NO DECAY
n.valid_from NO DECAY
n.valid_to NO DECAY
n.summary DECAY HALF LIFE 1209600
n.summary DECAY FLOOR 0.10
n.ephemeralContext DECAY HALF LIFE 86400
}
Property-level NO DECAY on a decaying node is meaningful — it marks structural properties that remain fully visible even as the node's content decays. Properties like tenantId, sessionId, and bi-temporal timestamps (system_created_at, valid_from, etc.) are query infrastructure, not decaying content. A NO DECAY property also acts as a suppression anchor: a node with at least one NO DECAY property cannot be fully suppressed because that property remains visible at score 1.0.
Note: property-level NO DECAY is only useful when the parent node does decay. On a decayEnabled: false node (like :KnowledgeFact or :WisdomDirective), property-level NO DECAY is redundant — the entire node is already permanent.
Knowledge Facts — supersession model¶
CREATE DECAY PROFILE knowledge_fact_retention_binding
FOR (n:KnowledgeFact)
APPLY {
DECAY PROFILE 'knowledge_fact_retention'
}
The referenced profile has decayEnabled: false — the entire node is permanent. Property-level NO DECAY would be redundant here. Properties are protected by supersession, not by decay anchors.
Wisdom Directives — evidence-gated revision¶
CREATE DECAY PROFILE wisdom_directive_retention_binding
FOR (n:WisdomDirective)
APPLY {
DECAY PROFILE 'wisdom_directive_retention'
}
Same as Knowledge: the referenced profile disables decay entirely. Wisdom directives are revised through evidence-gated graph operations (:REVISES edges), not by time.
Evidence edges — 30-day decay with permanent provenance¶
The evidence edge decays with a 30-day half-life, but r.sourceId is declared NO DECAY — it is a structural property that must remain visible for provenance queries even as the edge's relevance fades.
CREATE DECAY PROFILE evidence_edge_retention_binding
FOR ()-[r:EVIDENCES]-()
APPLY {
DECAY PROFILE 'evidence_decay'
DECAY VISIBILITY THRESHOLD 0.10
r.sourceId NO DECAY
}
Supersession edges — permanent provenance¶
Every :SUPERSEDES edge is a permanent record of the Knowledge-layer supersession chain. Entity-level NO DECAY makes the entire edge permanent — property-level NO DECAY would be redundant.
Consolidation edges — permanent link from episodes to facts¶
Revision edges — permanent wisdom provenance¶
Derived-from edges — permanent lineage¶
Step 3: Promotion Profiles¶
Note: a promotion profile's
scoreFlooris a different field from a decay bundle'sscoreFloor. The promotion floor is applied before the cap inside the promoted-curve computation (max(promoFloor, base * multiplier)); the decay floor is the final clamp on the resulting score. Both clamp upward; the promotion floor only matters when the matchingWHENpredicate fires. Neither is a visibility cutoff — that role belongs to the decay bundle'svisibilityThreshold.
Memory Consolidation Tiers¶
CREATE PROMOTION PROFILE memory_reinforced OPTIONS {
multiplier: 1.25,
scoreFloor: 0.0,
scoreCap: 1.0
}
CREATE PROMOTION PROFILE consolidation_candidate OPTIONS {
multiplier: 1.50,
scoreFloor: 0.80,
scoreCap: 1.0
}
Wisdom Stability Tiers¶
CREATE PROMOTION PROFILE wisdom_provisional OPTIONS {
multiplier: 1.0,
scoreFloor: 0.0,
scoreCap: 1.0
}
CREATE PROMOTION PROFILE wisdom_established OPTIONS {
multiplier: 1.0,
scoreFloor: 0.50,
scoreCap: 1.0
}
CREATE PROMOTION PROFILE wisdom_canonical OPTIONS {
multiplier: 1.0,
scoreFloor: 0.90,
scoreCap: 1.0
}
Evidence Traversal Tier¶
CREATE PROMOTION PROFILE reinforced_evidence OPTIONS {
multiplier: 1.20,
scoreFloor: 0.0,
scoreCap: 1.0
}
Step 4: Promotion Policies¶
Memory Episode Consolidation¶
Tracks access patterns across sessions and agents. Uses Kalman filtering to smooth noisy behavioral signals and session gating to prevent within-session sycophancy loops.
Architectural boundary: the promotion policy determines when an entity is a candidate for consolidation (via WHEN predicates and promotion tiers). It does not perform the consolidation itself — creating a :KnowledgeFact node and linking it with a :CONSOLIDATES_TO edge is an application-layer concern. The Heimdall plugin (pkg/heimdall/plugin.go) serves as the reference implementation.
CREATE PROMOTION POLICY memory_episode_consolidation
FOR (n:MemoryEpisode)
APPLY {
ON ACCESS {
SET n.accessCount = coalesce(n.accessCount, 0) + 1
SET n.lastAccessedAt = timestamp()
SET n.accessIntervals = coalesce(n.accessIntervals, '') + ',' + toString(timestamp())
WITH KALMAN SET n.crossSessionAccessRate =
CASE WHEN n._lastSessionId <> $_session
THEN coalesce(n.crossSessionAccessRate, 0) + 1
ELSE n.crossSessionAccessRate
END
SET n._lastSessionId = $_session
}
WHEN n.accessCount >= 3
APPLY PROFILE 'memory_reinforced'
WHEN n.accessCount >= 5 AND n.sourceAgreement >= 0.80
APPLY PROFILE 'consolidation_candidate'
}
Wisdom Directive Stability¶
Stability tiers gate revision: a provisional directive can be revised by any contradicting evidence; a canonical directive requires overwhelming counter-evidence. evidenceCount, contradictionRate, and crossSessionSupport in the WHEN predicates resolve from accessMeta first, falling back to stored node properties.
CREATE PROMOTION POLICY wisdom_directive_stability
FOR (n:WisdomDirective)
APPLY {
ON ACCESS {
SET n.evaluationCount = coalesce(n.evaluationCount, 0) + 1
SET n.lastEvaluatedAt = timestamp()
}
WHEN n.evidenceCount < 3
APPLY PROFILE 'wisdom_provisional'
WHEN n.evidenceCount >= 3 AND n.contradictionRate < 0.20
APPLY PROFILE 'wisdom_established'
WHEN n.evidenceCount >= 10 AND n.contradictionRate < 0.05 AND n.crossSessionSupport >= 3
APPLY PROFILE 'wisdom_canonical'
}
Evidence Edge Traversal¶
More frequently traversed evidence links carry higher weight in retrieval even as their base decay score drops.
CREATE PROMOTION POLICY evidence_traversal_tiering
FOR ()-[r:EVIDENCES]-()
APPLY {
ON ACCESS {
SET r.traversalCount = coalesce(r.traversalCount, 0) + 1
SET r.lastTraversedAt = timestamp()
}
WHEN r.traversalCount >= 5
APPLY PROFILE 'reinforced_evidence'
}
How It Works¶
Memory Layer — Ebbinghaus Forgetting with Spaced Repetition¶
A :MemoryEpisode created at time t₀ has a base score of:
| Age | Base Score | With memory_reinforced (×1.25) | With consolidation_candidate (floor 0.80) |
|---|---|---|---|
| 0 days | 1.000 | 1.000 | 1.000 |
| 7 days | 0.500 | 0.625 | 0.800 |
| 14 days | 0.250 | 0.313 | 0.800 |
| 21 days | 0.125 | 0.156 | 0.800 |
| 30 days | 0.045 | 0.056 | 0.800 |
The consolidation_candidate floor of 0.80 models the spaced-repetition finding: sufficiently rehearsed content resists forgetting indefinitely until consolidated into a :KnowledgeFact.
Knowledge Layer — Canonical Graph Ledger¶
:KnowledgeFact nodes never decay. When a fact is updated, the canonical graph ledger pattern applies:
- Create a new
:KnowledgeFactnode with the updated assertion - Create a
:SUPERSEDESedge from the new node to the old, carryingsuperseded_at,superseded_by_agent,evidence_source - Move the
:CURRENTpointer to the new version
The old fact remains in the graph as an immutable historical record. See Canonical Graph Ledger for the full pattern.
Wisdom Layer — Evidence-Gated Stability¶
:WisdomDirective nodes never decay. They progress through stability tiers based on accumulated evidence:
| Tier | Trigger | Score Floor | Revision Requirement |
|---|---|---|---|
| Provisional | evidenceCount < 3 | 0.0 | Any contradicting evidence |
| Established | evidenceCount >= 3, contradictionRate < 0.20 | 0.50 | Multiple independent sources |
| Canonical | evidenceCount >= 10, contradictionRate < 0.05, crossSessionSupport >= 3 | 0.90 | Overwhelming counter-evidence; flagged for human review |
When evidence gates are met for revision, the operation creates a new :WisdomDirective (status: active), sets the old one to status: 'retired', and links them via a :REVISES edge with provenance.
Edge Decay¶
| Edge Type | Behavior | Purpose |
|---|---|---|
:EVIDENCES | 30-day half-life, reinforced at 5+ traversals | Supporting evidence fades; the fact it supports does not |
:SUPERSEDES | No decay | Permanent provenance: old facts linked to successors |
:CONSOLIDATES_TO | No decay | Permanent link from consolidated episode to fact |
:REVISES | No decay | Permanent wisdom revision history |
:DERIVED_FROM | No decay | Permanent lineage |
Multi-Agent Session Gating¶
The $_session and $_agent variables are passed from HTTP headers and prevent within-session access inflation:
| Header | Variable | Purpose |
|---|---|---|
X-Query-Session | $_session | Same-session deduplication |
X-Query-Agent | $_agent | Per-agent tracking |
X-Query-Tenant | $_tenant | Multi-tenant isolation |
The WITH KALMAN SET n.crossSessionAccessRate = CASE WHEN ... pattern gates the counter so that 50 accesses from one session count as one observation. The Kalman filter then smooths genuine cross-session signals.
Verification¶
After running all DDL statements:
Expected: 5 bundles and 8 bindings.
Expected: 6 promotion profiles and 3 promotion policies.
CLI Maintenance¶
nornicdb decay recalculate --data-dir ./data
nornicdb suppress --data-dir ./data --threshold 0.10
nornicdb decay stats --data-dir ./data
Customization¶
- Replace node labels:
:MemoryEpisode→ your ephemeral data label,:KnowledgeFact→ your persistent fact label,:WisdomDirective→ your policy/rule label - Adjust half-lives:
604800(7 days) works for conversational memory; adjust for your domain's natural forgetting rate - Tune promotion thresholds:
accessCount >= 3andsourceAgreement >= 0.80are starting points; tune based on your multi-agent setup - Add property rules: Extend
APPLYblocks with domain-specific properties that needNO DECAYor custom half-lives
Optional: Inverted Memory Layer (Consolidation)¶
The default Memory profile above implements the classic Ebbinghaus forgetting curve — episodes start strong and fade unless reinforced. Roynard's full model also covers the inverse dynamic: a memory that is not retrieved gains strength as it consolidates, while frequent retrieval (interference) erodes it. This is supported as a first-class profile by giving halfLifeSeconds a negative value and anchoring on LAST_ACCESSED. The scoreFloor choice matters more on the inverted curve than on the forward one because the inverted curve evaluates to exactly 0.0 immediately after access:
CREATE DECAY PROFILE memory_consolidation OPTIONS {
halfLifeSeconds: -86400, -- negative → invert the curve
function: 'exponential',
scoreFrom: 'LAST_ACCESSED', -- age = time since last retrieval
visibilityThreshold: 0.10,
scoreFloor: 0.10 -- equals threshold → visible right after access
}
A node bound to this profile scores 0.10 (the floor) immediately after access — visible because suppression uses a strict < check — and strengthens monotonically toward 1.0 as it sits idle. The next access drops the score back to 0.10, never below.
Setting scoreFloor: 0.0 here would produce a different behavior: every access drops the score to strict zero, the entity is suppressed and deindexed instantly, and stays hidden until ~3.3 hours of idle time pass for the curve to climb back over the threshold. Choose 0.0 only when you explicitly want a "cooldown" memory that disappears between retrievals.
Pair the consolidation profile with a dampening promotion profile to model interference:
CREATE PROMOTION PROFILE retrieval_interference OPTIONS {
multiplier: 0.5,
scoreFloor: 0.0,
scoreCap: 1.0
}
CREATE PROMOTION POLICY consolidation_interference
FOR (n:MemoryEpisode)
APPLY {
ON ACCESS {
SET n.accessCount = coalesce(n.accessCount, 0) + 1
SET n.lastAccessedAt = timestamp()
}
WHEN n.accessCount >= 5
APPLY PROFILE 'retrieval_interference'
}
Net behavior — opposite of the default forgetting layer:
- Idle episodes consolidate (score rises toward 1.0).
- Each retrieval resets the consolidation clock and bumps
accessCount. - Once
accessCountcrosses the interference threshold, the dampener pins the multiplier at0.5and frequent retrievals never reach the asymptote.
Use this layer when modeling skills/procedures (which strengthen with rest) instead of episodic recall (which strengthens with rehearsal). The two layers can coexist on different label sets within the same database.
Related Documentation¶
- Knowledge-Layer Policies — System overview
- Decay Profiles — DDL syntax reference
- Promotion Policies — Promotion DDL reference
- Visibility Suppression — Suppression and deindex behavior
- Canonical Graph Ledger — FactKey/FactVersion/SUPERSEDES pattern