Files
self-directed-learning/state/SCHEMA.md
T
Krystie dc5dc94d79 Initial commit: Adaptive Recall sidecar for UMP (Phase 5)
Multi-channel retrieval sidecar over Universal Memory Protocol:
- 3-channel RRF (UMP FTS5 + Qdrant vector + knowledge graph)
- ACT-R re-ranking (Anderson 1983) with access tracking
- Co-occurrence graph edges (Phase 6) for dense traversal
- Memory lifecycle decay (Phase 4) with per-kind confidence
- MCP shim routes recall through sidecar, falls back to canonical UMP

Architecture:
- src/server.js      HTTP sidecar on port 4380
- src/graph.js       2592-node / 111-edge graph from UMP (or +cooccur: 13k+)
- src/actr.js        A_i = -d*ln(age) + beta*log1p(freq) + epsilon*conf
- src/access_log.js  per-URN counter + last_accessed_at
- src/ump-recall-mcp.js  MCP shim (recall via sidecar, others passthrough)

Eval results (851-record UMP corpus):
- 2ch RRF over baseline: +50pp recall@10
- 3ch RRF (+graph): +60pp, 12 unique wins
- ACT-R re-rank: 4/20 #1 changes, 84% top-5 retention

Tests: 76/76 passing across graph (27), actr (27), access_log (28),
decay (20), mcp-shim (sidecar + fallback). Run with: npm test

Inspired by AIAppsAPI/adaptive-recall but built from scratch against
existing DNS2 infrastructure (UMP at :4317, Qdrant at :6333,
Ollama at :11434). No paid SaaS, MIT-licensed.
2026-07-12 19:24:47 -07:00

2.6 KiB

UMP memory.ump.json — actual schema (verified 2026-07-12)

File: /root/.openclaw/agents/main/workspace/state/ump-local/memory.ump.json Format: JSON array, 851 records Total records: 851 (not 846 — was 768 in Qdrant backfill, 846 in watcher's last run; real total is 851)

Top-level fields per record

{
  "ump": "0.1",                          // schema version (string)
  "id": "urn:ump:jzbhzerwqn23hfzre...",  // URN string (not "urn"!)
  "kind": "procedural",                  // see kinds below
  "body": {
    "subject": "...",                    // short title
    "text": "..."                        // full content
  },
  "scope": {
    "owner": "did:key:z6Mk...",          // actor DID
    "project": "openclaw/workspace",
    "visibility": "private",
    "tags": ["..."]
  },
  "time": {
    "created": "2026-06-06T21:49:57.806Z",  // ISO 8601
    "modified": "...",
    "observed": "...",
    "valid_from": "...",
    "valid_to": null
  },
  "lifecycle": {                         // NESTED, not flat!
    "status": "active",                  // see statuses below
    "confidence": 0.6                    // 0.0 - 1.0
  },
  "superseded_by": [],                   // array or null
  "provenance": {...},
  "integrity": {...}
}

Kinds (distribution)

kind count
procedural 245
semantic 231
identity 152
episodic 152
note 42
working 29

(Total: 851)

Lifecycle statuses (distribution)

status count meaning
active 708 normal, queryable
candidate 53 pending promotion
archived 42 kept but deprioritized
tombstoned 48 deleted logically — skip in retrieval

Critical corrections vs spec

  • Field is id, NOT urn
  • lifecycle.status is nested in lifecycle.status, NOT flat status
  • lifecycle.confidence is nested, NOT flat
  • kind values include note (in addition to standard 5)
  • Status values: active, candidate, archived, tombstoned (not just active/archived)
  • No topic field — uses scope.project and scope.tags
  • No access_count or last_accessed_at fields yet (would need to be added)

Code patterns

import fs from 'node:fs';
const data = JSON.parse(fs.readFileSync(UMP_FILE, 'utf-8'));
for (const r of data) {
  const urn = r.id;                      // NOT r.urn
  const text = r.body?.text ?? r.body?.subject ?? '';
  const kind = r.kind;                   // procedural|note|semantic|working|identity|episodic
  const status = r.lifecycle?.status ?? 'active';
  const confidence = r.lifecycle?.confidence ?? 1.0;
  const createdAt = r.time?.created ?? null;
}