Skip to content

DB-first note writes omit observations and relations until a later file re-index #1076

Description

@phernandez

Summary

Cloud-hosted write_note calls can successfully persist typed Markdown and return 202 while leaving the observation and relation tables empty. Graph rows appear only if a later index_file job happens to re-parse the materialized object. This makes schema inference and relation traversal nondeterministic after successful DB-first writes.

Classification: Basic Memory persistence/indexing regression. The incomplete accepted-write path is in basic_memory.indexing.accepted_note_write_runner, not Cloud MCP routing.

Development acceptance evidence

Environment: Basic Memory Cloud development

  • Cloud revision: 4aa58632c32999b088efcfe61911c053b7a4ad89
  • Basic Memory revision: c616148fb0b30f1dfe492e273ec43706c0326759
  • Tenant: a5a774be-50e0-4c50-811d-42a2525faebc
  • Project external ID: d71bee1e-69a4-4842-90de-fd93aa779a47
  • Project DB ID: 3

The acceptance run wrote three notes with note_type=dev_accept_person:

## Observations

- [name] Ada Acceptance
- [role] Engineer

## Relations

- works_at [[XSYS Target 20260716T1110Z]]

All three calls succeeded. note_content.markdown_content contains the exact [name], [role], and works_at Markdown, checksums match, and Tigris origin objects contain the same data. search_notes(note_types=["dev_accept_person"]) returns all three notes.

However, Neon shows no graph rows for entity IDs 13, 14, and 15:

  • observation: zero rows
  • relation: zero rows

schema_infer(note_type="dev_accept_person", threshold=0.25) therefore returns:

{"error":"No schema pattern found for 'dev_accept_person' (threshold: 25%)"}

A control note in the same project did acquire observations/relations, but only after Tigris notification enqueued index_file job trace 019f6aa0ae27f04fb3ea107af903cc84. The typed-note materialization jobs completed successfully but did not produce equivalent index_file jobs.

Root cause

run_accepted_note_create() prepares parsed Markdown, then create_accepted_pending_entity() writes only scalar entity fields. persist_accepted_note_write() writes note_content and search state. Neither persists the parsed observations or relations that are already present in the prepared write.

The Cloud DB-first path then materializes Tigris asynchronously. Graph correctness is accidentally dependent on a later object notification and index_file pass. Notifications are not a valid commit path for graph rows and may also no-op against matching accepted checksums.

Relevant code:

  • src/basic_memory/indexing/accepted_note_mutation_runner.py::_run_accepted_note_create
  • src/basic_memory/indexing/accepted_note_write_runner.py::create_accepted_pending_entity
  • accepted update/edit paths should be audited for the same omission

Expected behavior

A successful accepted create/update/edit transaction atomically persists:

  • entity scalar fields
  • note content
  • observations
  • relations (with unresolved targets allowed)
  • search state

Tigris materialization and object notifications should reconcile file state, not be required to make the knowledge graph correct.

Suggested fix

Extend the accepted mutation write transaction to replace observations and relations from the already parsed prepared Markdown before commit. Reuse the existing repository semantics used by EntityService.update_entity_and_observations() and update_entity_relations(), adapted to the accepted-write dependency protocols.

Add regression coverage for:

  1. create typed note -> observations and unresolved/resolved relations exist immediately;
  2. update/edit -> old graph rows are replaced atomically;
  3. schema inference succeeds immediately after typed MCP writes without any storage notification;
  4. relation traversal succeeds immediately after write_note;
  5. materialization or duplicate index_file does not duplicate/remove graph rows;
  6. missing/delayed Tigris notification does not affect DB graph correctness.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions