Skip to content

feat(memory): add durable memory store#181

Merged
hadamrd merged 4 commits into
trunkfrom
loop/171-feat-memory-add-durable-memory-store-wit
Jun 2, 2026
Merged

feat(memory): add durable memory store#181
hadamrd merged 4 commits into
trunkfrom
loop/171-feat-memory-add-durable-memory-store-wit

Conversation

@hadamrd
Copy link
Copy Markdown
Owner

@hadamrd hadamrd commented Jun 2, 2026

Fixes #171

Summary

  • Add a SQLite-backed curated memory store for semantic, episodic, and procedural memory items.
  • Persist provenance including source event refs, source task refs, confidence, created timestamps, evidence refs, and supersession links.
  • Add active memory and rejected-path queries; superseded items are excluded from boot-style active results but remain inspectable.
  • Add a MemoryStore Protocol plus FakeMemoryStore contract coverage for tests.
  • Tighten provenance validation for non-empty authorship.

Acceptance Criteria

  • Active semantic/episodic/procedural items round-trip after reopening the store: covered by tests/test_memory_store.py.
  • Superseding a memory item removes it from active results but leaves it inspectable: covered by tests/test_memory_store.py.
  • Rejected-path query returns only active rejected-path tagged items: covered by tests/test_memory_store.py.
  • Invalid confidence/provenance is rejected using model constraints: covered by tests/test_memory_store.py.
  • Curator promotion policy does not promote empty reason-to-remember candidates: covered by tests/test_memory_store.py.

Verification

  • lumen: discovery skipped (mcp__lumen__semantic_search unavailable in this session)
  • env -u VIRTUAL_ENV uv run --extra dev pytest tests/test_memory_store.py -q
  • env -u VIRTUAL_ENV uv run --extra dev pytest tests/test_eventlog_sqlite.py -q
  • env -u VIRTUAL_ENV uv run --extra dev ruff check src/forge_loop/memory/init.py src/forge_loop/memory/models.py src/forge_loop/memory/store.py src/forge_loop/_testing/memory_store.py tests/test_memory_store.py
  • env -u VIRTUAL_ENV uv run --extra dev ruff format --check src/forge_loop/memory/init.py src/forge_loop/memory/models.py src/forge_loop/memory/store.py src/forge_loop/_testing/memory_store.py tests/test_memory_store.py
  • env -u VIRTUAL_ENV uv run --extra dev mypy src/forge_loop/memory src/forge_loop/_testing/memory_store.py tests/test_memory_store.py
  • env -u VIRTUAL_ENV uv run --extra dev pyright src/forge_loop/memory src/forge_loop/_testing/memory_store.py tests/test_memory_store.py

Implement a small SQLite-backed curated memory store for #171 so semantic, episodic, procedural, and rejected-path memories survive process restarts with provenance and supersession state. This keeps boot context durable without storing raw transcripts.
@hadamrd hadamrd added the critic:blocking Critic found blocking issues label Jun 2, 2026
Copy link
Copy Markdown
Owner Author

@hadamrd hadamrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sev2/product] The PR exports a new MemoryStore protocol and adds a FakeMemoryStore contract surface, but the repo has no downstream consumer wired to the store or protocol. Under the no-scaffold-theatre rule, wire this into the maestro/boot memory-loading path in this PR, or quarantine/remove the plug-in surface behind an experimental extra until it has an in-repo consumer.

"""


class MemoryStore(Protocol):
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sev2/product] The PR exports a new MemoryStore protocol and adds a FakeMemoryStore contract surface, but the repo has no downstream consumer wired to the store or protocol. Under the no-scaffold-theatre rule, wire this into the maestro/boot memory-loading path in this PR, or quarantine/remove the plug-in surface behind an experimental extra until it has an in-repo consumer.

Copy link
Copy Markdown
Owner Author

@hadamrd hadamrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sev2/tests] SqliteMemoryStore.supersede adds a public failure path for missing memory IDs, but the new tests only cover successful supersession. Add an adversarial test that asserts supersede('missing', by_memory_id=...) raises KeyError for the real store and fake, so this public error contract is locked down.

def list_rejected_paths(self) -> tuple[MemoryItem, ...]:
return tuple(item for item in self.list_active() if REJECTED_PATH_TAG in item.tags)

def supersede(self, memory_id: str, *, by_memory_id: str) -> MemoryItem:
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sev2/tests] SqliteMemoryStore.supersede adds a public failure path for missing memory IDs, but the new tests only cover successful supersession. Add an adversarial test that asserts supersede('missing', by_memory_id=...) raises KeyError for the real store and fake, so this public error contract is locked down.

Copy link
Copy Markdown
Owner Author

@hadamrd hadamrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manifesto violations:

  • [sev2] testing.md#TE-002+ def supersede(self, memory_id: str, *, by_memory_id: str) -> MemoryItem: → Add a sad-path test for the new public supersede API, e.g. assert SqliteMemoryStore(...).supersede('missing', by_memory_id='mem-new') raises KeyError, and include the fake store in the same contract check.

MAJDOUB Khalid added 3 commits June 2, 2026 23:53
Address #181 critic feedback by connecting the durable memory store to the curator/status path, reporting real memory health from .forge/memory.db, and covering missing supersession failures. Also create parent directories for the SQLite memory DB.
Ensure superseded memory cannot point at a missing replacement id. Cover both missing source and missing replacement contracts for the real SQLite store and fake store.
Require every MemoryProvenance to carry either an event ref or a task ref, update status fixtures to use valid task provenance, and add negative coverage for missing source provenance.
@hadamrd hadamrd removed the critic:blocking Critic found blocking issues label Jun 2, 2026
@hadamrd
Copy link
Copy Markdown
Owner Author

hadamrd commented Jun 2, 2026

Fresh critic rerun on head 2404c34 after the memory-store fixes found no sev1/sev2 blocker. Local verification: tests/test_memory_store.py, tests/test_cli_status_control_plane.py::TestStatusControlPlane + tests/test_eventlog_sqlite.py, scoped ruff check/format, focused mypy, and pyright. Replay CI is green.

@hadamrd hadamrd merged commit 4a28569 into trunk Jun 2, 2026
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.

feat(memory): add durable memory store with provenance and supersession

1 participant