Skip to content

feat(memory): ICM slices 1-3 — memoirs + feedback + importance/decay#580

Merged
NagyVikt merged 4 commits into
mainfrom
agent/claude/icm-slice-3-importance-decay
May 20, 2026
Merged

feat(memory): ICM slices 1-3 — memoirs + feedback + importance/decay#580
NagyVikt merged 4 commits into
mainfrom
agent/claude/icm-slice-3-importance-decay

Conversation

@NagyVikt
Copy link
Copy Markdown
Collaborator

@NagyVikt NagyVikt commented May 18, 2026

Summary

Unifies the ICM integration on one branch. The three slices from docs/icm-integration-plan.md (memoirs, feedback, importance + temporal decay) were drafted in parallel, all targeting migration 015 — this PR lands them in canonical order on a single, testable branch.

Slice 1 — memoirs (typed knowledge graphs)

  • Tables: memoirs, memoir_concepts, memoir_relations; FTS5 mirror over name/content/labels with the standard (ai, ad, au) triggers.
  • Nine relation types (part_of, depends_on, related_to, contradicts, refines, alternative_to, caused_by, instance_of, superseded_by); self-loops forbidden; (source, target, relation) unique.
  • MemoryStore.{createMemoir, addConcept, refineConcept, linkConcepts, searchConcepts, inspectConcept, listMemoirs} — concept content routes through prepareMemoryText (same redact → compress pipeline as observations).
  • Seven MCP tools (memoir_create / list / add_concept / refine / link / search / inspect) with progressive disclosure: memoir_search returns compact hits; memoir_inspect returns body + BFS neighbourhood.

Slice 2 — feedback record/search/stats

  • Table: feedback (topic, prediction, correction, optional context, importance, created_by) + feedback_fts virtual table.
  • MemoryStore.recordFeedback / searchFeedback / getFeedback / feedbackStats — prediction/correction/context bodies pass through prepareMemoryText, so the compression invariant holds at the write boundary.
  • MCP: feedback_record / feedback_search / feedback_stats with compact-hit progressive disclosure.

Slice 3 — observation importance + temporal decay

  • New columns on observations: importance (critical | high | medium | low, default medium), access_count, last_accessed_at, weight.
  • Storage.recordAccess(ids): single transactional UPDATE that increments access_count, stamps last_accessed_at, and recomputes weight. Critical/high pin to baseWeight(importance); medium/low decay as baseWeight / (1 + access_count * 0.1).
  • Storage.pruneLowDecay({ minWeight }) + Storage.countLowDecayCandidates: delete / count medium-or-low rows below the threshold. Critical/high are never affected.
  • MCP: search, semantic_search, get_observations carry importance + weight in each row (additive). task_post accepts optional importance.
  • CLI: colony memory prune [--min-weight <n>] [--dry-run].

Migration ordering

File Version Slice
015-icm-memoirs.ts 15 1
016-icm-feedback.ts 16 2
017-icm-importance-decay.ts 17 3

SCHEMA_SQL keeps every ICM table as CREATE TABLE IF NOT EXISTS (fresh DBs); existing DBs pick up slice 3's column additions via COLUMN_MIGRATIONS. The numbered migration files remain canonical references — the runtime path stays SCHEMA_SQL + COLUMN_MIGRATIONS.

Write-amplification strategy (slice 3)

Debounced 50ms batch via setTimeout: read paths queue returned ids into a Set, the first queue kicks a setTimeout, and the timer flushes the coalesced set in one transactional UPDATE. Every access is counted (unlike threshold sampling), write amplification is capped at one UPDATE per ~50ms hot window, and MemoryStore.close() deterministically flushes pending ids. unref() keeps the timer from holding Node alive. Readonly stores skip the buffer entirely.

Verification

pnpm typecheck                              # green across 17 workspaces
pnpm --filter @colony/mcp-server test       # 302 passed
COLONY_SKIP_PERF=1 pnpm --filter @colony/storage test
# 193 passed | 1 skipped (perf, gated per its own COLONY_SKIP_PERF docstring)
pnpm --filter @colony/core test             # 259 passed; one pre-existing
# `savings-reference.test.ts` failure also fails on the 2d831bb baseline before
# this merge — unrelated to ICM.

Includes the slice 3 perf-smoke receipt that was already in the original PR body:

[importance-perf] n=100 mean=21.77ms p95=41.99ms

Reference

🤖 Generated with Claude Code

NagyVikt added 3 commits May 18, 2026 11:25
Adds importance/access_count/last_accessed_at/weight columns on
observations and threads them through the read+write path:

- New importance tier per observation (default medium). critical/high
  pin their weight to baseWeight; medium/low decay as
  baseWeight / (1 + access_count * 0.1) when read.
