A merged entity comes back before the merge - #337
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: WaylandYang <wayland0916@gmail.com>
WaylandYang
added a commit
that referenced
this pull request
Sep 5, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: WaylandYang <wayland0916@gmail.com>
This was referenced Sep 5, 2026
WaylandYang
added a commit
that referenced
this pull request
Sep 7, 2026
Entity merges had a clock since #337 but no reader: opening an entity showed the facts a merge invalidated, never the merge. `entity_history` now carries merged_in / merged_away / merge_reverted, with the other entity by name and the revert attributed from the audit trail rather than borrowed from the merge row. Signed-off-by: WaylandYang <wayland0916@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #336. The second cut of 0019, after #317 did facts and derivations.
Entities had no clock of their own:
merged_intosays a merge happened, not when. So two entities merged in March were still one node at anas_ofin February, and the facts that moved with the merge showed up under a name that did not hold them yet.Where the clock actually is
entity_mergeshas carriedcreated_atandreverted_atsince 0005 — the same shaperecord_axisreads everywhere else, so the predicate joins the others rather than starting a new idea:merge_in_effect_at— this merge was in force at Tentity_visible_at— the entity existed at T and no merge in force at T had swallowed it. This replacesmerged_into IS NULLon the read paths; with a NULL parameter the two are the same answer, so nothing about "now" changes.Which entity a fact belonged to
A merge rewrites rows in place —
UPDATE facts SET subject_id = target— so the row cannot say who held it before. The ids that moved are recorded, though, sofact_owner_at(migration0027) reads them back:revert_mergealready moved those rows back, and undoing them twice would put a fact on an entity it never sat on.It is a SQL function rather than a CTE per read site for the reason 0019 gives about spread-out defences, and it is the same choice
fact_surface_predicatealready made. The mapping is only applied when a time is actually given: wrappingsubject_idin a function costs the index, and "now" is the path every draw takes.What rewinds now
Node visibility and the node total in
overview, the BFS that finds neighbours, node degree, the edges themselves, and the entity panel's facts. Facts a merge invalidated on the way (self-loops, reconciliation) needed nothing — they carryinvalidated_at, so #317's predicate already brings them back.Out of scope, as the issue says: the target's own type and profile at T, which
entity_mergesalso records.Verified
a_merge_rewinds_with_the_second_clock.rs, database-backed, walks one base through four instants: now (B is merged away, C is back because its merge was reverted), mid-March (all three stand, each holding its own fact — the row says A, the ledger says B), mid-May (inside the reverted merge's window, so C is folded in and A holds all three facts), and January (the entities did not exist yet, so the graph is empty rather than falling back to now). Checked against the previous implementation: it fails there, on the first assertion that a merged entity reappears.One older fixture changed with it.
the_second_clock_can_be_rewound.rsrecorded its facts in March but created its entities atnow()— a ledger that cannot exist, and one that this change now notices, since an entity that did not exist at T is no longer drawn. Its entities are backdated to January.Full
utopia-storesuite (42 binaries) green against a freshly migrated database withUTOPIA_TEST_REQUIRE_DB=1, 144 server unit tests green,cargo fmt --checkandcargo clippy --all-targetsclean.🤖 Generated with Claude Code