-
-
Notifications
You must be signed in to change notification settings - Fork 19
Pr 24 fix #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pr 24 fix #29
Changes from all commits
f5b7605
d0cd06c
13ed1f5
20f281c
63dee5b
8dafb6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,17 @@ | ||
| """Ensure the repo root is importable when running ``python -m pytest`` from anywhere.""" | ||
| import os | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| ROOT = Path(__file__).resolve().parent | ||
| if str(ROOT) not in sys.path: | ||
| sys.path.insert(0, str(ROOT)) | ||
|
|
||
| # The update reminder reaches out to the network to look up the newest release. Keep the | ||
| # offline test suite network-inert by default; tests that exercise the feature opt back in | ||
| # explicitly via monkeypatch (see tests/test_update_check.py). | ||
| os.environ.setdefault("ENGRAPHIS_UPDATE_CHECK", "0") | ||
|
|
||
| # The legacy scripts/test_*.py files are HTTP smoke tests (need a running server + | ||
| # httpx), not unit tests. Keep pytest focused on the tests/ suite. | ||
| collect_ignore_glob = ["scripts/*"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2307,14 +2307,44 @@ def _new_repo(old_repo_id): | |
| ) | ||
|
|
||
| # 3) Edges: relabel workspace/repo, remapping any entity ids folded in step 2. | ||
| # When a remapped source edge would collide with an existing live edge | ||
| # on the unique index (workspace_id, [repo_id,] src, dst, relation, layer), | ||
| # merge its evidence rows into the survivor and drop the duplicate. | ||
| src_edges = [dict(x) for x in c.execute( | ||
| "SELECT id, repo_id, src, dst FROM edges WHERE workspace_id=?", (wid_src,))] | ||
| "SELECT id, repo_id, src, dst, relation, layer, valid_to, expired_at " | ||
| "FROM edges WHERE workspace_id=?", (wid_src,))] | ||
| for ed in src_edges: | ||
| new_src = entity_remap.get(ed["src"], ed["src"]) | ||
| new_dst = entity_remap.get(ed["dst"], ed["dst"]) | ||
| new_repo = _new_repo(ed["repo_id"]) | ||
| is_live = ed["valid_to"] is None and ed["expired_at"] is None | ||
| if is_live: | ||
| if new_repo is None: | ||
| collision = c.execute( | ||
| "SELECT id FROM edges WHERE workspace_id=? AND repo_id IS NULL " | ||
| "AND src=? AND dst=? AND relation=? AND layer=? " | ||
| "AND valid_to IS NULL AND expired_at IS NULL LIMIT 1", | ||
| (wid_dst, new_src, new_dst, ed["relation"], ed["layer"]), | ||
| ).fetchone() | ||
| else: | ||
| collision = c.execute( | ||
| "SELECT id FROM edges WHERE workspace_id=? AND repo_id=? " | ||
| "AND src=? AND dst=? AND relation=? AND layer=? " | ||
| "AND valid_to IS NULL AND expired_at IS NULL LIMIT 1", | ||
| (wid_dst, new_repo, new_src, new_dst, ed["relation"], ed["layer"]), | ||
| ).fetchone() | ||
| if collision: | ||
| # Merge evidence: re-point supports, skip duplicates. | ||
| c.execute( | ||
| "UPDATE OR IGNORE edge_supports SET edge_id=? WHERE edge_id=?", | ||
| (collision["id"], ed["id"]), | ||
| ) | ||
|
Comment on lines
+2337
to
+2341
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When both colliding edges have a live support with the same Useful? React with 👍 / 👎. |
||
| c.execute("DELETE FROM edge_supports WHERE edge_id=?", (ed["id"],)) | ||
| c.execute("DELETE FROM edges WHERE id=?", (ed["id"],)) | ||
|
Comment on lines
+2338
to
+2343
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a source edge collides with a target edge during a normal workspace merge, this moves the normalized Useful? React with 👍 / 👎.
Comment on lines
+2342
to
+2343
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For a collision between otherwise identical live edges, the source row is deleted without incorporating its Useful? React with 👍 / 👎. |
||
| continue | ||
| c.execute( | ||
| "UPDATE edges SET workspace_id=?, repo_id=?, src=?, dst=? WHERE id=?", | ||
| (wid_dst, _new_repo(ed["repo_id"]), | ||
| entity_remap.get(ed["src"], ed["src"]), entity_remap.get(ed["dst"], ed["dst"]), | ||
| ed["id"])) | ||
| (wid_dst, new_repo, new_src, new_dst, ed["id"])) | ||
|
|
||
| # 4) Memories / sessions / events: relabel workspace/repo per distinct repo_id | ||
| # bucket (ids, content and history are untouched). | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For symmetric relations such as
related, source and destination edges are canonicalized by their original entity IDs inStore.upsert_edge(). If the source IDs sort in the opposite order from the target IDs, remapping produces the reverse(src, dst)pair here, so the collision query misses the target edge and the merge leaves two live copies of the same undirected relation. This occurs for overlapping entities with independently generated IDs and persists until a later process startup happens to run_deduplicate_live_edges().Useful? React with 👍 / 👎.