Skip to content

feat(agentcore): idempotent sqlite -> agentcore migration (agentcore migrate)#80

Merged
emp3thy merged 2 commits into
mainfrom
feat/agentcore-sqlite-migration
Jul 13, 2026
Merged

feat(agentcore): idempotent sqlite -> agentcore migration (agentcore migrate)#80
emp3thy merged 2 commits into
mainfrom
feat/agentcore-sqlite-migration

Conversation

@emp3thy

@emp3thy emp3thy commented Jul 13, 2026

Copy link
Copy Markdown
Owner

What

Adds better-memory agentcore migrate — a first-class, idempotent migration of local SQLite reflections + semantic memories (with full usage metadata) into AWS Bedrock AgentCore, so a user on the default sqlite backend can switch to agentcore without losing curated knowledge or its useful_count/times_misled/times_overlooked/polarity/status/confidence.

Why it was non-trivial — validated live against real AWS

Three live probes (throwaway memories, torn down) overturned the naive design:

  • Client-authored records into the episodic reflections namespace silently drop the entire custom metadata map (schema-gated: episodic declares its schema under reflectionConfiguration, which governs only AWS-extracted records). The JSON content body, however, round-trips and content-body updates persist.
  • userPreference (semantic) declares a top-level memoryRecordSchema that DOES govern client writes — declared keys are retained, updatable, and listable; undeclared keys drop.

Resulting hybrid architecture

  • Reflections: ALL state (polarity, status, counters, source_row_id, evidence_count, updated_at) lives in the content body. _parse_reflection_record reads body-first with metadata fallback, so AWS-extracted records are unchanged and migrated ones resolve from the body.
  • Reflection rating on migrated records rewrites the body (metadata writes are a no-op on client records); extracted records keep the metadata path.
  • Semantic: source_row_id is declared in the userPreference schema; content stays raw text.

Feature

  • better_memory/storage/agentcore_migrate.py: pure record builders, deterministic requestIdentifier (bm-{kind}-{sqlite_id}), canonical content-hash, batching, and an agentcore_migration ledger in the source db (create/update/skip/retire) for resumable idempotency + client-side reconcile-by-source_row_id.
  • CLI flags: --dry-run --include --restart --provision --db --project --region --home --batch-size --verify. No-rollback partial-failure; single-run advisory lock; exit 0/2/1. Does not touch settings.json (activation stays a separate, deliberate step).
  • Read-parity fix: semantic_list returns SemanticMemory-shaped objects so relevant.py injection works in agentcore mode.

Testing

  • Hermetic unit (builders / ledger / body-first reader / rating parity) + T2 fake-endpoint integration modelling the live-proven metadata-drop / body-persist behaviour.
  • Full suite: 1482 passed, 22 skipped, 0 failures; type-clean (pyright).
  • Live end-to-end against real AWS (throwaway memory, torn down): a fixture spanning both polarities/scopes/statuses migrated and read back through the shipped agentcore reader byte-identical to sqlite's own retrieve() on all three buckets + semantic; idempotency held across initial + rerun + reconcile-after-ledger-loss (final record count == seeded count, zero duplicates).

Built by agent teams (design scouts → build with per-task spec + code-quality review + fix loop → live validation). Design doc: docs/superpowers/specs/2026-07-13-sqlite-agentcore-migration-design.md.

🤖 Generated with Claude Code

emp3thy and others added 2 commits July 13, 2026 20:12
… migrate`)

Add `better-memory agentcore migrate`, a first-class, idempotent migration of
local SQLite reflections + semantic memories into AWS Bedrock AgentCore, so a
user on the default sqlite backend can switch to agentcore without losing
curated knowledge or its usage metadata.

Architecture validated live against real AWS (throwaway memories, torn down):
- AWS silently drops the custom metadata map on client-authored BASE records in
  the episodic reflections namespace (schema-gated: episodic declares its schema
  under reflectionConfiguration, which governs only extraction), but the JSON
  content body round-trips and content-body updates persist. So ALL migrated
  reflection state (polarity, status, counters, source_row_id, updated_at) lives
  in the content body; `_parse_reflection_record` reads body-first with metadata
  fallback (AWS-extracted records unchanged).
- userPreference (semantic) DOES honour a top-level declared memoryRecordSchema,
  so semantic state lives in declared metadata (content stays raw text); the
  migration idempotency key source_row_id is declared in that schema.

Feature:
- Writer + local ledger (better_memory/storage/agentcore_migrate.py): pure
  record builders, deterministic requestIdentifier (bm-{kind}-{sqlite_id}),
  canonical content_hash, batching, and an agentcore_migration ledger in the
  source db (create/update/skip/retire decisions) for resumable idempotency.
- CLI: --dry-run, --include, --restart, --provision, --db, --project, --region,
  --home, --batch-size, --verify; no-rollback partial-failure handling; single
  -run advisory lock; exit 0 converged / 2 partial / 1 fatal. Does not touch
  settings.json (activation stays a separate, deliberate step).
- Read parity: rating on migrated reflections rewrites the content body (metadata
  writes are a no-op on client records); semantic_list returns SemanticMemory
  -shaped objects so relevant.py injection works in agentcore mode.
- Client-side reconcile-by-source_row_id rebuilds ledger state after loss.

Tests: hermetic unit (builders/ledger/reader/rating parity) + T2 fake-endpoint
integration modelling the live-proven metadata-drop / body-persist behavior.
Full suite: 1482 passed, 22 skipped, 0 failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pyright

build_reflection_record returns dict|None (None for retired/superseded rows);
CI pyright checks test files, so active-row call sites that subscript the
result need narrowing. Add a _rr() helper / inline asserts. No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@emp3thy
emp3thy merged commit 2f31ee4 into main Jul 13, 2026
3 checks passed
@emp3thy
emp3thy deleted the feat/agentcore-sqlite-migration branch July 13, 2026 20:15
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