From 74abbfa30c7750cd5f2d67ca0d958a78e68dcda4 Mon Sep 17 00:00:00 2001 From: safishamsi Date: Tue, 18 Aug 2026 15:59:23 +0100 Subject: [PATCH] test(build): assert legacy-confidence normalization is idempotent; open 0.9.47 Adds the round-trip idempotency check the deep-dive flagged: after the first load heals a legacy numeric confidence to INFERRED, reloading the persisted graph stays silent with a stable score (the warning must not just move one run later). Dates 0.9.46, opens 0.9.47. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 6 +++++- tests/test_build.py | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a053e4e2..3f117e71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ Full release notes with details on each version: [GitHub Releases](https://github.com/safishamsi/graphify/releases) -## 0.9.46 (unreleased) +## 0.9.47 (unreleased) + +- Fix: a legacy `graph.json` that stored a numeric edge `confidence` (from a pre-enum version) no longer warns once per edge on every incremental reload; the numeric value is normalized to the `INFERRED` tag with the original float preserved in `confidence_score` (thanks @Trantor-develops). + +## 0.9.46 (2026-08-17) - Fix: node-id normalization is now caseless-stable for combining-mark sequences — `casefold` and NFKC don't commute, so a single pass left `normalize_id(s) != normalize_id(s.casefold())` for inputs like Greek ypogegrammeni followed by a combining accent; normalization now iterates casefold+NFKC to a fixpoint. Letter/digit-bearing ids are unchanged, so existing graphs are not re-keyed. diff --git a/tests/test_build.py b/tests/test_build.py index b4c1dd14..b376b173 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -228,6 +228,28 @@ def test_legacy_numeric_confidence_links_spelling_reload(capsys): assert data["confidence_score"] == 0.85 +def test_legacy_numeric_confidence_normalization_is_idempotent(capsys): + """Healing must survive a round-trip: after the first load rewrites the tag to + INFERRED (with the float in confidence_score), a second load of the persisted + graph must stay silent and leave the score stable — otherwise the warning + would just move one run later.""" + from networkx.readwrite import json_graph + raw = {"nodes": [{"id": "n1", "label": "A", "file_type": "code", "source_file": "a.py"}, + {"id": "n2", "label": "B", "file_type": "code", "source_file": "b.py"}], + "links": [{"source": "n1", "target": "n2", "relation": "calls", + "confidence": 0.85, "source_file": "a.py"}]} + G1 = build_from_json(raw) + capsys.readouterr() + # persist exactly as graph.json would, then reload + persisted = json_graph.node_link_data(G1, edges="links") + G2 = build_from_json(persisted) + err = capsys.readouterr().err + assert "invalid confidence" not in err + d = edge_data(G2, "n1", "n2") + assert d["confidence"] == "INFERRED" + assert d["confidence_score"] == 0.85 + + def test_node_alias_canonical_field_wins(): """#2194: when both the canonical field and its alias are present, the canonical value wins and the alias key is left untouched."""