fix(knowledge_graph): backfill NULL metadata on duplicate add_triple - #1139
fix(knowledge_graph): backfill NULL metadata on duplicate add_triple#1139arnoldwender wants to merge 2 commits into
Conversation
5ee15fb to
b04ec5b
Compare
5508e7b to
441c936
Compare
|
Rebased on Conflict resolution: the test class Ran |
|
Friendly ping — post-3.3.6 release this is still mergeable on |
441c936 to
6eb3b3c
Compare
|
Friendly ping — post-v3.5.0 status: |
6eb3b3c to
c24a5d5
Compare
|
Rebased onto current Re-verified that the bug is still live on if existing:
return existing["id"] # Already exists and still validso a second CI is running fresh against the current base. Happy to split, narrow, or adjust the confidence rule if you'd prefer it stay untouched. |
When add_triple is called a second time with the same (subject, predicate,
object) and valid_to IS NULL, the existing id was returned and every new
metadata value — valid_from, source_closet, source_file, source_drawer_id,
adapter_name, confidence — was silently dropped.
This is the exact 'a later adapter refines what an earlier one did not know'
pattern the RFC 002 §5.5 provenance fields were designed to support, and it
breaks the Incremental-only principle (CLAUDE.md): a second pass with better
information should be additive, never a silent no-op.
Policy on re-adding an existing open triple:
- backfill NULL fields when a new non-NULL value is provided
- never overwrite an already-populated field — explicit invalidate() is
the only path to change settled values
- confidence is the one exception: a strictly higher value replaces a
weaker one so stronger evidence wins (weaker evidence is ignored)
- the returned id is unchanged — idempotent contract preserved
Four regression tests cover the four cases: valid_from backfill, provenance
backfill, refusal to overwrite existing metadata, and monotonic confidence.
CONTRIBUTING.md line 73 calls out knowledge_graph.py coverage as an explicit
maintainer ask.
c24a5d5 to
b3208a2
Compare
|
Rebased onto current Re-verified the bug is still live rather than just re-pinging: Falsifier re-run on the rebased branch: reverting only the production hunk and keeping the tests turns 3 of the 4 new tests RED ( Full suite green locally on the rebased branch: 4472 passed, 31 skipped, ruff 0.16.1 clean. |
|
@igorls — friendly ping on this one. It is the last of my April cohort still open; #1104, #2194, #2208 and #1140 all landed over the last two weeks, so I suspect this one just fell through the cracks rather than being held back. Current state after today's rebase onto No rush and no hard feelings if the answer is "not this shape" — happy to split it, shrink it, or close it if it is not worth the review slot. |
What and Why
When
add_triple()is called a second time with the same(subject, predicate, object)and the existing triple is still open (valid_to IS NULL), every new metadata value —valid_from,source_closet,source_file,source_drawer_id,adapter_name,confidence— was silently dropped:This breaks the Incremental-only principle from CLAUDE.md: a later pass with better information should be additive, never a silent no-op. It is the exact "a later adapter refines what an earlier one did not know" pattern the RFC 002 §5.5 provenance fields (
source_drawer_id,adapter_name) were designed to support.CONTRIBUTING.md explicitly calls out
knowledge_graph.pyas an area where coverage is wanted.Root Cause
mempalace/knowledge_graph.py:189-196Change Summary
Policy for re-adding an existing open triple — preserves idempotency, adds provenance plumbing:
invalidate()remains the only path to change settled valuesReproduction
Before this PR:
[None]After this PR:
['2015-04-01']Test Plan
Four regression tests covering every branch of the new policy:
test_duplicate_add_backfills_null_valid_from— NULL date gets filledtest_duplicate_add_backfills_null_provenance— RFC 002 provenance fields roundtriptest_duplicate_add_does_not_overwrite_existing_metadata— locks the "never clobber" ruletest_duplicate_add_upgrades_confidence_only_when_higher— stronger evidence wins, weaker is ignoredtest_duplicate_triple_returns_existing_idandtest_invalidated_triple_allows_re_addstill pass — idempotent contract intactpytest tests/ --ignore=tests/benchmarks— 1070/1070 pass, no downstream regressionsruff check/ruff format --check— cleanOut of scope
Audit surfaced two adjacent correctness concerns I left for follow-up PRs to keep this one surgical:
valid_from <= valid_toon inputquery_*(as_of=...)(e.g.'2025-10-01'vs'2025')Happy to file them separately if this direction is welcome.