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

86 lines
2.6 KiB
Markdown

# 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
```jsonc
{
"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
```js
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;
}
```