- Storage.recordAccess increments access_count and recomputes weight in
  a single transactional UPDATE.
- Storage.pruneLowDecay (and matching colonyq memory prune --min-weight
  / --dry-run) removes near-zero-weight medium/low rows; critical/high
  are never affected.
- search and get_observations MCP responses include importance + weight
  (additive — older callers ignore them).
- task_post MCP tool accepts optional importance.

Write-amplification strategy: (b) debounced 50ms batch. The MemoryStore
read paths queue returned ids into a Set, start a single setTimeout, and
flush one transactional UPDATE. This guarantees every access is counted
(unlike threshold-sampling) while keeping write amplification at most
one extra UPDATE per ~50ms hot loop and making `close()` deterministic
by flushing pending bookkeeping before the SQLite handle goes away.

Schema bump 14 -> 15. Migration is forward-only via COLUMN_MIGRATIONS so
existing DBs upgrade in place.
Adds the cross-agent "AI predicted X, real answer was Y" feedback lane
specified in docs/icm-integration-plan.md slice 2 so a future agent can
search prior corrections by topic before repeating a known mistake.

- packages/storage migration 015 introduces `feedback` + a porter-
  unicode61 `feedback_fts` virtual table with the standard ai/ad/au
  triggers, plus indexes on `topic` and `created_at`. Importance is a
  four-level CHECK enum defaulting to `medium`.
- packages/core MemoryStore exposes `recordFeedback`, `searchFeedback`,
  `getFeedback`, and `feedbackStats`. All three prose fields
  (prediction/correction/context) route through `prepareMemoryText`,
  the same redact-then-compress path observations use, so the
  compression invariant holds at the write boundary.
- apps/mcp-server registers `feedback_record`, `feedback_search`, and
  `feedback_stats` with progressive-disclosure return shapes (id,
  topic, importance, FTS5 score, snippet, created_at). docs/mcp.md
  carries the contract.

Follow-up out of scope here: a pre-tool-use hook that auto-surfaces
prior corrections on inbound prompts. Tracking separately so this
slice can ship behind a manual query surface first.
Brings the slice 1 memoirs work (originally drafted on the local main
checkout) onto agent/claude/icm-slice-3-importance-decay so all three
ICM slices ship as one integrated branch:

- 015-icm-memoirs.ts        — typed knowledge graphs (slice 1)
- 016-icm-feedback.ts       — feedback record/search/stats (slice 2, was 015)
- 017-icm-importance-decay.ts — importance + decay (slice 3, was 015)

Schema bump 14 → 17. SCHEMA_SQL keeps every ICM table as
`CREATE TABLE IF NOT EXISTS` (fresh DBs) and existing DBs continue to
pick up slice 3 columns via COLUMN_MIGRATIONS. The migration files are
canonical references (the runtime path stays SCHEMA_SQL + COLUMN_MIGRATIONS).

Memoirs:
- `memoirs`, `memoir_concepts`, `memoir_relations` tables; FTS5 mirror
  over name/content/labels with the standard `(ai, ad, au)` triggers.
- Nine relation types (part_of/depends_on/related_to/contradicts/refines/
  alternative_to/caused_by/instance_of/superseded_by). Self-loops
  forbidden; `(source, target, relation)` is unique.
- `MemoryStore.{createMemoir, addConcept, refineConcept, linkConcepts,
  searchConcepts, inspectConcept, listMemoirs}` — content flows through
  `prepareMemoryText` so the compression invariant matches observations.
- Seven MCP tools (`memoir_create / list / add_concept / refine / link /
  search / inspect`) — progressive disclosure: `memoir_search` returns
  compact hits, `memoir_inspect` returns body + BFS neighbourhood.

Verified:
- pnpm typecheck — green across all 17 workspaces.
- pnpm --filter @colony/storage test — 193 passed, 1 skipped (perf,
  via COLONY_SKIP_PERF=1; flaky on local disk per test comment).
- pnpm --filter @colony/mcp-server test — 302 passed.
- pnpm --filter @colony/core test — 259 passed; 1 pre-existing failure
  in test/savings-reference.test.ts (also fails on 2d831bb baseline,
  unrelated to ICM).
@NagyVikt NagyVikt changed the title feat(memory): ICM slice 3 — importance + temporal decay feat(memory): ICM slices 1-3 — memoirs + feedback + importance/decay May 18, 2026
…e-3-importance-decay

# Conflicts:
#	docs/mcp.md
#	packages/core/src/memory-store.ts
#	packages/storage/src/schema.ts
#	packages/storage/src/storage.ts
@NagyVikt NagyVikt merged commit 0950b42 into main May 20, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant