Skip to content

feat(palace): promote tags/trust/access_count/last_accessed/reinforcements/superseded_by/active to first-class Drawer fields (mp-migration 7/8)#17

Closed
quangdang46 wants to merge 1 commit into
mainfrom
feat/mp-pr7-drawer-fields
Closed

feat(palace): promote tags/trust/access_count/last_accessed/reinforcements/superseded_by/active to first-class Drawer fields (mp-migration 7/8)#17
quangdang46 wants to merge 1 commit into
mainfrom
feat/mp-pr7-drawer-fields

Conversation

@quangdang46
Copy link
Copy Markdown
Owner

Summary

Promote 7 fields previously stored only in Drawer::metadata to first-class typed fields:

Field Type Replaces metadata key
tags Vec<String> "tags"
trust Option<String> "trust"
access_count u64 "access_count"
last_accessed Option<DateTime<Utc>> "last_accessed"
reinforcements Vec<Reinforcement> (new pub struct) "reinforcements"
superseded_by Option<DrawerId> "superseded_by"
active bool (default true) "active"

Motivation

The upcoming MemoryProvider trait methods (PRs 1 + 2 of the migration series) need typed access to these fields:

  • boost(&id, amount) / decay(&id, amount) → mutate access_count + last_accessed
  • reinforce(&id, session_id, message_index) → push to reinforcements
  • supersede(&old, &new) → set superseded_by on old, active = false on old
  • tag(&id, tag) / untag(&id, tag) → mutate tags
  • link(&from, &to, weight) → doesn't touch these directly but uses same lifecycle

Without typed fields, every trait method would have to do metadata.insert("access_count", json!(...)) round-trips, which is lossy (no type checking), error-prone (typos in key strings), and harder to test.

This is PR 7/8 in the jcode → mempalace Mode C library migration series.

Change

crates/core/src/palace.rs:

  • Added 7 fields to Drawer struct, each with #[serde(default, skip_serializing_if = …)] for backwards compat
  • New pub struct Reinforcement { session_id, message_index, timestamp }
  • Drawer::new initializes all 7 new fields to defaults
  • New builder methods: Drawer::tags(Vec<String>) and Drawer::trust(&str)
  • New Drawer::migrate_metadata(&mut self) — idempotent, lifts legacy metadata keys to typed fields and cleans the HashMap

crates/core/src/palace/store/usearch_sqlite.rs:

  • 2 Drawer { … } literals updated to initialize the 7 new fields (the only pre-existing call sites in the codebase)

crates/core/src/layers.rs:

  • 1 Drawer { … } literal updated to initialize the 7 new fields

Tests (crates/core/src/palace.rs:tests):

  • drawer_new_field_defaults — all 7 default to expected values
  • drawer_typed_builders.tags([...]).trust("high") work
  • drawer_legacy_serde_load — old JSON with metadata-only fields loads cleanly
  • drawer_migrate_metadata — legacy keys lifted to typed fields
  • drawer_migrate_metadata_idempotent — safe to call twice
  • drawer_new_field_serde_roundtrip — typed fields survive serde

Backwards compatibility

Drawers serialised before this change continue to load:

  • tags/trust/etc. are absent from the JSON → #[serde(default)] fills them
  • The legacy metadata["tags"] value remains in the HashMap until migrate_metadata is called
  • No migration is forced on read — call sites that don't care about the new fields work unchanged

Test plan

  • cargo test -p mempalace-core --lib1133 passed, 0 failed (1499s, includes 7 new tests)
  • cargo check -p mempalace-core clean
  • cargo fmt --check -p mempalace-core clean
  • CI green on ubuntu/macos/windows

Series

# Title Branch Status
6 docs(palace): document Embedder reuse on search_with_embedding docs/mp-pr6-document-embedder-reuse merged #16
7 feat(palace): add tags/trust/access_count/reinforcements/superseded_by to Drawer feat/mp-pr7-drawer-fields this PR
8 feat(palace): add MemoryProvider::graph_stats_legacy (jcode shape) feat/mp-pr8-graph-stats-legacy next
1 feat(palace): add boost/decay/reinforce/supersede/set_metadata to MemoryProvider feat/mp-pr1-mutation-methods next
2 feat(palace): add tag/untag/link/list_tags to MemoryProvider feat/mp-pr2-tag-link next
4 feat(palace): add MemoryProvider::recent for retention-ranked recall feat/mp-pr4-recent next
3 feat(palace): add MemoryScope::All and Wing/Room variants feat/mp-pr3-scope-variants next
5 feat(palace): add ActivityEvent sink on PalaceBuilder feat/mp-pr5-activity-sink next

🤖 Generated with Claude Code

…ments/superseded_by/active to first-class Drawer fields

The new fields mirror values previously stored only in the
`metadata` HashMap under the keys 'tags', 'trust', 'access_count',
'last_accessed', 'reinforcements', 'superseded_by', 'active'. They
are now typed first-class fields for:

  - type-safe access from the upcoming MemoryProvider trait methods
    (boost/decay/reinforce/supersede/tag/link in mp-migration 1+2/8)
  - clean serde round-trip without manual value extraction
  - query-builder support (drawer.tags([...]).trust('high'))

`#[serde(default, skip_serializing_if = ...)]` on every new field
keeps backwards compatibility — drawers serialised before this change
still load cleanly. The reverse direction (writing the typed field)
is handled by `Drawer::migrate_metadata` which is idempotent and
safe to call repeatedly; it lifts legacy metadata keys into typed
fields when those fields are still at their default value, and
cleans the metadata HashMap so the source-of-truth is unambiguous.

`Reinforcement` is promoted to a pub struct in palace.rs (mirrors
jcode's `memory_types::Reinforcement`). `DrawerId` is reused for
`superseded_by` to keep the ID type consistent across the API.

Existing call sites in:
  - crates/core/src/palace/store/usearch_sqlite.rs (Drawer literal x2)
  - crates/core/src/layers.rs:652 (Drawer literal x1)
updated to initialise the new fields with their defaults.

This is PR 7/8 in the jcode → mempalace Mode C library migration
series. Required for PR 1 (mutation methods) and PR 2 (tag/link).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@quangdang46
Copy link
Copy Markdown
Owner Author

Code already merged via PR #24 (cherry-picked). Closing.

@quangdang46 quangdang46 closed this Jun 3, 2026
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