From c9f58f1eb899308f537b1b38d817e5bf599e2063 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sun, 25 Oct 2026 10:19:30 -0400 Subject: [PATCH] provenance: the round trip maps a document label to the stored code Co-Authored-By: Claude Opus 5.5 --- packages/flow/openadapt_flow/provenance.py | 17 ++++++++ packages/flow/openadapt_flow/verify_run.py | 10 ++++- packages/flow/tests/test_provenance.py | 48 ++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/packages/flow/openadapt_flow/provenance.py b/packages/flow/openadapt_flow/provenance.py index 8af4abbbd..8e9bcb193 100644 --- a/packages/flow/openadapt_flow/provenance.py +++ b/packages/flow/openadapt_flow/provenance.py @@ -358,12 +358,24 @@ def bound_fields(effects: Any) -> dict[str, str]: return out +def bound_maps(effects: Any) -> dict[str, dict[str, str]]: + """Parameter name -> the value map its field declares (label -> code).""" + + out: dict[str, dict[str, str]] = {} + for effect in effects: + value = getattr(effect, "value", None) + if value is not None and value.param and value.map: + out.setdefault(value.param, dict(value.map)) + return out + + def round_trip( provenance: ProvenanceFile, record: Optional[Mapping[str, Any]], fields: Mapping[str, str], documents: Mapping[str, Any], second: Extractor, + maps: Optional[Mapping[str, Mapping[str, str]]] = None, ) -> RoundTrip: """Compare ``record`` (as read back) with the source of each value. @@ -398,6 +410,11 @@ def round_trip( checks.append(FieldCheck(name, "no_document")) continue source_value = second.read(document, name, prov.source) + mapping = (maps or {}).get(name) + if mapping is not None and source_value is not None: + # The document shows the label, the record stores its code (a + # state's name and "MA", a provider's name and their id). + source_value = mapping.get(source_value.strip()) stored = "" if record.get(record_field) is None else str(record[record_field]) if source_value is None: checks.append(FieldCheck(name, "missing", stored)) diff --git a/packages/flow/openadapt_flow/verify_run.py b/packages/flow/openadapt_flow/verify_run.py index 1d9d4100e..77a9cf7d3 100644 --- a/packages/flow/openadapt_flow/verify_run.py +++ b/packages/flow/openadapt_flow/verify_run.py @@ -527,6 +527,7 @@ def _judge_source( from openadapt_flow.provenance import ( bound_fields, + bound_maps, default_extractor, round_trip, ) @@ -542,7 +543,14 @@ def _judge_source( } extractor = second or default_extractor(provenance) record = _target_record(effects, verdicts) if delta_held else None - result = round_trip(provenance, record, bound_fields(effects), documents, extractor) + result = round_trip( + provenance, + record, + bound_fields(effects), + documents, + extractor, + maps=bound_maps(effects), + ) evidence = result.summary() evidence["first_extractors"] = sorted( {p.extractor for p in provenance.params.values()} diff --git a/packages/flow/tests/test_provenance.py b/packages/flow/tests/test_provenance.py index d8f856ec5..ab1f08c77 100644 --- a/packages/flow/tests/test_provenance.py +++ b/packages/flow/tests/test_provenance.py @@ -328,3 +328,51 @@ def test_a_task_whose_values_differ_from_its_provenance_is_refused( with pytest.raises(GuardRefused) as refused: load_task(path) assert refused.value.code == "task_invalid" + + +def test_round_trip_maps_a_label_to_the_stored_code() -> None: + """The referral prints the state's name; the record stores its code.""" + + from openadapt_flow.provenance import bound_fields, bound_maps + + document = IntakeDocument( + "r.txt", "text", ["Last name: Okafor", "State: Massachusetts"] + ) + sha = sha256_bytes(document.text.encode("utf-8")) + prov = ProvenanceFile( + params={ + name: ParamProvenance( + value=value, + source=SourceRef(kind="document", sha256=sha, line=line, label=label), + extractor="model:first-reader", + ) + for name, value, line, label in ( + ("lname", "Okafor", 1, "Last name"), + ("state_label", "Massachusetts", 2, "State"), + ) + } + ) + match = {"lname": {"param": "lname"}} + effects = [ + Effect(kind=EffectKind.RECORD_WRITTEN, match=match), + Effect( + kind=EffectKind.FIELD_EQUALS, + match=match, + field="state", + value={"param": "state_label", "map": {"Massachusetts": "MA"}}, + ), + ] + from openadapt_flow.provenance import default_extractor + + def judge(stored: str) -> str: + return round_trip( + prov, + {"id": 1, "lname": "Okafor", "state": stored}, + bound_fields(effects), + {sha: document}, + default_extractor(prov), + maps=bound_maps(effects), + ).status + + assert judge("MA") == "held" + assert judge("RI") == "refuted"